制作一个小工具:自动生成“将特定枚举值转换成字符串的C++函数”的代码

需求

假设我有一种枚举类型:

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#的枚举类型。

小工具的使用流程如下:

  1. 将C++的枚举定义拷贝到C#代码中。
  2. 运行C#程序,自动生成“将特定枚举值转换成字符串的C++函数”的代码
  3. 将生成的结果拷贝到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 "";
}

当为新的枚举类型生成代码时,需要做如下操作:

  1. 将开始的枚举类型(“fruit_type”)替换成新的枚举类型,例如enum_x
  2. 在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 "";
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值