[intro] Preview: Introspection library

Hi, I'd be interested in comments on an introspection library I've been developing. there has been no "Is there any interest..."-e-mail yet, as it was started out of necessity in "namespace detail" of another library, so you're also welcome to express interest/disinterest in a library like this, without any specific comments. source code: https://svn.boost.org/trac/boost/browser/sandbox/intro/boost/intro/ the library defines a concept that exposes the members of a class, for introspection algorithms to work with. e.g. the "print" algorithm applied to a simple object implementing the concept yields the following results: intro::print(a,std::cout); ---- { A_base = { int member_of_base = 789; }; int a = 123; float b = 456.7; vector<int,class std::allocator<int> > vec = {2, 4, 6, 8, 10}; } --- other algorithms that are currently implemented: - apply_members[_binary]: apply a user-supplied functor to each member - apply_recursive: follow pointers, references, STL container elements, etc. - serialize: serialization of an object tree of 1M objects: 80 ms. Boost.Serialization: 330 ms. serialization of an object graph of 1M objects: 410 ms. Boost.Serialization: 2700 ms. - move: move-construct an object - reset_shared planned or not yet in the sandbox are deserialize, print_recursive, copy_deep, copy_shallow, equal_deep, equal_shallow. an implementation of the concept looks like this: template<class Mapping> friend void introspect(Mapping mapping,type<A>){ mapping (base_class<A_base>() ) (member<A,int ,&A::m_a>() ) (member<A,float ,&A::m_b>() ) (member<A,std::vector<int>,&A::m_vec>()) (); } members can also be associated with tags or data, and can be assigned semantics (e.g. polymorphic, shared/unique for pointers), but I won't go into too much detail for now. Best, Stefan

On 10-06-28 8:00 AM, Stefan Strasser wrote:
template<class Mapping> friend void introspect(Mapping mapping,type<A>){ mapping (base_class<A_base>() ) (member<A,int ,&A::m_a>() ) (member<A,float ,&A::m_b>() ) (member<A,std::vector<int>,&A::m_vec>()) (); }
I think a library like this would be useful. Isn't it simpler to specialize a template? Why does this need to be done at run-time? How do you introspect a type's constructors? Thanks and good luck. Sohail (where did my signature file go?)

On Mon, Jun 28, 2010 at 3:32 PM, Sohail Somani <sohail@taggedtype.net> wrote:
On 10-06-28 8:00 AM, Stefan Strasser wrote:
template<class Mapping> friend void introspect(Mapping mapping,type<A>){ mapping (base_class<A_base>() ) (member<A,int ,&A::m_a>() ) (member<A,float ,&A::m_b>() ) (member<A,std::vector<int>,&A::m_vec>()) (); }
I think a library like this would be useful.
Just FYI there is also another introspection/reflection library being developed for boost called Mirror. It can be downloaded from sourceforge here: http://sourceforge.net/projects/mirror-lib/files/ or from the boost vault. There are actually two versions: pre-c++0x (which is currently on hold) and c++0x (being actively developed) The current (work-in-progress) docs for the c++0x version can be found here: http://kifri.fri.uniza.sk/~chochlik/mirror-lib/html/index.html To get some idea on how the library is used I suggest looking here: http://kifri.fri.uniza.sk/~chochlik/mirror-lib/html/examples.html The registering process is explained here: http://kifri.fri.uniza.sk/~chochlik/mirror-lib/html/mirror__registering.html although much more can be found in the docs.
Isn't it simpler to specialize a template? Why does this need to be done at run-time? How do you introspect a type's constructors?
Mirror allows to introspect constructors and actually there is a utility called factory generator, which uses this meta-information to generate factory classes for various product types.The docs for this utility can be found here: http://kifri.fri.uniza.sk/~chochlik/mirror-lib/html/mirror__factory__generat... but they are not finished.
[snip] BR, Matus

Matus Chochlik wrote:
Just FYI there is also another introspection/reflection library being developed for boost called Mirror.
Do these two libraries really are concerned with the same problem domain? If that should be the case, "Mirror" seems to be a better name than "Intro", which has too much meaning in general, but not enough meaning with respect to the problem domain at hand. Regards, Thomas

On Mon, Jun 28, 2010 at 4:15 PM, Thomas Klimpel <Thomas.Klimpel@synopsys.com> wrote:
Matus Chochlik wrote:
Just FYI there is also another introspection/reflection library being developed for boost called Mirror.
Do these two libraries really are concerned with the same problem domain?
From a quick glance at Stefan's library I would say yes. There are some things which one can do and the other can't and vice-versa. For example Mirror currently does not cooperate well with Boost.Serialization, but this is one of the things on my TODO list.
If that should be the case, "Mirror" seems to be a better name than "Intro", which has too much meaning in general, but not enough meaning with respect to the problem domain at hand.
Regards, Thomas _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
-- ________________ ::matus_chochlik

On 10-06-28 10:01 AM, Matus Chochlik wrote:
Mirror allows to introspect constructors and actually there is a utility called factory generator, which uses this meta-information to generate factory classes for various product types.The docs for this utility can be found here: http://kifri.fri.uniza.sk/~chochlik/mirror-lib/html/mirror__factory__generat... but they are not finished.
That's funny, I've been working on something similar from a different angle: http://bitbucket.org/cheez/dicpp/wiki/Home Will check out Mirror, thanks for the link.

Zitat von Matus Chochlik <chochlik@gmail.com>:
On Mon, Jun 28, 2010 at 3:32 PM, Sohail Somani <sohail@taggedtype.net> wrote:
On 10-06-28 8:00 AM, Stefan Strasser wrote:
template<class Mapping> friend void introspect(Mapping mapping,type<A>){ mapping (base_class<A_base>() ) (member<A,int ,&A::m_a>() ) (member<A,float ,&A::m_b>() ) (member<A,std::vector<int>,&A::m_vec>()) (); }
I think a library like this would be useful.
Just FYI there is also another introspection/reflection library being developed for boost called Mirror. It can be downloaded from sourceforge here: http://sourceforge.net/projects/mirror-lib/files/ or from the boost vault.
I did look at Mirror as you know, however, AFAIU it does not fulfill my requirement of an easy-to-implement concept that can be used to implement no-overhead introspection algorithms. In case I got that wrong or in case that changes, I'd be happy to switch to Mirror for gaining access to the class members, as the focus of my library is on the algorithms, with introspection as a means to this end. The problem domain of "Intro" isn't really introspection/reflection, but algorithms using introspection.
For example Mirror currently does not cooperate well with Boost.Serialization, but this is one of the things on my TODO list.
Intro does not coorperate with Boost.Serialization but implements serialization on its own (although it is planned to fall back to the Serializable concept in case introspect() is not available.) Boost.Serialization has several implementation related issues but also problems that are by design (e.g. "track_selectively") that do not allow for optimal performance. see my performance comparison. Stefan

On Mon, Jun 28, 2010 at 4:50 PM, Stefan Strasser <strasser@uni-bremen.de> wrote:
Zitat von Matus Chochlik <chochlik@gmail.com>:
On Mon, Jun 28, 2010 at 3:32 PM, Sohail Somani <sohail@taggedtype.net> wrote:
On 10-06-28 8:00 AM, Stefan Strasser wrote:
template<class Mapping> friend void introspect(Mapping mapping,type<A>){ mapping (base_class<A_base>() ) (member<A,int ,&A::m_a>() ) (member<A,float ,&A::m_b>() ) (member<A,std::vector<int>,&A::m_vec>()) (); }
I think a library like this would be useful.
Just FYI there is also another introspection/reflection library being developed for boost called Mirror. It can be downloaded from sourceforge here: http://sourceforge.net/projects/mirror-lib/files/ or from the boost vault.
I did look at Mirror as you know, however, AFAIU it does not fulfill my requirement of an easy-to-implement concept that can be used to implement no-overhead introspection algorithms.
I would be interested in some examples if you were kind enough to provide them.
In case I got that wrong or in case that changes, I'd be happy to switch to Mirror for gaining access to the class members, as the focus of my library is on the algorithms, with introspection as a means to this end.
After I tie up some loose ends on Mirror, I'm planning to ask for opinions about the library on this list and start a wider discussion. However currently I have to attend to some other projects, but I'm expecting to have some free time to work on Mirror in a couple of days again.
The problem domain of "Intro" isn't really introspection/reflection, but algorithms using introspection.
For example Mirror currently does not cooperate well with Boost.Serialization, but this is one of the things on my TODO list.
Intro does not coorperate with Boost.Serialization but implements serialization on its own (although it is planned to fall back to the Serializable concept in case introspect() is not available.)
My mistake I've had only a quick glance at Intro, from which I incorrectly assumed that it does.
Boost.Serialization has several implementation related issues but also problems that are by design (e.g. "track_selectively") that do not allow for optimal performance. see my performance comparison.
Cooperation with Boost.Serialization's object tracking is one of the things I need to incorporate into Mirror in order for it to be usable with Serialization. BR Matus

template<class Mapping> friend void introspect(Mapping mapping,type<A>){ mapping (base_class<A_base>() ) (member<A,int ,&A::m_a>() ) (member<A,float ,&A::m_b>() ) (member<A,std::vector<int>,&A::m_vec>()) (); }
I would be interested in some examples if you were kind enough to provide them.
for algorithms? serialization. see my initial mail for a list. for concept implementation? I'll try my best, using your documentation only and the same class as above: BOOST_MIRROR_REG_CLASS_BEGIN(class,test,A) BOOST_MIRROR_REG_BASE_CLASSES_BEGIN BOOST_MIRROR_REG_BASE_CLASS(_,public,A_base) BOOST_MIRROR_REG_BASE_CLASSES_END BOOST_MIRROR_REG_CLASS_MEM_VARS_BEGIN BOOST_MIRROR_REG_CLASS_MEM_VAR(private,_,_,m_a) BOOST_MIRROR_REG_CLASS_MEM_VAR(_,_,_,m_b) BOOST_MIRROR_REG_CLASS_MEM_VAR(_,_,_,m_vec) BOOST_MIRROR_REG_CLASS_MEM_VARS_END BOOST_MIRROR_REG_CLASS_END inherent in a reflection library is that the "registering" concept defined by it will be imposed on the library end user, i.e. if I write a library that needs reflection on types and I use Mirror for that, I require the user of my library to implement the mirror concept for each type. besides my dislike for the use of macros, the user should not have to provide any information that isn't required by the component requiring reflection on (parts of!) that type. the component using reflection might also require additional information provided by the user, e.g. in the case of a lot of Intro algorithms, whether a pointer refers to shared memory or not: (member<A,B *,&A::ptr_to_B,shared_pointer>() ) //(1) or tag names for XML serialization, or ... I'm not saying that I have a better way to register classes/members in Mirror, I'm not even sure there is a better way. just trying to explain why requiring the user of the library that uses Intro to register all his types in the way shown above wasn't an option. I don't like the introspect() concept either and would have rather used Serializable, but it is the best I could come up with, and Serializable results in very inefficient algorithms especially when using more than one instance at a time (e.g. copying an object, or comparing 2 objects) Stefan (1) shared_pointer is the default semantics for type "B *", so it doesn't have to be specified. typedefed to semantics<mpl::set<unique,sealed>,semantics<mpl::set<shared,polymorphic> > >, i.e. the member ptr_to_B itself is unique, but the object it is pointing to might be pointed to by other pointers (shared), and might not have type B but a derived type.

Zitat von Sohail Somani <sohail@taggedtype.net>:
On 10-06-28 8:00 AM, Stefan Strasser wrote:
template<class Mapping> friend void introspect(Mapping mapping,type<A>){ mapping (base_class<A_base>() ) (member<A,int ,&A::m_a>() ) (member<A,float ,&A::m_b>() ) (member<A,std::vector<int>,&A::m_vec>()) (); }
I think a library like this would be useful.
Isn't it simpler to specialize a template? Why does this need to be done at run-time?
I assume you mean as opposed to something like typedef mpl::vector<member<...>,member<...> > introspect; there are several reasons for this: - I prefer the syntax above - although it is a runtime function it isn't really executed at runtime. an optimizing compiler generates no code. - if you need a MPL sequence, you can easily obtain one: struct functor{ template<class Sequence> void operator()(Sequence) const{ } }; intro::apply_local_sequence<A>(functor()); - you can associate state with a member. for example: mapping (member<...>(),"member_name") (); which is useful e.g. for algorithms serializing to XML, or mapping a member to an SQL database field. - most algorithms only need to iterate members and don't need a MPL sequence, which saves lots of instantiations. they simply implement a functor that gets called for each member and call intro::for_each<A>(functor());
How do you introspect a type's constructors?
none of the algorithms need reflection of constructors or other member functions. if you need it it can be added without changing the library. it would probably look something like: template<class A0,class A1...> struct constructor{}; mapping (constructor<arg_type1,arg_type2>()) ();

On 06/28/10 07:00, Stefan Strasser wrote:
Hi,
I'd be interested in comments on an introspection library I've been developing.
[snip] Could this library be used to implement a class metafunction template: template<typename T> gcptr_locations { typedef vector_c < std::ptrdiff_t , offset0 , offset1 ... , offsetn > gcptr_offsets ; }; where gcptr_offsets would contain the offsets from the start of a T to the location of all the: template<typename S> struct gcptr; contained within T? gcptr<S> might be, for instance, a smart pointer to an S. This would be useful for garbage collection. BTW, based on just the class name, the code: https://svn.boost.org/trac/boost/browser/sandbox/intro/boost/intro/detail/in... could be replaced with that here: https://svn.boost.org/trac/boost/browser/sandbox/variadic_templates/boost/io... An example of its use is here: https://svn.boost.org/trac/boost/browser/sandbox/variadic_templates/libs/com...

Zitat von Larry Evans <cppljevans@suddenlink.net>:
On 06/28/10 07:00, Stefan Strasser wrote: Could this library be used to implement a class metafunction template:
<snip>
where gcptr_offsets would contain the offsets from the start of a T to the location of all the:
template<typename S> struct gcptr;
contained within T? gcptr<S> might be, for instance, a smart pointer to an S. This would be useful for garbage collection.
in principle, yes. one of my use cases is similar to this. however, I would not recommend implementing this as a metafunction, as it is much easier to use the "apply_members" algorithm when building your GC reference graph. struct functor{ template<class T,class Semantics> void operator()(T &,Semantics){} template<class T,class Semantics> void operator()(gc_ptr<T> &ptr,Semantics){ //do something with ptr } }; intro::apply_members(functor(),obj); this generates no code with an optimizing compiler (except your own).
BTW, based on just the class name, the code:
https://svn.boost.org/trac/boost/browser/sandbox/intro/boost/intro/detail/in...
could be replaced with that here:
https://svn.boost.org/trac/boost/browser/sandbox/variadic_templates/boost/io...
thanks for pointing this out. I did search boost, but only the release. I'll only use released libraries as dependencies for now.

On 6/28/2010 8:00 AM, Stefan Strasser wrote:
Hi,
I'd be interested in comments on an introspection library I've been developing.
there has been no "Is there any interest..."-e-mail yet, as it was started out of necessity in "namespace detail" of another library, so you're also welcome to express interest/disinterest in a library like this, without any specific comments.
And the documentation for this library is where ... ?

On Mon, Jun 28, 2010 at 8:00 AM, Stefan Strasser <strasser@uni-bremen.de> wrote:
I'd be interested in comments on an introspection library I've been developing.
You may find paper "Scrap++: Scrap Your Boilerplate in C++"[1] to be interesting for ideas. One thing that confuses me about your library is I don't know whether the reflections are semantic or syntactic. I do think a clear separation of syntax and semantics is important to writing clear code. Perhaps you can show what a shared_ptr "implementation of the concept" looks like? Fusion's adapters, which are purely syntactic, are perfect for what I've needed so far. Syntactically speaking, are you adding anything besides data inheritance? [1] http://doi.acm.org/10.1145/1159861.1159871 -David -- David Sankel Sankel Software www.sankelsoftware.com 585 617 4748 (Office)

Zitat von David Sankel <camior@gmail.com>:
On Mon, Jun 28, 2010 at 8:00 AM, Stefan Strasser <strasser@uni-bremen.de> wrote:
I'd be interested in comments on an introspection library I've been developing.
You may find paper "Scrap++: Scrap Your Boilerplate in C++"[1] to be interesting for ideas. One thing that confuses me about your library
thanks. (direct link: http://sms.cs.chalmers.se/publications/papers/2006-WGP.pdf ) do I understand the paper correctly that you can't traverse two instances of a type at the same time? that's a central part to my approach. the (const) traversal of a single instance could have been accomplished with the Serializable concept, but algorithms like copying or comparing objects could only be implemented very inefficiently by serialization.
is I don't know whether the reflections are semantic or syntactic. I do think a clear separation of syntax and semantics is important to writing clear code. Perhaps you can show what a shared_ptr "implementation of the concept" looks like?
it depends on the shared_ptr implementation if you'd actually implement introspect() for shared_ptr, but I guess you mean supporting shared_ptr in a specific algorithm. apply_recursive, which seems to be comparable to scrap++: template<class F,class Data,class T,class Semantics> void apply_recursive(F const &f,Data &data,shared_ptr<T> &ptr,Semantics){ if(ptr) intro::apply_member_recursive(f,data,*ptr,typename Semantics::indir_semantics()); } this function is found via ADL. apply_member_recursive calls f(*ptr), but only after polymorphic dispatch if needed, and after it has been checked that *ptr hasn't already been traversed, e.g. through another pointer.
Fusion's adapters, which are purely syntactic, are perfect for what I've needed so far. Syntactically speaking, are you adding anything besides data inheritance?
I'm not familiar with that distinction wrt reflection. Stefan

On Tue, Jun 29, 2010 at 6:21 PM, Stefan Strasser <strasser@uni-bremen.de> wrote:
Zitat von David Sankel <camior@gmail.com>:
On Mon, Jun 28, 2010 at 8:00 AM, Stefan Strasser <strasser@uni-bremen.de> wrote:
I'd be interested in comments on an introspection library I've been developing.
You may find paper "Scrap++: Scrap Your Boilerplate in C++"[1] to be interesting for ideas. One thing that confuses me about your library
thanks. (direct link: http://sms.cs.chalmers.se/publications/papers/2006-WGP.pdf )
do I understand the paper correctly that you can't traverse two instances of a type at the same time?
I'm not too familiar with that paper, but the SYB approach in general covers this use case, coined twin traversal.
that's a central part to my approach. the (const) traversal of a single instance could have been accomplished with the Serializable concept, but algorithms like copying or comparing objects could only be implemented very inefficiently by serialization.
Semantics deal with the meaning of something and syntax deals with the internal structure of the representation. The serialization library describes its feature as a function that converts a supported object to a sequence of bytes and back into an "equivalent structure". The meaning of "equivalent structure" unfortunately isn't defined in the documentation. Consider the following example: struct Z { A * a; , B b; }; If we consider syntactic equivalence, the result of serializing this would be something like: Z{ a=0x029348af, b=B{...} } If we use semantic equivalence, serialization could result in any number of things, including: Z { a=new A{...}, b=B{...} } or even: Z {} It depends on what the meaning of a Z is. I argue that a traversable concept should not mix syntax and assumed semantics, but should instead be strictly syntactic. The algorithms that are included in the library should mainly work on this level.
is I don't know whether the reflections are semantic or syntactic. I do think a clear separation of syntax and semantics is important to writing clear code. Perhaps you can show what a shared_ptr "implementation of the concept" looks like?
it depends on the shared_ptr implementation if you'd actually implement introspect() for shared_ptr, but I guess you mean supporting shared_ptr in a specific algorithm. apply_recursive, which seems to be comparable to scrap++:
All the algorithms included in the library that work on a semantic level (like equality, serialization, etc.), might have a default implementation that works only on the syntax (although this is scary), but ought to at least be overridable for any structure where the syntax doesn't match the semantics. For example, consider the function inc that increments an "int" in-place. template<typename F, typename T> T applyToAll( F f, T t ); struct X { int a; //Invariant: a is always 0; }; struct Y { //... Some member variable has an X in its internal structure. }; Y y0 (...); Y y1 = applyToAll( inc, y ); //y1 invariant broke! I'd argue that fixing this problem should _not_ involve changing X's introspect function and thus mixing syntax and semantics.
Fusion's adapters, which are purely syntactic, are perfect for what I've needed so far. Syntactically speaking, are you adding anything besides data inheritance?
I'm not familiar with that distinction wrt reflection.
Hope the above helps. David -- David Sankel Sankel Software www.sankelsoftware.com 585 617 4748 (Office)

Zitat von David Sankel <camior@gmail.com>:
The serialization library describes its feature as a function that converts a supported object to a sequence of bytes and back into an "equivalent structure". The meaning of "equivalent structure" unfortunately isn't defined in the documentation.
Consider the following example:
struct Z { A * a; , B b; };
If we consider syntactic equivalence, the result of serializing this would be something like:
Z{ a=0x029348af, b=B{...} }
If we use semantic equivalence, serialization could result in any number of things, including:
Z { a=new A{...}, b=B{...} }
or even:
Z {}
as long as the semantics of a member are limited to this member and don't interact with other members, they can be specified in introspect(), e.g. ( member<Z,A *,&Z::a,shared_pointer>() ) shared_pointer is a typedef that, combined with the defaults, results in an instantiation of semantics<Set,IndirSemantics>: semantics<unique,semantics<mpl::set<polymorphic,shared> > which means that the deserialize function can treat the pointer itself as a unique data member, but the pointee might be shared with another pointer and be of a derived type. the user can add user-defined semantics there and use them in algorithm overrides. this obviously is only useful when the semantics of one members doesn't depend on another member (e.g. introspect() of std::vector would be such a case). or, similar to one of your examples: class optional{ bool is_initialized; T t; }; when "is_initialized"==false, "t" doesn't matter for "equivalence". although it would be possible to define a new descriptor like "member<...>()" that covers that case, I think a class like that should not have an introspect() function but override the algorithms instead.
I argue that a traversable concept should not mix syntax and assumed semantics, but should instead be strictly syntactic. The algorithms that are included in the library should mainly work on this level.
that's good in theory, but how would the user specifiy semantics separate from introspect()? especially with common semantics like object tracking. if he'd have to override the algorithms every time a pointer shall point to a unique or shared object (whatever the non-default is), there'd have to be an override for almost every type containing a pointer.
is I don't know whether the reflections are semantic or syntactic. I do think a clear separation of syntax and semantics is important to writing clear code. Perhaps you can show what a shared_ptr "implementation of the concept" looks like?
it depends on the shared_ptr implementation if you'd actually implement introspect() for shared_ptr, but I guess you mean supporting shared_ptr in a specific algorithm. apply_recursive, which seems to be comparable to scrap++:
All the algorithms included in the library that work on a semantic level (like equality, serialization, etc.), might have a default implementation that works only on the syntax (although this is scary), but ought to at least be overridable for any structure where the syntax doesn't match the semantics. For example, consider the function inc that increments an "int" in-place.
template<typename F, typename T> T applyToAll( F f, T t );
struct X { int a; //Invariant: a is always 0; };
if "a" is defined as immutable ("const") it is ignored by a "apply_members" functor that only takes non-const references. in general however, a class that has a member that doesn't behave like an independent, mutable, data member should not implement introspect(). so when an algorithm is used on that type a compiler error is generated until an override for that type and that algorithm is implemented.

On Wed, Jun 30, 2010 at 5:28 AM, Stefan Strasser <strasser@uni-bremen.de>wrote:
Zitat von David Sankel <camior@gmail.com>:
The serialization library describes its feature as a function that
converts a supported object to a sequence of bytes and back into an "equivalent structure". The meaning of "equivalent structure" unfortunately isn't defined in the documentation.
Consider the following example:
struct Z { A * a; , B b; };
If we consider syntactic equivalence, the result of serializing this would be something like:
Z{ a=0x029348af, b=B{...} }
If we use semantic equivalence, serialization could result in any number of things, including:
Z { a=new A{...}, b=B{...} }
or even:
Z {}
as long as the semantics of a member are limited to this member and don't interact with other members, they can be specified in introspect(), e.g. ...
Thanks for explaining how introspect works. This confirmed my suspicion that syntax and semantics were mixed together.
class optional{ bool is_initialized; T t; };
when "is_initialized"==false, "t" doesn't matter for "equivalence". although it would be possible to define a new descriptor like "member<...>()" that covers that case, I think a class like that should not have an introspect() function but override the algorithms instead.
This is a good example of where tying the syntax to the semantics really hurts. If I want to syntactically print an instance of optional above, I wouldn't be able to do so because creating an introspect function would screw up another algorithm. How many semantic tags do you need to cover the semantics required for all algorithms that you've come up with and all the algorithms that others haven't come up with yet? I argue that a traversable concept should not mix syntax and assumed
semantics, but should instead be strictly syntactic. The algorithms that are included in the library should mainly work on this level.
that's good in theory, but how would the user specifiy semantics separate from introspect()? especially with common semantics like object tracking.
That's an awesome question and the right one to be asking! -David -- David Sankel Sankel Software www.sankelsoftware.com 585 617 4748 (Office)

Zitat von David Sankel <camior@gmail.com>:
class optional{ bool is_initialized; T t; };
This is a good example of where tying the syntax to the semantics really hurts. If I want to syntactically print an instance of optional above, I wouldn't be able to do so because creating an introspect function would screw up another algorithm.
you'd have to override the print algorithm, yes. but seperating member definitions from semantics alone would not solve this case. the semantics of the optional class members above can not be expressed with semantics tags the ultimate way to express semantics is to override an algorithm, just like the "equivalence" semantics are expressed through a serialize() function in Boost.Serialization. so in a sense semantics _are_ separate from the definitions, if you need them to.
How many semantic tags do you need to cover the semantics required for all algorithms that you've come up
2
with and all the algorithms that others haven't come up with yet?
...
I argue that a traversable concept should not mix syntax and assumed
semantics, but should instead be strictly syntactic. The algorithms that are included in the library should mainly work on this level.
that's good in theory, but how would the user specifiy semantics separate from introspect()? especially with common semantics like object tracking.
That's an awesome question and the right one to be asking!
when data has to be associated with members, tags can be used. the same thing could be used for semantics: template<class Mapping> friend void introspect(Mapping mapping,type<A>){ mapping (base_class<A_base>() ) (member<A,int ,&A::m_a>() , a_tag()) (member<A,float ,&A::m_b>() , b_tag()) (member<A,std::vector<int>,&A::m_vec>(), vec_tag()) (); } another point in code: (A::a_tag() , "xml_tag_name_for_a") (A::b_tag() , "xml_tag_name_for_b") ... the same could be used for semantics. however, I think it is too verbose to be used as the default. it could be used to specify additional semantics at most, when you need to specify semantics on existing types for new algorithms.

David Sankel wrote:
The serialization library describes its feature as a function that converts a supported object to a sequence of bytes and back into an "equivalent structure". The meaning of "equivalent structure" unfortunately isn't defined in the documentation.
Consider the following example:
struct Z { A * a; , B b; };
If we consider syntactic equivalence, the result of serializing this would be something like:
Z{ a=0x029348af, b=B{...} }
If we use semantic equivalence, serialization could result in any number of things, including:
Z { a=new A{...}, b=B{...} }
or even:
Z {}
It depends on what the meaning of a Z is.
The serialization library implements the latter. It never occurred to me to formally define "equivalent structure" as only the latter definition is useful for the purposes for which the library is intended - persistence and marshalling. If anyone want's to suggest an enhancement to the documentation I'll consider it. Robert Ramey

Zitat von Robert Ramey <ramey@rrsd.com>:
David Sankel wrote:
The serialization library describes its feature as a function that converts a supported object to a sequence of bytes and back into an "equivalent structure". The meaning of "equivalent structure" unfortunately isn't defined in the documentation.
Consider the following example:
struct Z { A * a; , B b; };
If we consider syntactic equivalence, the result of serializing this would be something like:
Z{ a=0x029348af, b=B{...} }
If we use semantic equivalence, serialization could result in any number of things, including:
Z { a=new A{...}, b=B{...} }
or even:
Z {}
It depends on what the meaning of a Z is.
The serialization library implements the latter. It never occurred to me to formally define "equivalent structure" as only the latter definition is useful for the purposes for which the library is intended - persistence and marshalling. If anyone want's to suggest an enhancement to the documentation I'll consider it.
I don't see a problem wrt this in Boost.Serialization, implementation- or documentation-wise. the semantics of "equivalence" is defined by the individual serialize() functions in Boost.Serialization. this issue only arises in Intro and possibly in Mirror because we try to deduce the serialization semantics from the member definitions (and some semantics tags), so that serialize() and other algorithm implementations don't have to be overridden for each type. I do think however, with the caveat that I don't know how exactly it was reached, that it wasn't a good design decision to deduce the semantics of a member from its type. especially with object tracking, where the semantics of a pointer is deduced from the pointee type.

Stefan Strasser wrote:
I do think however, with the caveat that I don't know how exactly it was reached,
Truth is it was wasn't really decided. It was arrived at as the most expedient solution as to how to vary the serialization. The only alternative proposed was to require that each invocation specify the the attributes of the serialization- tracking. etc. I could see the merit in this - but would lead to a very unwieldly and user unfriendly system.
that it wasn't a good design decision to deduce the semantics of a member from its type. especially with object tracking, where the semantics of a pointer is deduced from the pointee type.
maybe - but no one proposed much alternative at a time. In practice it's worked out pretty well. Most of the traits can be overridden for special cases using a "strong typedef" wrapper class and this has turned out to be necessary only in the most rare of cases. Tracking is the one trait which can't be overridden in this manner and I've considered ways to address that. But it hasn't been high priority because no one has ever asked for it. I would guess that this is the case because those that might have a need for it prefer to write thier own alternative from scratch. Robert Ramey
participants (8)
-
David Sankel
-
Edward Diener
-
Larry Evans
-
Matus Chochlik
-
Robert Ramey
-
Sohail Somani
-
Stefan Strasser
-
Thomas Klimpel