2

boost::variant has member types which is some kind of boost::mpl structure. Is there a way to get an index of type in that structure at compile time, so late in in runtime i could do

if(myVariantInstance.which() == typeIndex)
{
   /*...*/
}

Instead of

if(myVariantInstance.type() == typeid(ConcreteType))
{
  /*...*/
}
3
  • Possible duplicate of boost::mpl::vector - getting to a type's base-offset
    – cha5on
    Commented Jun 8, 2016 at 2:16
  • @cha5on, nope. I spent some time looking for solution myself and already seen that question. With mpl::vector it works like a charm, but with variant's types it fails with missing pos member for type iterator. Consider this (pastebin.com/Hd01nJQy) snippet, second static_assert fails.
    – K117
    Commented Jun 8, 2016 at 4:49
  • I see what you mean, sorry for the confusion.
    – cha5on
    Commented Jun 9, 2016 at 23:18

3 Answers 3

3

I found solution for getting index of type in boost::variant without boost::mpl if you are interested in.

#include <iostream>
#include <type_traits>

#include <boost/variant/variant.hpp>

using myvariant = boost::variant<int, bool, double, int>;

template <typename T, typename ... Ts>
struct type_index;

template <typename T, typename ... Ts>
struct type_index<T, T, Ts ...>
    : std::integral_constant<std::size_t, 0>
{};

template <typename T, typename U, typename ... Ts>
struct type_index<T, U, Ts ...>
    : std::integral_constant<std::size_t, 1 + type_index<T, Ts...>::value>
{};


template <typename T, typename ... Ts>
struct variant_first_same_type_idx;

template <typename T, typename Head, typename ... Tail>
struct variant_first_same_type_idx<T, boost::variant<Head, Tail ... >>
    : type_index<T, Head, Tail ...>
{};

int main()
{
    std::cout << variant_first_same_type_idx<int, myvariant>::value << std::endl;
    std::cout << variant_first_same_type_idx<bool, myvariant>::value << std::endl;
    std::cout << variant_first_same_type_idx<double, myvariant>::value << std::endl;
}

The output of this program is:

0
1
2

UPDATE FOR C++17:

#include <iostream>
#include <stdexcept>
#include <type_traits>

#include <boost/variant/variant.hpp>

using myvariant = boost::variant<int, bool, double, int>;

// std::type_identity in C++20
template <typename T>
struct type_identity
{
    using type = T;
};

template <typename T, typename... Ts>
constexpr std::size_t get_same_type_idx(type_identity<boost::variant<Ts...>>)
{
    const bool is_same[]{std::is_same_v<Ts, T>...};
    // std::find in C++20
    for (std::size_t i = 0; i < sizeof...(Ts); ++i)
    {
        if (is_same[i])
        {
            return i;
        }
    }
    throw std::out_of_range("Type not found");
}

template <typename T, typename V>
constexpr std::size_t variant_first_same_type_idx = get_same_type_idx<T>(type_identity<V>{});

int main()
{
    std::cout << variant_first_same_type_idx<int, myvariant> << std::endl;
    std::cout << variant_first_same_type_idx<bool, myvariant> << std::endl;
    std::cout << variant_first_same_type_idx<double, myvariant> << std::endl;
}
1

It's a bit convoluted, and there might be a better way, but you could use boost::mpl::copy. Here's something that ought to work, based off of the example from your comment:

#include <boost/variant.hpp>
#include <boost/mpl/copy.hpp>
#include <boost/mpl/find.hpp>
#include <boost/mpl/vector.hpp>

typedef boost::mpl::vector<int, long, char> MyMplVector;
typedef boost::mpl::find<MyMplVector, long>::type MyMplVectorIter;
static_assert(MyMplVectorIter::pos::value == 1, "Error");

typedef boost::variant<int, long, char> MyVariant;
typedef boost::mpl::vector<> EmptyVector;
typedef boost::mpl::copy<
  MyVariant::types,
  boost::mpl::back_inserter<EmptyVector>>::type ConcatType;

typedef boost::mpl::find<ConcatType, long>::type MyVariantTypesIter;
static_assert(MyVariantTypesIter::pos::value == 1, "Error");
1
  • Thanks. It might be useful later. For now i implemented is static_visitor like this: pastebin.com/Pfn44GFf
    – K117
    Commented Jun 10, 2016 at 11:28
1
#include <boost/mpl/index_of.hpp>    
#include <iostream>

typedef boost::variant<int, std::string> VARIANT;
std::ostream &operator<<(std::ostream &_rS, const VARIANT&_r)
{    switch (_r.which())
     {    default:
             return _rS << "what the *";
          case boost::mpl::index_of<VARIANT::types, int>::type::value:
             return _rS << boost::get<int>(_r);
          case boost::mpl::index_of<VARIANT::types, std::string>::type::value:
             return _rS << boost::get<std::string>(_r);
     }
}

PS. I was always curious about people using the visitor access pattern...

PPS. I know that one does not need to implement an output operator as boost::variant already provides one -- just for explanation purposes...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.