I'm creating a class C that inherits from variable amount of classes. List of those classes is defined, for example: A,B. In function of class C I need to call functions from all base classes but objects can be C<A,B> , C<A>or C<B> so if I will call functions of class A in C<B> I will get an error. Here is example of the classes and how I've tried to solve problem:
class A
{
int a;
public:
virtual void set_a(const int &value)
{
a = value;
}
protected:
virtual int get_a()
{
return this->a;
}
};
class B
{
int b;
public:
virtual void set_b(const int &value)
{
b = value;
}
protected:
virtual int get_b()
{
return this->b;
}
};
template<class ...T>
struct Has_A
{
template<class U = C<T...>>
static constexpr bool value = std::is_base_of < A, U > ::value;
};
template<class ...T>
class C :
virtual public T...
{
public:
#define HAS_A Has_A<T...>::value
void f()
{
#if HAS_A<>
auto a = this->get_a();
#endif
auto b = this->get_b();
cout << HAS_A<>;
}
};
When I call f() of object C<A,B> it skips the call get_a() but output is true.
Initially, I wrote this
template<class U = C<T...>>
typename std::enable_if<!std::is_base_of<A, U>::value, int>::type get_a()
{
return -1;
}
template<class U = C<T...>>
typename std::enable_if<std::is_base_of<A,U>::value, int>::type get_a()
{
return A::get_a();
}
But I don't want to rewrite this for all functions of A and B. Let's assume that A has 10 more functions.
Is there any beautiful solution?
P.S Sorry for my English. I never used SFINAE before.
Basically I have bunch of genes and I want to write convenient wrap for them where one can configure genes that he wants organism to have.
In current standard, this is trivial:
void f() {
if constexpr(Has_A<T...>::value) {
auto a = get_a();
}
auto b = get_b();
}
If you can use C++17, the bipll's solution (if constexpr ()) is (IMHO) the better one.
Otherwise, C++11 or C++14, I'm not sure it's a good idea but I propose the following solution because it seems to me funny (and a little perverted).
First of all, instead of Has_A I propose a more generic isTypeInList
template <typename...>
struct isTypeInList;
template <typename X>
struct isTypeInList<X> : public std::false_type
{ };
template <typename X, typename ... Ts>
struct isTypeInList<X, X, Ts...> : public std::true_type
{ };
template <typename X, typename T0, typename ... Ts>
struct isTypeInList<X, T0, Ts...> : public isTypeInList<X, Ts...>
{ };
I also propose the use of the simple indexSequence
template <std::size_t...>
struct indexSequence
{ };
that is inspired to std::index_sequence that (unfortunately) is available only starting from C++14.
So, inside C<T...>, you can define the template using
template <typename X>
using list = typename std::conditional<isTypeInList<X, Ts...>{},
indexSequence<0u>,
indexSequence<>>::type;
so that list<A> is indexSequence<0> if A is part of the T... variadic list, indexSequence<> (empty sequence) otherwise.
Now you can write f() that simply call an helper function f_helper() that receive as many indexSequences as many types you need to check.
By example: if you need to know if A and B are part of the T... variadic list, you have to write f() as follows
void f ()
{ f_helper(list<A>{}, list<B>{}); }
Now f_helper() can be a private function and can be
template <std::size_t ... As, std::size_t ... Bs>
void f_helper (indexSequence<As...> const &,
indexSequence<Bs...> const &)
{
using unused = int[];
int a { -1 };
int b { -1 };
(void)unused { 0, ((void)As, a = this->get_a())... };
(void)unused { 0, ((void)Bs, b = this->get_b())... };
// do something with a and b
}
The idea is that As... is 0 if A is in T... or empty list otherwise.
So
int a { -1 };
initialize a with the value of your fake get_a().
With
(void)unused { 0, ((void)As, a = this->get_a())... };
is executed a = this->get_a(), only one time, iff (if and only if) A is in the T... variadic list.
The funny part of this solution is that a = this->get_a() isn't a problem when A isn't in the variadic list. Isn't there if As... is an empty list.
The following is a C++11 full working example (where I've renamed in Ts... the T... variadic sequence for C)
#include <utility>
#include <iostream>
#include <type_traits>
class A
{
private:
int a;
public:
virtual void set_a (int const & value)
{ a = value; }
protected:
virtual int get_a ()
{ std::cout << "get_a()!" << std::endl; return this->a; }
};
class B
{
private:
int b;
public:
virtual void set_b (int const & value)
{ b = value; }
protected:
virtual int get_b ()
{ std::cout << "get_b()!" << std::endl; return this->b; }
};
template <typename...>
struct isTypeInList;
template <typename X>
struct isTypeInList<X> : public std::false_type
{ };
template <typename X, typename ... Ts>
struct isTypeInList<X, X, Ts...> : public std::true_type
{ };
template <typename X, typename T0, typename ... Ts>
struct isTypeInList<X, T0, Ts...> : public isTypeInList<X, Ts...>
{ };
template <std::size_t...>
struct indexSequence
{ };
template <typename ... Ts>
class C : virtual public Ts...
{
private:
template <typename X>
using list = typename std::conditional<isTypeInList<X, Ts...>{},
indexSequence<0u>,
indexSequence<>>::type;
template <std::size_t ... As, std::size_t ... Bs>
void f_helper (indexSequence<As...> const &,
indexSequence<Bs...> const &)
{
using unused = int[];
int a { -1 };
int b { -1 };
(void)unused { 0, ((void)As, a = this->get_a())... };
(void)unused { 0, ((void)Bs, b = this->get_b())... };
// do something with a and b
}
public:
void f ()
{ f_helper(list<A>{}, list<B>{}); }
};
int main()
{
C<> c0;
C<A> ca;
C<B> cb;
C<A, B> cab;
std::cout << "--- c0.f()" << std::endl;
c0.f();
std::cout << "--- ca.f()" << std::endl;
ca.f();
std::cout << "--- cb.f()" << std::endl;
cb.f();
std::cout << "--- cab.f()" << std::endl;
cab.f();
}
I think you can do this with function-member-pointer.
call_if_base calls the given function-pointer only if baseT is the base of T. However all function-results are ignored and it requires at least one parameter.
template <class baseT, class T, typename funcT, class ...Args>
typename std::enable_if<std::is_base_of<baseT, T>::value, void>::type call_if_base(T& obj, funcT func, Args... args) {
(dynamic_cast<baseT&>(obj).*func)(args...);
}
template <class baseT, class T, typename funcT, class ...Args>
typename std::enable_if<!std::is_base_of<baseT, T>::value, void>::type call_if_base(T& obj, funcT func, Args... args) {
}
template<class ...T>
class C :
virtual public T...
{
public:
void set(const int &value) {
call_if_base<A, C>(*this, &A::set_a, 0);
call_if_base<B, C>(*this, &B::set_b, 5);
}
};
or as member-functions
template<class ...T>
class C :
virtual public T...
{
public:
void set(const int &value) {
call_if_base<A>(&A::set_a, 0);
call_if_base<B>(&B::set_b, 5);
}
protected:
template <class baseT, typename funcT, class ...Args>
typename std::enable_if<std::is_base_of<baseT, C>::value, void>::type call_if_base(funcT func, Args... args) {
(dynamic_cast<baseT&>(*this).*func)(args...);
}
template <class baseT, typename funcT, class ...Args>
typename std::enable_if<!std::is_base_of<baseT, C>::value, void>::type call_if_base(funcT func, Args... args) {
}
};
Related
I am trying to understand how to use std::enable_if to choose between 2 functions implementation. In this case, if the type TupleOfCallback doesn't contains all the type, it will not compile because std::get<...> will throw an error.
For exemple:
Executor<Entity1*, Entity2*> task([](Entity1 *e){}, [](Entity2 *2){});
This will not compile because Entity3* is not part of the tuple.
It seem that we can choose between two functions with the same prototype,
void Exec(Entity3 *entity)
{
//enabled when Entity3* is **not** in the tuple
}
OR
void Exec(Entity3 *entity)
{
//enabled when Entity3 is in the tuple
std::get<std::function<void(Entity3*)>>(m_Callbacks)(entity);
}
But i dont understand how to achieve this goal.
C++ template mechanism is still hard for me, any help is welcome.
template<typename ...T>
class Executor
{
typedef std::tuple<std::function<void(T)>...> TupleOfCallback;
public:
Executor(const std::function<void(T)> &...func)
{
}
void Exec(Entity1 *entity)
{
std::get<std::function<void(Entity1*)>>(m_Callbacks)(entity);
}
void Exec(Entity2 *entity)
{
std::get<std::function<void(Entity2*)>>(m_Callbacks)(entity);
}
void Exec(Entity3 *entity)
{
std::get<std::function<void(Entity3*)>>(m_Callbacks)(entity);
}
public:
TupleOfCallback m_Callbacks;
};
Building on this one Check if parameter pack contains a type. You can use two traits to select which method to call:
#include <iostream>
#include <type_traits>
struct Entity1 {};
struct Entity2 {};
struct Entity3 {};
template<typename What, typename ... Args>
struct is_present {
static constexpr bool value {(std::is_same_v<What, Args> || ...)};
};
template<typename T>
struct is_entity : is_present<T,Entity1,Entity2,Entity3> {};
template <typename T, typename ...Args>
struct is_present_entity {
static constexpr bool value = is_present<T,Args...>::value && is_entity<T>::value;
};
template <typename T, typename ...Args>
struct is_not_present_entity {
static constexpr bool value = (!is_present<T,Args...>::value) && is_entity<T>::value;
};
template<typename ...T>
class Executor
{
public:
template <typename U, std::enable_if_t< is_present_entity<U,T...>::value,bool> = true>
void Exec(U* t){
std::cout << "foo\n";
}
template <typename U, std::enable_if_t< is_not_present_entity<U,T...>::value,bool> = true>
void Exec(U* t){
std::cout << "bar\n";
}
};
struct foo {};
int main(void) {
Executor<Entity1,Entity2> ex;
Entity1 e1;
ex.Exec(&e1);
Entity3 e3;
ex.Exec(&e3);
// foo f;
// ex.Exec(&f);
}
output:
foo
bar
Another C++17 option:
template <typename T>
class ExecutorLeaf
{
public:
std::function<void(T)> callback;
void Exec(T entity) { callback(entity); }
};
template <typename... Ts>
class Executor : ExecutorLeaf<Ts>...
{
public:
Executor(const std::function<void(Ts)>&...funcs) : ExecutorLeaf<Ts>{funcs}... {}
using ExecutorLeaf<Ts>::Exec...; // C++17
// Fallback
template <typename T> void Exec(T) {}
};
Demo
If you can guarantee that all types appear only once, then the following should work:
template<typename... Ts>
class Executor {
using TupleOfCallback = std::tuple<std::function<void(Ts)>...>;
public:
Executor(const std::function<void(Ts)>&... func);
template<class E>
std::enable_if_t<(std::is_same_v<Ts, E*> || ...)>
Exec(E* entity) {
std::get<std::function<void(E*)>>(m_Callbacks)(entity);
}
template<class E>
std::enable_if_t<!(std::is_same_v<Ts, E*> || ...)>
Exec(E* entity)
{ }
public:
TupleOfCallback m_Callbacks;
};
The basic idea is to use fold-expression to detect whether E* is included in Ts..., thereby enabling the corresponding function.
Demo.
I'm new in the C++ world.
Sorry for my nooby question.
I have a class
template <typename T>
class Foo
{
T t_;
void say_hello()
{ std::cout << "Ciao";}
// work with T ...
};
I want to specialize this template class for 3 types.
If type is (A or B or C), Then use this class
template<>
class Foo<A or B or C>
{
void say_hello()
{ std::cout << "Hello";}
};
What's the best way to do this?
Thank you for your help.
A possible solution uses SFINAE
template <typename T, typename = void>
class Foo
{
T t_;
void say_hello()
{ std::cout << "Ciao";}
// work with T ...
};
template <typename T>
class Foo<T, std::enable_if_t<std::is_same_v<T, A>,
|| std::is_same_v<T, B>,
|| std::is_same_v<T, C>>
{
void say_hello()
{ std::cout << "Hello";}
};
If you don't use T inside the Foo specialization (as in your example) you can also use a sort of self-inheritance
template <typename T>
class Foo
{
T t_;
void say_hello()
{ std::cout << "Ciao";}
// work with T ...
};
template <>
class Foo<A>
{
void say_hello()
{ std::cout << "Hello";}
};
template <>
class Foo<B> : public Foo<A>
{ };
template <>
class Foo<C> : public Foo<A>
{ };
Off Topic: if you want to use say_hello() outside the class, is better if you make it public (or if you declare Foo as a struct).
There are several possibilities, for example:
Specialization of the method only:
template<>
void Foo<A>::say_hello() { std::cout << "Hello"; }
template<>
void Foo<B>::say_hello() { std::cout << "Hello"; }
template<>
void Foo<C>::say_hello() { std::cout << "Hello"; }
or, in C++17, you might do:
template <typename T>
class Foo
{
T t_;
void say_hello()
{
if constexpr(std::is_same_v<T, A> || std::is_same_v<T, B> || std::is_same_v<T, C>) {
std::cout << "Hello";
} else {
std::cout << "Ciao";
}
}
// work with T ...
};
Whereas regular if works in that example, it would fail if you call code specific to A, B, C.
if constexpr won't have that issue.
A variant of the SFINAE solution that is a bit more concise for more classes.
template<class T, class... Ts>
struct is_one_of;
template<class T, class Ts>
struct is_one_of<T, T, Ts...> : std::true_type {}; //maybe add std::decay_t
template<class T, class S, class Ts>
struct is_one_of<T, S, Ts...> : is_one_of<T, Ts...> {};
template<class T>
struct is_one_of<T> : std::false_type{};
template<class T, class... Ts>
constexpr bool is_one_of_v = is_one_of<T, Ts...>::value;
template <typename T, typename = void>
class Foo
{
T t_;
void say_hello()
{ std::cout << "Ciao";}
// work with T ...
};
template <typename T>
class Foo<T, std::enable_if_t<is_one_of_v<T, A, B, C>
{
void say_hello()
{ std::cout << "Hello";}
};
I want to have a generic function (or method) that accepts arguments of different types. If the provided type has 'one' method, the function should use it. If it has 'two' method, the function should use it instead.
Here's the invalid code:
#include <iostream>
template<typename Type> void func(Type t)
{
t.one();
}
template<typename Type> void func(Type t) // redefinition!
{
t.two();
}
class One
{
void one(void) const
{
std::cout << "one" << std::endl;
}
};
class Two
{
void two(void) const
{
std::cout << "two" << std::endl;
}
};
int main(int argc, char* argv[])
{
func(One()); // should print "one"
func(Two()); // should print "two"
return 0;
}
Is it possible to achieve using SFINAE? Is it possible to achieve using type_traits?
Clarification:
I would be more happy if this would be possible using SFINAE. The best-case scenario is: use first template, if it fails use the second one.
Checking for method existence is only an example. What I really want is also checking for compatibility with other classes.
The task could be rephrased:
If the class supports the first interface, use it.
If the first interface fails, use the second interface.
If both fail, report an error.
Yes, it's possible. In C++11 an onward it's even relatively easy.
#include <iostream>
#include <type_traits>
template<class, typename = void>
struct func_dispatch_tag :
std::integral_constant<int, 0> {};
template<class C>
struct func_dispatch_tag<C,
std::enable_if_t<std::is_same<decltype(&C::one), void (C::*)() const>::value>
> : std::integral_constant<int, 1> {};
template<class C>
struct func_dispatch_tag<C,
std::enable_if_t<std::is_same<decltype(&C::two), void (C::*)() const>::value>
> : std::integral_constant<int, 2> {};
template<class C>
void func(C const&, std::integral_constant<int, 0>) {
std::cout << "fallback!\n";
}
template<class C>
void func(C const &c, std::integral_constant<int, 1>) {
c.one();
}
template<class C>
void func(C const &c, std::integral_constant<int, 2>) {
c.two();
}
template<class C>
void func(C const &c) {
func(c, func_dispatch_tag<C>{});
}
struct One
{
void one(void) const
{
std::cout << "one\n";
}
};
struct Two
{
void two(void) const
{
std::cout << "two\n";
}
};
struct Three {};
int main(int argc, char* argv[])
{
func(One()); // should print "one"
func(Two()); // should print "two"
func(Three());
return 0;
}
Important points:
We SFINAE on the second parameter of func_dispatch_tag. The compiler looks at all the template specializations which result in the parameters <C, void>. Since any of the latter is "more specialized" when SF doesn't occur (i.e when std::enable_if_t is void), it gets chosen.
The chosen specialization of the trait defines a tag which we do a tag dispatch on. Tag dispatch depends on function overloading, instead of function template specialization (that cannot be partially specialized).
You can define a fallback function (like I did), or static_assert. The number of tags we can define is limited only by the range of an int, so extending to other members is just a matter of adding another func_dispatch_tag specialization.
The member must be accessible, or SF will occur. Also, a class that has both members will result in ambiguity. Bear that in mind.
Here's another way. There's a little more boilerplate, but in the actual expression of the different implementations of func() it could be argued that the 'list of tests that passed' is more expressive.
Food for thought anyway.
Code is c++11. c++14 and 17 would be more succinct.
#include <iostream>
#include <type_traits>
#include <tuple>
// boilerplate required prior to c++17
namespace notstd {
using namespace std;
template<typename... Ts> struct make_void { typedef void type;};
template<typename... Ts> using void_t = typename make_void<Ts...>::type;
}
// test for having member function one()
template<class T, class Enable = notstd::void_t<>> struct has_one : std::false_type {};
template<class T> struct has_one<T, notstd::void_t<decltype(std::declval<T>().one())>> : std::true_type {};
//test for having member function two()
template<class T, class Enable = notstd::void_t<>> struct has_two : std::false_type {};
template<class T> struct has_two<T, notstd::void_t<decltype(std::declval<T>().two())>> : std::true_type {};
// a type collection of tests that pass
template<template <class...> class...Tests> struct passes_tests {
};
// meta-function to append a type
template<class Existing, template <class...> class Additional> struct append_pass;
template< template <class...> class...Tests, template <class...> class Additional>
struct append_pass<passes_tests<Tests...>, Additional> {
using type = passes_tests<Tests..., Additional>;
};
//
// meta-functions to compute a list of types of test that pass
//
namespace detail
{
template<class Previous, class T, template<class...> class Test, template<class...> class...Rest>
struct which_tests_pass_impl
{
using on_pass = typename append_pass<Previous, Test>::type;
using on_fail = Previous;
using this_term = typename std::conditional< Test<T>::value, on_pass, on_fail >::type;
using type = typename which_tests_pass_impl<this_term, T, Rest...>::type;
};
template<class Previous, class T, template<class...> class Test>
struct which_tests_pass_impl<Previous, T, Test>
{
using on_pass = typename append_pass<Previous, Test>::type;
using on_fail = Previous;
using this_term = typename std::conditional< Test<T>::value, on_pass, on_fail >::type;
using type = this_term;
};
}
template<class Type, template<class...> class...Tests> struct which_tests_pass
{
using type = typename detail::which_tests_pass_impl<passes_tests<>, Type, Tests...>::type;
};
//
// various implementations of func()
//
namespace detail
{
template<class T>
void func(T t, passes_tests<has_one>)
{
t.one();
}
template<class T>
void func(T t, passes_tests<has_one, has_two>)
{
t.one();
}
template<class T>
void func(T t, passes_tests<has_two>)
{
t.two();
}
template<class T>
void func(T t, passes_tests<>)
{
// do nothing
}
}
template<class T>
void func(T t)
{
detail::func(t, typename which_tests_pass<T, has_one, has_two>::type());
}
//
// some types
//
struct One
{
void one(void) const
{
std::cout << "one" << std::endl;
}
};
struct Two
{
void two(void) const
{
std::cout << "two" << std::endl;
}
};
// test
int main(int argc, char* argv[])
{
func(One()); // should print "one"
func(Two()); // should print "two"
return 0;
}
The code below
handles member function constness correctly
is agnostic of the functions return types
prints a comprehensive error on failure
It could be even shorter with C++14, where you don't have to specify the return types of implemented functions and have templated variable declarations. If you want to handle rvalue overloads correctly, you need to provide another overload to as_memfun.
If testing for member functions alone is not enough, there is another approach in the last section, which offers far better customization options but is also lengthier to setup.
#include <utility>
#include <functional>
namespace detail {
template<typename T> struct _false : std::integral_constant<bool, false> { };
template<typename T> struct HasNone {
static_assert(_false<T>::value, "No valid method found");
};
template<typename T, typename R>
constexpr auto as_memfun (R (T::* arg) ())
-> R (T::*) ()
{ return arg; }
template<typename T, typename R>
constexpr auto as_memfun (R (T::* arg) () const)
-> R (T::*) () const
{ return arg; }
template<typename T> constexpr auto check_has_two(int)
-> decltype(as_memfun(&T::two))
{ return as_memfun(&T::two); }
template<typename T> constexpr auto check_has_two(...)
-> HasNone<T>;
template<typename T> constexpr auto check_has_one(int)
-> decltype(as_memfun(&T::one))
{ return as_memfun(&T::one); }
template<typename T> constexpr auto check_has_one(...)
-> decltype(check_has_two<T>(0))
{ return check_has_two<T>(0); }
template<typename T>
struct res { constexpr static auto detail = check_has_one<T>(0); };
}
template<typename T>
auto func(T t) -> decltype((t.*detail::res<T>::detail)()) {
return (t.*detail::res<T>::detail)();
}
And here are some test you would probably like to have
struct One {
void one();
};
struct Two {
void two();
};
struct TestBoth {
char one() const;
void two();
};
struct TestWilderStuff {
int one;
void two() const;
};
int main() {
func(One{});
func(Two{});
func(TestBoth{});
static_assert(decltype(func(TestBoth{})){} == 0, "Failed function selection");
func(TestWilderStuff{});
}
Since you seem to have more extensive constructions in mind than just testing for member function existence, here is the beginning of a vastly more powerful mechanism. You can use it as a drop-in replacement for the above solution and although it is far lengthier, it offers more customization and the possibility to do elaborate tests on your types in every step of the way.
#include <utility>
#include <functional>
namespace detail {
template<typename T> struct _false :
std::integral_constant<bool, false> { };
template<typename T> struct HasNone {
static_assert(_false<T>::value, "No valid method found");
};
// Generic meta templates used below
namespace Generics {
template<typename Getter, typename Else>
struct ChainGetter {
template<typename T> constexpr static auto get_(int)
-> decltype(Getter::template get<T>())
{ return Getter::template get<T>(); }
template<typename T> constexpr static auto get_(...)
-> decltype(Else::template get<T>())
{ return Else::template get<T>(); }
template<typename T> constexpr static auto get()
-> decltype(get_<T>(0))
{ return get_<T>(0); }
};
template<typename Getter, typename Test>
struct TestGetter {
template<typename T, typename R> using _type = R;
template<typename T> constexpr static auto get_()
-> decltype(Getter::template get<T>())
{ return Getter::template get<T>(); }
template<typename T> constexpr static auto test()
-> decltype(Test::template test<T>(get_<T>()));
template<typename T> constexpr static auto get()
-> _type<decltype(test<T>()),
decltype(get_<T>())
>
{ return get_<T>(); }
};
template<template<typename> class F>
struct FailGetter {
template<typename T>
constexpr static auto get() -> F<T>;
};
}
// Test only exists for member function pointer arguments
struct IsMemberFunctionTest {
template<typename _, typename T, typename R>
constexpr static void test (R (T::* arg) ());
template<typename _, typename T, typename R>
constexpr static void test (R (T::* arg) () const);
};
// Get member pointer to T::one
struct GetOne {
template<typename T>
constexpr static auto get() -> decltype(&T::one) { return &T::one; }
};
// Get member pointer to T::two
struct GetTwo {
template<typename T>
constexpr static auto get() -> decltype(&T::two) { return &T::two; }
};
using namespace Generics;
using getter_fail = FailGetter<HasNone>;
using get_two_tested = TestGetter<GetTwo, IsMemberFunctionTest>;
using getter_two = ChainGetter<get_two_tested, getter_fail>;
using get_one_tested = TestGetter<GetOne, IsMemberFunctionTest>;
using getter_one = ChainGetter<get_one_tested, getter_two>;
template<typename T>
struct result { constexpr static auto value = getter_one::template get<T>(); };
}
template<typename T>
auto func(T t) -> decltype((t.*detail::result<T>::value)()) {
return (t.*detail::result<T>::value)();
}
Consider the following code:
#include <iostream>
struct ActionOption {
virtual void foo(int) const = 0;
};
template <int> struct ActionType;
template <> struct ActionType<0> : ActionOption {
virtual void foo(int) const override {std::cout << "ActionType<0>::foo(int) called.\n";}
};
template <> struct ActionType<1> : ActionOption {
virtual void foo(int) const override {std::cout << "ActionType<1>::foo(int) called.\n";}
};
template <> struct ActionType<2> : ActionOption {
virtual void foo(int) const override {std::cout << "ActionType<2>::foo(int) called.\n";}
};
template <> struct ActionType<3> : ActionOption {
virtual void foo(int) const override {std::cout << "ActionType<3>::foo(int) called.\n";}
};
template <> struct ActionType<4> : ActionOption {
virtual void foo(int) const override {std::cout << "ActionType<4>::foo(int) called.\n";}
};
template <int...> struct PossibleActions;
template <> struct PossibleActions<> { void operator()(int) const {} };
template <int First, int... Rest>
struct PossibleActions<First, Rest...> : ActionType<First>, PossibleActions<Rest...> {
void operator()(int a) const {
ActionType<First>::foo(a);
PossibleActions<Rest...>::operator()(a);
}
};
// Anything that can call ActionType<2>::foo(int) can also call ActionType<3>::foo(int).
struct Object : PossibleActions<1, 2,3, 4> {
void foo(int a) {PossibleActions<1,2,3,4>()(a);}
};
struct Blob : PossibleActions<0, 2,3, 4> {
void foo(int a) {PossibleActions<0,2,3,4>()(a);}
};
int main() {
Object object;
object.foo(12); // ActionType<1>::foo(int) called ActionType<2>::foo(int) called ActionType<3>::foo(int) called ActionType<4>::foo(int) called
std::cout << std::endl;
Blob blob;
blob.foo(12); // ActionType<0>::foo(int) called ActionType<2>::foo(int) called ActionType<3>::foo(int) called ActionType<4>::foo(int) called
std::cout << std::endl;
}
It runs except here is the problem: anything that can call ActionType<2>::foo(int) can also call ActionType<3>::foo(int). Thus every time I define a new class, if I use 2 or 3 I have to use both in PossibleActions<I...>. This is problematic for maintenance of course (say I decide in the future that using 2 must also use 3, 7, and 20). The following solution:
using TwoAndThree = PossibleActions<2,3>;
struct Object : PossibleActions<1,4>, TwoAndThree {
void foo(int a) {PossibleActions<1,4>()(a); TwoAndThree()(a);}
};
struct Blob : PossibleActions<0,4>, TwoAndThree {
void foo(int a) {PossibleActions<0,4>()(a); TwoAndThree()(a);}
};
is not acceptable because I need ActionType<N>::foo(int) called in numerical order. Splitting PossibleActions<1,4>()(a); is a very is poor solution too because it runs into the same maintenaince problem (makes maintenance even worse I think).
template <> struct ActionType<2> : ActionOption { virtual void foo(int) const override {std::cout << "ActionType<2>::foo(int) called.\n";} };
template <> struct ActionType<3> : ActionType<2> { virtual void foo(int) const override {std::cout << "ActionType<3>::foo(int) called.\n";} };
does not compile due to ambiguity (and using virtual inheritance did not help), and I can't think of anything else. Is there a solution to this problem?
Perhaps redefine PossibleActions with template <typename... Args> struct PossibleActions;? But then the recursion is lost.
Or is it?
Related question: Is there a way to carry out recursion with Args... where some types are int but some are not (and with those that are not use recursion with the ints that define those types)? For example
PossibleActions<1, TwoAndThree, 4, EightAndTen, 20>()(a);
iterates through 1,2,3,4,8,10,20 as desired because TwoAndThree = PossibleActions<2,3> and EightAndTen = PossibleActions<8,10>??? If possible, that would solve the problem.
Credit goes to Piotr. S for this solution (I wish I could offer him points, but he likes to hide his amazingness for some reason). Though his second solution is nice too, I prefer the syntax offered by his first solution. His Sort struct had to be generalized with
template <typename, typename...> struct Sort;
template <typename T, typename A, typename B>
struct Sort<T,A,B> {
using type = typename Merge<T,A,B>::type;
};
template <typename T, typename First, typename Second, typename... Rest>
struct Sort<T, First, Second, Rest...> {
using type = typename Sort<T, typename Sort<T, First, Second>::type, Rest...>::type;
};
so I did that for him. This allows the syntax
struct Widget : Sort<PossibleActions<0,5>, OneAndFour, TwoAndThree>
which I like better. I added template-templates into the picture too:
#include <iostream>
namespace Detail {
template <typename T, typename, typename, T...> struct Merge;
template <typename T, template <T...> class S, T... Ks>
struct Merge<T, S<>, S<>, Ks...> {
using type = S<Ks...>;
};
template <typename T, template <T...> class S, T... Is, T... Ks>
struct Merge<T, S<Is...>, S<>, Ks...> {
using type = S<Ks..., Is...>;
};
template <typename T, template <T...> class S, T... Js, T... Ks>
struct Merge<T, S<>, S<Js...>, Ks...> {
using type = S<Ks..., Js...>;
};
template <typename T, bool, typename, typename, T...> struct Strip;
template <typename T, template <T...> class S, T I, T... Is, T J, T... Js, T... Ks>
struct Strip<T, true, S<I, Is...>, S<J, Js...>, Ks...> {
using type = Merge<T, S<I, Is...>, S<Js...>, Ks..., J>;
};
template <typename T, template <T...> class S, T I, T... Is, T J, T... Js, T... Ks>
struct Strip<T, false, S<I, Is...>, S<J, Js...>, Ks...> {
using type = Merge<T, S<Is...>, S<J, Js...>, Ks..., I>;
};
template <typename T, template <T...> class S, T I, T... Is, T J, T... Js, T... Ks>
struct Merge<T, S<I, Is...>, S<J, Js...>, Ks...> : Strip<T, (I > J), S<I, Is...>, S<J, Js...>, Ks...>::type {};
template <typename, typename...> struct Sort;
template <typename T, typename A, typename B>
struct Sort<T,A,B> {
using type = typename Merge<T,A,B>::type;
};
// Piotr S.'s Sort generalized to accept any number of template arguments.
template <typename T, typename First, typename Second, typename... Rest>
struct Sort<T, First, Second, Rest...> {
using type = typename Sort<T, typename Sort<T, First, Second>::type, Rest...>::type;
};
}
template <typename... P>
using Sort = typename Detail::Sort<int, P...>::type;
struct ActionOption {
virtual void foo(int) const = 0;
};
template <int> struct ActionType;
template <> struct ActionType<0> : ActionOption {
virtual void foo(int) const override {std::cout << "ActionType<0>::foo(int) called.\n";}
};
template <> struct ActionType<1> : ActionOption {
virtual void foo(int) const override {std::cout << "ActionType<1>::foo(int) called.\n";}
};
template <> struct ActionType<2> : ActionOption {
virtual void foo(int) const override {std::cout << "ActionType<2>::foo(int) called.\n";}
};
template <> struct ActionType<3> : ActionOption {
virtual void foo(int) const override {std::cout << "ActionType<3>::foo(int) called.\n";}
};
template <> struct ActionType<4> : ActionOption {
virtual void foo(int) const override {std::cout << "ActionType<4>::foo(int) called.\n";}
};
template <> struct ActionType<5> : ActionOption {
virtual void foo(int) const override {std::cout << "ActionType<5>::foo(int) called.\n";}
};
template <int...> struct PossibleActions;
template <> struct PossibleActions<> { void operator()(int) const {} };
template <int First, int... Rest>
struct PossibleActions<First, Rest...> : ActionType<First>, PossibleActions<Rest...> {
void operator()(int a) const {
ActionType<First>::foo(a);
PossibleActions<Rest...>::operator()(a);
}
};
using OneAndFour = PossibleActions<1,4>;
using TwoAndThree = PossibleActions<2,3>;
struct Thing : PossibleActions<0,1,2,3,4> {
void foo(int a) {PossibleActions<0,1,2,3,4>::operator()(a);}
};
struct Object : Sort<PossibleActions<1,4>, TwoAndThree> {
void foo(int a) {Sort<PossibleActions<1,4>, TwoAndThree>()(a);}
};
struct Blob : Sort<PossibleActions<0,4>, TwoAndThree> {
void foo(int a) {Sort<PossibleActions<0,4>, TwoAndThree>()(a);}
};
struct Widget : Sort<PossibleActions<0,5>, OneAndFour, TwoAndThree> {
void foo(int a) {Sort<PossibleActions<0,5>, OneAndFour, TwoAndThree>()(a);}
};
int main() {
Thing thing;
thing.foo(12); // ActionType<0>::foo(int) ActionType<1>::foo(int) called ActionType<2>::foo(int) called ActionType<3>::foo(int) called ActionType<4>::foo(int) called
std::cout << std::endl;
Object object;
object.foo(12); // ActionType<1>::foo(int) called ActionType<2>::foo(int) called ActionType<3>::foo(int) called ActionType<4>::foo(int) called
std::cout << std::endl;
Blob blob;
blob.foo(12); // ActionType<0>::foo(int) called ActionType<2>::foo(int) called ActionType<3>::foo(int) called ActionType<4>::foo(int) called
std::cout << std::endl;
Widget widget;
widget.foo(12); // ActionType<0>::foo(int) called ActionType<1>::foo(int) called ActionType<2>::foo(int) called ActionType<3>::foo(int) called ActionType<4>::foo(int) called ActionType<5>::foo(int) called
}
Note however, the solution actually fails if the original packs are themselves not sorted. This will probably require a helper sorter struct to use first on the original packs before carrying out the above.
I'm trying to write code that is effectively:
template <typename... Mixins>
class Checker : public Mixins... {
public:
template<typename... Args>
Checker(Args&&... args) : Mixins(std::forward<Args>(args))... { }
bool check(void) {
bool tests = true;
// TODO: for each Mixins that has Mixin::check
{
tests = tests && Mixin::check();
}
if (!tests) {
// TODO: for each Mixin that has Mixin::handle
{
Mixin::handle()
}
}
return tests
}
};
I just don't know how to do the loops over just those Mixins satisfying the member conditions specified. I've tried various solutions for iteration and for enable_if type semantics but nothing seems to compile.
Additional
So, what I've tried so far visa vi for-each base recursion and SFINAE function elimination is the following, which doesn't compile:
template <typename C, typename... Mx>
struct do_checks;
template <typename C>
struct do_checks<C> {
static bool check(C *) {
return true;
}
};
template <typename C, typename E, typename... Mx>
struct do_checks<C, E, Mx...> {
struct general_ { };
struct special_ : general_ { };
template <typename> struct int_ { typedef int type; };
template <typename F, typename int_<decltype(F::check)>::type = 0>
static bool check(C *ptr, special_) {
if (!ptr->F::check()) {
return false;
}
return do_checks<C, Mx...>::check(ptr);
}
template <typename F>
static bool check(C *ptr, general_) {
return do_checks<C, Mx...>::check(ptr);
}
};
Where F should be E. I don't know how to tell the compiler to compile the first "check" if the int_ has a type and the second if it doesn't.
You can create a trait named has_check which returns true for a type which has a static check() method.
namespace detail
{
template<class T, class = decltype(T::check())>
std::true_type has_check_impl(void*);
template<class T>
std::false_type has_check_impl(...);
}
template<class T>
struct has_check : decltype(detail::has_check_impl<T>(nullptr)) { };
Then you can use it as the predicate to std::enable_if to enable/disable the overload if it has the method:
private:
template<class T, class...>
using first = T;
template<class T = void,
class =
first<T,
typename std::enable_if<has_check<Mixins>::value, T>::type...>>
static void foo(void*)
{
auto x{ (0, Mixins::check(), 0)... };
(void)x;
}
static void foo(...)
{
auto x{ (0, Mixins::handle(), 0)... };
(void)x;
}
public:
static void check() {
foo(nullptr);
}