0

Is it possible to assign a function from a specific instance of a class as a parameter? Multiple classes can have the same signature. Here an example. Thank you!

UPDATE: The context is that we are using a third party library that supply us with call back after a device finish the process. The typedef is what look like the signature from the third party library.

UPDATE: After checking the link, the solution seems to embed a specific class. Is there a way to be more generic?

typedef double(*Processor) (
    double  first,
    double  second
);

double Adjustment = 0.5;
double Multiply(double first, double second)
{
    return first * second * Adjustment;
}

class Substract
{
public:
    double Adjustment;

    Substract(double adjustment) : Adjustment(adjustment) {}

    double Process(double first, double second)
    {
        return first - second - Adjustment;
    }
};

class Add
{
public:
    double Adjustment;

    Add(double adjustment) : Adjustment(adjustment) {}

    double Process(double first, double second)
    {
        return first + second + Adjustment;
    }
};

void Process(Processor processor)
{
    double result = processor(100, 50);
    std::cout << "Multiply=" << result << std::endl;
}

int _tmain(int argc, _TCHAR* argv[])
{
    double result;

    Process(Multiply); // <-- success

    Substract* substract = new Substract(0.5);
    Process(substract->Process); // <-- not compiling
    delete substract;

    Add* add = new Add(0.5);
    Process(add->Process); // <-- not compiling
    delete add;

    getchar();

}
Tom Sawyer
  • 835
  • 2
  • 10
  • 32
  • 3
    Possible duplicate of [Function pointer to member function](https://stackoverflow.com/questions/2402579/function-pointer-to-member-function) – UnholySheep May 09 '18 at 13:57
  • 1
    Is there a way to hide my question temporary to check the link? Then I will close it if it works or I will rephrase my question? Thanks – Tom Sawyer May 09 '18 at 14:04
  • 1
    @SebastienDErrico: The poster can delete its own question and then undelete it - of course you cannot if it was deleted not by you but by the community or a moderator. – Serge Ballesta May 09 '18 at 14:41
  • "After checking the link, the solution seems to embed a specific class. Is there a way to be more generic?" If you want generic function, you can always use template. – he rambled May 09 '18 at 15:30
  • @herambled If our third party supplier used typedef double(*Processor), something that we cannot change, is that means we cannot specified a function from an instance, we are stuck using a global function? – Tom Sawyer May 09 '18 at 16:02
  • Well, at least not elegantly. member functions must be provided `this` pointer, and `Processor` typedef in your code does not have it. So, if you really want that, you could assign your class instances to global variable, and write a free function that comports to `Processor` signiture that invokes member functions using the global variables. Having said that, it is EXTREMELY dirty. – he rambled May 09 '18 at 16:09
  • @herambled this is EXACTLY what I am trying to refactor. I was trying to remove all global elements creating dependencies to others global elements. Because we are not in position to modify the Processor typedef, I conclude that we are stuck to leave the implementation as is. – Tom Sawyer May 09 '18 at 17:38
  • @herambled Thank you for your time. Do you want to turn your comment to an answer? I will click it as best answer. Thanks! – Tom Sawyer May 09 '18 at 17:39
  • Sure :) I'll post it with some example code in case that some other people might need it. – he rambled May 09 '18 at 17:41

1 Answers1

1

There is no elegant way. Member functions must be provided with this pointer, and Processor typedef in your code does not have it. Even if it has some kind of void* that can store your instance pointer, the calling convention of free function and member functions are different. It need to use something like std::invoke to properly handle it, which is unlikely for your case.

So, if you really want that, you could assign your class instances to global variable, and write a free function that comports to Processor signiture that invokes member functions using the global variables. Having said that, it is EXTREMELY dirty.

For example, you can do this:

typedef double(*Processor) (
    double  first,
    double  second
);

void Process(Processor processor)
{
    double result = processor(100, 50);
    std::cout << "Multiply=" << result << std::endl;
}

class Add
{
public:
    double Adjustment;

    Add(double adjustment) : Adjustment(adjustment) {}

    double Process(double first, double second)
    {
        return first + second + Adjustment;
    }
};

Add* g_add;

double add_f(double first, double second)
{
    return g_add->Process(first, second);
}

int _tmain(int argc, _TCHAR* argv[])
{
    g_add = new Add(0.5);
    Process(add_f);
    delete g_add; g_add = nullptr;

    getchar();
}

But it leads to VERY BAD software design. Replace your third party library to one that supports C++ functors if possible. If not, use this technique only if you are unquestionably sure that you can manage this horrible design.

he rambled
  • 154
  • 2
  • 8
  • Hahaha, thank you for the last paragraph. Unfortunately, our company is integrating a device and the official lib bundled with it force us to use callback with a typedef and I conclude from your answer that the typedef force us to use global instances. – Tom Sawyer May 09 '18 at 18:27
  • @sebastien Bad things happen :( – he rambled May 09 '18 at 18:28