简体   繁体   中英

Variadic template class iterate over types

I am attempting to create a variadic template class with a method that requires iterating over the template arguments and calling an unrelated template function with each type.

The idea is for this templated class to be extended with specific template parameters in order to encapsulate the base behavior.

A simple example of the general idea would be this (terrible) Checker class:

class Arg {
    Arg() {};
    virtual ~Arg() = default;
};

class A : public Arg{ /* ... */ };
class B : public Arg{ /* ... */ };
class C : public Arg{ /* ... */ };

template <typename ... SubClasses>
class Checker {
public:

    Checker() {};
    virtual ~Checker() = default;

    bool isOneOf(Arg* base) const;
};


template <typename ... SubClasses>
bool Checker<SubClasses>::isOneOf(Arg* arg) const
{
    // NOTE: I don't know how I would iterate over the class types....
    for (auto SubClass : SubClasses...) { // <-- not valid code
        if (std::dynamic_cast<SubClass>(arg)) {
            return true;
        }
    }
    return false;
}

class Whatever : public Checker<A, B> {};

auto whatever = new Whatever();
auto a = new A();
auto b = new B();
auto c = new C();

whatever->isOneOf(a); // true
whatever->isOneOf(b); // true
whatever->isOneOf(c); // false

In order for isOneOf to work, it needs to be able to iterate over the template arguments

I'm using a C++14 compiler and am not able to use boost .

I suppose that a possible C++14 solution (but also C++11) is

template <typename ... SubClasses>
bool Checker<SubClasses...>::isOneOf (Arg * arg) const
 {
   using unused = bool[];

   bool ret { false };

   (void)unused { false, ret |= (nullptr != dynamic_cast<SubClasses *>(arg))... };

   return ret;
 }

Unfortunately isn't shot circuiting (that is: all nullptr != dynamic_cast tests are evaluated (if the compiler doesn't optimize it) also when the first one is true).

If you can use C++17, with template folding all is simpler

template <typename ... SubClasses>
bool Checker<SubClasses...>::isOneOf (Arg * arg) const
 { return (... || (nullptr != dynamic_cast<SubClasses *>(arg)) ); }
template<class T>struct tag_t{using type=T;};
template<class Tag>using type_t=typename Tag::type;

template<class F>
void foreach_arg(F&&f){
  return [f=std::forward<F>(f)](auto&&...args){
    int discard[]={0,(void(
      f(decltype(args)(args)
    ),0)...};
   (void)discard;
  };
}

now:

bool isOneOf(Arg* base) const{
  bool r=false;
  foreach_arg( [&](auto tag){ r = r || dynamic_cast<type_t<decltype(tag)>>( base ); } )
  ( tag_t<SubClasses>{}... );
  return r;
}

With C++17, it should be possible to tinker with fold expressions and get this done in one shot. But, with C++14, probably the most readable, if not the most terse, approach is to use a helper template class, and some specialization...

#include <iostream>

class Arg {

public:
    Arg() {};
    virtual ~Arg() = default;
};

template<typename ...Args> struct is_one_of_helper;

template<>
struct is_one_of_helper<> {

    static bool isOneOf(Arg *arg)
    {
        return false;
    }
};

template<typename A, typename ...Args>
struct is_one_of_helper<A, Args...> {

    static bool isOneOf(Arg *arg)
    {
        if (dynamic_cast<A *>(arg))
            return true;

        return is_one_of_helper<Args...>::isOneOf(arg);
    }
};

template <typename ... SubClasses>
class Checker {
public:

    Checker() {};
    virtual ~Checker() = default;

    bool isOneOf(Arg* base) const
    {
        return is_one_of_helper<SubClasses...>::isOneOf(base);
    }
};

class A : public Arg{ /* ... */ };
class B : public Arg{ /* ... */ };
class C : public Arg{ /* ... */ };

int main()
{
    Arg *a=new A;
    Arg *b=new B;
    Arg *c=new C;

    Checker<B, C> checker;

    std::cout << checker.isOneOf(a)
          << std::endl;

    std::cout << checker.isOneOf(b)
          << std::endl;

    std::cout << checker.isOneOf(c)
          << std::endl;
}

Resulting output:

0
1
1

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM