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();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With