如何从数组构造元组
How to construct a tuple from an array
我正在设计一个 C++ 库,它从一些实验中读取报告数据的 CSV 文件并进行一些聚合并输出 pgfplots 代码。我想让图书馆尽可能通用和易于使用。我还想将它与 CSV 文件中表示的数据类型隔离开来,并将选项留给用户根据需要解析每一列。我也想避免使用 Boost Spirit Qi 或其他重型解析器。
我的简单解决方案是让用户为每一列创建一个类型,并使用采用 "char *" 的构造函数。构造函数对给定的值进行自己的解析,该值是数据中的一个单元格。然后用户传递给我一个类型列表;模式,表示一行数据中的类型。我使用这个类型列表来创建一个元组,其中元组的每个成员都负责解析自己。
现在的问题是如何初始化(构造)这个元组。处理元组当然不是直截了当的,因为迭代它们的元素主要是编译时操作。我首先使用 Boost Fusion 来完成这个任务。然而,我使用的函数(转换)虽然可能将一个元组作为输入(使用适当的适配器),但它似乎不是 return 一个元组。我需要 return 值是一个元组,以便其他一些代码可以将它用作关联的类型到值容器(通过 std::get<T>
按类型访问它),同时仅使用标准工具,即, 不使用升压。所以我必须将 Fusion 的任何转换 returned 转换为 std::tuple。
我的问题是如何避免这种转换,更好的是如何完全避免 Boost Fusion。
想到的一个简单解决方案是使用 std::tuple 的构造函数,并以某种方式将每个元素传递给它各自需要构造的 "const *"。然而,虽然使用一些复杂的基于模板的枚举技术是可能的,但我想知道是否有一种直接的类似 "parameter-pack" 的方法,或者更简单的方法将值传递给单个元素的构造函数元组。
为了弄清楚我在寻找什么,请看一下下面的代码。
#include <cstdio>
#include <array>
template <typename...> struct format {};
template <typename...> struct file_loader {};
template <typename... Format>
struct
file_loader<format<Format...> > {
void load_file() {
size_t strsize = 500u;
char *str = new char[strsize]();
auto is = fopen("RESULT","r");
/* example of RESULT:
dataset2,0.1004,524288
dataset1,0.3253,4194304
*/
while(getline(&str, &strsize, is) >= 0) {
std::array<char*, 3> toks{};
auto s = str;
int i = 2;
while(i --> 0)
toks[i] = strsep (&s, ",");
toks[2] = strsep (&s, ",\n");
std::tuple<Format...> the_line{ /* toks */ } ; // <-- HERE
//// current solution:
// auto the_line{
// as_std_tuple( // <-- unnecessary conversion I'd like to avoid
// boost::fusion::transform(boost::fusion::zip(types, toks), boost::fusion::make_fused( CAST() ))
// )};
// do something with the_line
}
}
};
#include <string>
class double_type {
public:
double_type() {}
double_type(char const *token) { } // strtod
};
class int_type {
public:
int_type() {}
int_type(char const *token) { } // strtoul
};
int main(int argc, char *argv[]) {
file_loader< format< std::string,
double_type,
int_type > >
{}.load_file();
return 0;
}
我在评论中将有趣的行突出显示为 "HERE"。
我的问题恰恰是:
Is there a way to construct a std::tuple instance (of heterogeneous
types, each of which is implicitly convertible from "char *") with
automatic storage duration (on the stack) from a std::array<char *, N>
,
where N equals the size of that tuple?
我要的答案应该
- 避免 Boost Fusion
- (简单条件)避免使用超过 5 行基于样板模板的枚举代码
- 或者,说明为什么这在 C++14 标准中是不可能的
答案可以使用 C++17 构造,我不介意。
谢谢,
与涉及 std::tuple
的所有问题一样,使用 index_sequence
给你一个参数包来索引数组:
template <class... Formats, size_t N, size_t... Is>
std::tuple<Formats...> as_tuple(std::array<char*, N> const& arr,
std::index_sequence<Is...>)
{
return std::make_tuple(Formats{arr[Is]}...);
}
template <class... Formats, size_t N,
class = std::enable_if_t<(N == sizeof...(Formats))>>
std::tuple<Formats...> as_tuple(std::array<char*, N> const& arr)
{
return as_tuple<Formats...>(arr, std::make_index_sequence<N>{});
}
您将用作:
std::tuple<Format...> the_line = as_tuple<Format...>(toks);
我正在设计一个 C++ 库,它从一些实验中读取报告数据的 CSV 文件并进行一些聚合并输出 pgfplots 代码。我想让图书馆尽可能通用和易于使用。我还想将它与 CSV 文件中表示的数据类型隔离开来,并将选项留给用户根据需要解析每一列。我也想避免使用 Boost Spirit Qi 或其他重型解析器。
我的简单解决方案是让用户为每一列创建一个类型,并使用采用 "char *" 的构造函数。构造函数对给定的值进行自己的解析,该值是数据中的一个单元格。然后用户传递给我一个类型列表;模式,表示一行数据中的类型。我使用这个类型列表来创建一个元组,其中元组的每个成员都负责解析自己。
现在的问题是如何初始化(构造)这个元组。处理元组当然不是直截了当的,因为迭代它们的元素主要是编译时操作。我首先使用 Boost Fusion 来完成这个任务。然而,我使用的函数(转换)虽然可能将一个元组作为输入(使用适当的适配器),但它似乎不是 return 一个元组。我需要 return 值是一个元组,以便其他一些代码可以将它用作关联的类型到值容器(通过 std::get<T>
按类型访问它),同时仅使用标准工具,即, 不使用升压。所以我必须将 Fusion 的任何转换 returned 转换为 std::tuple。
我的问题是如何避免这种转换,更好的是如何完全避免 Boost Fusion。
想到的一个简单解决方案是使用 std::tuple 的构造函数,并以某种方式将每个元素传递给它各自需要构造的 "const *"。然而,虽然使用一些复杂的基于模板的枚举技术是可能的,但我想知道是否有一种直接的类似 "parameter-pack" 的方法,或者更简单的方法将值传递给单个元素的构造函数元组。
为了弄清楚我在寻找什么,请看一下下面的代码。
#include <cstdio>
#include <array>
template <typename...> struct format {};
template <typename...> struct file_loader {};
template <typename... Format>
struct
file_loader<format<Format...> > {
void load_file() {
size_t strsize = 500u;
char *str = new char[strsize]();
auto is = fopen("RESULT","r");
/* example of RESULT:
dataset2,0.1004,524288
dataset1,0.3253,4194304
*/
while(getline(&str, &strsize, is) >= 0) {
std::array<char*, 3> toks{};
auto s = str;
int i = 2;
while(i --> 0)
toks[i] = strsep (&s, ",");
toks[2] = strsep (&s, ",\n");
std::tuple<Format...> the_line{ /* toks */ } ; // <-- HERE
//// current solution:
// auto the_line{
// as_std_tuple( // <-- unnecessary conversion I'd like to avoid
// boost::fusion::transform(boost::fusion::zip(types, toks), boost::fusion::make_fused( CAST() ))
// )};
// do something with the_line
}
}
};
#include <string>
class double_type {
public:
double_type() {}
double_type(char const *token) { } // strtod
};
class int_type {
public:
int_type() {}
int_type(char const *token) { } // strtoul
};
int main(int argc, char *argv[]) {
file_loader< format< std::string,
double_type,
int_type > >
{}.load_file();
return 0;
}
我在评论中将有趣的行突出显示为 "HERE"。
我的问题恰恰是:
Is there a way to construct a std::tuple instance (of heterogeneous types, each of which is implicitly convertible from "char *") with automatic storage duration (on the stack) from a
std::array<char *, N>
, where N equals the size of that tuple?
我要的答案应该
- 避免 Boost Fusion
- (简单条件)避免使用超过 5 行基于样板模板的枚举代码
- 或者,说明为什么这在 C++14 标准中是不可能的
答案可以使用 C++17 构造,我不介意。
谢谢,
与涉及 std::tuple
的所有问题一样,使用 index_sequence
给你一个参数包来索引数组:
template <class... Formats, size_t N, size_t... Is>
std::tuple<Formats...> as_tuple(std::array<char*, N> const& arr,
std::index_sequence<Is...>)
{
return std::make_tuple(Formats{arr[Is]}...);
}
template <class... Formats, size_t N,
class = std::enable_if_t<(N == sizeof...(Formats))>>
std::tuple<Formats...> as_tuple(std::array<char*, N> const& arr)
{
return as_tuple<Formats...>(arr, std::make_index_sequence<N>{});
}
您将用作:
std::tuple<Format...> the_line = as_tuple<Format...>(toks);