Je travaille sur un morceau de code qui présente un comportement très étrange. J'ai réussi à le reproduire dans un programme simple de style "hello world", voici le 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;
};
// Callback de style C
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(this));
}
void onReady() override
{
cout << "4) ConfigurableTest::onReady\n";
}
};
int main()
{
ConfigurableTest test;
test.SetUp();
myCallback(myUserData);
return 0;
}
Chaque fois que myCallback
est appelé, quelque chose doit être testé. Et voici la sortie qui devrait être affichée :
1) ConfigurableTest::SetUp
2) registerCallback
3) readyReceiver
4) ConfigurableTest::onReady
Mais, à moins que je spécifie un héritage virtuel
pour la classe Test
, voici la sortie que je vois :
1) ConfigurableTest::SetUp
2) registerCallback
3) readyReceiver
1) ConfigurableTest::SetUp
2) registerCallback
Comme vous pouvez le voir, ConfigurableTest::onReady
n'est jamais appelé, mais ConfigurableTest::SetUp
est en fait appelé deux fois !
Quelle est l'origine de ce comportement ? Comment puis-je refactoriser le code pour reproduire le comportement correct sans utiliser l'héritage virtuel
?