I have a policy based design where I have some function foo()
defined in some policies but not others. The base class always inherits from some class called AlwaysBase
which has an inferior foo()
function.
I want to make it so if a "better" foo()
exists (one from a Policy class) that one will always be chosen.
Here is some code:
// logic for determining if a class has a member function (not really related)
template <class> struct sfinae_true: std::true_type{};
template <class T> static auto test_foo(int) -> sfinae_true<decltype(std::declval<T>().foo())>;
template <class> static auto test_foo(long) -> std::false_type;
template <class T> struct has_foo : decltype(test_foo<T>(0)){};
class AlwaysBase{ public: void foo(){ cout << "in AlwaysBase foo()" << endl; } };
class BPolicy{};
class CPolicy{ public: void foo(){ cout << "in Cpolicy foo()" << endl; } };
template<typename T, typename ...Args>
class A: public T, public A<Args...>
{
public:
using std::conditional_t<has_foo<T>::value, T, AlwaysBase>::foo;
};
template<typename T>
class A<T>: public T, public AlwaysBase
{
public:
using std::conditional_t<has_foo<T>::value, T, AlwaysBase>::foo;
};
int main()
{
A<BPolicy> b;
b.foo(); // outputs: In AlwaysBase foo()
A<CPolicy> c;
c.foo(); // outputs: In CPolicy foo()
A<CPolicy, BPolicy> cb;
cb.foo(); // outputs: In CPolicy foo()
A<BPolicy, CPolicy> bc;
bc.foo(); // outputs: In AlwaysBase foo() // I WANT THIS TO OUTPUT!!!!: In CPolicy foo
}
I understand what is happening here, when BPolicy
is first, that using statement gets used in the variadic args class first and shadows any preceding using statement that would be found in the terminating base class (namely the CPolicy::foo).
I would like for the order to not matter when specifying policies, and if a Policy has a determinant, that one should always be chosen first over the AlwaysBase::foo
.
Use recursion. If empty lists are allowed, this is very easy:
// recursive invariant: As<Ts...>::foo is always the foo from the first capable Ts or from AlwaysBase
template<typename... Ts>
class A : public AlwaysBase { // template<> class A<>
public:
// there are no capable Ts
using AlwaysBase::foo;
};
template<typename T, typename... Ts>
class A<T, Ts...> : public T, public A<Ts...> {
public:
// from the A<Ts...> superclass, not AlwaysBase
// try to use T, fall back to Ts, and only then fall back to AlwaysBase
using std::conditional_t<has_foo<T>::value, T, A<Ts...>>::foo;
};
Constraining to nonempty lists is a bit more annoying:
template<typename T, typename... Ts>
class A : public T, public A<Ts...> {
public:
using std::conditional_t<has_foo<T>::value, T, A<Ts...>>::foo;
};
template<typename T>
class A<T> : public T, public AlwaysBase {
public:
using std::conditional_t<has_foo<T>::value, T, AlwaysBase>::foo;
};
Order still matters, though. If two policies provide foo
, the first one wins.