
On 04/27/11 18:23, Noah Roberts wrote:
On 4/27/2011 1:06 PM, Larry Evans wrote:
Use of mine is more like so:
struct field0 { typedef int value_type; }; struct field1...2 ...
struct my_reflected_type : build_class < var< field0, rw> , var< field1, r> // can't assign through reflection.
Why not:
var< field> , var< field const>
instead? I guess I'd have to see the code to really understand why the extra type parameter is needed, but maybe a short explanation would be enough.
I suppose that'd be an interesting method of doing it. The field itself though isn't the type stored so the const would have to be applied to that value or applied in the same way I end up using the access rights.
I did understand that a fieldI was not what was actually stored and that, instead, a FieldI::value_type was stored. The only reason for using "FieldI const" instead of "FieldI,r" as the template arguments to var was just to eliminate the need for more "type flags", such as r and rw. However, now that I've thought about it some more, that seems a minor point. [snip]
>::type { virtual ~my_reflected_type(){} DECLARE_PARAM_CONSTRUCTOR(my_reflected_type);
Could you provide the source code for:
DECLARE_PARAM_CONSTRUCTOR
to give us some idea of what it's doing?
template < typename Params > name (Params const& params) : type(params) {}
It just saves time. The type you inherit from (which declares itself as type inside itself like a lot of meta-objects) has two constructors. One is the default, the other is just like the above and forwards the params to the value implementation objects to either accept or discard. A lot of times you won't care to make one of these yourself, but instead make a constructor that uses params to initialize. It's currently important for base classes though so derived classes can forward params.
So, params is bassed to each FieldI type and that FieldI type decides which of the params to use to initialize it's FieldI::value_type? IOW, params has to contains all the information needed to initialize all type types in: base_class < var< field0, rwFlag0> , var< field1, rwFlag1> , var< field2, rwFlag2> ... , var< fieldN, rwFlagN>
and somehow, the var or fieldI classes figure how to intialize from the single params argument? Some actual prototype code would help me understand better.
Doing correct base construction is one of the to-be-solved things.
Ah, so maybe I should wait for that to have my last question answered?
};
struct my_reflected_type2 : derive_from < my_reflected_type , var< field2, rw> >::type { my_reflected_type2() : type(( field0()<= some_val , field1()<= some_val , field2()<= some_val )) { // directly access read-only value and assign... var<field1,r>::value = 66.; } };
Hmm... does the user have to repeat this some number of times, same the number of fields in the my_reflected_type? IOW, for I=3...N (where N is number of fields in my_reflected_type):
my_reflected_typeI { //repeat like my_reflected_type2 };
Or is this done by the macro using boost,preprocessor library? I would guess the latter because providing all this boilerplate code would seem too much to me.
My apologies. I should have read closer the code you provided. Apparently, my_reflected_type2 is an extension of my_reflected_type because it uses template, derive_from<my_reflected_type,...>. So, I guess the base_class<V0,V1,V2,...>::type does an mpl::fold of derive_from?
You can initialize or not initialize any field you chose,
Understandable.
thus named params rather than construction like tuple<> or others.
I don't understand "thus named params". Could you clarify?
If you initialize with a set of params, the values they target will
How are "values targeted"? I guess that's done with the <= operator you mention further down; however, that appears to use separate values for each field; yet, params seems to be used to intialize each field. Again, prototype code might clear things up.
be thus initialized while those you don't supply will be default constructed. (so yeah, sort of has to do with my earlier question)
Did you have a chance to took at the code linked-to in my reply: http://article.gmane.org/gmane.comp.lib.boost.user/67038 to your earlier question? That link to was a test driver, whose relevant part was started with: trace_scope ts("all_of_aligned index_component DEMO"); The implementation tested in that part is here: http://svn.boost.org/svn/boost/sandbox/variadic_templates/boost/composite_st... AFAICT, that code does what you describe, but with variadic templates. Briefly, it does this starting from the variadic CTOR: template < index_type... Keys , typename... CtorTypes > indexed_ctor_args_all_of_aligned ( index_component < index_type , Keys , CtorTypes >const&... ctor_args ) { args_ftor < index_component < index_type , Keys , CtorTypes >... > f_args ( this->address() , ctor_args... ) ; mpl::for_each<typename layout_indices::indices>(f_args); } The ctor_args are produced (as shown in the driver) with arguments created with: index<IndexType,IndexValue>::operator=<Component> defined here: http://svn.boost.org/svn/boost/sandbox/variadic_templates/boost/composite_st... I'd guess this index<,>::operator= plays a role similar to your <= operator. The args_ftor::get_our is used with SFINAE and args_ftor::inject to select whether to default construct the component or copy construct the component. This is done by first passing 'this' to the overloaded get_our in method: template < index_base IndexValue > void operator() ( mpl::integral_c<index_base,IndexValue> a_index )const Since '*this' is derived from all of: index_component<index_type,Keys,CtorTypes>... either the selected component is found by the compiler and: template <... > void inject ( index_component<index_type,Key,CtorType>const* a_arg )const is called and the Component copy ctor is used to inject into the components buffer, or the component is not found and: template <... > void inject ( not_in_args* )const is called and the default ctor is used to inject into the components buffer. In each case the components buffer is populated using placement new. This components buffer together with placement new allows separation of reserving the memory for the components from the initialization of components. BTW, I think (although I've not tried at all to do this with fusion) that by separating the component storage reservation from the component initialization, accomplishing this:
Doing correct base construction
would be a bit easier than with something like fusion which, since it uses member variables, must do the storage allocation at about the same time, i.e. in the same class, as the initialization. That's one reason I'm interested in seeing how you will do this (which, according to the completion of the above quote:
Doing correct base construction is one of the to-be-solved things.
has not yet been done.). Also, I'd think a non-variadic version could be done without much trouble, in case you've no access to a variadic templates compiler.
If your value can't be default constructed you HAVE to provide a param for it. It then has to be copy constructable for the param will first initialize the value from your provided data. The type that feeds into a set of params is specifically the value of the field in other words (though I'm sure this could be changed, thinking of it now).
Noah, I'm still not sure of the meaning of: the param will first initialize the value from your provided data because I thought param was the provided data. Also, I'm unsure of the meaning of: The type that feeds into a set of params Could you please clarify? [snip]
Building records on the fly with field()<=value syntax was very desired. Comes in handy when you have a function expecting a record with x,y,z fields in it, have those values, but don't want to build the record class.
I don't understand that. Could you be more concrete in describing this use case, maybe by providing a concrete example?
So for instance:
template < typename Record > void fun(Record const& rec) { static_assert(has_field<Record, what_i_need>::value, "Supplied record doesn't have the necessary data!");
..... }
I can call this function like so:
what_i_need::value_type value; int something_else;
fun(( what_i_need() <= value , int_field() <= something_else));
Ah, now I see. This expression: ( what_i_need() <= value , int_field() <= something_else ) is an example of "Building records on the fly". I'm guessing you're doing something like what proto does with expression template where you define an operator <= to produce 1 element in something like a map and then use the comma operator to augment that map.
Records and param lists being nearly interchangeable in this regard.
So, the above expression which I copied from your code is what you mean by a "param list"?
Only showing the int_field() because it can happen...the function simply ignores what it doesn't know about.
---------------------------- BTW, a previous post of your's to the users list:
http://thread.gmane.org/gmane.comp.lib.boost.user/66818/focus=67038
seems related to your:
field()<=value
syntax. Is it?
Sort of. It's more about initializing the values themselves with the params. I ended up with something more like:
template < typename Params > value_impl(Params & p, typename boost::enable_if< has_field<Params,my_field> >::type * = 0) : value(get<my_field>(p)) {}
template < typename Params > value_impl(Params & p, typename boost::disable_if< has_field<Params,my_field> >::type * = 0) : value() {}
OK, now things are getting clearer. The has_field in the above corresponds roughly to the get_our mentioned above. The Params corresponds to the superclasses of args_ftor. the value(get<my_field>(p)) corresponds to: void inject ( index_component<index_type,Key,CtorType>const* a_arg )const and the value() corresponds to: void inject ( not_in_args* )const Just above, you say:
I ended up with something more like:
However, earlier you say:
Doing correct base construction is one of the to-be-solved things.
so, is this value_impl code not yet working, or do you mean something else is not working when you say "base construction is one of the to-be-solved things"?
I don't have rights to the code though I do the idea. If I did it I'd need to address a few things:
* correct base initialization. With reflected types though your "base" is not your direct, actual base so that becomes an issue.
* Better implementation of fields represented by abstract types.
Currently there's three approaches: 1) have the field in the base and make it refer to the abstract type.
Do you mean the field is a pointer or reference to an abstract type?
2) have the field in the derived and make it refer to the most derived type. Of course now you don't have access to it from the base.
Could you use CRTP? http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern I think spirit uses this a lot.
3) Put one field in the base and a different field in the derived. Make the base's a "reference" field. Does the trick but it's hacky.
Hmmm. So 1) actually has a pointer, and the derived CTOR uses new to create the pointer which it then passes to the base CTOR? And so 3) actually has a field whose reference is passed to the CTOR of the base?
I really want the best of both worlds. Put the field in the base to the abstract type. When I request the field from a derived record have it return the derived type. In other words:
struct base_field_type; struct derived_field_type;
struct field { typedef base_field_type value_type; };
derived d; base * b = &d;
decltype(get<field>(d)) == derived_field_type& decltype(get<field>(*b)) == base_field_type&
* functions. I imagine something like so:
struct reflected : build_class < fun<fieldx, void(int)> >::type { void call(fieldx, int) { .... } };
reflected r; call<fieldx>(r, 42); // maybe call<fieldx>(r)(42);
I'm not sure how much actual value this has though. Personally I don't have much use for it. On the other hand, I do have use for "fields" implemented by functions such that:
int x = get<fieldx>(r);
is actually a call to a function rather than reference to a variable in r.
Ok, then that's beyond what the code I proposed is capable of, AFAICT. So, as I said, I'd be interested in seeing how you do this. Best of luck! -regards, Larry