Formal Review Request: TypeErasure

AMDG I'd like to request a formal review of the TypeErasure library that I've posted about several times before here. The TypeErasure library is a generalization of boost::any and boost::function. It allows easy composition of arbitrary type erased operators. As an example of basic usage, we can simulate Boost.Any: any<mpl::vector<copy_constructible<>, typeid_<> > > x(10); int i = any_cast<int>(x); // i == 10 The library is available in the Boost Sandbox at http://svn.boost.org/svn/boost/sandbox/type_erasure/ You can download archives with pre-built documentation from http://sourceforge.net/projects/steven-watanabe.u/files/ Online documentation can be found here: http://steven_watanabe.users.sourceforge.net/type_erasure/libs/type_erasure/ In Christ, Steven Watanabe

On 05/22/12 15:34, Steven Watanabe wrote: [snip]
I've done a somewhat brief review of just the online documentation. I'll try to do a more complete review later. Here's my current comments: * Virtual functions do not *require* dynamic memory: The page: http://steven_watanabe.users.sourceforge.net/type_erasure/libs/type_erasure/... says: C++ provides runtime polymorphism through virtual functions. ... They require dynamic memory management. However, I don't think dynamic memory is required because just references to stack locations or pointers pointing to stack locations could be used to take advantage of virtual functions. * 'Operation' term used before defined: The page: http://steven_watanabe.users.sourceforge.net/type_erasure/libs/type_erasure/... says: Operations can have more than one any argument. before any definition of 'Operation' occurs. Only by seeing the: addable<> argument to the mpl::vector does the term 'Operation' begin to make sense, and then the reader has to guess that all the mpl::vector arguments are operations, and that all the previous 'operations' were unary. However, I'm still not sure that's correct. It would help if some link to 'Operation' defintion were provided everywhere that the term, 'Operation', occurs. Maybe, where the term, 'opeation" occurs, there should be a link to: http://steven_watanabe.users.sourceforge.net/type_erasure/libs/type_erasure/... * 'match' term used before defined: The page: http://steven_watanabe.users.sourceforge.net/type_erasure/libs/type_erasure/... says: The types of the arguments must match or the behavior is undefined. before any definition of 'match' or 'arguments' occurs. I'm just guessing from the arguments to x and y, that 'arguments' means arguments to the any CTOR, but then the CTOR argument to z is an expression; hence, I'm guessing that that expression is allowed by the addable<> template argument to the mpl::vector arg to the any_type. * public construct/copy/destruct item 1 undefined T: The page: http://steven_watanabe.users.sourceforge.net/type_erasure/libs/type_erasure/... under: 1. template<typename U> explicit any(const U & data); says: oncept must not refer to any placeholder besides T. However, T is not defined (or the definition is not obvious). * Mistitled Assign operator description: The page: http://steven_watanabe.users.sourceforge.net/type_erasure/libs/type_erasure/... says: any public construct/copy/destruct 1. any& operator=(const any & other); Assigns to an any. Shouldn't this be retitled something like: any public assign operator 1. any& operator=(const any & other); Assigns to an any. * Misplaced copy CTOR description: The page: http://steven_watanabe.users.sourceforge.net/type_erasure/libs/type_erasure/... says: any public member functions 1. template<typename U> any(U & arg); This this is a constructor, not a member function, shouldn't this be moved to: http://steven_watanabe.users.sourceforge.net/type_erasure/libs/type_erasure/... and placed under: any public construct/copy/destruct Also, all of the other items seem to be CTOR descriptions also; hence, shouldn't they be similarly moved? * Typo "more general" in 'Design Notes.Constructors': The page: http://steven_watanabe.users.sourceforge.net/type_erasure/libs/type_erasure/... says: The last kind of constructor "upcasts" from one concept to another more general more general concept. with the typo of duplicated "more general". -regards, Larry

AMDG On 05/23/2012 07:31 AM, Larry Evans wrote:
In theory, you're correct. It isn't strictly necessary. However, in practice, the cases where you can avoid new are the exception, rather than the rule. I don't want to complicate the introduction by being overly pedantic.
"Operation" is not defined, because it doesn't have any special formal meaning in my library. Just take it in it's usual sense of something that you can do with an any.
The very next sentence explicitly contrasts *binary* addition with increment. I don't see how I can make this more clear without a lot of formalism that would just be off-putting for most people.
arguments in this case means the arguments of +. types means the types that the anys hold. match just means that they're consistent with what addable<> expects i.e. the same type. This is the *tutorial*. I don't want to put all the technical details here. If you come away from this sentence with the idea that adding an any that stores an int to an any that stores a string is bad, it's clear enough for my purposes.
T is a class template parameter. I'm not sure what I can do to make it more obvious. Anyway, this comment is no longer correct.
* Mistitled Assign operator description: * Misplaced copy CTOR description:
This is all auto-generated by doxygen/boostbook. I'll see if I can figure out what went wrong.
Fixed. In Christ, Steven Watanabe

Steven Watanabe-4 wrote
Hello Steven, I quickly read the docs (see a couple of minor comments below). I'll compile and play with the examples this weekend. I'm interested in the library. If you are looking for a Review Manager, I'd like to volunteer if that's OK with you and the Review Wizards. Minor Comments 1) It'd be nice to have a motivating example up front (something not trivial but not as complex as the range formatter either). 2) Why placeholders _a, _b, ... instead of _1, _2, ...? 3) I found the syntax of concept_interface difficult to understand... for example, Base looks arbitrary. But then it's very nice to be able to c.push_back(10) instead of call(...) 4) Same for overloaded concept_interface where both Base and Enable look arbitrary. (I wonder if macros could generate some of the boiler-plate code for the overload.) 5) There's a FIXME in the Reserved Identifier section. --Lorenzo -- View this message in context: http://boost.2283326.n4.nabble.com/Formal-Review-Request-TypeErasure-tp46303... Sent from the Boost - Dev mailing list archive at Nabble.com.

AMDG On 05/23/2012 07:12 PM, lcaminiti wrote:
It sounds good to me. You're a library author, so I'm sure the Review Wizard will be okay with it.
What about something based on any_printable? typedef any< mpl::vector< copy_constructible<>, typeid_<>, ostreamable<> > > any_printable; I believe that this has come up somewhere. ("Why can't I print boost::any to std::cout?") Is this about the level of complexity that you're thinking?
2) Why placeholders _a, _b, ... instead of _1, _2, ...?
Because they represent named arguments, not positional arguments. I used _1, _2, etc. in an earlier version of the library, and some people were confused about how they related to _1, _2 in Boost.Bind and Boost.MPL
Did you look at the doxygen/boostbook reference for concept_interface? Is that sufficiently clear? I realize that my descriptions in the examples are often a bit sparse. Would something like this be clearer? Do you think it's too verbose? in custom.cpp: This works, but we'd really like to call `c.push_back(10)`. We can add members to __any by specializing __concept_interface. The first argument is `push_back`, since we want to inject a member into every __any that uses the `push_back` concept. The second argument, Base, is used by the library to chain multiple uses of __concept_interface together. We have to inherit from it publicly. Other than that we can ignore it. The third argument is the placeholder that represents this any. If someone used `push_back<_c, _b>`, we only want to insert a `push_back` member in the container, not the value type. Thus, the third argument is the container placeholder. in overload.cpp: This uses SFINAE to detect whether a using declaration is needed. Note that the fourth argument of __concept_interface is a dummy parameter which is always void and is intended to be used for SFINAE.
5) There's a FIXME in the Reserved Identifier section.
Yeah. I haven't come up with any scheme that covers all the identifiers I want to be able to use without being too broad. I suppose I can always ignore the issue until problems surface like everyone else. In Christ, Steven Watanabe

Steven Watanabe-4 wrote
Even somethings a bit more complex but printable is OK too. I will use the lib in some use cases that I ran into the past and let you know if I find a nice example not too complex (I don't remember what the use cases were but they were about type erasure).
Got it. I missed that from the docs...
Maybe I used the wrong term in saying "arbitrary". I meant it looks arbitrary to users because it essentially just exposes a library implementation detail (but it's clear it's a "special" base that the lib needs you to put there for the implementation to work). There is probably no way for getting rid of Base, Enable, etc but to use macros. I think macros would be a nice addition as an alternative to define concepts in your lib. I'll see what macro syntax could be implemented (especially with variadic macros, we can do a lot of nice stuff).
Thanks. --Lorenzo -- View this message in context: http://boost.2283326.n4.nabble.com/Formal-Review-Request-TypeErasure-tp46303... Sent from the Boost - Dev mailing list archive at Nabble.com.

Maybe I used the wrong term in saying "arbitrary". I meant it looks arbitrary to users because it essentially just exposes a library implementation detail (but it's clear it's a "special" base that the lib needs you to put there for the implementation to work). There is probably no way for getting rid of Base, Enable, etc but to use macros. I think macros would be a nice addition as an alternative to define concepts in your lib. I'll see what macro syntax could be implemented (especially with variadic macros, we can do a lot of nice stuff). If there was a desire for it, /maybe/ the following macro could be implemented: BOOST_TYPE_ERASURE_CONCEPT( concept (push_back_callable) ( class C, typename T ) ( void member_body(C, push_back) ( (T const&) value ) ; ) ) std::vector<int> v; boost::type_erasure::any< boost::type_erasure::push_back_callable<_self, int> , _self&
c(v); c.push_back(10);
The macro would expand to code equivalent to this (from TypeErasure docs): namespace boost { namespace type_erasure { template< class C, typename T> struct push_back_callable { static void apply ( C& obj, T const& value ) { obj.push_back(value); } }; template< class C, typename T, class Base > struct concept_interface< push_back_callable<C, T>, Base, C > : Base { void push_back ( typename rebind<Base, T const&>::type value ) { call(push_back_callable<C, T>(), *this, value); } }; }} // namespace I haven't tried to implement the macro so I can't be sure if it can be done. Also, I'd have to study your TypeErasure lib more to make sure the macro is general enough to handle all cases where TypeErasure concepts can be defined. --Lorenzo -- View this message in context: http://boost.2283326.n4.nabble.com/Formal-Review-Request-TypeErasure-tp46303... Sent from the Boost - Dev mailing list archive at Nabble.com.

lcaminiti wrote
Why this does not compile? #include <boost/type_erasure/any.hpp> #include <boost/type_erasure/builtin.hpp> #include <boost/type_erasure/operators.hpp> #include <boost/mpl/vector.hpp> #include <iostream> struct display { public: template< typename T > explicit display ( T const& obj ) : obj_(obj) {} public: void print ( void ) { std::cout << obj_ << std::endl; } private: boost::type_erasure::any< boost::mpl::vector< boost::type_erasure::copy_constructible<> , boost::type_erasure::typeid_<> , boost::type_erasure::ostreamable<> > > obj_; }; int main ( void ) { display i(123), s("abc"); i.print(); s.print(); return 0; } Using GCC 4.5.3 on Cygwin with Boost 1.50.0 Beta 1, I get: In file included from ../boost/type_erasure/detail/access.hpp:14:0, from ../boost/type_erasure/any.hpp:34, from 01.cpp:2: ../boost/type_erasure/detail/storage.hpp: In constructor ‘boost::type_erasure::detail::storage::storage(const T&) [with T = char [4]]’: ../boost/type_erasure/any.hpp:167:22: instantiated from ‘boost::type_erasure::any<Concept, T>::any(const U&) [with U = char [4], Concept = boost::mpl::vector<boost::type_erasure::copy_constructible<>, boost::type_erasure::typeid_<>, boost::type_erasure::ostreamable<> >, T = boost::type_erasure::_self]’ 01.cpp:11:49: instantiated from ‘display::display(const T&) [with T = char [4]]’ 01.cpp:26:28: instantiated from here ../boost/type_erasure/detail/storage.hpp:24:44: error: ISO C++ forbids initialization in array new In file included from ../../../1_50_0_beta1.cygwin/boost/preprocessor/iteration/detail/iter/forward1.hpp:52:0, from ../boost/type_erasure/detail/rebind_placeholders.hpp:127, from ../boost/type_erasure/detail/normalize.hpp:31, from ../boost/type_erasure/any.hpp:36, from 01.cpp:2: ../boost/type_erasure/detail/rebind_placeholders.hpp: At global scope: ../boost/type_erasure/detail/rebind_placeholders.hpp: In instantiation of ‘boost::type_erasure::detail::rebind_placeholders_in_argument<boost::type_erasure::_self(const boost::type_erasure::_self&), boost::mpl::map<boost::mpl::pair<boost::type_erasure::_self, char [4]>, 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, mpl_::na, mpl_::na> >’: ../boost/type_erasure/detail/rebind_placeholders.hpp:149:63: instantiated from ‘boost::type_erasure::detail::rebind_placeholders<boost::type_erasure::constructible<boost::type_erasure::_self(const boost::type_erasure::_self&)>, boost::mpl::map<boost::mpl::pair<boost::type_erasure::_self, char [4]>, 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, mpl_::na, mpl_::na> >’ ../boost/type_erasure/detail/adapt_to_vtable.hpp:53:7: instantiated from ‘boost::type_erasure::detail::rebind_placeholders<boost::type_erasure::detail::vtable_adapter<boost::type_erasure::constructible<boost::type_erasure::_self(const boost::type_erasure::_self&)>, boost::type_erasure::detail::storage(const boost::type_erasure::detail::storage&)>, boost::mpl::map<boost::mpl::pair<boost::type_erasure::_self, char [4]>, 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, mpl_::na, mpl_::na> >’ ../../../1_50_0_beta1.cygwin/boost/mpl/aux_/has_type.hpp:20:1: instantiated from ‘const bool boost::mpl::aux::has_type<boost::type_erasure::detail::rebind_placeholders<boost::type_erasure::detail::vtable_adapter<boost::type_erasure::constructible<boost::type_erasure::_self(const boost::type_erasure::_self&)>, boost::type_erasure::detail::storage(const boost::type_erasure::detail::storage&)>, boost::mpl::map<boost::mpl::pair<boost::type_erasure::_self, char [4]>, 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, mpl_::na, mpl_::na> >, mpl_::bool_<true>
Thanks, --Lorenzo -- View this message in context: http://boost.2283326.n4.nabble.com/Formal-Review-Request-TypeErasure-tp46303... Sent from the Boost - Dev mailing list archive at Nabble.com.

lcaminiti wrote
Is this a sensible use of this library or is there a much better way to do the following? #include <boost/type_erasure/any.hpp> #include <boost/type_erasure/builtin.hpp> #include <boost/type_erasure/operators.hpp> #include <boost/mpl/vector.hpp> #include <iostream> struct display { public: template< typename T > explicit display ( T const& obj ) : obj_(obj) {} public: void print ( void ) { std::cout << obj_ << std::endl; } public: boost::type_erasure::any< boost::mpl::vector< boost::type_erasure::copy_constructible<> , boost::type_erasure::typeid_<> , boost::type_erasure::ostreamable<> > > obj_; }; int main ( void ) { display i(-1), d('x'); i.print(); d.print(); return 0; } I had to do something like this in the past where: 1. I didn't want display to be a template but I wanted it to handle generic types as passed to the constructor. 2. I knew a-priori the operations that display needed from T (e.g., operator<<). 3. I had to store the object of the generic type T so the operation on T could be performed later (e.g., by print). I needed to do this as an implementation detail so the original problem I was trying to solve has little relevance in general. But I wanted to ask if using this library is a reasonable approach to do the above of it there are better ways. Regards, --Lorenzo -- View this message in context: http://boost.2283326.n4.nabble.com/Formal-Review-Request-TypeErasure-tp46303... Sent from the Boost - Dev mailing list archive at Nabble.com.

AMDG On 06/18/2012 12:25 PM, lcaminiti wrote:
This is a textbook use case for the library. Use TypeErasure states the intent of the code clearly. obj_ can hold any type that is CopyConstructible, and Ostreamable. (you don't actually need typeid_ here).
In Christ, Steven Watanabe

Steven Watanabe-4 wrote
Oh yes, because I'm not doing type casting. Also I meant for obj_ to be private. A member like the following could also be added: class display { ... public: template< typename U > void set ( U const& obj ) { obj_ = obj; } }; This might be a reasonable example for the motivation section maybe with a comparison of why this example cannot be worked with Boost.Any alone.
-- View this message in context: http://boost.2283326.n4.nabble.com/Formal-Review-Request-TypeErasure-tp46303... Sent from the Boost - Dev mailing list archive at Nabble.com.

Steven Watanabe wrote:
I'd like to request a formal review of the TypeErasure library that I've posted about several times before here. The TypeErasure library is a generalization of boost::any and boost::function. It allows easy composition of arbitrary type erased operators.
It seems the library is a little more than that. Have you considered naming it Boost.DuckTyping? I think TypeErasure doesn't really capture the full scope of what you've done. A much longer example where you use the library to implement something with some explanation of why you would want to use the library in such a case would be very helpful in the docs. Something like Sean's value semantics document example. For that matter, you could reimplement his document example using your library and provide draw concept and copy on write concept for types used instead of relying on inheritance. Regards, Luke

I read the docs as this library is very impressive and does some of the same things I do with Boost.Reflect so I can appreciate the problem domain. http://bytemaster.github.com/boost_reflect/group__boost__reflect__quickstart... I would like to make comments / ask some questions: 1) More motivating examples would be useful. Given the difficulty (verbosity/boiler plate) of implementing 'push_back', I cannot imagine using this library as an 'end coder' for custom interfaces. The only place I could see using anything other than the built in concepts would be to simplify the development of my own APIs for my users sake, not mine. 1.1) better documentation on what all of the template parameters mean and why they are used would be helpful. 2) I cannot begin to imagine the template errors generated by using such a complex type. But this is par for the course with Boost. 3) How does using such an erasure effect compile times? 4) It is difficult to tell from the documentation, but what is the cost of dispatching methods via the any interface? 5) Many scripting languages have a generic value that could be an int, double, string, bool, array, map, or function. This could perhaps belong to a new type... boost::type_erasure::variant<conceptA,conceptB,etc> I have recently been working on a new interface along those lines: https://github.com/bytemaster/boost_reflect/blob/master/tests/value_test.cpp struct sub_val { std::string happy; double day; }; BOOST_REFLECT( sub_val, (happy)(day) ); struct test { std::string b; sub_val sub; std::vector<sub_val> data; }; BOOST_REFLECT( test, (a)(b)(sub)(data) ) test t(1,"test_ref"); t.sub.happy = "oh happy pie day"; value_cref v(t); // const ref value vc(t); // copy value_ref vr(t); // regular ref BOOST_ASSERT( v["sub"]["happy"].as<std::string>() == "oh happy pie day" ); Yours has compile-time checking and supports many more concepts. Mine does runtime checking and throws on error. I would like to compare your approach with mine in boost::reflect to show various tradeoffs. I think our work has different strengths and weaknesses and I would love to find a way to blend it. Compare: struct vector_concept { void push_back( int ); void push_front (int); int size(); int front(); int at(int); }; BOOST_REFLECT_ANY( vector_concept, (push_back)(push_front)(size)(front)(at) ) boost::reflect::any_ptr<vector_concept> v( new std::vector<int>() ); v->push_back(5); The way I see it, I was able to define 5+ methods on an interface in one line of easy to understand code. If you could achieve something like this with your library then it would be a huge win. Mine is implemented in a manner that results in sizeof(reflect::any_ptr) to grow with each member such as 'push_back' is really a public member functor. Your approach does not have this overhead. My approach allows visiting each member and dynamically specifying an implementation. Yours enables many more concepts (operators, etc) to be implemented. Mine could be used by any coder, yours requires one that can throughly understand templates and debug all manner of 'place holders'. I consider myself skilled and would expect to have trouble implementing something like push_back, especially if it required complex usage of place holders. Better documentation could help. Clearly we are solving slightly different problems, and I would love to use your library as a high performance, lower overhead version of my reflect::any_ptr<> class in places where I do not need some of the more dynamic features. Good job! I would probably vote to include it based upon the built-in concepts alone. Some fancy macro help would seal the deal. Dan

AMDG On 05/23/2012 11:27 PM, Daniel Larimer wrote:
It isn't as bad as you might think and I know how to change the library to keep the template instantation depth for the most common errors down to 3 or 4.
3) How does using such an erasure effect compile times?
It's fairly slow. I've made no attempt to optimize the metaprogramming at this point.
4) It is difficult to tell from the documentation, but what is the cost of dispatching methods via the any interface?
The cost is one call through a function pointer.
5) Many scripting languages have a generic value that could be an int, double, string, bool, array, map, or function. This could perhaps belong to a new type... boost::type_erasure::variant<conceptA,conceptB,etc>
I'd say that this is out of scope for my library currently.
Reflection is really a separate issue.
This is a nice interface, but it has several limitations that I see no way around: a) It supports member functions only b) Overloading is impossible Some problems can be solved with difficulty: c) there's no way to know the number of arguments at preprocessing time, so you can't declare a regular function. I think it would be possible to have a macro that acts something like this for simple cases, though.
I did use macros in the implementation. Something like this wouldn't be very hard to implement: BOOST_TYPE_ERASURE_MEMBER(push_back, 2) BOOST_TYPE_ERASURE_MEMBER(push_front, 2) In Christ, Steven Watanabe

On May 24, 2012, at 12:48 PM, Steven Watanabe wrote:
b) Overloading is impossible - not quite, you would have to extend the macro to enumerate the signature of each method. I did this in prior versions of the code.
What does the number 2 mean? I think this would be great. Good work, I will probably find use for your library in my own projects. Dan

on Thu May 24 2012, "Simonson, Lucanus J" <lucanus.j.simonson-AT-intel.com> wrote:
-1 IMO DuckTyping is an ill-defined term that is sometimes used to hide a multitude of sins. -- Dave Abrahams BoostPro Computing http://www.boostpro.com

Dave Abrahams wrote:
IMO DuckTyping is an ill-defined term that is sometimes used to hide a multitude of sins.
Yes, after making that post I thought about it a little more and decided I don't know how to define duck typing, but I know it when I see it. If it walks like a duck and it talks like a duck, then it's a duck. Until it doesn't talk like a duck, then it's a run time error. That's not a language feature we want, and it's not what the library implements. Primarily it seems the library is adding a layer of type checking on top of type erasure. Boost.Any does type erasure. This library does more. I'd like the combination of static polymorphism and type erasure to have a name. ConceptAny, DynamicConcepts, AnnonymousConcepts, TypeSafeAny. Regards, Luke

Daniel James wrote:
I just had an evil thought for a name: MetaTyping Evil because meta is overused, but perhaps apt in this case. Types of types. It has similar meaning to concepts, but wouldn't be confused with concepts. However, given Steven's lack of interest in the topic of a new name I'm guessing he wants to stick with TypeErasure and that's fine with me. Regards, Luke

On May 30, 2012, at 6:21 PM, "Simonson, Lucanus J" <lucanus.j.simonson@intel.com> wrote:
I just had an evil thought for a name: MetaTyping
(totally OT) That name suggests to me a type system library for metaprogramming, which is unfortunately (although sorta conveniently (?)) duck typed ATM. Any takers? :-) It does seem to me that the TypeErasure library has outgrown its name into some sort of general dynamic concepts - congrats Steven, a real mind bender! - but I won't argue that it completely solves type erasure, so no arguments there. Cheers, Gordon

Gordon Woodhull <gordon <at> woodhull.com> writes:
On May 30, 2012, at 6:21 PM, "Simonson, Lucanus J" <lucanus.j.simonson <at>
intel.com> wrote:
I just had an evil thought for a name: MetaTyping
No opinion on that, but I've got another suggestion: Boost.Interface. The name comes back from an abandoned lib proposal by Jonathan Turkanis: http://www.cdiggins.com/bil.html which revolves around duck typing (though with a different approach than Steve's lib.) "Interface" is (surprisingly) a not much overloaded word in C++. Joaquín M López Muñoz Telefónica Digital

on Mon Jun 04 2012, Joaquín M LópezMuñoz <joaquin-AT-tid.es> wrote:
It's also a well-understood word in the OO community, meaning roughly the right thing. I guess one question is whether we'd want to sully the whole "concept-based polymorphism" flavor with OO terminology ;-) -- Dave Abrahams BoostPro Computing http://www.boostpro.com

Dave Abrahams wrote:
It's also a well-understood word in the OO community, meaning roughly the right thing. I guess one question is whether we'd want to sully the whole "concept-based polymorphism" flavor with OO terminology ;-)
So is that a +0.5? I like the name because it is sufficiently general to capture the scope of the library. This name should help answer the " what this library is for, what is the motivation?" question Neil raised. It is a library for defining interfaces. Generic interfaces kind of goes without saying since it is a boost library. I wouldn't tend to jump to the OO-specific definition of Interface when I see it as the name of a boost library, since it is one of the most general programming terms. Boost.Algorithm, Boost.Interface, Boost.Parameter. It seems to fit right in there. Regards, Luke

on Mon Jun 04 2012, "Simonson, Lucanus J" <lucanus.j.simonson-AT-intel.com> wrote:
+0.72
Well, yeah, but it's specifically about ad-hoc runtime polymorphism (mouthful) which is different from "Generic interface" (could mean static polymorphism with concepts).
:^) Point taken -- Dave Abrahams BoostPro Computing http://www.boostpro.com

Le 04/06/12 08:31, Joaquín M LópezMuñoz a écrit :
What about.Boost.Signature? See http://gcc.gnu.org/onlinedocs/gcc-2.95.3/gcc_5.html#SEC112. Vicente

on Tue May 29 2012, "Simonson, Lucanus J" <lucanus.j.simonson-AT-intel.com> wrote:
In my view it does less. Less type erasure (where I consider interface and behavior part of type). :-) -- Dave Abrahams BoostPro Computing http://www.boostpro.com

Steven Watanabe <watanabesj <at> gmail.com> writes:
This library is indeed very interesting. From a quick look at some of the documentation, I have just one initial comment: I believe it is a mistake to "propagate constness" for "any"s that capture references, as that is inconsistent with normal C++ semantics and it leads to a situation of trying to fight the C++ type system rather than work with it.

AMDG On 05/24/2012 04:21 PM, Jeremy Maitin-Shepard wrote:
The situation is one that isn't even allowed with C++ references: int & const ref = i; // error The problem with const any<Concept, _self&> xx; is that anything that isn't specially designed to handle it will see the const and treat it as const. Some parts of the library will work with it, but in others it was just too difficult. In Christ, Steven Watanabe

Steven Watanabe <watanabesj <at> gmail.com> writes:
On 05/24/2012 04:21 PM, Jeremy Maitin-Shepard wrote:
Steven Watanabe <watanabesj <at> gmail.com> writes:
[snip]
True, but a reference can be a member of a struct to which you have a const reference, in which case it is treated just like a pointer, i.e. no const propagation.
I can see now that there would be a problem with using concept_interface to inject member functions, since there is no way to simultaneously define both a const and non-const member function. Particularly with the introduction of member reference qualification, not being able to template on the type of "this" is a significant defect in the language. At least it can be worked around by not using member functions (except for operator overloading where it can't be avoided). Maybe support for _self* or even shared_ptr<_self> (as a special syntax to indicate that internally a shared_ptr to the underlying type should be stored) could be a useful alternative?

On 26/05/12 21:25, Jeremy Maitin-Shepard wrote:
In the past I've achieved this by defining both const and non-const member functions and forwarding to a free function which then can be templated on the type of this. As you say, things get worse with member reference qualification because now three member functions are needed. I don't know whether such a technique would be applicable in this case. John

on Tue May 22 2012, Steven Watanabe <watanabesj-AT-gmail.com> wrote:
Online documentation can be found here: http://steven_watanabe.users.sourceforge.net/type_erasure/libs/type_erasure/
http://steven_watanabe.users.sourceforge.net/type_erasure/libs/type_erasure/... says: The types of the arguments must match or the behavior is undefined. I presume you mean that the dynamic, erased types must match? (presumably you could easily prevent the use of different static types at compile-time). IMO it's crucial that you be rigorous about these distinctions in your documentation. I wonder if undefined behavior is really the best possible choice here. I honestly don't know what's most useful. Do you have a rationale for your choice? Also, I must add that the following sentence is so diametrically opposed to the one above that putting it in a different paragraph doesn't help the reader enough to deal with the cognitive dissonance: The operands do not all have to be the same type. Cheers, -- Dave Abrahams BoostPro Computing http://www.boostpro.com

AMDG On 05/29/2012 03:04 AM, Dave Abrahams wrote:
I've tried to clarify this.
It's impossible to enforce this statically, so the only alternative is to throw an exception. I do provide a mode that does this. By default the library assumes that you've made sure that the types match. If you add relaxed_match, it will check whether the arguments match and if they don't, either fall back on some reasonable default behavior or throw. In Christ, Steven Watanabe

on Tue May 22 2012, Steven Watanabe <watanabesj-AT-gmail.com> wrote:
Online documentation can be found here: http://steven_watanabe.users.sourceforge.net/type_erasure/libs/type_erasure/
http://steven_watanabe.users.sourceforge.net/type_erasure/libs/type_erasure/... starts using two things out of the blue that you haven't introduced yet: 1. any<...> with template arguments that aren't mpl sequences 2. _self IMO that should be corrected. -- Dave Abrahams BoostPro Computing http://www.boostpro.com

AMDG Lorenzo has offered to manage the review. Looking at the review schedule, there's nothing scheduled after Multiprecision (June 8-17). Does the beginning of July work? That leaves a two-week buffer between reviews. In Christ, Steven Watanabe On 05/29/2012 11:24 AM, Ronald Garcia wrote:

Hi Steven, Yes, the beginning of July would work fine. You may want to avoid overlapping with U.S. Independence day, since that may shrink the pool of reviewers for a time. Let us know when you would like to begin. Best, Ron On Jun 7, 2012, at 5:49 PM, Steven Watanabe wrote:

Ronald Garcia-4 wrote
I'll be on vacation 7/1-15. For me it would be best the second half of July. --Lorenzo -- View this message in context: http://boost.2283326.n4.nabble.com/Formal-Review-Request-TypeErasure-tp46303... Sent from the Boost - Dev mailing list archive at Nabble.com.

Hi Steven, While I can use the library just fine in C++98 mode, I am not able to compile the code (basic.cpp) with -std=c++0x enabled on GCC 4.6.3. The compilation fails with the following error message : ./boost/type_erasure/detail/vtable.hpp:155:28: error: constexpr static data member ‘value’ must have an initializer ./boost/type_erasure/detail/vtable.hpp:159:52: error: redeclaration ‘boost::type_erasure::detail::vtable_init<Table, T>::value’ differs in ‘constexpr’ ./boost/type_erasure/detail/vtable.hpp:155:28: error: from previous declaration ‘boost::type_erasure::detail::vtable_init<Table, T>::value’ ./boost/type_erasure/detail/vtable.hpp:159:52: error: ‘boost::type_erasure::detail::vtable_init<Table, T>::value’ declared ‘constexpr’ outside its class ./boost/type_erasure/detail/vtable.hpp:159:52: error: declaration of ‘const Table boost::type_erasure::detail::vtable_init<Table, T>::value’ outside of class is not definition [-fpermissive] I tried to improve the situation myself by replacing line 155 by
static constexpr Table value(T::value...);
But it didn't help (the given message doesn't make much sense to me : "T::value is not a type"). Is it a limitation of GCC 4.6 ? Many thanks, Julien

2012/5/22 Steven Watanabe <watanabesj@gmail.com>
[snip]
This look promising ! If I understand well, your library could greatly help to achieve value semantics and concepts-based polymorphism as described by Sean Parent here : http://cppnow.org/session/value-semantics-and-concepts-based-polymorphism/, right? If so, I would like to see a simpler "polymorphic range formatter" example, because the current one is the first and only example who deal with inheritance and virtual functions, but it is so complicated that it lost me. To be clear, if I have the following code using traditional OO style with runtime polymorphism : (Note that I dropped every functions template to avoid complications for now) --------------------- #include <iostream> #include <vector> class abstract_printer { public: void print(std::ostream& os, const std::vector<int>& v) const { do_print(os, v); } virtual ~abstract_printer() {} protected: virtual void do_print(std::ostream& os, const std::vector<int>& elements) const = 0; }; class separator_printer : public abstract_printer { public: explicit separator_printer(const std::string& sep) : separator(sep) {} protected: virtual void do_print(std::ostream& os, const std::vector<int>& v) const { for(int i : v){ os << separator.c_str() << i; } } private: std::string separator; }; class column_separator_printer : public abstract_printer { public: column_separator_printer(const std::string& sep, std::size_t num_columns) : separator(sep), cols(num_columns) {} protected: virtual void do_print(std::ostream& os, const std::vector<int>& v) const { std::size_t count = 0; for(int i : v){ os << i; int temp = i; ++temp; os << separator.c_str(); if(++count % cols == 0) { os << "\n"; } } } private: std::string separator; std::size_t cols; }; ------------- As noted in you documentation, runtime polymorphism almost every time requires dynamic memory management, so we are stuck and forced to use those classes pretty much like this: ------------- abstract_printer* p1 = new separator_printer(","); abstract_printer* p2 = new column_separator_printer(",", 4); std::vector<abstract_printer*> printers; printers.push_back(p1); printers.push_back(p2); for(abstract_printer* printer : printers) { printer->print(std::cout, test); std::cout << std::endl; } for(abstract_printer* printer : printers) { delete printer; } ------------- Now what are the minimal, non-intrusive modifications using Boost.TypeErase we have to make so we can write in a value-based style, with regular types ? Can we achieve something like this ? : -------------- std::vector<any<printable>> printers; printers.push_back(separator_printer(",")); printers.push_back(column_separator_printer(",", 4)); for(any<printable>& printer : printers) { printer->print(std::cout, test); } ------------- Thanks. Thomas.

AMDG On 05/30/2012 01:30 AM, Thomas Petit wrote:
template<class T = _self> struct printable { static void apply(const T& t, std::ostream& os, const std::vector<int>& v) { t.print(os, v); } }; namespace boost { namespace type_erasure { template<class T, class Base> struct concept_interface< ::printable<T>, Base, T> : Base { void print(std::ostream& os, const std::vector<int>& v) const { call(printable<T>(), *this, os, v); } }; }} The two differences in usage are that you have to use printable<> and printer.print(std::cout, test). In Christ, Steven Watanabe

2012/5/30 Steven Watanabe <watanabesj@gmail.com>
Amazing !! That's a lot less code that I would have imagine. It maybe even could be generate with some macro ? Just a note, with MSVC11 beta I also had to add the concept copy_constructible<> to be able to put my any<mpl::vector<printable<>, copy_constructible<>>> in a std::vector. If others are interested, here is the complete, working code : ------------------------------------- #include <iostream> #include <vector> #include <boost/type_erasure/any.hpp> #include <boost/type_erasure/iterator.hpp> #include <boost/type_erasure/operators.hpp> #include <boost/type_erasure/tuple.hpp> #include <boost/type_erasure/same_type.hpp> #include <boost/mpl/vector.hpp> namespace mpl = boost::mpl; using namespace boost::type_erasure; template<class T = _self> struct printable { static void apply(const T& t, std::ostream& os, const std::vector<int>& v) { t.print(os, v); } }; namespace boost { namespace type_erasure { template<class T, class Base> struct concept_interface< ::printable<T>, Base, T> : Base { void print(std::ostream& os, const std::vector<int>& v) const { call(printable<T>(), *this, os, v); } }; }} class abstract_printer { public: void print(std::ostream& os, const std::vector<int>& v) const { do_print(os, v); } virtual ~abstract_printer() { std::cout << "~abstract_printer()" << std::endl; } protected: virtual void do_print(std::ostream& os, const std::vector<int>& elements) const = 0; }; class separator_printer : public abstract_printer { public: explicit separator_printer(const std::string& sep) : separator(sep) {} protected: virtual void do_print(std::ostream& os, const std::vector<int>& v) const { for(int i : v){ os << separator.c_str() << i; } } private: std::string separator; }; class column_separator_printer : public abstract_printer { public: column_separator_printer(const std::string& sep, std::size_t num_columns) : separator(sep), cols(num_columns) {} protected: virtual void do_print(std::ostream& os, const std::vector<int>& v) const { std::size_t count = 0; for(int i : v){ os << i; int temp = i; ++temp; os << separator.c_str(); if(++count % cols == 0) { os << "\n"; } } } private: std::string separator; std::size_t cols; }; void print() { int test[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; std::vector<int> vtest(test, test + 10); typedef any<mpl::vector<printable<>, copy_constructible<>>> polyprintable; std::vector<polyprintable> printers; printers.push_back(polyprintable(separator_printer(","))); printers.push_back(polyprintable(column_separator_printer(",", 4))); for(polyprintable& printer : printers) { printer.print(std::cout, vtest); std::cout << std::endl; } } int main() { print(); } -----------------------------------

Can you please add a bit more introduction in the doc that explains what this library is for, what is the motivation? Perhaps a _simple_ example of usage?

My 0.02EUR. A big issue of type deletion is dealing with storage. 1) Please do a better job of SBO than boost::function. It'd be cool if you let the user pick the SBO size or even configure his own container. boost::function might have been implemented using shared_ptr, but there would have been issues with stateful f-tors. 2) On a related note I could see the use of an any_ref type holding only a virtual table pointer and a ref to an object it doesn't own. Just two words no heaping. That pushes the storage problem out to the user. I think you could do that optimization with a specialization where the 2nd parameter is a ref. Would the second parameter ever be shared_ptr<_self> ? 3) What about optionality? Should they always use optional<any<> > ? boost::function can be null. 4) What about and any_base<> + any_derived<> ? Based on several cases where I've seen/done type deletion a common pattern is to define a base class and a templated derived class. boost::asio::detail::op_base+op The technique can be implemented with either function pointers or virtual functions. If you could provide an elegant way of doing that, it'd be pretty cool. The user is free to store his any_derived<> however he sees fit. My example is a buffered logging class that takes functor< void (std::ostream&)> and stores them in a linear buffer, which gets flushed out later. Can that be done in a more formal way? 5) The library overlaps quite a bit with Concept checking libs. Perhaps you should expose this to the user. You can implement an assert_fits_any<any_type,T> (is a macro better)? You can probably also implement a check_any<any_type,T > metafunction and that lets the user check a type to see if it fits and do something else if it doesn't. 6) Could you add an example where it has a call operator like boost::function? I can see some antipatterns forming: * people shouldn't use any to replace classic interfaces and virtual functions * special purpose type deletion may be better like any_range Pretty cool library! Chris -----Original Message----- From: boost-bounces@lists.boost.org [mailto:boost-bounces@lists.boost.org] On Behalf Of Steven Watanabe Sent: Tuesday, May 22, 2012 10:34 PM To: boost@lists.boost.org Subject: [boost] Formal Review Request: TypeErasure AMDG I'd like to request a formal review of the TypeErasure library that I've posted about several times before here. The TypeErasure library is a generalization of boost::any and boost::function. It allows easy composition of arbitrary type erased operators. As an example of basic usage, we can simulate Boost.Any: any<mpl::vector<copy_constructible<>, typeid_<> > > x(10); int i = any_cast<int>(x); // i == 10 The library is available in the Boost Sandbox at http://svn.boost.org/svn/boost/sandbox/type_erasure/ You can download archives with pre-built documentation from http://sourceforge.net/projects/steven-watanabe.u/files/ Online documentation can be found here: http://steven_watanabe.users.sourceforge.net/type_erasure/libs/type_erasure/ In Christ, Steven Watanabe _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

on Wed Jun 06 2012, "Hite, Christopher" <Christopher.Hite-AT-partner.commerzbank.com> wrote:
* people shouldn't use any to replace classic interfaces and virtual functions
Excuse me, but why not? Doing polymorphism this way eliminates all kinds of nastiness that we get with classic interfaces and virtual functions. -- Dave Abrahams BoostPro Computing http://www.boostpro.com

* people shouldn't use any to replace classic interfaces and virtual functions
Excuse me, but why not? Doing polymorphism this way eliminates all kinds of nastiness that we get with classic interfaces and virtual functions.
Perhaps we should make sure the documentation clearly states what kinds of nastiness the library allows us to avoid and how it is intended to be used. It solves a lot of problems. We should explain what they are and why using the library to define interfaces is an improvement on past practice. For example, nastiness related to generic programming can also be avoided. A template library with generic interfaces requires that the header only implementation of the implementation details of the library be pulled into every compilation unit that uses it. With the new any we can keep much of the goodness of the generic interfaces (type safety, etc.), but compile to a binary library with any based interfaces that can be linked to in a large application without needing to recompile every module that uses it every time the internal implementation of the library changes, just relink. Also, the source code of the library can be kept closed while only the any-based interfaces in a public header file are exposed to the library user. Yes, the extra abstraction comes at extra cost, but that cost is unavoidable if we are to solve the problem. The linker needs at least one indirection to be satisfied. Then, of course, we need to explain why it is better than just requiring the user to inherit from some base library type in typical OO style. Regards, Luke

Chris:
So the nice thing about the any vs. classic virtual functions is it is nonintrusive. Consider a library that defines this: struct push_back_able{ virtual void push_back(int) =0; }; void get_ints(push_back_able&); vector<int>, list<int>, etc aren't derived from push_back_able and you can't/shouldn't go change stl containers to make it do it. So if you want to feed ints into a vector<int> you'll have to make a new class which isA push_back_able and references the vector. At this point you've probably already thought of some kind of utility like: template<typename T> push_back_delegator<T> make_ push_back_delegator(T&); So you can do this: std::vector<int> v; get_ints(make_ push_back_delegator(v)); any<> helps you do this, hopefully with less code. ================================================================= Now I'm going to try to draw a counter example where I think virtual methods make more sense. I've picked something with several functions with different signatures so nobody can argue it can be easily type-deleted as a functor concept. struct session_handler{ virtual void handle_connect() =0; virtual void handle_message(const message&) =0; virtual void handle_disconnect() =0; }; Now alternatively we could define an any<..., _self&> and hand that to session instead of session_handler&. I think that's a bad idea. 1) defining the any requires a bit more code: you' need to define boost:: type_erasure::concept_interface's to get the calls to look nice. 2) the language construct is already there and is cleaner to read. C++11 override also helps. The main difference between these two examples is the dependency. STL should never depend on someone's ad-hoc push_back_able concept. By contrast someone writing my_session_handler knows his code is dependent on that interface. So he doesn't mind inheriting the base class, since it is just formalizing that relationship. I support the boost style of code which has very few virtual methods exposed to the user (as opposed to something like ACE). However I still believe they make sense sometimes. Chris

on Thu Jun 07 2012, "Hite, Christopher" <Christopher.Hite-AT-partner.commerzbank.com> wrote:
Actually, that's only one of many nice things. See Sean Parent's 2nd talk from C++Now! Oh, the videos aren't posted yet. Well, the chain of implications, off the top of my head, goes like this: OOP => different sized objects being handled through the same interface => dynamic allocation (- complicated) => memory management (- hard) and => reference semantics (- hard to reason about) => shared state (- hard to reason about) => MT synchronization => deadlocks, races, and inefficiency In a nutshell, concept-based-runtime-polymorphism/type-erasure allows you to have runtime polymorphism while preserving value semantics. The value of that win is very commonly underestimated. -- Dave Abrahams BoostPro Computing http://www.boostpro.com

Standardize Steven's library as part of the STL? Boost.Any is already being considered for standardization. I think Steven has proven we don't need a new core language feature and that this type safe type erasure can be done as a library. I suppose the question is, can this be done better with a change to the language itself? I suppose Concepts would make it easier, but that is like saying having a cornucopia would make eating breakfast easier. Living within the constraints of the linker I don't see how we can improve efficiency, but I haven't given it much thought. Perhaps we can identify things about the library that we don't like but have to live with and think about what language changes would be needed to deal with those things. Possibly Lorenzo's feedback about Base looking arbitrary and unintuitive? Once again, I haven't given it much thought. Regards, Luke

On 7 June 2012 14:33, Simonson, Lucanus J <lucanus.j.simonson@intel.com>wrote:
Take the following base class: struct S { void f(); void g(); void h(); }; In the virtual function world, to enforce that interface, one merely has to write: struct AbstractS { virtual void f() = 0; virtual void g() = 0; virtual void h() = 0; }; and have others derive from it. If I wanted a type erased version of the equivalent callable interface, what do I have to do? If it isn't as simple as writing AbstractS above, only a small fraction of C++ developers will ever attempt it, let alone do it on a regular basis. Steven's library does a great job at covering a lot of cases, but adding custom concepts is a chore. Like lambdas, Steven is showing what is possible with a library, but I think we need a language extension to make it useable by the masses. -- Nevin ":-)" Liber <mailto:nevin@eviloverlord.com> (847) 691-1404

Nevin Liber wrote
What if this were possible? Would this be an acceptable way to add custom concepts? BOOST_TYPE_ERASURE_CONCEPT( concept (sable) ( class S ) ( void member_body(S, f) ( void ) , void member_body(S, g) ( void ) , void member_body(S, h) ( void ) ) ) --Lorenzo -- View this message in context: http://boost.2283326.n4.nabble.com/Formal-Review-Request-TypeErasure-tp46303... Sent from the Boost - Dev mailing list archive at Nabble.com.

On 7 June 2012 16:14, lcaminiti <lorcaminiti@gmail.com> wrote:
It would certainly help a lot. If we want people to replace the common idiom of creating AbstractS and storing the instantiated object in unique_ptr<AbstractS> (or a different smart pointer), the replacement needs to be almost as easy, at least for that very common case. -- Nevin ":-)" Liber <mailto:nevin@eviloverlord.com> (847) 691-1404

Nevin Liber wrote:
Given the current language constraints, that would be a help. However, the question was what should be added to the language to make this nicer. What's more, the use of such macros compounds the problems of templates (long backtraces, no concepts, etc., some of which Steven may well have addressed), by putting everything on one, obscure line (so far as the compiler sees it). I agree with you, Nevin, that it should be little more difficult, if any, than just declaring an ABC for the interface. Sebastian's suggestions of compile-time reflection and declaration injection, if feasible, might be just the ticket. Whatever turns out to be most general, without exposing too much implementation detail which ties implementer's hands, would be nice. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com ________________________________ IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

On 6/7/12 11:14 PM, lcaminiti wrote:
I think this would be a step in the right direction, but not quite good enough yet. I have used and written similar macro based IDLs on/for several systems, and they were always disliked by most developers, even though they worked quite well. For such a common feature the Syntax has to be as easy as Possible, and I think (but am not sure) this can't be done without a language feature. I could Imagine a Syntax like either the above AbstractS (with or without the virtual) or even more ideally be able to do something like "create_type_erased_concept_from<S>;" which should be implementable (maybe with a macro instead of a template) if we had some kind of compile time reflection. regards Fabio

on Thu Jun 07 2012, Nevin Liber <nevin-AT-eviloverlord.com> wrote:
Yes; concepts :-) -- Dave Abrahams BoostPro Computing http://www.boostpro.com

on Thu Jun 07 2012, Steven Watanabe <watanabesj-AT-gmail.com> wrote:
Not in a library, maybe, but concepts would lay the groundwork for the necessary language feature; I think it could be a very small extension on top of concepts. -- Dave Abrahams BoostPro Computing http://www.boostpro.com

On Jun 7, 2012, at 3:08 PM, Nevin Liber wrote:
A language construct such that implemented special interpretation of the construct: anytype concept_name x; Or perhaps simply specify a concept type like any other type.... That would greatly improve compile times AND readability compared to the template hoops required to do what TypeErasure does. In my opinion, just because something *can* be done with templates doesn't mean it should. C++2x should embrace any feature that could reduce compile times to the same extent that variadic templates did for c++11. I am not familiar with the 'concepts' feature that was dropped from C++0x, but if it increases compile times that that may not be a way to go.

Dave Abrahams:
Actually, that's only one of many nice things. See Sean Parent's 2nd talk from C++Now! Oh, the videos aren't posted yet.
Well, the chain of implications, off the top of my head, goes like this:
In a nutshell, concept-based-runtime-polymorphism/type-erasure allows you to have runtime polymorphism while preserving value semantics. The value of that win is very commonly underestimated.
I think you're overselling it a bit here. You can have value sematics with interfaces, just pass around these and heap all the time in clone and maintain single points of ownership or shared_ptr. It has about the same cost as boost::function. struct interface{ virtual ~interface()=0; virtual interface* clone()=0; }; To be fair consider the version of any<..,_self&> which doesn't tackle storage and ownership and compare that with an interface with virtual functions. With the any<...,_self&> I've got a virtual table pointer and a pointer to the type-deleted object. With interface& I've got a pointer to an object with a virtual table intruding into it. Assuming you've got a massive interface with tons of functions on it, it doesn't make sense to use any. Another example, I often make shared objects (or DLLs) with a single factory function that gets me an interface. This interface is generally pretty massive since it exposes everything in the library. I don't think it would makes sense to have that factory function return an any, because it would have a complex ugly definition. Also there's an issue with the definition of any changing whereas virtual table layouts won't change. I notice the boost library never has massive interfaces. Someone here might argue that you shouldn't have them in general. I don't know. I think asio works well with simple functors, though it seems a little forced that completion handlers take an error which can be no error. In some cases you can break interfaces apart and into multiple pieces. subscribeConnected subscribeMessageRecieved subscribeDisconnected Do you guys think it is just a question of style? I'd have a really hard time envisioning something like DOM which is designed by interface (probably by java programmers), re-written with a bunch of any<> and functors. Though I wouldn't say Xercies is prefect. Most things in boost are nice light independent libraries; most things in industry are hugh modules with complex interfaces between them. Maybe that's the difference. Maybe I'm wrong and we can deprecate virtual functions from C++. Chris

Hi All, Dave,
(Sorry for being late, I had a lot to learn when reading about concept based polymorphism, I hope I got them right) Overall, I think you over dramatise the situation. Dynamic allocation and memory management are one and the same drawback. I guess that with <memory> C++03 has done huge steps forward to make this easier. Granted, it's not perfect yet (I've seen many articles recommending caution with shared_ptr), but I don't think it justifies introducing value types everywhere. Moreover, when using polymorphic value types implemented this way, you are still paying for one dynamic allocation and one deletion per copy of the object (this may be implementation–specific). Since those objects are supposed to be copied frequently, I wonder whether this will be a good trade of. Shared state, synchronisation, deadlocks and races are, again, one single aspect of the same problem. Though I'd agree that it is a good thing to have more value types and go toward more functional programming, I think the problem will not go away with (polymorphic) value types alone: you'll need as well higher level parallelism concepts (à la TBB) if you want to avoid shared state completely. I have tried to see where I could use such types in the code I have been writing recently. I have found a good candidate where I need to pass a value across thread and library boundaries at the same time. The thread boundary implies that I want to copy the object, the library boundary that I'll loose type information (in that case). While this may not be the perfect use case, it helped me realise two things: - boost::variant is a good approximation of polymorphic value types, less convenient but may be faster (no dynamic memory management) and probably simpler to use. - I have no idea when and where one should use this paradigm. I had implied that the scope of TypeErasure was to replace inheritance–based polymorphism whenever possible. Now I think it may be more modest, though it is unclear to me what is the actual field of application. Could anyone tell me how they intend to use type erasure ? Regards, Julien

Suppose you wanted to create a graphics engine that would render any object that has a 'draw()' method. Currently you have two options: define a pure virtual base class and introduce a dependency between circle and square along with increasing the size of circle and square by the vtable pointer... or use a templates and do everything in the header file thus you no longer have a 'hidden' implementation and your compile times increase quickly. Assuming you use type erasure with references and therefore avoid the memory allocation, you can make any type polymorphic without introducing a vtable to your primitives. The primary use case for this technique in my mind is to move generic code from headers to source files and therefore maintaing better 'encapsulation' and separation between API and implementation. The users of the API get the same experience of using a template method, but now you get the benefit of a private (or closed source) implementation. I can now store a vector<squares> without waisting memory or having to worry about polymorphic destruction on my square or the fact that a vtable would mess with the binary representation of the squares preventing me from passing it to OpenCL without a transform to a non-polymorphic type. Think of this as 'non-intrusive' run-time polymorphism.

on Wed Jun 13 2012, Julien Nitard <julien.nitard-AT-m4tp.org> wrote:
Not if you account for the possibility of GC or shared pointers.
Says who?
Not if you account for the possibility of transactional memory.
Of course. And I don't want to avoid shared state completely. I want to avoid sharing state all over the place "by default." By the way, this affects single-threaded code too. If you haven't heard of "defensive copying" in the Java world, look it up.
Not if you account for the need to have runtime-polymorphic functions. Dispatching over a variant is still harder than overriding a virtual function, and the set of choices is closed to extension.
- I have no idea when and where one should use this paradigm. I had implied
Inferred?
that the scope of TypeErasure was to replace inheritance–based polymorphism whenever possible.
That's definitely one useful way to apply it. -- Dave Abrahams BoostPro Computing http://www.boostpro.com

Dave Abrahams wrote:
that the scope of TypeErasure was to replace inheritance–based polymorphism whenever possible.
That's definitely one useful way to apply it.
I would say the scope of the library is to provide (type safe) runtime polymorphism whenever inheritance-based polymorphism is not possible. I'd question the value of replacing legacy inheritance based type systems with TypeErasure based type system for its own sake. It needs to enable something worthwhile or I'd rather the developer effort go elsewhere. I'm not disagreeing with Dave, I'm just saying its usefulness needs to be evaluated on a case by case basis. With inheritance based type systems you get a tower-of-babel problem where everyone wants to talk about the same thing, but everyone has created a different language to talk about it with different base classes and interfaces. That leads to a type incompatibility problem between different modules in any reasonably large real world application. What the library allows you to do is define an interface that unifies the divergent inheritance hierarchies that have no base class in common, but do have concepts in common. We could do that with static polymorphism in the past, but there is still scope in the needs of C++ programmers to use dynamic polymorphism. For example, a container of objects of different size/type (perhaps by reference) that all have a concept in common. A real example from an application I have worked on is that two object types inherit (eventually) from the same base type and each has an interface for a concept they have in common. Getting some attribute of the object. However, that interface isn't a method of the (common) base class and is declared differently for each of the two types. Epic fail. With TypeErasure you can declare an interface that accepts any object of either type and knows how to map the concept they have in common to the disparate interfaces of the various types without needing to fix the broken inheritance hierarchy, which is often not feasible due to the mass of legacy code that may depend upon it. While such a situation might seem contrived, I would say it is pretty typical of legacy C++. Even within the same dynamic polymorphic type system you can easily end up with senseless type incompatibilities. Once again, we could also solve it with static polymorphism. All the library does is layer type erasure on top of static polymorphism to make non-intrusive, type-safe dynamic polymorphism. Regards, Luke

on Wed Jun 13 2012, "Simonson, Lucanus J" <lucanus.j.simonson-AT-intel.com> wrote:
Seriously? So, you would dispense with boost/std::function because an inheritance-based approach is possible?
I'd question the value of replacing legacy inheritance based type systems with TypeErasure based type system for its own sake.
I don't think I ever suggested that one should do that. Although, if you have the time, it would make an interesting experiment, and might even show overall value.
Sure. For new code, it's reasonable to consider a value-based paradigm in lieu of legacy inheritance paradigms. -- Dave Abrahams BoostPro Computing http://www.boostpro.com

Seriously? So, you would dispense with boost/std::function because an inheritance-based approach is possible?
I wasn't trying to define the entire scope of the library, just to state what I think is definitely in its scope that is in sharp contrast to the "replace whenever possible" suggested scope. Clearly there is scope for the library includes both cases where inheritance is a viable option and cases where it is not. I was trying to draw attention away from use cases that overlap with existing use of inheritance to highlight what the library makes possible that inheritance doesn't. I would say the motivation for this library is particularly to enable the things that inheritance doesn't cover very well. If people understand this library as only a more verbose and confusing way to do exactly the same thing they having been doing with inheritance then most of the value of the library will be overlooked. Type erasure and inheritance really isn't an either/or proposition. They play well together. One thing I like about this library is it shifts the focus of design away from "objects" and to "interfaces", where I think it belongs. Providing an interface that uses TypeErasure and additionally providing an abstract base class that the user of the interface can inherit from if they choose for convenience in satisfying the concepts required by the interface would be perfectly valid interface design decision, and probably a good idea for interfaces that require complex concepts to lower the bar for using the interface enough to get the majority of C++ programmers over it. Regards, Luke

Luke:
A real example from an application I have worked on is that two object types inherit (eventually) from the same base type and each has an interface for a concept they have in common. Getting some attribute of the object. However, that interface isn't a method of the (common) base class and is declared differently for each of the two types. Epic fail. With TypeErasure you can declare an interface that accepts any object of either type and knows how to map the concept they have in common to the disparate interfaces of the various types without needing to fix the broken inheritance hierarchy, which is often not feasible due to the mass of legacy code that may depend upon it. While such a situation might seem contrived, I would say it is pretty typical of legacy C++.
The classic OOP pattern is delegation, which you definitely have to do if they're different (though any<> might translate int to double). http://en.wikipedia.org/wiki/Delegation_pattern#C.2B.2B_example_.28complex.2... Any<> might solve the problem where you need a random subset of functions in two classes that you want to use for some interface. OOP says "shouldn't that concept have a name?" Marying up two complex libraries is never easy. Dave Abrahams wrote:
Sure. For new code, it's reasonable to consider a value-based paradigm in lieu of legacy inheritance paradigms. Dave I think generally if you personally were going to start a new project you'd try hard to avoid fat interfaces anyway. Can you picture yourself asking a user to implement an interface with a dozen methods on a object?
I completely agree with using functors for simple interfaces. There need to be good utilities to type-delete them. Consider this big monster object with this interface. It's not movable/copyable (maybe it has its own threads). I think you'd find something like this acceptable. struct monster : noncopyable{ typedef signal<void ()>::slot_type slot; connection subscribeA(slot); connection subscribeB(slot); connection subscribeC(slot); ... }; Now let's say for some reason we need run time polymorphism two impls of monster. I would propose making subscribe* virtual. Would you really do something different? Would you do something like this? typedef function< connection (slot)> S; typedef tuple<S,S,S> deleted_monster; template<T> deleted_monster make_ deleted_monster (T&); // uses names ABC I think the use case for value based interface for a fat interface doesn't come up much. Chris

on Fri Jun 15 2012, "Hite, Christopher" <Christopher.Hite-AT-partner.commerzbank.com> wrote:
I can't, but I don't see the relevance.
I completely agree with using functors for simple interfaces.
By "functors" do you mean runtime-polymorphic function objects?
If it's not movable/copyable it has already failed the test. The idea is to construct a world of values, and this is not one.
Didn't we say this was about new code? If you've got legacy stuff like this you do what you have to do.
I don't understand what point you're trying to make. -- Dave Abrahams BoostPro Computing http://www.boostpro.com

Dave Abrahams wrote:
"Deferred Callable Object" http://www.boost.org/doc/libs/1_49_0/libs/fusion/doc/html/fusion/functional/...
If it's not movable/copyable it has already failed the test. The idea is to construct a world of values, and this is not one. What "test"? You mean for any. Well you could use any<...,_self&> with it. I'm going to assume your "idea" is meant generally for all/most C++.
Let's just say you've got to write a little monster like the one above. It's got threads, io_service, mutexes, signal2, containers, sockets, etc inside it that don't like being moved/copied and don't need to be. What would you do Dave? Would you prefer to put all of monster's guts in a shared_ptr-ed pimple so monster can be copied? This is an honest question. I may be a couple steps behind on best practices of C++. I'd probably give the user of monster an object he can't move/copy. Is that generally bad? Do you think all objects be copy/movable? Here's a point asio::sockets moveable but not copyable to maintain 1:1 relationship with external resources. http://www.boost.org/doc/libs/1_49_0/doc/html/boost_asio/reference/basic_str... If a user tries to copy a socket, he probably doesn't really want the fd copied.
This scenario could happen to you in the future. How would you write monster? At a later point how would you achieve polymorphism of two impls of monster?
I don't understand what point you're trying to make. I'm trying to figure out if you prefer extremely functionally oriented code. Why expose member functions at all? We could just give the user a collection of free type-deleted functors.
For any copyable object I could take all the member functions, bind them with the object, type-delete and return them in a tuple or a fusion::map with name tags. The user can then take any subset of those functions to be an interface. I think Haskall programmers think like this: http://www.haskell.org/haskellwiki/OOP_vs_type_classes#Packing_data_.26_func... Seeing as how C++11 has gotten functional with lambda expressions and perfect function forwarding, maybe I need to go check out Haskell. Chris

on Mon Jun 18 2012, "Hite, Christopher" <Christopher.Hite-AT-partner.commerzbank.com> wrote:
Sorry, I don't follow any of this. When you say "functors," what do you mean? That word has a formal meaning in computer science that is probably not what you mean, and an informal meaning in C++ (just another word for "function object") that doesn't /seem/ to be what you mean.
I mean, it's not relevant to the argument I'm making about the feasibility of coding entirely with type-erased value semantics in lieu of traditional OOP polymorphism.
You mean for any.
No
Well you could use any<...,_self&> with it. I'm going to assume your "idea" is meant generally for all/most C++.
All non-legacy.
Let's just say you've got to write a little monster like the one above.
I don't need to.
I wouldn't write a monster in the first place.
I think it's 99.9% reasonable to make all objects movable, and maybe 75% reasonable to make all objects copyable. You can do either or both of these, the difference is a matter of degree: just how uncompromising do you have to be to build a world that makes sense?
I wouldn't do it.
I often do.
Why expose member functions at all? We could just give the user a collection of free type-deleted functors.
Because that, by itself, wouldn't achieve runtime polymorphism. You'd still need to bind the "type-deleted functors" (by which I presume you mean e.g. std::function) to the object somehow.
I recommend it, for mind expansion value if nothing else. -- Dave Abrahams BoostPro Computing http://www.boostpro.com

Dave Abrahams wrote:
Perhaps it's abuse of the term, but I use "functor" to mean callable, usually copyable object (including function pointer).
I think we're talking about the same thing. Would you perfer I use "function object"?
I wouldn't write a monster in the first place. You believe in OOP encapsulation (separation of problems), right? I think if you had this kind of problem, you'd also try to hide the details behind some minimal interface. I can understand you proposing a better interface.
I can't imagine you saying "C++ programmers shouldn't do network programming". So imagine a good C++ programmer like has yucky details to encapsulate: * networking protocol * TCP sockets - to multiple servers * UDP/multicast sockets - which should join/leave when needed * containers for subscriptions - say B's have ids * unsubscribe - optionally lazy * threads - single or pool of threads All the users care about is subscribing and getting updates. What should the interface look like? Was I that far off? Did I try to hide too much?
I think it's 99.9% reasonable to make all objects movable, and maybe 75% reasonable to make all objects copyable. You can do either or both of these, the difference is a matter of degree: just how uncompromising do you have to be to build a world that makes sense? A good candidate for the 25% is any object that is supposed to represent and external resource. Example: a TCP session with some other process. It makes sense to have a non-copyable object which exclusively owns that session whose deconstructor logs out and disconnects. You can copy the fd. You could connect a second time. You probably can not tell the server on the other side to copy all the state from associated with one session to another.
Here's the 0.1% case I tend to have, but maybe you think it's bad form. If an object has members which are needed for synchronization with a private thread, it can't be moved. Say I've got an class that has a private member mutex and thread it uses. I can't safely just move the mutex. A move constructor would have to * tell the worker thread to go park itself and wait for instructions from some temporary synchronization object * wait until it's parked * move the inards of my object including mutexes * tell the thread to continue using the new location That seems hard and error prone. So I'd never implement it unless there was a need. It might be done easliy by smart_ptr<> pimple but that costs something too. It's also something a user of an object can do. Let the user decide how ownership should work. Anyway such a pimple type though private would still be a 0.1% type. Here's a nice example: boost::asio::io_service isn't movable. Should it be? I wouldn't call asio legacy code. To me it appears to be the latest style of interface. Chris

on Wed Jun 20 2012, "Hite, Christopher" <Christopher.Hite-AT-partner.commerzbank.com> wrote:
I don't follow this either.
Frankly, I would (the C++ community never should have started using "functor" that way), but that's not the point. You seemed to mean a particular kind of function object; a dynamically-polymorphic kind, i.e. one with type erasure, e.g. boost::function or std::function. Now it seems you mean the term more generally. Your earlier reference to fusion makes it seem like when anyone says "polymorphic" you automatically think of static polymorphism. Most people think of dynamic polymorphism, especially when the word "runtime" precedes "polymorphic."
I believe in both encapsulation and separation of concerns, but on OOP I reserve judgement.
Me neither.
It should be movable at least.
Was I that far off? Did I try to hide too much?
I don't think of it that way.
Would you prefer to put all of monster's guts in a shared_ptr-ed pimple so monster can be copied?
Only if your pet monster is immutable. Otherwise, no.
Why not give him one he can move, so e.g. he can put it in a vector without getting into dynamic allocation?
Yes. Those are typically movable and non-copyable in the standard library (e.g. thread, mutex, ...).
You have created a piece of shared, mutable state (the mutex). To move it (a mutating operation), you'd need to at /least/ synchronize... which sorta "begs the question."
Right. Such an object should be treated as const (and thus non-movable) whenever it is being accessed by multiple threads.
It might be done easliy by smart_ptr<> pimple but that costs something too.
Yes, but not much compared to everything your monster has to pay in taxes and protection money just to stay in business.
It's also something a user of an object can do.
Not the way I'm thinking of it. I was thinking you might pimpl-ize just the parts that need to have stable addresses, e.g. use a unique_ptr<Mutex> internally.
Let the user decide how ownership should work.
Another way to say that is "make the user decide how ownership should work."
Maybe; I don't know that class real well. Has ASIO been move-enabled at all?
I wouldn't call asio legacy code. To me it appears to be the latest style of interface.
By "legacy code" I just mean "existing code that isn't move-enabled." -- Dave Abrahams BoostPro Computing http://www.boostpro.com

Dave Abrahams wrote: >>> > Consider this big monster object with this interface. It's not >>> > movable/copyable (maybe it has its own threads). >>> > I think you'd find something like this acceptable. >>> > >>> > struct monster : noncopyable{ >>> > typedef signal<void ()>::slot_type slot; >>> > >>> > connection subscribeA(slot); >>> > connection subscribeB(slot); >>> > connection subscribeC(slot); >>> > ... >>> > }; >>> Would you prefer to put all of monster's guts in a shared_ptr-ed >>> pimple so monster can be copied? > Only if your pet monster is immutable. Otherwise, no. I'm not sure what you mean by immutable. It has deep state. Users can change that state. However someone could say a shared_monster is immutable the pointer to its guts can't be changed. >> This is an honest question. I may be a couple steps behind on best >> practices of C++. I'd probably give the user of monster an object he >> can't move/copy. > Why not give him one he can move, so e.g. he can put it in a vector without getting into dynamic allocation? Why not give him a unmovable object and let him put it in a smart pointer, deciding if he wants a singlton, single owner or shared instance? If we start off with an unmovable object which does no dynamic allocation. A movable verson probably forces it on the user. > Yes. Those are typically movable and non-copyable in the standard library (e.g. thread, mutex, ...). Nevin Liber points out +> I don't believe that std::mutex is movable mutex isn't movable. Which seems logical to me. mutex::lock() isn't const either. > > Here's the 0.1% case I tend to have, but maybe you think it's bad > > form. If an object has members which are needed for synchronization > > with a private thread, it can't be moved. Say I've got an class that > > has a private member mutex and thread it uses. I can't safely just > > move the mutex. > You have created a piece of shared, mutable state (the mutex). To move it (a mutating operation), you'd need to at /least/ synchronize... > which sorta "begs the question." Yeah that's my point. An object encapsulating a thread needs extra (questionable) complexity to support move. > > A move constructor would have to > > > > * tell the worker thread to go park itself and wait for instructions > > from some temporary synchronization object > > * wait until it's parked > > * move the inards of my object including mutexes > > * tell the thread to continue using the new location That seems hard > > and error prone. So I'd never implement it unless there was a need. > Right. Such an object should be treated as const (and thus non-movable) whenever it is being accessed by multiple threads. The thread is on the inside, probably started in the constructor. Would you make all other methods besides ctor/dtor const? There's no const-ctor in C++. > Not the way I'm thinking of it. I was thinking you might pimpl-ize just the parts that need to have stable addresses, e.g. use a unique_ptr<Mutex> internally. But that won't work! When the encapsulated thread wakes up and locks the mutex, he'll need a way back to the object. You'd need structure with: * the mutex * any condition variables * a pointer back to the object - protected by mutex, modified by move Your thread also must be outside any non-static member functions of the object unless it has the lock. It can't loop in moster::run if _this_ can be changed. The unmovable version might have had state that was private to the worker and could be modified outside a lock. So you've either increased contention on the mutex, or might try introducing a special move mutex which adds its own complexity. BTW that move support structure wouldn't itself be movable . I don't know if that breaks your style or not. > > Let the user decide how ownership should work. > Another way to say that is "make the user decide how ownership should work." Isn't that how C++ works? The use decides where in memory to construct an object. The object decides if it's movable. > > Here's a nice example: boost::asio::io_service isn't movable. Should > > it be? > Maybe; I don't know that class real well. Has ASIO been move-enabled at all? sockets: move but no copy - which we agree with http://www.boost.org/doc/libs/1_49_0/doc/html/boost_asio/reference/basic_stream_socket.html io_service: no move, no copy http://www.boost.org/doc/libs/1_49_0/doc/html/boost_asio/reference/io_service/io_service.html stand http://www.boost.org/doc/libs/1_49_0/doc/html/boost_asio/reference/io_service__strand/strand.html no copy; no move. Makes sense to me since it's a synchronization object like io_service is passed as non-const & to all sockets, which probably isn't your style since the user has to make sure io_service outlives its users. I'm guessing you'd perfer shared_ptrs. Dave, thanks for intruducing me to your "maximum mobility C++". I hope I haven't been too frustrating. Chris

on Fri Jun 22 2012, "Hite, Christopher" <Christopher.Hite-AT-partner.commerzbank.com> wrote:
"Deep" is not a relevant term in a value semantics world (in fact, it will break your ability to think about values). Does it have a modifiable state (value)? If so it's mutable. If it has no modifiable state, then it's immutable.
Users can change that state. However someone could say a shared_monster is immutable the pointer to its guts can't be changed.
If you pimplize it the way you've described, you have shared mutable guts and so its guts can be changed even if the shared_ptr is const.
Because it limits interoperability, and encourages people to pimpl-ize the whole thing, which encourages reference semantics and implicit sharing, and is harder to reason about.
If we start off with an unmovable object which does no dynamic allocation. A movable verson probably forces it on the user.
Not necessarily. It depends if you allow it to be moved after the thread is started.
Sorry; locks are movable but mutexes aren't.
If the thread is necessarily started in the ctor, and the move ctor moves the mutex, then the move ctor is necessarily broken and you might as well disable it.
Then don't move the object while there are multiple threads accessing it. If the threads are always alive, you simply can't move it and you might as well not make it movable.
The object may also decide where in memory to construct its parts.
Not at all. I learned something, too. -- Dave Abrahams BoostPro Computing http://www.boostpro.com

On 22 June 2012 11:03, Hite, Christopher < Christopher.Hite@partner.commerzbank.com> wrote:
It adds noise and makes it harder to reason about. You've Here is one example: When you see a vector<shared_ptr<Foo>>, is the author's intentions: a) Make Foo storable in a vector b) Foo is polymorphic c) Really meant to have shared ownership for these Foo objects Now, if the author made Foo copyable and type erasure took care of polymorphism, vector<shared_ptr<Foo>> is really about having shared ownership. -- Nevin ":-)" Liber <mailto:nevin@eviloverlord.com> (847) 691-1404

On 21 June 2012 19:06, Dave Abrahams <dave@boostpro.com> wrote:
To be fair, most of the other C++11 containers have ways to put elements in them that don't require moveability/copyability on the element type. For deque and list, you can use emplace_front or emplace_back, and for the node-based containers, you can use emplace. -- Nevin ":-)" Liber <mailto:nevin@eviloverlord.com> (847) 691-1404

On 15 June 2012 09:36, Hite, Christopher < Christopher.Hite@partner.commerzbank.com> wrote:
I think the use case for value based interface for a fat interface doesn't come up much.
It comes up as soon as I want a container of them. Since they are not copyable or movable given just a base class reference, I now have to clutter things up by externally managing them. Instead of a vector<Foo>, I have to use something like vector<unique_ptr<Foo>>. -- Nevin ":-)" Liber <mailto:nevin@eviloverlord.com> (847) 691-1404

Hi again, On Thu, Jun 14, 2012 at 2:45 AM, Dave Abrahams <dave@boostpro.com> [...]
Well, if you go all that way to have polymorphism on value types and you take references to them, I fail to see why you did the effort in the first place. You actually said:
and => reference semantics (- hard to reason about)
I am probably missing the point, but I am not sure where.
I'll be doing that, many thanks for the advice. Julien

on Thu Jun 14 2012, Julien Nitard <julien.nitard-AT-m4tp.org> wrote:
Yes. It's a small but important distinction. Value types with polymorphic behavior are meant to have proper copy semantics, but that doesn't necessarily mean you're expected to copy them "frequently." In general, you should avoid copying anything frequently. -- Dave Abrahams BoostPro Computing http://www.boostpro.com

AMDG On 06/06/2012 08:25 AM, Hite, Christopher wrote:
I don't have any SBO right now. I will at some point, but I want to make sure that I have the interface completely nailed down before I start getting into messy optimizations like this.
2) On a related note I could see the use of an any_ref type holding only a virtual table pointer and a ref to an object it doesn't own. Just two words no heaping. That pushes the storage problem out to the user.
I think you could do that optimization with a specialization where the 2nd parameter is a ref.
I've supported this since I started this rewrite of my library.
I intended to support this. It looks like I haven't implemented it yet, though.
I don't understand. If you want a base/templated derived class, the easiest thing is just to implement it that way.
Not possible with the current interface.
6) Could you add an example where it has a call operator like boost::function?
This is callable<R(T1, T2, ...)>. In Christ, Steven Watanabe

Chris:
Steven Watanabe:
I don't want to have to do it. Typically each time someone runs into one of these problems they do their type-deletion that way. Each time it's done for a particular use case, usually based on the signature of a callable concept. If you want more examples of this: asio::detail::descriptor_read_op => descriptor_read_op_base - deletes the handler type descriptor_read_op_base =>reactor_op - deletes the buffer type Could/would you do that more cleanly with an any<>? I think so. If you're OK with heap storage, instead of holding a base* you can hold an any<..>. I'm not so sure if you want control over the storage, though it looks like any<...,_self&> allows me to avoid the heap. There'd have to be an indirection more. I think it can be avoided by your library by exposing something you're forced to implement anyway. Chris

On 05/22/2012 10:34 PM, Steven Watanabe wrote:
I am not sure if this has already been a topic here, but I wonder if the any type can be default constructible? The docs state that this is possible via type_erase::constructible< _self() >, but it is not working, at least for me.

AMDG On 06/13/2012 01:46 PM, Karsten Ahnert wrote:
What do you want the default constructor to do? constructible<_self()> captures the default constructor of the stored type. However, it doesn't map to a default constructor of any<> because there would be no way to know what the contained type is. typedef mpl::vector<..., constructible<_self()> > Concept; T value; any<Concept> x(value); any<Concept> y(binding_of(x)); y contains a default constructed instance of T. Now, as far as having a default constructor that sets the any to a null state goes, I intended to implement it, but forgot about it. I should have it in before the review, though. In Christ, Steven Watanabe

On 05/22/12 15:34, Steven Watanabe wrote: [snip]
Online documentation can be found here: http://steven_watanabe.users.sourceforge.net/type_erasure/libs/type_erasure/
There's a small typo here: http://steven_watanabe.users.sourceforge.net/type_erasure/libs/type_erasure/... which contains: The last kind of constructor "upcasts" from one concept to another more general more general concept. which contains 2 'more general's. [snip]

On 06/16/12 13:54, Steven Watanabe wrote:
I tried creating the html from the svn download; however, I got errors: compilation; default-directory: "~/prog_dev/boost-svn/ro/boost_1_49_0/sandbox/ro/type_erasure/type_erasure/libs/type_erasure/doc/" -*- Compilation started at Sun Jun 17 09:14:16 bjam sh: icpc: not found ...patience... ...patience... ...found 2322 targets... ...updating 34 targets... common.mkdir /home/evansl/prog_dev/boost-svn/ro/boost_1_49_0/bin.v2/tools/quickbook common.mkdir /home/evansl/prog_dev/boost-svn/ro/boost_1_49_0/bin.v2/tools/quickbook/src common.mkdir /home/evansl/prog_dev/boost-svn/ro/boost_1_49_0/bin.v2/tools/quickbook/src/gcc-4.7n common.mkdir /home/evansl/prog_dev/boost-svn/ro/boost_1_49_0/bin.v2/tools/quickbook/src/gcc-4.7n/release common.mkdir /home/evansl/prog_dev/boost-svn/ro/boost_1_49_0/bin.v2/tools/quickbook/src/gcc-4.7n/release/link-static gcc.compile.c++ /home/evansl/prog_dev/boost-svn/ro/boost_1_49_0/bin.v2/tools/quickbook/src/gcc-4.7n/release/link-static/quickbook.o gcc.compile.c++ /home/evansl/prog_dev/boost-svn/ro/boost_1_49_0/bin.v2/tools/quickbook/src/gcc-4.7n/release/link-static/actions.o . . . gcc.link /home/evansl/prog_dev/boost-svn/ro/boost_1_49_0/bin.v2/tools/quickbook/src/gcc-4.7n/release/link-static/quickbook quickbook.quickbook-to-boostbook bin/gcc-4.7n/debug/type_erasure.xml /home/evansl/prog_dev/boost-svn/ro/boost_1_49_0/bin.v2/tools/quickbook/src/gcc-4.7n/release/link-static/quickbook: /usr/lib/libstdc++.so.6: version `GLIBCXX_3.4.15' not found (required by /home/evansl/prog_dev/boost-svn/ro/boost_1_49_0/bin.v2/tools/quickbook/src/gcc-4.7n/release/link-static/quickbook) "/home/evansl/prog_dev/boost-svn/ro/boost_1_49_0/bin.v2/tools/quickbook/src/gcc-4.7n/release/link-static/quickbook" -I"../../.." -I"/home/evansl/prog_dev/boost-svn/ro/boost_1_49_0" --output-file="bin/gcc-4.7n/debug/type_erasure.xml" "type_erasure.qbk" ...failed quickbook.quickbook-to-boostbook bin/gcc-4.7n/debug/type_erasure.xml... doxygen-action bin/gcc-4.7n/debug/reference-xml.xml-dir /bin/sh: doxygen: not found rm -f "bin/gcc-4.7n/debug/reference-xml/*.xml" & "doxygen" "bin/gcc-4.7n/debug/reference-xml.doxyfile" && echo "Stamped" > "bin/gcc-4.7n/debug/reference-xml.xml-dir" ...failed doxygen-action bin/gcc-4.7n/debug/reference-xml.xml-dir... ...skipped <pbin/gcc-4.7n/debug>reference-xml.doxygen for lack of <pbin/gcc-4.7n/debug>reference-xml.xml-dir... ...skipped <pbin/gcc-4.7n/debug>reference-xml.boostbook for lack of <pbin/gcc-4.7n/debug>reference-xml.doxygen... ...skipped <p.>reference.xml for lack of <pbin/gcc-4.7n/debug>reference-xml.boostbook... ...skipped <pbin/gcc-4.7n/debug>type_erasure.docbook for lack of <pbin/gcc-4.7n/debug-object(xinclude-scanner)@1310>type_erasure.xml... ...skipped <phtml>standalone_HTML.manifest for lack of <pbin/gcc-4.7n/debug>type_erasure.docbook... ...failed updating 2 targets... ...skipped 5 targets... ...updated 27 targets... Compilation exited abnormally with code 1 at Sun Jun 17 09:19:58 I can understand the doxygen errors (I don't have it installed); however, I don't know how to solve the GLIBCXX_3.4.15 error. Any ideas? TIA. -regards, Larry

On 06/17/12 09:27, Larry Evans wrote: [snip]

On 05/22/12 15:34, Steven Watanabe wrote: [snip]
Online documentation can be found here: http://steven_watanabe.users.sourceforge.net/type_erasure/libs/type_erasure/
The intro: http://steven_watanabe.users.sourceforge.net/type_erasure/libs/type_erasure/... lists 3 limitations of virtual functions and claims TypeErasure solves these problems. It would help me a lot to understand these limitations and how TypeErasure solves these problems by providing a before and after example code with explanation of how the problems in the before code(using virtual functions) is solved by the after code (using TypeErasure). [snip] -regards, Larry

On 05/22/12 15:34, Steven Watanabe wrote: [snip]
The library is available in the Boost Sandbox at http://svn.boost.org/svn/boost/sandbox/type_erasure/
The type_erasure version I have is: ~/prog_dev/boost-svn/ro/boost_1_49_0/sandbox/ro/type_erasure/type_erasure $ svn update At revision 79008. However, compiling multi.cpp with gcc4.8 (2012-04-22) gives a uninitialized constexpr error. compilation; default-directory: "~/prog_dev/boost-svn/ro/boost_1_49_0/sandbox/ro/type_erasure/type_erasure/libs/type_erasure/example/" -*- Compilation started at Tue Jun 19 08:36:12 bjam multi sh: icpc: not found ...patience... ...found 550 targets... ...updating 4 targets... common.mkdir bin/multi.test/gcc-4.8v common.mkdir bin/multi.test/gcc-4.8v/debug gcc.compile.c++ bin/multi.test/gcc-4.8v/debug/multi.o In file included from ../../../boost/type_erasure/binding.hpp:28:0, from ../../../boost/type_erasure/any.hpp:39, from multi.cpp:11: ../../../boost/type_erasure/detail/vtable.hpp:155:28: error: constexpr static data member 'value' must have an initializer static constexpr Table value; ^ ../../../boost/type_erasure/detail/vtable.hpp:159:52: error: redeclaration 'boost::type_erasure::detail::vtable_init<Table, T>::value' differs in 'constexpr' constexpr Table vtable_init<Table, T...>::value(T::value...); ^ ../../../boost/type_erasure/detail/vtable.hpp:155:28: error: from previous declaration 'boost::type_erasure::detail::vtable_init<Table, T>::value' static constexpr Table value; ^ ../../../boost/type_erasure/detail/vtable.hpp:159:52: error: 'boost::type_erasure::detail::vtable_init<Table, T>::value' declared 'constexpr' outside its class constexpr Table vtable_init<Table, T...>::value(T::value...); ^ ../../../boost/type_erasure/detail/vtable.hpp:159:52: error: declaration of 'const Table boost::type_erasure::detail::vtable_init<Table, T>::value' outside of class is not definition [-fpermissive] constexpr Table vtable_init<Table, T...>::value(T::value...); ^ "/home/evansl/download/gcc/4.8-20120422/install/bin/g++" -ftemplate-depth-128 -Wl,-rpath -std=gnu++11 -DCXX0X_VARIADIC_TEMPLATES -DBOOST_USE_MPL_VARIADIC_TEMPLATES -O0 -fno-inline -Wall -g -fPIC -I"../../.." -I"/home/evansl/prog_dev/boost-svn/ro/boost_1_49_0" -c -o "bin/multi.test/gcc-4.8v/debug/multi.o" "multi.cpp" ...failed gcc.compile.c++ bin/multi.test/gcc-4.8v/debug/multi.o...

On 06/19/12 08:42, Larry Evans wrote:
template<class Table, class... T> struct vtable_init { static constexpr Table value=Table(T::value...); }; from: template<class Table, class... T> struct vtable_init { static constexpr Table value; }; template<class Table, class... T> constexpr Table vtable_init<Table, T...>::value(T::value...); may be a solution. -regards, Larry

On 06/27/12 10:04, Larry Evans wrote:
Or not. When the attached is compiled with gcc4.8 with -std=gnu++11, there's a link error :( ./concept_call_noph.o: In function `boost::type_erasure::binding<boost::type_erasure::incrementable<int>
collect2: error: ld returned 1 exit status
I don't know why :( -regards, Larry

On 05/22/12 15:34, Steven Watanabe wrote:
The page: */libs/type_erasure/doc/html/boost_typeerasure /concept.html#boost_typeerasure.concept.concept_map with section title, "Concept Maps", seems to be about specializing a builtin concept. I don't see anything in that section about a map. A more descriptive title would be, "Specializing builtin concepts" with a link to: */libs/type_erasure/doc/html/boost_typeerasure/predef.html -regards, Larry

On 06/21/12 09:38, Steven Watanabe wrote: [snip] that familiar with concepts and I'm probably not the only one reading about type_erasure in that situation. A google on "c++ concept map" showed: http://en.wikipedia.org/wiki/Concepts_%28C%2B%2B%29#Concept_maps which would be a good target of the link, or maybe a definition somewhere else in you documents.

Steven Watanabe-4 wrote
I don't know if what is behind your "Concept Map" couldn't be renamed to Signature Map as I guess that you are mapping only signatures, and not types or data members. Am I missing something? Best, Vicente -- View this message in context: http://boost.2283326.n4.nabble.com/Formal-Review-Request-TypeErasure-tp46303... Sent from the Boost - Dev mailing list archive at Nabble.com.

Steven Watanabe wrote:
FWIW - Personally I've always found the word "concept" used in this context as extremely unexpressive and misleading. I believe that this naming has prevented this extremely useful idea from spreading as far within the C++ community as it deserves to be. Maybe we should start referring to this idea as "Type Requirements". I never figured out what "concept map" is supposed to mean. Maybe you've cleared that up for me. Maybe it should be referred to "Type Requirement Inheritance" Robert Ramey

On 2012-06-21 18:45, Robert Ramey wrote:
Shouldn't that be "Type Requirement Specialization" or perhaps "Type Requirement Instantiation"? Burk.-- Burkhard Daniel * mail@burkhard.net * http://www.burkhard.net Our lives are poems sung against the wind, with nothing but our voices to make a difference.

on Thu Jun 21 2012, "Robert Ramey" <ramey-AT-rrsd.com> wrote:
I never figured out what "concept map" is supposed to mean.
A concept map specifies the way that a concept relates to a particular model (or set of related models) of that concept. Most of us are only used to thinking of an "identity concept map," e.g. the concept says there must be a "+" operation and the model supplies that operation as an operator+(). Concept maps allow a generalization where the model might supply the operation as a function called "add()." You'd use a concept map to connect the concept's "+" operation to the model's "add()" function. This feature enables post-hoc adaptation, wherein if you find a type T that models a concept C "in spirit" you can make it model that concept *in fact* without the authors of T and C knowing anything about one another. HTH, -- Dave Abrahams BoostPro Computing http://www.boostpro.com

On 06/21/12 09:38, Steven Watanabe wrote:
The page: */libs/type_erasure/doc/html/boost_typeerasure/conceptdef.html uses the term 'primitive concept': A primitive concept must be a specialization of a class template, with a static member function called apply. What's the difference between a 'Concept Map' and 'primitive concept'? If they are the same, why not use the same term for both? [snip] -regards, Larry

AMDG On 06/21/2012 10:34 AM, Larry Evans wrote:
They aren't the same. A primitive concept specified the interface and (optionally) a default Concept Map. A Concept Map specifies how a specific type models the concept. In Christ, Steven Watanabe

Steven Watanabe-4 wrote
I read the docs and didn't find the concept naming confusing (on the contrary, it's "standard" terminology). However, I agree that a Wiki link to concepts would be useful from in the docs as not all readers will be familiar with the C++0x concept proposals: http://en.wikipedia.org/wiki/Concepts_(C%2B%2B) --Lorenzo -- View this message in context: http://boost.2283326.n4.nabble.com/Formal-Review-Request-TypeErasure-tp46303... Sent from the Boost - Dev mailing list archive at Nabble.com.

AMDG On 06/21/2012 04:49 PM, lcaminiti wrote:
The only use of "Concept Map" is the title of the section. I believe that the contents of the section make it sufficiently clear what I'm talking about. In Christ, Steven Watanabe

On 06/21/12 21:38, Steven Watanabe wrote: link to
The contents say: Sometimes it is useful to non-intrusively adapt a type to model a concept. For example, suppose that we want to make std::type_info model less_than_comparable. To do this, we simply specialize the concept definition. which is clear, especially the part: specialize the concept definition. However what is not clear is how the title, "Concept Map", is related to what's in the content. That's what prompted my original post in this sub-thread: http://article.gmane.org/gmane.comp.lib.boost.devel/231814 Your response: b) "Concept Map" is the concept term for specialization. shown here: http://article.gmane.org/gmane.comp.lib.boost.devel/231815 prompted me to google for "C++ Concept Map" which lead to here: http://en.wikipedia.org/wiki/Concepts_%28C%2B%2B%29#Concept_maps and only after reading that was the rationale for the title clear. (However, after thinking some more, it seems a combination of the concept definition *and* the concept_interface corresponds to the "Concept Map" shown in the wikipedia article. That's because the concept definition has no member functions (such as operator+ or push_back) defined in it. The concept definition, AFAICT, only needs to supply a static member function called apply. */libs/type_erasure/doc/html/boost_typeerasure/conceptdef.html The member functions are added by concept_interface. libs/type_erasure/doc/html/boost_typeerasure/concept.html#boost_typeerasure.concept.custom Only after the member functions are added by the concept_interface is an entry for the concept map complete. I guess, here, the map is from a type, T, to an any type like T but with the added member functions added by the combination of the concept definition and the concept_interface. Is that about right? ). In summary, as you say, the content is clear, but the rationale for the title is not, unless a link to something like the wikipedia article is provided. HTH -regards, Larry

On 06/21/12 13:17, Steven Watanabe wrote:
Would not: template<class T = _self, class U = T> struct less_than_comparable { static bool apply(const T& lhs, const U& rhs) { return lhs < rhs; } }; from: http://svn.boost.org/svn/boost/sandbox/type_erasure/boost/type_erasure/opera... satisfy the definition you give above for a primitive concept: A primitive concept specified the interface and (optionally) a default Concept Map. Where the optional default Concept Map is the general template, as shown in the above operator.hpp. Somehow, I assume by "specified the interface" you mean, somehow, the specialization idea: A primitive concept must be a specialization of a class template, with a static member function called apply. from: */libs/type_erasure/doc/html/boost_typeerasure/conceptdef.html IOW, an example of "specified the interface" would be the specialization of less_than_comparable, less_than_comparable<std::type_info> shown here: */libs/type_erasure/doc/html/boost_typeerasure/concept.html#boost_typeerasure.concept.concept_map Of course, I could be completely wrong; hence, could you be more specific about what you mean by "specified the interface" and "(optionally) a default Concept Map"? I can't help but draw the conclusion that a "Concept Map" is simply the general template and all specializations of a: class template, with a static member function called apply as mentioned in conceptdef.html; however, I also feel that's way too simple. What am I missing? TIA. -regards, Larry

AMDG On 06/21/2012 06:00 PM, Larry Evans wrote:
You're making this way more complex than it needs to be. The section called "Concept Maps" is part of the tutorial and explains how to implement something that corresponds to a concept_map in the C++ standard concept proposals. I'm not using the term "Concept Map" in any formal way. conceptdef.html on the other hand is my attempt to define the requirements /precisely/. As such it necessarily includes a lot of things that are discussed in a less formal (and more approachable) way, elsewhere. In Christ, Steven Watanabe

On 05/22/12 15:34, Steven Watanabe wrote: [snip]
*/libs/type_erasure/doc/html/boost_typeerasure/concept.html#boost_typeerasure.concept.custom states: Let's define a concept to allow push_back on a sequence. To do this, we create a class template with a template parameter for each argument, and a static member function called apply that calls push_back. Now the example following that paragraph does have a placeholder; however, the quoted paragraph doesn't state that it's needed. */libs/type_erasure/doc/html/boost_typeerasure/conceptdef.html states: The template parameters of the concept may involve placeholders. * Each template argument may be a cv and/or reference qualified placeholder type. * If a template argument is a function type, its arguments and return type may be cv/reference qualified placeholders. but, because of the 'may's it doesn't require a placeholder. Now, obviously, the placeholder is the only way to retrieve the value of the data stored in the any, and you could reason that a placeholder is obviously needed; however, I think that needs to be explicitly stated so there's no confusion (I confess, I was actually confused by this and it took several trials before I finally concluded that a placeholder was required). The attached file, when compiled with: #define MODEL_DEFAULT_SELF compiles and runs OK. However, with: //#define MODEL_DEFAULT_SELF gcc4.8 fails to compile it and gives a very obscure error message containing: ../../../boost/type_erasure/call.hpp:530:89: required from 'typename boost::type_erasure::detail::call_result<Op, void(U0&)>::type boost::type_erasure::call(const Op&, U0&) [with Op = concept<>; U0 = int; typename boost::type_erasure::detail::call_result<Op, void(U0&)>::type = void]' concept_ph.cpp:84:5: required from here ../../../boost/type_erasure/detail/get_signature.hpp:23:5: error: incomplete type 'void' used in nested name specifier BOOST_TYPEOF_NESTED_TYPEDEF_TPL(nested, &Concept::apply) ^ which seems to support the position that some placeholder is required in the concept's template arguments, AFAICT. HTH. -regards, Larry

AMDG On 06/26/2012 11:41 AM, Larry Evans wrote:
That's correct. The errors that you're getting stem from a different cause. The library doesn't actually require any placeholders, although it isn't terribly useful without them.
That would be a bug. The code should not compile with either setting.
For some reason, gcc tends to make errors with call point to BOOST_TYPEOF. Anyway, I can compile the following just fine with MSVC 10. int i = 0; call(binding<incrementable<int> >(mpl::map0<>()), incrementable<int>(), i); // i = 1 The problems with your code are: a) call(concept<int>(), y) - The library cannot deduce the the binding because none of the arguments is an any. b) call(concept<_self>(), a_any) - concept<_self> is not part of the requirements of a_any. In Christ, Steven Watanabe

On 06/26/12 14:18, Steven Watanabe wrote:
I tried the b) case again and it does not compile. I must of accidentally rm'ed the concept<> from the requirements while posting the code. I remember making a bunch of "minor" edits to make the code more presentable, and I must have failed to test compile it after the last edit :( Sorry for that typo. When corrected, and when: #define MODEL_DEFAULT_SELF the run is: ./concept_ph.exe :e=empty :apply(empty ) Thanks for the explanation.

On 06/26/12 14:18, Steven Watanabe wrote:
[snip]
But, AFAICT, that call doesn't have a concept as the 1st arg. To make the above call like the ones in my attached file, this would have to be: call(incrementable<int>(), i); The attached has that, and when it's compiled, it produces errors similar to the one previously mentioned here: http://article.gmane.org/gmane.comp.lib.boost.devel/232109 more specifically, the errors include: ../../../boost/type_erasure/detail/get_signature.hpp:23:5: error: incomplete type 'void' used in nested name specifier BOOST_TYPEOF_NESTED_TYPEDEF_TPL(nested, &Concept::apply) ^ However, now I realize that's probably because there's no any in the argument list, a flaw you mention in the a) problem below. So, I'm inferring from your call example, which includes the binding<...>(...) arg, that somehow an any arg provides something like a binding? It would help me if more documentation on the args to call were supplied. Initially, I had thought call just used the 1st arg(the concept) for it's type, and then just called that type's static apply with the remaining args after, maybe, substituting the placeholders with ... Hmm... now I'm not sure what I was thinking, but what's becoming clearer to me is I need some more clarification on how call works because I'm not having much success in using it so far :(

AMDG On 06/27/2012 08:48 AM, Larry Evans wrote:
I agree. Here's the requirements in unedited form: - The binding argument determines the dispatching. - The concept argument must be present in the binding. - If a binding is not specified it will be deduced from the arguments. At least one of the arguments must be an any. - Whether an argument is treated as an any is determined by the signature of the concept. Arguments that are placeholders are involved in the dispatching. An argument that is an /any/ in the signature of the concept gets no special handling.
In Christ, Steven Watanabe
participants (26)
-
Burkhard Daniel
-
Daniel James
-
Daniel Larimer
-
Dave Abrahams
-
Fabio Fracassi
-
Gordon Woodhull
-
Hite, Christopher
-
Jeremy Maitin-Shepard
-
Joaquín M López Muñoz
-
John Bytheway
-
Julien Nitard
-
Karsten Ahnert
-
Larry Evans
-
lcaminiti
-
Nathan Ridge
-
Neal Becker
-
Nevin Liber
-
Robert Ramey
-
Ronald Garcia
-
Sebastian Redl
-
Simonson, Lucanus J
-
Steven Watanabe
-
Stewart, Robert
-
Thomas Petit
-
Vicente Botet
-
Vicente J. Botet Escriba