0

I have a class called Renderer that holds two callback objects;

void (*drawCall)(const sf::Drawable& drawable, const sf::RenderStates& states);
void (*drawPrimCall)(const sf::Vertex* vertices, unsigned int vertexCount,
    sf::PrimitiveType type, const sf::RenderStates& states);

I would like to have a public function that binds these callbacks, it's templated and needs a class that has two draw methods each with their valid parameters, something like this:

template<typename T>
inline void setRenderer(T* instance)
{
    using namespace std::placeholders;
    drawCall = std::bind(&T::draw, instance, _1, _2);
    drawPrimCall = std::bind(&T::draw, instance, _1, _2, _3, _4);
}

However, as you might imagine this does not work. I'm still learning about std::bind and I'm not sure about the use of placeholders, it's understood what I'm trying to do here.

I've tried looking for answers in Google but it has been a bit daunting, thanks for any help!

UPDATE:

Ok, so the answer given by @Holt works, after changing to std::function my callbacks look like this:

std::function<void(const sf::Drawable& drawable, const sf::RenderStates& states)> drawCall_fn;
std::function<void(const sf::Vertex* vertices, unsigned int vertexCount,
                   sf::PrimitiveType type, const sf::RenderStates& states)> drawPrimCall_fn;

Now for the setRenderer method due to constraints on external libraries I also need to pass a function reference, like so;

template<typename T>
inline void setRenderer(T* instance, void(T::*drawCall)(const sf::Drawable&, const sf::RenderStates&))
{
    using namespace std::placeholders;
    drawCall_fn = std::bind(drawCall, instance, _1, _2);
}

Such that when called I can do it like this:

renderer.setRenderer<sf::RenderTarget>(&window, &sf::RenderTarget::draw);

**Note: I'm only dealing with drawCall_fn for the moment as I can easily expand after.

UPDATE2: Added missing parameters for setRenderer drawCall.

Belfer4
  • 351
  • 3
  • 11
  • 1
    What are the types of `drawCall` and `drawPrimCall`? `std::function`? – Holt Jan 06 '17 at 13:17
  • Looks to me like you are trying to convert a member function pointer to a function pointer which is impossible. See http://stackoverflow.com/questions/4296281/how-can-i-pass-a-member-function-to-a-function-pointer for example. – Chris Drew Jan 06 '17 at 13:31
  • @Holt They are function pointers but I was looking into making them std::function s. I'm trying out your answer atm. – Belfer4 Jan 06 '17 at 14:20
  • @Holt Thanks for your answer so far it has helped me, but please have a look at the updated question. Thanks again! – Belfer4 Jan 06 '17 at 14:42
  • 1
    @Belfer4 Are you sure you have `drawCall = std::bind(drawCall, ...);`? Shouldn't one of the `drawCall` be named differently? I don't really understand what you want to do here... – Holt Jan 06 '17 at 14:44
  • @Holt Sorry yes it now should be drawCall_fn, I've updated it now. – Belfer4 Jan 06 '17 at 14:46

2 Answers2

2

You cannot do what you want using function pointers because you need to "capture" (either with a capturing lambda or with a std::bind) the instance. You should use std::function:

std::function<void(const sf::Drawable&, const sf::RenderStates&)> drawCall;
std::function<void(const sf::Vertex*, unsigned int,
    sf::PrimitiveType, const sf::RenderStates&)> drawPrimCall;

Then, you can either use lambda or force the overload resolution of &T::draw to assign them:

drawCall = [instance](const sf::Drawable& drawable, const sf::RenderStates& states) {
    instance->draw(drawable, states);
});

// Or:
drawCall = std::bind((void (T::*)(const sf::Drawable&, const sf::RenderStates&))&T::draw, 
                     instance, _1, _2);

In both cases, you have to re-specify the list of arguments, but you can use a small helper function to avoid this:

template <typename T, typename R, typename... Args, typename... BArgs>
void gbind_mem(std::function<R(Args...)> &fn, R(T::*mfn)(Args...), 
               T *instance, BArgs&&... args) {
    fn = std::bind(mfn, instance, std::forward<BArgs>(args)...);
}

Then you can easily bind using:

gbind_mem(drawCall, &T::draw, instance, _1, _2);

You let the compiler do the hard job of deducing the parameters from drawCall.

Alexis Wilke
  • 19,179
  • 10
  • 84
  • 156
Holt
  • 36,600
  • 7
  • 92
  • 139
0

...that holds two callback objects

There is no such thing as a "callback object" in C++. What you're probably looking for are function pointers or std::function. In the case of function pointers, your syntax is incorrect - you need to add a *:

void (*drawCall)(const sf::Drawable& drawable, const sf::RenderStates& states = sf::RenderStates::Default);
void (*drawPrimCall)(const sf::Vertex* vertices, unsigned int vertexCount, sf::PrimitiveType type,
                    const sf::RenderStates& states = sf::RenderStates::Default);

You will then be able to set them. Example:

void (*callback)(int); 
void real_function(int) { }

int main()
{
    callback = &real_function;
}

If your "real function" signature does not match the callback's, you should use captureless lambda expressions, which are implicitly convertible to function pointers, to adapt it:

void (*callback)(int); 
void real_function(int, float) { }

int main()
{
    callback = [](int x){ return real_function(x, 5.f); };
}

If you provide a minimal example on wandbox I will be able to give you a more specific answer.

Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416