我正在编写CSV解析器,我认为实践一些高级C ++是一个好主意。特别是,有一个有用的功能可在给定定界符的情况下分割CSV文件的一行。尽管这是一个编写简单的函数,但是现在我希望该函数返回具有不同数量的参数和类型的元组。例如:
int main() {
auto [a, b, c] = extract<int, std::string, float>("42;hello;3.1415", ';');
std::cout << a << ' ' << b << ' ' << c << std::endl;
}
应打印出:
42 hello 3.1415
所以我想到了可变参数模板函数:
template <typename... T>
std::tuple<T...> extract(const std::string&& str, const char&& delimiter) {
std::tuple<T...> splited_line;
/* ... */
return splited_line;
}
但是我不能用可变参数修改该函数内部的元组,就像这样:
std::get<i>(splited_line) // doesn't work
这并不奇怪,我对这种语言还很陌生。我现在想知道如何以一种优雅的方式实现这个小功能。
感谢您的帮助。
您可能会做类似的事情:
// Parsing parts
std::vector<std::string> split(const std::string& s, char delimiter);
template <typename T>
T ConvertTo(const std::string& s);
// Variadic part
template <typename... Ts, std::size_t ... Is>
std::tuple<Ts...> extract_impl(std::index_sequence<Is...>,
const std::vector<std::string>& v)
{
return { ConvertTo<Ts>(v[Is])... };
}
template <typename... Ts>
std::tuple<Ts...> extract(const std::string& s, char delimiter) {
const auto strings = split(s, delimiter);
if (strings.size() != sizeof...(Ts)) {
// Error handling
// ...
}
return extract_impl<Ts...>(std::index_sequence_for<Ts...>, strings);
}
template<class F>
auto foreach_argument( F&& f ) {
return [f = std::forward<F>(f)](auto&&...elems) {
( (void)f(elems), ... );
};
}
template <class... Ts>
std::tuple<Ts...> extract(const std::string& str, const char delimiter) {
std::tuple<Ts...> splited_line;
std::size_t i = 0;
std::size_t index = 0;
auto operation = [&](auto&& elem){
if (index == std::string::npos)
return;
auto next = str.find( delimiter, index );
std::string element = str.substr( index, next );
index = next;
// parse the string "element" into the argument "elem"
++i;
};
std::apply(foreach_argument(operation), splitted_line);
return splited_line;
}
这首先导致默认构造的Ts
,如果未找到该元素,则其仍为默认构造的。
返回值
std::optional<std::tuple<Ts...>>
或者如果不匹配则抛出一个>]
std::tuple<std::optional<Ts>...>
在函数中,
apply
中的lambda将在找到元素时将其.emplace
。然后,在返回之前确保所有元素均有效,否则抛出或返回空的可选内容。
即,将std::tuple<std::optional<Ts>...>>
转换为std::tuple<Ts...>
之类的东西:
return std::apply( [](auto&&elems){ return std::make_tuple( *elems... ); }, splitted_line );