[type_erasure] Review started (July 18-27, 2012)

Hello all, *** The review of Steven Watanabe's proposed Boost.TypeErasure library begins on July 18, 2012 and ends on July 27, 2012. *** THE LIBRARY C++ provides runtime polymorphism through virtual functions. They are a very useful feature, but they do have some limitations. * They are intrusive. In generic programming, we can design an interface which allows third-party types to be adapted to it. * They require dynamic memory management. Of course, most of the problems can be avoided by using an appropriate smart pointer type. Even so, it still acts like a pointer rather than a value. * Virtual functions' ability to apply multiple independent concepts to a single object is limited. The Boost.TypeErasure library solves these problems allowing us to mirror static generic programming at runtime. Library source: http://svn.boost.org/svn/boost/sandbox/type_erasure/ Pre-built documentation: http://steven_watanabe.users.sourceforge.net/type_erasure/libs/type_erasure/ You can also download archives with pre-built documentation from: http://sourceforge.net/projects/steven-watanabe.u/files/ YOUR REVIEW Please submit a review to the mailing-list by replying to this email ("[boost] [type_erasure] Review ..." should be in the subject). Please state clearly whether you think this library should be accepted as a Boost library. Other questions you may want to consider: 1. What is your evaluation of the design? 2. What is your evaluation of the implementation? 3. What is your evaluation of the documentation? 4. What is your evaluation of the potential usefulness of the library? 5. Did you try to use the library? With what compiler? Did you have any problems? 6. How much effort did you put into your evaluation? A glance? A quick reading? In-depth study? 7. Are you knowledgeable about the problem domain? Thanks in advance to all who participate in the review discussion -- I'm looking forward to it! The review manager. --Lorenzo

Hello all, On Wed, Jul 18, 2012 at 1:13 AM, Lorenzo Caminiti <lorcaminiti@gmail.com> wrote:
*** The review of Steven Watanabe's proposed Boost.TypeErasure library begins on July 18, 2012 and ends on July 27, 2012. ***
*** Boost.TypeErasure review ends in 5 days. Please submit your reviews :D *** There have been interesting discussions on the library on the ML but I have not received any official review yet :( Especially if you are a user of Boost Any, Function, and Any Iterator, you definitely want to take a look at Type Erasure as it generalizes solutions provided by those other libraries. Thank you. The review manager. --Lorenzo
THE LIBRARY
C++ provides runtime polymorphism through virtual functions. They are a very useful feature, but they do have some limitations. * They are intrusive. In generic programming, we can design an interface which allows third-party types to be adapted to it. * They require dynamic memory management. Of course, most of the problems can be avoided by using an appropriate smart pointer type. Even so, it still acts like a pointer rather than a value. * Virtual functions' ability to apply multiple independent concepts to a single object is limited. The Boost.TypeErasure library solves these problems allowing us to mirror static generic programming at runtime.
Library source: http://svn.boost.org/svn/boost/sandbox/type_erasure/
Pre-built documentation: http://steven_watanabe.users.sourceforge.net/type_erasure/libs/type_erasure/
You can also download archives with pre-built documentation from: http://sourceforge.net/projects/steven-watanabe.u/files/
YOUR REVIEW
Please submit a review to the mailing-list by replying to this email ("[boost] [type_erasure] Review ..." should be in the subject).
Please state clearly whether you think this library should be accepted as a Boost library.
Other questions you may want to consider: 1. What is your evaluation of the design? 2. What is your evaluation of the implementation? 3. What is your evaluation of the documentation? 4. What is your evaluation of the potential usefulness of the library? 5. Did you try to use the library? With what compiler? Did you have any problems? 6. How much effort did you put into your evaluation? A glance? A quick reading? In-depth study? 7. Are you knowledgeable about the problem domain?
Thanks in advance to all who participate in the review discussion -- I'm looking forward to it!

Lorenzo wrote:
Hello all, *** Boost.TypeErasure review ends in 5 days. Please submit your reviews :D
Library source: http://svn.boost.org/svn/boost/sandbox/type_erasure/
Just in case any other Boost SVN neophytes like me trying to get hold of this - I'm finding that http anonymous access is not available, however https access does work. The URL is otherwise identical.

This is my review of the type erasure library following a relatively brief look at it. 0) I vote to accept the library into Boost 1) Evaluation of design I like it. The central class, any< mpl::vector< .. > >, is exactly what I'd expect. It is a mild shame that the notation for references is the old and familiar boost::ref() style as used in several other Boost libraries, but accept this is not technically possible. 2) Evaluation of implementation. Not evaluated. 3) Evaluation of documentation. As others have said, further examples will lower the learning curve. Although the references section looks forbidding at first, my gut feel is that it will be like other Boost libraries - the reference section only "comes alive" when you know the library quite well and you just need to remind yourself what is (and what isn't) available. 4) Usefulness I think this enormous potential and would be very likely to use it my own code. I've several times found that it would be nice to generalize boost::function or adobe::any_iterator but never had the wherewithal to do it well. For example a library I've been involved in had its own notion of a "multi_function" like a boost::function<> but all instances had to support multiple function signatures, This turned out to be almost trivial with Boost.TypeErasure. A complete program listing follows: #include <boost/type_erasure/any.hpp> #include <boost/type_erasure/callable.hpp> #include <boost/mpl/vector.hpp> #include <iostream> namespace mpl = boost::mpl; using namespace boost::type_erasure; typedef mpl::vector< copy_constructible<> , typeid_<> , callable< double(double) , const _self > , callable< double(double,double) ,const _self >
multi_function_concepts;
5. Did you try to use the library? With what compiler? Did you have any
typedef any< multi_function_concepts , _self > non_ref_multi_function; typedef any< multi_function_concepts , _self& > ref_multi_function; template<typename RefInfo> double test_multi_function( any< multi_function_concepts , RefInfo > const& mf ) { return mf(3.0) + mf(1.0,2.0); } struct model_of_multi_function { typedef double state_t; state_t m_state; explicit model_of_multi_function(state_t state) : m_state(state) { } double operator()(double x) const { return m_state + x; } double operator()(double x, double y) const { return m_state*x*y; } }; int main() { model_of_multi_function m(5.0); non_ref_multi_function x( m ); ref_multi_function y(m); std::cout << "Result1 was : " << test_multi_function(x) << "\n"; std::cout << "Result2 was : " << test_multi_function(y) << "\n"; m.m_state = 6.0; //should not change state of x, should change state of y std::cout << "Result3 was : " << test_multi_function(x) << "\n"; std::cout << "Result4 was : " << test_multi_function(y) << "\n"; } problems? Yes I tried to used the library to prove "multi_function" worked as above. Compiled and ran only with VC10 - no problems to report.
6. How much effort did you put into your evaluation? A glance? A quick reading? In-depth study?
About 3 hours in total: playing with library, reading docs, reading test code.
7. Are you knowledgeable about the problem domain?
Only in so much that I'm one of many to think "what is the true generalization of Boost.Function??". From my look this evening, Boost.TypeErasure appears to be it. One final point on the name. Paul Bristow wrote:
PS I can't come up with a better name than TypeErasure but it is certainly a bit forbidding :-)
I'd like to respectively disagree. My intuition notion of what the library might be about having only heard the name turned out to match exactly with what it can really do. That suggests it is a good name! -----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Lorenzo Caminiti Sent: 23 July 2012 2:10 PM To: boost@lists.boost.org; boost-announce@lists.boost.org; boost-users@lists.boost.org Subject: Re: [boost] [type_erasure] Review started (July 18-27, 2012) Hello all, On Wed, Jul 18, 2012 at 1:13 AM, Lorenzo Caminiti <lorcaminiti@gmail.com> wrote:
*** The review of Steven Watanabe's proposed Boost.TypeErasure library begins on July 18, 2012 and ends on July 27, 2012. ***
*** Boost.TypeErasure review ends in 5 days. Please submit your reviews :D *** There have been interesting discussions on the library on the ML but I have not received any official review yet :( Especially if you are a user of Boost Any, Function, and Any Iterator, you definitely want to take a look at Type Erasure as it generalizes solutions provided by those other libraries. Thank you. The review manager. --Lorenzo
THE LIBRARY
C++ provides runtime polymorphism through virtual functions. They are a very useful feature, but they do have some limitations. * They are intrusive. In generic programming, we can design an interface which allows third-party types to be adapted to it. * They require dynamic memory management. Of course, most of the problems can be avoided by using an appropriate smart pointer type. Even so, it still acts like a pointer rather than a value. * Virtual functions' ability to apply multiple independent concepts to a single object is limited. The Boost.TypeErasure library solves these problems allowing us to mirror static generic programming at runtime.
Library source: http://svn.boost.org/svn/boost/sandbox/type_erasure/
Pre-built documentation:
http://steven_watanabe.users.sourceforge.net/type_erasure/libs/type_erasure/
You can also download archives with pre-built documentation from: http://sourceforge.net/projects/steven-watanabe.u/files/
YOUR REVIEW
Please submit a review to the mailing-list by replying to this email ("[boost] [type_erasure] Review ..." should be in the subject).
Please state clearly whether you think this library should be accepted as a Boost library.
Other questions you may want to consider: 1. What is your evaluation of the design? 2. What is your evaluation of the implementation? 3. What is your evaluation of the documentation? 4. What is your evaluation of the potential usefulness of the library? 5. Did you try to use the library? With what compiler? Did you have any problems? 6. How much effort did you put into your evaluation? A glance? A quick reading? In-depth study? 7. Are you knowledgeable about the problem domain?
Thanks in advance to all who participate in the review discussion -- I'm looking forward to it!
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

On Wednesday 18 July 2012 01:13:20 Lorenzo Caminiti wrote:
Hello all,
*** The review of Steven Watanabe's proposed Boost.TypeErasure library begins on July 18, 2012 and ends on July 27, 2012. ***
Hi, This is not yet a complete review but rather some thoughts and questions I came up with as I read the documentation and the source code. I did not follow the discussion so my apologies if these have already been answered. 1. As I can see, the implementation contains a function table to implement concepts, am I right? Why this table is stored per-object and not created statically, for every stored type/concept combination? Considering the number of concepts, this table can take much space in every "any" instance. 2. Is it possible to convert between "any" instances with different concept sets or value types? For instance, are these possible? any< my_concepts, _self& > ref_a; any< my_concepts, _self > a = ref_a; or any< mpl::vector< addable<>, incrementable<>, decrementable<> >
a;
any< mpl::vector< addable<> >
b = a;

AMDG On 07/23/2012 07:35 AM, Andrey Semashev wrote:
On Wednesday 18 July 2012 01:13:20 Lorenzo Caminiti wrote:
Hello all,
*** The review of Steven Watanabe's proposed Boost.TypeErasure library begins on July 18, 2012 and ends on July 27, 2012. ***
Hi,
This is not yet a complete review but rather some thoughts and questions I came up with as I read the documentation and the source code. I did not follow the discussion so my apologies if these have already been answered.
1. As I can see, the implementation contains a function table to implement concepts, am I right? Why this table is stored per-object and not created statically, for every stored type/concept combination? Considering the number of concepts, this table can take much space in every "any" instance.
The table is created statically. The table is only constructed dynamically for conversions. In Christ, Steven Watanabe

On 07/23/12 11:43, Steven Watanabe wrote:
AMDG
On 07/23/2012 07:35 AM, Andrey Semashev wrote:
On Wednesday 18 July 2012 01:13:20 Lorenzo Caminiti wrote:
Hello all,
*** The review of Steven Watanabe's proposed Boost.TypeErasure library begins on July 18, 2012 and ends on July 27, 2012. ***
Hi,
This is not yet a complete review but rather some thoughts and questions I came up with as I read the documentation and the source code. I did not follow the discussion so my apologies if these have already been answered.
1. As I can see, the implementation contains a function table to implement concepts, am I right? Why this table is stored per-object and not created statically, for every stored type/concept combination? Considering the number of concepts, this table can take much space in every "any" instance.
The table is created statically. The table is only constructed dynamically for conversions.
The output of the attached shows: ./table_size.exe sizeof(void*)=8 _a,_b:table size=24 _a,_b,_c,_d:table size=24 indicating that the table size is not dependent on the number of concepts. However, it does show the table size is considerably larger than the size of a virtual function table pointer (which I assume is the same size as void*). HTH. -Larry

2. Is it possible to convert between "any" instances with different concept sets or value types? For instance, are these possible? any< mpl::vector< addable<>, incrementable<>, decrementable<> >
a;
any< mpl::vector< addable<> >
b = a;
I find this an interesting question. I use the review to change a private but real project and replace all of my OO-style interfaces by TypeErasure. I got the following to compile and work (you need copy_constructible): typedef any< boost::mpl::vector< boost::type_erasure::typeid_<>, boost::type_erasure::copy_constructible<>, boost::type_erasure::addable<>, boost::type_erasure::incrementable<>, boost::type_erasure::decrementable<> > > AnyA; typedef any< boost::mpl::vector< boost::type_erasure::copy_constructible<>, boost::type_erasure::typeid_<>, boost::type_erasure::addable<> > > AnyB; AnyA a(10); AnyB b = a; OTOH, this does not compile: AnyB b2(10); AnyA a2=b2; Which brings me to the following use case. Supposing I have 3 classes IA <- IB <- IC and 1 object of each held as shared_ptr, I can dynamic_pointer_cast from one to another. But if I have 3 any's of concepts A,B,C, B containing A's concepts + some more, and C containing B's concepts + some more, I can "upcast" as shown above from C to B and B to A, but not the other way around, right? Even if I have an object fulfilling A,B and C. Thanks, Christophe PS: so far I got only one warning: check_match does not use its parameter op.

AMDG On 07/24/2012 10:39 AM, Christophe Henry wrote:
<snip>
I find this an interesting question. I use the review to change a private but real project and replace all of my OO-style interfaces by TypeErasure. I got the following to compile and work (you need copy_constructible):
typedef any< boost::mpl::vector< boost::type_erasure::typeid_<>, boost::type_erasure::copy_constructible<>, boost::type_erasure::addable<>, boost::type_erasure::incrementable<>, boost::type_erasure::decrementable<> > > AnyA;
typedef any< boost::mpl::vector< boost::type_erasure::copy_constructible<>, boost::type_erasure::typeid_<>, boost::type_erasure::addable<> > > AnyB;
AnyA a(10); AnyB b = a;
OTOH, this does not compile: AnyB b2(10); AnyA a2=b2;
Right.
Which brings me to the following use case. Supposing I have 3 classes IA <- IB <- IC and 1 object of each held as shared_ptr, I can dynamic_pointer_cast from one to another. But if I have 3 any's of concepts A,B,C, B containing A's concepts + some more, and C containing B's concepts + some more, I can "upcast" as shown above from C to B and B to A, but not the other way around, right? Even if I have an object fulfilling A,B and C.
That's correct. As I replied to Fabio in another thread, this can be implemented, but requires some kind of reflection. If enough people need it, I can make it a priority.
PS: so far I got only one warning: check_match does not use its parameter op.
Fixed. In Christ, Steven Watanabe

About conversion between different "any" types, I've found a note in the docs that says it's possible. Disregard the question in my previous email. Some more thoughts: 1. In the docs, "Functions with Multiple Arguments", it is said that the underlying types of the two "any" arguments must match or the behavior is undefined. I assume this is due to the fact that the type_id_ concept is optional and the implementation doesn't really know the underlying types of the "any" values and simply assumes they match. I think this is a too dagerous assumption and some basic check must always be performed. Probably, the underlying type information must always be stored in one way or another and verified when needed. Maybe, the type_id_ concept must be mandatory even. 2. In the same section, I didn't really understand the second code snippet. Why is the tuple needed? Why not this: any<requirements, _a> a = &array[0]; a = a + 2; Also, from the implementation perspective, will it simply add 2 to the pointer or construct an "any" from 2 and then perform the addition? 3. Are there plans to make the underlying value storage customizable? It would be great if "any" supported small object optimization or custom memory allocators. 4. How can I test if an "any" instance is null? I assume that I cannot compare the instance with a default-constructed "any" because this would result in attempt to compare stored values. 5. The "Why do I have to specify the presence of a destructor explicitly?" rationale doesn't seem like a good enough reason. The code already has a specialization for references, and it is natural that references are stored as a pointer to the external value (I really hope there are no dynamic memory allocations performed in this case). So for references, there is no need to call a destructor of the object at all. On the other hand, I can't imagine an "any" with an object by value which cannot be destructed.

Hi Guys, Let me add to the list of questions, below (I don't want to start a N-th thread, if this is inappropriate please let me know): Julien On Tue, Jul 24, 2012 at 12:31 AM, Andrey Semashev <andrey.semashev@gmail.com> wrote:
About conversion between different "any" types, I've found a note in the docs that says it's possible. Disregard the question in my previous email.
Some more thoughts:
1. In the docs, "Functions with Multiple Arguments", it is said that the underlying types of the two "any" arguments must match or the behavior is undefined. I assume this is due to the fact that the type_id_ concept is optional and the implementation doesn't really know the underlying types of the "any" values and simply assumes they match. I think this is a too dagerous assumption and some basic check must always be performed. Probably, the underlying type information must always be stored in one way or another and verified when needed. Maybe, the type_id_ concept must be mandatory even.
2. In the same section, I didn't really understand the second code snippet. Why is the tuple needed? Why not this:
any<requirements, _a> a = &array[0]; a = a + 2;
Also, from the implementation perspective, will it simply add 2 to the pointer or construct an "any" from 2 and then perform the addition?
My 2cts: I think this is because the return type depends on the 2nd operand in the most general case.
3. Are there plans to make the underlying value storage customizable? It would be great if "any" supported small object optimization or custom memory allocators.
4. How can I test if an "any" instance is null? I assume that I cannot compare the instance with a default-constructed "any" because this would result in attempt to compare stored values.
5. The "Why do I have to specify the presence of a destructor explicitly?" rationale doesn't seem like a good enough reason. The code already has a specialization for references, and it is natural that references are stored as a pointer to the external value (I really hope there are no dynamic memory allocations performed in this case). So for references, there is no need to call a destructor of the object at all. On the other hand, I can't imagine an "any" with an object by value which cannot be destructed.
6. I think it's not possible to define a basic concept with two function at once. This should be mentioned somewhere that one "basic" concept is equivalent to one operation. Actually, the whole idea of "basic" concept and composed concept could deserve much more details in a theoretical section, separate from the practice. This is important because people will try mapping the techniques they know (i.e. abstract class / interfaces) to what is in TypeErasure and there is a big difference : for concept-based polymorphism you don't start by the parent concept, you start by it's functions and then you compose them back.

On Tuesday 24 July 2012 00:56:07 Julien Nitard wrote:
2. In the same section, I didn't really understand the second code snippet.
Why is the tuple needed? Why not this: any<requirements, _a> a = &array[0]; a = a + 2;
Also, from the implementation perspective, will it simply add 2 to the pointer or construct an "any" from 2 and then perform the addition?
My 2cts: I think this is because the return type depends on the 2nd operand in the most general case.
Well, If my understanding is correct, according to the "requirements" typedef, the result should be an "any" containing an object of the same type as the left argument (which is the pointer in this case). If there is a way to describe the operator when the result type differs from the both argument types, the mentioned example doesn't show that. Actually, is this possible with the library?

AMDG On 07/23/2012 09:07 AM, Andrey Semashev wrote:
If there is a way to describe the operator when the result type differs from the both argument types, the mentioned example doesn't show that. Actually, is this possible with the library?
Yes. The return type can be any placeholder or a specific type. addable<_a, _a, double> // a + a -> double addable<_a, _b, _c> // a + b -> c However, it adds extra complexity because all the placeholders still need to be bound. In Christ, Steven Watanabe

AMDG On 07/23/2012 08:31 AM, Andrey Semashev wrote:
About conversion between different "any" types, I've found a note in the docs that says it's possible. Disregard the question in my previous email.
Some more thoughts:
1. In the docs, "Functions with Multiple Arguments", it is said that the underlying types of the two "any" arguments must match or the behavior is undefined. I assume this is due to the fact that the type_id_ concept is optional and the implementation doesn't really know the underlying types of the "any" values and simply assumes they match. I think this is a too dagerous assumption and some basic check must always be performed.
I thought I had an assertion, but it looks like I missed that.
Probably, the underlying type information must always be stored in one way or another and verified when needed. Maybe, the type_id_ concept must be mandatory even.
relaxed_match causes an exception to be thrown.
2. In the same section, I didn't really understand the second code snippet. Why is the tuple needed? Why not this:
any<requirements, _a> a = &array[0]; a = a + 2;
Also, from the implementation perspective, will it simply add 2 to the pointer or construct an "any" from 2 and then perform the addition?
This doesn't work. There are two placeholders, _a and _b. When we create an any, we have to know the types that all the placeholders bind to.
3. Are there plans to make the underlying value storage customizable? It would be great if "any" supported small object optimization or custom memory allocators.
Yes. I intentionally avoided trying to optimize the library
4. How can I test if an "any" instance is null? I assume that I cannot compare the instance with a default-constructed "any" because this would result in attempt to compare stored values.
typeid_of(a) == typeid(void)
5. The "Why do I have to specify the presence of a destructor explicitly?" rationale doesn't seem like a good enough reason. The code already has a specialization for references, and it is natural that references are stored as a pointer to the external value (I really hope there are no dynamic memory allocations performed in this case).
There is no memory allocation.
So for references, there is no need to call a destructor of the object at all. On the other hand, I can't imagine an "any" with an object by value which cannot be destructed.
The point is interface consistency. The requirements on the contained type can be determined solely from the Concept. If the destructor were implicitly added in some cases but not others, it would cause a lot of problems. In Christ, Steven Watanabe

Also, one more question. I didn't find any visitation support in the library. Is there any or is it planned? This was one feature I missed in Boost.Any.

AMDG On 07/23/2012 08:44 AM, Andrey Semashev wrote:
Also, one more question. I didn't find any visitation support in the library. Is there any or is it planned?
No.
This was one feature I missed in Boost.Any.
What exactly is visitation supposed to do? It's impossible to create a Boost.Variant style apply_visitor for Boost.Any. In Christ, Steven Watanabe

On Monday 23 July 2012 09:26:21 Steven Watanabe wrote:
AMDG
On 07/23/2012 08:44 AM, Andrey Semashev wrote:
Also, one more question. I didn't find any visitation support in the library. Is there any or is it planned?
No.
This was one feature I missed in Boost.Any.
What exactly is visitation supposed to do? It's impossible to create a Boost.Variant style apply_visitor for Boost.Any.
No, not exactly like in Boost.Variant because "any" doesn't communicate the list of possible types of the stored value. Instead, the visitor can manifest the types it's ready to process. This kind of visitation I had to implement in my library. I think, this would be a useful feature.

AMDG On 07/23/2012 09:37 AM, Andrey Semashev wrote:
On Monday 23 July 2012 09:26:21 Steven Watanabe wrote:
On 07/23/2012 08:44 AM, Andrey Semashev wrote:
Also, one more question. I didn't find any visitation support in the library. Is there any or is it planned?
No.
This was one feature I missed in Boost.Any.
What exactly is visitation supposed to do? It's impossible to create a Boost.Variant style apply_visitor for Boost.Any.
No, not exactly like in Boost.Variant because "any" doesn't communicate the list of possible types of the stored value. Instead, the visitor can manifest the types it's ready to process. This kind of visitation I had to implement in my library. I think, this would be a useful feature.
I'm not going to implement this in my library. It can be implemented without loss of generality or performance using the interfaces provided by the library. It also doesn't require any particularly advanced knowledge of the library. In Christ, Steven Watanabe

-----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Lorenzo Caminiti Sent: Wednesday, July 18, 2012 6:13 AM To: boost@lists.boost.org; boost-announce@lists.boost.org; boost-users@lists.boost.org Subject: [boost] [type_erasure] Review started (July 18-27, 2012)
*** The review of Steven Watanabe's proposed Boost.TypeErasure library begins on July 18, 2012 and ends on July 27, 2012. ***
THE LIBRARY
C++ provides runtime polymorphism through virtual functions. They are ...
I've started to read the documentation (and have a possibly-related headache ;-) My impression is that I need this library, so I downloaded and started to try the examples (so I could fumble to confirm my own understanding) but immediately hit compile fails with VS 2010. Am I right in concluding that I need a C++11 compliant compiler ? Paul --- Paul A. Bristow, Prizet Farmhouse, Kendal LA8 8AB UK +44 1539 561830 07714330204 pbristow@hetp.u-net.com

AMDG On 07/24/2012 07:02 AM, Paul A. Bristow wrote:
I've started to read the documentation (and have a possibly-related headache ;-)
My impression is that I need this library, so I downloaded and started to try the examples (so I could fumble to confirm my own understanding) but immediately hit compile fails with VS 2010.
My primary test compiler is VS 2010, so it ought to work. What are the errors that you're getting?
Am I right in concluding that I need a C++11 compliant compiler ?
You shouldn't. In Christ, Steven Watanabe

-----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Steven Watanabe Sent: Tuesday, July 24, 2012 3:12 PM To: boost@lists.boost.org Subject: Re: [boost] [type_erasure] Review started (July 18-27, 2012)
AMDG
On 07/24/2012 07:02 AM, Paul A. Bristow wrote:
I've started to read the documentation (and have a possibly-related headache ;-)
My impression is that I need this library, so I downloaded and started to try the examples (so I could fumble to confirm my own understanding) but immediately hit
compile fails with VS 2010.
My primary test compiler is VS 2010, so it ought to work. What are the errors that you're getting?
Am I right in concluding that I need a C++11 compliant compiler ?
Well that's good. I have got the code using SVN from boost-sandbox (this looks a day out of date compared to Sourceforge version but ...) am compiling with these #includes directories I:\boost-sandbox/type_erasure;I:\boost-trunk; and am using libs/example/intro.cpp The first glitch is I:\boost-sandbox\type_erasure\libs\type_erasure\example\intro.cpp(35): error C2039: 'vector' : is not a member of 'std' so I added #include<vector> then I get intro.cpp I:\boost-sandbox\type_erasure\libs\type_erasure\example\intro.cpp(54): error C2332: 'class' : missing tag name I:\boost-sandbox\type_erasure\libs\type_erasure\example\intro.cpp(54): error C2993: '' : illegal type for non-type template parameter '<unnamed-tag>' I:\boost-sandbox\type_erasure\libs\type_erasure\example\intro.cpp(54): error C2143: syntax error : missing ',' before '...' I:\boost-sandbox\type_erasure\libs\type_erasure\example\intro.cpp(55): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int I:\boost-sandbox\type_erasure\libs\type_erasure\example\intro.cpp(55): error C2143: syntax error : missing ',' before '&' What am I missing? Paul

AMDG On 07/24/2012 09:02 AM, Paul A. Bristow wrote:
Well that's good.
I have got the code using SVN from boost-sandbox (this looks a day out of date compared to Sourceforge version but ...)
The Sourceforge version is fixed as of the start of the review. I'm continuing to update svn.
am compiling with these #includes directories
I:\boost-sandbox/type_erasure;I:\boost-trunk;
and am using libs/example/intro.cpp
The first glitch is
I:\boost-sandbox\type_erasure\libs\type_erasure\example\intro.cpp(35): error C2039: 'vector' : is not a member of 'std'
so I added
#include<vector>
Fixed.
then I get
intro.cpp I:\boost-sandbox\type_erasure\libs\type_erasure\example\intro.cpp(54): error C2332: 'class' : missing tag name I:\boost-sandbox\type_erasure\libs\type_erasure\example\intro.cpp(54): error C2993: '' : illegal type for non-type template parameter '<unnamed-tag>' I:\boost-sandbox\type_erasure\libs\type_erasure\example\intro.cpp(54): error C2143: syntax error : missing ',' before '...' I:\boost-sandbox\type_erasure\libs\type_erasure\example\intro.cpp(55): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int I:\boost-sandbox\type_erasure\libs\type_erasure\example\intro.cpp(55): error C2143: syntax error : missing ',' before '&'
What am I missing?
Oh. I just added this example yesterday. You're correct. It requires C++11. The other examples should work fine. In Christ, Steven Watanabe

-----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Steven Watanabe Sent: Tuesday, July 24, 2012 6:21 PM To: boost@lists.boost.org Subject: Re: [boost] [type_erasure] Review started (July 18-27, 2012)
AMDG
Oh. I just added this example yesterday. You're correct. It requires C++11.
Murphy's Law triumphs again!
The other examples should work fine.
OK - I can compile and run print_sequence example. Neat. Thanks Paul

On 07/18/12 00:13, Lorenzo Caminiti wrote:
Hello all,
*** The review of Steven Watanabe's proposed Boost.TypeErasure library begins on July 18, 2012 and ends on July 27, 2012. ***
The attached produces runtime error: ./rebind_any.exe creating b_self any(const U&) creating b_copy terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::type_erasure::bad_any_cast>
' what(): std::bad_cast make: *** [run] Aborted
Yet, the type returned by rebind_any was used in the any_cast; hence, it must be a good cast, AFAICT. What am I doing wrong? TIA. -Larry

On 07/25/12 10:57, Larry Evans wrote:
On 07/18/12 00:13, Lorenzo Caminiti wrote:
Hello all,
*** The review of Steven Watanabe's proposed Boost.TypeErasure library begins on July 18, 2012 and ends on July 27, 2012. ***
The attached produces runtime error:
./rebind_any.exe creating b_self any(const U&) creating b_copy terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::type_erasure::bad_any_cast>
' what(): std::bad_cast make: *** [run] Aborted
Yet, the type returned by rebind_any was used in the any_cast; hence, it must be a good cast, AFAICT.
What am I doing wrong?
OOPS. I should have realized that the AnyB arg to rebind_any has no information about what's the actual type stored in the b_self because AnyB was defined before b_self was created. Sorry for noise. However, the doc: http://steven_watanabe.users.sourceforge.net/type_erasure/libs/type_erasure/... was a bit confusing since it says: A metafunction that returns any type corresponding to a placeholder. If T is not a placeholder, returns T unchanged. Since the T was _self, which is a placeholder, I assumed it would return the type corresponding to _self; however, since that's unknown until the actual Any is created, it must return something else. I actually demangled the type returned and it was: b_type=boost::type_erasure::any<boost::mpl::vector<boost::type_erasure::copy_constructible<boost::type_erasure::_self>, boost::type_erasure::typeid_<boost::type_erasure::_self>, boost::type_erasure::addable<boost::type_erasure::_self, boost::type_erasure::_self, boost::type_erasure::_self>, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na>, boost::type_erasure::_self> which looks like it's just the Any template arg to rebind_any, which leaves me wondering what's the purpose of rebind_any. -regards, Larry

AMDG On 07/25/2012 09:36 AM, Larry Evans wrote:
However, the doc:
http://steven_watanabe.users.sourceforge.net/type_erasure/libs/type_erasure/...
was a bit confusing since it says:
A metafunction that returns any type corresponding to a placeholder.
I've modified this to say "A metafunction that changes the placeholder of an any" Is that clearer? In Christ, Steven Watanabe

On 07/25/12 13:33, Steven Watanabe wrote:
AMDG
On 07/25/2012 09:36 AM, Larry Evans wrote:
However, the doc:
http://steven_watanabe.users.sourceforge.net/type_erasure/libs/type_erasure/...
was a bit confusing since it says:
A metafunction that returns any type corresponding to a placeholder.
I've modified this to say
"A metafunction that changes the placeholder of an any"
Is that clearer?
Sorry, no. How does it change the placeholder. Hmmm, do you mean the return type(i.e. rebind_any<any<C,S>,T>::type) is any<C,T>?

On 07/25/12 14:16, Larry Evans wrote:
On 07/25/12 13:33, Steven Watanabe wrote:
AMDG
On 07/25/2012 09:36 AM, Larry Evans wrote:
However, the doc:
http://steven_watanabe.users.sourceforge.net/type_erasure/libs/type_erasure/...
was a bit confusing since it says:
A metafunction that returns any type corresponding to a placeholder.
I've modified this to say
"A metafunction that changes the placeholder of an any"
Is that clearer?
Sorry, no. How does it change the placeholder. Hmmm, do you mean the return type(i.e. rebind_any<any<C,S>,T>::type) is any<C,T>?
Yes, based on output of the attached: ./rebind_any.exe any_self_t=boost::type_erasure::any<boost::mpl::vector<boost::type_erasure::copy_constructible<boost::type_erasure::_self>, boost::type_erasure::typeid_<boost::type_erasure::_self>, boost::type_erasure::addable<boost::type_erasure::_self, boost::type_erasure::_self, boost::type_erasure::_self>, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na>, boost::type_erasure::_self> rebind_self2a_t=boost::type_erasure::any<boost::mpl::vector<boost::type_erasure::copy_constructible<boost::type_erasure::_self>, boost::type_erasure::typeid_<boost::type_erasure::_self>, boost::type_erasure::addable<boost::type_erasure::_self, boost::type_erasure::_self, boost::type_erasure::_self>, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na, mpl_::na>, boost::type_erasure::_a> However, again, it uses my demangled_type_name function :(

Hi all, here's my review for TypeErasure.
Please state clearly whether you think this library should be accepted as a Boost library.
Let's answer this upfront: YES!
Other questions you may want to consider: 1. What is your evaluation of the design?
Seen from outside, the design is clean and allows powerful constructs.
2. What is your evaluation of the implementation?
I only had a quick glance.
3. What is your evaluation of the documentation?
What is there is very good, but we could do with more documentation. A few suggestions: - provide a small example of usage of each concept to help new users manage faster the learning curve. I had some difficulties getting istreamable working and had to look at the tests to finally get it. - provide more real-looking examples of the sort of the polymorphic range formatter. The more the better. I'll myself provide one later on in this message.
4. What is your evaluation of the potential usefulness of the library?
Huge! And by this I mean a very interesting programming style which should be made available also to average programmers. Which makes the doc even more important.
5. Did you try to use the library? With what compiler? Did you have any problems?
Yes, on a real private project I do on my free time. I used VC9. I'm in vacations so I couldn't try gcc and VC10 yet but I'll do this in the next few weeks. I got no problem besides a single warning about unused variable (fixed). I tried the library for 2 different use cases: - a streamable any (actually I need a boost-serializable any but didn't come to it yet). One always needs something like this. In the past, I had to modify a Boost.Any to achieve this. In my design, this helps implement my low-level saving/loading to/from file easily as the low level layer needs no knowledge about the types it gets, they're just a bunch of serializable things. - improve my MVC (Model-View-Controller) design. Here's my use case: I started with a classical (OO style) interface-based design a graphical editor where the user can place and edit items of different kinds on a drawing area. The interface for these items is something along the lines: struct IItem { ... virtual void createItem(...)=0; // creates in model virtual void deleteItem(...)=0; // deletes from model ... }; Different views dialogs however need some more concrete item types, like: struct IType1 : public IItem { virtual void setName(...)=0; virtual string const& getName() const = 0; ... }; struct ISubType1 : public IType1 { ... }; So far so good but then I want some classes which implement this. Where will I implement these name members? In a class realizing IType1 (thus having to redo it for ISubType1)? Or in a class which I also inherit from, in concrete realizations of IType1 and ISubType1 (thus having either the dreaded diamond or forwarding methods in all concrete classes)? And here I have only one concept (naming) and a single type. Get a few more of each and this will drain the life faster out of a C++ developer than a visit of a yearly vampire festival (unless you're also a java developer, in which case you're already used to this :) ). More seriously, the problem here is mixing interface definitions and realizations. I changed IItem to an ItemConcept: BOOST_TYPE_ERASURE_MEMBER((Controller)(has_createModelItem), createItem, 1); BOOST_TYPE_ERASURE_MEMBER((Controller)(has_deleteModelItem), deleteItem, 1); typedef ::boost::mpl::vector< Controller::has_createItem<void(...)>, Controller::has_deleteItem<void(...)>, ...
ItemConcept; typedef boost::type_erasure::any<ItemConcept> AnyItem;
I can even use composition to build my Type1Concept: BOOST_TYPE_ERASURE_MEMBER((Controller)(has_setName), setName, 1); BOOST_TYPE_ERASURE_MEMBER((Controller)(has_getName), getName, 0); typedef ::boost::mpl::vector< ItemConcept, Controller::has_setName<void(...)>, Controller::has_getName<...()>, ...
Item1Concept; typedef boost::type_erasure::any<Item1Concept> AnyItem1;
From a user perspective, it is equivalent to use an IItem or an ItemConcept, so that I now have a solution equivalent from the user's perspective, but much better from the implementer's: I defined interfaces without paying any dependency and I'm perfectly free to implement as I want, without fearing a diamond or other ugly surprises.
While it was in front of my eyes, I realized the best part only yesterday. Let's say my IItem would need a template method struct IItem { ... // as before virtual void createItem(...)=0; // creates in model virtual void deleteItem(...)=0; // deletes from model ... template <class T> virtual void f (T& t); // will not compile }; Sure, we all know this is not possible (sigh) . However, if I postulate that I require T to be foo-able for all realizations of this interface, meaning I can implement f as: t.foo(); It is anyway good style to document the template parameter's requirements anyway, so this bears no cost. Ok, then I can use an any<fooable> in my concept typedef ::boost::mpl::vector< Controller::has_createItem<void(...)>, Controller::has_deleteItem<void(...)>, Controller::has_f_able<void(any<fooable>&)>, ...
ItemConcept;
This is really close from virtual template methods. There is only one thing missing to be able to throw away all these interfaces: the ability to navigate through dynamic/static cast in the hierarchy. TypeErasure supports upcasting of concepts but not downcasting. According to Steven, this would be possible. I won't make it an acceptance condition, but I could make good use of this feature. My use case: different view items get an IItem, then a view Item for Type1 would safely cast down its IItem to IType1. This is at the moment not possible with TypeErasure. I view this as a killer feature, so I can only advise providing it at a later point. One last request: I would like the possibility to read the concept of an any through a metaprogram. I found this: /** INTERNAL ONLY */ typedef Concept _boost_type_erasure_concept_type; I try to avoid using internals, could this be made part of the public interface? Use case: My view item for Type1 gets an any<Type1Concept> and needs a property page for it. Obviously it would need name setting/showing, which other any<ItemConcept> would not. The property page dialog could, with a simple template function, get the concepts supported by an any and build the dialog accordingly. I would thus be able to write a single generic dialog class for all types.
6. How much effort did you put into your evaluation? A glance? A quick reading? In-depth study?
About 10-15 hours trying on real, not toy code.
7. Are you knowledgeable about the problem domain?
I've used Boost.Any/Function several years and made my own serializable any. I want to thank Steven for providing this great library, which I will use in any case, accepted or not. Thanks Lorenzo for managing the review and getting it scheduled so fast. Christophe

AMDG On 07/26/2012 02:57 AM, Christophe Henry wrote:
Hi all,
here's my review for TypeErasure.
Thank you very much for your review.
Please state clearly whether you think this library should be accepted as a Boost library.
Let's answer this upfront: YES!
Other questions you may want to consider: 1. What is your evaluation of the design?
Seen from outside, the design is clean and allows powerful constructs.
2. What is your evaluation of the implementation?
I only had a quick glance.
3. What is your evaluation of the documentation?
What is there is very good, but we could do with more documentation. A few suggestions: - provide a small example of usage of each concept to help new users manage faster the learning curve. I had some difficulties getting istreamable working and had to look at the tests to finally get it.
Okay.
- provide more real-looking examples of the sort of the polymorphic range formatter. The more the better. I'll myself provide one later on in this message.
4. What is your evaluation of the potential usefulness of the library?
Huge! And by this I mean a very interesting programming style which should be made available also to average programmers. Which makes the doc even more important.
5. Did you try to use the library? With what compiler? Did you have any problems?
Yes, on a real private project I do on my free time. I used VC9. I'm in vacations so I couldn't try gcc and VC10 yet but I'll do this in the next few weeks. I got no problem besides a single warning about unused variable (fixed).
Good to hear.
I tried the library for 2 different use cases: - a streamable any (actually I need a boost-serializable any but didn't come to it yet). One always needs something like this. In the past, I had to modify a Boost.Any to achieve this. In my design, this helps implement my low-level saving/loading to/from file easily as the low level layer needs no knowledge about the types it gets, they're just a bunch of serializable things.
- improve my MVC (Model-View-Controller) design. Here's my use case: I started with a classical (OO style) interface-based design a graphical editor where the user can place and edit items of different kinds on a drawing area. The interface for these items is something along the lines: <snip example>
This looks great. I'll work this into a complete example.
There is only one thing missing to be able to throw away all these interfaces: the ability to navigate through dynamic/static cast in the hierarchy. TypeErasure supports upcasting of concepts but not downcasting. According to Steven, this would be possible. I won't make it an acceptance condition, but I could make good use of this feature. My use case: different view items get an IItem, then a view Item for Type1 would safely cast down its IItem to IType1. This is at the moment not possible with TypeErasure. I view this as a killer feature, so I can only advise providing it at a later point.
I hadn't considered this very important, but you're the second person to request this, so I'll bump up it's priority.
One last request: I would like the possibility to read the concept of an any through a metaprogram. I found this: /** INTERNAL ONLY */ typedef Concept _boost_type_erasure_concept_type;
I try to avoid using internals, could this be made part of the public interface?
This is provided as a metafunction concept_of. In Christ, Steven Watanabe

-----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Lorenzo Caminiti Sent: Wednesday, July 18, 2012 6:13 AM To: boost@lists.boost.org; boost-announce@lists.boost.org; boost-users@lists.boost.org Subject: [boost] [type_erasure] Review started (July 18-27, 2012) *** The review of Steven Watanabe's proposed Boost.TypeErasure library begins on July 18, 2012 and ends on July 27, 2012. *** I'll post a fuller review in due course, but after playing with this I am tempted to cut to the chase and say YES. But here is an example that I have knocked up in a few minutes from print_sequence.cpp example (after a longer time struggling with the name TypeErasure and the concept in the docs). #include <libs/units/example/measurement.hpp> // An uncertain type called measure. int test[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; double testd[] = { 1., 2., 3., 4., 5., 6., 7., 8., 9., 10. }; quantity<length> testq[] = {2345.6 * meters, 123.4 * meters, 0.0123 * meters}; quantity<length, measurement<double> > testqu[] = {measurement<double>(45210.0, 1234.0) * meters, measurement<double>(789, 2.5) * meters, measurement<double>(0.000567,2.34e-5) * meters}; separator_printer p1(", "); // Construct a printer. p1.print(std::cout, test); // 1,2,3,4,5,6,7,8,9,10 std::cout << std::endl; separator_printer p1a("___"); // Construct a printer with underscore separator. p1a.print(std::cout, test); // 1___2___3___4___5___6___7___8___9___10 std::cout << std::endl; p1.print(std::cout, testd); // Type now double. std::cout << std::endl; // 1,2,3,4,5,6,7,8,9,10 std::cout.precision(5); // Make some fancy layout. std::cout << std::showpos << std::showpoint << std::endl; p1.print(std::cout, testd); // Type still double. std::cout << std::endl; // +1.0000,+2.0000,+3.0000,+4.0000,+5.0000,+6.0000,+7.0000,+8.0000,+9.0000,+10.000 std::cout << std::noshowpos << std::endl; // Switch off fancy +. p1.print(std::cout, testq); // Type now quantity of length. std::cout << std::endl; // 2.3456 km, 123.40 m, 12.300 mm // Note the cool change of unit prefix from km to m to mm! p1.print(std::cout, testqu); // Type now quantity of length with uncertainty estimate. std::cout << std::endl; // 45.210(+/-1.2340) km, 789.00(+/-2.5000) m, 567.00(+/-23.400) um // Note the cool change of unit prefix *and* showing uncertainty! I'm impressed ! Now if I can manage to string enough neurons together to write my own 'printers' etc, ... But already I sense this is a must-have bit of software. :-) Paul PS I can't come up with a better name than TypeErasure but it is certainly a bit forbidding :-(

Please state clearly whether you think this library should be accepted as a Boost library. Yes.
Other questions you may want to consider: 1. What is your evaluation of the design? I like it. Given the constraints you have to work with in C++, defining and using concepts is remarkably simple. I'm a bit worried about boost::any, being in the main boost namespace, conflicting with boost::type_erasure::any. The presence of both means
On 18.07.2012 07:13, Lorenzo Caminiti wrote: that I cannot do a using directive on both namespaces. On the other hand, I can't think of a better name than any.
2. What is your evaluation of the implementation? Didn't look. 3. What is your evaluation of the documentation? I liked it. Some elements appear to be undocumented (e.g. random_access_iterator) - these should be documented, if only with a note that they are not really user-facing. The abstract_printer example could use more comments in the code explaining what the important parts do and the reasoning behind them. 4. What is your evaluation of the potential usefulness of the library? High. I know I've written my own custom type erasures more than once. This library would save a lot of work there. 5. Did you try to use the library? With what compiler? Did you have any problems? Didn't try. 6. How much effort did you put into your evaluation? A glance? A quick reading? In-depth study? I read the documentation thoroughly, twice. 7. Are you knowledgeable about the problem domain? Yes, I would say so.
Sebastian

AMDG On 07/27/2012 04:33 AM, Sebastian Redl wrote:
Please state clearly whether you think this library should be accepted as a Boost library. Yes.
Other questions you may want to consider: 1. What is your evaluation of the design? I like it. Given the constraints you have to work with in C++, defining and using concepts is remarkably simple. I'm a bit worried about boost::any, being in the main boost namespace, conflicting with boost::type_erasure::any. The presence of both means
On 18.07.2012 07:13, Lorenzo Caminiti wrote: that I cannot do a using directive on both namespaces. On the other hand, I can't think of a better name than any.
I'm happy to consider alternate names, but what I have now is the best I was able to come up with.
2. What is your evaluation of the implementation? Didn't look. 3. What is your evaluation of the documentation? I liked it. Some elements appear to be undocumented (e.g. random_access_iterator) - these should be documented, if only with a note that they are not really user-facing.
Most of the built in concepts could use a bit more documentation.
The abstract_printer example could use more comments in the code explaining what the important parts do and the reasoning behind them.
Okay. In Christ, Steven Watanabe

Hi all, Please find my review of Type Erasure below.
Please state clearly whether you think this library should be accepted as a Boost library.
Yes.
1. What is your evaluation of the design?
The library is easy to use, at least for the use cases I am interested in. Still it provides what I consider to be very very advanced features. Though, I'd question the need for some those very advanced functionalities. Will many people use something has cumbersome as : tuple<requirements, _a, _b> t(&array[0], 2); any<requirements, _a> x(get<0>(t) + get<1>(t)); I can't really evaluate if this has a huge cost in terms of complexity in the code but I think the usages (i.e. the benefits) are likely to be limited even among library developers. (By the way can this be considered multi dispatch in C++ ?) For instance, if removing this feature or simplifying the construction would allow removing the possibility to construct a "reference any" from a "reference-to-const any" I'd choose to remove the bug. Now, I may be not knowledgeable enough to judge this point precisely. I think I may limit the use cases of Type Erasure to "super interface" and that there is more to it.
2. What is your evaluation of the implementation?
The implementation is nearly not documented. There are very few comments in the private part of the code and there is no coding guidelines. This makes a review of the implementation very hard especially because a library like this is composed mostly of boilerplate TMP code. I wanted to figure out how the vtable was created and used, but I got discouraged. Adding some help for would be hackers could be interesting. Overall, the library has been submitted while still "young" (I am not disregarding the many month of efforts that were likely put into this). Steven mentions that he has not tried optimizing it yet. This is a sound choice for a young library but I'd recommend it to be done (and reviewed) before the library is released. We want to avoid a "boost::function effect" : a different guy slapping the library for being to slow and having a better design every 6 month. I think no one so far reviewed the implementation so it won't hurt to have a second round. Another thing, would be to work on error messages, because this being C++ they are horrible. I don't know if static asserts may help a lot easily or not, but if anything can be done, it'll be welcome.
3. What is your evaluation of the documentation?
This is clearly the weak point of the library. The library offers a new style of programming. One that deserves a proper step by step tutorial. One that deserves a few guidelines. Right now it's more or less backward. Very advanced concepts like placeholder types jointly capture variables are introduced before showing how to make your own concept and what's a concept by the way. There are simple usages of Type Erasure that should be made more accessible. I have been writing a sample tutorial that reflects how I would have liked to learn what Type Erasure is about. Please find it attached. It doesn't pretend to be complete or correct (it could use a serious amount of re reading) but it shows what I believe to be a much more natural progression. Please excuse me for the ugly shell in a .txt but work happened. I had no time for better. Some other pieces of advice: - I'd rename "concept map" to "concept adapter". There's no map for the user, it's your internal logic that is using one to find the correct adapter for a given concept and type. - I'd rewrite the "Concept Definitions" page. You define 3 kinds of high level concepts first, then explain what a primitive concept is. I'd keep the placeholder stuff for a different section. The good point is that the reference documentation looks complete and helpful.
4. What is your evaluation of the potential usefulness of the library?
It's huge. While writing interfaces behaving similarly was somehow always possible it was way too much of a pain to actually implement each time and few people actually dived. Type Erasure does an excellent job at streamlining the writing of interfaces. It has a good chance to become as ubiquitous as the libraries it generalizes (any and function). It may be too young to be a candidate for the next C++ standard update but it feels more than adequate.
5. Did you try to use the library? With what compiler? Did you have any problems?
I used the library to write simple tests programs only. I used g++ (4.6) and clang (3.0) in both C++03 and C++11 modes. No problems besides the fact that this is advanced C++ TMP and that the error messages are as unfriendly as usual.
6. How much effort did you put into your evaluation? A glance? A quick reading? In-depth study?
In-depth study.
7. Are you knowledgeable about the problem domain?
No I am not. I am just familiar with more traditional type erasure having implemented a couple of type erased wrappers. Many thanks to Steven for the library, it clearly offers new perspective for C++. Regards, Julien

AMDG On 07/27/2012 11:14 AM, Julien Nitard wrote:
Hi all,
Please find my review of Type Erasure below.
Please state clearly whether you think this library should be accepted as a Boost library.
Yes.
1. What is your evaluation of the design?
The library is easy to use, at least for the use cases I am interested in. Still it provides what I consider to be very very advanced features.
Though, I'd question the need for some those very advanced functionalities. Will many people use something has cumbersome as :
tuple<requirements, _a, _b> t(&array[0], 2); any<requirements, _a> x(get<0>(t) + get<1>(t));
The main benefit for this is for type erased function arguments: typedef mpl::vector< random_access_iterator<_iter>, same_type<random_access_iterator<_iter>::value_type, _a>, less_than_comparable<_a>, move_constructible<_a>, move_assignable<_a>, callable<bool(const _a&, const _a&), _f>, copy_constructible<_f>
sort_requirements;
void sort_impl(iter_type, iter_type, func_type); // capture the arguments and forward to the // separately compiled implementation template<class Iter, class F> void sort(Iter, Iter, F); Requirements like this are very common in generic programming.
I can't really evaluate if this has a huge cost in terms of complexity in the code but I think the usages (i.e. the benefits) are likely to be limited even among library developers. (By the way can this be considered multi dispatch in C++ ?)
It isn't really multiple dispatch. The argument types can't vary independently.
For instance, if removing this feature or simplifying the construction would allow removing the possibility to construct a "reference any" from a "reference-to-const any" I'd choose to remove the bug.
This shouldn't be possible. Do you have a test case?
Now, I may be not knowledgeable enough to judge this point precisely. I think I may limit the use cases of Type Erasure to "super interface" and that there is more to it.
2. What is your evaluation of the implementation?
The implementation is nearly not documented. There are very few comments in the private part of the code and there is no coding guidelines. This makes a review of the implementation very hard especially because a library like this is composed mostly of boilerplate TMP code. I wanted to figure out how the vtable was created and used, but I got discouraged. Adding some help for would be hackers could be interesting.
The vtable isn't terribly complex. It's essentially a statically initialized fusion::map. template<class T1, class T2> struct vtable2 { typename T1::type t1 = T1::value; typename T2::type t2 = T2::value; typename T1::type lookup(T1*) const { return t1; } typename T2::type lookup(T2*) const { return t2; } }; A lot of the library internals are similar in that they're essentially simple components with a lot of preprocessor baggage around them. The only real algorithmic complexity is in normalize.hpp.
Overall, the library has been submitted while still "young" (I am not disregarding the many month of efforts that were likely put into this). Steven mentions that he has not tried optimizing it yet. This is a sound choice for a young library but I'd recommend it to be done (and reviewed) before the library is released. We want to avoid a "boost::function effect" : a different guy slapping the library for being to slow and having a better design every 6 month. I think no one so far reviewed the implementation so it won't hurt to have a second round.
Another thing, would be to work on error messages, because this being C++ they are horrible. I don't know if static asserts may help a lot easily or not, but if anything can be done, it'll be welcome.
I can make sure that the library doesn't produce deep template instantiation backtraces in most cases. There are a few cases where a static assertion would help too.
3. What is your evaluation of the documentation?
This is clearly the weak point of the library. The library offers a new style of programming. One that deserves a proper step by step tutorial. One that deserves a few guidelines.
Right now it's more or less backward. Very advanced concepts like placeholder types jointly capture variables are introduced before showing how to make your own concept and what's a concept by the way.
The "Functions with multiple arguments" appears early because it introduces placeholders, which are used in many sections. Any order is going to be somewhat problematic, because of the way all the core components of the library are connected. Whatever I introduce first is going to depend on things that aren't explained until later. I think the first examples in the docs need to be based on - only predefined concepts - no placeholders used explicitly - capture by value
There are simple usages of Type Erasure that should be made more accessible. I have been writing a sample tutorial that reflects how I would have liked to learn what Type Erasure is about. Please find it attached. It doesn't pretend to be complete or correct (it could use a serious amount of re reading) but it shows what I believe to be a much more natural progression. Please excuse me for the ugly shell in a .txt but work happened. I had no time for better.
Some other pieces of advice: - I'd rename "concept map" to "concept adapter". There's no map for the user, it's your internal logic that is using one to find the correct adapter for a given concept and type.
The term "Concept map" is taken from the C++ concept proposals.
- I'd rewrite the "Concept Definitions" page. You define 3 kinds of high level concepts first, then explain what a primitive concept is. I'd keep the placeholder stuff for a different section.
In this section, I'm trying to specify exactly what makes a valid concept. It's a bit hard to do that without discussing placeholders. Also, I'm more focussed on precision than on readability here. Informal descriptions can go in earlier sections.
The good point is that the reference documentation looks complete and helpful.
In Christ, Steven Watanabe

On 07/27/12 15:19, Steven Watanabe wrote:
AMDG
On 07/27/2012 11:14 AM, Julien Nitard wrote:
Please find my review of Type Erasure below.
Please state clearly whether you think this library should be accepted as a Boost library.
Yes.
1. What is your evaluation of the design?
The library is easy to use, at least for the use cases I am
interested in.
Still it provides what I consider to be very very advanced features.
Though, I'd question the need for some those very advanced functionalities. Will many people use something has cumbersome as :
tuple<requirements, _a, _b> t(&array[0], 2); any<requirements, _a> x(get<0>(t) + get<1>(t));
The main benefit for this is for type erased function arguments:
typedef mpl::vector< random_access_iterator<_iter>, same_type<random_access_iterator<_iter>::value_type, _a>, less_than_comparable<_a>, move_constructible<_a>, move_assignable<_a>, callable<bool(const _a&, const _a&), _f>, copy_constructible<_f>
sort_requirements;
void sort_impl(iter_type, iter_type, func_type);
// capture the arguments and forward to the // separately compiled implementation template<class Iter, class F> void sort(Iter, Iter, F);
What does "capture the arguments" here mean? I'd guess it means substituting some actual arguments types for the placeholders and actual argument values for the, I guess you'd call them, contained types, but I don't see any actual argument values in the example; hence, that guess doesn't fit. Could you please elaborate on this example?
[snip]
2. What is your evaluation of the implementation?
The implementation is nearly not documented. There are very few comments in the private part of the code and there is no coding guidelines. This makes a review of the implementation very hard especially because a library like this is composed mostly of boilerplate TMP code. I wanted to figure out how the vtable was created and used, but I got discouraged. Adding some help for would be hackers could be interesting.
The vtable isn't terribly complex. It's essentially a statically initialized fusion::map.
template<class T1, class T2> struct vtable2 { typename T1::type t1 = T1::value; typename T2::type t2 = T2::value; typename T1::type lookup(T1*) const { return t1; } typename T2::type lookup(T2*) const { return t2; } };
I've seen the T::type code a lot (I think in error messages); however, I've never understood what the T's were. Could elaborate on what they are and how they're produced?
A lot of the library internals are similar in that they're essentially simple components with a lot of preprocessor baggage around them. The only real algorithmic complexity is in normalize.hpp.
But normalize.hpp contains no in-source comments, which perfectly illustrate Julien's remark: There are very few comments in the private part of the code Like Julien, I also tried to figure out how vtable worked, which lead me to examing normalize.hpp. The lack of in-source comments made this very discouraging. I finally resorted to breaking down the source and printing the demangled types names; however, I've still not figured it out. I'd suggest breaking down the source, somewhat like in the attached, attach comments to the parts explaining what they do, then creating test cases for each part in put those unit tests in the */libs/type_erasure/test directory. This would not only document the implementation better, but provide better testing, and if the in-source comments weren't enough, looking at the unit tests might help future hackers figure out what the code does.
[snip]
Another thing, would be to work on error messages, because this being C++ they are horrible. I don't know if static asserts may help a lot easily or not, but if anything can be done, it'll be welcome.
I can make sure that the library doesn't produce deep template instantiation backtraces in most cases. There are a few cases where a static assertion would help too.
What about using: http://www.boost.org/doc/libs/1_50_0/libs/concept_check/concept_check.htm because, as that .htm page says: The Boost Concept Checking Library uses some standard C++ constructs to enforce early concept compliance and that provides more informative error messages upon non-compliance. [snip] -regards, Larry

Hi [ ... ]
For instance, if removing this feature or simplifying the construction would allow removing the possibility to construct a "reference any" from a "reference-to-const any" I'd choose to remove the bug.
This shouldn't be possible. Do you have a test case?
No, it's not a bug I found, you mentioned that limitation in the doc, "Using References", last 3 sentences. [ snipped rant about lack of doc in the code ]
The vtable isn't terribly complex. It's essentially a statically initialized fusion::map. [...] A lot of the library internals are similar in that they're essentially simple components with a lot of preprocessor baggage around them. The only real algorithmic complexity is in normalize.hpp.
Others have found the code to be easy enough to read, so I'll take the blame for not being fluent enough for reading this kind of code quickly. I still think that more comments the better ;) I'll give it a second try since the review was extended.
I can make sure that the library doesn't produce deep template instantiation backtraces in most cases. There are a few cases where a static assertion would help too.
Please do ! [ ... ]
Right now it's more or less backward. Very advanced concepts like placeholder types jointly capture variables are introduced before showing how to make your own concept and what's a concept by the way.
The "Functions with multiple arguments" appears early because it introduces placeholders, which are used in many sections.
Yes but those sections are already advanced, I think more are needed before.
Any order is going to be somewhat problematic, because of the way all the core components of the library are connected. Whatever I introduce first is going to depend on things that aren't explained until later.
You don't have to introduce every thing at once. You will have to ignore things you consider important at first to draw people into reading the documentation not lose them after 5 sentences of meta concepts they've never heard of (quite likely for this library). That is unless you only care about advanced users, but the library is so good it would be a pity.
I think the first examples in the docs need to be based on - only predefined concepts - no placeholders used explicitly - capture by value
To show only predefined concepts is ignoring that a great deal of interfaces don't use any operators and do not require any construction, copy or deletion semantics which is what the predefined concepts are about. The main way I see myself using TypeErasure is by creating concept and then adapting types. After rewriting this paragraph about 20 times I think that the order for the capturing mode isn't that relevant as long as both get their share of love early on. It appears more and more clearly to me that there are two ways of using TypeErasure: - values and operators, that comes from the boost::any/function/any_iterator adobe::poly origins. These are for libraries developers such as boost or stl coders. - references and methods, for people who have long been contaminated by other languages as part of former experiences like myself. These are for more concrete uses, i.e. I need a socket and a connect function that takes an ip and a port in the same format. I guess the current documentation is biased on the first one right now but shouldn't. It is my strong belief that more coders use the 2nd paradigm. What's interesting it's now it's easy to mix them when ever needed ;)
- I'd rewrite the "Concept Definitions" page. You define 3 kinds of high level concepts first, then explain what a primitive concept is. I'd keep the placeholder stuff for a different section.
In this section, I'm trying to specify exactly what makes a valid concept. It's a bit hard to do that without discussing placeholders. Also, I'm more focussed on precision than on readability here. Informal descriptions can go in earlier sections.
Then split it into two parts. A "white lie" introduction and then a full spec for those willing/needing to know the details. Some additional comments not in my review: Two missing concepts that you could consider: ranges (well actually more than one by itself) and copy-on-write. Not a priority obviously. Thanks again for all those efforts, Julien

-----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Steven Watanabe Sent: Friday, July 27, 2012 9:19 PM To: boost@lists.boost.org Subject: Re: [boost] [type_erasure] Review started (July 18-27, 2012)
AMDG
The main benefit for this is for type erased function arguments:
typedef mpl::vector< random_access_iterator<_iter>, same_type<random_access_iterator<_iter>::value_type, _a>, less_than_comparable<_a>, move_constructible<_a>, move_assignable<_a>, callable<bool(const _a&, const _a&), _f>, copy_constructible<_f>
sort_requirements;
void sort_impl(iter_type, iter_type, func_type);
// capture the arguments and forward to the // separately compiled implementation template<class Iter, class F> void sort(Iter, Iter, F);
In trying to work up this example, I couldn't find move_constructible and move_assignable - are this a TODO? But should work without this? This also highlighted the usefulness of an index - I tried to build one using b2 --enable-index , but the simple hooks and eyes are missing. There are quite lot of possible requirements, and they each have their include file. Thanks Paul
Requirements like this are very common in generic programming.
The good point is that the reference documentation looks complete and helpful.
In Christ, Steven Watanabe
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

On 7/17/2012 10:13 PM, Lorenzo Caminiti wrote:
Please state clearly whether you think this library should be accepted as a Boost library.
Yep.
Other questions you may want to consider: 1. What is your evaluation of the design?
Solid. One question: Does concept_interface really need to explicitly inherit from Base? What about mpl::inherit_linearly? I suppose this has problems with overloading, right? Blarg. The overloading thing sure is nasty, but I have no suggestions. :-(
2. What is your evaluation of the implementation?
I didn't look.
3. What is your evaluation of the documentation?
The reference docs are thorough. The user docs could be more gentle. I'm afraid it will be rather imposing for most non-guru C++ programmers. There is very little motivation given. Check out Sean Parent's BoostCon 2012 talk on value semantics[^1] to see a really good example of building motivation using a concrete example, and then presenting the solution. It helps get the points across. The one and only complete example in the docs (polymorphic range formatter) has this only for building motivation:
This example demonstrates using Boost.TypeErasure to implement a virtual "template" function.
That's a pretty thin motivation. WHY is a virtual template interesting? What problem does it solve? Is this really all that you have to say about this example? In short, I'd put a handful of examples at the front of the users' guide that are relate-able and illustrative. Otherwise, you just have a listing of features and sample usages, with no motivation, no rationale, and no thread tying the features together. It might even help to describe what "type erasure" is, how it's been traditionally used, and what one of the simpler examples would look like without Boost.TypeErasure. That way, folks can get a feel for the drudge-work that your library saves them. Aside: I don't much care for the name "TypeErasure". In purpose, it's much like the proposed Boost.Interface library. And that is closer to how the library is used: to define an interface and build a type that implements it. The fact that it uses a mechanism called "type erasure" to achieve that is really just an implementation detail. Some might feel the word "interface" comes with too much OO baggage, or imply reference semantics. I don't feel that way, but then you might consider "StaticInterface" or "ValueInterface". Actually, I quite like the latter. I'd also consider going through the docs and changing references to "requirements" to "interface". Details: On "Using references"
Note
_self is the default placeholder, so it is easiest to use _self&. We could use another placeholder instead. any<__typeid<_a>, _a&> has exactly the same behavior
__typeid should be typeid_ "Concept Maps" A link here to your built-in Concept Definitions would be nice. "See _here_ for a comprehensive list of built-in concept maps that can be specialized like this." "Associated Types"
Referring to the full name of the associated type can be cumbersome when it's used many times.
This, after a line of code that reads "any_cast<int>(*x)". After some head-scratching, I'm inferring that *x returns an any. Of course. And same_type is a promise that deduced<pointee<T> > will always come out to be "int", so please just hard-code that in the interface and run whatever checks are necessary to guarantee that that is true. This is all clear after reflection, but it would be nice if the docs were more explicit on this point.
4. What is your evaluation of the potential usefulness of the library?
Very.
5. Did you try to use the library? With what compiler? Did you have any problems?
No, I didn't try to use it.
6. How much effort did you put into your evaluation? A glance? A quick reading? In-depth study?
2+ hrs.
7. Are you knowledgeable about the problem domain?
Yes. [^1] http://www.youtube.com/watch?v=_BpMYeUFXv8&feature=plcp -- Eric Niebler BoostPro Computing http://www.boostpro.com

AMDG On 07/27/2012 12:46 PM, Eric Niebler wrote:
On 7/17/2012 10:13 PM, Lorenzo Caminiti wrote:
Please state clearly whether you think this library should be accepted as a Boost library.
Yep.
Other questions you may want to consider: 1. What is your evaluation of the design?
Solid. One question:
Does concept_interface really need to explicitly inherit from Base? What about mpl::inherit_linearly? I suppose this has problems with overloading, right? Blarg. The overloading thing sure is nasty, but I have no suggestions. :-(
A straight linear hierarchy also allows members to be hidden. e.g. the specialization for bidirectional_iterator overrides the iterator_category typedef from forward_iterator. Also, I'd still need a parameter, since Base doubles for CRTP.
2. What is your evaluation of the implementation?
I didn't look.
3. What is your evaluation of the documentation?
The reference docs are thorough. The user docs could be more gentle. I'm afraid it will be rather imposing for most non-guru C++ programmers.
There is very little motivation given. Check out Sean Parent's BoostCon 2012 talk on value semantics[^1] to see a really good example of building motivation using a concrete example, and then presenting the solution. It helps get the points across. The one and only complete example in the docs (polymorphic range formatter) has this only for building motivation:
This example demonstrates using Boost.TypeErasure to implement a virtual "template" function.
That's a pretty thin motivation. WHY is a virtual template interesting? What problem does it solve? Is this really all that you have to say about this example?
Noted.
In short, I'd put a handful of examples at the front of the users' guide that are relate-able and illustrative. Otherwise, you just have a listing of features and sample usages, with no motivation, no rationale, and no thread tying the features together.
Okay.
It might even help to describe what "type erasure" is, how it's been traditionally used, and what one of the simpler examples would look like without Boost.TypeErasure. That way, folks can get a feel for the drudge-work that your library saves them.
Aside: I don't much care for the name "TypeErasure". In purpose, it's much like the proposed Boost.Interface library. And that is closer to how the library is used: to define an interface and build a type that implements it. The fact that it uses a mechanism called "type erasure" to achieve that is really just an implementation detail. Some might feel the word "interface" comes with too much OO baggage, or imply reference semantics. I don't feel that way, but then you might consider "StaticInterface" or "ValueInterface". Actually, I quite like the latter.
I'm open to suggestions, but I'm not sure what the "Static" in StaticInterface refers to, and I'm also not convinced that ValueInterface really appropriate, since the library supports references, not just value semantics.
I'd also consider going through the docs and changing references to "requirements" to "interface".
Details:
On "Using references"
Note
_self is the default placeholder, so it is easiest to use _self&. We could use another placeholder instead. any<__typeid<_a>, _a&> has exactly the same behavior
__typeid should be typeid_
Fixed.
"Concept Maps" A link here to your built-in Concept Definitions would be nice. "See _here_ for a comprehensive list of built-in concept maps that can be specialized like this."
Not all built-in concepts can be specialized. I suppose I should indicate which ones can be.
"Associated Types"
Referring to the full name of the associated type can be cumbersome when it's used many times.
This, after a line of code that reads "any_cast<int>(*x)". After some head-scratching, I'm inferring that *x returns an any. Of course. And same_type is a promise that deduced<pointee<T> > will always come out to be "int", so please just hard-code that in the interface and run whatever checks are necessary to guarantee that that is true. This is all clear after reflection, but it would be nice if the docs were more explicit on this point.
Done. In Christ, Steven Watanabe

On 7/27/2012 8:43 PM, Steven Watanabe wrote:
On 07/27/2012 12:46 PM, Eric Niebler wrote:
Aside: I don't much care for the name "TypeErasure". In purpose, it's much like the proposed Boost.Interface library. And that is closer to how the library is used: to define an interface and build a type that implements it. The fact that it uses a mechanism called "type erasure" to achieve that is really just an implementation detail. Some might feel the word "interface" comes with too much OO baggage, or imply reference semantics. I don't feel that way,
I was hoping you'd also comment on the suggestion "Boost.Interface", but I guess I didn't explicitly suggest that. I'm suggesting it now.
but then you might consider "StaticInterface" or "ValueInterface". Actually, I quite like the latter.
I'm open to suggestions, but I'm not sure what the "Static" in StaticInterface refers to,
I could explain what I was thinking, but better to just let this suggestion drop.
and I'm also not convinced that ValueInterface really appropriate, since the library supports references, not just value semantics.
Sure. C++ also supports reference semantics, but value semantics are the default for the language and most std library components, and for your library as well. I think value semantics for post-hoc interface-based polymorphism is THE killer feature of your library, and it's how I expect it will be used most of the time. I strongly encourage you to consider the name Boost.ValueInterface or something more descriptive of the anticipated *usage* of your library, rather than how it's implemented. That said, it's your baby and of course you're free to call it whatever you like. -- Eric Niebler BoostPro Computing http://www.boostpro.com

On 7/29/2012 3:29 PM, Eric Niebler wrote:
I strongly encourage you to consider the name Boost.ValueInterface or something more descriptive of the anticipated *usage* of your library, rather than how it's implemented.
Hit send too soon, sorry. Your library can also be thought of as adding structural typing or duck typing to C++, so: - Boost.(Structural|Duck)(Types|Interface(s)?)? would all be fine, descriptive names for the library, IMO. -- Eric Niebler BoostPro Computing http://www.boostpro.com

On 7/18/12 7:13 AM, Lorenzo Caminiti wrote:
*** The review of Steven Watanabe's proposed Boost.TypeErasure library
Please state clearly whether you think this library should be accepted as a Boost library.
A clear Yes, here.
Other questions you may want to consider: 1. What is your evaluation of the design?
Let me start by saying that this library is a very impressive piece of software. It opens up new possibilities in solving several problems in developing large scale software systems in a way I haven't seen before. I have two concerns about the design: a. downcasting. It has been mentioned in the discussion, that this would be a useful feature. Since this feature is coupled with how the any type stores the concepts and cannot be implemented without breaking encapsulation, it needs to be part of the library, or the library has to provide stable hooks for it. Steven has already bumped up the priority of this, so I am confident that this will be solved in some way. Since this feature needs quite a bit of user facing machinery a mini review for this might be needed. I do not make the inclusion of this feature a condition on acceptance, because I trust Steven to include at some time. b. creating custom concepts/concept interfaces. Consider this paragraph as dreaming out loud. I like the way Steven solved the problem of defining concepts but I still like it to be easier, because I believe that how easy it is to define concepts is the key to widespread adoption. Ideally it should not be more complex to define a concept than to define a (virtual) interface. My ideal solution would be able to do this: struct int_container_concept { //... void push_back(int) = 0; size_t size() = 0; // b.2 //... }; void legacy_func(int_container_concept& c); // b.1 BOOST_CREATE_CONCEPT(int_container_concept); void test() std::vector<int> v = /*filles somehow*/; any<int_container_concept> con = v; legacy_func(con); } I know that it is impossible to implement the BOOST_CREATE_CONCEPT macro in the above example without language support. So until we get compile time reflection in c++ we need to live with the additional boilerplate in some way. In the "Design" section Steven explains that to scrap the boilerplate he departed from defining the user facing interface using abstract classes. My concern with this departure is that it is quite different, and not directly compatible. There are two things type erasures concepts cannot do: b.1. I cannot use a concept without using any<>. (i.e. the legacy_func marked above would need to take an any<int_container_concept>& instead) This might be a deal-breaker for some users of the library. b.2. I cannot define a concept that has more than one function directly, i.e. I have to define concepts for push_back and size and then compose them (in this case 35 lines of code). This is of course mainly an issue of syntactic sugar, but in Stevens concept definition boiler plate and interface information (member name, arguments, composition lists) are intermixed. I seem to recall that there was some discussion about having some macros to simplify the boilerplate. I think something along the lines of BOOST_CREATE_CONCEPT(container_concept, typename T, (void (push_back, T), void (size)) ) wouldn't be too bad.
2. What is your evaluation of the implementation?
I only skimmed through the implementation, and found it surprisingly easy to understand for such a meta programming heavy library. Even so it could use a few comments. The Implementation is clean, yet still unoptimized. I think this is reasonable at this point in time. I trust Steven to apply optimizations as time goes by. It would be nice to see the Small Object Optimization relatively soon. The error messages are not to good, especially a "concept missmatch" spews out several rather unhelpful implementation details. (This is also already aknowledged by Steven.)
3. What is your evaluation of the documentation?
The documentation that is there is solid. As others have noted It needs more Motivation, Tutorials and Examples. I especially would like to second one point Eric Niebler made:
In short, I'd put a handful of examples at the front of the users' guide that are relate-able and illustrative. Otherwise, you just have a listing of features and sample usages, with no motivation, no rationale, and no thread tying the features together.
It might even help to describe what "type erasure" is, how it's been traditionally used, and what one of the simpler examples would look like without Boost.TypeErasure. That way, folks can get a feel for the drudge-work that your library saves them.
I think this (especially the second paragraph) is the key to making the docu more accessible.
4. What is your evaluation of the potential usefulness of the library?
Very useful, a potential game changer! It opens the door to a compelling alternative to building systems with it rather than inheritance based polymorphism. It also provides a compelling motivation for bringing "static reflection" to C++ because this feature would allow trivial definition of custom concepts and knowing which concepts a concrete class supports.
5. Did you try to use the library? With what compiler? Did you have any problems?
Build the examples with clang-3.1(Apple) and a build a small toy example myself to gain some insights. No problems there.
6. How much effort did you put into your evaluation? A glance? A quick reading? In-depth study?
Deep reading of the documentation, superficial code reading, some experiments, lots of thinking. 2-3 days.
7. Are you knowledgeable about the problem domain?
Quite. Best regards, and thanks to Steven for this great library. Fabio

AMDG On 07/29/2012 12:28 PM, Fabio Fracassi wrote:
b. creating custom concepts/concept interfaces.
Consider this paragraph as dreaming out loud. I like the way Steven solved the problem of defining concepts but I still like it to be easier, because I believe that how easy it is to define concepts is the key to widespread adoption. Ideally it should not be more complex to define a concept than to define a (virtual) interface. My ideal solution would be able to do this:
struct int_container_concept { //... void push_back(int) = 0; size_t size() = 0; // b.2 //... };
void legacy_func(int_container_concept& c); // b.1
BOOST_CREATE_CONCEPT(int_container_concept);
void test() std::vector<int> v = /*filles somehow*/; any<int_container_concept> con = v;
legacy_func(con); }
I know that it is impossible to implement the BOOST_CREATE_CONCEPT macro in the above example without language support. So until we get compile time reflection in c++ we need to live with the additional boilerplate in some way. In the "Design" section Steven explains that to scrap the boilerplate he departed from defining the user facing interface using abstract classes. My concern with this departure is that it is quite different, and not directly compatible. There are two things type erasures concepts cannot do:
b.1. I cannot use a concept without using any<>. (i.e. the legacy_func marked above would need to take an any<int_container_concept>& instead) This might be a deal-breaker for some users of the library.
If you have an abstract base for your legacy interface, you can do this: template<class T = _self> struct legacy_concept : mpl::vector<...> {}; namespace boost { namespace type_erasure { template<class T, class Base> struct concept_interface<legacy_concept<T>, Base, T> : Base, legacy_base { // implement legacy_base methods }; }} Now any<legacy_concept<> > is derived from legacy_base. Of course, once you've written all this out, you don't gain much over just using: template<class T> struct legacy_adapter : legacy_base { // constructors and forwarding T impl_; }; which is probably easier to understand. The only thing you gain is the ability to compose with other concepts.
b.2. I cannot define a concept that has more than one function directly, i.e. I have to define concepts for push_back and size and then compose them (in this case 35 lines of code). This is of course mainly an issue of syntactic sugar, but in Stevens concept definition boiler plate and interface information (member name, arguments, composition lists) are intermixed.
I seem to recall that there was some discussion about having some macros to simplify the boilerplate. I think something along the lines of
BOOST_CREATE_CONCEPT(container_concept, typename T, (void (push_back, T), void (size)) )
wouldn't be too bad.
I rather dislike complex macros like this. In Christ, Steven Watanabe

On 7/30/12 2:27 AM, Steven Watanabe wrote:
If you have an abstract base for your legacy interface, you can do this:
template<class T = _self> struct legacy_concept : mpl::vector<...> {};
namespace boost { namespace type_erasure {
template<class T, class Base> struct concept_interface<legacy_concept<T>, Base, T> : Base, legacy_base { // implement legacy_base methods };
}}
Now any<legacy_concept<> > is derived from legacy_base.
Of course, once you've written all this out, you don't gain much over just using:
template<class T> struct legacy_adapter : legacy_base { // constructors and forwarding T impl_; };
which is probably easier to understand. The only thing you gain is the ability to compose with other concepts.
Which is a quite significant win imo.
b.2. I cannot define a concept that has more than one function directly, i.e. I have to define concepts for push_back and size and then compose them (in this case 35 lines of code). This is of course mainly an issue of syntactic sugar, but in Stevens concept definition boiler plate and interface information (member name, arguments, composition lists) are intermixed.
I seem to recall that there was some discussion about having some macros to simplify the boilerplate. I think something along the lines of
BOOST_CREATE_CONCEPT(container_concept, typename T, (void (push_back, T), void (size)) )
wouldn't be too bad.
I rather dislike complex macros like this.
I am non to fond of them either. In this case though it makes the code much cleaner, consider a syntax like above or similar (haven't checked whether this is even possible, my PP foo is not to good) to this: BOOST_CREATE_CONCEPT(push_back, typename T, MEMBER push_back (void (T)) ) concisely gives you all the information (concept name, member func name and signature) a user of this concept needs. In contrast to spelling it out template<class C, class T> // < Part I of the signature struct push_back { // < Concept name here static void apply(C& cont, const T& arg) { cont.push_back(arg); } // the rest of the signature is hidden somewhere in the apply // but never explicitly spelled out }; namespace boost { namespace type_erasure { // lots of duplicated information and unrelated details to // (mis)understand and get wrong template<class C, class T, class Base> struct concept_interface< ::push_back<C, T>, Base, C> : Base { void push_back(typename rebind_any<Base, const T&>::type arg) { call(::push_back<C, T>(), *this, arg); } }; } } Writing this is not to onerous, but (quickly) reading code that uses it ... regards Fabio

AMDG On 07/29/2012 12:28 PM, Fabio Fracassi wrote:
b.2. I cannot define a concept that has more than one function directly, i.e. I have to define concepts for push_back and size and then compose them (in this case 35 lines of code). This is of course mainly an issue of syntactic sugar, but in Stevens concept definition boiler plate and interface information (member name, arguments, composition lists) are intermixed.
I seem to recall that there was some discussion about having some macros to simplify the boilerplate. I think something along the lines of
BOOST_CREATE_CONCEPT(container_concept, typename T, (void (push_back, T), void (size)) )
wouldn't be too bad.
What about this: BOOST_TYPE_ERASURE_MEMBERS((push_back)(size)...) template<class V, class T = _self> struct container_concept : mpl::vector< has_push_back<void(const V&), T>, has_size<std::size_t(), const T>, ...> {}; In Christ, Steven Watanabe

On 7/30/12 3:43 PM, Steven Watanabe wrote:
AMDG
On 07/29/2012 12:28 PM, Fabio Fracassi wrote:
b.2. I cannot define a concept that has more than one function directly, i.e. I have to define concepts for push_back and size and then compose them (in this case 35 lines of code). This is of course mainly an issue of syntactic sugar, but in Stevens concept definition boiler plate and interface information (member name, arguments, composition lists) are intermixed.
I seem to recall that there was some discussion about having some macros to simplify the boilerplate. I think something along the lines of
BOOST_CREATE_CONCEPT(container_concept, typename T, (void (push_back, T), void (size)) )
wouldn't be too bad.
What about this:
BOOST_TYPE_ERASURE_MEMBERS((push_back)(size)...)
template<class V, class T = _self> struct container_concept : mpl::vector< has_push_back<void(const V&), T>, has_size<std::size_t(), const T>, ...> {};
I like it. (even though I do not yet understand how it maps to what is currently there.) It still exposes some implementation details (like mpl:vector and the T template parameter), but this is a purely aesthetic point. best regards Fabio

First I want to thank Steven for all the work he has put into this library. I have a number of cases where this library will be very useful as is.
Please state clearly whether you think this library should be accepted as a Boost library.
Yes, with the conditions below about how references and conversions are handled. The basic features are very useful by themselves and are suitable for the majority of uses. If possible, these could be accepted and added to Boost immediately, with a mini-review of the other components when ready.
1. What is your evaluation of the design?
For the basic use cases I think this is absolutely the right design. I like the decision to use an MPL sequence of concepts, and that concepts can be composed this way as well. This alone meets the needs of the majority of use cases, including the various uses of type erasure already in Boost. I haven't had a need for something like multiple placeholders, but the design looks reasonable as well. As far as I can tell, the current design for references would prevent me from being able to use this library in a major portion of my code. I haven't received Steven's reply to my other email, so I'll just summarize the issue here. I have some generic function that takes its argument by reference and might modify it or make a copy of it. I want to provide a version that can be compiled separately. Something like: template<class T> void f_impl(T& t) { ++t; T t2(t); ++t2; } void f(any<...>& i) { f_impl(i); } The function won't behave properly if it is instantiated with T = any<..., _self&> (++t2 would increment t), so it has to be instantiated with T = any<..., _self>. But now calling the function is problematic: void g(int& i) { ... // do something with i f(???); } There is no way I can see to have f modify the original variable i. This is similar to the sort example Steven gave in a previous email [1], but with arguments passed by reference. I propose an overloaded constructor that signals that the underlying object should be held by reference but copied when the any object is copied: int i = 0; any<requirements> a(i, capture_reference); any<requirements> b(a); ++a; // i == 1 ++b; // i == 1 still, any_cast<int>(b) == 2 This has worked well in my own code and should be easy to implement. I think this would also avoid the bug mentioned at the bottom of the references page of the documentation.
2. What is your evaluation of the implementation?
Unfortunately I have only had time for a brief look at the code, so most of my understanding comes from reading the design notes section of the documentation. The main point I'm unsure of is the decision to dynamically generate vtables upon conversion. I have my own class for applying type erasure to any type that models a Fusion associative sequence (templated by an MPL sequence of key/value pairs) that I use pretty heavily. It uses a custom vtable similar to Steven's library, except that the vtables are always statically generated even for upward and downward conversions. I was interested to compare the two approaches since I had considered dynamic vtables but never tried to implement them. I was pleased to find that only a few lines of code were required to make a similar class with Steven's library (although a bunch of boilerplate would still be needed to have the type-erased class itself be a Fusion sequence). I made a vector of objects of type fusion::map<fusion::pair<key1, int>, fusion::pair<key2, double>>, as well as a vector of type-erased maps (any_map<mpl::vector<mpl::pair<key1, int&>, mpl::pair<key2, double&>>>). I ran four benchmarks in which I summed key1 of: 1) the Fusion map directly 2) any_map 3) a temporary up-conversion to an any_map with only key1 4) a temporary up-conversion to an any_map with only key1, with the original held by reference The case I had in mind with tests 3 & 4 was looping through a vector of any_maps and calling some function with a parameter type that requires an up cast. The results were: Boost.TypeErasure My any_map 1 0.38 s 0.37 s 2 5.45 s 4.08 s 3 105.30 s 66.69 s 4 72.64 s 5.28 s The difference in test 2 is due to my class being smaller (2 pointers/16 bytes for mine vs 32 bytes). If I increase the size of my class the difference disappears. The large increase from test 2 to test 3 is expected since it requires memory allocation inside a tight loop, however test 4 shows this can be avoided by using static vtables and capturing references (at the cost of each cast adding an extra layer of virtual function calls). I was also concerned about the memory cost of storing a large number of casted objects if they each have their own vtable, but haven't tested this. Presumably a mechanism could be implemented to share dynamically generated vtables that would also improve the tests above. Steven, do you have cases in mind where dynamic vtables might be more appropriate?
3. What is your evaluation of the documentation?
There has already been a lot of discussion about the documentation, so I'll just add a couple thoughts: I think relaxed_match should be prominently documented, as I found it unexpected that any cannot be default constructed otherwise. I would like see some examples that demonstrate how the more advanced features would be used in real code. The example Steven gave by email [1] for a sort function is a good candidate to be flushed out and included.
4. What is your evaluation of the potential usefulness of the library?
Very useful.
5. Did you try to use the library? With what compiler? Did you have any problems?
Yes, with VS10 and gcc 4.7.0. I only have a couple minor points: I think default construction without relaxed_match could be a very common error, and special care should be taken to provide a clear error message. In the attached benchmark code, key1 and key2 need to be defined rather than just declared so that the library can determine whether they are placeholder types. If this cannot be avoided, a note in the documents might be nice, along with whether it is acceptable to specialize is_placeholder.
6. How much effort did you put into your evaluation? A glance? A quick reading? In-depth study?
At least a full day in total.
7. Are you knowledgeable about the problem domain?
Reasonably. The class I described above for type erasure of Fusion associative sequences is fairly similar in implementation to Steven's. I have also written type erasure classes to provide Python bindings for something like a Proto-ized version of Boost.Range. Thanks again for this library, Alec [1] http://article.gmane.org/gmane.comp.lib.boost.devel/233069

AMDG On 08/01/2012 10:09 PM, Chapman, Alec wrote:
First I want to thank Steven for all the work he has put into this library. I have a number of cases where this library will be very useful as is.
Please state clearly whether you think this library should be accepted as a Boost library.
Yes, with the conditions below about how references and conversions are handled. The basic features are very useful by themselves and are suitable for the majority of uses. If possible, these could be accepted and added to Boost immediately, with a mini-review of the other components when ready.
1. What is your evaluation of the design?
For the basic use cases I think this is absolutely the right design. I like the decision to use an MPL sequence of concepts, and that concepts can be composed this way as well. This alone meets the needs of the majority of use cases, including the various uses of type erasure already in Boost. I haven't had a need for something like multiple placeholders, but the design looks reasonable as well.
As far as I can tell, the current design for references would prevent me from being able to use this library in a major portion of my code. I haven't received Steven's reply to my other email, so I'll just summarize the issue here.
I have some generic function that takes its argument by reference and might modify it or make a copy of it. I want to provide a version that can be compiled separately. Something like:
template<class T> void f_impl(T& t) { ++t; T t2(t); ++t2; }
void f(any<...>& i) { f_impl(i); }
The function won't behave properly if it is instantiated with T = any<..., _self&> (++t2 would increment t), so it has to be instantiated with T = any<..., _self>. But now calling the function is problematic:
void g(int& i) { ... // do something with i f(???); }
There is no way I can see to have f modify the original variable i. This is similar to the sort example Steven gave in a previous email [1], but with arguments passed by reference.
I propose an overloaded constructor that signals that the underlying object should be held by reference but copied when the any object is copied:
int i = 0; any<requirements> a(i, capture_reference); any<requirements> b(a); ++a; // i == 1 ++b; // i == 1 still, any_cast<int>(b) == 2
This has worked well in my own code and should be easy to implement. I think this would also avoid the bug mentioned at the bottom of the references page of the documentation.
I don't think this is a good idea, as it would require an extra flag whether it's needed or not.
2. What is your evaluation of the implementation?
Unfortunately I have only had time for a brief look at the code, so most of my understanding comes from reading the design notes section of the documentation. The main point I'm unsure of is the decision to dynamically generate vtables upon conversion.
I have my own class for applying type erasure to any type that models a Fusion associative sequence (templated by an MPL sequence of key/value pairs) that I use pretty heavily. It uses a custom vtable similar to Steven's library, except that the vtables are always statically generated even for upward and downward conversions. I was interested to compare the two approaches since I had considered dynamic vtables but never tried to implement them. I was pleased to find that only a few lines of code were required to make a similar class with Steven's library (although a bunch of boilerplate would still be needed to have the type-erased class itself be a Fusion sequence).
I'm not sure how you can make this work in all possible cases. I know that there are cases where I can avoid creating a new vtable, but what about mpl::vector<incrementable<>, addable<> > -> mpl::vector<addable<>, incrementable<> > This should be legal, but I can't just take a pointer into the existing vtable.
I made a vector of objects of type fusion::map<fusion::pair<key1, int>, fusion::pair<key2, double>>, as well as a vector of type-erased maps (any_map<mpl::vector<mpl::pair<key1, int&>, mpl::pair<key2, double&>>>). I ran four benchmarks in which I summed key1 of: 1) the Fusion map directly 2) any_map 3) a temporary up-conversion to an any_map with only key1 4) a temporary up-conversion to an any_map with only key1, with the original held by reference
The case I had in mind with tests 3 & 4 was looping through a vector of any_maps and calling some function with a parameter type that requires an up cast.
The results were: Boost.TypeErasure My any_map 1 0.38 s 0.37 s 2 5.45 s 4.08 s 3 105.30 s 66.69 s 4 72.64 s 5.28 s
The difference in test 2 is due to my class being smaller (2 pointers/16 bytes for mine vs 32 bytes). If I increase the size of my class the difference disappears.
The large increase from test 2 to test 3 is expected since it requires memory allocation inside a tight loop, however test 4 shows this can be avoided by using static vtables and capturing references (at the cost of each cast adding an extra layer of virtual function calls).
I was also concerned about the memory cost of storing a large number of casted objects if they each have their own vtable, but haven't tested this. Presumably a mechanism could be implemented to share dynamically generated vtables that would also improve the tests above.
Use the any(any<Concept2, T2>, binding<Concept>) constructor. It allows you to convert the vtable once, and re-use it for all the anys.
Steven, do you have cases in mind where dynamic vtables might be more appropriate?
3. What is your evaluation of the documentation?
There has already been a lot of discussion about the documentation, so I'll just add a couple thoughts: I think relaxed_match should be prominently documented, as I found it unexpected that any cannot be default constructed otherwise. I would like see some examples that demonstrate how the more advanced features would be used in real code. The example Steven gave by email [1] for a sort function is a good candidate to be flushed out and included.
4. What is your evaluation of the potential usefulness of the library?
Very useful.
5. Did you try to use the library? With what compiler? Did you have any problems?
Yes, with VS10 and gcc 4.7.0. I only have a couple minor points:
I think default construction without relaxed_match could be a very common error, and special care should be taken to provide a clear error message.
In the attached benchmark code, key1 and key2 need to be defined rather than just declared so that the library can determine whether they are placeholder types. If this cannot be avoided, a note in the documents might be nice, along with whether it is acceptable to specialize is_placeholder.
That would be a problem. I'll try to change the implementation so that incomplete types are considered to be non-placeholders.
6. How much effort did you put into your evaluation? A glance? A quick reading? In-depth study?
At least a full day in total.
7. Are you knowledgeable about the problem domain?
Reasonably. The class I described above for type erasure of Fusion associative sequences is fairly similar in implementation to Steven's. I have also written type erasure classes to provide Python bindings for something like a Proto-ized version of Boost.Range.
In Christ, Steven Watanabe

On 08/02/2012 8:23 AM, Steven Watanabe wrote:
AMDG
First I want to thank Steven for all the work he has put into this library. I have a number of cases where this library will be very useful as is.
Please state clearly whether you think this library should be accepted as a Boost library.
Yes, with the conditions below about how references and conversions are handled. The basic features are very useful by themselves and are suitable for
On 08/01/2012 10:09 PM, Chapman, Alec wrote: the majority of uses. If possible, these could be accepted and added to Boost immediately, with a mini-review of the other components when ready.
1. What is your evaluation of the design?
For the basic use cases I think this is absolutely the right design. I like
the decision to use an MPL sequence of concepts, and that concepts can be composed this way as well. This alone meets the needs of the majority of use cases, including the various uses of type erasure already in Boost. I haven't had a need for something like multiple placeholders, but the design looks reasonable as well.
As far as I can tell, the current design for references would prevent me from
being able to use this library in a major portion of my code. I haven't received Steven's reply to my other email, so I'll just summarize the issue here.
I have some generic function that takes its argument by reference and might
modify it or make a copy of it. I want to provide a version that can be compiled separately. Something like:
template<class T> void f_impl(T& t) { ++t; T t2(t); ++t2; }
void f(any<...>& i) { f_impl(i); }
The function won't behave properly if it is instantiated with T = any<...,
_self&> (++t2 would increment t), so it has to be instantiated with T = any<..., _self>. But now calling the function is problematic:
void g(int& i) { ... // do something with i f(???); }
There is no way I can see to have f modify the original variable i. This is
similar to the sort example Steven gave in a previous email [1], but with arguments passed by reference.
I propose an overloaded constructor that signals that the underlying object
should be held by reference but copied when the any object is copied:
int i = 0; any<requirements> a(i, capture_reference); any<requirements> b(a); ++a; // i == 1 ++b; // i == 1 still, any_cast<int>(b) == 2
This has worked well in my own code and should be easy to implement. I think
this would also avoid the bug mentioned at the bottom of the references page of the documentation.
I don't think this is a good idea, as it would require an extra flag whether it's needed or not.
The way I implement it is with a separate vtable for references that is mostly the same as the vtable for values. The difference is that the reference vtable specifies that when the object is copied the value vtable should be used. This also works naturally for proxy references (like std::vector<bool>::reference) where the vtables really need to be completely different. This requires no increase in the object size and only generates the additional vtable if it is actually used. It looks like you have put a lot of thought into how to apply type erasure to generic algorithms, which was my motivation as well. Is what I am trying to do already possible with your library, or do you think that generic algorithms that take their arguments by reference aren't a common enough case?
2. What is your evaluation of the implementation?
Unfortunately I have only had time for a brief look at the code, so most of my understanding comes from reading the design notes section of the documentation. The main point I'm unsure of is the decision to dynamically generate vtables upon conversion.
I have my own class for applying type erasure to any type that models a Fusion associative sequence (templated by an MPL sequence of key/value pairs) that I use pretty heavily. It uses a custom vtable similar to Steven's library, except that the vtables are always statically generated even for upward and downward conversions. I was interested to compare the two approaches since I had considered dynamic vtables but never tried to implement them. I was pleased to find that only a few lines of code were required to make a similar class with Steven's library (although a bunch of boilerplate would still be needed to have the type-erased class itself be a Fusion sequence).
I'm not sure how you can make this work in all possible cases. I know that there are cases where I can avoid creating a new vtable, but what about
typedef any<mpl::vector<incrementable<>, addable<>>> any1; typedef any<mpl::vector<addable<>, incrementable<>>> any2; any1 x(...); any2 y(x); ++y; I allow an any to contain another any (if the concepts are different) and treat it the same as other types. For example, in the code above ++y would go through two virtual functions calls (the first calls increment of any1, which then calls a virtual function that actually does the increment).
mpl::vector<incrementable<>, addable<> > -> mpl::vector<addable<>, incrementable<> >
This should be legal, but I can't just take a pointer into the existing vtable.
I made a vector of objects of type fusion::map<fusion::pair<key1, int>, fusion::pair<key2, double>>, as well as a vector of type-erased maps (any_map<mpl::vector<mpl::pair<key1, int&>, mpl::pair<key2, double&>>>). I ran four benchmarks in which I summed key1 of: 1) the Fusion map directly 2) any_map 3) a temporary up-conversion to an any_map with only key1 4) a temporary up-conversion to an any_map with only key1, with the original held by reference
The case I had in mind with tests 3 & 4 was looping through a vector of any_maps and calling some function with a parameter type that requires an up cast.
The results were: Boost.TypeErasure My any_map 1 0.38 s 0.37 s 2 5.45 s 4.08 s 3 105.30 s 66.69 s 4 72.64 s 5.28 s
The difference in test 2 is due to my class being smaller (2 pointers/16 bytes for mine vs 32 bytes). If I increase the size of my class the difference disappears.
The large increase from test 2 to test 3 is expected since it requires memory allocation inside a tight loop, however test 4 shows this can be avoided by using static vtables and capturing references (at the cost of each cast adding an extra layer of virtual function calls).
I was also concerned about the memory cost of storing a large number of casted objects if they each have their own vtable, but haven't tested this. Presumably a mechanism could be implemented to share dynamically generated vtables that would also improve the tests above.
Use the any(any<Concept2, T2>, binding<Concept>) constructor. It allows you to convert the vtable once, and re-use it for all the anys.
That's good to know; I'll give it a try later. But wouldn't this only work if I know all the anys in the vector have the same underlying type (in my application they wouldn't)? I suppose I would have to build a map from type_info to binding and look up the binding for each element.
Steven, do you have cases in mind where dynamic vtables might be more appropriate?
3. What is your evaluation of the documentation?
There has already been a lot of discussion about the documentation, so I'll just add a couple thoughts: I think relaxed_match should be prominently documented, as I found it unexpected that any cannot be default constructed otherwise. I would like see some examples that demonstrate how the more advanced features would be used in real code. The example Steven gave by email [1] for a sort function is a good candidate to be flushed out and included.
4. What is your evaluation of the potential usefulness of the library?
Very useful.
5. Did you try to use the library? With what compiler? Did you have any problems?
Yes, with VS10 and gcc 4.7.0. I only have a couple minor points:
I think default construction without relaxed_match could be a very common error, and special care should be taken to provide a clear error message.
In the attached benchmark code, key1 and key2 need to be defined rather than just declared so that the library can determine whether they are placeholder types. If this cannot be avoided, a note in the documents might be nice, along with whether it is acceptable to specialize is_placeholder.
That would be a problem. I'll try to change the implementation so that incomplete types are considered to be non-placeholders.
6. How much effort did you put into your evaluation? A glance? A quick reading? In-depth study?
At least a full day in total.
7. Are you knowledgeable about the problem domain?
Reasonably. The class I described above for type erasure of Fusion associative sequences is fairly similar in implementation to Steven's. I have also written type erasure classes to provide Python bindings for something like a Proto-ized version of Boost.Range.
In Christ, Steven Watanabe
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

On 08/02/2012 8:23 AM, Steven Watanabe wrote:
Use the any(any<Concept2, T2>, binding<Concept>) constructor. It allows you to convert the vtable once, and re-use it for all the anys.
This constructor doesn't seem to work with gcc 4.7.0 because the copy constructor for binding doesn't exist: typedef mpl::vector<te::copy_constructible<>, te::addable<>, te::incrementable<>> add_inc; typedef mpl::vector<te::copy_constructible<>, te::incrementable<>> inc; te::any<add_inc> x(3); te::binding<inc> binding(te::binding_of(x), mpl::map<mpl::pair<te::_self, te::_self>>()); te::any<inc> y(x, binding); boost/type_erasure/any.hpp:444:9: error: use of deleted function âboost::type_erasure::binding<boost::mpl::vector<boost::type_erasure::copy_constructible<>, boost::type_erasure::incrementable<> > >::binding(const boost::type_erasure::binding<boost::mpl::vector<boost::type_erasure::copy_constructible<>, boost::type_erasure::incrementable<> > >&)â So I switched to VC10 for my benchmarks. This time I ran five tests summing a vector of anys by: 1) using the any elements directly. 2) up-converting using the default constructor (and capturing by reference) 3) up-converting using a single binding object 4) up-converting, looking up the binding in a map 5) up-converting, looking up the binding in a map that directly compares addresses of type_info objects The results were: 1) 0.73 s (no conversion) 2) 41.19 s (default conversion) 3) 3.80 s (single binding) 4) 31.52 s (slow, correct lookup) 5) 4.92 s (fast lookup) Using std::type_info::before for map comparisons adds a lot of overhead (4 vs 5). In practice I could get away with just comparing addresses and storing multiple copies. For me the relevant comparison is test 5 vs test 1, so conversion is still 7x slower than not converting (shared_ptr copying?). Recall from my previous test that using static vtables for conversions only added 30% overhead. My impression is that static vtables would be a better default behavior, possibly with the option for the user to specify a dynamic binding if they really need to optimize for many repeated function calls. The current behavior has huge overhead by default. Avoiding this requires the user to write a decent amount of code that is dangerous in terms of introducing undefined behavior (if the wrong binding is applied), or creating its own performance problems (the choice of map lookup), and even then the overhead still appears to be very large. Specifying a binding is essentially like casting from a void*. I like that this capability is provided, but I think the library should strive to keep the number of cases where it is required in user code to a minimum.
I propose an overloaded constructor that signals that the underlying object should be held by reference but copied when the any object is copied:
int i = 0; any<requirements> a(i, capture_reference); any<requirements> b(a); ++a; // i == 1 ++b; // i == 1 still, any_cast<int>(b) == 2
This has worked well in my own code and should be easy to implement. I think this would also avoid the bug mentioned at the bottom of the references page of the documentation.
I don't think this is a good idea, as it would require an extra flag whether it's needed or not.
Steven, do you have any thoughts on my previous reply to this? I have implemented this without any cost. I still feel that the features I've commented on are at too much of an experimental stage and that better solutions exist. My experience has been that erased types need to behave as similarly as possible to their underlying types, especially if they are to be used in generic code. I don't see the advantages of forcing the user to deal with what is essentially a parallel version of the type system for references when there is IMHO a more natural approach that carries no cost and is more generally useable. See my comments above as well. Conversions become less useful if they can't be used naturally like other types.

AMDG On 08/03/2012 06:36 PM, Chapman, Alec wrote:
On 08/02/2012 8:23 AM, Steven Watanabe wrote:
Use the any(any<Concept2, T2>, binding<Concept>) constructor. It allows you to convert the vtable once, and re-use it for all the anys.
This constructor doesn't seem to work with gcc 4.7.0 because the copy constructor for binding doesn't exist:
typedef mpl::vector<te::copy_constructible<>, te::addable<>, te::incrementable<>> add_inc; typedef mpl::vector<te::copy_constructible<>, te::incrementable<>> inc; te::any<add_inc> x(3); te::binding<inc> binding(te::binding_of(x), mpl::map<mpl::pair<te::_self, te::_self>>()); te::any<inc> y(x, binding);
boost/type_erasure/any.hpp:444:9: error: use of deleted function âboost::type_erasure::binding<boost::mpl::vector<boost::type_erasure::copy_constructible<>, boost::type_erasure::incrementable<> > >::binding(const boost::type_erasure::binding<boost::mpl::vector<boost::type_erasure::copy_constructible<>, boost::type_erasure::incrementable<> > >&)â
I think this is a bug in boost::shared_ptr.
So I switched to VC10 for my benchmarks. This time I ran five tests summing a vector of anys by: 1) using the any elements directly. 2) up-converting using the default constructor (and capturing by reference) 3) up-converting using a single binding object 4) up-converting, looking up the binding in a map 5) up-converting, looking up the binding in a map that directly compares addresses of type_info objects
The results were: 1) 0.73 s (no conversion) 2) 41.19 s (default conversion) 3) 3.80 s (single binding) 4) 31.52 s (slow, correct lookup) 5) 4.92 s (fast lookup)
Using std::type_info::before for map comparisons adds a lot of overhead (4 vs 5). In practice I could get away with just comparing addresses and storing multiple copies.
For me the relevant comparison is test 5 vs test 1, so conversion is still 7x slower than not converting (shared_ptr copying?). Recall from my previous test that using static vtables for conversions only added 30% overhead.
My impression is that static vtables would be a better default behavior, possibly with the option for the user to specify a dynamic binding if they really need to optimize for many repeated function calls. The current behavior has huge overhead by default. Avoiding this requires the user to write a decent amount of code that is dangerous in terms of introducing undefined behavior (if the wrong binding is applied), or creating its own performance problems (the choice of map lookup), and even then the overhead still appears to be very large. Specifying a binding is essentially like casting from a void*. I like that this capability is provided, but I think the library should strive to keep the number of cases where it is required in user code to a minimum.
- Wrapping the any increases the cost of all any operations. I'm not convinced that a 1-to-1 ratio of calls/conversions is realistic usage. - This particular example can (and will) be optimized to re-use the original vtable.
I propose an overloaded constructor that signals that the underlying object should be held by reference but copied when the any object is copied:
int i = 0; any<requirements> a(i, capture_reference); any<requirements> b(a); ++a; // i == 1 ++b; // i == 1 still, any_cast<int>(b) == 2
This has worked well in my own code and should be easy to implement. I think this would also avoid the bug mentioned at the bottom of the references page of the documentation.
I don't think this is a good idea, as it would require an extra flag whether it's needed or not.
Steven, do you have any thoughts on my previous reply to this? I have implemented this without any cost.
It can't be implemented without cost. There are really two options: 1) Add a flag 2) Manage the vtable /inside/ the dispatching layer. - Implies that every function knows about all the other functions. This extra coupling makes the more advanced features of the library hard and also increases code bloat. I'm assuming that you had already taken option (2), so it didn't impose any additional cost. Also, - It can't handle const references correctly - It doesn't work with type erased function functions that need to return a reference I think such a feature is too limited for the costs it imposes.
I still feel that the features I've commented on are at too much of an experimental stage and that better solutions exist. My experience has been that erased types need to behave as similarly as possible to their underlying types, especially if they are to be used in generic code. I don't see the advantages of forcing the user to deal with what is essentially a parallel version of the type system for references when there is IMHO a more natural approach that carries no cost and is more generally useable. See my comments above as well. Conversions become less useful if they can't be used naturally like other types.
In Christ, Steven Watanabe

Lorenzo, Thank you for managing the review, and I apologize that this reply along with Steven's previous message have gone past the review period. I understand if you don't have the time to continue following the discussion. I'll just summarize that I still feel that some of the advanced features of this library are currently too experimental, and that I think that Steven's reluctance so far to acknowledge their limitations or consider alternatives makes me doubt that these features will reach a mature state unless this is a condition for their acceptance (although I still believe that the library would be very useful without these features). More detailed responses are below. On 08/04/2012 1:15 PM, Steven Watanabe wrote:
I think this is a bug in boost::shared_ptr. Okay. Can you file a bug report?
- Wrapping the any increases the cost of all any operations. I'm not convinced that a 1-to-1 ratio of calls/conversions is realistic usage.
I'm aware there is a cost and I think a reasonable debate can be had about which is the better default behavior. Is wrapping difficult to provide as an option? The test I gave is realistic usage for me. Here's a real example: any_interval1 (implemented using my any_map) has lower_bound and upper_bound functions. any_interval2 contains additional functions. I have a vector of any_interval2 and want to use std::count_if with a separately compiled predicate (which to give one example might test whether the interval length is greater than some threshold). The predicate accepts const any_interval1&. Here the number of calls is very low, the predicate logic is simple, and the current conversion overhead really would be the dominant cost. There are many other interval calculations I need to do where this is the case. Keep in mind that with a 30% function call cost vs a 700% conversion cost, you would need 23 function calls to break even. Moreover, the user has to write a decent amount of code to choose the correct binding; the default behavior has 5600% overhead.
- This particular example can (and will) be optimized to re-use the original vtable.
Okay, I'll reevaluate when it is ready. In my test, just doing a map lookup added 1.1s (test 5 vs 4) or 150% overhead, so this would seem to be a lower bound.
I propose an overloaded constructor that signals that the underlying object should be held by reference but copied when the any object is copied:
int i = 0; any<requirements> a(i, capture_reference); any<requirements> b(a); ++a; // i == 1 ++b; // i == 1 still, any_cast<int>(b) == 2
This has worked well in my own code and should be easy to implement. I think this would also avoid the bug mentioned at the bottom of the references page of the documentation.
I don't think this is a good idea, as it would require an extra flag whether it's needed or not.
Steven, do you have any thoughts on my previous reply to this? I have implemented this without any cost.
It can't be implemented without cost. There are really two options: 1) Add a flag 2) Manage the vtable /inside/ the dispatching layer. - Implies that every function knows about all the other functions. This extra coupling makes the more advanced features of the library hard and also increases code bloat.
I'm assuming that you had already taken option (2), so it didn't impose any additional cost.
I don't think I understand. Are you saying that the only way to give the copy constructor access to the vtable is to also give every other function access as well? It's true that I have special logic for my copy constructor, but so does your library. I imagine your copy constructor at line 211 of any.hpp looking something like: any(const any& other) : table(other.table.copy()), data(::boost::type_erasure::call(constructible<T(const T&)>(), other)) {} Or if you want to avoid the extra virtual function call: any(const any& other) { other.table.clone(table, data); } I think you'll need to do something like this anyway for certain optimizations. See my comment near the end about dynamic vtables. Can you be more specific about which advanced features would be harder? I have implemented many of them and haven't run into any difficulties.
Also, - It can't handle const references correctly
You can do: const int x = 0; const any<...> y(x, as_const_reference); const any<...> y(const_cast<int&>(x), as_reference); // if you want to make it stand out more any<...> w(x, as_const_reference); // compiles but incorrect This would internally hold x in a (mutable) void*, but it would be cast back to const int* for the const member functions. Not declaring the any as const would be a problem, but you document a similar hole in your system: const any<incrementable<>, _self&> z(x); any<incrementable<>, _self&> w(z); // compiles but incorrect To me this seems harder to detect since you have to look back to the declaration of z to know there is a problem.
- It doesn't work with type erased function functions that need to return a reference
My experience has been that in most cases you can return just any<...>&. Consider std::max, for example. In the other cases I have used a proxy that holds an any and upon copying constructs the any to capture by reference. This behaves similarly to your approach. The most notable need I've had is for writing an any_iterator that would allow any_iterator<any<addable<>>> to be constructed from any_iterator<int>. I've tried two different approaches, both of which have drawbacks: 1) Make the iterator's reference type be any<addable<>, _self&> and value_type be any<addable<>>, which of course limits it to being an input iterator. 2) Have the iterator store an any<addable<>>, capturing the underlying value by reference with my technique. This avoids the problem with the reference type, but increases the iterator size and limits the referent lifetime to the lifetime of the iterator. This also limits it to an input iterator: https://svn.boost.org/trac/boost/ticket/2640 I'd be interested to hear if you have better ideas on how to approach this.
I think such a feature is too limited for the costs it imposes.
This is required for type erased functions that take references. There are plenty of these in Boost and my own code. Why do you believe this is a limited case while you point to a subset of functions returning a reference as a limitation of my approach? All I can say is that in my applications this really is needed. I'm having a hard time understanding why you believe certain features justify their costs while others do not. Perhaps this would be clearer to me if you gave some examples of how you see the more advanced features being used. As another example, I'm not terribly opposed to dynamic vtables, but this does come at a cost and has not been necessary for me. In addition to making it more difficult for the user to do efficient conversions, it doubles the size of the any (I didn't see a reply to an earlier email about the binding size: http://permalink.gmane.org/gmane.comp.lib.boost.devel/233002). I can see that this cost could be optimized out by storing the reference count in the vtable itself, but this requires exactly the same coupling of the vtable to the constructor/destructor that you oppose for my reference technique. Is this an optimization that you plan on doing?
I still feel that the features I've commented on are at too much of an experimental stage and that better solutions exist. My experience has been that erased types need to behave as similarly as possible to their underlying types, especially if they are to be used in generic code. I don't see the advantages of forcing the user to deal with what is essentially a parallel version of the type system for references when there is IMHO a more natural approach that carries no cost and is more generally useable. See my comments above as well. Conversions become less useful if they can't be used naturally like other types.
In Christ, Steven Watanabe
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

AMDG On 08/05/2012 12:22 PM, Chapman, Alec wrote:
Lorenzo, Thank you for managing the review, and I apologize that this reply along with Steven's previous message have gone past the review period. I understand if you don't have the time to continue following the discussion.
I'll just summarize that I still feel that some of the advanced features of this library are currently too experimental, and that I think that Steven's reluctance so far to acknowledge their limitations or consider alternatives makes me doubt that these features will reach a mature state unless this is a condition for their acceptance (although I still believe that the library would be very useful without these features).
More detailed responses are below.
On 08/04/2012 1:15 PM, Steven Watanabe wrote:
I think this is a bug in boost::shared_ptr. Okay. Can you file a bug report?
IIRC, this was already reported and fixed.
- Wrapping the any increases the cost of all any operations. I'm not convinced that a 1-to-1 ratio of calls/conversions is realistic usage.
I'm aware there is a cost and I think a reasonable debate can be had about which is the better default behavior.
Is wrapping difficult to provide as an option?
I really don't like the idea of providing options to control the implementation details of the library. I'm certainly not going to consider it until after all the easier optimizations are in place.
The test I gave is realistic usage for me. Here's a real example:
any_interval1 (implemented using my any_map) has lower_bound and upper_bound functions. any_interval2 contains additional functions. I have a vector of any_interval2 and want to use std::count_if with a separately compiled predicate (which to give one example might test whether the interval length is greater than some threshold). The predicate accepts const any_interval1&. Here the number of calls is very low, the predicate logic is simple, and the current conversion overhead really would be the dominant cost. There are many other interval calculations I need to do where this is the case.
Keep in mind that with a 30% function call cost vs a 700% conversion cost, you would need 23 function calls to break even. Moreover, the user has to write a decent amount of code to choose the correct binding; the default behavior has 5600% overhead.
- This particular example can (and will) be optimized to re-use the original vtable.
Okay, I'll reevaluate when it is ready. In my test, just doing a map lookup added 1.1s (test 5 vs 4) or 150% overhead, so this would seem to be a lower bound.
The overhead of the conversion once I implement this optimization will be zero.
<snip> I don't think I understand. Are you saying that the only way to give the copy constructor access to the vtable is to also give every other function access as well? It's true that I have special logic for my copy constructor, but so does your library.
Not really. The only special handling of constructors is that any knows how to call them. There's nothing special in the dispatching layer. Besides, it isn't just constructors. Any function that returns an any would need to know about the vtable.
I imagine your copy constructor at line 211 of any.hpp looking something like:
any(const any& other) : table(other.table.copy()), data(::boost::type_erasure::call(constructible<T(const T&)>(), other)) {}
It's just table(other.table). Copying the table just copies pointers right now.
Or if you want to avoid the extra virtual function call:
any(const any& other) { other.table.clone(table, data); }
I think you'll need to do something like this anyway for certain optimizations. See my comment near the end about dynamic vtables.
Can you be more specific about which advanced features would be harder? I have implemented many of them and haven't run into any difficulties.
In particular, it's tricky to preserve the binding interface unchanged. I'm not sure how far any changes would ripple, since binding is used everywhere. Also, and more importantly, if anything in the vtable has to know the whole vtable, it's impossible to implement the optimization I mentioned earlier of just offsetting the vtable pointer for conversions.
Also, - It can't handle const references correctly
You can do: const int x = 0; const any<...> y(x, as_const_reference); const any<...> y(const_cast<int&>(x), as_reference); // if you want to make it stand out more any<...> w(x, as_const_reference); // compiles but incorrect
Exactly. This is a major hole in the type system, which I'm not willing to open.
This would internally hold x in a (mutable) void*, but it would be cast back to const int* for the const member functions. Not declaring the any as const would be a problem, but you document a similar hole in your system:
const any<incrementable<>, _self&> z(x); any<incrementable<>, _self&> w(z); // compiles but incorrect
To me this seems harder to detect since you have to look back to the declaration of z to know there is a problem.
Where do I say that this is incorrect? There's nothing wrong with this code.
- It doesn't work with type erased function functions that need to return a reference
My experience has been that in most cases you can return just any<...>&. Consider std::max, for example.
That only works for functions that return one of their arguments.
In the other cases I have used a proxy that holds an any and upon copying constructs the any to capture by reference. This behaves similarly to your approach.
The most notable need I've had is for writing an any_iterator that would allow any_iterator<any<addable<>>> to be constructed from any_iterator<int>. I've tried two different approaches, both of which have drawbacks:
1) Make the iterator's reference type be any<addable<>, _self&> and value_type be any<addable<>>, which of course limits it to being an input iterator.
2) Have the iterator store an any<addable<>>, capturing the underlying value by reference with my technique. This avoids the problem with the reference type, but increases the iterator size and limits the referent lifetime to the lifetime of the iterator. This also limits it to an input iterator: https://svn.boost.org/trac/boost/ticket/2640
I'd be interested to hear if you have better ideas on how to approach this.
I don't. I think generally anything that needs to use proxy references needs to be implemented directly instead of using a function template.
I think such a feature is too limited for the costs it imposes.
This is required for type erased functions that take references. There are plenty of these in Boost and my own code. Why do you believe this is a limited case while you point to a subset of functions returning a reference as a limitation of my approach? All I can say is that in my applications this really is needed.
I understand the problem, but I'm not willing to take a half-way approach like this. As far as I am concerned, references to const have to be supported and making it possible to cast away const silently is not an option. I'm less concerned about type-erased functions that return references, but this case makes proxy references necessary, so capturing references in a regular any would have to be an additional feature rather than a complete replacement for proxy references. I've also considered something like this: reference<any<C> > ref(x1); reference<const any<C> > cref(x2); ref.get(); // any<C>& cref.get(); // const any<C>& I haven't thought through all the consequences of this yet, but it at least makes it possible to handle const references correctly and also imposes no cost when it isn't used.
I'm having a hard time understanding why you believe certain features justify their costs while others do not. Perhaps this would be clearer to me if you gave some examples of how you see the more advanced features being used.
As another example, I'm not terribly opposed to dynamic vtables, but this does come at a cost and has not been necessary for me.
Dynamic vtables aren't exactly a feature. They're an implementation detail. They're the easiest way to implement the interface that I wanted.
In addition to making it more difficult for the user to do efficient conversions, it doubles the size of the any (I didn't see a reply to an earlier email about the binding size: http://permalink.gmane.org/gmane.comp.lib.boost.devel/233002).
I can reduce binding to two pointers instead of three fairly easily.
I can see that this cost could be optimized out by storing the reference count in the vtable itself, but this requires exactly the same coupling of the vtable to the constructor/destructor that you oppose for my reference technique.
No it doesn't. The vtable reference count can be handled independently from anything else.
Is this an optimization that you plan on doing?
I considered this, but getting it right is tricky. - The reference count must be atomic. - The reference count must support static initialization. - Adding a reference count limits sharing of converted vtables. In Christ, Steven Watanabe

Hello Alec, On Sun, Aug 5, 2012 at 3:22 PM, Chapman, Alec <archapm@fas.harvard.edu> wrote:
Lorenzo, Thank you for managing the review, and I apologize that this reply along with Steven's previous message have gone past the review period. I understand if you don't have the time to continue following the discussion.
Thanks to you for submitting a review and participating in the discussions!
I'll just summarize that I still feel that some of the advanced features of this library are currently too experimental, and that I think that Steven's reluctance so far to acknowledge their limitations or consider alternatives makes me doubt that these features will reach a mature state unless this is a condition for their acceptance (although I still believe that the library would be very useful without these features).
I understand your "yes" was conditional and I understand the topics you have been discussing with Steven. I will of course consider your review together with all other reviews in my report. Regards, --Lorenzo
More detailed responses are below.
On 08/04/2012 1:15 PM, Steven Watanabe wrote:
I think this is a bug in boost::shared_ptr. Okay. Can you file a bug report?
- Wrapping the any increases the cost of all any operations. I'm not convinced that a 1-to-1 ratio of calls/conversions is realistic usage.
I'm aware there is a cost and I think a reasonable debate can be had about which is the better default behavior.
Is wrapping difficult to provide as an option?
The test I gave is realistic usage for me. Here's a real example:
any_interval1 (implemented using my any_map) has lower_bound and upper_bound functions. any_interval2 contains additional functions. I have a vector of any_interval2 and want to use std::count_if with a separately compiled predicate (which to give one example might test whether the interval length is greater than some threshold). The predicate accepts const any_interval1&. Here the number of calls is very low, the predicate logic is simple, and the current conversion overhead really would be the dominant cost. There are many other interval calculations I need to do where this is the case.
Keep in mind that with a 30% function call cost vs a 700% conversion cost, you would need 23 function calls to break even. Moreover, the user has to write a decent amount of code to choose the correct binding; the default behavior has 5600% overhead.
- This particular example can (and will) be optimized to re-use the original vtable.
Okay, I'll reevaluate when it is ready. In my test, just doing a map lookup added 1.1s (test 5 vs 4) or 150% overhead, so this would seem to be a lower bound.
I propose an overloaded constructor that signals that the underlying object should be held by reference but copied when the any object is copied:
int i = 0; any<requirements> a(i, capture_reference); any<requirements> b(a); ++a; // i == 1 ++b; // i == 1 still, any_cast<int>(b) == 2
This has worked well in my own code and should be easy to implement. I think this would also avoid the bug mentioned at the bottom of the references page of the documentation.
I don't think this is a good idea, as it would require an extra flag whether it's needed or not.
Steven, do you have any thoughts on my previous reply to this? I have implemented this without any cost.
It can't be implemented without cost. There are really two options: 1) Add a flag 2) Manage the vtable /inside/ the dispatching layer. - Implies that every function knows about all the other functions. This extra coupling makes the more advanced features of the library hard and also increases code bloat.
I'm assuming that you had already taken option (2), so it didn't impose any additional cost.
I don't think I understand. Are you saying that the only way to give the copy constructor access to the vtable is to also give every other function access as well? It's true that I have special logic for my copy constructor, but so does your library. I imagine your copy constructor at line 211 of any.hpp looking something like:
any(const any& other) : table(other.table.copy()), data(::boost::type_erasure::call(constructible<T(const T&)>(), other)) {}
Or if you want to avoid the extra virtual function call:
any(const any& other) { other.table.clone(table, data); }
I think you'll need to do something like this anyway for certain optimizations. See my comment near the end about dynamic vtables.
Can you be more specific about which advanced features would be harder? I have implemented many of them and haven't run into any difficulties.
Also, - It can't handle const references correctly
You can do: const int x = 0; const any<...> y(x, as_const_reference); const any<...> y(const_cast<int&>(x), as_reference); // if you want to make it stand out more any<...> w(x, as_const_reference); // compiles but incorrect
This would internally hold x in a (mutable) void*, but it would be cast back to const int* for the const member functions. Not declaring the any as const would be a problem, but you document a similar hole in your system:
const any<incrementable<>, _self&> z(x); any<incrementable<>, _self&> w(z); // compiles but incorrect
To me this seems harder to detect since you have to look back to the declaration of z to know there is a problem.
- It doesn't work with type erased function functions that need to return a reference
My experience has been that in most cases you can return just any<...>&. Consider std::max, for example. In the other cases I have used a proxy that holds an any and upon copying constructs the any to capture by reference. This behaves similarly to your approach.
The most notable need I've had is for writing an any_iterator that would allow any_iterator<any<addable<>>> to be constructed from any_iterator<int>. I've tried two different approaches, both of which have drawbacks:
1) Make the iterator's reference type be any<addable<>, _self&> and value_type be any<addable<>>, which of course limits it to being an input iterator.
2) Have the iterator store an any<addable<>>, capturing the underlying value by reference with my technique. This avoids the problem with the reference type, but increases the iterator size and limits the referent lifetime to the lifetime of the iterator. This also limits it to an input iterator: https://svn.boost.org/trac/boost/ticket/2640
I'd be interested to hear if you have better ideas on how to approach this.
I think such a feature is too limited for the costs it imposes.
This is required for type erased functions that take references. There are plenty of these in Boost and my own code. Why do you believe this is a limited case while you point to a subset of functions returning a reference as a limitation of my approach? All I can say is that in my applications this really is needed.
I'm having a hard time understanding why you believe certain features justify their costs while others do not. Perhaps this would be clearer to me if you gave some examples of how you see the more advanced features being used.
As another example, I'm not terribly opposed to dynamic vtables, but this does come at a cost and has not been necessary for me. In addition to making it more difficult for the user to do efficient conversions, it doubles the size of the any (I didn't see a reply to an earlier email about the binding size: http://permalink.gmane.org/gmane.comp.lib.boost.devel/233002).
I can see that this cost could be optimized out by storing the reference count in the vtable itself, but this requires exactly the same coupling of the vtable to the constructor/destructor that you oppose for my reference technique. Is this an optimization that you plan on doing?
I still feel that the features I've commented on are at too much of an experimental stage and that better solutions exist. My experience has been that erased types need to behave as similarly as possible to their underlying types, especially if they are to be used in generic code. I don't see the advantages of forcing the user to deal with what is essentially a parallel version of the type system for references when there is IMHO a more natural approach that carries no cost and is more generally useable. See my comments above as well. Conversions become less useful if they can't be used naturally like other types.
participants (12)
-
Andrey Semashev
-
Chapman, Alec
-
Christophe Henry
-
Eric Niebler
-
Fabio Fracassi
-
Julien Nitard
-
Larry Evans
-
Lorenzo Caminiti
-
Paul A. Bristow
-
Pete Bartlett
-
Sebastian Redl
-
Steven Watanabe