Same template class specialization for std::variant and boost::variant template types
Asked Answered
R

3

0

I want to create a class specialization that has the same implementation if it gets passed any std::variant or any boost::variant. I tried to play around with std::enable_if, std::disjunction and std::is_same but I couldn't make it compile. Here is a code sample to show what I want to achieve.

#include <variant>
#include <iostream>
#include <boost/variant.hpp>

template <typename T>
struct TypeChecker;

template <typename T>
struct TypeChecker<T>
{
    void operator()()
    {
        std::cout << "I am other type\n";
    }
}

template <typename ... Ts>  // I want to be able to capture Ts... in TypeChecker scope
struct TypeChecker<std::variant<Ts...> or boost::variant<Ts...>> // what to insert here?
{
    void operator()()
    {
        std::cout << "I am either std::variant or boost::variant\n";
    }
}

int main()
{
    TypeChecker<std::variant<int, float>>{}();
    TypeChecker<boost::variant<int, float>>{}();
    TypeChecker<int>{}();
}

Expected result:

I am either std::variant or boost::variant
I am either std::variant or boost::variant
I am other type
Roxanneroxburgh answered 8/7, 2022 at 11:38 Comment(3)
There is no or keyword/operator in C++Redolent
@JakobStark There is or keyword in C++ and it's synonymous to ||, but it cannot be used directly here without some metaprogramming.Elstan
@Elstan I actually did not know this and have never seen it before. Thanks for pointing out ;)Redolent
R
5

You can use a template template parameter e.g. like this

template <typename T>
struct TypeChecker {
    void operator()() {
        std::cout << "I am other type\n";
    }
};

template<typename ... Ts, template<typename...> typename V>
requires std::same_as<V<Ts...>, std::variant<Ts...>> ||
         std::same_as<V<Ts...>, boost::variant<Ts...>>
struct TypeChecker<V<Ts...>>
{
    void operator()()
    {
        std::cout << "I am either std::variant or boost::variant\n";
    }
};

Note that this uses C++20 constraints. If you can't use C++20 you can use std::enable_if instead like e.g. this:

template<typename ... Ts, template<typename...> typename V>
struct TypeChecker<V<Ts...>> : std::enable_if_t<
        std::is_same_v<V<Ts...>, std::variant<Ts...>> ||
        std::is_same_v<V<Ts...>, boost::variant<Ts...>>, std::true_type>
{
    void operator()()
    {
        std::cout << "I am either std::variant or boost::variant\n";
    }
};

You can see it live on godbolt.

Redolent answered 8/7, 2022 at 12:27 Comment(1)
I like this pre-c++20 solution, simple and not much additional code, thank you!Roxanneroxburgh
T
3

The idea is to create a 'dispatcher' class, i.e., add another level of indirection. While this still takes some code, you don't have to reimplement it for every function:

#include <variant>
#include <iostream>
#include <boost/variant.hpp>

template <typename T, typename... Ts>
struct TypeCheckerOther
{
    void operator()()
    {
        std::cout << "I am other type\n";
    }
};

template <typename T, typename... Ts>
struct TypeCheckerVariant
{
    void operator()()
    {
        std::cout << "I am either std::variant or boost::variant\n";
    }
};

template <typename T>
struct TypeCheckerImpl
{
    using type = TypeCheckerOther<T>;
};

template <typename... Ts>
struct TypeCheckerImpl<std::variant<Ts...>>
{
    using type = TypeCheckerVariant<std::variant<Ts...>, Ts...>;
};

template <typename... Ts>
struct TypeCheckerImpl<boost::variant<Ts...>>
{
    using type = TypeCheckerVariant<boost::variant<Ts...>, Ts...>;
};

template<typename T>
using TypeChecker = typename TypeCheckerImpl<T>::type;


int main()
{
    TypeChecker<std::variant<int, float>>{}();
    TypeChecker<boost::variant<int, float>>{}();
    TypeChecker<int>{}();
}
Tremayne answered 8/7, 2022 at 12:26 Comment(0)
H
2

std::eneble_if or std::same_as are cool, but in some cases old classic way looks better:

template <typename T>
struct is_any_variant : std::false_type {};

template <typename ...Ts>
struct is_any_variant<std::variant<Ts...>> : std::true_type {};

template <typename ...Ts>
struct is_any_variant<boost::variant<Ts...>> : std::true_type {};

template <typename T>
constexpr bool is_any_variant_v = is_any_variant<T>::value;

Live demo

Hemicycle answered 8/7, 2022 at 12:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.