输出几个 std::collections 到 CSV / 行第一次迭代的函数
Function which outputs several std::collections to CSV / row first iteration
我想编写一个具有以下签名(或类似签名)的函数:
template <typename... Ts>
void collection_to_csv(const std::string filepath, const Ts& ... containers);
该函数应将 csv 文件写入位于 filepath
的文件,其中容器可以是来自 STD 的任意数量的可迭代容器,并将定义输出的 CSV 文件的列。例如。我可以这样使用它:
std::vector<int> column1{1, 2, 3, 4};
std::list<float> column2{1.5, 2.5, 3.5, 4.5};
collections_to_csv(someFilePath, c1, c2);
这可能吗?到目前为止,我一直在尝试像这样使用可变参数模板:
template <typename... Ts>
void collection_to_csv(const std::string filepath, std::initializer_list<std::string> headers, const Ts& ... containers)
{
std::ofstream file;
file.open(filepath, std::ofstream::out | std::ofstream::trunc);
for (std::size_t irow=0; irow < size; ++irow)
{
for (const auto& container : containers) //But how to iterate through the variadic arguments when they could be different types?
{
file << container[i] << ",";
}
}
file.flush();
file.close();
}
我所看到的关于可变参数模板的所有内容都表明它们 'iterated' 使用递归,但这会强制执行 'column first' 迭代顺序。我需要一个 'row first' 订单,这让我觉得这里的可变参数模板可能是错误的工具。有没有更好的方法?
您可以使用 std::tuple
存储每个 containers
的迭代器,然后使用 std::apply
将它们一一递增。
#include <iostream>
#include <string>
#include <tuple>
#include <vector>
#include <list>
template <typename... Ts>
void collection_to_csv(const Ts&... containers) {
const auto ncol = [](const auto& first, const auto&...) {
return first.size();
}(containers...);
auto iters = std::make_tuple(containers.begin()...);
for (std::size_t icol = 0; icol < ncol; ++icol) {
std::apply(
[&](auto&... iter) {
((std::cout << *iter++ << ","), ...);
}, iters);
std::cout << "\n";
}
}
int main() {
std::vector column1{1, 2, 3, 4};
std::list column2{1.5, 2.5, 3.5, 4.5};
collection_to_csv(column1, column2);
}
感谢C++23的引入views::zip
,collection_to_csv
可以更简单的实现只用views::zip
,即zip all containers
并迭代他们的元素一一。
#include <ranges>
#include <tuple>
template <typename... Ts>
void collection_to_csv(const Ts&... containers) {
for (const auto& values : std::ranges::zip_view(containers...)) {
std::apply(
[](const auto&... value) { ((std::cout << value << ","), ...); }, values);
std::cout << "\n";
}
}
使用带有 std::index_sequence
的辅助函数并在 lambda 表达式上解包,你可以让它像 this:
一样工作
#include <cstddef>
#include <iostream>
#include <list>
#include <tuple>
#include <utility>
#include <vector>
template <typename... TIters, std::size_t... Indices>
void collections_to_csv_helper(std::tuple<TIters...> begins,
std::tuple<TIters...> ends,
std::index_sequence<Indices...>) {
auto currents = begins;
while (((std::get<Indices>(currents) != std::get<Indices>(ends)) && ...)) {
(([&] {
auto& current = std::get<Indices>(currents);
std::cout << *current << '\t';
current++;
}()),
...);
std::cout << '\n';
}
}
template <typename... Containers>
void collections_to_csv(const Containers&... containers) {
collections_to_csv_helper(std::tuple{containers.begin()...},
std::tuple{containers.end()...},
std::index_sequence_for<Containers...>{});
}
int main() {
std::vector<int> column1{1, 2, 3, 4};
std::list<float> column2{1.5, 2.5, 3.5, 4.5, 5.5};
collections_to_csv(column1, column2);
}
输出:
1 1.5
2 2.5
3 3.5
4 4.5
请注意,它上升到 最短 列表并忽略其他列表的其余部分。
或者,还有一些非标准的 zip 方法可以在单个迭代器中压缩多个集合,例如boost 和其他一些库。
我想编写一个具有以下签名(或类似签名)的函数:
template <typename... Ts>
void collection_to_csv(const std::string filepath, const Ts& ... containers);
该函数应将 csv 文件写入位于 filepath
的文件,其中容器可以是来自 STD 的任意数量的可迭代容器,并将定义输出的 CSV 文件的列。例如。我可以这样使用它:
std::vector<int> column1{1, 2, 3, 4};
std::list<float> column2{1.5, 2.5, 3.5, 4.5};
collections_to_csv(someFilePath, c1, c2);
这可能吗?到目前为止,我一直在尝试像这样使用可变参数模板:
template <typename... Ts>
void collection_to_csv(const std::string filepath, std::initializer_list<std::string> headers, const Ts& ... containers)
{
std::ofstream file;
file.open(filepath, std::ofstream::out | std::ofstream::trunc);
for (std::size_t irow=0; irow < size; ++irow)
{
for (const auto& container : containers) //But how to iterate through the variadic arguments when they could be different types?
{
file << container[i] << ",";
}
}
file.flush();
file.close();
}
我所看到的关于可变参数模板的所有内容都表明它们 'iterated' 使用递归,但这会强制执行 'column first' 迭代顺序。我需要一个 'row first' 订单,这让我觉得这里的可变参数模板可能是错误的工具。有没有更好的方法?
您可以使用 std::tuple
存储每个 containers
的迭代器,然后使用 std::apply
将它们一一递增。
#include <iostream>
#include <string>
#include <tuple>
#include <vector>
#include <list>
template <typename... Ts>
void collection_to_csv(const Ts&... containers) {
const auto ncol = [](const auto& first, const auto&...) {
return first.size();
}(containers...);
auto iters = std::make_tuple(containers.begin()...);
for (std::size_t icol = 0; icol < ncol; ++icol) {
std::apply(
[&](auto&... iter) {
((std::cout << *iter++ << ","), ...);
}, iters);
std::cout << "\n";
}
}
int main() {
std::vector column1{1, 2, 3, 4};
std::list column2{1.5, 2.5, 3.5, 4.5};
collection_to_csv(column1, column2);
}
感谢C++23的引入views::zip
,collection_to_csv
可以更简单的实现只用views::zip
,即zip all containers
并迭代他们的元素一一。
#include <ranges>
#include <tuple>
template <typename... Ts>
void collection_to_csv(const Ts&... containers) {
for (const auto& values : std::ranges::zip_view(containers...)) {
std::apply(
[](const auto&... value) { ((std::cout << value << ","), ...); }, values);
std::cout << "\n";
}
}
使用带有 std::index_sequence
的辅助函数并在 lambda 表达式上解包,你可以让它像 this:
#include <cstddef>
#include <iostream>
#include <list>
#include <tuple>
#include <utility>
#include <vector>
template <typename... TIters, std::size_t... Indices>
void collections_to_csv_helper(std::tuple<TIters...> begins,
std::tuple<TIters...> ends,
std::index_sequence<Indices...>) {
auto currents = begins;
while (((std::get<Indices>(currents) != std::get<Indices>(ends)) && ...)) {
(([&] {
auto& current = std::get<Indices>(currents);
std::cout << *current << '\t';
current++;
}()),
...);
std::cout << '\n';
}
}
template <typename... Containers>
void collections_to_csv(const Containers&... containers) {
collections_to_csv_helper(std::tuple{containers.begin()...},
std::tuple{containers.end()...},
std::index_sequence_for<Containers...>{});
}
int main() {
std::vector<int> column1{1, 2, 3, 4};
std::list<float> column2{1.5, 2.5, 3.5, 4.5, 5.5};
collections_to_csv(column1, column2);
}
输出:
1 1.5
2 2.5
3 3.5
4 4.5
请注意,它上升到 最短 列表并忽略其他列表的其余部分。
或者,还有一些非标准的 zip 方法可以在单个迭代器中压缩多个集合,例如boost 和其他一些库。