Search code examples
c++openglfreeglut

can I pass information to function-pointer callbacks without using globals?


I am going to use an example with which I am having trouble with, but I will try to keep the topic as generic as possible. I have a function which takes a parameter of type void (*)(), which I want to bind from another function taking input parameters using std::bind. Can this be done? Below is my example from freeglut:

(I had read this: convert std::bind to function pointer, which confirms that the following doesn't work - workarounds???)

void callback(Obj obj1, Obj obj2, Obj obj3)
{
  // do something with the object and display data
}

// meanwhile, in main()
std::function<void ()> callbackFunctor = &std::bind(callback, obj1, obj2, obj3);

// the following doesn't work because glutIdleFunc expects a type void (*)()
glutIdleFunc(callbackFunctor );

Aside from making obj1, obj2 and obj3 globals, how does one normally pass information onto the idle callback in GLUT?


Solution

  • You can't convert a std::function to a function pointer (you can do the opposite).

    I was looking around a bit and I found this post on how to store variadic packs.

    If you modify that code a bit, making the members static, it might be of use to you:

    namespace helper {
        template <std::size_t... Ts>
        struct index {};
    
        template <std::size_t N, std::size_t... Ts>
        struct gen_seq : gen_seq<N - 1, N - 1, Ts...> {};
    
        template <std::size_t... Ts>
        struct gen_seq<0, Ts...> : index<Ts...> {};
    }
    
    template <typename... Ts>
    class Action {
    private:
        static std::function<void (Ts...)> f_;
        static std::tuple<Ts...> args_;
    public:
        template <typename F, typename... Args>
        static void setAction(F&& func, Args&&... args) {
            f_ = std::forward<F>(func);
            args_ = std::make_tuple(std::forward<Args>(args)...);
        }
    
        template <typename... Args, std::size_t... Is>
        static void func(std::tuple<Args...>& tup, helper::index<Is...>) {
            f_(std::get<Is>(tup)...);
        }
    
        template <typename... Args>
        static void func(std::tuple<Args...>& tup) {
            func(tup, helper::gen_seq<sizeof...(Args)>{});
        }
    
        static void act() {
            func(args_);
        }
    };
    
    // Init static members.
    template <typename... Ts> std::function<void (Ts...)> Action<Ts...>::f_;
    template <typename... Ts> std::tuple<Ts...> Action<Ts...>::args_;
    

    Using the code above you can store any function or function object with arbitrary parameters specified at runtime as an Action and use its static member function act(), which takes no parameters, to call the stored function with the previously specified parameters.

    As act() is convertible to void (*)() it can be passed to you callback function.

    The following should work:

    auto someCallable = [] (int x) { std::cout << "Number " << x << std::endl };
    Action<int>::setAction(someCallable, 1); // Pass func and parameters.
    glutIdleFunc(Action<int>::act);