Home > other >  Creating a vector of the type of std::any
Creating a vector of the type of std::any

Time:10-23

Consider the following example

#include <iostream>
#include <any>
#include <vector>
#include <map>
#include <typeinfo>

typedef enum TYPE{
    INT8=0,
    INT16=1,
    INT32=2
} TYPE;

int main()
{
    std::map<TYPE, std::any> myMap;
    
    myMap[TYPE::INT8] = (int8_t)0;
    myMap[TYPE::INT16] = (int16_t)0;
    myMap[TYPE::INT32] = (int32_t)0;

    std::vector<decltype(myMap[TYPE::INT8])> vec; 
}

I have a map in this example, going from some enum to std::any. I actually need a flexible data structure that can map from a specific type (enum TYPE in this case), to multiple data types (different types of int), hence the use of std::any.

Going ahead, I would like to ascertain the type of value given for the key and construct a vector with it. I tried the above code, and it runs into a compilation error because decltype will return std::any(correctly so).

I would want to extract the "true type" from the std::any and create that type of vector. How would I achieve that.

A small snippet of the compilation error is as follows -

/opt/compiler-explorer/gcc-12.2.0/include/c  /12.2.0/bits/new_allocator.h:63:26: error: forming pointer to reference type 'std::any&'
   63 |       typedef _Tp*       pointer;

/opt/compiler-explorer/gcc-12.2.0/include/c  /12.2.0/bits/new_allocator.h:112:7: error: forming pointer to reference type 'std::any&'
  112 |       allocate(size_type __n, const void* = static_cast<const void*>(0))

/opt/compiler-explorer/gcc-12.2.0/include/c  /12.2.0/bits/stl_vector.h:1293:7: error: 'void std::vector<_Tp, _Alloc>::push_back(value_type&&) [with _Tp = std::any&; _Alloc = std::allocator<std::any&>; value_type = std::any&]' cannot be overloaded with 'void std::vector<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = std::any&; _Alloc = std::allocator<std::any&>; value_type = std::any&]'
 1293 |       push_back(value_type&& __x)

TIA

CodePudding user response:

As suggested in the comments by @Ted Lyngmo, I think std::variant serves you better. Especially with C -20's templated lambdas, the std::visit function can work wonders with these to get around the awkwardness of dealing with type enums and the like.

Note that you can not get around the runtime type detection. In any case, here is an example of how it can work.

#include <cstdint>
#include <iostream>
#include <variant>
#include <vector>


using VariantScalar = std::variant<
  std::int8_t, std::int16_t, std::int32_t>;
using VariantVector = std::variant<
  std::vector<std::int8_t>,
  std::vector<std::int16_t>,
  std::vector<std::int32_t>>;


VariantVector fill_vector(VariantScalar scalar, std::size_t n)
{
  auto make_vector = [n]<class IntType>(IntType v) -> VariantVector {
    return std::vector<IntType>(n, v);
  };
  return std::visit(make_vector, scalar);
}
void print_vector(const VariantVector& vec)
{
  std::visit([]<class T>(const std::vector<T>& vec) {
      for(const T& s: vec)
          std::cout << s << ' ';
      std::cout << '\n';
  }, vec);
}

int main()
{
  VariantScalar s(std::int8_t(1));
  VariantVector vec = fill_vector(s, 5);
  print_vector(vec);
}

CodePudding user response:

Assuming you have the following enum definition:

enum class TYPE{
    INT8=0,
    INT16=1,
    INT32=2
};

Then you can define a helper:

template <TYPE>
struct my_type {}; // Base case


template <>
struct my_type<TYPE::INT8> {
  using type = int8_t;
};


template <>
struct my_type<TYPE::INT16> {
  using type = int16_t;
};


template <>
struct my_type<TYPE::INT32> {
  using type = int32_t;
};


template <TYPE t>
using my_type = typename my_type<t>::type;

That you can use for your vector

std::vector<my_type<TYPE::INT8>> vec; 
  • Related