OK, I have an idea of what I might do:
I'm planning on making a Event handling system, and this is how I will connect events to functions and be able to send any type of data. This is the basic idea:

#include <iostream>

template <class T> T *from(void *data)
{
    return (T *)data;
}

template <class T> void *to(T data)
{
    return (void *)data;
}

void foo(void *data)
{
    const char *test = from <const char> (data);
    std::cout << test << std::endl;
}

void call_function(void *data, void(*function)(void*))
{
    function(data);
}

int main()
{
    const char *test = "Hello";
    call_function(to <const char *> (test), foo);

    std::cin.get();
    return 0;
}

This works just fine, but is it really a viable option in practise?

Recommended Answers

All 6 Replies

It seems a bit too convoluted. There is a standard (now) method for doing this. See this example using Boost.Bind and Boost.Function. Both of these libraries are now part of the standard (C++11) in the <functional> header.

You can basically implement your example like this:

#include <iostream>
#include <functional>
#include <string>

void foo(const std::string& data)
{
    std::cout << data << std::endl;
}

void call_function(std::function< void() > f)
{
    f();
}

int main()
{
    std::string test = "Hello";
    call_function( std::bind(foo, std::cref(test)) );

    std::cin.get();
    return 0;
}

Simple, hey?

OK, I did not know about that thanks. However, I would like to keep it backwards compatible for now. And I'd rather not use Boost. I know I'm probably reinventing the wheel here, but I sometimes like (just for the challenge/experience really) to create my own methods/functions as much as I can.

So, with that in consideration, do you think there is a better method that is compatible with C++11 and previous versions?

I haven't been programming C++ long, so I'm trying to get my experience level up, and I won't do that by using premade tools that do the job for me. I've just finished school now, so I want to get going with some of the more complex sides of C++ before I begin college.

Thanks for the comments/suggestions, I will keep this in mind for future reference.

Caelan.

Alright, for the sake of do-it-yourself experience, we can discuss how you could do it.

The central issue here is something called "type erasure". This is something you encounter for time to time in C++. It's not always possible but often useful. The Boost.Function library (and the standard std::function template) do type erasure on function objects (or function pointers).

BTW, your solution, using the void*, is what people would call a "poor man's type erasure technique". It is very often used in C (or C-style C++), because that's pretty much the only technique available there. The fluff you have with the to and from functions is just eye-candy.

In your case, what you want is for the caller of the callback function not to have to care about the type of the function it is calling, nor about the type of the parameters it is giving it, if any. So, these are the types you want to "erase".

In your problem, the caller doesn't know anything except that it must call the callback function. Even though, in your code, you provide a void* to the function call, in reality, the caller contributes nothing of its own to the function and does not expect anything back either. So, for the sake of not complicating things more than they need to be, your caller should look something like this:

void call_function(some_callable_type f)
{
    f();  // provides no parameter, recieves nothing in return.
}

The some_callable_type should not be a template parameter of call_function, it should be some sort of place-holder class that hides away the actual type and parameters of the underlying function that is called when f is called. In other words, the some_callable_type class erases the type of the callback function. So, the question is, how to implement some_callable_type? Well, first of all, it needs to be callable (have a call operator that takes no argument), and it will need to "dispatch" that call at run-time (because it cannot be done at compile-time since the type is erased). In other words, you might simply use a virtual call operator:

class some_callable_type {
  public:
    // always need a virtual destructor:
    virtual ~some_callable_type() { };

    virtual void operator()() const = 0;
};

Now, all you would need to do is create some useful derived classes, like this:

template <typename T>
class funcptr_one_param : public some_callable_type {
  public:
    typedef void (*func_ptr_type)(const T&);

    funcptr_one_param(func_ptr_type aFunc, const T& aParam) : func(aFunc), param(aParam) { };

    virtual void operator()() const {
      func(param);
    };

  private:
    func_ptr_type func;
    T param;
};

And so on so forth for each type of call-back you want to support. And then, you would just add a bit of sugar frosting on top of that:

template <typename T>
funcptr_one_param< T > make_callback(void (*func)(const T&), const T& param) {
  return funcptr_one_param< T >(func, param);
};

Then, your caller function would look like this:

void call_function(const some_callable_type& f)
{
    f();
}

And your main, like this:

int main()
{
    std::string test = "Hello";
    call_function( make_callback(foo, test) );

    std::cin.get();
    return 0;
}

However, this isn't very flexible. For one, you can't copy the callback function object (or pointer). This would require some additional wrapper (similar to reference_wrapper in the latest standard). Roughly speaking, it would be something like this:

class some_callable_type {
  public:
    // always need a virtual destructor:
    virtual ~some_callable_type() { };

    virtual void operator()() const = 0;
    virtual some_callable_type* clone() = 0;
};

class wrapped_callable_obj {
  private:
    some_callable_type* p_obj;
  public:
    template <typename FunctionType>
    wrapped_callable_obj(FunctionType f) : p_obj(new FunctionType(f)) { };

    wrapped_callable_obj(const wrapped_callable_obj& rhs) : p_obj(rhs.p_obj->clone()) { };

    friend 
    void swap(wrapped_callable_obj& lhs, wrapped_callable_obj& rhs) {
      std::swap(lhs.p_obj, rhs.p_obj);
    };

    wrapped_callable_obj& operator=(wrapped_callable_obj rhs) {
      swap(*this, rhs);
    };

    ~wrapped_callable_obj() { delete p_obj; };

    void operator()() const { (*p_obj)(); };

};

template <typename T>
class funcptr_one_param : public some_callable_type {
  public:
    typedef void (*func_ptr_type)(const T&);

    funcptr_one_param(func_ptr_type aFunc, const T& aParam) : func(aFunc), param(aParam) { };

    virtual void operator()() const {
      func(param);
    };

    virtual some_callable_type* clone() {
      return new funcptr_one_param(*this);
    };

  private:
    func_ptr_type func;
    T param;
};

template <typename T>
wrapped_callable_obj make_callback(void (*func)(const T&), const T& param) {
  return wrapped_callable_obj(funcptr_one_param< T >(func, param));
};

void call_function(wrapped_callable_obj f)
{
    f();
}

int main()
{
    std::string test = "Hello";
    call_function( make_callback(foo, test) );

    std::cin.get();
    return 0;
}

The other issue is the fact that you are going to need to make one derived class for each type of callback you want to support (multiple parameters, with or without return type, etc..), including an overload of the eye-candy function make_callback for each too. And if you do all this correctly, you are going to end up with exactly the implementation used by the Boost.Bind / Boost.Function duo of libraries (i.e., Boost.Bind provides the eye-candy analogous to make_callback, and Boost.Function provides the type-erased function objects analogous to wrapped_callable_obj).

The above demonstration has all the essential components of most type erasure techniques in C++. The important thing to understand here, in this "callback" scenario, is that one of the very desirable features is retaining type-safety on both sides of the type-erased interface. In other words, the callback function is implemented without any weird casts (i.e., the foo function recieves a string, not some void* that it needs to cast to something else), this makes the solution more flexible and safer (there aren't any casts at all in this). And from the caller's perspective, it recieves a callable object, period, no weird "unknown type" object or overly restrictive function pointers. This is why this is the preferred solution. In your solution, you have neither of these things, which leads to some awkward results.

Oh, and you might say: "my solution seems way simpler". Well, it is simpler to implement, that's for sure, but that is only because you leave a lot of the trouble to the caller (having to carry around the parameter and function pointer) and to the callback function definition (having to cast, or worse, the void* parameter it gets). It is worth the trouble to implement a "complicated" solution if you end up with a simpler, safer and more usable solution from a user's point of view (caller / callback).

commented: Nice explanation mike! Worth reading. +12

Thanks a lot for the explanation looks very detailed, I will read it all in the morning. Thanks again. :)

Oh just to let you know, your time has not been wasted, I have read it, but I just haven't had sufficient time to really take it in. But i'll give that a go now.

Thanks again.

OK, This is what I've come up with. Thanks a lot!

#include <iostream>
#include <string>

class CallbackWrapper
{
    public:
        virtual ~CallbackWrapper() { };
        virtual void Call() = 0;
};

template <typename MT> class CallbackFunction : public CallbackWrapper
{
    typedef void (*CALLBACK)(MT);
    CALLBACK func;
    MT data;
    public:
        CallbackFunction() { };
        void SetData(void (*f)(MT), MT d)
        {
            func = f;
            data = d;
        }
        void Call()
        {
            func(data);
        }
};

class Callback
{
    CallbackWrapper *CallbackClass;
    public:
        Callback() { };
        ~Callback()
        {
            delete CallbackClass;
        }
        template <typename T> void SetCallback(CallbackFunction <T> *func)
        {
            CallbackClass = func;
        }
        void Call()
        {
            CallbackClass->Call();
        }
};

template <typename CT> Callback NewCallback(void (*func)(CT), CT data)
{
    CallbackFunction <CT> *cf = new CallbackFunction <CT>;
    cf->SetData(func, data);
    Callback cb;
    cb.SetCallback(cf);
    return cb;
};

void Call(Callback CallbackFunc)
{
    CallbackFunc.Call();
}

void foo(std::string str)
{
    std::cout << str << "\n";
}

int main()
{
    std::string str;
    str.append("Hello, World!");
    Call( NewCallback(&foo, str) );
    return 0;
}
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.