Создать кортеж из вектора строк в C ++ - PullRequest
0 голосов
/ 26 июня 2018

У меня есть вектор строк, каждая из которых является результатом применения std :: to_string к некоторому базовому типу данных (например, char, int, double). Я хотел бы функцию, чтобы отменить это в кортеж соответствующих типов.

У меня есть простой шаблон функции для инвертирования std :: to_string:

template<typename T>
T from_string(std::string s)
{
}

template<>
int from_string<int>(std::string s)
{
    return std::stoi(s);
}

template<>
double from_string<double>(std::string s)
{
    return std::stod(s);
}

//... and more such specializations for the other basic types

Я хочу такую ​​функцию, как:

template<typename... Ts>
std::tuple<Ts> undo(const std::vector<std::string>>& vec_of_str)
{
    // somehow call the appropriate specializations of from_string to the elements of vector_of_str and pack the results in a tuple. then return the tuple.     
}

Функция должна вести себя так:

int main()
{
    auto ss = std::vector<std::string>>({"4", "0.5"});
    auto tuple1 = undo<int, double>(ss);
    std::tuple<int, double> tuple2(4, 0.5);

    // tuple1 and tuple2 should be identical. 
}

Я думаю, что мне нужно «перебрать» параметры в Ts (возможно, правильным термином является «unpack»), вызвать предыдущую функцию from_string для каждого и затем упаковать результаты каждого приложения from_string в кортеж. Я видел (и использовал) примеры, которые распаковывают пакет параметров шаблона - они обычно рекурсивны (но не обычным способом вызова самой функции), но я не вижу, как сделать остальное.

Ответы [ 2 ]

0 голосов
/ 26 июня 2018

Для C ++ 11 - полезно, если у вас нет C ++ 14 (требуется решением Максима), или если вы хотите научиться реализовывать рекурсивные вариационные шаблоны:

#include <string>
#include <vector>
#include <tuple>
#include <cassert>

template <std::size_t N, typename T>
struct Undo
{
    static void f(T& tuple, const std::vector<std::string>& vec_of_str)
    {
        Undo<N - 1, T>::f(tuple, vec_of_str);
        std::get<N - 1>(tuple) = from_string<
            typename std::tuple_element<N - 1, T>::type
        >(vec_of_str[N - 1]);
    }
};

template <typename T>
struct Undo<0, T>
{
    static void f(T&, const std::vector<std::string>&)
    {
    }
};

template <typename... Ts>
std::tuple<Ts...> undo(const std::vector<std::string>& vec_of_str)
{
    assert(vec_of_str.size() == sizeof...(Ts));
    std::tuple<Ts...> ret;
    Undo<sizeof...(Ts), std::tuple<Ts...>>::f(ret, vec_of_str);
    return ret;
}
0 голосов
/ 26 июня 2018

Пример:

#include <vector>
#include <string>
#include <tuple>
#include <cassert>

#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string/trim.hpp>

template<class... Ts, size_t... Idxs>
std::tuple<Ts...>
parse(std::vector<std::string> const& values, std::index_sequence<Idxs...>) {
    return {boost::lexical_cast<Ts>(boost::algorithm::trim_copy(values[Idxs]))...};
}

template<class... Ts>
std::tuple<Ts...> undo(std::vector<std::string> const& values) {
    assert(sizeof...(Ts) == values.size());
    return parse<Ts...>(values, std::make_index_sequence<sizeof...(Ts)>{});
}

int main() {
    auto ss = std::vector<std::string>({"4", "0.5"});
    auto tuple1 = undo<int, double>(ss);
    std::tuple<int, double> tuple2(4, 0.5);
    std::cout << (tuple1 == tuple2) << '\n';
    assert(tuple1 == tuple2);
}

Если строковые значения не содержат начальных и / или конечных пробелов, этот вызов boost::algorithm::trim_copy может быть удален. Это происходит из-за сбоя boost::lexical_cast на пустом месте.


Без boost::lexical_cast вам нужно будет повторно реализовать его, что-то вроде:

template<class T> T from_string(std::string const& s);
template<> int      from_string<int>(std::string const& s)    { return std::stoi(s); }
template<> double   from_string<double>(std::string const& s) { return std::stod(s); }
// And so on.

template<class... Ts, size_t... Idxs>
std::tuple<Ts...>
parse(std::vector<std::string> const& values, std::index_sequence<Idxs...>) {
    return {from_string<Ts>(values[Idxs])...};
}
...