考虑以下功能(它使用http://localhost:8080/invoice)
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列中的值。 v0
类型的Base *
包含由value
填充的成员read_row
,并在process
- 方法中处理。 Base
是计算方法的接口类(例如:一个是Max,另一个是MinMaxAvg)。
如何重写此函数以接受任意数量的Base *
参数以处理多个列?
read_header
和read_row
是variadic-template函数,因此可以接受任意数量的参数,但它们只适用于标量。
如何展开/解压缩variadic-argument以便它调用或使用成员?
我尝试了一些东西,阅读了一些例子,但是我无法创造出有效的东西,这是我当前的/嘲笑代码:
template<unsigned int COL>
void func(const std::string &fn, Base &... values)
{
io::CSVReader<COL> in(fn);
// that's it :-(
}
答案 0 :(得分:1)
一些精心设计的包装扩展将会花哨:
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(), ...);
}
}
答案 1 :(得分:1)
使用包扩展运算符...
来解压缩可变参数。
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)...);
}
}
答案 2 :(得分:1)
两个步骤:首先,我们需要根据需要扩展我们的功能:
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&#39;答案),我们走了:
void process()
{ }
template <typename ... Bases>
void process(Base* b, Bases* ... values)
{
b->process();
process(values ...);
}
在模板函数之前定义这两个函数,它可以工作......
编辑:从J.Doe ......偷窃大小...(基地)+ 1。
答案 3 :(得分: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)...);
}
答案 4 :(得分:0)
可编译示例(您需要填写代码以便从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...);
}
}