RFC: type erasure

AMDG Over the last few months I've re-done my type erasure library from scratch, fixing a lot of the bad design decisions I made originally. Here's a basic sample of what you can do with it: simulate boost::any: type_erasure::any< mpl::vector<copy_constructible<>, typeid_<> > > x(10); simulate boost::function<void(int)>: type_erasure::any< mpl::vector<copy_constructible<>, typeid_<>, callable<void(int)> > > f(foo); The code is available from the Vault: http://tinyurl.com/3z9jcwp The library is nearly complete. I've included pre-built html documentation in the zip. Questions, comments, and criticism are welcome. In Christ, Steven Watanabe

On Sun, May 22, 2011 at 6:18 PM, Steven Watanabe <watanabesj@gmail.com>wrote:
The code is available from the Vault: http://tinyurl.com/3z9jcwp
The library is nearly complete. I've included pre-built html documentation in the zip. Questions, comments, and criticism are welcome.
This sounds great, I'll try to check it out in the upcoming week. -- -Matt Calabrese

AMDG On 05/22/2011 03:58 PM, Matt Calabrese wrote:
On Sun, May 22, 2011 at 6:18 PM, Steven Watanabe <watanabesj@gmail.com>wrote:
The code is available from the Vault: http://tinyurl.com/3z9jcwp
The library is nearly complete. I've included pre-built html documentation in the zip. Questions, comments, and criticism are welcome.
This sounds great, I'll try to check it out in the upcoming week.
Thanks. It would be awesome if we could integrate this with your Generic library, so a single concept definition is sufficient for both. I think I can arrange it so defining a concept only requires a few forward declarations from my library. In Christ, Steven Watanabe

On Sun, May 22, 2011 at 7:27 PM, Steven Watanabe <watanabesj@gmail.com>wrote:
Thanks. It would be awesome if we could integrate this with your Generic library, so a single concept definition is sufficient for both.
Yeah, that would be amazing. I bet we could get something workable with simple concepts in not too much time, though right now I have other priorities for the library (I want to first get all of the concepts of N2914 working and tested). The complicated parts of applying type erasure here that I can think of are with associated types -- for instance, a type-erased container should probably automatically be working with type-erased iterators, right? So handling things correctly implies my library figuring out all of the constraints that affect an associated type in any way, which could be tricky, as constraints can be scattered around the encapsulating concept. While I can check the constraints fine, at a high level I don't really know which constraints affect which types, whether directly or indirectly, if you get what I'm saying. All I know is whether a given constraint is satisfied or not, but it's difficult to determine exactly which associated types are affected by which constraints since they can be referenced in any kind of arbitrary type expression. Anyway, this would be awesome to accomplish, especially if we could figure out a way to handle the general case, though, at least at my end, it's a ways off. I feel like Sean Parent talked a little bit about similar functionality with Poly. I'll check it out and try to keep all of this somewhere in my mind as I make further progress with the Generic library. -- -Matt Calabrese

AMDG On 05/22/2011 04:58 PM, Matt Calabrese wrote:
On Sun, May 22, 2011 at 7:27 PM, Steven Watanabe <watanabesj@gmail.com>wrote:
Thanks. It would be awesome if we could integrate this with your Generic library, so a single concept definition is sufficient for both.
Yeah, that would be amazing. I bet we could get something workable with simple concepts in not too much time, though right now I have other priorities for the library (I want to first get all of the concepts of N2914 working and tested). The complicated parts of applying type erasure here that I can think of are with associated types -- for instance, a type-erased container should probably automatically be working with type-erased iterators, right? So handling things correctly implies my library figuring out all of the constraints that affect an associated type in any way, which could be tricky, as constraints can be scattered around the encapsulating concept. While I can check the constraints fine, at a high level I don't really know which constraints affect which types, whether directly or indirectly, if you get what I'm saying.
Yeah. I don't really have a good way to handle associated types at all. I suspect that the way I'm using placeholders will interact badly with associated types in your macros.
All I know is whether a given constraint is satisfied or not, but it's difficult to determine exactly which associated types are affected by which constraints since they can be referenced in any kind of arbitrary type expression.
If the constraint is of the form SomeConcept<A, B, C>, I can use placeholders for the arguments, and pick it apart with TMP.
Anyway, this would be awesome to accomplish, especially if we could figure out a way to handle the general case, though, at least at my end, it's a ways off. I feel like Sean Parent talked a little bit about similar functionality with Poly. I'll check it out and try to keep all of this somewhere in my mind as I make further progress with the Generic library.
In Christ, Steven Watanabe

On Sun, May 22, 2011 at 8:32 PM, Steven Watanabe <watanabesj@gmail.com>wrote:
If the constraint is of the form SomeConcept<A, B, C>, I can use placeholders for the arguments, and pick it apart with TMP.
True. We should probably even be able to handle more complicated situations such as requires SomeConcept< OtherConcept< A >::value_type >; if I make my concepts themselves aware of the placeholders and define their associated types accordingly when those placeholders are used, though I haven't given much thought to it yet. -- -Matt Calabrese

Hi, Steven. This update is GREAT! Up to now, I had used the following idiom for Type Erasure: http://d.hatena.ne.jp/faith_and_brave/20110215/1297755842 It is easy to use this library than my idiom. 2011/5/23 Steven Watanabe <watanabesj@gmail.com>:
Over the last few months I've re-done my type erasure library from scratch, fixing a lot of the bad design decisions I made originally. Here's a basic sample of what you can do with it:
========================
Akira Takahashi mailto:faithandbrave@gmail.com blog : http://d.hatena.ne.jp/faith_and_brave/

Steven Watanabe-4 wrote:
AMDG
Over the last few months I've re-done my type erasure library from scratch, fixing a lot of the bad design decisions I made originally. Here's a basic sample of what you can do with it:
simulate boost::any:
type_erasure::any< mpl::vector<copy_constructible<>, typeid_<> > > x(10);
simulate boost::function<void(int)>:
type_erasure::any< mpl::vector<copy_constructible<>, typeid_<>, callable<void(int)> > > f(foo);
The code is available from the Vault: http://tinyurl.com/3z9jcwp
The library is nearly complete. I've included pre-built html documentation in the zip. Questions, comments, and criticism are welcome.
Hi, I've just taken a diagonal read at the documentation and the library seems really promising. I like how concepts are materialized using a template class that implements the default behavior of the concept and that can be specialized to map type to concepts. I understand that you need a second step to introduce the functions themselves specializing concept_interface. What I don't understand is the overloading issues. Could you use a concrete example to try to clarify the problems you have addressed? I would try to study more deeply your design in order to see the advantage it could provide to my library. While developing Boost.Opaque (available on the sandbox https://svn.boost.org/svn/boost/sandbox/opaque/libs/opaque/doc/html/index.ht...), I was confronted to a similar problem, that is, how to add in a declarative way, more operations to the underlying class. I have reached to manage with this using what I have called a meta-mixin. A MetaMixin is a meta-function having as nested type a Mixin. The archetype of a MetaMixin is struct MetaMixinArchetype { template (*typename Final, typename Base*) struct type : Base { ... }; }; Metamixins are is a similar way to your concepts, passing them in a mpl-sequence. I will appreciate a lot if you can take a look and comment the design of Boost.Opaque. Best, Vicente -- View this message in context: http://boost.2283326.n4.nabble.com/RFC-type-erasure-tp3542886p3545573.html Sent from the Boost - Dev mailing list archive at Nabble.com.

AMDG On 05/23/2011 02:34 PM, Vicente Botet wrote:
I like how concepts are materialized using a template class that implements the default behavior of the concept and that can be specialized to map type to concepts. I understand that you need a second step to introduce the functions themselves specializing concept_interface.
What I don't understand is the overloading issues. Could you use a concrete example to try to clarify the problems you have addressed?
Okay. Problem 1: Name hiding for member functions. typedef mpl::vector< callable<int(int)>, callable<double(double)>
test_concept;
If we just use the basic definition of concept_interface, the second overload will hide the first since the inheritance structure looks like: struct callable1 { int operator()(int); }; struct callable2 : callable1 { double operator()(double); }; For free functions, I had a problem akin to name hiding when I defined a single namespace scope overload with concept_interface arguments. Using an inline friend function that takes the derived type works with some care to avoid duplicate definitions. You can check the tests for details on what I expect to work. Most of the tests for specific concepts have a test_overload test case. In Christ, Steven Watanabe

Steven Watanabe-4 wrote:
AMDG
On 05/23/2011 02:34 PM, Vicente Botet wrote:
I like how concepts are materialized using a template class that implements the default behavior of the concept and that can be specialized to map type to concepts. I understand that you need a second step to introduce the functions themselves specializing concept_interface.
What I don't understand is the overloading issues. Could you use a concrete example to try to clarify the problems you have addressed?
Okay. Problem 1: Name hiding for member functions.
typedef mpl::vector< callable<int(int)>, callable<double(double)>
test_concept;
If we just use the basic definition of concept_interface, the second overload will hide the first since the inheritance structure looks like:
struct callable1 { int operator()(int); };
struct callable2 : callable1 { double operator()(double); };
I find this normal and expected behavior, so I don't see yet why do you need to avoid this hiding. I don't understand the use case of the example: when a using declaration will be needed?
For free functions, I had a problem akin to name hiding when I defined a single namespace scope overload with concept_interface arguments. Using an inline friend function that takes the derived type works with some care to avoid duplicate definitions.
I'm sorry but I don't reach to see what is the real problem. I guess it is because I don't see the need of introducing the using sentence. Please could you present a real concrete case of overloading free functions that needs two different specializations of concept interface?
You can check the tests for details on what I expect to work. Most of the tests for specific concepts have a test_overload test case.
I've read some of them. I guess I understand what you expect to work, but I don't see why you need to specialize twice the concept_interface class for the same concept. Best, Vicente -- View this message in context: http://boost.2283326.n4.nabble.com/RFC-type-erasure-tp3542886p3548241.html Sent from the Boost - Dev mailing list archive at Nabble.com.

AMDG On 05/24/2011 02:07 PM, Vicente Botet wrote:
Steven Watanabe-4 wrote:
On 05/23/2011 02:34 PM, Vicente Botet wrote:
I like how concepts are materialized using a template class that implements the default behavior of the concept and that can be specialized to map type to concepts. I understand that you need a second step to introduce the functions themselves specializing concept_interface.
What I don't understand is the overloading issues. Could you use a concrete example to try to clarify the problems you have addressed?
Okay. Problem 1: Name hiding for member functions.
typedef mpl::vector< callable<int(int)>, callable<double(double)>
test_concept;
If we just use the basic definition of concept_interface, the second overload will hide the first since the inheritance structure looks like:
struct callable1 { int operator()(int); };
struct callable2 : callable1 { double operator()(double); };
I find this normal and expected behavior, so I don't see yet why do you need to avoid this hiding.
It's normal in the sense that it's what C++ does by default. I don't see how it can possibly be the expected behavior that: struct func { template<class T> T operator()(T t) { return t; } }; typedef mpl::vector< callable<int(int)>, callable<double(double)>
test_concept;
func f1; any<test_concept> f(f1); f(1); // calls func::operator()<double>?? From the point of view of someone trying to use the library, the two instances of callable are equal. Having one hide the other is surprising.
I don't understand the use case of the example: when a using declaration will be needed?
I can't parse this sentence.
For free functions, I had a problem akin to name hiding when I defined a single namespace scope overload with concept_interface arguments. Using an inline friend function that takes the derived type works with some care to avoid duplicate definitions.
I'm sorry but I don't reach to see what is the real problem. I guess it is because I don't see the need of introducing the using sentence. Please could you present a real concrete case of overloading free functions that needs two different specializations of concept interface?
Consider operator+. Either the first argument or the second argument or both may be a type_erasure::any. I need two specializations, because there are two arguments. To use friend function defined inline I have to inject operator+ into exactly one of the arguments. I originally tried to handle free functions with namespace scope overloads like this: template<class Base, class T, class U, class R> typename rebind_any<Base, R>::type operator+( const concept_interface<addable<T, U, R>, Base, T>&, const U&); template<class Base, class T, class U, class R> typename rebind_any<Base, R>::type operator+( const T&, const concept_interface<addable<T, U, R>, Base, U>&); template<class Base1, class Base2, class T, class U, class R> typename rebind_any<Base, R>::type operator+( const concept_interface<addable<T, U, R>, Base1, T>&, const concept_interface<addable<T, U, R>, Base2, U>&); Unfortunately, this failed with the example print_sequence.cpp, because of ostreamable<_os, const char*>, ostreamable<_os, _t> Only one of these was being considered for overload resolution.
You can check the tests for details on what I expect to work. Most of the tests for specific concepts have a test_overload test case.
I've read some of them. I guess I understand what you expect to work, but I don't see why you need to specialize twice the concept_interface class for the same concept.
I couldn't get all the tests to pass with any other solution I tried. If you have a simpler way to specialize concept_interface for ostreamable or callable that passes all my tests I'd love to hear about it, but I've tried and this is the best I could come up with. To summarize the reasons for multiple specializations: - For member functions, the second and subsequent instances of a concept need to be treated differently from the first, because they need an extra using declaration (which would be ill-formed in the first occurrence). - For non-member functions, one specialization is needed for each argument that can be a type_erasure::any to make sure that the function gets added to the overload set regardless of what subset of the arguments is erased. In Christ, Steven Watanabe

Steven Watanabe-4 wrote:
AMDG
On 05/24/2011 02:07 PM, Vicente Botet wrote:
Steven Watanabe-4 wrote:
On 05/23/2011 02:34 PM, Vicente Botet wrote:
I like how concepts are materialized using a template class that implements the default behavior of the concept and that can be specialized to map type to concepts. I understand that you need a second step to introduce the functions themselves specializing concept_interface.
What I don't understand is the overloading issues. Could you use a concrete example to try to clarify the problems you have addressed?
Okay. Problem 1: Name hiding for member functions.
typedef mpl::vector< callable<int(int)>, callable<double(double)>
test_concept;
If we just use the basic definition of concept_interface, the second overload will hide the first since the inheritance structure looks like:
struct callable1 { int operator()(int); };
struct callable2 : callable1 { double operator()(double); };
I find this normal and expected behavior, so I don't see yet why do you need to avoid this hiding.
It's normal in the sense that it's what C++ does by default. I don't see how it can possibly be the expected behavior that:
struct func { template<class T> T operator()(T t) { return t; } };
typedef mpl::vector< callable<int(int)>, callable<double(double)>
test_concept;
func f1; any<test_concept> f(f1); f(1); // calls func::operator()<double>??
From the point of view of someone trying to use the library, the two instances of callable are equal. Having one hide the other is surprising.
I understand it now. It seems that I have forgotten this case on Boost.Opaque. Many many thanks.
For free functions, I had a problem akin to name hiding when I defined a single namespace scope overload with concept_interface arguments. Using an inline friend function that takes the derived type works with some care to avoid duplicate definitions.
I'm sorry but I don't reach to see what is the real problem. I guess it is because I don't see the need of introducing the using sentence. Please could you present a real concrete case of overloading free functions that needs two different specializations of concept interface?
Consider operator+. Either the first argument or the second argument or both may be a type_erasure::any. I need two specializations, because there are two arguments. To use friend function defined inline I have to inject operator+ into exactly one of the arguments.
I don't see if I understood it now. I suspect that you mean that either the first argument or the second argument or both may be a placeholder, isn't it? I originally tried to handle free functions with namespace scope overloads like this: ... Unfortunately, this failed with the example print_sequence.cpp, because of ostreamable<_os, const char*>, ostreamable<_os, _t> Only one of these was being considered for overload resolution.
You can check the tests for details on what I expect to work. Most of the tests for specific concepts have a test_overload test case.
I've read some of them. I guess I understand what you expect to work, but I don't see why you need to specialize twice the concept_interface class for the same concept.
I couldn't get all the tests to pass with any other solution I tried. If you have a simpler way to specialize concept_interface for ostreamable or callable that passes all my tests I'd love to hear about it, but I've tried and this is the best I could come up with. I'm just trying to understand what kind of problems you have addressed to see if I can have the same in Boost.Opaque. To summarize the reasons for multiple specializations: - For member functions, the second and subsequent instances of a concept need to be treated differently from the first, because they need an extra using declaration (which would be ill-formed in the first occurrence). - For non-member functions, one specialization is needed for each argument that can be a type_erasure::any to make sure that the function gets added to the overload set regardless of what subset of the arguments is erased. Thanks again for the clearer explanations. Best, Vicente -- View this message in context: http://boost.2283326.n4.nabble.com/RFC-type-erasure-tp3542886p3549166.html Sent from the Boost - Dev mailing list archive at Nabble.com.

On Mon, May 23, 2011 at 11:01 PM, Steven Watanabe <watanabesj@gmail.com> wrote:
Okay. Problem 1: Name hiding for member functions.
typedef mpl::vector< callable<int(int)>, callable<double(double)>
test_concept;
If we just use the basic definition of concept_interface, the second overload will hide the first since the inheritance structure looks like:
struct callable1 { int operator()(int); };
struct callable2 : callable1 { double operator()(double); };
This can be solved by an "using callable1::operator();" . I just had to solve a very similar problem 20 minutes ago, but in my case all the elements in the inheritance chains only had operator() in need of un-hiding. I guess the problem in your case is that in general your callable2 is not necessarily inheriting from something that is callable, that is, you are using operator() as the name but it could be anything. A solution is adding every possible name (but with special arguments so that they won't actually partecipate in overload resolution) in the most base class and adding an "using <every possible name> at every level of the inheritance: struct base { struct dont_call_me{} void operator()(dont_call_me); void operator+(dont_call_me); void construct(dont_call_me); } struct negable : base { using base::operator(); using base::operator+; using base::construct; void operator-(); // can't be overloaded so no risk of hiding } struct addable : negable { using negable::operator(); using negable::operator+; using negable::construct; whatever operator+(whatever); }; struct callable2 : addable { using addable::operator(); using addable::operator+; using addable::construct; whatever operator()(whatever); }; struct callable1 : callable2 { using callable2::operator(); using callable2::operator+; using callable2::construct; whateverelse operator()(whateverelse); } struct constructible : callable2 { using callable2::operator(); using callable2::operator+; using callable2::construct; void construct(args..) template<class ...T> constructible(T... args) { construct(args...); } // calls this construct or any in the base classes } Probably it will break in some cases I'm not foreseeing. Will definitely not work for user specified foo-able. HTH, -- gpd

AMDG On 05/24/2011 04:39 PM, Giovanni Piero Deretta wrote:
On Mon, May 23, 2011 at 11:01 PM, Steven Watanabe <watanabesj@gmail.com> wrote:
struct callable1 { int operator()(int); };
struct callable2 : callable1 { double operator()(double); };
This can be solved by an "using callable1::operator();" . I just had to solve a very similar problem 20 minutes ago, but in my case all the elements in the inheritance chains only had operator() in need of un-hiding.
I guess the problem in your case is that in general your callable2 is not necessarily inheriting from something that is callable, that is, you are using operator() as the name but it could be anything.
Exactly.
A solution is adding every possible name (but with special arguments so that they won't actually partecipate in overload resolution) in the most base class and adding an "using <every possible name> at every level of the inheritance:
I've used this solution in the past. Now, I think it's better to detect the second and subsequent instances and add a using declaration. The advantage is that there are no extra overloads, so &any<...>::operator() is unambiguous if there's only a single overload.
Probably it will break in some cases I'm not foreseeing. Will definitely not work for user specified foo-able.
In Christ, Steven Watanabe

AMDG On 05/23/2011 02:34 PM, Vicente Botet wrote:
I would try to study more deeply your design in order to see the advantage it could provide to my library. While developing Boost.Opaque (available on the sandbox https://svn.boost.org/svn/boost/sandbox/opaque/libs/opaque/doc/html/index.ht...), I was confronted to a similar problem, that is, how to add in a declarative way, more operations to the underlying class. I have reached to manage with this using what I have called a meta-mixin.
A MetaMixin is a meta-function having as nested type a Mixin. The archetype of a MetaMixin is
struct MetaMixinArchetype { template (*typename Final, typename Base*) struct type : Base { ... }; };
Metamixins are is a similar way to your concepts, passing them in a mpl-sequence.
I will appreciate a lot if you can take a look and comment the design of Boost.Opaque.
It looks like your MetaMixin is pretty much equivalent to what I did with concept_interface. For what you're trying to do, I don't see that it can really be improved much. I'd say that for the most part, you're better off without the extra complexity in my design. I have to generate both the interface and the dispatching code from a single specification. You only need to define forwarding functions. In Christ, Steven Watanabe

Steven Watanabe-4 wrote:
AMDG
On 05/23/2011 02:34 PM, Vicente Botet wrote:
I would try to study more deeply your design in order to see the advantage it could provide to my library. While developing Boost.Opaque (available on the sandbox https://svn.boost.org/svn/boost/sandbox/opaque/libs/opaque/doc/html/index.ht...), I was confronted to a similar problem, that is, how to add in a declarative way, more operations to the underlying class. I have reached to manage with this using what I have called a meta-mixin.
A MetaMixin is a meta-function having as nested type a Mixin. The archetype of a MetaMixin is
struct MetaMixinArchetype { template (*typename Final, typename Base*) struct type : Base { ... }; };
Metamixins are is a similar way to your concepts, passing them in a mpl-sequence.
I will appreciate a lot if you can take a look and comment the design of Boost.Opaque.
It looks like your MetaMixin is pretty much equivalent to what I did with concept_interface. For what you're trying to do, I don't see that it can really be improved much.
Thanks for your advice.
I'd say that for the most part, you're better off without the extra complexity in my design. I have to generate both the interface and the dispatching code from a single specification. You only need to define forwarding functions.
Why do you needed to generate the interface and forwarding the call separately? Is because you want in addition to be able to emulate concept maps? Aren't type-erasure and concepts orthogonal? What I mean is that your library is doing concept-erasure and that trying to solve just type-erasure could maybe be simpler. I'm wondering if type_erasure had a MPL sequence of something like my meta-mixins, the meta-mixing could always forward the call to a concept adapter if what we want is concept erasure. Please note that I don't understand yet the whole design of Boost.TypeErasure. Best, Vicente -- View this message in context: http://boost.2283326.n4.nabble.com/RFC-type-erasure-tp3542886p3546242.html Sent from the Boost - Dev mailing list archive at Nabble.com.

On 23/05/2011 00:18, Steven Watanabe wrote:
Over the last few months I've re-done my type erasure library from scratch, fixing a lot of the bad design decisions I made originally. Here's a basic sample of what you can do with it:
simulate boost::any:
type_erasure::any< mpl::vector<copy_constructible<>, typeid_<> > > x(10);
simulate boost::function<void(int)>:
type_erasure::any< mpl::vector<copy_constructible<>, typeid_<>, callable<void(int)> > > f(foo);
The code is available from the Vault: http://tinyurl.com/3z9jcwp
The library is nearly complete. I've included pre-built html documentation in the zip. Questions, comments, and criticism are welcome.
How does it compare to Adobe.Poly?

AMDG On 05/23/2011 02:57 PM, Mathias Gaunard wrote:
How does it compare to Adobe.Poly?
I'm not very familiar with Adobe.Poly, but from a quick glance, the biggest differences are a) My library is based on composing independent primitives. Poly seems to be designed for a straight linear hierarchy. b) I can capture constraints that affect multiple types. There's an example of this in print_sequence.cpp. I don't think Poly could handle this at all. In Christ, Steven Watanabe

On Sun, May 22, 2011 at 7:18 PM, Steven Watanabe <watanabesj@gmail.com>wrote:
AMDG
Over the last few months I've re-done my type erasure library from scratch, fixing a lot of the bad design decisions I made originally. Here's a basic sample of what you can do with it:
simulate boost::any:
type_erasure::any< mpl::vector<copy_constructible<>, typeid_<> > > x(10);
simulate boost::function<void(int)>:
type_erasure::any< mpl::vector<copy_constructible<>, typeid_<>, callable<void(int)> > > f(foo);
The code is available from the Vault: http://tinyurl.com/3z9jcwp
Hi Steven, The link is broken. I want to test your library, is there somewhere else where I can download it? Regards, Fernando.

AMDG On 07/20/2011 05:49 AM, Fernando Pelliccioni wrote:
On Sun, May 22, 2011 at 7:18 PM, Steven Watanabe <watanabesj@gmail.com>wrote:
The code is available from the Vault: http://tinyurl.com/3z9jcwp
The link is broken. I want to test your library, is there somewhere else where I can download it?
The vault no longer exists. All the existing content has been moved to github. You can get the file from: https://github.com/boost-vault/Miscellaneous In Christ, Steven Watanabe

On Wed, Jul 20, 2011 at 12:14 PM, Steven Watanabe <watanabesj@gmail.com>wrote:
AMDG
On 07/20/2011 05:49 AM, Fernando Pelliccioni wrote:
On Sun, May 22, 2011 at 7:18 PM, Steven Watanabe <watanabesj@gmail.com wrote:
The code is available from the Vault: http://tinyurl.com/3z9jcwp
The link is broken. I want to test your library, is there somewhere else where I can download it?
The vault no longer exists. All the existing content has been moved to github. You can get the file from: https://github.com/boost-vault/Miscellaneous
Hi Steven, I want to write something like this ... void test_iostream_concept() { typedef mpl::vector< ostreamable<>, destructible<> > requirements; typedef any< requirements, _self&> any_type; typedef std::vector<any_type> ostr_vec; ostr_vec vec; //boost iostreams io::stream<writer_1> writer1; io::stream<writer_2> writer2; io::stream<io::file_sink> file_writer; io::stream<io::null_sink> null_writer; std::ofstream f1("test.txt"); vec.push_back( any_type(writer1) ); vec.push_back( any_type(std::cout) ); vec.push_back( any_type(f1) ); ostr_vec::const_iterator it = vec.begin(); ostr_vec::const_iterator end = vec.end(); for ( ; it != end; ++it ) { std::cout << *it << std::endl; //(*it) << "hello " << "world!"; //(1) compile-time error. any_type do not support operator<< } } The line (1) do not compile because *any_type* do not has *operator<<.* I could create my *custom concept*, like this... template<class C, class T> struct bitwise_left_shift : primitive_concept<bitwise_left_shift<C, T>, void(C&, const T&)> { static void apply( C& ostr, const T& arg ) { ostr.operator<<(arg); //ostr << arg; } }; I used in this way ... it works!!! void test_custom_concept_bitwise_left_shift() { any<bitwise_left_shift<_self, *int*>, _self&> ostr( std::cout ); int i = 10; bitwise_left_shift<_self, int>()(ostr, i); } BUT!.... *ostr* object only works for *int's.* * * How do I get *ostr* works generically? * * Something like this... void test_custom_concept_bitwise_left_shift() { //pseudo-code, do not compiles any<bitwise_left_shift<_self, *T*>, _self&> ostr( std::cout ); int i = 10; bitwise_left_shift<_self, *T*>()(ostr, i); float ff = 10.98; bitwise_left_shift<_self, *T*>()(ostr, ff); } How I define *T* ? Many thanks! Fernando Pelliccioni.

AMDG On 07/20/2011 12:11 PM, Fernando Pelliccioni wrote:
Hi Steven,
I want to write something like this ...
void test_iostream_concept() { typedef mpl::vector< ostreamable<>, destructible<> > requirements;
typedef any< requirements, _self&> any_type;
typedef std::vector<any_type> ostr_vec; ostr_vec vec;
//boost iostreams io::stream<writer_1> writer1; io::stream<writer_2> writer2; io::stream<io::file_sink> file_writer; io::stream<io::null_sink> null_writer;
std::ofstream f1("test.txt");
vec.push_back( any_type(writer1) ); vec.push_back( any_type(std::cout) ); vec.push_back( any_type(f1) );
ostr_vec::const_iterator it = vec.begin(); ostr_vec::const_iterator end = vec.end();
for ( ; it != end; ++it ) { std::cout << *it << std::endl; //(*it) << "hello " << "world!"; //(1) compile-time error. any_type do not support operator<< }
}
In this case, you don't need type erasure. io::stream is a std::ostream. You'd be better off using std::vector<std::ostream*>.
The line (1) do not compile because *any_type* do not has *operator<<.*
I could create my *custom concept*, like this...
template<class C, class T> struct bitwise_left_shift : primitive_concept<bitwise_left_shift<C, T>, void(C&, const T&)> { static void apply( C& ostr, const T& arg ) { ostr.operator<<(arg); //ostr << arg; } };
I used in this way ... it works!!!
void test_custom_concept_bitwise_left_shift() { any<bitwise_left_shift<_self, *int*>, _self&> ostr( std::cout );
int i = 10; bitwise_left_shift<_self, int>()(ostr, i);
}
This is basically what ostreamable does. You can use ostreamable<_self, int>.
BUT!.... *ostr* object only works for *int's.* * * How do I get *ostr* works generically? * *
Something like this...
void test_custom_concept_bitwise_left_shift() { //pseudo-code, do not compiles
any<bitwise_left_shift<_self, *T*>, _self&> ostr( std::cout );
int i = 10; bitwise_left_shift<_self, *T*>()(ostr, i);
float ff = 10.98; bitwise_left_shift<_self, *T*>()(ostr, ff);
}
How I define *T* ?
Unfortunately, it's impossible to allow streaming arbitrary types like this. The dispatching required conflicts fundamentally with separate compilation. There are a few ways to approximate it, though. a) Use std::ostream. I would strongly encourage you to take this route if it is possible. b) Enumerate all the possible stream types, and use boost::variant<std::ostream*, ...>. c) Enumarate all the types that you need to work with: typedef mpl::vector< ostreamable<_self, int>, ostreamable<_self, float>, ...
concept;
any<concept, _self&> ostr(std::cout); In Christ, Steven Watanabe

Hi, On Wed, Jul 20, 2011 at 4:38 PM, Steven Watanabe <watanabesj@gmail.com>wrote:
AMDG
On 07/20/2011 12:11 PM, Fernando Pelliccioni wrote:
Hi Steven,
I want to write something like this ...
void test_iostream_concept() { typedef mpl::vector< ostreamable<>, destructible<> > requirements;
typedef any< requirements, _self&> any_type;
typedef std::vector<any_type> ostr_vec; ostr_vec vec;
//boost iostreams io::stream<writer_1> writer1; io::stream<writer_2> writer2; io::stream<io::file_sink> file_writer; io::stream<io::null_sink> null_writer;
std::ofstream f1("test.txt");
vec.push_back( any_type(writer1) ); vec.push_back( any_type(std::cout) ); vec.push_back( any_type(f1) );
ostr_vec::const_iterator it = vec.begin(); ostr_vec::const_iterator end = vec.end();
for ( ; it != end; ++it ) { std::cout << *it << std::endl; //(*it) << "hello " << "world!"; //(1) compile-time error. any_type do not support operator<< }
}
In this case, you don't need type erasure. io::stream is a std::ostream. You'd be better off using std::vector<std::ostream*>.
Sure, it is true, but, the example isn't complete, I need to add another Custom Concept, *left_shift + lockable (mutex).*
The line (1) do not compile because *any_type* do not has *operator<<.*
I could create my *custom concept*, like this...
template<class C, class T> struct bitwise_left_shift : primitive_concept<bitwise_left_shift<C, T>, void(C&, const T&)> { static void apply( C& ostr, const T& arg ) { ostr.operator<<(arg); //ostr << arg; } };
I used in this way ... it works!!!
void test_custom_concept_bitwise_left_shift() { any<bitwise_left_shift<_self, *int*>, _self&> ostr( std::cout );
int i = 10; bitwise_left_shift<_self, int>()(ostr, i);
}
This is basically what ostreamable does. You can use ostreamable<_self, int>.
Perfect!
BUT!.... *ostr* object only works for *int's.* * * How do I get *ostr* works generically? * *
Something like this...
void test_custom_concept_bitwise_left_shift() { //pseudo-code, do not compiles
any<bitwise_left_shift<_self, *T*>, _self&> ostr( std::cout );
int i = 10; bitwise_left_shift<_self, *T*>()(ostr, i);
float ff = 10.98; bitwise_left_shift<_self, *T*>()(ostr, ff);
}
How I define *T* ?
Unfortunately, it's impossible to allow streaming arbitrary types like this. The dispatching required conflicts fundamentally with separate compilation.
Ups!
There are a few ways to approximate it, though.
a) Use std::ostream. I would strongly encourage you to take this route if it is possible.
b) Enumerate all the possible stream types, and
use boost::variant<std::ostream*, ...>. c) Enumarate all the types that you need to work with:
typedef mpl::vector< ostreamable<_self, int>, ostreamable<_self, float>, ...
concept;
any<concept, _self&> ostr(std::cout);
mmmm, I have to do in a type unsafe way... Thanks a lot! Fernando.
participants (7)
-
Akira Takahashi
-
Fernando Pelliccioni
-
Giovanni Piero Deretta
-
Mathias Gaunard
-
Matt Calabrese
-
Steven Watanabe
-
Vicente Botet