22

I'm trying to use std::function in conjunction with std::bind, but I'm having some problems.

This works:

#include <functional>
#include <iostream>

void print() {
    std::cout << 2;
}

int main() {
    std::function<void ()> foo = print;
    (*foo.target<void (*)()>())(); //prints 3
}

This crashes at the second line of main:

#include <functional>
#include <iostream>

void print (int i) {
    std::cout << i;
}

int main() {
    std::function<void ()> foo = std::bind (print, 2);
    (*foo.target<void (*)()>())();
}

I'm really holding the std::function<void ()> and need to be able to return the function; not just call it. I expect the usage would be something like this:

#include <functional>
#include <iostream>

void print (int i) {
    std::cout << i;
}

int main() {
    Container c (std::bind (print, 2));

    //I would expect the original
    c.func() (3); //prints 3

    if (c.func() == print) /* this is what I'm mostly getting at */
}

Is there any way to get the original function to return it, or an alternative? It does kind of conflict with the return type as well, as void (*)() matches the bound signature quite nicely.

ildjarn
  • 62,044
  • 9
  • 127
  • 211
chris
  • 60,560
  • 13
  • 143
  • 205
  • This is just not possible: there is no function with a `void()` signature in your code. Had it been possible we wouldn't need `std::function`. – R. Martinho Fernandes Jun 07 '12 at 19:46
  • @R.MartinhoFernandes, it was a nice trick to store a generic function, but it seems only calling it works out. – chris Jun 07 '12 at 19:48
  • @chris: Is the whole point of storing a generic function. Comparing generic functions becomes *very* tricky, and this is just the tip of the iceberg. – Puppy Jun 07 '12 at 19:59
  • @DeadMG, Well, I'd best not get into it. It's not an overly important feature of my class, but it's nice to have. I can always keep a separate variable if I need to. – chris Jun 07 '12 at 20:00

4 Answers4

35

This is quite impossible. The whole reason that std::function exists is that function pointers suck horrifically and should never, ever, be used by anyone, ever again, except for the doomed souls bearing the Burning Standards of Hell C interoperation, because they cannot handle functions with state.

A std::function<void()> cannot, in the general case, be converted to a void(*)(). The only reason this works in the first example is because it happens to be a void(*)() originally.

Puppy
  • 144,682
  • 38
  • 256
  • 465
  • 1
    I guess I'll just have to forget about comparing it then, thank you. – chris Jun 07 '12 at 19:52
  • 46
    *function pointers suck*... only in the same way that `int` sucks, or raw pointers... they are the low level blocks on which higher level constructs are built. Without raw pointers and int you would not have `std::string`, without function pointers you would not have `std::function`... – David Rodríguez - dribeas Jun 07 '12 at 20:12
  • 3
    @David: Not really. You can implement `std::function` without any knowledge of function pointers. – Puppy Jun 08 '12 at 01:21
  • 1
    @DeadMG: Do you care to illustrate us? Oh, you mean with lambdas? If so, consider `boost::bind` and `boost::function` in a C++03 compiler, or any signals library (sigc++, boost::signals, Qt signals), or any user of Sun or HP compilers that don't have lambda support yet... – David Rodríguez - dribeas Jun 08 '12 at 01:29
  • 1
    @DavidRodriguez: Uh, why not just ... use a template like a sane person? – Puppy Jun 08 '12 at 01:40
  • @DeadMG: Again, how do you write a generic template for this? Or do you expect users to write a functor that encapsulates the function call and derives from the private type used for the type-erasure in `std::function`, pass the object to the `std::function` constructor/assignment? Maybe ignore the private type and just create a plain functor that gets wrapped, that can be done, but you end up with a dynamic dispatch and two forwarding calls between `operator()` in `std::function` and the actual function... the advantage: no need for bind, that is now the responsibility of the user... – David Rodríguez - dribeas Jun 08 '12 at 03:34
  • This is quite possible (see my answer below). – fredbaba Aug 24 '13 at 20:36
  • 1
    std::function is quite damn slow compared to a raw function pointer, so yeah, it makes sense to use them in some cases. – Jean-Bernard Jansen Apr 07 '16 at 17:18
  • 2
    Nothing in C or C++ "sucks". It made perfectly sense at the time it was invented to solve a practical problem still relevant in the present. Like everything we created. – Andreas Spindler Oct 03 '21 at 11:30
19

This can be achieved using a little template meta-programming. I recently had use for this while writing a generic C++ wrapper around OpenGL GLUT (which depends on callback function pointers). The approach:

  1. Instantiate an instance of a singleton template type.
  2. Store your std::function as a member of to the singleton instance
  3. Invoke your std::function through a static member function (static member functions and free functions have the same type, so the "invoke" function can be used as a free function pointer)

Tested under C++11 on GCC 4.8.

#include <unistd.h>
#include <thread>
#include <chrono>
#include <mutex>
#include <functional>
#include <iostream>
#include <cmath>

template <const size_t _UniqueId, typename _Res, typename... _ArgTypes>
struct fun_ptr_helper
{
public:
    typedef std::function<_Res(_ArgTypes...)> function_type;

    static void bind(function_type&& f)
    { instance().fn_.swap(f); }

    static void bind(const function_type& f)
    { instance().fn_=f; }

    static _Res invoke(_ArgTypes... args)
    { return instance().fn_(args...); }

    typedef decltype(&fun_ptr_helper::invoke) pointer_type;
    static pointer_type ptr()
    { return &invoke; }

private:
    static fun_ptr_helper& instance()
    {
        static fun_ptr_helper inst_;
        return inst_;
    }

    fun_ptr_helper() {}

    function_type fn_;
};

template <const size_t _UniqueId, typename _Res, typename... _ArgTypes>
typename fun_ptr_helper<_UniqueId, _Res, _ArgTypes...>::pointer_type
get_fn_ptr(const std::function<_Res(_ArgTypes...)>& f)
{
    fun_ptr_helper<_UniqueId, _Res, _ArgTypes...>::bind(f);
    return fun_ptr_helper<_UniqueId, _Res, _ArgTypes...>::ptr();
}

template<typename T>
std::function<typename std::enable_if<std::is_function<T>::value, T>::type>
make_function(T *t)
{
    return {t};
}

int main()
{
    std::cout << (void*)get_fn_ptr<0>(make_function(::sin))<<std::endl;
    return 0;
}
fredbaba
  • 1,466
  • 1
  • 15
  • 26
  • 1
    I've made one minor modification to this solution, which is to add a typename for the unique ID. Paired with `std::enable_if` and `std::is_literal` this lets me use enums or other literal-type identifiers as the unique ID, further scoping the uniqueness and preventing collisions (at the cost of making this helper code a bit more verbose). – monkey0506 Oct 24 '16 at 14:10
  • 2
    This solution doesn't work with multithreading when multiple threads set the `std::function` of the singleton class. – Fred Schoen Feb 13 '18 at 13:16
  • 2
    It also doesn't work re-entrantly, or indeed, in any remotely complex scenario. It's undependable. – Puppy Aug 26 '20 at 18:06
7

You can't get a function pointer out of an std::function, as there may not even be one. It could be a member function pointer instead, or an object that implements operator().

K-ballo
  • 80,396
  • 20
  • 159
  • 169
1

This isn't possible in general but most C APIs have a 'context' pointer you can pass alongside the C function pointer. So for interop, you can wrap it with something like this (which assumes the API passes this 'context' parameter in first):

template <class R, class... Args>
struct CFunctionPointer
{
    std::function<R(Args...)> func;

    static R callback(void* context, Args... args)
    {
        const CFunctionPointer* unpackedThis = reinterpret_cast<const CFunctionPointer*>(context);
        return unpackedThis->func(std::forward<Args>(args)...);
    }
};

Which would then be called like this:

auto callable = CFunctionPointer<void, const uint8_t*, const uint8_t*>{ [](const uint8_t* begin, const uint8_t* end) { std::cout << "Hello world\n"; } };
cfunc(..., &callable, &callable.callback);

sadly duplicating the argument types twice.

Tom Whittock
  • 4,081
  • 19
  • 24