如何一致地定义可同时作为enum、int和string使用的选项列表

How to consistently define list of options to use as enum, int and string at the same time?

本文关键字:string int 列表 选项 enum 定义 何一致      更新时间:2023-10-16

我提供了一个显示分辨率的例子,但问题更普遍。

假设我们有以下配置:640x480;1024 x768;1600 x900分辨率下玩。如果它们是分开定义的,它看起来像:

std::string RESTR[] = { "640x480", "1024x768", "1600x900" };
struct Enum { enum ERes { _640x480, _1024x768, _1600x900 }; };
uint RES[][2] = { { 640, 480 }, { 1024, 768 }, { 1600, 900 } };

维护所有的列表太容易出错。它应该以如下方式给出(或者至少是非常相似的方式):

Res(  640, 480 )
Res( 1024, 768 )
Res( 1600, 900 )
Resolution类似乎是一个很好的解决方案,它在需要时表现为字符串,在其他用例中返回值。但是在运行应用程序之前,它没有枚举。为了在编译时魔术的开发中利用解析枚举,需要定义预处理器或模板。如何解决这个问题?

对于这类问题是否有标准的最先进的方法?

仅使用c++工具有两种不同的方法来解决这类问题(它们都使用预处理器的宏):

  1. 基于可变宏的方法(您可以编写自己的代码或使用Boost)。预处理器库宏).
  2. 基于x -宏技术的方法

如果您使用c++ 03或更低版本,则第二种方法适用于您(如果您使用c++ 11或更高版本,或者您的c++编译器支持C99,则第一种方法可用)。下面是它的描述:

  • 创建一个包含文件,不包含保护(你可以给它一个不同于你通常的头文件扩展名,例如inc),名称为Resolution,内容如下:

    RESOLUTION(  640, 480 )
    RESOLUTION( 1024, 768 )
    RESOLUTION( 1600, 900 )
    
  • 按如下方式定义数组:

    #define TO_STRING_LITERAL_(S) #S
    #define TO_STRING_LITERAL(S) TO_STRING_LITERAL_(S)
    #define STRING_RESOLUTION_(X, Y) X ## x ## Y
    #define STRING_RESOLUTION(X, Y) TO_STRING_LITERAL(STRING_RESOLUTION_(X, Y)),
    #define ENUM_RESOLUTION(X, Y) _ ## X ## x ## Y,
    #define INT_RESOLUTION(X, Y) { X , Y },
    #define RESOLUTION(X, Y) STRING_RESOLUTION(X, Y)
    std::string RESTR[] =
    {
    #    include "Resolution.inc"
    };
    #undef RESOLUTION
    #define RESOLUTION(X, Y) ENUM_RESOLUTION(X, Y)
    struct Enum
    {
         enum ERes
         {
    #        include "Resolution.inc"
         };
    };
    #undef
    #define RESOLUTION(X, Y) INT_RESOLUTION(X, Y)
    uint RES[][2] =
    {
    #   include "Resolution.inc"
    };
    #undef
    

更新: @rici在评论中建议对STRING_RESOLUTION宏进行有趣的简化:

#define STRING_RESOLUTION(X, Y) #X "x" #Y

这是可能的,因为一系列字符串文字被c++编译器识别为一个文字:"string1" "string2" "string3" == "string1string2string3" .

如果您不习惯使用enum,您可以使用以下内容:

#define CONCAT2(A, B) A ## B
#define CONCAT(A, B) CONCAT2(A, B)
#define STR2(A) #A
#define STR(A) STR2(A)
#define DEFINE_RESOLUTION(ResClass, X, Y) 
   struct ResClass { 
      static const int val = CONCAT(0x, CONCAT(X, Y)); 
      static char const* toString() 
      { 
         return STR(CONCAT(X, CONCAT(x, Y))); 
      } 
      static int getX() { return (X); } 
      static int getY() { return (Y); } 
   };
DEFINE_RESOLUTION(Res1, 640, 480);
DEFINE_RESOLUTION(Res2, 1024, 768);
// Test the code
#include <iostream>    
int getint()
{
   return Res1::val;
   // return Res2::val;
}
int main()
{
   int v = getint();
   switch (v)
   {
      case Res1::val:
         std::cout << Res1::getX() << "n";
         std::cout << Res1::getY() << "n";
         std::cout << Res1::toString() << "n";
         break;
      case Res2::val:
         std::cout << Res2::getX() << "n";
         std::cout << Res2::getY() << "n";
         std::cout << Res2::toString() << "n";
         break;
      default:
         std::cout << "Got defaultn";
   }
   return 0;
}
输出:

640
480
640x480