On 2014-08-20 00:34, Adam Wulkiewicz wrote:
Roland Bock wrote:
Ok AFAIU a struct like _member_t should define some convenient member variable for the user and must define operator() for the library. But the rest could be automatically generated, couldn't it? Why not just pass a list of templates of classes adapted to MemberType concept (defined operator()) into the table/column/etc.? I'm thinking about something like the code below. I don't know exactly what's required so this is just an example of a technique rather than a solution ready-to-use in sqlpp. The idea is good. For the columns, you will have to add a few more
On 2014-08-19 18:56, Adam Wulkiewicz wrote: parameters, e.g. the value_type (mandatory), can_be_null, must_not_insert, must_not_update, null_is_trivial, trivial_is_null, maybe as per your suggestion a default value or a function for producing it. That default stuff might be tough in such a design.
The additional traits would be a list of variadic template parameters. So if this list contained only one type, e.g. sqlpp::default_traits the default traits could be generated e.g. by specializing sqlpp::make_traits<>. Since it's impossible to define a default argument of a template parameters pack (another missing language feature?) it could be "simulated" with something like:
template
struct column { using traits = make_traits
; }; Or all traits could be passed as one list type like MPL sequence or someting like that as you wrote below.
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.
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 would thus add a struct which inherits from the column template instance for each column, e..g.
struct alpha: public column
{}; With variadic templates the construction of traits out of this would be straightforward. An alternative would be to take additional parameters/traits list as the 3rd parameter as you wrote below.
Btw, why a column must be aware about a Table?
For three reasons at least: 1. representation: in most statement types, more than one table can be involved, e.g. when using some kind of join. To avoid name clashes, columns are represented as tablename.columnname when being serialized 2. consistency checking: sqlpp11 performs a lot of chekcs at compile time that your statements are consistent, for instance, it detects if you are selecting columns from tables which are not mentioned in the from clause. It therefore has to know which tables the selected columns belong to. 3. determining can_be_null for result fields: if you are using any of the outer joins, then selected columns of those outer tables can be null. In order to determine this at compile time, again the column has to be associated with its table. And this association has to be done not only for those pre-defined tables and their columns, it also has to work with sub-selects which are used as tables, of course :-)
Can a table also have some traits specified?
Not today, but that will probably change soon. read-only would be a very good trait for a table, for instance.
I'm asking because then there would be 2 lists that should be passed - Members and Traits.
I tried something similar a while back but failed, which is mainly due to lack of perseverance, I guess.
Right now, I am happy with the current design because it is quite easy to change things, like introducing that default value or a function for handling attempts to read NULL.
Sure, I'm not saying that you should change the design. I'm just sharing my thoughts.
And I really appreciate it :-)
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. Cheers, Roland