可以将 #defines 列表转换为字符串
假设我在外部库的头文件中有一个#define 列表。这些#define 表示从函数返回的错误代码。我想编写一个转换函数,它可以将错误代码作为输入,并返回表示实际 #define
名称的字符串文字作为输出。
举个例子,如果我有,
#define NO_ERROR 0
#define ONE_KIND_OF_ERROR 1
#define ANOTHER_KIND_OF_ERROR 2
我希望能够像这样调用一个函数
int errorCode = doSomeLibraryFunction();
if (errorCode)
writeToLog(convertToString(errorCode));
,并让 convertToString()
能够自动转换该错误代码,而不是像
const char* convertToString(int errorCode)
{
switch (errorCode)
{
case NO_ERROR:
return "NO_ERROR";
case ONE_KIND_OF_ERROR:
return "ONE_KIND_OF_ERROR";
...
...
...
我 一样是一个巨大的开关盒一种感觉是,如果这是可能的,那么使用模板和元编程是可能的,但这只能起作用,错误代码实际上是一种类型,而不是一堆处理器宏。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
如果您确实想保留
#define
,我会选择 Michael 优雅的#define STR(code)
答案。但是,define 更像是 C 语言,而不是 C++,define 的最大缺点是不能将它们放在命名空间中。它们会污染您包含它们的任何程序的全局命名空间。如果您有权更改它,我建议您使用匿名枚举:这与您的
#define
完全相同有,你可以将它放在命名空间中。现在您可以使用 Michael 的其他答案,涉及static const char* const error_names
数组来执行您最初要求的操作。If you definitely want to keep the
#define
's I would go with Michael's elegant#define STR(code)
answer. But defines are more C than C++, and the big downside to defines is you can't put them in a namespace. They will pollute the global namespace of any program you include them in. If it's in your power to change it, I would recommend using an anonymous enum instead:This is exactly the same as the
#define
s you have, and you can put it in a namespace. And now you can use Michael's other answer involving thestatic const char* const error_names
array yo do what you had originally asked.实际上,您可以采用两种方式,即具有两个从代码来回转换为错误的函数。
当然,第一件事是
#define
不应该用于常量,枚举可能是最好的,但是枚举不能扩展,这要求所有错误都定义在同一个地方(哎呀,非常感谢您的依赖关系...)不过,您可以再做一次,使用名称空间来隔离符号,并进行预处理来处理整个生成过程。对于查找部分,我们将使用 Bimap。
首先我们需要定义一个 Handler 类(不需要内联所有这些,但对于示例来说更容易)
然后我们只需要提供一些语法糖:
在注册未知错误的情况下我们可以更暴力一些(断言例如)。
然后我们总是可以将该宏包装成一个可以一次性定义多个符号的宏......但这只是一个练习。
用法:
免责声明:我不太确定 lambda 语法,但它确实简化了编写。
You can actually have it both ways, ie having two functions that translate from code to error back and forth.
The first thing of course is that
#define
should not be used for constants, an enum would probably be best, however an enum cannot be extended, which requires that all your errors be defined in the same place (ouch, thank you so much for dependencies...)You can do it another may though, using namespaces to isolate the symbols, and preprocessing to handle the whole generation. For the lookup part, we'll use a Bimap.
First we need to define a Handler class (not necessary to inline all that, but it's easier for examples)
Then we just need to provide some syntactic sugar:
We could be a bit more violent in case of a registration of an unknown error (assert for example).
And then we can always wrap that macro into one that can define multiple symbols in one go... but that's left as an exercise.
Usage:
Disclaimer: I am not too sure about the lambda syntax, but it did simplified the writing.
#define FOO 1
由预处理器作为简单的文本替换进行处理。如果你想保留这些定义,你需要巨大的开关。#define FOO 1
is handled by the preprocessor as a simple text replacement. If you want to preserve those definitions, you need the giant switch.我通常用巨型开关盒的方式来做,尽管我用以下方法使它更容易:
这是一个很好的问题,我有兴趣看看人们有什么更好的方法
I normally do it the giant switch case way, although I make it somewhat easier with:
This is a good question, I'm interested to see what better ways people have
在生成的代码中流行的另一种方法是:
我更喜欢 switch case 方式 已经提到,但根据代码的结构方式,作为构建过程的一部分,自动生成该数组可能会更容易
Another way to do it that's popular in generated code is:
I prefer the switch case way I already mentioned, but depending on how your code is structured it might be easier as part of your build process to generate that array automatically
你是对的。无法在运行时恢复预处理器定义的标识符(除非您可以阅读源代码,但这是作弊)。您最好创建一个名称常量数组并使用错误代码对其进行索引(当然要进行适当的边界检查) - 编写脚本来生成它应该很容易。
You are correct. There's no way to recover preprocessor-defined identifiers at runtime (unless you can read the source, but that's cheating). You would be better off creating a constant array of the names and indexing it with the error code (with proper boundary checks of course) - it should be quite easy to write a script to generate it.
看一下 boost 预处理器。
您可以创建代码对的列表/数组/序列:
相关链接:
http://www.boost.org/doc/libs/1_41_0/libs/preprocessor/doc/ref/seq_for_each.html
http://www.boost.org/doc/libs/1_41_0/libs/preprocessor/doc/ref/ tuple_elem.html
take a look at boost preprocessor.
You could create list/array/sequence of code pairs:
relevant link:
http://www.boost.org/doc/libs/1_41_0/libs/preprocessor/doc/ref/seq_for_each.html
http://www.boost.org/doc/libs/1_41_0/libs/preprocessor/doc/ref/tuple_elem.html
这里的一种可能性是编写一个小程序来解析包含#defines的.h文件,并为convertToString()函数发出相应的源代码。然后,只要 .h 文件发生更改,您就可以让该程序作为构建过程的一部分自动运行。预先需要做更多的工作,但是一旦实现,您将不再需要手动更新 int<->string 转换函数。
如果您想支持使用相同常量的多种语言的代码,这尤其有用。例如,在我的一个应用程序中,更改 #defines 头文件会导致自动重新生成相应的 C++、Python 和 Java 文件。这使得项目维护变得更加容易并且不易出错。
One possibility here is to write a little program that parses the .h file that contains the #defines, and emits the corresponding source code for the convertToString() function. Then you can have that program automatically run as part of your build process whenever the .h file is changed. It's a little more work up front, but once it's implemented you'll never again need to manually update your int<->string conversion function.
This is especially useful if you want to support code in multiple languages that uses the same constants. For example, in one of my applications, changing my #defines header file causes the corresponding C++, Python, and Java files to be auto-regenerated. This makes project maintenance much easier and less error-prone.