0

I am trying to write a class that will be appended with some function pointers.

This pointers will be called from another method of this class. I will be storing the function pointers on a void* vector so anything can go on a single vector, instead of a different vector for each type.

I intend to declare a different AppendCallback methods for any different function I need to call from inside the class, for example:

void MyClass:AppendCallback(void (*Callback)())
{
    _CallbackVector.push_back((void*)Callback);
    _IdVector.push_back(VoidID);
}

void MyClass:AppendCallback(void (*Callback)(uint32_t));
void MyClass:AppendCallback(void (*MyOtherClass::Callback)());
void MyClass:AppendCallback(void (*MyOtherClass::Callback)(uint32_t));

There will be a second vector that only contains identifiers to know what the void* points to, this is going to be assigned also on the AppendCallback Methods.

How can I cast the void pointer again to the function pointers for calling those functions?

Maybe something like this?

void MyClass::Service(uint32_t x)
{
    for(uint i = 0; i < _CallbackVector.size(); i++)
    {
        switch(_IdVector[i])
        {
            case VoidID: void(*_CallbackVector[i]()); break;
            case Uint32ID: void(*_CallbackVector[i](x)); break;
        }
    }
}

Edit:

Is this a proper way of casting from a void* to a function pointer?

Jesper Juhl
  • 30,449
  • 3
  • 47
  • 70
Cheche Romo
  • 137
  • 6
  • Yes, you will have to store some sort of type information yourself. – arrowd Aug 09 '19 at 18:39
  • 1
    iirc, in C++ there is no provision for storing a function pointer into a `void*`, nor for casting a `void*` back to a function pointer. It may work for your platform, but I think it is UB. You may want to add `language-lawyer` to this question. – Eljay Aug 09 '19 at 18:44
  • 2
    This doesn’t address the question, but names that begin with an underscore followed by a capital letter (`_CallbackVector`, `_IdVector`) and names that contain two consecutive underscores are reserved for use by the implementation. Don’t use them in your code. – Pete Becker Aug 09 '19 at 18:47
  • May be a dup: https://stackoverflow.com/questions/36645660/why-cant-i-cast-a-function-pointer-to-void – Eljay Aug 09 '19 at 18:47
  • As a general rule of thumb; If you are using `void*` in C++ you are (most likely) *doing it wrong*. – Jesper Juhl Aug 09 '19 at 18:48
  • 3
    `void*` is not required to be large enough to hold a function pointer. Read about `std::function`. – Pete Becker Aug 09 '19 at 18:51
  • So the proper way should be with a Vector specialized on each different type? – Cheche Romo Aug 09 '19 at 18:52
  • See http://eel.is/c++draft/conv.mem#:conversion,pointer-to-member,void* (read *all* of it). – Jesper Juhl Aug 09 '19 at 18:53
  • Possible duplicate of [Function pointers casting in C++](https://stackoverflow.com/questions/1096341/function-pointers-casting-in-c) –  Aug 09 '19 at 18:56
  • Function pointers are different from normal pointers, but a pointer to a function pointer isn't. `void (*fptr)() = &func; void* ptr = &fptr;` – Weak to Enuma Elish Aug 09 '19 at 22:07

1 Answers1

3

That's not allowed in C++:

Converting a void* to a function pointer directly is not allowed (should not compile using any of the casts) in C++98/03. It is conditionally supported in C++0x (an implementation may choose to define the behavior and if it does define it then it must do what the standard says it should do. A void*, as defined by the C++98/03 standard, was meant to point to objects and not to contain function pointers or member pointers.

And again:

You can't.

You can cast a data pointer to void* and then back to the same pointer type you have started with. std::function is not a pointer type, so the cast is statically invalid, and it's not the same thing you have started with. You have started with a .target of type void()() but it's not a data pointer, it's a function pointer, so casting it to void and back is implementation-defined.

In your specific situation, you could try something like this:

class PointerToFunction {

};

template <typename Type>
class PointerToFunctionType : public PointerToFunction, std::function<Type> {
    public:
        using std::function<Type>::function; // I think this is the right syntax for this trick: https://softwareengineering.stackexchange.com/a/197901/342247


};

// Now, we can do this:

std::vector<PointerToType*> _CallbackVector;

Basically, we're using an inheritance trick similar to how boost::any/std::any (since C++17) is implemented to store any std::function in a pointer. The only issue is knowing how to convert it back. Doing this properly would depend on how you expected to convert that void* back to begin with (i.e., knowing its type), so I'll leave that part up to you.