字符串化模板参数
C++ 中是否可以对模板参数进行字符串化? 我尝试了这个:
#include <iostream>
#define STRINGIFY(x) #x
template <typename T>
struct Stringify
{
Stringify()
{
std::cout << STRINGIFY(T) << endl;
}
};
int main()
{
Stringify<int> s;
}
但我得到的是 T
,而不是 int
。似乎预处理器宏是在模板实例化之前评估的。
还有其他方法可以做到这一点吗?
有什么办法可以在模板实例化后进行预处理吗? (编译器是VC++)。
Is it possible in C++ to stringify template arguments?
I tried this:
#include <iostream>
#define STRINGIFY(x) #x
template <typename T>
struct Stringify
{
Stringify()
{
std::cout << STRINGIFY(T) << endl;
}
};
int main()
{
Stringify<int> s;
}
But what I get is a T
, and not an int
. Seems that the preprocessor macros are evaluated before template instantiation.
Is there any other way to do this?
Is there any way for the preprocessing to take place after template instantiation? (Compiler is VC++).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
您可以尝试
编辑:根据评论修复。
You could try
Edit: Fixed based on comments.
您可以使用一些模板魔法。
这比 RTTI(即
typeinfo
)有一个优势 - 它在编译期间得到解决;缺点 - 你需要自己提供类型信息(除非有一些我不知道的库已经做到了这一点;甚至可能是 Boost 中的某些东西)。或者,正如 Martin York 在评论中建议的那样,使用内联函数模板:
但是,如果您需要存储有关该特定类型的更多信息,那么类模板可能会更好。
You could use some template magic.
This has an advantage over RTTI (i.e.
typeinfo
) - it is resolved during compilation; and disadvantage - you need to provide type information yourself (unless there is some library that does that already that I'm not aware of; maybe something in Boost even).Or, as Martin York suggested in comments, use inline function templates instead:
But, if you'll ever need to store more information about that particular type, then class templates will probably be better.
您的代码不起作用,因为负责搜索和扩展您在代码中使用的宏的预处理器不知道该语言本身。它只是一个文本解析器。它会在函数模板中找到 STRINGIFY(T) 并对其进行扩展,早在您为该模板提供类型之前就已经进行了。不幸的是,事实证明,您总是会得到“T”而不是您期望的类型名。
正如 litb 建议的那样,我(糟糕)实现了这个“getTypeName”函数模板,该模板返回您传递给它的类型名
:上面的代码会产生以下输出,并且启用了 GCC 标志 -s(“从二进制文件中删除所有符号”)
:实在是太丑了)。
需要考虑的几点:
__PRETTY_FUNCTION__
格式不同,则字符串匹配可能会中断,您必须修复它。出于同样的原因,我还警告 getTypeName() 可能有利于调试(而且,仍然可能对此没有好处),但它肯定很糟糕,很糟糕,并且不利于其他目的,例如比较模板中的两种类型或类似的东西(我不知道,只是猜测某人可能会想到什么......)。仅将其用于调试,并且最好不要在发布版本中调用它(使用宏来禁用),这样您就不会使用__PRETTY_FUNCTION__
,因此编译器不会为其生成字符串。尽管有这些缺点,我想说它确实很快。第二次查找相同的类型名称时,将需要选择对包含该名称的全局 std::string 的引用。而且,与之前建议的模板专业化方法相比,除了模板本身之外,您无需声明任何其他内容,因此它确实更容易使用。
Your code doesn't work because the preprocessor, responsible for searching and expanding the macros you use in your code, is not aware of the language itself. It is just a text parser. It finds that STRINGIFY(T) in the very function template and expand it, much before you give a type to that template. As it turns out, you will always get "T" instead of the typename you expected, unfortunately.
As litb suggested, I've (badly) implemented this `getTypeName' function template that returns the typename you pass it:
The code above results in the following output with GCC flag -s ("strip all symbols from binary") enabled:
So, you see, getTypename() does a fairly better job, at the cost of that fugly string parsing hack (I KNOW, it's damn ugly).
A few points to take into account:
__PRETTY_FUNCTION__
's differently, the string matching can break and you'll have to fix it. For this same reason I also warn that getTypeName() might be good for debugging (and, still, maybe not even good for that), but it is surely bad, bad, and bad for other purposes such as comparing two types in a template or something like that (I don't know, just guessing what someone might think of..). Use it solely for debugging, and preferentially don't call it in release builds (use macros to disable), so that you don't use__PRETTY_FUNCTION__
and thus the compiler doesn't produce the string for it.Despite those disadvantages, I'd like to say that it sure is fast. For the second time you lookup for one same type name, it will cost picking a reference to a global std::string containing the name. And, comparatively to the template specialiazation methods suggested before, there is nothing else you have to declare besides the very template itself, so it is really much easier to use.
不,您不能像变量一样处理类型。您可以编写提取元素的 typeid() 并打印名称的代码,但结果值可能不是您所期望的(类型名称不是标准化的)。
如果您想要使用的类型数量有限,您还可以使用模板专业化(和一些宏魔法)来实现更有趣的版本:
或者您甚至可以组合两个版本:使用 typeinfo 实现 printtype 通用模板,然后提供您想要拥有更漂亮名称的类型的专业化。
No, you cannot work on types as if they were variables. You could write code that extracted the typeid() of an element and printed the name, but the resulting value will probably not be what you expect (type names are not standarized).
You can also work with template specializations (and some macro magic) to achieve a more interesting version if the number of types you want to work with is limited:
Or you could even combine both versions: implement the printtype generic template using typeinfo and then provide specializations for the types you want to have fancier names.
这打破了我编写 C++ 代码的主要原则之一:避免同时在模板功能和预处理器中使用技巧。
模板及其引入语言的肮脏部分原因是试图让开发人员放弃使用预处理器。如果你同时使用两者,那么恐怖分子就会获胜。
This breaks one of my primary tenets of C++ code writing: Avoid using tricks in both the template features and the preprocessor at the same time.
Part of the reason for templates and the nastiness they introduce into the language was an attempt to wean developers away from using the preprocessor. If you use both, then the terrorists win.
如果您使用 boost/core/demangle.hpp,您可以获得可靠的人类可读字符串。
If you use boost/core/demangle.hpp, you can get a reliable human-readable string.
在我的代码中,我使用“类名”的“可怕”双重声明,
因为 c++ 无法从模板中提取字符串“MyServer”...
使用 cpp“包装器”来“摆脱”这个的唯一“方法”
in my code I use the "awful" double-declaration of the "Class-Name"
because c++ is NOT able to extract the string "MyServer" from the template…
the only "way" to get "rid" of this… using a cpp "wrapper"
这就是我所做的:我有一个
demangle()
函数(在abi::__cxa_demangle()
之上实现,我用几个方便的模板函数重载调用它,< code>nameof(),具有我想要字符串化的类型或相同的实例。它相当紧凑,因此我将在
demangle.hh
中重现它。我们有:...然后在
demangle.cpp
中:要使用它,我认为您必须链接到
libc++
(或任何您本地的等效项)才能使用 < code>abi::__cxa_demangle()。对于 OP 来说,这可能是次优的,因为我个人喜欢在运行时进行对 constexpr 友好的操作。 leu,但由于我患有严重的宏滥用过敏症,我发现这是解决此问题的最不合理的解决方案(
terminator
命名空间无关紧要 - 我在中使用此代码。从终止处理程序调用的基于 libunwind 的堆栈跟踪器 - 随意s///g
该令牌)Here’s what I do: I have a
demangle()
function (implemented on top ofabi::__cxa_demangle()
which I call with a couple of convenience template function overloads,nameof()
, with either the type I want stringified or an instance of same.It’s fairly compact, so I’ll reproduce it here in all its glory. In
demangle.hh
we have:… And then in
demangle.cpp
:To use this, I think you’ll have to link to
libc++
(or whatever your local equivalent is) to useabi::__cxa_demangle()
. What may be suboptimal for the OP is the fact that this does the demangling and stringification at runtime. I’d personally love somethingconstexpr
-friendly in leu of this, but since I suffer from a severe macro-abuse allergy, I find this to be the least generally-unreasonable solution to this problem.(the
terminator
namespace is inconsequential – I use this code in a libunwind-based stacktracer called from termination handler – feel free tos///g
that token)