Home > Mobile >  StrSplit template
StrSplit template

Time:08-29

I'm trying to convert this function to a template:

std::vector<std::string> StrSplit(const std::string& i_str, const std::string& i_delim)
{
    std::vector<std::string> result;

    size_t found = i_str.find(i_delim);
    size_t startIndex = 0;

    while (found != std::string::npos)
    {
        result.emplace_back(std::string(i_str.begin()   startIndex, i_str.begin()   found));
        startIndex = found   i_delim.size();
        found = i_str.find(i_delim, startIndex);
    }
    if (startIndex != i_str.size())
        result.emplace_back(std::string(i_str.begin()   startIndex, i_str.end()));
    return result;
}

The goal is to make it support string and wstring what I tried:

template <typename T, typename T2>
std::vector<T> StrSplit(T& i_str, T2& i_delim)
{
    std::vector<T> result;

    size_t found = i_str.find(i_delim);
    size_t startIndex = 0;

    while (found != T::npos)
    {
        result.emplace_back(T(i_str.begin()   startIndex, i_str.begin()   found));
        startIndex = found   i_delim.size();
        found = i_str.find(i_delim, startIndex);
    }
    if (startIndex != i_str.size())
        result.emplace_back(T(i_str.begin()   startIndex, i_str.end()));
    return result;
}

I'm getting this error: binary '=': no operator found which takes a right-hand operand of type 'T' (or there is no acceptable conversion)

I don't know enough about templates and this error message doesn't help much.

CodePudding user response:

Besides the obvious, as already answered and commented, you may also use a shorter form for splitting the string. C has a dedicated functionality for that.

The std::regex_token_iterator. Please read here about it. It is existing since C 11. Because of the nature of regexes, it is very versatile and powerful. (Though, maybe for some use cases too slow).

With this iterator you can basically iterate over patterns in strings. Or, specify the delimiter as a pattern.

In conjunction with the std::vectors range constructor (5), the result is typically a one-liner for splitting a string.

See for example:

#include <iostream>
#include <string>
#include <vector>
#include <regex>
#include <iterator>
using namespace std::string_literals;

template <typename C>
std::vector<std::basic_string<C>> split(const std::basic_string<C>& s, const std::basic_string<C>& delimiter) {
    const std::basic_regex<C> re{ delimiter };
    return { std::regex_token_iterator<std::basic_string<C>::const_iterator, C>( s.begin(), s.end(), re, -1),{} };
}

int main() {
    for (const auto& s : split("aXXbXXcXXd"s, "XX"s)) std::cout << s << ' ';
    for (const auto& ws : split(L"aaXXbbXXccXXdd"s, L"XX"s)) std::wcout << ws << ' ';
}

CodePudding user response:

You seem a bit confused about whether T is a std::string or a std::vector<std::string>. Some of your code treats it as the first and some as the second.

Here's my effort. I retained the const (why would you remove those) and also have only one template argument, because I can't see that you would want to mix different string types in one function call.

I also removed the explicit T constructor call. This isn't necessary when using emplace_back in either this code, or in the original code.

template <typename T>
std::vector<T> StrSplit(const T& i_str, const T& i_delim)
{
    std::vector<T> result;

    size_t found = i_str.find(i_delim);
    size_t startIndex = 0;

    while (found != T::npos)
    {
        result.emplace_back(i_str.begin()   startIndex, i_str.begin()   found);
        startIndex = found   i_delim.size();
        found = i_str.find(i_delim, startIndex);
    }
    if (startIndex != i_str.size())
        result.emplace_back(i_str.begin()   startIndex, i_str.end());
    return result;
}

Since this code does depend on the parameters being strings a different approach is to template on the character type, not the string type. Like this

template <typename T>
std::vector<std::basic_string<T>> StrSplit(const std::basic_string<T>& i_str, const std::basic_string<T>& i_delim)
{
    std::vector<std::basic_string<T>> result;

    size_t found = i_str.find(i_delim);
    size_t startIndex = 0;

    while (found != std::basic_string<T>::npos)
    {
        result.emplace_back(i_str.begin()   startIndex, i_str.begin()   found);
        startIndex = found   i_delim.size();
        found = i_str.find(i_delim, startIndex);
    }
    if (startIndex != i_str.size())
        result.emplace_back(i_str.begin()   startIndex, i_str.end());
    return result;
}
  •  Tags:  
  • c
  • Related