-1

I have a unique problem I am trying to solve and am unable to find a good solution. I am aware this might not be the best architecture but I am exploring solutions.

Let's say I have a base class, and two child classes inheriting from this base class. The child classes have a mix of unique functions that all have the same function prototype (same return value and arguments).

Class Base
{
  Base(){}     
}

Class Child : Base
{
  Child(){}  
  void FunctionA(int Arg1, int Arg2){};
  void FunctionB(int Arg1, int Arg2){};
}

Class WildChild : Base
{
  WildChild(){}  
  void FunctionC(int Arg1, int Arg2){};
  void FunctionD(int Arg1, int Arg2){};
}

Is there any way to define member function pointers such that the Base class could use these function pointers, even though the Base class doesn't implement the corresponding functions?

The way I understand it is that the child classes need to implement their own function pointer definitions and could populate a table of function pointers...

For Child

typedef void (Child::*ChildFunctionPtrType)(int Arg1, int Arg2);
ChildFunctionPtrType funcPtrTable[2];
funcPtrTable[0] = &Child::FunctionA;
funcPtrTable[1] = &Child::FunctionB;

For WildChild

typedef void (WildChild::*WildChildFunctionPtrType)(int Arg1, int Arg2);
WildChildFunctionPtrType funcPtrTable[2];
funcPtrTable[0] = &WildChildFunctionPtrType::FunctionC;
funcPtrTable[1] = &WildChildFunctionPtrType::FunctionD;

But what I really need is a function pointer table that could be used by the Base class but contain pointers to member functions in the Child classes.

typedef void (Base::*FunctionPtrType)(int Arg1, int Arg2);
Class Base
{
  Base(){}              
  FunctionPtrType funcPtrTable[SIZE_OF_TABLE];
}

Child::PopulateTable
{
  funcPtrTable[0] = &Child::FunctionA;
  funcPtrTable[1] = &Child::FunctionB;
}

WildChild::PopulateTable
{
  funcPtrTable[0] = &WildChild::FunctionC;
  funcPtrTable[1] = &WildChild::FunctionD;
}

Is this possible? If not, is this possible using templates?

  • 4
    At the risk of sounding really dumb, what does this scheme of yours do that virtual functions/a vtable doesn't? – Borgleader Mar 22 '18 at 18:47
  • Real high level overview, I have 100's of objects that could serve up data that is spread out all over the place (Child Classes). Another external component interacts with my application, with each piece of data requested being a Class ID and a Data ID. The interface is the Base class. The data is being returned in a buffer, so the child classes need to populate the buffer in a certain way, with each id being different. For speed, readability, and code auto-generation, I need to make a table with function ID offsets and have base class call the correct function. – Robert Lefevre Mar 22 '18 at 19:03

1 Answers1

0

This paradigm can be useful for creating testable systems and mocking out objects, but keep in mind it can be much more expensive than virtual functions.

The answer is to use std::function and std::bind (C++11 and above). Here's an example:

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

class Base
{
public:
    Base() { }

    void functionA(int a, int b) {
        if (fnA_) {
            fnA_(a, b);
        }
    } 
    void functionB(int a, int b) {
        if (fnB_) {
            fnB_(a, b);
        }
    } 
protected:
    typedef std::function<void(int, int)> FunctionA;
    typedef std::function<void(int, int)> FunctionB;

    FunctionA& fnA() { return fnA_;}
    FunctionB& fnB() { return fnB_;}

private:
    FunctionA fnA_;
    FunctionB fnB_;
};

class Child : public Base {
public:
    Child() {
        fnA() = std::bind(&Child::functionAImpl, this, std::placeholders::_1, std::placeholders::_2);
        fnB() = std::bind(&Child::functionBImpl, this, std::placeholders::_1, std::placeholders::_2);
    }
private:
    // Please make them different names
    void functionAImpl(int a, int b) {
        std::cout << "Child::functionAImpl: a=" << a << ", b=" << b << std::endl;
    } 
    void functionBImpl(int a, int b) {
        std::cout << "Child::functionBImpl: a=" << a << ", b=" << b << std::endl;
    } 
};

class WildChild : public Base {
public:
    WildChild() {
        fnA() = std::bind(&WildChild::functionAImpl, this, std::placeholders::_1, std::placeholders::_2);
        fnB() = std::bind(&WildChild::functionBImpl, this, std::placeholders::_1, std::placeholders::_2);
    }
private:
    // Please make them different names
    void functionAImpl(int a, int b) {
        std::cout << "WildChild::functionAImpl: a=" << a * 2 << ", b=" << b * -3 << std::endl;
    } 
    void functionBImpl(int a, int b) {
        std::cout << "WildChild::functionBImpl: a=" << a * -4 << ", b=" << b * 5 << std::endl;
    } 
};
int main(int argc, const char** argv) {
    Child child;
    child.functionA(3, 4);
    child.functionB(5, 6);
    WildChild wildChild;
    wildChild.functionA(3, 4);
    wildChild.functionB(5, 6);
    return 0;
}

And here's the output from clang:

clang++ --std=c++11 -g today.cpp && ./a.out
Child::functionAImpl: a=3, b=4
Child::functionBImpl: a=5, b=6
WildChild::functionAImpl: a=6, b=-12
WildChild::functionBImpl: a=-20, b=30

Hope this helps!

Jason
  • 1,086
  • 5
  • 10