如何在可变参数模板函数中迭代可变参数元组?

问题描述 投票:0回答:2

我正在编写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

这并不奇怪,我对这种语言还很陌生。我现在想知道如何以一种优雅的方式实现这个小功能。

感谢您的帮助。

c++ variadic-templates variadic-functions stdtuple
2个回答
0
投票

您可能会做类似的事情:

// 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);
}

0
投票
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 );
© www.soinside.com 2019 - 2024. All rights reserved.