A class Base
, which I have no control over, has a function that accepts a member pointer to any class function. It is meant to be used as follows:
class Derived : public Base {
void bindProperties() {
Base::bindProperty("answer", &Derived::getAnswer);
}
int getAnswer() const { return 42; }
};
Some way (that I neither know nor care about), Base
stores this pointer and later allows me to call Derived::get("answer")
(of course, this is a simplified situation).
The down side is, that we tried to be smart in the past, and used multiple inheritance:
class ICalculator {
virtual int getAnswer() const;
};
template<class T>
class LifeAndUniverseCalculator : public T, public ICalculator {
virtual int getAnswer() const /* override */ { return 42; }
void bindProperties() {
T::bindProperty("answer", &ICalculator::getAnswer); // (*)
}
};
thinking that the multiple inheritance is not bad, as long as we only use it to inherit an interface and only have one "concrete" base class.
The templating is because sometimes we want to derive from Base
and sometimes from one of its derived classes (which I also don't have access to) - if that is irrelevant you can pretend I wrote Base
instead of T
and drop the template.
Anyway, the problem I am having now, is that when I call
LifeAndUniverseCalculator calc;
calc.bindProperties();
int answer = calc.get("answer");
I get gibberish. I figured it may be something with pointers into vtables, so I tried replacing
T::bindProperty("answer", &ICalculator::getAnswer);
by
T::bindProperty("answer", &LifeAndUniverseCalculator::getAnswer);
hoping that it would calculate the offset correctly, but clearly that does not work (as you have figured out by now, I am really second guessing how this all works).
I thought of some options, such as
getting rid of the multiple inheritance and putting everything in ICalculator
directly in LifeAndUniverseCalculator
(it's the only derived class)
creating wrapper functions for all ICalculator
stuff in LifeAndUniverseCalculator
, e.g. LifeAndUniverseCalculator::Calculator_GetAnswer
just calls ICalculator::GetAnswer
.
I'd like to know
Base
and they would be willing and able to change their class, what specifically would I need to ask, if you are able to say something sensible based on my description.If you need a MCVE, there is one which I think captures the problem on IDEOne.
In your MCVE, the function A::bindFunction
(analogous to Base::bindProperty
in your simplified code) force casts a member of function of B
to a member function of A
. This strikes me as the root problem. This can be fixed by changing the type of A::f
to be an std::function<int(void)>
:
class A
: public ABase {
public:
// int a, b;
class Unknown{};
typedef int(A::*Function)();
template<typename T, typename Func>
void bindFunction(T* owner, Func myf) {
f = std::bind(myf,owner);
}
int call() {
return f();
}
//Function f;
std::function<int(void)> f;
};
...
class Combined
: public A, public B {
public:
Combined(int value) : B(value), A() {}
virtual void /*A::*/bind() /* override */ {
A::bindFunction( this, &Combined::getValue );
}
};
With only this change, your MCVE works, printing out
The answer to Life, The Universe and Everything is 42
However, I recognize that the code that I changed belongs to a class that you've explicitly mentioned that you cannot modify. Is this indeed what Base
does -- it casts member functions of other classes to member functions of itself? (Or perhaps, while my fix makes the code work, I've misidentified the problem).