4

Suppose I have a function which takes a nullary functor as an argument:

void enqueue( boost::function<void()> & functor );

I have another function which takes an int and does something internally:

void foo( int a);

I would like to nest, but not compose, these together so that I get a functor with the signature:

boost::function<void(int)> functor

Which when called with a value - say 4 - performs the following:

enqueue( boost::bind(&foo, 4) )

My first attempt was the following:

boost::function<void(int)> functor = boost::bind(&enqueue, boost::bind(&foo,_1))

This fails because bind performs composition when given a nested bind. foo was first called, then the value void was "returned" to enqueue, which fails.

My second attempt was the following:

boost::function<void(int)> functor = boost::bind(&enqueue, boost::protect( boost::bind(&foo, _1) ) )

This failed because enqueue accepts a nullary, not unary functor.

Can what I'm seeking be done?

Other information:

  • This is basically identical to the unanswered boost forum question from 6 years ago: http://lists.boost.org/boost-users/2004/07/7125.php
  • Some reading suggests that using boost::lambda::bind with boost::lambda::unlambda and boost::lambda::protect may do what I'm seeking. Unfortunately boost::lambda has an unacceptably low number of allowed placeholders (3), and high compile-time overhead.
tgoodhart
  • 3,111
  • 26
  • 37

2 Answers2

4

Interesting question...

What you basically want is a "bound call to bind". In the same manner than binding a call to foo(x, y) is written bind(&foo, x, y), binding a call to bind(&foo, x) should be like bind(&bind, &foo, x). However, taking the address of an overloaded function quickly gets ugly and, as boost::bind has more overloads than I could count, it gets pretty ugly:

// One single line, broken for "readability"
boost::function<void(int)> f = boost::bind(
  &enqueue, 
  boost::bind(
    static_cast<
      boost::_bi::bind_t<
        void, void(*)(int), boost::_bi::list_av_1<int>::type
      >
      (*)(void(*)(int), int)
    >(&boost::bind), 
    &foo, 
    _1
  )
);

You'll probably agree that, while "interesting", the above won't win readability contests. Separating the obtention of the proper bind overload from the rest makes things a bit more manageable:

boost::_bi::bind_t<void, void(*)(int), boost::_bi::list_av_1<int>::type>
  (*bind_foo)(void(*)(int), int) = &boost::bind;

boost::function<void(int)> q = boost::bind(&enqueue, boost::bind(bind_foo, &foo, _1));

but I still hesitate to recommend it ;)

Edit:

Answering the OP's comment about how/if C++0x would help to clean the syntax: It does:

auto f = [](int i){enqueue([=](){foo(i);});};
Éric Malenfant
  • 13,938
  • 1
  • 40
  • 42
  • Amazing. I think that while boost is very convenient, it has not been designed for being used at this "meta" level. For example, I tried to get the type of each element of a tuple (if I remember well, the type returned by a get<0>(), for example) and I couldn't. I realized it used some internal `detail::` type. To offer a true meta-programming level, those types should have been made accessible (same as `value_type` in maps, say). – Diego Sevilla Nov 04 '10 at 20:52
  • @Diego Sevilla: I only partially agree. For your tuple example, metafunctions are provided. See `element::type` in http://www.boost.org/doc/libs/1_44_0/libs/tuple/doc/tuple_advanced_interface.html – Éric Malenfant Nov 04 '10 at 20:59
  • Ahh, live to learn. Damn! I missed that part. Really thanks for the pointer! – Diego Sevilla Nov 04 '10 at 22:08
  • StackOverflow comes through again. While ugly, the latter form is still much more clear (in my book) than creating a separate "binder" function like been doing. I wonder if/how using std::bind and c++0x lambdas could clean it up. – tgoodhart Nov 04 '10 at 22:32
  • @ÉricMalenfant: It´s been a while since you answered this questions and it just helped me when facing a similar problem. EDIT: I will start a new question and link it here. Probably gets unreadable inside the comment. – Chris Jan 08 '16 at 08:45
0

'Nest' manually:

class Enqueuer {
 std::function<void (int)> mFunc;

public:
 void operator()(int pVal) {
  enqueue(std::bind(mFunc, pVal));
 }

 Enqueuer(std::function<void (int)> pFunc)
  : mFunc(pFunc) {}
};

// usage:
Enqueuer e(foo);
e(1);
e(2);
e(3);
Paul Michalik
  • 4,331
  • 16
  • 18