c++ - 将 C++ 字符串解析为元组

标签 c++ parsing templates c++11 variadic-templates

我正在开发一个简单的 CSV 解析器,它将文件的行存储在一个元组中。如果不是因为文件中各行的条目数及其类型都是变量,这将是一项简单的任务。因此,这些行可能是这样的:

1,2.2,你好,18,世界

解析器应该能够像这样工作:

ifstream file("input.csv");
SimpleCSVParser<int, float, string, int, string> parser(file);

当我尝试实现一个函数来解析实际行时,事情变得复杂了。我仍然没有找到一种方法来从参数列表中提取下一个类型以在调用 file >> var 之前声明变量。我还需要在循环中执行此操作,以某种方式从每次迭代的结果构建一个元组。

那么如何使用纯 C++11 将字符串解析为元组? 我试过这个:

template <typename ...Targs>
tuple<Targs...> SimpleCSVParser<Targs...>::iterator::operator*() {
    istringstream in(cur);
    in.imbue(locale(locale(), new commasep)); // for comma separation
    tuple<Targs...> t;
    for (size_t i = 0; i < sizeof...(Targs); ++i) {
        tuple_element<i,decltype(t)>::type first;
        in >> first;
        auto newt = make_tuple(first);
        // what do I do here?
    }
}

但它不起作用,因为我用来提取类型的元组是空的。

最佳答案

看来,我认为您尝试迭代元组索引/类型不起作用。但是,您可以 做的是为每个成员调用一个读取函数。这个想法是将元组的处理委托(delegate)给一个函数,该函数使用参数包将操作扩展为对每个元素的操作。 std::index_sequence<...>可用于获取整数序列。

像这样:

template <typename T>
bool read_tuple_element(std::istream& in, T& value) {
    in >> value;
    return true;
}

template <typename Tuple, std::size_t... I>
void read_tuple_elements(std::istream& in, Tuple& value, std::index_sequence<I...>) {
    std::initializer_list<bool>{ read_tuple_element(in, std::get<I>(value))... });
}

template <typename ...Targs>
tuple<Targs...> SimpleCSVParser<Targs...>::iterator::operator*() {
    std::istringstream in(cur);
    in.imbue(std::locale(std::locale(), new commasep)); // for comma separation
    std::tuple<Targs...> t;
    read_tuple_elements(in, t, std::make_index_sequence<sizeof...(Targs)>{});
    if (in) { // you may want to check if all data was consumed by adding && in.eof()
        // now do something with the filled t;
    }
    else {
        // the value could *not* successfully be read: somehow deal with that
    }
}

上述代码的基本思想是简单地创建一个合适的调用序列 read_tuple_element() .在进入通用代码之前,假设我们想要实现对 std::tuple<T0, T1, T2> value 的读取。只有三个元素。我们可以使用(为简洁起见,使用 rte() 而不是 read_tuple_element())实现读取:

rte(get<0>(value)), rte(get<1>(value)), rte(get<2>(value));

现在,如果我们有一个索引序列std::size_t... I,而不是为每个元素写出这个我们可以[几乎]使用

得到这个序列
rte(get<I>(value))...;

不过,不允许像这样扩展参数包。相反,需要将参数包放入某些上下文中。上面的代码使用了 std::initializer_list<bool>为此:a std::initializer_list<T> 的元素按照列出的顺序构建。也就是说,我们得到了

std::initializer_list<bool>{ rte(get<I>(value))... };

缺少的一点是如何创建参数包 I评估一系列合适的指标。方便的是,标准库定义了 std::make_index_sequence<Size>这会创建一个 std::index_sequence<I...>具有 I 的一系列值作为0, 1, 2, ..., Size-1 .所以,调用read_tuple_elements()std::make_index_sequence<sizeof...(Targs){}创建一个具有合适参数列表的对象,可以推导出这些参数列表,然后用于将元组扩展为传递给 read_tuple_element() 的元素序列.

关于c++ - 将 C++ 字符串解析为元组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34314806/

相关文章:

c++ - 使用仿函数的自动类型推断不起作用

ios - 删除 NSString 中的 "jacknode"- iOS

c++ - 为什么游戏不使用数学表达式模板?

amazon-web-services - 无法按所需顺序执行嵌套模板,从而进一步回滚

c++ - 来自 Omnet++ 网络的柠檬图

c++ - Windows 经典主题上消失的单选按钮

windows - 在 Windows 上的 R 中读取带有 SUB (1a) (Control-Z) 字符的文本文件

javascript - 使用js脚本生成<head>和&lt;footer&gt;

c++ - zlib c++ 和解压文件

C++解析代码(手写)