What I'm trying to do is to have a callback interface with function members only. I have, today, 2 final classes, that use this interface to define their behavior by saving specific callbacks in the CallbackStore.
What I achieved, from my constraints (here), is this following minimal compiling code, but only with one child class:
#include <iostream>
#include <stdint.h>
using namespace std;
static constexpr int STORE_SIZE = 4;
void* operator new(size_t size)
{
cout << "ERROR HEAP USED" << endl;
}
template<typename T, size_t storeSize>
class CallbackStore
{
public:
CallbackStore() : that_(nullptr) {};
CallbackStore(T* that) : that_(that) {};
using CallbackCondition = bool (*) (T*);
using CallbackAction = void (*) (T*,int);
struct Step
{
CallbackCondition pCallbackCondition;
CallbackAction pCallbackAction;
};
void setStep(int stepId,CallbackCondition pCallbackCondition, CallbackAction pCallbackAction)
{
if(stepId<storeSize)
{
store[stepId].pCallbackCondition = pCallbackCondition;
store[stepId].pCallbackAction = pCallbackAction;
}
else
{
cout << "pointer error" << endl;
}
}
void callStep(int stepId, int param)
{
if((stepId<storeSize) &&
(store[stepId].pCallbackCondition != nullptr) &&
(store[stepId].pCallbackAction != nullptr) &&
(that_ != nullptr))
{
bool isActive = (*(store[stepId].pCallbackCondition))(that_);
if(isActive) {(*(store[stepId].pCallbackAction))(that_,param);}
}
else
{
cout << "pointer error" << endl;
}
}
Step store[storeSize];
T* that_;
};
template<typename Base, typename... ArgT>
class Interface : public Base // interface
{
public:
Interface() : Base() {};
Interface(ArgT... arg) : Base(arg...) {};
public:
static bool True(Base* baseInstance)
{
return true;
}
static bool IsNegative(Base* baseInstance)
{
return ((static_cast<Base*>(baseInstance))->getValue() < 0);
}
static bool IsNull(Base* baseInstance)
{
return ((static_cast<Base*>(baseInstance))->getValue() == 0);
}
static bool IsPositive(Base* baseInstance)
{
return (IsNegative(baseInstance) == false);
}
static void PrintValue(Base* baseInstance, int value)
{
cout << "print this value : " << value << "." << endl;
}
};
template<typename Base>
class Interface<Base,void>// interface
{
public:
Interface() : Interface<Base,void>() {};
};
class MotherA
{
public:
MotherA(){};
MotherA(int x): x_(x){};
int getValue() { return x_; }
void setValue(int x) { x_ = x; }
protected:
int x_ = -3;
};
class ListModel : public MotherA
{
};
class FinalLChild : public Interface<ListModel>, public CallbackStore<ListModel, STORE_SIZE>
{
public:
FinalLChild(): Interface(), CallbackStore(this)
{
setStep(0, &Interface::IsNegative, &Interface::PrintValue );
setStep(1, &Interface::IsNegative, &Interface::PrintValue );
setStep(2, &Interface::IsNull, &Interface::PrintValue );
setStep(3, &Interface::True, &Interface::PrintValue );
};
};
class ValueModel : public MotherA
{
public:
ValueModel() : MotherA(), y_(0) {};
ValueModel(int x,int y) : MotherA(x), y_(y) {};
void reset(){x_= y_;};
int y_ = 0;
};
class ValueChild : public Interface<ValueModel,int,int>, public CallbackStore<ValueModel, STORE_SIZE>
{
public:
ValueChild() : Interface(), CallbackStore(nullptr){};
ValueChild(int x,int y): Interface(x,y), CallbackStore(this){};
};
class FinalVChild : public ValueChild
{
public:
FinalVChild():ValueChild(2,0)
{
setStep(0, &Interface::IsPositive, &Interface::PrintValue );
setStep(1, &Interface::IsPositive, &Interface::PrintValue );
setStep(2, &Interface::IsNull, &Interface::PrintValue );
setStep(3, &Interface::IsNull, &Interface::PrintValue );
};
};
int main()
{
FinalVChild c;
for(int i = 0; i < STORE_SIZE; i++)
{
c.callStep(i,8);
}
cout << "reset:\n";
c.reset();
for(int i = 0; i < STORE_SIZE; i++)
{
c.callStep(i,8);
}
// shall print "print this value : 8." 3 times if x_ is null, twice if x_ is negative.
}
By adding a new child class,
class ListModel : public MotherA
{
};
class FinalLChild : public Interface<ListModel>, public CallbackStore<ListModel, STORE_SIZE>
{
public:
FinalLChild(): Interface(), CallbackStore(this)
{
setStep(0, &Interface::IsNegative, &Interface::PrintValue );
setStep(1, &Interface::IsNegative, &Interface::PrintValue );
setStep(2, &Interface::IsNull, &Interface::PrintValue );
setStep(3, &Interface::True, &Interface::PrintValue );
};
};
and in the main()
FinalLChild d;
cout << "d:\n";
for(int i = 0; i < STORE_SIZE; i++)
{
d.callStep(i,8);
}
this throws the following compilation error:
main.cpp: In instantiation of ‘class Interface<ListModel>’:
<span class="error_line" onclick="ide.gotoLine('main.cpp',113)">main.cpp:113:57</span>: required from here
main.cpp:65:5: error: ‘Interface::Interface(ArgT ...) [with Base = ListModel; ArgT = {}]’ cannot be overloaded
Interface(ArgT... arg) : Base(arg...) {};
^~~~~~~~~
main.cpp:64:5: error: with ‘Interface::Interface() [with Base = ListModel; ArgT = {}]’
Interface() : Base() {};
^~~~~~~~~
I suppose the problem is this couple of constructors
Interface() : Base() {};
Interface(ArgT... arg) : Base(arg...) {};
When ArgT...
is the empty list, they are the same constructor and collide.
I propose to SFINAE enable/disable the second one as follows
template <bool B = (sizeof...(ArgT)>0u), std::enable_if_t<B, bool> = true>
Interface(ArgT... arg) : Base(arg...) {};
This way the second construct is enabled only when ArgsT...
isn't empty.
An alternative could be avoid SFINAE (the second constructor ever enabled) but transform it in a template one, with and variadic un-named list of unused template parameters, so the first one has the precedence
Interface() : Base() {};
template <typename ...>
Interface(ArgT... arg) : Base(arg...) {};