
On Mon, May 12, 2008 at 1:39 PM, Giovanni Piero Deretta <gpderetta@gmail.com> wrote:
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 wrote:
Marco Costalba <mcostalba@gmail.com> wrote:
Where boost.function naturally models a function, multi-signature boost::function naturally models an overload set
I think this is a great explanation of your function object, Marco, and you should keep this focus. I would encourage you to consider seriously making this purpose explicit by renaming/refactoring your function object into something along the lines of overload_set - give it a name and interface that describes what the function object does (stores and dispatches callable objects based on call signatures similarly to the compiler's "overload set" for a function) rather than naming it according to how it does it (by maintaining multiple boost::functions). This is an interesting and potentially very useful utility by itself - a call-wrapper for multiple callable objects; a cross between a call-wrapper and container, really. <snip>
* 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.
<snip>
* 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.
That's a good comparison/explanation of poly- vs mono-morphic function objects. By this definition, Marco's multi-signature function is already polymorphic. Well, it is now that it supports result_of, which I thought it already did, but I must have misread something in another thread.
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");
This seems normal to me. But I don't see the point of your next step, Giovanni.
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.
twice() is already polymorphic. If you want to use it polymorphically, you don't need to do any additional work. Why wrap it in the first place?
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"
How does that help? You could just have written: twice f2; // no need for magic here! f2(10); // ok: 20 f2(10.7); // ok: 21.4 f2(std::string("hello")); // great: "hellohello" I don't see the point of wrapping twice().
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).
If you have to choose the types beforehand, then I'm not sure that this extension is so natural. I mean, I see what you're getting at - there is a need for a function object (call it polymorphic_function, for example) whose operator() would be templated. But I don't think this multi-signature function should be coerced into that function object. They're not the same thing. As I understand it, the point of boost::function is that it can defer/hold arbitrary callable objects; i.e. it promotes second-class builtins to first-class. For consistency, it also holds function objects, though there's no other point to it since function objects are already first-class and already deferred. It would be nice to have a call-wrapper, like polymorphic_function, that would do the same thing as boost::function (promote builtins, single interface for builtins and functors, etc.) but without "fixing" the types of polymorphic function objects. For example, with boost::function we can write: template<class T0, class T1> T0 call(function<T0(T1)> f, T1 t1) { return f(t1); } int g(int) { return 0; } struct h { int operator()(int) { return 0; } }; function<int(int)> f0 = g; function<int(int)> f1 = h(); call(f0, 0); call(f1, 0); With polymorphic_function we could write: template<class Magic> typename result_of< polymorphic_function<Magic>(int)
::type call(polymorphic_function<Magic> f) { return f(0); }
g0(g<int>, g<float>);
int g(int) { return 0; } struct h { template<class T> T operator()(T) { return 0; } }; polymorphic_function<Magic> f0 = g; polymorphic_function<Magic> f1 = h(); call(f0); call(f1); In other words, by some Magic, the types of the parameters at the call site do not need to be known when polymorphic_function defers/wraps a callable object. However, no matter how powerful the Magic, the following is not possible. template<class T> T g(T t) { return t; } // assigning an unresolved function polymorphic_function<Magic> f0 = g; // error call(f0); This is because the overload set generated by the function template g() has no type. Actually, an overload can't even be generated without knowing the types at the call site. You cannot promote a builtin function template to first-class and completely preserve the original's polymorphism. However, in some use-cases, we can get around this problem with something like a boost::overload_set that wraps multiple callable objects and can be used to approximate a "deferral" of the compiler's overload resolution. This is where the multi-signature function could help. template<class T> T g(T t) { return t; } overload_set< mpl::vector<int(int), float(float)>) polymorphic_function<Magic> f0 = g0; call(f0); I have some ideas about the Magic involved with polymorphic_function, but I'll save that for another thread. My point is just that Marco's multi-signature function is a solution to one problem but not both. <snip>
Currently your interface does not support directly binding a polymorphic function object to all signatures, but as you shown, it can be done easily.
I know this conversation has progressed elsewhere in this thread, and I'm not completely caught up. I think that binding multiple signatures to a single function object by default, without the user explicitly requesting it, may not be a good thing. My initial preference is to allow ref() for these situations, as Marco has suggested, but I'll reserve judgement until I can better see where y'all are going with it. Daniel Walker