Home > front end >  Get type contained in variant at run time in C ?
Get type contained in variant at run time in C ?

Time:11-10

In C , how do I print the type contained in a variant at run time?

My use case: passing a dictionary of values from Python to C using pybind11, and I want to print out the types that are received.

CodePudding user response:

You can get a generic solution with std::visit and some typename printing library, such as Boost.TypeIndex. An exemplary solution:

#include <iostream>
#include <type_traits>
#include <variant>

#include <boost/type_index.hpp>

int main()
{
  std::variant<char, bool, int, double> v;

  auto print_variant_type =
      [](auto&& value)
      {
          using T = std::decay_t<decltype(value)>;
          std::cout << boost::typeindex::type_id<T>().pretty_name() << std::endl;
      };

  v = 'a';
  std::visit(print_variant_type, v); // prints out "char"

  v = true;
  std::visit(print_variant_type, v); // prints out "bool"

  v = 1;
  std::visit(print_variant_type, v); // prints out "int"

  v = 1.0;
  std::visit(print_variant_type, v); // prints out "double"
}

Live demo: https://godbolt.org/z/Web5zeGof

The only drawback is that it can print "ugly" type names for library types that are type aliases (such as std::string). An alternative for a particular variant instance, possibly more suitable for you, may be using a mapping from a variant index to type names:

using variant_type = std::variant<char, bool, int, double, std::string>;

static const std::array variant_type_names =
  { "char", "bool", "int", "double", "std::string" };

void print_variant_type(const variant_type& v)
{
  assert(v.index() < variant_type_names.size());
  std::cout << variant_type_names[v.index()] << std::endl;
}

int main()
{
  variant_type v;

  v = 'a';
  print_variant_type(v); // prints out "char"

  v = true;
  print_variant_type(v); // prints out "bool"

  v = 1;
  print_variant_type(v); // prints out "int"

  v = 1.0;
  print_variant_type(v); // prints out "double"

  v = std::string("some string");
  print_variant_type(v); // prints out "std::string"
}

Live demo: https://godbolt.org/z/9na1qzEKs

CodePudding user response:

You could use function overloading like so:

#include <iostream>
#include <variant>

using VariantT = std::variant<int, float>;

namespace {
std::string name(const float& ) {
    return "float";
}

std::string name(const int& ) {
    return "int";
}

std::string variantName(const VariantT& v) {
    return std::visit(
       [](const auto &v) { return name(v); },
       v
    );
}

}

int main() {
    std::variant<int, float> v;

    v = 1;

    std::cout << variantName(v) << std::endl;

    v = 1.f;

    std::cout << variantName(v) << std::endl;
}

CodePudding user response:

Tested under MSVC 2022 and C 17. Should work on gcc and clang but untested.

#include <string>
#include <variant>
#include <type_traits>

/**
 * \brief Variant type to string.
 * \tparam T Variant type.
 * \param v Variant.
 * \return Variant type as a string.
 */
template<typename T>
std::string variant_type_string(T v)
{
    std::string result;
    if constexpr(std::is_constructible_v<T, int>) { // Avoids compile error if variant does not contain this type.
        if (std::holds_alternative<int>(v)) { // Runtime check of type that variant holds.
            result = "int";
        }
    }
    else if constexpr(std::is_constructible_v<T, std::string>) {
        if (std::holds_alternative<std::string>(v)) {
            result = "string";
        }
    }
    else if constexpr(std::is_constructible_v<T, bool>) {
        if (std::holds_alternative<bool>(v)) {
            result = "bool";
        }
    }
    else {
        result = "?";
    }
    return result;
}

To use:

std::variant<int, std::string> v { 42 };
std::cout << variant_type_string(v);
// Prints: int
  • Related