//
// make_s.cpp: Five versions of the make_s function template
// as described in my article, "Character Pointers VS. STL Strings"
//
// Leor Zolman
// August 20, 2002
//

#include <string>
#include <list>
#include <algorithm>
#include <functional>
#include <cassert>

//
// First iteration: mostly pointers, a string just to
// hold the result.
//

template<typename Container>
Container make_s1(const char *intext,
	 bool stripLeadingSpaces = true)
{
	using std::string;
	int len;
	char *incopy = new char[(len = strlen(intext)) + 1];
	strcpy(incopy, intext);
	Container c;
	char *end = incopy + len;
	char *nextComma;

	char *curPos = incopy;
	while (true)
	{
		nextComma = std::find(curPos, end, ',');
		*nextComma = '\0';
		
		if (stripLeadingSpaces)
			while (isspace(*curPos))
				++curPos;

		string s(curPos);		// from curPos to the NUL
		c.push_back(s);			// not for sets/multisets

		if (nextComma == end)
			break;

		curPos = ++nextComma;
	}
	delete[] incopy;
	return c;
}


//
// Second iteration: starting to think in terms 
// of iterators.
//

template<typename Container>
Container make_s2(const char *intext,
	bool stripLeadingSpaces = true)
{
	using std::string;
	int len;
	char *incopy = new char[(len = strlen(intext)) + 1];
	strcpy(incopy, intext);
	Container c;
	char *end = incopy + len;
	char *nextComma;

	char *curPos = incopy;
	while (true)
	{
		// no need now to stuff a '\0' at *nextComma:
		nextComma = std::find(curPos, end, ',');

		if (stripLeadingSpaces)
			while (isspace(*curPos))
				++curPos;

		c.push_back(string(curPos, nextComma));

		if (nextComma == end)
			break;

		curPos = ++nextComma;
	}
	delete[] incopy;
	return c;
}



//
//	Third iteration: goodbye, dynamic allocation;
//  hello,	strings.
//

template<typename Container>
Container make_s3(const char *intext,
	bool stripLeadingSpaces = true)
{
	using std::string;
	string incopy(intext);	  // now a std::string; no len needed
	Container c;
	string::iterator nextComma;	// string iterators, not char *s

	string::iterator curPos = 
		incopy.begin();
	while (true)
	{
		nextComma = std::find(curPos, incopy.end(), ',');

		if (stripLeadingSpaces)
			while (curPos != nextComma && isspace(*curPos))
				++curPos;

		c.push_back(string(curPos, nextComma));

		if (nextComma == incopy.end())
			break;

		curPos = ++nextComma;
	}
										// nothing to delete
	return c;
}



//
// Fourth iteration: bid last char *'s farewell by 
// taking a string &.
//

template<typename Container>
Container make_s4(const std::string &intext,
	bool stripLeadingSpaces = true)
{
	using std::string;
	Container c;
	string::const_iterator nextComma;

	string::const_iterator curPos = intext.begin();

	while (true)
	{
		nextComma = std::find(curPos, intext.end(), ',');

		if (stripLeadingSpaces)
			while (curPos != nextComma && isspace(*curPos))
				++curPos;

		c.push_back(string(curPos, nextComma));

		if (nextComma == intext.end())
			break;

		curPos = ++nextComma;
	}
	
	return c;
}

//
// extra credit: remove the inner loop
//

template<typename Container>
Container make_s5(const std::string &intext,
	bool stripLeadingSpaces = true)
{
	using std::string;
	Container c;
	string::const_iterator nextComma;

	string::const_iterator curPos = intext.begin();

	while (true)
	{
		nextComma = std::find(curPos, intext.end(), ',');

		if (stripLeadingSpaces)	 // call to find_if replaces loop
			curPos = std::find_if(curPos, nextComma,
					std::not1(std::ptr_fun(isspace)));

		c.push_back(string(curPos, nextComma));

		if (nextComma == intext.end())
			break;

		curPos = ++nextComma;
	}
	
	return c;
}


int main()
{
	using std::list;
	using std::string;

	list<string> ls = make_s1<list<string> >("this,is,a,test");
	assert(*--ls.end() == "test");

	list<string> ls2 = make_s2<list<string> >("this,is,a,test");
	assert(*--ls2.end() == "test");

	list<string> ls3 = make_s3<list<string> >("this,is,a,test");
	assert(*--ls3.end() == "test");

	list<string> ls4 = make_s4<list<string> >("this,is,a,test");
	assert(*--ls4.end() == "test");

	list<string> ls5 = make_s5<list<string> >("this,is,a,test");
	assert(*--ls5.end() == "test");

	return 0;
}
