То, что я пытаюсь сделать, это иметь интерфейс обратного вызова только с функциональными членами. Сегодня у меня есть 2 последних класса, которые используют этот интерфейс для определения своего поведения, сохраняя определенные обратные вызовы в CallbackStore.
Из моих ограничений (здесь) я получил следующий минимальный код компиляции, но только с одним дочерним классом:
#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.
}
Добавляя новый дочерний класс,
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 );
};
};
и в основном()
FinalLChild d;
cout << "d:\n";
for(int i = 0; i < STORE_SIZE; i++)
{
d.callStep(i,8);
}
это вызывает следующую ошибку компиляции:
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() {};
^~~~~~~~~
Вместо специализации шаблона
template<typename Base>
class Interface<Base,void>// interface
{
public:
Interface() : Interface<Base,void>() {};
};
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 NoneT {};
class NoneT {};
class ListModel : public MotherA
{
};
class FinalLChild : public Interface<ListModel,NoneT>, 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 );
};
};
Я полагаю, проблема в этой паре конструкторов
Interface() : Base() {};
Interface(ArgT... arg) : Base(arg...) {};
Когда ArgT...
— пустой список, они являются одним и тем же конструктором и сталкиваются.
Я предлагаю SFINAE включить/отключить второй следующим образом
template <bool B = (sizeof...(ArgT)>0u), std::enable_if_t<B, bool> = true>
Interface(ArgT... arg) : Base(arg...) {};
Таким образом, вторая конструкция активна только тогда, когда ArgsT...
не пуст.
Альтернативой может быть отказ от SFINAE (второй когда-либо включенный конструктор), но преобразование его в шаблон с вариативным безымянным списком неиспользуемых параметров шаблона, поэтому первый имеет приоритет
Interface() : Base() {};
template <typename ...>
Interface(ArgT... arg) : Base(arg...) {};