Boost logo

Boost :

Subject: Re: [boost] sqlpp11, 3rd iteration
From: Adam Wulkiewicz (adam.wulkiewicz_at_[hidden])
Date: 2014-08-20 13:21:05


Roland Bock wrote:
> On 2014-08-20 00:34, Adam Wulkiewicz wrote:
>> Roland Bock wrote:
>>> But thats manageable. And yes, the code would be shorter, although not
>>> that much, I suspect. The only problem I have with it is that now the
>>> column types are going to be about a hundred characters long. And users
>>> are going to operate on columns all the time. So error message have to
>>> be short.
>> Do you have in mind the code of the library or user's code?
> User's code:
>
> * You have the member template which must be defined outside.
> * You have the get_name method which should be defined outside the
> member template since I have no instance of that template where I
> need the name (you still cant use a string literal as template
> parameter directly, right? Like table_t<"sample">?)
> * You need to group the member template and the get_name method since
> they are always used in combination
> * You need a struct or class to hold the default value or function
>
> And if you don't want to have all this flying around as individual
> pieces with individual names, then you will group it into a class. And
> you're back to where you started.
Yes, passing a string literal isn't possible unfortunately.

So the member and a name must be bound together somehow but the rest
could still be automatically generated. In particular, IMHO the
specification of a default value should be optional (e.g. passed as yet
another trait). The library shouldn't require defining it each time,
even as some dummy function if a user wanted to use exceptions. Besides,
defining the default value generator as external to the member-"name"
binding would probably be preferable because the same generator could be
reused for many columns. However I don't expect that the generator would
do something complicated, rather just return a value. But for integral
members it could be predefined in the sqlpp and it could be passed as
just 1 additional type.

>
>> I expect that the user's code, even not using defaults, would be a lot
>> shorter.
>> But the most important is that the definition of a table would
>> probably be more clear, in one place, etc.
>> Or am I wrong?
> I think you're wrong, although I'd love to be wrong about that :-)
>
> Based on my thoughts above you'd end up with
>
> struct Alpha
> {
> struct _name_t
> {
> static constexpr const char* _get_name() { return "alpha"; }
> template<typename T>
> struct _member_t
> {
> T alpha;
> T& operator()() { return alpha; }
> const T& operator()() const { return alpha; }
> };
> };
> struct _trivial_t
> {
> int64_t get_trivial_value() { return 42; }
> }
> };
>
> struct alpha: public column_t<MyTable, Alpha,
> sqlpp::make_traits<sqlpp::integral, ...>>;
>
> (I need to be able to combine name and trivial value freely, for
> instance when using an alias of a column, thats why those have to be
> separated).
>
> I seem to be ending up with exactly the same number of lines in the user
> code.
>
> Technically, I /could/ do without the name_t and move the get_name
> function into the member template code, but that would also mean
> inheriting multiple versions of the get_name method into tables and rows.

Hmm, is the member template used in many places? AFAIU it must be used
at least 2 times, to define columns, tables, etc. and later to construct
a row. Well, it isn't that important.

If you write it this way:

     struct Alpha
     {
       static constexpr const char* _get_name() { return "alpha"; }
       template<typename T>
       struct _member_t
       {
         T alpha;
         T& operator()() { return alpha; }
         const T& operator()() const { return alpha; }
       };
     };

     struct alpha: public column_t<MyTable, Alpha,
         sqlpp::make_traits<sqlpp::integral, sqlpp::trivial_integral<42>, ...>>;

it's shorter :)

The above assuming that sqlpp::trivial_integral<> is a
default-constructible functor returning a value convertible to int64_t.
So basically trivial_t but with operator() instead of get_trivial_value().

<snip>
>>> If you want to put everything into that one list of template parameters,
>>> it is much tougher, IMO. I mean how would you add a function for
>>> handling access to NULL value? You would need another class, I think.
>>> And you would have to group those tags into a tuple or type_set, because
>>> otherwise it would be ugly to add another optional parameter...
>> I'm guessing that the function or ... could be passed as yet another
>> trait like:
>>
>> struct alpha: public column<tab_member, alpha_member,
>> sqlpp::trivial_value<some_generator> >
>>
>>
>> If not passed, a default trivial value would be used.
>>
>> The best would be to somehow pass a static value in compile-time but
>> only integral types could be handled this way. The reference to the
>> global external variable of non-integral type could also be passed as
>> a template parameter but still it would have to be defined somewhere
>> so it wouldn't be convenient.
>>
>> So some_generator could be a type of default-constructible function
>> object or a pointer to function, etc.
>>
>> Or do someone knows some trick that could be used here?
> Well, anonymous in-place class definitions would help to keep the
> relevant information in one place. Another missing language feature, I
> think. Something like
>
> struct alpha : public column_t<
> Table,
> struct {
> static constexpr const char* _get_name() { return "alpha"; }
> template<typename T>
> struct_member_t
> {
> T alpha;
> T& operator()() { return alpha; }
> const T& operator()() const { return alpha; }
> };
> },
> struct { int64_t _get_trivial() const { return 42;}},
> sqlpp::make_traits<sqlpp::integral>
> > {};
>
> But that's still not much shorter :-(
>
> I believe that the key is the name stuff. If we could use names in the
> same way as types and values, for instance as template parameters, this
> would be much easier, both in user code and in the library code.
>

It could be convenient to generate a type of a non-parameter lambda
expression in unevaluated context, something like:

sqlpp::make_traits<sqlpp::trivial_value<decltype([](){return 0;})>>

but this is not possible too.

Regards,
Adam


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk