简体   繁体   English

可变递归模板

[英]Variadic Recursive Template

I have a particular format that requires space delimited tokens with a final null terminator (the null is part of the output). 我有一个特殊的格式,需要使用空格分隔的标记和最终的空终止符(null是输出的一部分)。 I created a function to send a series of space delimited tokens to an output stream: 我创建了一个函数来将一系列以空格分隔的标记发送到输出流:

// C++ variadic template function to output tokens to a stream delimited by spaces
template <typename T>
void join(std::ostream& os, T const& arg)
{
  // This is the last argument, so insert a null in the stream to delimit
  os << arg << '\000';
}

// Join one, add a space, try again.
template <typename T, typename... Args>
void join(std::ostream& os, T const& arg, Args... args)  // recursive variadic function
{
  os << arg << " ";
  join(os, args...);
}

This works fine for things like join(os, 1, foo, "THING", M_PI); 这适用于join(os, 1, foo, "THING", M_PI); . However, I also have some tokens that need to be not space-delimited, like join(os, "KEY=", val); 但是,我也有一些需要不以空格分隔的标记,如join(os, "KEY=", val); .

I tried to play with the arguments, thinking maybe I could stuff a nullptr in the argument list and use that to overload the method, skipping the space, but I cannot for the life of me figure out how to do that kind of overload. 我试着玩这些参数,想想也许我可以在参数列表中填充nullptr并使用它来重载方法,跳过空间,但我不能为我的生活弄清楚如何做这种过载。

Most of the questions on variadic templates I have seen are fairly old, before standards compliance was more ubiquitous. 在标准合规性普遍存在之前,我所看到的关于可变参数模板的大多数问题都相当陈旧。 If this has been answered elsewhere, please point me to it. 如果在其他地方已经回答了这个问题,请指出。 I'm using GCC 7.3. 我正在使用GCC 7.3。

Or, just tell me I am overly complicating what should be very simple. 或者,告诉我,我过于复杂应该非常简单。 Maybe the variadic template is not the right hammer for this bolt? 也许可变参数模板不是这个螺栓的正确锤子?

Introduce a helper 介绍一个帮手

template <typename A, typename B>
struct pack_t {
  const A& a;
  const B& b;
};

template <typename A, typename B>
std::ostream& operator<<(std::ostream& os, pack_t<A, B> pac) {
  return os << pac.a << pac.b; 
}

template <typename A, typename B>
auto pack(const A& a, const B& b) noexcept {
  return pack_t<A, B>{a, b}; 
}

Use it like 像它一样使用它

join(std::cout, "Hello", "World", pack("pi=", 3.14));

Complete code ( live ) 完整代码( 现场

#include <iostream>

template <typename A, typename B>
struct pack_t {
  const A& a;
  const B& b;
};

template <typename A, typename B>
std::ostream& operator<<(std::ostream& os, pack_t<A, B> pac) {
  return os << pac.a << pac.b; 
}

template <typename A, typename B>
auto pack(const A& a, const B& b) noexcept {
  return pack_t<A, B>{a, b}; 
}

template <typename T>
void join(std::ostream& os, const T& arg) {
  os << arg << '\0';
}

template <typename T, typename... Args>
void join(std::ostream& os, const T& arg, const Args&... args) {
  os << arg << ' ';
  join(os, args...);
}

int main() {
  join(std::cout, "Hello", "World", pack("pi=", 3.14));
}

Note that you can extend the helper to support more-than-two arguments by basing pack_t on std::tuple via aggregation or inheritance. 请注意,通过聚合或继承将pack_t基于std::tuple ,可以扩展帮助程序以支持多于两个的参数。 For example, 例如,

namespace impl {

template <typename T, std::size_t... Idx>
struct pack_t {
  T v;
};

template <std::size_t... Idx, typename... Ts>
auto pack(std::index_sequence<Idx...>, Ts&&... vs) noexcept {
  auto v = std::forward_as_tuple(std::forward<Ts>(vs)...);
  return pack_t<decltype(v), Idx...>{std::move(v)};
}

template <typename T, std::size_t... Idx>
std::ostream& operator<<(std::ostream& os, pack_t<T, Idx...> args) {
  return ((os << std::get<Idx>(std::move(args.v))), ...);
}

}

template <typename... Ts>
auto pack(Ts&&... vs) noexcept {
  return impl::pack(
    std::index_sequence_for<Ts...>{}, std::forward<Ts>(vs)...);
}

Now, you can pack a varying number of arguments ( live ). 现在,您可以打包不同数量的参数( 实时 )。

I suppose you can select a special type to skip the preceding space; 我想你可以选择一种特殊的类型来跳过前面的空格; by example you can define a no_space_tag class or struct 例如,您可以定义no_space_tag类或结构

struct no_space_tag {};

and add a join() version that intercept a no_space_tag object in second position (third, counting os ) and doesn't add the space after the first (second) element; 并添加一个join()版本,该版本拦截第二个位置的no_space_tag对象(第三个,计算os )并且不在第一个(第二个)元素之后添加空格; I mean 我的意思是

template <typename T, typename ... Args>
void join (std::ostream & os, T const & arg, no_space_tag const &,
           Args ... args)
 {
   os << arg;
   join(os, args...);
 }

so you can add a no_space_tag object in this way 所以你可以用这种方式添加一个no_space_tag对象

join(std::cout, "KEY=", no_space_tag{}, val);

or also in this way 或者也是这样

no_space_tag  nst;

join(std::cout, "KEY=", nst, val, ',', "KEY=", nst, val2);

to avoid to add the preceding space. 避免添加前面的空格。

The following is a full working example 以下是一个完整的工作示例

#include <iostream>

struct no_space_tag {};

template <typename T>
void join (std::ostream & os, T const & arg)
 { os << arg << '\000'; }

template <typename T, typename ... Args>
void join (std::ostream & os, T const & arg, Args ... args)
 {
   os << arg << " ";
   join(os, args...);
 }

template <typename T, typename ... Args>
void join (std::ostream & os, T const & arg, no_space_tag const &,
           Args ... args)
 {
   os << arg;
   join(os, args...);
 }

int main()
 {
   char foo {'a'};
   long val { 42L };

   join(std::cout, 1, foo, "THING", "M_PI");
   join(std::cout, "KEY=", no_space_tag{}, val);
 }

You could use something like this. 你可以使用这样的东西。 The First parameter obliges you to pass at least one parameter, but I think this is justified. First参数要求您传递至少一个参数,但我认为这是合理的。

#include <iostream>
#include <array>
#include <ostream>
#include <cmath>

template<typename First, typename ... Rest>
void join(std::ostream& _s, bool _sep, First&& _f, Rest&& ... _rest)
{
    _s << _f;
    std::array<int, sizeof...(_rest)> status{(_s << (_sep ? " " : "")  << std::forward<Rest>(_rest), 0) ...};
    _s << '\n';
}

int main()
{
    join(std::cout, true, "These", "are", "some", "args");
    join(std::cout, true, "These", "are", 4, "args");
    join(std::cout, true, "pi", "is", "a", "constant", "=", M_PI);
    join(std::cout, false, "KEY=", "val");

    return 0;
}

This prints 这打印

These are some args
These are 4 args
pi is a constant = 3.14159
KEY=val

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM