用于创建格式化字符串的可变参数模板

时间:2015-04-19 12:29:00

标签: c++ templates c++11 variadic-templates

我想创建一个格式化方法,使用给定的typename参数生成一个字符串。我正在使用可变参数模板:

template<typename T>
std::string GetFormat()
{
    std::string ret;
    if (typeid(T) == typeid(int))
        ret = "i";
    else if (typeid(T) == typeid(float))
        ret = "f";
    else
        ret = "n";

    return ret;
}

template<typename... Args>
std::string GetFormatVArgs()
{
   std::string ret;
   // for each arg in Args
   //     ret += GetFormat<arg>()
   return ret;
}

void main()
{
    std::string str = GetFormatVArgs<float, float, int>();
    std::cout << str;
}

预期产出:

ffi

如何针对每个typename进行迭代并将其提交给GetFormat()

5 个答案:

答案 0 :(得分:2)

好的,实际上可能有一种更简单的方法,但是你可以去:

void helper(std::string& out)
{}

template<typename T, typename... Ts>
void helper(std::string& out, T*, Ts... ts)
{
    out += GetFormat<T>();
    helper(out, ts...);
}

template<typename... Ts>
std::string GetFormatVArgs()
{
   std::string ret;
   helper(ret, typename std::add_pointer<Ts>::type()...);
   return ret;
}

之前我已经制作了可变函数,但它们总是有参数,因此可以利用类型推导,而我无法找到一种简单的方法来做到这一点而不需要类型推导。所以我让主GetFormatVArgs函数创建了一个可变参数列表,我可以将它传递给辅助函数。这样我就可以利用类型演绎。我创建指针而不是对象的原因是因为您可能希望使用不能默认构造的类型,或者默认构造很昂贵的类型。

答案 1 :(得分:2)

这是另一个解决方案,它非常直接且更简单,不会使用任何对话和技巧。

  • 不需要RTTI!
  • 根本没有运行时费用!
  • 没有使用std :: string,也没有使用其他运行时函数!

所有数据都是初始化的静态数据,这可以通过应用程序中的数据区域复制来完成,而不会在启动后在运行时花费任何成本。

class GetChar
{
    public:
        template <typename T> static constexpr const char Get();

};  

template<>constexpr const char GetChar::Get<int>() { return 'i';}
template<>constexpr const char GetChar::Get<double>() { return 'd';}
template<>constexpr const char GetChar::Get<float>() { return 'f';}

template <typename ... ARGS>
class Format2
{       
    static const char string[sizeof...(ARGS)+1];

    public:
    constexpr static const char* GetFormat() { return string; }
};

template <typename ... ARGS>
const char Format2<ARGS...>::string[sizeof...(ARGS)+1]={GetChar::Get<ARGS>()...,'\0'};


int main()
{ 
    cout << Format2<int,double,float,int>::GetFormat() << endl;
}

答案 2 :(得分:1)

我认为这可能是解决您问题的最简单方法。与Benjamin Lindley的解决方案不同,这不取决于类型扣除,而是取决于SFINAE。

template <typename T, typename... Args, typename std::enable_if<sizeof...(Args) == 0>::type* = nullptr>
std::string GetFormatVArgs() { return typeid(T).name(); }

template <typename T, typename... Args, typename std::enable_if<sizeof...(Args) != 0>::type* = nullptr>
std::string GetFormatVArgs() { return typeid(T).name() + GetFormatVArgs<Args...>();
}

答案 3 :(得分:1)

这是我的解决方案版本

    using namespace std;

    class END {};

    template <typename TYPE>
    class GetFormatChar
    {
    };

    template<> class GetFormatChar<int>     { char c={'i'}; };
    template<> class GetFormatChar<float>   { char c={'f'}; };
    template<> class GetFormatChar<double>  { char c={'d'}; };
    template<> class GetFormatChar<END>     { char c={'\0'}; };

    template<typename HEAD, typename ... ARGS>
    class Format_Impl: GetFormatChar<HEAD>, Format_Impl<ARGS...>
    {
    };

    template<typename HEAD>
    class Format_Impl<HEAD>: GetFormatChar<HEAD>,GetFormatChar<END>
    {
    };

    template <typename ... ARGS>
    class Format: Format_Impl<ARGS...>
    {
        public:
            constexpr char* GetFormat() { return (char*)(this); }

    };


    int main()
    {
        Format<int,double,float,int> f;
        std::cout << f.GetFormat()  << endl;
    }

它做什么以及如何运作:

它只是从类中创建一个对象,该对象取决于给定模板参数的类型。每个只包含一个字符。此char在每个构造函数中初始化。 (编辑:直接在类定义中初始化)。

因为没有其他数据成员,所以字符逐个字节地排序。这很简单意味着我们有一个字符串。另外我们需要一个结尾'\ 0',它是用最后一个模板的专门化创建的,只有一个参数,这里序列停止。现在我们简单地将自己的对象转换为字符串,因为我们知道我们的内存布局与字符串“相同”。

因此它适用于编译时的特化,而不适用于运行时!

编辑:不需要构造函数,只需使用初始化,我们有c ++ 11: - )

与其他此处提供的解决方案相比,它没有任何运行时成本。运行时不需要字符串+操作。该字符串可直接在内存中使用。如果对象是静态创建的,则字符串将在数据空间中准备,而不需要任何额外费用。

好的,金属编码有点难,但它有效。 (如果没有,请给我反馈:-))

答案 4 :(得分:0)

我像Klaus建议的那样解决了它,但有点简单

template<typename T>
char GetFormat() { return 'n'; }

template<>
char GetFormat<int>() { return 'i'; }

template<>
char GetFormat<float>() { return 'f'; }

template <typename... Args>
std::string GetFormatVArgs()
{
    char formats[] = { GetFormat<Args>()... , '\0'};
    std::string ret(formats);
    return ret;
}