Topic: Forwarding Problem: a "cheating" solution


Author: "Cosmos" <cosmos.cplusplus@gmail.com>
Date: Fri, 15 Dec 2006 10:35:45 CST
Raw View
I am currently writing a C++ library and at a certain point I realized
the need to implement functor objects that could do perfect forwarding.

Knowing that this is the famous C++ Forwarding Problem
(http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2002/n1385.htm)
initially I accepted that it was not possible.

By studying Boost.Bind library and experimenting, I found the following
idea... which most people would probably consider "cheating":

in order to create a functor object B that will accept exactly the same
arguments as a function F and forward them to F, the type signature of
B should be identical to the type signature of F.

A VERY brief implementation of the idea can be expressed in C++ as
follows (the example shows a functor object forwarding calls to a
function accepting two parameters, different number of parameters can
be added in linear time)

template<class RT, class T1, class T2>
    class Bind
{
  private:
    RT (* func)(T1, T2);

  public:
    Bind(RT (* function)(T1, T2)) : func(function) { }

    RT operator()(T1 arg1, T2 arg2) const {
        return func(arg1, arg2);
    }
};

template<class RT, class T1, class T2>
    Bind<RT, T1, T2> bind(RT (* function)(T1, T2))
{
    return Bind<RT, T1, T2>(function);
}


that can be used as in the following example:

int F(int & arg1, const int & arg2)
{
    return arg1 += arg2;
}

Bind<int,int &,const int &> B(F);

int x = 0;
F(x, 5);
bind(F) (x, 7);
B(x, -3);

the perfect-forwading approach exposed above can be generalized to
manage:
* functions accepting any number of parameters (in linear
  amount of code)
* functions returning void (need to write a partial specialization
  template<class T1, class T2> Bind<void, T1, T2>)
* partial binding and Boost.Bind _1 ... _9 plaheholders, as in
  bind(F, _1, <argument2>) and bind(F, _2, _1)
  This is non-trivial because the number and type of template
  arguments of Bind<...> should be deduced by the number and
  types of the arguments passed to bind()
* partial binding with literal constant values, using an ugly but
  effective syntax: bind(F, _1, make_ref(5)) (see note)
* forwarding to member functions (RT (T1::* method)(T2)) and
  const-qualified member functions (RT (T1::* method)(T2) const)
* currying

I have implemented all these generalizations in a C++ library "liblsd"
for functions and member functions up to 9 parameters. The resulting
implementation of bind() is for most aspects a superset of Boost.Bind.

note: make_ref() is used to overcome the known C++ limitation that
"template<class T> T func(T & arg)"
will NOT match a literal constant argument, as in func(5). For this
reason, make_ref() is defined as follows:

template<class T> T & make_ref(T & arg) { return arg; }
template<class T> const T & make_ref(const T & arg) { return arg; }

since make_ref() only depends on ONE template argument, it circumvents
the problem - well known from the Forwarding Problem - that a template
function of N template parameters needs 2**N overloads to allow for
each argument both (T &) and (const T &) types

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: David Abrahams <dave@boost-consulting.com>
Date: Sun, 17 Dec 2006 23:47:00 CST
Raw View
"Cosmos" <cosmos.cplusplus@gmail.com> writes:

> I am currently writing a C++ library and at a certain point I realized
> the need to implement functor objects that could do perfect forwarding.
>
> Knowing that this is the famous C++ Forwarding Problem
> (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2002/n1385.htm)
> initially I accepted that it was not possible.
>
> By studying Boost.Bind library and experimenting, I found the following
> idea... which most people would probably consider "cheating":
>
> in order to create a functor object B that will accept exactly the same
> arguments as a function F and forward them to F, the type signature of
> B should be identical to the type signature of F.
>
> A VERY brief implementation of the idea can be expressed in C++ as
> follows (the example shows a functor object forwarding calls to a
> function accepting two parameters, different number of parameters can
> be added in linear time)
>

<snip>

> template<class RT, class T1, class T2>
>     Bind<RT, T1, T2> bind(RT (* function)(T1, T2))
> {
>     return Bind<RT, T1, T2>(function);
> }
>

<snip>

Yes, this approach requires the ability to deduce a unique set of
parameter types for the thing being bound.  Sometimes that isn't
possible because the thing doesn't tell you what the parameter types
are.  Sometimes it isn't possible because the thing has multiple (or
an infinite number of) parameter sets, as in a function object with an
overloaded or templated function call operator.  Those cases are the
ones that motivate a solution to "the forwarding problem."


--
Dave Abrahams
Boost Consulting
www.boost-consulting.com

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]