如何在 C++ 中打印模板类型名?

how to print template typename in c++?

我正在为 boost numeric_cast 编写包装器,包装器函数类似于:

#include <boost/numeric/conversion/cast.hpp>
#include <stdexcept>

template <typename Source, typename Target>
Target numeric_cast(Source src)
{
    try
    {
        // calling boost numeric_cast here
    }
    catch(boost::numeric::bad_numeric_cast& e)
    {
        throw std::runtime_error("numeric_cast failed, fromType: " + Source + " toType: " + Target);
    }
}

我遇到了这个错误:

error: expected primary-expression before ‘(’ token
  throw std::runtime_error("numeric_cast failed ...
                          ^

我认为错误是要求处理错误消息中的 SourceTarget。那么有没有办法打印templatetypename呢?我是 c++ 的初学者,所以这可能是一个愚蠢的问题...

您可以使用typeid(T).name()获取模板参数的原始字符串:

#include <boost/numeric/conversion/cast.hpp>
#include <stdexcept>
#include <typeinfo>

template <typename Source, typename Target>
Target numeric_cast(Source src)
{
    try
    {
        // calling boost numeric_cast here
    }
    catch(boost::numeric::bad_numeric_cast& e)
    {
        throw (std::string("numeric_cast failed, fromType: ") + 
               typeid(Source).name() + " toType: " + typeid(Target).name());
    }
}

Demo.

请注意字符串文字 "numeric_cast failed, fromType:" 应该是 std::string 类型以支持 '+' 运算符。

首先你应该翻转模板参数。 Source 可以自动推导,另一方面 Target 不能。所以 Target 必须明确提供并且应该在模板参数列表中排在第一位,所以 Source 无论如何都可以推导出来。

第二个问题是不能像那样添加字符串文字(这来自 C)。要在 C++ 中构建复杂的字符串,请使用 std::ostringstream.

要获取类型名称信息,您可以使用 typeid。由于此名称已损坏并且您已经使用了 boost,因此您可以使用 boost 去除该名称并获得很好的人类可读类型名称。

最后一件事:直接使用std::runtime_error是开发者懒惰的体现。为此类情况引入自己的例外 class 是一种很好的做法。

#include <iostream>
#include <sstream>
#include <boost/numeric/conversion/cast.hpp>
#include <boost/core/demangle.hpp>
#include <typeinfo>
#include <stdexcept>

template<typename T>
std::string typeName()
{
    return boost::core::demangle(typeid(T).name());
}

// this is used to bind all exceptions related to local library
class MyLibExceptions : public std::exception
{};

class BadNumericCast : public MyLibExceptions
{
public:
    template<typename Target, typename Source>
    BadNumericCast(Source arg, Target, const char *extra)
    {
        std::ostringstream desc;
        desc << extra << " from type: '" << typeName<Source>()
             << "' with value: " << arg 
             << " to type: '" << typeName<Target>() << '\'';
        mDesc = desc.str();
    }

    const char* what() const noexcept override
    {
        return mDesc.c_str();
    }

private:
    std::string mDesc;
};

template <typename Target, typename Source>
Target numeric_cast(Source arg)
{
    try
    {
        return boost::numeric::converter<Target, Source>::convert(arg);
    }
    catch(boost::numeric::bad_numeric_cast& e)
    {
        throw BadNumericCast{arg, Target{}, e.what()};
    }
}

Live demo