需求
假设我有一种枚举类型:
enum fruit_type
{
apple,
banana,
orange,
};
我时常会遇到需要将枚举值转换为字符串的情况,即我需要如下的函数:
const char* GetString_fruit(fruit_type fruit)
当我调用GetString_fruit(apple)
时期望能得到字符串"apple"
。
然而它该怎么实现呢?
关键在于:原始的C++没有“反射系统”(或者说“类型系统”),枚举值在代码中的字符串,是无法知道的。
制作一个自己的C++反射系统是比较复杂的(虚幻4引擎有实现)。
而另一方面,“枚举值转换为字符串”这样的逻辑其实也可以用不太“优雅”的方式手动表示出来,例如:
const char* GetString_fruit(fruit_type fruit)
{
if (fruit == fruit_type::apple)
return "apple";
else if (fruit == fruit_type::banana)
return "banana";
else if (fruit == fruit_type::orange)
return "orange";
else
return "";
}
但这样的缺点也很明显:
- 字符串是手动打出的,有可能出错。
- 虽然设计一些宏可以让代码少一些,但我想来想去,不管怎样都需要一个“时间复杂度”为
N
的劳动量,即枚举的值越多,劳动量越大。
因此,对于那些枚举的值较多的枚举类型,我想制作一个小工具来取代手工,自动生成这样一个函数的代码。
基本思路
我选择C#
作为语言,理由如下:
- C#具有反射机制
- 在枚举方面,C#的语法和C++语法一样,这意味着我直接将C++中枚举的定义拷贝过来,不做任何处理,就是一个C#的枚举类型。
小工具的使用流程如下:
- 将C++的枚举定义拷贝到C#代码中。
- 运行C#程序,自动生成“将特定枚举值转换成字符串的C++函数”的代码
- 将生成的结果拷贝到C++中,即可使用。
小工具实现
创建一个控制台应用(.Net Framework)
C#代码如下(以“fruit_type”为例):
using System;
using System.Text;
//要为其生成函数的枚举类型(拷贝自C++代码):
enum fruit_type
{
apple,
banana,
orange,
};
namespace AutoGenFuncForCppEnum
{
class Program
{
static void Main(string[] args)
{
//指定为其生成函数的枚举类型
Type TheEnumType = typeof(fruit_type);
//函数的代码
string[] FunctionCode = GenerateFunctionCode(TheEnumType);
//将函数的代码保存到文件中
System.IO.File.WriteAllLines("GetEnumString_"+ TheEnumType.Name+".txt", FunctionCode, Encoding.UTF8);
//将每一行都输出,查看结果
foreach (string line in FunctionCode)
Console.WriteLine(line);
Console.ReadLine();//没有特别的意义,只是想让程序等待一下
}
//为特定枚举类型生成函数代码
static string[] GenerateFunctionCode(Type TheEnumType)
{
//获得所有枚举值的字符串
string[] EnumNames = Enum.GetNames(TheEnumType);
//结果字符串数组(长度是枚举值数目+3,一个是函数头,一个是else return"";,一个是})
string[] result = new string[EnumNames.Length + 3];
//函数头:
result[0] = "const char* GetEnumString_" + TheEnumType.Name + "(" + TheEnumType.Name + " value){";
//添加每一个值的分支
for (int i = 0; i < EnumNames.Length; i++)
{
string enum_name = EnumNames[i];
result[i + 1] = " " + ((i > 0) ? "else " : "") + "if (value == " + TheEnumType.Name + "::" + enum_name + ")return \"" + enum_name + "\";";
}
//最后
result[EnumNames.Length + 1] = " else return \"\";";
result[EnumNames.Length + 2] = "}";
return result;
}
}
}
生成的结果是:
const char* GetEnumString_fruit_type(fruit_type value){
if (value == fruit_type::apple)return "apple";
else if (value == fruit_type::banana)return "banana";
else if (value == fruit_type::orange)return "orange";
else return "";
}
当为新的枚举类型生成代码时,需要做如下操作:
- 将开始的枚举类型(“fruit_type”)替换成新的枚举类型,例如
enum_x
- 在Main中指定新的枚举类型,即
Type TheEnumType = typeof(enum_x);
测试一个更复杂的枚举类型
using System;
using System.Text;
//要为其生成函数的枚举类型(拷贝自C++代码):
enum VkResult
{
VK_SUCCESS = 0,
VK_NOT_READY = 1,
VK_TIMEOUT = 2,
VK_EVENT_SET = 3,
VK_EVENT_RESET = 4,
VK_INCOMPLETE = 5,
VK_ERROR_OUT_OF_HOST_MEMORY = -1,
VK_ERROR_OUT_OF_DEVICE_MEMORY = -2,
VK_ERROR_INITIALIZATION_FAILED = -3,
VK_ERROR_DEVICE_LOST = -4,
VK_ERROR_MEMORY_MAP_FAILED = -5,
VK_ERROR_LAYER_NOT_PRESENT = -6,
VK_ERROR_EXTENSION_NOT_PRESENT = -7,
VK_ERROR_FEATURE_NOT_PRESENT = -8,
VK_ERROR_INCOMPATIBLE_DRIVER = -9,
VK_ERROR_TOO_MANY_OBJECTS = -10,
VK_ERROR_FORMAT_NOT_SUPPORTED = -11,
VK_ERROR_FRAGMENTED_POOL = -12,
VK_ERROR_UNKNOWN = -13,
VK_ERROR_OUT_OF_POOL_MEMORY = -1000069000,
VK_ERROR_INVALID_EXTERNAL_HANDLE = -1000072003,
VK_ERROR_FRAGMENTATION = -1000161000,
VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS = -1000257000,
VK_ERROR_SURFACE_LOST_KHR = -1000000000,
VK_ERROR_NATIVE_WINDOW_IN_USE_KHR = -1000000001,
VK_SUBOPTIMAL_KHR = 1000001003,
VK_ERROR_OUT_OF_DATE_KHR = -1000001004,
VK_ERROR_INCOMPATIBLE_DISPLAY_KHR = -1000003001,
VK_ERROR_VALIDATION_FAILED_EXT = -1000011001,
VK_ERROR_INVALID_SHADER_NV = -1000012000,
VK_ERROR_INCOMPATIBLE_VERSION_KHR = -1000150000,
VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT = -1000158000,
VK_ERROR_NOT_PERMITTED_EXT = -1000174001,
VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT = -1000255000,
VK_THREAD_IDLE_KHR = 1000268000,
VK_THREAD_DONE_KHR = 1000268001,
VK_OPERATION_DEFERRED_KHR = 1000268002,
VK_OPERATION_NOT_DEFERRED_KHR = 1000268003,
VK_PIPELINE_COMPILE_REQUIRED_EXT = 1000297000,
VK_ERROR_OUT_OF_POOL_MEMORY_KHR = VK_ERROR_OUT_OF_POOL_MEMORY,
VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR = VK_ERROR_INVALID_EXTERNAL_HANDLE,
VK_ERROR_FRAGMENTATION_EXT = VK_ERROR_FRAGMENTATION,
VK_ERROR_INVALID_DEVICE_ADDRESS_EXT = VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS,
VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS_KHR = VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS,
VK_ERROR_PIPELINE_COMPILE_REQUIRED_EXT = VK_PIPELINE_COMPILE_REQUIRED_EXT,
VK_RESULT_MAX_ENUM = 0x7FFFFFFF
}
namespace AutoGenFuncForCppEnum
{
class Program
{
static void Main(string[] args)
{
//指定为其生成函数的枚举类型
Type TheEnumType = typeof(VkResult);
//函数的代码
string[] FunctionCode = GenerateFunctionCode(TheEnumType);
//将函数的代码保存到文件中
System.IO.File.WriteAllLines("GetEnumString_"+ TheEnumType.Name+".txt", FunctionCode, Encoding.UTF8);
//将每一行都输出,查看结果
foreach (string line in FunctionCode)
Console.WriteLine(line);
Console.ReadLine();//没有特别的意义,只是想让程序等待一下
}
//为特定枚举类型生成函数代码
static string[] GenerateFunctionCode(Type TheEnumType)
{
//获得所有枚举值的字符串
string[] EnumNames = Enum.GetNames(TheEnumType);
//结果字符串数组(长度是枚举值数目+3,一个是函数头,一个是else return"";,一个是})
string[] result = new string[EnumNames.Length + 3];
//函数头:
result[0] = "const char* GetEnumString_" + TheEnumType.Name + "(" + TheEnumType.Name + " value){";
//添加每一个值的分支
for (int i = 0; i < EnumNames.Length; i++)
{
string enum_name = EnumNames[i];
result[i + 1] = " " + ((i > 0) ? "else " : "") + "if (value == " + TheEnumType.Name + "::" + enum_name + ")return \"" + enum_name + "\";";
}
//最后
result[EnumNames.Length + 1] = " else return \"\";";
result[EnumNames.Length + 2] = "}";
return result;
}
}
}
生成结果:
const char* GetEnumString_VkResult(VkResult value) {
if (value == VkResult::VK_SUCCESS)return "VK_SUCCESS";
else if (value == VkResult::VK_NOT_READY)return "VK_NOT_READY";
else if (value == VkResult::VK_TIMEOUT)return "VK_TIMEOUT";
else if (value == VkResult::VK_EVENT_SET)return "VK_EVENT_SET";
else if (value == VkResult::VK_EVENT_RESET)return "VK_EVENT_RESET";
else if (value == VkResult::VK_INCOMPLETE)return "VK_INCOMPLETE";
else if (value == VkResult::VK_SUBOPTIMAL_KHR)return "VK_SUBOPTIMAL_KHR";
else if (value == VkResult::VK_THREAD_IDLE_KHR)return "VK_THREAD_IDLE_KHR";
else if (value == VkResult::VK_THREAD_DONE_KHR)return "VK_THREAD_DONE_KHR";
else if (value == VkResult::VK_OPERATION_DEFERRED_KHR)return "VK_OPERATION_DEFERRED_KHR";
else if (value == VkResult::VK_OPERATION_NOT_DEFERRED_KHR)return "VK_OPERATION_NOT_DEFERRED_KHR";
else if (value == VkResult::VK_PIPELINE_COMPILE_REQUIRED_EXT)return "VK_PIPELINE_COMPILE_REQUIRED_EXT";
else if (value == VkResult::VK_ERROR_PIPELINE_COMPILE_REQUIRED_EXT)return "VK_ERROR_PIPELINE_COMPILE_REQUIRED_EXT";
else if (value == VkResult::VK_RESULT_MAX_ENUM)return "VK_RESULT_MAX_ENUM";
else if (value == VkResult::VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS)return "VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS";
else if (value == VkResult::VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS_KHR)return "VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS_KHR";
else if (value == VkResult::VK_ERROR_INVALID_DEVICE_ADDRESS_EXT)return "VK_ERROR_INVALID_DEVICE_ADDRESS_EXT";
else if (value == VkResult::VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT)return "VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT";
else if (value == VkResult::VK_ERROR_NOT_PERMITTED_EXT)return "VK_ERROR_NOT_PERMITTED_EXT";
else if (value == VkResult::VK_ERROR_FRAGMENTATION_EXT)return "VK_ERROR_FRAGMENTATION_EXT";
else if (value == VkResult::VK_ERROR_FRAGMENTATION)return "VK_ERROR_FRAGMENTATION";
else if (value == VkResult::VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT)return "VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT";
else if (value == VkResult::VK_ERROR_INCOMPATIBLE_VERSION_KHR)return "VK_ERROR_INCOMPATIBLE_VERSION_KHR";
else if (value == VkResult::VK_ERROR_INVALID_EXTERNAL_HANDLE)return "VK_ERROR_INVALID_EXTERNAL_HANDLE";
else if (value == VkResult::VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR)return "VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR";
else if (value == VkResult::VK_ERROR_OUT_OF_POOL_MEMORY)return "VK_ERROR_OUT_OF_POOL_MEMORY";
else if (value == VkResult::VK_ERROR_OUT_OF_POOL_MEMORY_KHR)return "VK_ERROR_OUT_OF_POOL_MEMORY_KHR";
else if (value == VkResult::VK_ERROR_INVALID_SHADER_NV)return "VK_ERROR_INVALID_SHADER_NV";
else if (value == VkResult::VK_ERROR_VALIDATION_FAILED_EXT)return "VK_ERROR_VALIDATION_FAILED_EXT";
else if (value == VkResult::VK_ERROR_INCOMPATIBLE_DISPLAY_KHR)return "VK_ERROR_INCOMPATIBLE_DISPLAY_KHR";
else if (value == VkResult::VK_ERROR_OUT_OF_DATE_KHR)return "VK_ERROR_OUT_OF_DATE_KHR";
else if (value == VkResult::VK_ERROR_NATIVE_WINDOW_IN_USE_KHR)return "VK_ERROR_NATIVE_WINDOW_IN_USE_KHR";
else if (value == VkResult::VK_ERROR_SURFACE_LOST_KHR)return "VK_ERROR_SURFACE_LOST_KHR";
else if (value == VkResult::VK_ERROR_UNKNOWN)return "VK_ERROR_UNKNOWN";
else if (value == VkResult::VK_ERROR_FRAGMENTED_POOL)return "VK_ERROR_FRAGMENTED_POOL";
else if (value == VkResult::VK_ERROR_FORMAT_NOT_SUPPORTED)return "VK_ERROR_FORMAT_NOT_SUPPORTED";
else if (value == VkResult::VK_ERROR_TOO_MANY_OBJECTS)return "VK_ERROR_TOO_MANY_OBJECTS";
else if (value == VkResult::VK_ERROR_INCOMPATIBLE_DRIVER)return "VK_ERROR_INCOMPATIBLE_DRIVER";
else if (value == VkResult::VK_ERROR_FEATURE_NOT_PRESENT)return "VK_ERROR_FEATURE_NOT_PRESENT";
else if (value == VkResult::VK_ERROR_EXTENSION_NOT_PRESENT)return "VK_ERROR_EXTENSION_NOT_PRESENT";
else if (value == VkResult::VK_ERROR_LAYER_NOT_PRESENT)return "VK_ERROR_LAYER_NOT_PRESENT";
else if (value == VkResult::VK_ERROR_MEMORY_MAP_FAILED)return "VK_ERROR_MEMORY_MAP_FAILED";
else if (value == VkResult::VK_ERROR_DEVICE_LOST)return "VK_ERROR_DEVICE_LOST";
else if (value == VkResult::VK_ERROR_INITIALIZATION_FAILED)return "VK_ERROR_INITIALIZATION_FAILED";
else if (value == VkResult::VK_ERROR_OUT_OF_DEVICE_MEMORY)return "VK_ERROR_OUT_OF_DEVICE_MEMORY";
else if (value == VkResult::VK_ERROR_OUT_OF_HOST_MEMORY)return "VK_ERROR_OUT_OF_HOST_MEMORY";
else return "";
}