字符串化模板参数(Stringifying template arguments)

2019-07-19 22:20发布

是否有可能在C ++来字符串化模板参数? 我尝试这样做:

#define STRINGIFY(x) #x

template <typename T>
struct Stringify
{
     Stringify()
     {
          cout<<STRINGIFY(T)<<endl;
     }
};

int main() 
{
     Stringify<int> s;
}

但我得到的是一个“T”,而不是“廉政”。 看来,预处理器模板分辨率前踢英寸

是否有任何其他方式做到这一点?

有没有什么方法用于预处理发生模板分辨率后? (编译器是VC ++)。

Answer 1:

你可以试试

 typeid(T).name()

编辑 :基于固定的意见。



Answer 2:

你可以使用一些模板魔术。

#include <iostream>

template <typename T>
struct TypeName { static const char *name; };

template <typename T>
const char *TypeName<T>::name = "unknown";

template <>
const char *TypeName<int>::name = "int";

template <typename T>
struct Stringify
{
     Stringify()
     {
          std::cout << TypeName<T>::name << std::endl;
     }
};

int main() 
{
     Stringify<int> s;
}

这个拥有超过RTTI(即优势typeinfo ) -它在编译过程中得到解决; 劣势 - 你需要提供自己的类型信息(除非有一些库,这是否已经是我不知道的;也许东西在升压偶数)。

或者,如苦参碱纽约的意见建议,使用内联函数模板代替:

template <typename T>
inline const char* typeName(void) { return "unknown"; }

template <>
inline const char* typeName<int>(void) { return "int"; }

// ...
std::cout << typeName<T>() << std::endl;

但是,如果你永远需要存储关于特定类型的详细信息,然后类模板可能会更好。



Answer 3:

您的代码不起作用,因为预处理器,负责搜索和扩大你在代码中使用宏,不知道语言本身的。 这仅仅是一个文本分析器。 报告认为,字符串化(T)在非常函数模板和扩展它,你给一个类型,该模板多前。 事实证明,你总是会得到“T”,而不是你所期望,可惜类型名称。

由于litb建议,我(严重)来实现,它返回传递给它的类型名这个`getTypeName”函数模板:

#include <iostream>

template <typename _Get_TypeName>
const std::string &getTypeName()
{
    static std::string name;

    if (name.empty())
    {
        const char *beginStr = "_Get_TypeName =";
        const size_t beginStrLen = 15; // Yes, I know...
                                       // But isn't it better than strlen()?

        size_t begin,length;
        name = __PRETTY_FUNCTION__;

        begin = name.find(beginStr) + beginStrLen + 1;
        length = name.find("]",begin) - begin;
        name = name.substr(begin,length);
    }

    return name;
}

int main()
{
    typedef void (*T)(int,int);

    // Using getTypeName()
    std::cout << getTypeName<float>() << '\n';
    std::cout << getTypeName<T>() << '\n'; // You don't actually need the
                                           // typedef in this case, but
                                           // for it to work with the
                                           // typeid below, you'll need it

    // Using typeid().name()
    std::cout << typeid(float).name() << '\n';
    std::cout << typeid(T).name() << '\n';

    return 0;
}

上面结合GCC标记-s以下输出(“剥去二进制所有符号”)的结果的代码启用:

float
void (*)(int, int)
f
PFviiE

所以,你看,getTypename()做了相当更好的工作,那是fugly串解析黑客的费用(我知道,这该死的丑陋)。

有几点考虑:

  • 该代码是唯一的GCC。 我不知道如何将它移植到其他编译器。 也许只有少数人有这样的设施,以生产这么漂亮的函数名,并从我搜查,MSVC ++没有一个,如果你问自己,。
  • 如果,在一个新的版本,GCC格式__PRETTY_FUNCTION__的不同,字符串匹配可以打破,你就必须解决它。 出于同样的原因,我也警告说,getTypeName() 可能用于调试(以及再,也许甚至不是好)是好的,但它肯定是坏,坏,坏用于其他目的,如在模板进行比较两种类型或类似的东西(我不知道,只是猜测哪些人可能会想到..)。 使用它仅用于调试和优先不要把它在发布版本(使用宏禁用),这样你就不能使用__PRETTY_FUNCTION__ ,因此编译器不会产生串吧。
  • 我绝对不是专家,我不知道一些奇怪的类型是否会导致字符串匹配失败。 我想询问谁看到这篇文章发表评论,如果他们知道这种情况的人。
  • 该代码使用静态的std :: string。 这意味着,如果一些异常是从它的构造函数和析构函数抛出,也没有办法,它会达到一个catch块,你会得到一个未处理的异常。 我不知道性病::字符串是否能做到这一点,但要注意的是,如果他们这样做,你就麻烦了可能。 我用它,因为它需要一个析构函数来释放内存。 你可以实现自己的类,虽然,确保不例外,除了分配失败被抛出(这几乎是致命的,不是吗?所以......),并返回一个简单的C字符串。
  • 随着类型定义你可以得到一些奇怪的结果,这样的(出于某种原因,该网站打破了这个片段的格式,所以我用这个粘贴链接): http://pastebin.com/f51b888ad

尽管有这些缺点,我想说,可以肯定的是快。 第二次你查找一个同类型的名称,这将花费挑选参阅包含名称的全局的std :: string。 而且,相比较之前建议的模板specialiazation方法,有没有别的你有除了非常模板本身声明,所以它是真的更容易使用。



Answer 4:

不,你不能在类型,好像他们是变量的工作。 你可以写提取元素的typeid的()和打印的名称代码,但由此产生的价值可能不是你所期望的(类型名称不标化)。

您还可以使用模板专门工作(和一些宏观魔法),实现了更有趣的版本,如果你想使用的种类数量是有限的:

template <typename T> const char* printtype(); // not implemented

// implement specializations for given types
#define DEFINE_PRINT_TYPE( type ) \
template<>\
const char* printtype<type>() {\
   return #type;\
}
DEFINE_PRINT_TYPE( int );
DEFINE_PRINT_TYPE( double );
// ... and so on
#undef DEFINE_PRINT_TYPE
template <typename T> void test()
{
   std::cout << printtype<T>() << std::endl;
}
int main() {
   test<int>();
   test<double>();
   test<float>(); // compilation error, printtype undefined for float
}

或者你甚至可以结合这两个版本:使用所属类别实现平面广告类型通用的模板,然后提供你想有票友名称类型的专业。

template <typename T>
const char* printtype()
{
   return typeid(T).name();
}


Answer 5:

这打破了我的C ++代码编写的主要原则之一:避免在这两个模板的功能,并在同一时间使用预处理技巧。

为模板的原因和污秽他们引入语言部分是从使用预处理断奶开发商走的尝试。 如果同时使用,那么恐怖分子赢了。



Answer 6:

如果您使用升压/核心/ demangle.hpp,你可以得到一个可靠的人类可读的字符串。

char const * name = typeid(T).name();
boost::core::scoped_demangled_name demangled( name );

std::cout << (demangled.get() ? demangled.get() : "Failed to demangle") << std::endl;


Answer 7:

这是我做的:我有一个demangle()函数(在上实现abi::__cxa_demangle()我称之为一对夫妇的便利模板函数重载, nameof()有任我想字符串化的类型或一个实例相同。

这是相当紧凑,所以我会在这里重现它在其所有的荣耀。 在demangle.hh我们有:

#pragma once
#include <typeinfo>

namespace terminator {

    /// actual function to demangle an allegedly mangled thing
    char const* demangle(char const* const symbol) noexcept;

    /// convenience function template to stringify a name of a type,
    /// either per an explicit specialization:
    ///     char const* mytypename = terminator::nameof<SomeType>();
    template <typename NameType>
    char const* nameof() {
        try {
            return demangle(typeid(NameType).name());
        } catch (std::bad_typeid const&) {
            return "<unknown>";
        }
    }

    ///  … or as implied by an instance argument:
    ///     char const* myinstancetypename = terminator::nameof(someinstance);
    template <typename ArgType>
    char const* nameof(ArgType argument) {
        try {
            return demangle(typeid(argument).name());
        } catch (std::bad_typeid const&) {
            return "<unknown>";
        }
    }

} /* namespace terminator */

...然后在demangle.cpp

#include "demangle.hh"

#include <cstdlib>
#include <cxxabi.h>
#include <mutex>
#include <memory>

namespace terminator {

    namespace {

        /// define one singular, private, static std::mutex,
        /// to keep the demangler from reentering itself
        static std::mutex mangle_barrier;

        /// define a corresponding private and static std::unique_ptr,
        /// using a delete-expression to reclaim the memory malloc()'ed by
        /// abi::__cxa_demangle() upon its return.
        /// … we use clang pragmas to add flags locally for this to work:
        #pragma clang diagnostic push
        #pragma clang diagnostic ignored "-Wglobal-constructors"
        #pragma clang diagnostic ignored "-Wexit-time-destructors"
        std::unique_ptr<char, decltype(std::free)&> demangled_name{ nullptr, std::free };
        #pragma clang diagnostic pop

    }

    char const* demangle(char const* const symbol) noexcept {
        if (!symbol) { return "<null>"; }
        std::lock_guard<std::mutex> lock(mangle_barrier);
        int status = -4;
        demangled_name.reset(
            abi::__cxa_demangle(symbol,
                                demangled_name.get(),
                                nullptr, &status));
        return ((status == 0) ? demangled_name.release() : symbol);
    }

} /* namespace terminator */

要使用此,我认为你必须链接到libc++ (或其他等值的本地货币是)使用abi::__cxa_demangle() 什么可能是次优的OP是事实,这确实在运行时demangling和字符串化。 我个人喜欢的东西constexpr -友好在这个列伊,但因为我患了严重的宏观虐待患有过敏症,我觉得这是最普遍,不合理地解决了这个问题。

(该terminator命名空间是无关紧要的-我从终止处理程序称为基于libunwind-stacktracer使用此代码-随意s///g该令牌)



Answer 8:

在我的代码我用的是“可怕”的“类名”的双重声明

MqFactoryC<MyServer>::Add("MyServer").Default();

因为C ++使用一段cpp的“包装”是无法从模板中提取字符串“MyServer的” ...唯一的“办法”获得的这种“摆脱” ...

#define MQ_CPPSTR(s) #s
#define MqFactoryCAdd(T) MqFactoryC<T>::Add(MQ_CPPSTR(T)).Default()


文章来源: Stringifying template arguments