How to transform a nested C++11 bind expression

462 Views Asked by At

Is it possible to transform a nested C++11 bind expression? For example, in the code below, the bind expression associated with f will first multiply its argument by two, before adding one to the result:

#include <iostream>
#include <functional>

using namespace std::placeholders;

int main(int argc, char *argv[])
{                                                                               
  auto add1 = [](int x) { return x+1; };
  auto mul2 = [](int x) { return x*2; };

  auto f = std::bind(add1, std::bind(mul2, _1));
  std::cout << f(0) << '\n';
  return 0;
}

Could we create a transformed version of f which instead first adds one, then multiplies the result by two; the result would behave as if defined as:

auto f2 = std::bind(mul2, std::bind(add1, _1));

This example is simplified by the fact that it's structure is analogous to a list; whereas a bind expression is more generally a tree.

2

There are 2 best solutions below

2
On

This is a brutal hack on VS2013, and is totally non-portable, but I was interested to see if I could make something work. It probably doesn't solve your problem, but I thought it was worth sharing. Because the return value of std::bind is implementation defined, portability is going to be a big roadblock on this problem. There's also lots handwaving on most of the template deduction.

template<class A, class B, class C>
std::_Bind<false, void, B&, std::_Bind<false, void, A&, C>> 
inverse(std::_Bind<false, void, A&, std::_Bind<false, void, B&, C>> f) {
    A func_a;
    B func_b;
    return std::bind(func_b, std::bind(func_a, _1));
}

int main() {
    auto add1 = [](int x) { return x+1; };
    auto mul2 = [](int x) { return x*2; };

    auto f = std::bind(add1, std::bind(mul2, _1));
    std::cout << f(0) << '\n';

    auto g = inverse(f);
    std::cout << g(0) << '\n';

    return 0;
}
0
On

std::bind is a black box. It does not support introspection (aside from std::result_of and std::is_bind_expression) or polymorphism (aside from std::placeholders). When you call f(0), it compiles to native code for 0*2+1.

Inside that black box is something like an expression template, referencing the composed functor types and providing storage for any bound arguments. Such implementation is tied to the compiler, and platform details will in fact differ greatly.

If you want to portably manipulate expressions, check out the Boost.Proto generalized expression template library. It's heavy stuff, though.