【问题标题】:Std::map holding anytype of valueStd::map 持有任何类型的值
【发布时间】:2021-09-25 00:05:47
【问题描述】:

我想用一个“通用”值映射一个键,我的意思是它可以是一个 int、float、char、string 等等。

我特别想这样做,因为我在模型中接收到转换后的 CAN 数据,并且可能的类型(当前)是 int、float 和 string。

第一个想法是创建一个可以以不同方式实现的抽象对象(每种设备类型一个),然后将其放入容器(地图或集合)中。

我认为使用“通用类型”作为值会更加轻便和高效,因此当数据来自 CAN 总线时,容器将自动构建而无需创建新的 ad-hoc 对象。

【问题讨论】:

  • 在 C++17 中:std::variant<int, float, std::string>。如果你仅限于 C++11,可以在 Boost 中找到类似的类。
  • stackoverflow.com/questions/35192561/…类似问题,可以用这个
  • 您要求擦除类型,这需要virtual somewhere
  • 甚至可以将所有类型转换为字符串并存储为映射键

标签: c++ c++11


【解决方案1】:

std::variant 是您的选择,如果您可以使用C++17。我问了与您的问题有关的question。下面我提供了如何实现它的示例:

#include <iostream>
#include <variant>
#include <assert.h>
#include <string_view>
#include <unordered_map>

enum class ValueType : uint8_t
{
    Undefined       = 0x00,
    Uint16,
    Uint32,
    AsciiString,
};

typedef uint64_t FieldId;

struct FieldInfo
{
    std::string _name;
    ValueType _type;
    uint8_t size;   //  in bytes
};

typedef std::unordered_map<FieldId, FieldInfo> FieldContainer;

static FieldContainer requestFields =
{
    { 0, { "user-id",   ValueType::Uint32, sizeof (uint32_t) } },
    { 1, { "group-id",  ValueType::Uint16, sizeof (uint16_t)} },
    { 2, { "user-name", ValueType::AsciiString, 0} },
};

std::variant<uint8_t, uint32_t, std::string_view> getValue(ValueType type,
                                                           const uint8_t* data,
                                                           size_t length)
{
    if (type == ValueType::Uint32)
    {
        assert(length == sizeof(uint32_t));
        return *reinterpret_cast<const uint32_t*>(data);
    }
    if (type == ValueType::Uint16)
    {
        assert(length == sizeof(uint16_t));
        return *reinterpret_cast<const uint16_t*>(data);
    }
    else if (type == ValueType::AsciiString)
    {
        return std::string_view(reinterpret_cast<const char*>(data), length);
    }

    return static_cast<uint8_t>(0);
}


int main(int argc, char *argv[])
{
    const uint8_t arr[] = {0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x53, 0x74, 0x61, 0x63, 0x6b, 0x4f, 0x76, 0x65, 0x72, 0x66, 0x6c, 0x6f, 0x77, 0x21};
    size_t length = sizeof(arr);

    const auto value0 = getValue(requestFields[0]._type, arr, requestFields[0].size);
    std::visit([](auto&& arg)
                    {
                        std::cout << "value-0: " << arg << std::endl;
                    }, value0);


    const auto value1 = getValue(requestFields[2]._type, arr, length);
    std::visit([](auto&& arg)
                    {
                        std::cout << "value-1: " << arg << std::endl;
                    }, value1);

    return 0;
}

输出:

value-0: 1819043144
value-1: Hello StackOverflow!

UPD.0:我没有在这里使用std::map,但我认为仅仅使用std::variant作为那个容器的值没什么大不了的。

【讨论】:

  • 这些reinterpret_casts 看起来像 UB。
  • 你的意思是data等于nullptr的情况吗?
  • 不,我的意思是这样的reinterpret_cast好像违反了this
猜你喜欢
  • 2014-09-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-30
  • 1970-01-01
  • 2012-04-06
  • 1970-01-01
  • 2018-11-08
相关资源
最近更新 更多