-1

Is it possible to call a member function with a specific object instance when only owning a void* pointer to the specific instance and a returnType(*function)(parameters) function pointer?

#include <iostream>
class testClass
{
    public:
    int classNum;
    testClass(int _num) : classNum(_num) {}
    void testFunction(int _num) { std::cout << _num + classNum << "\n"; }
};

int main()
{
    testClass instance(3);

    void(*function)(int) = (&testClass::testFunction); // edit: this is wrong, but "&test::testFunction" is the information provided nonetheless
    void* instancePtr = (void*)&instance;

    // can we call "testFunction(3)" on "instance" with only "function" and "instancePtr"?
}
ZeroZ30o
  • 375
  • 2
  • 18

2 Answers2

4

No; you have two problems:

  1. The pointer-to-member-function has the wrong type and won't bind to the (&testClass::testFunction).

  2. The object pointer has had its type erased. You can cast it from void* back to testClass* if you know that's what it is, but then you would never have needed it to be void*, right?

Overall a combination of magic like lambdas and std::function would be far superior.

Here's some magic:

int main()
{
    testClass instance(3);

    std::function<void(void*, int)> f = [](void* instancePtr, int _num) {
        static_cast<testClass*>(instancePtr)->testFunction(_num);
    };

    void* instancePtr = &instance;
    f(instancePtr, 42);
}

Your options really depend on what you're trying to do, though.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • Well, my aim is to make a class similar to std::function that can contain either a normal function pointer OR a class member function pointer + an associated pointer to object. Additionally I would like if this class is able fit in a vector by value (no templates or inheritance). I can achieve the first, but I'm having trouble with the second. Since the pointer to object would be stored in raw memory, its type would be lost. – ZeroZ30o Oct 24 '18 at 11:28
  • @ZeroZ30o I think I remember that std::function uses RTTI (virtual calls!) to achieve that. Probably some base class and then derived class templates that keep the type information for you. It's not trivial though – Lightness Races in Orbit Oct 24 '18 at 11:29
2

You need to change the pointer type to void(*function)(void*, int) so that it can accept the pointer to the object and the argument. Example:

int main() {
    testClass instance(3);
    void(*function)(void*, int) = [](void* p, int a) { static_cast<testClass*>(p)->testFunction(a); };
    void* instancePtr = &instance;
    function(instancePtr, 3);
}
Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271
  • The question is about resolving the original type without any prior knowledge of the class type. – Daniel Trugman Oct 24 '18 at 11:33
  • Can this lambda be stored in raw memory? And if so, is its size (with the same parameters and no capture) the same? @DanielTrugman knowledge of the class type is known during initialization, so if it is managed to be stored thanks to a pointer, it fits the use perfectly. – ZeroZ30o Oct 24 '18 at 11:37
  • @ZeroZ30o A lambda object with no capture can be converted to a plain function pointer. See `ClosureType::operator ret(*)(params)()` in https://en.cppreference.com/w/cpp/language/lambda – Maxim Egorushkin Oct 24 '18 at 11:39
  • 1
    Thank you! Works flawlessly -it would be even better if there was an automated way to generate the lambda (only the using the "function" class' constructor with a testClass* and testFunction e.g.: function(&instance, testFunction)), but it works very well as-is if a little tedious. – ZeroZ30o Oct 24 '18 at 11:53
  • 1
    @ZeroZ30o `template R(*)(void*, Args...) type_erase(R(T::*member)(Args...)) { return [](void * p, Args ... args){ return (*static_cast(p).*member)(std::forward(args)...); }; }` – Caleth Oct 24 '18 at 11:58
  • @MaximEgorushkin, well, I see that it did solve his specific edge-case, which is good enough for me. GJ – Daniel Trugman Oct 24 '18 at 12:00
  • @Caleth You do not capture `member`. – Maxim Egorushkin Oct 24 '18 at 12:01
  • Good point. You have to do some contortions to get back `T`, `R` and `Args...` if you pass `member` as a template parameter, but it's possible – Caleth Oct 24 '18 at 12:03
  • @MaximEgorushkin ? `R(*)(void*, Args...)` is a pointer to function, and `R(T::*)(Args...)` is a pointer to a member function of `T`. – Caleth Oct 24 '18 at 12:10
  • @Caleth Consider syntax of a function that returns a function pointer. – Maxim Egorushkin Oct 24 '18 at 12:13
  • @Caleth I've attempted a simpler version with Args necesarily being 1 parameter for now, but it won't recognise `tReturn(*)(void*, tParams)` as a return type: https://i.imgur.com/xZtk5NH.png – ZeroZ30o Oct 24 '18 at 12:14
  • @ZeroZ30o [Some contortions ensued](http://coliru.stacked-crooked.com/a/ed4c227423b1e01b) – Caleth Oct 24 '18 at 12:29
  • @Caleth woah, that seems overly complicated -why won't it simply accept `tReturn(*)(void*, tParams)` as a return parameter in the first place? – ZeroZ30o Oct 24 '18 at 12:31
  • @Caleth Yep, C++17 `auto` template arg solves this nicely. – Maxim Egorushkin Oct 24 '18 at 12:31
  • @ZeroZ30o it's ambiguous with the rest of the function declaration. Moving it out to an alias makes the grammar happy – Caleth Oct 24 '18 at 12:32
  • @Caleth I realised I'm running into an issue: the `auto member` in your code is provided in a class' constructor in my case. And class constructors, for some idiotic reason, cannot have their specific template called with `<>`. Do you have any idea on how to fix this? And yes, I do realize it would be a workaround for a workaround :P – ZeroZ30o Oct 24 '18 at 22:21
  • @ZeroZ30o you'd do it in a `make_` function template – Caleth Oct 24 '18 at 23:14
  • @ZeroZ30o it's not [much different](http://coliru.stacked-crooked.com/a/401f83c400acff9a) to `type_erase` – Caleth Oct 24 '18 at 23:35
  • @Caleth yeah I finally gave up trying to get the constructor to work, it's impossible; but I got everything working now -thanks for the help! – ZeroZ30o Oct 25 '18 at 10:59