I'd like to write a function to split strings in C++ to check my template learning. I've also read some relavent questions in this site but most of them don't use templates or string_view. A related question in StackOverflow I asked is here.
My goal is to write a function such that:
- The function can split
std::basic_string, not onlystd::string. - The delimiter can be a
CharT, orconst CharT* porstd::basic_string<CharT>, multiple chars means the delimiter should be fully matched (likestr.splitin Python), not any of the chars. - The results are put into a sequence container from STL.
- The user need not do any explicit conversion when parsing arguments, e.g.
std::string_view(some_str). - (Optional) The function can split
const CharT* por any string literals.
My major concerns are
- To minimize the number of the functions need to be overloaded.
- To write template codes in a more modern C++ way. (For example, I've learn a lot by reading
boost::algorithm::trim, many generic functions work on iterator ranges. But theboostversion ofsplitseems a bit complicate for me right now.) - How
string_viewin C++17 can help in this example. My version seems not take full advantages of it. - How to design a better function signature of the master template to relieve the pain when writing overloading?
Below is my first attempt.
#include <iostream>
#include <list>
#include <string>
#include <string_view>
#include <vector>
template <typename CharT, typename ContainerT>
void split(
std::basic_string_view<CharT> str,
std::basic_string_view<CharT> delimiters,
ContainerT &conts) {
conts.clear();
std::size_t start = 0, end;
std::size_t len = delimiters.size();
while ((end = str.find(delimiters, start)) !=
std::basic_string_view<CharT>::npos) {
if (end - start) {
conts.emplace_back(str, start, end - start);
}
start = end + len;
}
if (start != std::basic_string_view<CharT>::npos && start < str.size()) {
conts.emplace_back(str, start, str.size() - start);
}
}
template <typename CharT, typename ContainerT>
void split(
const std::basic_string<CharT> &str,
const std::basic_string<CharT> &delimiters,
ContainerT &conts) {
split(
std::basic_string_view<CharT>(str),
std::basic_string_view<CharT>(delimiters), conts);
}
template <typename CharT, typename ContainerT>
void split(
std::basic_string<CharT> str, const CharT *delimiter, ContainerT &conts) {
split(
std::basic_string_view<CharT>(str),
std::basic_string_view<CharT>(delimiter), conts);
}
template <typename CharT, typename ContainerT>
void split(
const std::basic_string<CharT> &str, CharT delimiter, ContainerT &conts) {
split(
std::basic_string_view<CharT>(str),
std::basic_string_view<CharT>(&delimiter, 1), conts);
}
template <typename Iter>
void print(Iter begin, Iter end) {
for (auto it = begin; it != end; ++it) {
std::cout << *it << "/";
}
std::cout << std::endl;
}
int main(int argc, char **argv) {
std::string str("haha,ha,,haha,,ha,,,ha,");
std::vector<std::string> strs;
split(std::string_view(str), std::string_view(","), strs);
print(strs.begin(), strs.end());
split(str, std::string(","), strs);
print(strs.begin(), strs.end());
split(str, ',', strs);
print(strs.begin(), strs.end());
split(str, ",,", strs);
print(strs.begin(), strs.end());
return 0;
}