
On Tue, May 13, 2008 at 7:59 PM, Marco Costalba <mcostalba@gmail.com> wrote:
On Tue, May 13, 2008 at 3:01 PM, Giovanni Piero Deretta <gpderetta@gmail.com> wrote:
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:
Have you tried to fix it with
msf_function<int(int), double(double), my_big_int(my_big_int)> x;
x = boost::ref( frob(10) );
Indeed boost::function supports ref() and cref() to avoid the copy when is not needed or when the copy is simply wrong. MSF supports ref() and cref() too and forwards them, still wrapped, to the underlying boost::function objects so that no copy of the wrapped polymorphic function object is made.
This has different semantics from what I wanted. I do not want to have to keep the wrapped object in the stack to add it in a wrapper. In this case I need I want MSF to copy my object (i.e. to manage its lifetime) but I want it to do only one copy (usually for efficiency, but sometimes like this case, for correctness).
Actually currently MSF does not copy anything, takes the functor object as const& and forwards to boost::function
Which holds a copy of the functor. It is not a problem of parameter passing, what matter is the number of long lived copies of my functor that end up being stored inside MSF. IMHO the user will expect only one copy of the functor to be stored.
It's the default implementation of boost::function that takes the functors by value instead of by reference.
See point 4 of boost::function FAQ
http://www.boost.org/doc/libs/1_35_0/doc/html/function/faq.html
If I have understood correctly you would like that wrapping in a boost::ref would be done internally in MSF, something like
template<typename F> void set_all(F const& f) { F& stored_object_copy = store_a_copy_somewhere(f);
do_set_all(boost::ref( stored_object_copy )); }
Is it correct?
Yes, these are the semantics i want, but I wouldn't implement it this way. boost::function just get in the way and needlessly complicates the implementation.
And in this case what is your suggestion for an API to fallback on the (currently default and more natural at least by an implementation point of view) N copy behaviour?
I think that this is such a rarely needed behaviour that MSF shouldn't provide an explicit API for it. Do you have a real use case for storing a per signature copy of each function object? The user can implement it if he wants: // warning: untested code :) namespace bf = boost::fusion; typedef mpl::vector<int(int), double(double), std::string(std::string)> sig_vector // assume we have stateful functor types IntToInt, DoubleToDouble, StringToString // which we want to store in a MSF typedef mpl::vector<IntToInt, DoubleToDouble, StringToString> f_vector; // a sequence of all typedef mpl::zip_view< mpl::vector<sig_vector, f_vector> > zipped; // Compute the fusion map from signatures to functors. We do it programmatically, but for simple // problems it might not worth it and just be defined statically. // (might have gotten the order wrong) typedef mpl::fold<zipped, bf::map<>, mpl::unpack_args< mpl::push_back<bf::pair<_1, _2 > > > function_map; struct forward { function_map map; template<typename T> T operator()(T x) { return fl::at_key<T(T)>(map)(x); } }; typedef msf<sig_vector> my_msf; my_msf f = forward(); // magic! This is actually more efficient of using multiple boost::functions, because you will have at most one dynamic memory allocation. Complex? Maybe, but I think that this is so rarely needed that it is not worth supporting out of the box (it might make a nice example though) Note that with my preferred inteface, adding a whole overload set of functions is trivially easy: void foo(int); void foo(double); // test ADL namespace my_namespace { class my_class; void foo(my_class) }; // ADL, again class my_other_class { friend void foo(my_other_class) {...} }; typedef mpl::vector<void(int), void(double), void(my_namespace::my_class), void(my_other_class)> sig_vectorl typedef msf<sig_vector> my_msf; // Adding all foo overloads for sig_vector, using current MSF behaviour struct add_sig { template<class SigP> void operator()(SigP, my_msf& f) { // assuming the static is not anbiguous f = static_cast<SigP>(&foo); } }; my_msf f; // sizeof(f) == O( sizeof(boost::function) * N ) mpl::for_each<sig_vector, boost::add_pointer>(boost::bind(add_sig(), _1, boost::ref(f))); // adding all overloads, If I can get it my way :) struct foo_fwd { template<typename T> void operator()(T x) { foo(x); } }; my_msf f = foo_fwd(); //sizeof(f) = O( sizeof(boost::function) ) Comments? Did I convince you? ;) -- gpd