0

I have a class foo with a method bar which takes something callable (function-pointer/ functor). this callable something should be passed to another method doit as an binded element with a third method bar_cb method.

#include <functional>
#include <iostream>

class foo {
public:
    template<typename T>
    void bar(T&& t) {
        std::cout << "bar\n";
        doit(std::bind(&foo::template bar_cb<T>, this, std::forward<T>(t)));
    }

    template<typename T>
    void doit(T&& t) {
        std::cout << "doit\n";
        t();
    }

    template<typename T>
    void bar_cb(T&& t) {
        std::cout << "bar_cb\n";
        t();
    }
};


void lala() {
    std::cout << "lala\n";
}

class functor {
public:
    void operator()() {
        std::cout << "functor::operator()\n";
    }
};


int main() {
    foo f;
    functor fn;
    f.bar(fn);
    f.bar(std::bind(lala));  // error

    return 0;
}

This works fine for functors but not for binded functions as argument for foo::bar (lala in my example). Is it possible to pass an unknowable type to a method and bind it in this method as an argument to another (and if so how)?

I know I could wrap a functor (std::function for example) around the function but since I can call an unknowable type I think there is a way to also bind it (I think I'm just missing something simple).

Here a link to an example.

user1810087
  • 5,146
  • 1
  • 41
  • 76
  • What is the actual (higher-level) problem you're trying to solve? – John Zwinck Dec 30 '14 at 01:30
  • @JohnZwinck the problem is that i want to have a class which takes either a functor or a function and call other functors with this `callable` as parameter. the real code is an variadic template class with different functors as template parameters. however, i tried to summerize the problem into a simple non-template class to understand what is wrong. also i want to avoid the overhead of `std::function`. – user1810087 Dec 30 '14 at 01:48
  • You are running into `bind`'s special handling for bind expressions. http://stackoverflow.com/questions/10777421/stdbind-a-bound-function – T.C. Dec 30 '14 at 01:49
  • @T.C. yes i know :( i've read some stuff about this. and it is quit hard because i don't understand good enough how bind is working under the hood. – user1810087 Dec 30 '14 at 01:53
  • 1
    Use a lambda in place of `std::bind`, perhaps? [Live example](http://rextester.com/MVYS30054). Not sure about the implications on perfect forwarding, though. – Igor Tandetnik Dec 30 '14 at 02:04
  • 1
    Write an `unbind` that takes a bind expression and wraps it in a type that is not a bind expression? Yet otherwise behaves the same? – Yakk - Adam Nevraumont Dec 30 '14 at 02:23

1 Answers1

2

The primary problem is that your bar_cb(T&&) doesn't deduce the template argument because the template argument is actually specified when using &foo::template bar_cb<X> with some template argument X. The bind() expression will, however, copy the bound function, i.e., it may or may not have the type which would be deduced. Also, std::bind() will not pass bind()-expression through but will rather call them!

The easiest work around is to not use std::bind() to bind the function but rather to use a lambda function:

template<typename T>
void bar(T&& t) {
    std::cout << "bar\n";
    doit([=](){ this->bar_cb(t); });
}

Doing so let's the compiler deduce the correction argument type for bar_cb() (with C++14 you may want to use the capture [this,t = std::forward<T>(t)] although your bar_cb() still won't see an rvalue).

To pass an already bind()-expression through another bind()-expression, without having bind() consider the inner bind()-expression a bind()-expression you need to make it look as if it is not a bind()-expression. You could do so with a thin function wrapper:

template <typename Fun>
class unbinder {
    Fun fun;
public:
    template <typename F>
    unbinder(F&& fun): fun(std::forward<F>(fun)) {}
    template <typename... Args>
    auto operator()(Args&&... args) const
        -> decltype(fun(std::forward<Args>(args)...)) {
        return fun(std::forward<Args>(args)...);
    }
};
template <typename Fun>
auto unbind(Fun&& fun)
    -> unbinder<Fun> {
    return unbinder<Fun>(std::forward<Fun>(fun));
}

Since the function stored in the bind() expression will be passed by lvalue, you'll need a different declaration for your bar_cb(), however:

template<typename T>
void bar_cb(T& t) {
    ...
}

With that, you can register the bind()-expression using

f.bar(unbind(std::bind(lala)));

If you want to use f.bar(std::bind(lala)) you'll need a conditional definition of bar(): if it receives a bind()-expression it needs to automatically hide the fact that it is a bind()-expression by applying unbind() or something similar:

template<typename T>
typename std::enable_if<!std::is_bind_expression<typename std::decay<T>::type>::value>::type
bar(T&& t) {
    std::cout << "bar (non-bind)\n";
    doit(std::bind(&foo::template bar_cb<T>, this, std::forward<T>(t)));
}
template<typename T>
typename std::enable_if<std::is_bind_expression<typename std::decay<T>::type>::value>::type
bar(T&& t) {
    std::cout << "bar (bind)\n";
    doit(std::bind(&foo::template bar_cb<unbinder<T>>, this, unbind(std::forward<T>(t))));
}
Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • thank you for this detailed answer. i will try both. btw, is there a specific reason why you pass by value in the lambda function. – user1810087 Dec 30 '14 at 11:28
  • Capturing the bound arguments by value resembles the original construction using `srd::bind()` more closely (if you wanted to capture the value by reference wirh `std::bind()` you'd have used `std::ref()`). Also, it seems to be the more failure-proof starting point as there are no life-time issues. Finally, capturing generic arguments by value gives the user of the function the choice to override the default, also using `std::ref()`. – Dietmar Kühl Dec 30 '14 at 12:02