如何使用pre-c ++ 0x(VS2008)实现“Variadic Template”?

时间:2011-10-07 04:26:09

标签: c++ boost variadic-functions variadic-templates

我正在使用Visual Studio 2008,我想实现字符串格式化功能没有变量参数列表

如何使用pre-c ++ 0x(VS2008)实现“Variadic Template”?

有没有像boost一样实现这个的库?

或者实现这个的另一种方式?

这是我的示例代码。 (当然,这不能被遵守,因为我正在使用VS2008。)

bool VarPrint(std::ostringstream& out, const std::string& s) 
{
    std::string::size_type offset = 0;
    if((offset = s.find("%")) != std::string::npos)
    {
        if(!(offset != s.size() - 1 && s[offset + 1] == '%'))
        {
            ASSERT(!"Missing Arguments!");
            return false;
        }
    }
    out << s;
    return true;
}

template<typename T, typename... Args>
bool VarPrint(std::ostringstream& out, const std::string& s, const T& value, const Args&... args) 
{
    std::string::size_type prev_offset = 0;
    std::string::size_type curr_offset = 0;
    while((curr_offset = s.find("%", prev_offset)) != std::string::npos)
    {
        out << s.substr(prev_offset, curr_offset);
            if(!(curr_offset != s.size() - 1 && s[curr_offset + 1] == '%'))
        {
            out << value;
            if(curr_offset + 2 < s.length())
                return VarPrint(out, s.substr(curr_offset + 2), args...);                   return true;
        }

        prev_offset = curr_offset + 2;
        if(prev_offset >= s.length)
            break;
    }
    ASSERT(!"Extra Argument Provided!");
    return false;
}

2 个答案:

答案 0 :(得分:18)

在C ++ 03中,您有不同的可能性:

  1. 为0-N参数生成重载(例如,使用Boost.Preprocessor)
  2. 使用Cons-Lists(cons(1)("some string")(foo)
  3. 使用对象并重载一些运算符(例如operator(),或像{Boost.Format一样operator%
  4. 第一个选项有点棘手,我觉得,因为不是每个人都能轻松理解宏,所以如果您计划很快迁移到C ++ 0x,我只会保留它用于短期解决方案。

    第三个选项可以提供一个很好的自定义触摸(使用多种语言的%符号进行格式化),但这也意味着需要记住每次特定的“可变参数”函数的工作方式。 / p>

    我个人的偏好是cons方法,因为它解决了这两个问题:

    • 该定义仅涉及模板,因此它比1更具可读性和可维护性。
    • 你定义了一次机器,然后你可以重新使用它来获得任何“可变”功能(并且它们保持功能),这样它就更加一致,并且可以节省你的工作< / LI>

    例如,以下是它的工作原理:

    此示例将使用的包含:

    #include <cassert>
    #include <iostream>
    #include <string>
    

    附加值的结果类型的帮助器(在预先添加时可能更有效,但这意味着以相反的顺序传递参数,这是违反直觉的):

    template <typename T, typename Next> struct Cons;
    struct ConsEmpty;
    
    template <typename Cons, typename U>
    struct cons_result;
    
    template <typename U>
    struct cons_result<ConsEmpty, U> {
      typedef Cons<U, ConsEmpty> type;
    };
    
    template <typename T, typename U>
    struct cons_result<Cons<T, ConsEmpty>, U> {
      typedef Cons<T, Cons<U, ConsEmpty> > type;
    };
    
    template <typename T, typename Next, typename U>
    struct cons_result<Cons<T, Next>, U> {
      typedef Cons<T, typename cons_result<Next, U>::type> type;
    };
    

    Cons模板本身,附加值operator()。请注意,它会创建一个具有不同类型的新项目:

    template <typename T, typename Next>
    struct Cons {
      Cons(T t, Next n): value(t), next(n) {}
    
      T value;
      Next next;
    
      template <typename U>
      typename cons_result<Cons, U>::type operator()(U u) {
        typedef typename cons_result<Cons, U>::type Result;
        return Result(value, next(u));
      }
    };
    
    struct ConsEmpty {
      template <typename U>
      Cons<U, ConsEmpty> operator()(U u) {
        return Cons<U, ConsEmpty>(u, ConsEmpty());
      }
    };
    
    template <typename T>
    Cons<T, ConsEmpty> cons(T t) {
      return Cons<T, ConsEmpty>(t, ConsEmpty());
    }
    

    重温VarPrint与它:

    bool VarPrint(std::ostream& out, const std::string& s, ConsEmpty) {
        std::string::size_type offset = 0;
        if((offset = s.find("%")) != std::string::npos) {
            if(offset == s.size() - 1 || s[offset + 1] != '%')  {
                assert(0 && "Missing Arguments!");
                return false;
            }
        }
        out << s;
        return true;
    }
    
    template<typename T, typename Next>
    bool VarPrint(std::ostream& out,
                  std::string const& s,
                  Cons<T, Next> const& cons) 
    {
        std::string::size_type prev_offset = 0, curr_offset = 0;
        while((curr_offset = s.find("%", prev_offset)) != std::string::npos) {
            out << s.substr(prev_offset, curr_offset);
            if(curr_offset == s.size() - 1 || s[curr_offset + 1] != '%') {
                out << cons.value;
                if(curr_offset + 2 < s.length())
                    return VarPrint(out, s.substr(curr_offset + 2), cons.next);
                return true;
            }
            prev_offset = curr_offset + 2;
            if(prev_offset >= s.length())
                break;
        }
        assert(0 && "Extra Argument Provided!");
        return false;
    }
    

    演示:

    int main() {
      VarPrint(std::cout, "integer %i\n", cons(1));
      VarPrint(std::cout, "mix of %i and %s\n", cons(2)("foo"));
    }
    

    输出:

    integer 1
    mix of 2 and foo
    

答案 1 :(得分:6)

C ++ 03中没有可变参数模板功能。 Boost和其他设计良好的库以不同的方式解决这个问题。对于函数,可以有多个N + 1个重载,其中每个重载从0到N个参数。对于类,可以使用最多N个参数的单个定义,默认为某些无效类型。这个更高的限制通常可以通过一些宏来配置;因为将其设置为高会在编译时产生开销,并将其设置为低将导致用户无法传递足够的参数。

对于您的特定情况,我将以递归方式实现VarPrint。递归中的每一步都将处理一个参数,并使用修改后的格式字符串发出递归调用,并且所有左侧值都向左移动一个位置。