Boost logo

Boost :

From: Giovanni Piero Deretta (gpderetta_at_[hidden])
Date: 2008-05-13 16:32:00


On Tue, May 13, 2008 at 7:59 PM, Marco Costalba <mcostalba_at_[hidden]> wrote:
> On Tue, May 13, 2008 at 3:01 PM, Giovanni Piero Deretta
> <gpderetta_at_[hidden]> 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

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk