如何编写以对象为参数的可变模板函数?
How to write a variadic-template function with objects as parameters?
考虑以下函数(它使用来自ben-strasser (github)的CSV解析器库)
void col1(const std::string &fn, Base *v0)
{
io::CSVReader<2> in(fn);
in.read_header(io::ignore_extra_column, "epoch", v0->column);
double ign;
while (in.read_row(ign, v0->value)) {
v0->process();
}
}
void col2(const std::string &fn, Base *v0, Base *v1)
{
io::CSVReader<3> in(fn);
in.read_header(io::ignore_extra_column, "epoch", v0->column, v1->column);
double ign;
while (in.read_row(ign, v0->value, v1->value)) {
v0->process();
v1->process();
}
}
此函数处理 CSV 文件的第 2 列中的值。Base *
类型的v0
包含由read_row
填充并在process
-方法中处理的成员value
。Base
是计算方法的接口类(例如:一个是Max,另一个是MinMaxAvg)。
如何重写此函数以接受任意数量的Base *
参数以处理多个列?
read_header
和read_row
是可变模板函数,因此可以接受任意数量的参数,但它们仅适用于标量。
如何扩展/解压缩可变参数,以便它调用或使用成员?
我尝试了一些东西,阅读了一些示例,但我无法创建有效的东西,这是我当前的/嘲笑代码:
template<unsigned int COL>
void func(const std::string &fn, Base &... values)
{
io::CSVReader<COL> in(fn);
// that's it :-(
}
一些位置良好的包扩展将有效:
template <class... Bases>
void col(const std::string &fn, Bases *... bases)
{
io::CSVReader<sizeof...(Bases) + 1u> in(fn);
in.read_header(io::ignore_extra_column, "epoch", bases->column...);
double ign;
while (in.read_row(ign, bases->value...)) {
// Awful C++11 arbitrary expansion trick
int dum[]{ 0, (void(
bases->process()
), 0)... };
(void) dum;
// Alternative, sweet and beautiful C++17 fold expression
// (void)(bases->process(), ...);
}
}
两个步骤:首先,我们需要根据需要扩展我们的函数:
template <typename ... Bases>
void f(std::string const& s, Bases* ... values)
{
io::CSVReader<sizeof...(Bases) + 1> in(s);
in.read_header(io::ignore_extra_column, "epoch", values->column ...);
double ign;
while(in.read_row(ign, values->value ...))
{
/* see below! */ process(values...);
}
}
到目前为止没问题,read_header和read_row是可变参数模板,很好。但是,调用成员函数有点棘手 - 看看上面对(但未知的)进程函数的调用。关键字编译时递归(101010的答案),我们来了:
void process()
{ }
template <typename ... Bases>
void process(Base* b, Bases* ... values)
{
b->process();
process(values ...);
}
在模板函数之前定义这两个函数,它就可以工作了......
编辑:窃取大小...(基地)+ 1 来自 J.Doe...
使用打包扩展运算符...
来解压缩可变参数。
template<typename... T> void nop(T&&...) { }
template<typename... Bases>
void func(const std::string &fn, Bases&&... bases)
{
io::CSVReader<sizeof...(Bases) + 1> in(fn);
in.read_header(io::ignore_extra_column, "epoch", bases->column...);
double ign;
while (in.read_row(ign, bases->value...)) {
// multiple ways to call process on all values
// prettier with C++17 stuff it seems
nop((bases->process(), 0)...);
}
}
使用可变参数模板,您必须实现一些较短的编译时递归:
template<unsigned int COL>
void func(const std::string &fn, Base &v) {
...
}
template<unsigned int COL, typename... Args>
void func(const std::string &fn, Base &v, Args&&... args) {
...
func<COL>(fn, std::forward<Args>(args)...);
}
可编译的示例(您需要填写代码以从csv文件读取并写入每个目标):
#include <string>
#include <cstdint>
#include <utility>
#include <tuple>
template<class Function, class...Ts>
void for_all(Function f, Ts&&...ts)
{
using expand = int[];
void(expand{0,
(f(std::forward<Ts>(ts)), 0)...
});
}
// some mocked io library
namespace io
{
template<std::size_t MaxRows>
struct CSVReader
{
CSVReader(const std::string& s)
{
}
template<class...Targets>
void read_headers(std::tuple<Targets...>& target)
{
read_headers_impl(std::make_index_sequence<sizeof...(Targets)>(), target);
}
template<class...Targets>
void read_row(std::tuple<Targets...>& targets)
{
read_values_impl(std::make_index_sequence<sizeof...(Targets)>(), targets);
}
// support for std::tie
template<class...Targets>
void read_row(const std::tuple<Targets...>& targets)
{
read_values_impl(std::make_index_sequence<sizeof...(Targets)>(), targets);
}
private:
template<std::size_t...Is, class Tuple>
void read_headers_impl(std::index_sequence<Is...>, Tuple& target)
{
for_all([](auto&& target) {
// read the header and assign it to target here
}, std::get<Is>(target)...);
}
template<std::size_t...Is, class Tuple>
void read_values_impl(std::index_sequence<Is...>, Tuple& target)
{
for_all([](auto&& target) {
// read the values and assign it to target here
}, std::get<Is>(target)...);
}
};
}
struct Base
{
std::string& value();
void process();
};
template<std::size_t N, class T, class Current = std::tuple<>> struct n_tuple;
template<std::size_t N, class T> using n_tuple_t = typename n_tuple<N, T>::type;
template<std::size_t N, class T, class Current>
struct n_tuple
{
using type = std::conditional_t<
N == std::tuple_size<Current>::value,
Current,
decltype(std::tuple_cat(std::declval<Current>(), std::declval<n_tuple_t<N-1, T>>()))
>;
};
template<class...Bases>
void col_n(const std::string &fn, Bases&...bases)
{
constexpr std::size_t column_count = sizeof...(Bases) + 1;
io::CSVReader<column_count> in(fn);
using headers_type = n_tuple_t<column_count, std::string>;
auto headers = headers_type();
in.read_headers(headers);
double ign;
auto value_refs = std::tie(ign, bases.value()...);
while (in.read_row(value_refs)) {
// now we only want to process each base
for_all([](auto&& base) {
base.process();
}, bases...);
}
}
相关文章:
- 函数何时会在c++中包含stack_Unwind_Resume调用
- 正在为Xtensa simcall函数编写回调函数
- 在C++中,如何在类和函数(可能是模板化的)的头中编写完整的实现
- 如何用C++编写BFS函数
- 编写代码时C++出现错误:错误 1 错误 C2601:'circle':本地函数定义是非法的
- 如何编写一个使用n倍三元条件语句的C++布尔函数
- 如何在没有函数的情况下编写此代码并使C++更简单?
- 编写一个函数以使用 n 百分比的 CPU 使用率
- 如何编写带有异常的构造函数
- 如何编写一个完美的缩写函数模板?
- 如何编写已继承的大纲构造函数?
- 编写一个函数来删除单链表中的节点(尾部除外),仅授予对该节点的访问权限
- 如何在大函数中编写多线程函数?
- 如何使用模板函数参数编写包装函数,该功能可以采用超载的成员函数
- c++类成员函数:如何编写这些函数
- 如何在我的 babysort 函数中编写最终的内部循环
- Javascript生成器函数——用C++编写
- 在函数外部编写代码
- 如何仅为函数指针编写正确的构造函数
- 何时应在非成员函数之前编写关键字 'static'?