确保 std::map 在编译时具有具体大小的优雅方法

问题描述

我试图确保 std::map 在编译时与 enum 类具有相同的大小。尽可能避免使用宏。

我尝试使用 static_assert,但阅读 Stack Overflow 后我得出的结论是它无法完成,因为 std::map 是在运行时“构造”的。或者至少这是我的理解。所以我得到这个“表达式必须是一个常量值”错误。

看代码一定比我拙劣的解释更清楚:

// event_types.h

enum class EventTypes {
  InitSuccessfull,KeyPressed,StartedCleanup,FinishedCleanup,Exit,count
};

static const std::map<EventTypes,std::string> kEventTypesNames = {
  { EventTypes::InitSuccessfull,"InitSuccessfull" },{ EventTypes::KeyPressed,"KeyPressed" },{ EventTypes::StartedCleanup,"StartedCleanup" },{ EventTypes::FinishedCleanup,"FinishedCleanup" },{ EventTypes::Exit,"Exit" }
};

// this doesn't work,"expression must have a constant value"(kEventTypesNames.size())
static_assert(kEventTypesNames.size() == static_cast<std::underlying_type<kuge::EventTypes>::type>(EventTypes::count));

// this neither works
const unsigned int map_size = kEventTypesNames.size();
static_assert(map_size == static_cast<std::underlying_type<kuge::EventTypes>::type>(EventTypes::count));

所以,我想要的是确保地图的大小与 enum 计数相同,所以我不会忘记在两个地方添加事件。

知道怎么做吗?或者,也许我应该考虑另一种(更好的)方法来让不需要地图的事件“字符串化”?

解决方法

使用辅助生成器,您可以:

std::map<EventTypes,std::string> MakeMap()
{ 
    constexpr std::pair<EventTypes,const char*> ini[]
    {
      { EventTypes::InitSuccessfull,"InitSuccessfull" },{ EventTypes::KeyPressed,"KeyPressed" },{ EventTypes::StartedCleanup,"StartedCleanup" },{ EventTypes::FinishedCleanup,"FinishedCleanup" },{ EventTypes::Exit,"Exit" }
    };
    static_assert(std::size_t(EventTypes::count) == std::size(ini));
    return {std::begin(ini),std::end(ini)};
}
static const std::map<EventTypes,std::string> kEventTypesNames = MakeMap();

Demo

,

您可以将数据存储在可以在编译时检查的数据类型中,例如数组。

static const std::map<EventTypes,std::string>::value_type kEventTypesNamesData[] = {
//                      Note "value_type",here ^^^^^^^^^^
  { EventTypes::InitSuccessfull,"Exit" }
};

// Compile-time size check
static_assert(end(kEventTypesNamesData)-begin(kEventTypesNamesData) == static_cast<std::underlying_type<EventTypes>::type>(EventTypes::count));

// Construct from data
static const std::map<EventTypes,std::string> kEventTypesNames( begin(kEventTypesNamesData),end(kEventTypesNamesData) );
,

如果您愿意走“非代码”路线,您可以创建一个小型代码生成器来生成代码。然后只需将生成的代码包含在您的程序中(可能使用 #include "generated_code.hpp" 或类似的东西)。

这是一个小的:

#include <string>
#include <iostream>
#include <sstream>

// This would be in your data file of enums   
std::string test = "InitSuccessful\n"
                    "KeyPressed\n"
                    "StartedCleanup\n"
                    "FinishedCleanup\n"
                    "AnotherNewEnum1\n"
                    "AnotherNewEnum2\n"
                    "AnotherNewEnum3\n"
                    "Exit";

int main() {
    std::istringstream strmIn(test);
    std::ostringstream enumOut;
    std::ostringstream mapOut;
    std::string line;
    enumOut << "enum class EventTypes {\n";
    mapOut << "static const std::map<EventTypes,std::string> EventTypesNames = {\n";

    while (std::getline(strmIn,line))
    {
        enumOut << "    " << line << ",\n";
        std::string mapStr = "    { EventTypes::" + line + ",\"" + line + "\" },";
        mapOut << mapStr << "\n";
    }
    enumOut << "    count\n};";
    mapOut << "};";

    // Output the generated source code
    std::cout << enumOut.str() << "\n\n\n";
    std::cout << mapOut.str() << "\n";
}

输出:

enum class EventTypes {
    InitSuccessful,KeyPressed,StartedCleanup,FinishedCleanup,AnotherNewEnum1,AnotherNewEnum2,AnotherNewEnum3,Exit,count
};


static const std::map<EventTypes,std::string> EventTypesNames = {
    { EventTypes::InitSuccessful,"InitSuccessful" },{ EventTypes::AnotherNewEnum1,"AnotherNewEnum1" },{ EventTypes::AnotherNewEnum2,"AnotherNewEnum2" },{ EventTypes::AnotherNewEnum3,"AnotherNewEnum3" },"Exit" },};

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...