Рекурсивная вариационная шаблонная функция для N-мерного вектора

Я пытался понять это некоторое время, и я просто не могу этого сделать.

Что я хотел бы сделать, так это создать функцию шаблона, для которой требуется любое количество аргументов параметра char, а затем один std::string в качестве параметра. Синтаксис примера будет выглядеть так:

nvec<3, std::string> info = string_to_vec<':', ';', '-'>(data);

nvec — это рекурсивный класс шаблонов, который создает n-мерный вектор, основанный на шаблоне определенного типа. Его код довольно прост и его можно увидеть здесь:

template <size_t dim, typename T>
struct multidimensional_vector
{
  typedef std::vector<typename multidimensional_vector<dim - 1, T>::type> type;
};

template <typename T>
struct multidimensional_vector<0, T>
{
  typedef T type;
};

template <size_t dim, typename T>
using nvec = typename multidimensional_vector<dim, typename T>::type;

Однако для string_to_vec может потребоваться любое количество символьных параметров (но не менее одного). Затем с этой информацией нужно вернуть соответствующий n-мерный вектор строки с данными, вставленными как разделенные этими символами. Например, предположим, что std::string, который я передаю, выглядит так:

std::string data = "1:2:3;4:5:6;7:8:9-10:11:12;13:14:15;16:17:18"

Тогда я ожидал бы, что эта функция вернет n-мерный вектор (шаблон строки типа), к которому можно получить доступ следующим образом:

info[0][0][0] // = 1
info[1][0][0] // = 10
info[1][1][1] // = 14
info[1][2][2] // = 18
// etc.

Хотя это нетрудно сделать, если размер вектора известен заранее (просто используйте много циклов for), это становится очень сложно, когда данные содержат произвольное количество разделителей (это означает, что вектор может быть любым). измерение). Возможно ли это сделать с рекурсивными вариативными шаблонами? Если да, то я, честно говоря, понятия не имею, с чего начать.


person JohnTravolski    schedule 20.07.2017    source источник


Ответы (2)


Проще, если разделители перечислены сверху вниз, сначала более высокий уровень. Если все в порядке, то что-то в этом роде:

template <char... delims>
struct StringToVecHelper {
    static std::string convert(const std::string& data) { return data; }
};

template <char delim, char... tail>
struct StringToVecHelper<delim, tail...> {

static nvec<sizeof...(tail)+1, std::string> convert(const std::string& data) {
    nvec<sizeof...(tail)+1, std::string> result;
    size_t start = 0;
    for (;;) {
        size_t pos = data.find(delim, start);
        std::string piece(data, start, pos - start);
        result.push_back(StringToVecHelper<tail...>::convert(piece));
        if (pos == std::string::npos) break;
        start = pos + 1;
    }
    return result;
}
};

template <char... delims>
auto string_to_vec(const std::string& data) {
    return StringToVecHelper<delims...>::convert(data);
}

Демо

person Igor Tandetnik    schedule 20.07.2017
comment
Вау, совершенно фантастически. Мне потребуется некоторое время, чтобы полностью понять, что здесь происходит, но я действительно впечатлен. Отличная работа! - person JohnTravolski; 21.07.2017

Я бы использовал структуру, которую легче специализировать:

template <char... Cs> struct string_to_vec_impl;

template <> struct string_to_vec_impl<>
{
    const std::string& operator()(const std::string& s) const { return s; }
};

template <char C, char...Cs> struct string_to_vec_impl<C, Cs...>
{
    nvec<1 + sizeof...(Cs), std::string> operator()(const std::string& s) const
    {
        std::vector<std::string> words = split(s, C); // split string by character c 
        nvec<1 + sizeof...(Cs), std::string> res;
        for (const auto& word : words) {
            res.push_back(string_to_vec_impl<Cs...>{}(word));
        }
        return res;
    }
};

template <char...Cs>
auto string_to_vec_impl(const std::string& s)
{
    return string_to_vec_impl<Cs...>(s)
}

Примечание:

  • Я использую метод split в качестве упражнения.
  • порядок разделителя обратный.
person Jarod42    schedule 20.07.2017