2

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

  • Preferably, is there a way to fix the line marked with (*) in a simple way?
  • If not, what is the best solution (one of the alternatives above, or something else)?
  • If I were able to contact the author of class 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.

3
  • In your MCVE, could it be a problem that the member pointer is cast to the type Function, defined as a pointer function to a member of A while the function being cast is not a member of A? Commented May 16, 2017 at 17:43
  • @jwimberley as far as I could see by poking at the header files, it is actually pointing to a member of Unknown but I guess that doesn't change the problem (the idea is probably that Unknown is zero bytes). Commented May 16, 2017 at 18:11
  • 1
    Your program has a terminal case of undefined behaviour. Unions cannot be used this way, and crashes are fully expected if you try to. Commented May 16, 2017 at 18:25

1 Answer 1

1

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).

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks jwimberly, even though I cannot modify that class I am in a position to contact the person who wrote it. With your help I should be able to make a concrete request for change. At least have an upvote, I will let you know how it continues.
Based on @n.m. 's comment, perhaps the real problem is with the cast function itself, and this is the reason my "fix" (which gets rid of the cast) works

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.