
On Sat, May 10, 2008 at 7:39 PM, Marco Costalba <mcostalba@gmail.com> wrote:
On Sat, May 10, 2008 at 6:08 PM, Giovanni Piero Deretta <gpderetta@gmail.com> wrote:
Well my idea was that an unconstrained template function object would be bound to all internal boost::functions
You mean something like this
template<int> struct end_seq_tag { typedef int type; }; template<> struct end_seq_tag<-1> { typedef char type; };
template<int n, typename T> void assign(T const& t, int*) { /* first convert to boost::function then add */
*this += boost::function< at_c<n, Signatures>::type >(t);
assign<n - 1>(t, (end_seq_tag<n - 1>::type*)0); /* iterate */ }
template<int n, typename T> void assign(T const&, char*) {} /* end condition here */
template<typename T> function& operator=(T const& t) { assign<signatures_size - 1>(t, (end_seq_tag<signatures_size - 1>::type*)0); return *this; }
I have written fast, probably there is a better way, but just to show the idea. Is this what you mean ?
Not sure if the code would compile, but, yes, that would be the idea, except for the fact that in my ideal world MSF would have single copy of F.
I think that in your case, using operator= is confusing because you are not really reassigning the MSF but just adding an overload. What if operator= tries to bind all signatures at once (and thus allows for boost::function like adaptability) and operator+= (or an explicit add member function) is strict and allows to replace a single overload?
operator+= is a nice idea, like an explicit set() or add() method. I have been stick to operator=() just to follow boost::function API, but I agree in case of MSF this could be confusing...
Well, in boost::function, operator= replaces the content of the function, which is not true in MSF case. Operator+= might qualify as operator abuse, so an explicit set or add would be better (but personally have no strong opinion on this)
Where boost.function naturally models a function, multi-signature boost::function naturally models an overload set
Actually it is a bit more, because you can have different state for each overload. I would need something in the middle: a single polymorphic closure which can be accessed dynamically with multiple objects.
Sorry but the above sentence does not parse for me. I have to think again on what you meant, sorry, but could you better explain what you mean ? It's my fault to be dumb ;-)
Not sure of what you cannot parse, so I'll try to be as explicitly as possible. * A monomorphic function object is a function object which has a single type for each argument (if any) and a single result type. Think for example of the standard (C++03) binders. In practice the argument types are not strictly fixed, as conversions are allowed, or even are fully templated (for example boost::bind). The important part is that the result type is fixed. You can think of boost::function as a wrapper that erases the exact type of the function object and leaves only the signature. Dispatching to the correct type is done at runtime (via virtual functions or, equivalently, pointer to functions). boost::function works for every monomorphic function object as long as it is CopyConstructible. If the function object has state, it will be correctly stored in the boost::fuction: struct sum_with_double_and_print { typedef void result_type; void operator()(int i) { std::cout << i + state << '\n'; } sum_with_double_and_print(double state_) : state(state_) {} double state; }; boost::function<void(int)> f = sum_with_duble_and_print(0.7); f(10); // prints 10.7 * A polymorphic function object is a functor which works for multiple argument types, and for which the return value type is (meta)function of the argument types. To let client code deduce the result type you should implement the result_of protocol. // a stateless polymorphic function object: struct twice { template<class Sig> struct result; // the result type is simply the argument type, but in principle it can be an // arbitrary function of the argument type. template<class T> struct result<twice(T)> { typedef T type; }; template<class T> T operator()(T f) { return f + f; } }; std::cout << twice()(10); // print 20 std::cout << twice()(std::string("hello")); // print "hellohello" You can (meta)compute the result type of a polymorphic function object using boost::result_of: boost::result_of<twice(int)>::type i; // i is an int boost::result_of<twice(std::string)>::type j ;// j is a string. i = twice()(10); j = twice()(std::string("hello"); Now, what if I want to erase the actual type of the function object? If I use boost::function I have to fix the argument type: boost::function<int(int)>f1= twice(); f1(10); // ok, returns 20 f1(10.7); // bad, returns 20 again! f2(std::string("hello")); // doesn't even compile! which means that the function object is no longer polymorphic. Well, i can pass an int to f1 and it will (kind of) work, but it will certainly no longer work for std::string. That's where a multisignature function object would help: boost::msf_function<double(double), int(int), std::string(std::string)> f2 = twice(); // magic here! f2(10); // ok: 20 f2(10.7); // ok: 21.4 f2(std::string("hello")); // great: "hellohello" I.e. MSF is the natural extension of boost::function in the realm of polymorphic function objects (well, you still have to choose beforehand the types you want to work with, but it is still much better than boost::function). [ BTW, by a quick at the code of MSF you do not seem to implement the result_of protocol. You definitely should, as MSF is definitely a polymorphic function object,] Currently your interface does not support directly binding a polymorphic function object to all signatures, but as you shown, it can be done easily. Still your current implementation has a problem. You can of course have statefull polymorphic function objects: struct frob { template<class Sig> struct result; template<class T> struct result<frob(T)> { // this is technically not correct, it should be decltype(int() + T()) // which for now should use some kind of promotion traits. typedef T type; }; template<class T> T operator()(T i) { return i + state++; } fromb(int state_) : state(state_) {} int state; }; frob(10) x; x(10); // returns 20 x(10); // returns 21 x(10); // returns 22 x(10.42); // returns 23.42 x(my_big_int(231211251414whatever)); // returns.. well, you got it right? If I put frob in a MSF : msf_function<int(int), double(double), my_big_int(my_big_int)> x = frob(10); it has two downsides ... 1) it does N dynamic allocations, one for each signature. 2) it has N copies of 'frob::state', which for a small object as an int it is not a problem, but in principle it can be wasteful ... and a serious problem: As there are n different states, the increment is separate for each type, and not for all of them: x(10); // returns 20 x(10); // returns 21 x(10); // returns 22 x(10.42); // returns 20.42 !!! x(my_big_int(10)); // returns (my_big_int)21 ! In practice MSF acts as N different boost::function objects with a think type based dispatcher. I can of course fix the problem by adding another layer of indirection: struct frobp { // forward template<class S> struct result : frob::result<S> {}; frob(int i) : p(new frob(i)) {} boost::shared_ptr<frob> p; template<class T> T operator()(T i) { return (*p) (i); } } msf_function<int(int), double(double), my_big_int(my_big_int)> x = frobp(10); Which is ugly, verbose, and ineficient. What I want is MSF to hold a *single* instance of my function object and add to the dynamic dispatching layer one operator() for each of the registered functions (whether this is done via function pointers or virtual functions doesn't matter). In practice this means that you cannot use boost::function for the implementation of MSF (but you might reuse some of its implementation). Note that the old behaviour (one instance for every signature type), can be implemented easily on top of my preferred design, without loss of efficiency. HTH, -- gpd
Thanks Marco _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost