Hey guys, I'm making a global operator, and I have a line where I'm comparing a std::string::difference_type to a std::string::size_type, I think the difference_type is usually a signed integer, and the size_type an unsigned..

Any ideas for an algorithm change? Also, there is a lambda function used I would like to remove without adding another function.

Basically the algorithm will malfunction if the std::string &left has only empty spaces,
so there is a count_if() to count spaces and compare the count to the size.

#include <iostream>
#include <string>
#include <algorithm>
#include <cctype>

//global operator overload.
//define STRING_OPER_RIGHT_SHIFT_REVERSE
//if you want "string right" to have the characters
//in reverse order.
bool operator>>(std::string &left, std::string &right){
	typedef std::string::reverse_iterator rit;
	if( !left.size() )
		return false;
	
	rit it_begin;
	rit it_end;
	rit it_e_end;//erase section end (whitespace removal)
	rit it;
	std::string::difference_type sum = 0;
	//detect if all spaces.
	sum =
	std::count_if(left.begin(),left.end(), [](std::string::value_type ch){
		return std::isspace(ch) ? true : false;
	});
	if( sum == left.size() ){
		left.clear();
		return false;
	}

	//Loop backward, find first !isspace or begin()
	for(it = left.rbegin(); it != left.rend() && std::isspace(*it); ++it) {}
	it_begin = it;
	//Loop backward from previous iterator, find first isspace, or begin()
	for( ; it != left.rend() && !std::isspace(*it); ++it) {}
	it_end = it;
	//Loop backward from previous iterator, find first !isspace, or begin()
	for( ; it != left.rend() && std::isspace(*it); ++it) {}
	it_e_end = it;

	//Remove range from left, set right equal to.
	right.clear();
	//Copy from begin to end.
#ifdef STRING_OPER_RIGHT_SHIFT_REVERSE
	right.insert(right.begin(),it_begin,it_end);
#else
	right.insert(right.begin(), it_end.base(),it_begin.base());
#endif
	//Erase from ebegin, to end.
	left.erase(it_e_end.base(),it_begin.base());

	return true;
}

int main()
{
	std::string input;
	std::string token;
	std::getline(std::cin,input);
	while(input >> token)
	{
		std::cout << token << std::endl;
	}
}

I started with:
begin = {
loop backward, find first !isspace, or begin()
}
end = {
loop backward from previous iterator, find first isspace() or begin()
}
e_end = {
loop backward from previous iterator, find first !isspace(), or begin().
}

copy begin to end to right hand side

erase begin to e_end (on left hand side string)

Edited 5 Years Ago by pseudorandom21: Added details

>>Also, there is a lambda function used I would like to remove without adding another function.

That's easy. Your Lambda is only calling the isspace function, just replace it with the isspace function. As so:

sum = std::count_if(left.begin(),left.end(),std::isspace);

>>I'm comparing a std::string::difference_type to a std::string::size_type

That's ok, it only issues a little warning. If you want to avoid the warning, cast the diff_type variable to the size_type type. Or, you can use end() - begin() to get the size as a diff_type instead of a size_type.

>>Any ideas for an algorithm change?

I think the algorithm is OK. You might simplify the code using more <algorithm> and <functional> utilities. Also, you should probably use the "assign" function instead of clear and insert on lines 41 to 46. The whole #define business for the reversion of the string is very awkward and unecessary. Either choose one, or implement the operator to take an inserter (front_ or back_insert_iterator) as second argument instead (with a default overload for a string). Finally, notice that line 49 is not going to delete the trailing space characters in the left string (you could just use left.end() for the end of the range to erase).

I never expected to have a problem like this, but I get an assertion failure, "string iterators are incompatible" with this slight modification:

#include <iostream>
#include <string>
#include <algorithm>
#include <functional>
#include <cctype>

//global operator overload.
bool operator>>(std::string &left, std::string &right){
	typedef std::string::reverse_iterator rit;
	
	if( !left.size() )
		return false;
	
	rit it_begin;
	rit it_end;
	rit it_e_end;//erase section end (whitespace removal)
	rit it;
	std::string::difference_type sum = 0;
	//detect if all spaces.
	sum =
		std::count_if(left.begin(),left.end(), std::isspace);
	if( sum == left.size() ){
		left.clear();
		return false;
	}

	//Loop backward, find first !isspace or begin()
	it_begin =
	std::find_if( left.rbegin(), left.rend(), isalnum );//<---- Error!  Idk why.

	//for(it = left.rbegin(); it != left.rend() && std::isspace(*it); ++it) {}
	//it_begin = it;

	//Loop backward from previous iterator, find first isspace, or begin()
	for( ; it != left.rend() && !std::isspace(*it); ++it) {}
	it_end = it;
	//Loop backward from previous iterator, find first !isspace, or begin()
	for( ; it != left.rend() && std::isspace(*it); ++it) {}
	it_e_end = it;

	//Remove range from left, set right equal to.
	right.assign(it_end.base(), it_begin.base());
	//Erase from ebegin, to end.
	left.erase(it_e_end.base(),it_begin.base());

	return true;
}

int main()
{
	std::string input;
	std::string token;
	std::getline(std::cin,input);
	while(input >> token)
	{
		std::cout << token << std::endl;
	}
	std::cout << "Input size: " << input.size() << std::endl;
	std::cout << "Token size: " << token.size() << std::endl;
}

I've never seen this assertion failure that I can recall, and I made this to test it, and it works just fine:

int main()
{
	using namespace std;
	string input;
	getline(cin,input);

	string::reverse_iterator diff =
		find_if(input.rbegin(), input.rend(),isalnum);
	cout << *diff << endl;
}

I'm using Visual Studio 2010, with the SP1 beta.

I just tried this on Linux GCC 4.6.0 and it works:

#include <iostream>
#include <string>
#include <algorithm>
#include <functional>
#include <cctype>

//global operator overload.
bool operator>>(std::string &left, std::string &right){
  typedef std::string::reverse_iterator rit;

  //detect if empty or all spaces.
  if( std::find_if( left.begin(), left.end(), isalnum ) == left.end() ) {
    left.clear();
    return false;
  };

  rit it_begin = std::find_if( left.rbegin(), left.rend(), isalnum ); //find last non-space char
  rit it_end   = std::find_if( it_begin,      left.rend(), isspace ); //find last token's start
  rit it_e_end = std::find_if( it_end,        left.rend(), isalnum ); //find second-last token's end
  right.assign(it_end.base(), it_begin.base()); //set rhs to last token
  left.erase(it_e_end.base(),left.rbegin().base()); //erase last token from lhs
  return true;
}

int main()
{
  std::string input, token;
  std::getline(std::cin,input);
  while(input >> token) {
    std::cout << token << std::endl;
  }
  std::cout << "Input size: " << input.size() << std::endl;
  std::cout << "Token size: " << token.size() << std::endl;
  return 0;
}

Watch out with the use of isspace and isalnum because the versions in the std namespace are templated overloaded functions for the different character types and encoding. You cannot use such templated overloads because their address cannot be taken (at least not as simply), and they have default arguments which make them none-unary. That would be solved by either not using <algorithm> stuff (and spelling out the loops) or using lambdas (from boost or C++0x).

PS: I have no idea what "string iterators are incompatible" is supposed to mean (probably that the first and second parameter of find_if are not of the same type of iterator, which is kind-of impossible).

but I get an assertion failure, "string iterators are incompatible"

Are you really sure that std::find_if(...) is failing? I'd imagine that the next line (35) would cause the failure -- it being invalid at that point.

I'm quite sure. It's probably what I get for installing the VS2010 SP1 Beta. Have you confirmed that it should be working? If so, I will report a bug.

This article has been dead for over six months. Start a new discussion instead.