如何实现“可变参数模板”与预c++0x(VS2008)?

发布于 2024-12-08 18:03:25 字数 1462 浏览 2 评论 0原文

我正在使用 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;
}

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

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

Is there any library which implements this like boost?

Or another way to implement this?

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;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

一杆小烟枪 2024-12-15 18:03:25

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

  1. 为 0-N 参数生成重载(例如使用 Boost.Preprocessor)
  2. 使用 Cons-Lists (cons(1)("some string")(foo))
  3. 使用对象并重载一些运算符(例如 operator()operator% 如 Boost.Format)

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

第三个选项可能提供良好的自定义功能(在许多语言中,格式化是通过 % 符号完成的),但这也意味着人们需要记住这个特定的“可变参数”函数每次是如何工作的。

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

  • 定义仅涉及模板,因此它比 1.
  • 定义一次 cons-machinery 更具可读性和可维护性,然后可以重新定义将它用于任何“可变”函数(它们仍然函数),因此它更加一致,并节省您的工作

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

本示例将使用的包含:

#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

In C++03, you have different possibilities:

  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)

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.

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

  • 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:

The includes that this example will use:

#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;
};

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());
}

A revisited VarPrint with it:

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;
}

And the demo:

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

Output:

integer 1
mix of 2 and foo
月隐月明月朦胧 2024-12-15 18:03:25

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

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

There is no variadic template functionality in C++03. Boost and other well designed libraries work around this in different ways. For functions, one can have a number of N + 1 overloads where each overload take from 0 to N arguments. For classes, one can have a single definition with up to N arguments that default to some invalid type. This higher limits are usually configurable via some macro; because setting it to high will impose an overhead in compile times, and setting it to low will cause users not being able to pass enough arguments.

For your particular case, I would implement VarPrint in a recursive way. Each step up in the recursion will process a single argument, and issue a recursive call with a modified format string and all the left values shifted left one position.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文