如何实现“可变模板"使用 pre-c++0x(VS2008)? [英] How to implement "Variadic Template" with pre-c++0x(VS2008)?

查看:23
本文介绍了如何实现“可变模板"使用 pre-c++0x(VS2008)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm using Visual Studio 2008, and I want to implement string formatting function without Variable Argument List.

如何使用 pre-c++0x(VS2008) 实现可变模板"?

How to implement "Variadic Template" with pre-c++0x(VS2008)?

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

Is there any library which implements this like boost?

或者另一种实现方式?

这是我的示例代码.(当然,这是无法编译的,因为我使用的是 VS2008.)

Here is my sample code. (of course, this can't be complied because i'm using 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;
}

推荐答案

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

In C++03, you have different possibilities:

  1. 为 0-N 个参数生成重载(例如使用 Boost.Preprocessor)
  2. 使用 Cons-Lists (cons(1)("some string")(foo))
  3. 使用对象并重载一些操作符(例如operator(),或者operator% 像Boost.Format)
  1. generate overloads for 0-N arguments (using Boost.Preprocessor for example)
  2. use Cons-Lists (cons(1)("some string")(foo))
  3. use object and overload some operator (operator() for example, or operator% like Boost.Format)

我觉得第一个选项有点棘手,因为不是每个人都能轻松理解宏,所以如果您打算很快迁移到 C++0x,我只会将其保留用于短期解决方案.

The first option is a bit tricky, I feel, because not everyone can understand macros easily, so I would only reserve it for short-terms solutions if you plan on migrating to C++0x soon.

第三个选项可能会提供一个很好的自定义触摸(在许多语言中使用 % 符号进行格式化),但这也意味着人们需要记住这个特定的可变参数"函数是如何工作的时间.

The third option may provide a nice custom touch (formatting is done with a % sign in many languages), but it also means that one needs to remember how this particular "variadic" function works each time.

我个人更喜欢 cons 方法,因为它可以解决这两个问题:

My personal preference is the cons approach because it solves both issues:

  • 该定义仅涉及模板,因此比 1 更具可读性和可维护性.
  • 您定义 cons-machinery 一次,然后您可以将其重新用于任何可变参数"函数(并且它们保留函数),因此它更加一致,并节省您的工作
  • the definition involves only templates, so it is more readable and maintanable than 1.
  • you define the cons-machinery once, and you can then re-use it for any "variadic" function (and they remain functions), so it is more consistent, and saves you work

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

For example, here is how it could work:

此示例将使用的包含:

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

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

A helper for the result type of appending a value (it could be more efficient with prepending, but that would mean passing the arguments in reverse order which is counter-intuitive):

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() 来附加值.请注意,它创建了一个不同类型的新项目:

The Cons template itself, with a magic operator() to append value. Note that it creates a new item with a different type:

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
", cons(1));
  VarPrint(std::cout, "mix of %i and %s
", cons(2)("foo"));
}

输出:

integer 1
mix of 2 and foo

这篇关于如何实现“可变模板"使用 pre-c++0x(VS2008)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆