Search code examples
c++dynamicvariant

std::get content from std::variant using an element from an integer array as targeted type in std::variant


I have a integer array to retrieve content in std::variant. But it fails compilation, error message No matching function to call 'get'. Can you explain why, and suggest working solution to achieve same purpose?

using my_type = std::variant<int, float, bool>;
constexpr int[] expected_types = {2,2,2,2};
 

bool validate(std::vector<my_type> input) {
  bool rst;
  if (input.size() != 4) {
    return false;
  }
  for (int i = 0; i < 4; i++) {
    rst = rst || std::get<my_type[i]>(input[i]);
  }
  return rst;
}

Here is more context. Class A, B, C both implements the interface bool foo(std::vector<my_type> input). But class A's input is in the format of {int, int, bool}, class B's input is in the format of {int, int, float}, class C's input is in the format of {bool,bool}. expected_types in each class saves its own types in input vector. Then std::get<expected_types[i]> is used to access element in the input vector. Can you suggest an clear and elegant pattern to fit my case?

Many thanks!


Solution

  • If I understand your validate function correctly, it's supposed to validate that the vector<variant<int, float, bool>> ...

    1. Has the correct length for the type A, B or C.
    2. Contains the variant types indicated by the variant indices inA::expected_types[], B::expected_types[] or C::expected_types[].

    It could then take the type it's testing the vector for (A, B or C) as a template parameter and use the variant::index() to check which type the individual variants holds in the vector.

    Combine it with a fold over && (since you want all to be true) and you'll get:

    template<class T>
    bool validate(const std::vector<my_type>& input) {
        if (input.size() != std::size(T::expected_types))  return false;
    
        return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
            // fold over && :
            return (... && (input[Is].index() == T::expected_types[Is]));
        }(std::make_index_sequence<std::size(T::expected_types)>());
    }
    

    If you really want to std::get the expected_types from the vector and use logical OR, that could be done in a similar way, but folding over ||:

    template <class T>
    bool get_bool(const std::vector<my_type>& input) {
        if (not validate<T>(input)) return false; // use the validation function above
    
        return [&]<std::size_t... Is>(std::index_sequence<Is...>) {
            // fold over ||
            return (... || (std::get<T::expected_types[Is]>(input[Is])));
        }(std::make_index_sequence<std::size(T::expected_types)>());
    }
    

    Demo