Search code examples
c++templatesc++17std-variant

Extract a c++ variant type from a list of type names


I am designing a pipeline class that needs to extract a std::variant from a list of distinct types in a filter class. For example:

template <typename T>
struct Filter {
  using type = T;
  // ...
}

template <typename... Filters>
struct Pipeline {
  
  // how to properly define the variant type?
  // std::variant<Filters::type...> buffer;

};

For example, if I have three different filter types:

Filter<int>, Filter<double>, Filter<std::string>

then, the variant should be std::variant<int, double, std::string>. However, I need to remove duplicate types in the variant, for example:

Filter<int>, Filter<double>, Filter<std::string>, Filter<int>

then, the variant should NOT be std::variant<int, double, std::string, int> but std::variant<int, double, std::string>. Additionally, I need to replace void with std::monostate. For example:

Filter<int>, Filter<double>, Filter<void>, Filter<int>, Filter<void>

then, the variant should be std::variant<int, double, std::monostate>.

How to design such a metaclass that can properly define the variant type based on the given type names template <typename... Filters> using c++17?


Solution

  • Based on Piotr's answer:

    #include <variant>
    
    template <typename T, typename... Ts>
    struct unique { using type = T; };
    
    template <typename... Ts, typename U, typename... Us>
    struct unique<std::variant<Ts...>, U, Us...>
      : std::conditional_t<(std::is_same_v<U, Ts> || ...), 
                            unique<std::variant<Ts...>, Us...>, 
                            unique<std::variant<Ts..., U>, Us...>> {};
    
    template <typename... Ts>
    using variant_t = typename unique<
      std::variant<>, 
      std::conditional_t<std::is_same_v<Ts, void>, std::monostate, Ts>...>::type;
    

    then your Pipeline can be defined as:

    template <typename T>
    struct Filter {
      using type = T;
    };
    
    template <typename... Filters>
    struct Pipeline {
      variant_t<typename Filters::type...> buffer;
    };
    

    Demo.