Home > front end >  std::variant having for possible type a ptr to an array of a given type. Can I store more informatio
std::variant having for possible type a ptr to an array of a given type. Can I store more informatio

Time:08-09

I am looking at using std::variant to store basic types such as float, int, float2, float2, bool2, bool4, etc. which is rather trivial, but I would also ideally like to construct variant objects holding a pointer to a typed array. I came up with this solution, which compiles and run without crashing (which doesn't mean a bug isn't in there)):

#include <variant>
#include <vector>
#include <iostream>
#include <algorithm>

struct float3
{
    float x, y, z;
    float3() : x(0), y(0), z(0) {}
    float3(const float& r) : x(r), y(r), z(r) {}
};

int main()
{
    typedef std::variant<float, float3, float3*> Variant;
    std::vector<float3> v(3);
    std::generate(v.begin(), v.end(), [n=0] () mutable { return   n; });

    Variant var(&v[0]);

    if (float3** ptr = std::get_if<float3*>(&var)) {
        std::cout << "this is a float3*: " << (*ptr)[0].x << std::endl;
    }
    else {
        std::cout << "didn't recognize type\n";
    }
    return 0;
};

However, by doing so, I lose some additional data that I'd like to store alongside this particular variant type such as the size of the array (in a number of bytes for example). Would it best to write my own custom Variant class and create a constructor like this:

class Variant 
{
public:
    enum Type
    {
        ...
        FLOAT,
        FLOAT2,
        FLOAT3,
        ...
    };
    Variant(std::shared_ptr<void*>& ptr, Type type, size_t size, size_t stride, ...) : 
        type(Type), 
        data(new Data(ptr, size, stride, ...)) 
    {}
    std::shared_ptr<Data> data;
};

With class Data

class Data
{
public:
   Data(const std::shared_ptr<void*>& ptr, size_t nb, size_t stride, ...) :
      ptr(ptr), nbytes(nb), stride(stride) ... {}
   std::shared_ptr<void*> ptr;
   size_t nbytes;
   size_t stride;
   ...
}

Or can I still somehow make it work with std::variant? Any suggestions would be greatly appreciated.

CodePudding user response:

This "pointer plus size" type already exists since C 20 and it's called std::span.

You can use std::variant<float, float3, std::span<float3>>

If you want an array of many different types, you could use std::variant<std::span<float3>, std::span<float2>, std::span<bool2>, etc>. Notice that you do have to write all the types in the variant (might want to make it a typedef), but you don't have to write special code for each type as you can use visit with a template:

std::variant<std::span<float3>, std::span<float2>, std::span<bool2>> myVariant = ........;

// print all the elements. auto parameter makes a lambda with a template function.
// a separate copy is compiled for each type.
std::visit(myVariant, [](auto& span) {
    for(auto& item : span)
        std::cout << item << std::endl;
});

std::span is for an array that someone else will delete - if you want the variant to delete its own memory, use std::vector

CodePudding user response:

You can actually store a lot of info in a type. For instance a range of integers using the dimensions of an array. You don't have to actually use the storage of an array and instead just declare a pointer to such an array.

Then just extract the dimension info. Here's an example of extracting 3 dimensions from a pointer to an array:

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

int main()
{
    typedef int(*pa)[2][3][4];
    int ia[2][3][4];
    pa a{&ia};
    std::variant<pa, float> v(&ia);
    std::cout << std::extent<std::remove_cvref_t<decltype(*std::get<0>(v))>,0>::value << "\n";
    std::cout << std::extent<std::remove_cvref_t<decltype(*std::get<0>(v))>, 1>::value << "\n";
    std::cout << std::extent<std::remove_cvref_t<decltype(*std::get<0>(v))>, 2>::value << "\n";
}

// outputs: 2,3,4
  • Related