4

I am working on a piece of code that presents a very odd behavior. I managed to replicate it in a simple hello world style program, following is the code:

#include <iostream>
using namespace std;

class Test
{
public:
    virtual ~Test() = default;

protected:
    virtual void SetUp() { }
};

class ICallbackReceiver
{
public:
    virtual ~ICallbackReceiver() = default;
    virtual void onReady() = 0;
};

// C-style callback
void readyReceiver(void* userdata)
{
    cout << "3) readyReceiver\n";
    static_cast<ICallbackReceiver*>(userdata)->onReady();
}

using callback_t = void(*)(void*);
callback_t myCallback;
void* myUserData;

void registerCallback(callback_t callback, void* userData)
{
    cout << "2) registerCallback\n";
    myCallback = callback;
    myUserData = userData;
}

class ConfigurableTest : public /*virtual*/ Test, public ICallbackReceiver
{
public:

    void SetUp() override
    {
        cout << "1) ConfigurableTest::SetUp\n";
        registerCallback(&readyReceiver, static_cast<void*>(this));
    }

    void onReady() override
    {
        cout << "4) ConfigurableTest::onReady\n";
    }
};

int main()
{
    ConfigurableTest test;
    test.SetUp();

    myCallback(myUserData);

    return 0;
}

Whenever myCallback is called something has to be tested. And this is the output that should be displayed:

1) ConfigurableTest::SetUp
2) registerCallback
3) readyReceiver
4) ConfigurableTest::onReady

But, unless I specify a virtual inheritance for the Test class, this is the output that I see:

1) ConfigurableTest::SetUp
2) registerCallback
3) readyReceiver
1) ConfigurableTest::SetUp
2) registerCallback

As you can see ConfigurableTest::onReady is never called, but ConfigurableTest::SetUp is actually called twice!

What is the origin of this behavior? How can I re-factor the code to reproduce the correct behavior without using virtual inheritance?

Nick
  • 10,309
  • 21
  • 97
  • 201

1 Answers1

4

The problem is because you make conversions ConfigurableTest -> void * -> ICallbackReceiver, what is not permitted. You need to convert to the same type ConfigurableTest back from void * - no error in this case.

See also multiple inheritance: unexpected result after cast from void * to 2nd base class

Anton Malyshev
  • 8,686
  • 2
  • 27
  • 45