c++ 根据枚举(kFloat,kUint32...)的值查找实际类型(float,uint32_t...)

yqkkidmi  于 2022-12-05  发布在  其他
关注(0)|答案(3)|浏览(140)

我正在从一个文件中阅读数据,数据的类型被存储为uint8_t,它指示我将要读取的数据的类型。这是与这些值的声明相对应的枚举。

enum DataType
{
    kInt8,
    kUint16,
    kInt16,
    kUint32,
    kInt32,
    kUint64,
    kInt64,
    kFloat16,
    kFloat32,
    kFloat64
};

然后,我得到一个函数,它读取存储在文件中的特定类型的值,如下所示:

template<typename T>
readData(T*& data, size_t numElements)
{
   ifs.read((char*)data, sizeof(T) * numElements);
}

uint8_t type;
ifs.read((char*)&type, 1);
uint32_t numElements;
ids.read((char*)&numElements, sizeof(uint32_t));
switch (type) {
    case kUint8:
        uint8_t* data = new uint8_t[numElements];
        readData<uint8_t>(data, numElements);
        // eventually delete mem
        ...
        break;
    case kUint16:
       uint16_t* data = new int16_t[numElements];
       readData<uint16_t>(data, numElements);
        // eventually delete mem
        ...
        break;
    case ... etc.
    default:
        break;
}

我刚刚介绍了两种情况,但最终,您需要对所有类型都这样做。这是大量的代码重复,所以我想做的是在给定枚举值的情况下找到值的实际类型。例如,如果枚举值是kUint32,则类型将是uint32_t,等等。如果我能够做到这一点,代码可以变得更加紧凑,类似于以下代码(伪代码):

DataType typeFromFile = kUint32;
ActualC++Type T = typeFromFile.getType();
readData<T>(data, numElements);

你能推荐什么技术来使它工作(或者你能推荐什么替代解决方案?)。

4nkexdtk

4nkexdtk1#

你试图获取一个运行时值,并将其Map到编译时类型。由于C++是一种编译时类型化语言,因此,在某些时候,你不得不做一些像switch/case语句这样的事情。这是因为每个选项都需要有自己的独立代码。
因此,我们的愿望是尽可能减少您实际执行的操作量。使用标准库工具执行此操作的最佳方法是使用variant
因此,您创建了一个variant<Ts>,使Ts类型为unique_ptr<T[]>,其中各个T是枚举中的类型序列 * 按顺序 *。这样,枚举索引与variant索引匹配。unique_ptr部分很重要,因为它将使variant为您销毁数组,而不必知道它存储的是哪种类型。
所以switch/case唯一需要做的事情就是创建数组。数组的处理可以在访问者中进行,访问者可以是一个模板as follows

variant_type data_array{};

    switch (type) {
        case kUint8:
            data_array = std::make_unique<std::uint8_t[]>(numElements);
        case kUint16:
            data_array = std::make_unique<std::uint16_t[]>(numElements);
        default:
        //error out. `break` is not an option.
          break;
    }

    std::visit([&](auto &arr)
    {
        using arrtype = std::remove_cvref_t<decltype(arr)>;
        readData<typename arrtype::element_type>(arr.get(), numElements);
       ///...
    }, data_array);

//data_array's destructor will destroy the allocated arrays.
oxcyiej7

oxcyiej72#

我将使用higher-order macro来处理这类事情。首先使用一个宏来定义枚举值和类型之间的Map:

#define FOREACH_DATATYPE(OP)                    \
  OP(kUint8, uint8_t)                           \
  OP(kInt8, int8_t)                             \
  OP(kUint16, uint16_t)                         \
  OP(kInt16, int16_t)

  // Fill in the rest yourself

然后使用它为所有枚举值和类型生成switch语句:

switch (type) {
#define CASE_TYPE(name, type)                   \
    case name: {                                \
      type* data = new type[numElements];       \
      readData<type>(data, numElements);        \
      break;}
    FOREACH_DATATYPE(CASE_TYPE)
#undef CASE_TYPE
  default:
      break;
  }
57hvy0tb

57hvy0tb3#

除了其他答案之外,另一种方法是首先定义一个编译时类型列表,然后根据类型分配枚举值(这可以防止列表与枚举顺序问题),并使用id<type>调用lambda:

#include <iostream>
#include <type_traits>

template<typename T>
struct id {
    using type = T;
};

template<typename... Ts>
struct list
{
    static constexpr size_t size = sizeof...(Ts);
    
    template<typename T>
    static constexpr size_t index_of = -1;
};

template<typename H, typename... Ts>
struct list<H, Ts...>
{
public:
    static constexpr size_t size = sizeof...(Ts) + 1;

    template<typename T>
    static constexpr size_t index_of =
        std::is_same_v<H, T> ? 0 : list<Ts...>::template index_of<T> + 1;

    template<typename F>
    static void visit_nth(size_t n, F f)
    {
        if constexpr (size > 0) {
            if (n > 0) {
                if constexpr (size > 1) {
                    list<Ts...>::visit_nth(n - 1, std::move(f));
                }
            } else {
                f(id<H>());
            }
        }
    }
};

using DataTypes = list<int8_t, uint8_t, int16_t, uint16_t>;

enum DataType
{
    kInt8   = DataTypes::index_of<int8_t>,
    kUint8  = DataTypes::index_of<uint8_t>,
    kInt16  = DataTypes::index_of<int16_t>,
    kUint16 = DataTypes::index_of<uint16_t>
};


int main()
{
    DataType typeFromFile = kUint8;
    DataTypes::visit_nth(typeFromFile, [&](auto type) {
        using T = typename decltype(type)::type;
        std::cout << std::is_same_v<T, uint8_t> << std::endl;
    });
}

相关问题