2

I have a C++ function that takes a comma separated string and splits in a std::vector<std::string>:

std::vector<std::string> split(const std::string& s, const std::string& delim, const bool keep_empty = true) { std::vector<std::string> result; if (delim.empty()) { result.push_back(s); return result; } std::string::const_iterator substart = s.begin(), subend; while (true) { subend = std::search(substart, s.end(), delim.begin(), delim.end()); std::string temp(substart, subend); if (keep_empty || !temp.empty()) { result.push_back(temp); } if (subend == s.end()) { break; } substart = subend + delim.size(); } return result; } 

However, I would really like to be able to apply this function to mutiple datatypes. For instance, if I have the input std::string:

1,2,3,4,5,6 

then I'd like the output of the function to be a vector of ints. I'm fairly new to C++, but I know there are something called template types, right? Would this be possible to create this function as a generic template? Or am I misunderstanding how template functions work?

3
  • 1
    Yes that works with a template. Create a function that gets a string and returns the template type. Now you can spezialize this function with different types. Do nothing for string, use atoi for int,... Commented Jul 14, 2014 at 21:55
  • You've screwed up the question. SO doesn't work like this. What impression is the future reader going to have when there's a question and then another one in the update that refers to an answer to the first question? Commented Jul 14, 2014 at 22:11
  • @40two Well, the update was in response to an answer posted here. So same question really IMO. :-) Commented Jul 14, 2014 at 22:12

3 Answers 3

5

You can declare the template function as:

template<class ReturnType> std::vector<ReturnType> split(const std::string&, const std::string&, const bool = true); 

and then specialize it for every vector type you want to allow:

template<> std::vector<std::string> split(const std::string& s, const std::string& delim, const bool keep_empty) { // normal string vector implementation } template<> std::vector<int> split(const std::string& s, const std::string& delim, const bool keep_empty) { // code for converting string to int } // ... 

You can read about string to int conversion here.

You will then need to call split as:

auto vec = split<int>("1,2,3,4", ","); 
Sign up to request clarification or add additional context in comments.

4 Comments

This looks super promising. I'm pretty new to this though, so I'm having trouble figuring out where/how to put these functions in my code - especially in relation to the code I posted. Do I put the guts of my code above in the template function? And then do the atio only in the specialized <int> function?
@Jefffery. I added some more code above to give a better understanding of what I'm having trouble with.
@Brett The function declaration (without definition) must come first. See here or here.
It's a good start. However with your solution, you have to replicate and adapt ALL the code for every type you want to handle. What's the benefit of this approach compared to traditional function overloading ? Isn't the question more about using templates in order to avoid this duplication ?
5

You can "templatise" this function - to start it you just need to replace std::vector<std::string> with 'std::vectorand addtemplate` before the function. But you need to take care of how to put the strings into the resulting vector. In your current implementation you just have

result.push_back(temp); 

because result is vector of strings, and temp is string. In the general case though it is not possible, and if you want to use this function with e.g. vector<int> this line will not compile. However this problem is easily solved with another function - template again - which will convert string to whatever type you want to use split with. Let's call this function convert:

template<typename T> T convert(const std::string& s); 

Then you need to provide specialisations of this function for any type you need. For instance:

template<> std::string convert(const std::string& s) { return s; } template<> int convert(const std::string& s) { return std::stoi(s); } 

In this way you do not need to specialise the entire function as the other answer suggests, only the part depending on the type. The same should be done for the line

result.push_back(s); 

in the case without delimiters.

Comments

4

Your function can be generalized fairly easily to return a vector of an arbitrary type using Boost.LexicalCast. The only hiccup is this condition:

if (delim.empty()) { result.push_back(s); return result; } 

This only works right now because both the input and output types are std::string, but obviously cannot work if you're returning a vector containing a type other than std::string. Using boost::lexical_cast to perform such an invalid conversion will result in boost::bad_lexical_cast being thrown. So maybe you want to rethink that part, but otherwise the implementation is straightforward.

#include <boost/lexical_cast.hpp> template<typename Result> std::vector<Result> split(const std::string& s, const std::string& delim, const bool keep_empty = true) { std::vector<Result> result; if (delim.empty()) { result.push_back(boost::lexical_cast<Result>(s)); return result; } std::string::const_iterator substart = s.begin(), subend; while (true) { subend = std::search(substart, s.end(), delim.begin(), delim.end()); std::string temp(substart, subend); if (keep_empty || !temp.empty()) { result.push_back(boost::lexical_cast<Result>(temp)); } if (subend == s.end()) { break; } substart = subend + delim.size(); } return result; } 

Basically, all I've done is made the result type a template parameter and replaced

result.push_back(x); 

with

result.push_back(boost::lexical_cast<Result>(x)); 

If you cannot use Boost, take a look at this answer that shows how to convert a string to some other type using a stringstream.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.