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
>; (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
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
> 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_traitssqlpp::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