Link to home
Start Free TrialLog in
Avatar of mrwad99
mrwad99Flag for United Kingdom of Great Britain and Northern Ireland

asked on

Sorting a vector via a predicate: compilation differences across different OSs!

Ah hello.

Please consider the following code, which looks simple but is hiding some fiendish problems:
#include <vector>
#include <algorithm>
#include <iostream>
#ifndef _WIN32
#include <strings.h>
#define stricmp strcasecmp
#endif

using namespace std;

class CParameter
{
public:
	CParameter(const char* pszName, int iLinenum) : m_strName(pszName), m_iLinenum(iLinenum) {}
	~CParameter() {}
	std::string& GetName() { return m_strName; }
	int GetLineNumber() const { return m_iLinenum; }
protected:
	int m_iLinenum;
	std::string m_strName;
};

struct sort_predicate
{

	//bool operator() (CParameter* const lhs, CParameter* const rhs)
	//{
	//	int nRet = stricmp(lhs->GetName().c_str(), rhs->GetName().c_str());
	//	return (nRet == 0) ? (lhs->GetLineNumber() < rhs->GetLineNumber()) : (nRet < 0);
	//}
	bool operator() (CParameter*& lhs, CParameter*& rhs)
	{
		int nRet = stricmp(lhs->GetName().c_str(), rhs->GetName().c_str());
		return (nRet == 0) ? (lhs->GetLineNumber() < rhs->GetLineNumber()) : (nRet < 0);
	}
	bool operator() (const std::string& lhs, CParameter*& rhs)
	{
		return stricmp(lhs.c_str(), rhs->GetName().c_str())< 0;
	}

	bool operator() (CParameter*& lhs, const std::string& rhs)
	{
		return stricmp(lhs->GetName().c_str(), rhs.c_str()) < 0;
	}        
};

int main()
{

	std::vector<CParameter*> m_parameters, storage;
	std::string strParamName = "Second";
	m_parameters.push_back(new CParameter("C", 1));	
	m_parameters.push_back(new CParameter("B", 4));
	m_parameters.push_back(new CParameter("D", 2));
	m_parameters.push_back(new CParameter("A", 5));
	m_parameters.push_back(new CParameter("A", 3));

	std::sort(m_parameters.begin(), m_parameters.end(), sort_predicate());

	cout << "First: " << m_parameters[0]->GetName().c_str() << " at line number " << m_parameters[0]->GetLineNumber() << endl;
	cout << "Second: " << m_parameters[1]->GetName().c_str() << " at line number " << m_parameters[1]->GetLineNumber() << endl;
	
}

Open in new window


OK.  So I am trying to compile this on three platforms, Windows (via VS 2005) Solaris 10 (via CC) and Oracle Enterprise Linux (via g++).  All three give different results.

On Windows, the code above just works fine.

On Solaris, it complains

".../include/CC/Cstd/./algorithm", line 665: Error: Could not find a match for sort_predicate::operator()(CParameter*const, CParameter*const) needed in std::__median<CParameter*, sort_predicate>(CParameter*const&, CParameter*const&, CParameter*const&, sort_predicate).
".../include/CC/Cstd/./algorithm", line 796:     Where: While instantiating "std::__introsort_loop<CParameter**, int, sort_predicate>(CParameter**, CParameter**, int, sort_predicate)".


On OEL, it complains:

/usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/bits/stl_algo.h:124: error: no match for call to '(sort_predicate) (CParameter* const&, CParameter* const&)'
foo2.cpp:31: note: candidates are: bool sort_predicate::operator()(CParameter*&, CParameter*&)
foo2.cpp:36: note:                 bool sort_predicate::operator()(const std::string&, CParameter*&)
foo2.cpp:41: note:                 bool sort_predicate::operator()(CParameter*&, const std::string&)


So I added in my bool operator() (CParameter* const lhs, CParameter* const rhs) function (commented above), and commented my bool operator() (CParameter*& lhs, CParameter*& rhs) one (to avoid ambiguity errors between the two).  The code then worked on Solaris and OEL.

Poking at this a little more, I commented out the definitions of

bool operator() (const std::string& lhs, CParameter*& rhs)
bool operator() (CParameter*& lhs, const std::string& rhs)

...and added back my original

bool operator() (CParameter*& lhs, CParameter*& rhs)

and the code then started working Solaris but still failed on OEL, complaining the same as before:

/usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/bits/stl_algo.h:124: error: no match for call to '(sort_predicate) (CParameter* const&, CParameter* const&)'
candidates are: bool sort_predicate::operator()(CParameter*&, CParameter*&)


Right.  So, can someone explain

1) Why the differences in initial compile behaviour as above - is it as simple as putting it down to different implementations of the STL?
2) Why did removing the two operators that took a string and a CParameter help on Solaris?  (Or is it again STL implementation specific).
3) (Most importantly) I always thought that the signature of an operator() in a sort predicate like this should be (TYPE&, TYPE&) where TYPE is whatever is stored in the container: Windows seems to agree with me on this (but from past quesions here I know many people don't trust Windows...), so can someon tell me what is the standard signature for operator() that should be used?

Many thanks in advance :)
Avatar of jkr
jkr
Flag of Germany image

Try

#include <vector>
#include <algorithm>
#include <iostream>
#ifndef _WIN32
#include <strings.h>
#define stricmp strcasecmp
#endif

using namespace std;

class CParameter
{
public:
	CParameter(const char* pszName, int iLinenum) : m_strName(pszName), m_iLinenum(iLinenum) {}
	~CParameter() {}
	const std::string& GetName() const { return m_strName; }
	int GetLineNumber() const { return m_iLinenum; }
protected:
	int m_iLinenum;
	std::string m_strName;
};

struct sort_predicate
{

	bool operator() (const CParameter* const& lhs, const CParameter* const& rhs)
	{
		int nRet = stricmp(lhs->GetName().c_str(), rhs->GetName().c_str());
		return (nRet == 0) ? (lhs->GetLineNumber() < rhs->GetLineNumber()) : (nRet < 0);
	}
};

int main()
{

	std::vector<CParameter*> m_parameters, storage;
	std::string strParamName = "Second";
	m_parameters.push_back(new CParameter("C", 1));	
	m_parameters.push_back(new CParameter("B", 4));
	m_parameters.push_back(new CParameter("D", 2));
	m_parameters.push_back(new CParameter("A", 5));
	m_parameters.push_back(new CParameter("A", 3));

	std::sort(m_parameters.begin(), m_parameters.end(), sort_predicate());

	cout << "First: " << m_parameters[0]->GetName().c_str() << " at line number " << m_parameters[0]->GetLineNumber() << endl;
	cout << "Second: " << m_parameters[1]->GetName().c_str() << " at line number " << m_parameters[1]->GetLineNumber() << endl;
	
}
                                  

Open in new window


That works for me with both VC++ and g++. I basically gave the compiler what it was asking for in

/usr/lib/gcc/x86_64-redhat-linux/4.4.6/../../../../include/c++/4.4.6/bits/stl_algo.h:124: error: no match for call to '(sort_predicate) (CParameter* const&, CParameter* const&)'
candidates are: bool sort_predicate::operator()(CParameter*&, CParameter*&)


and adjusted the constness of your class' members.
Avatar of mrwad99

ASKER

Thanks; I am aware that I could make the code work by changing it in that way, but it concerned me since I cannot find anywhere that describes what the const-ness of the arguments to operator() must be: I looked at the definition of sort() at http://www.cplusplus.com/reference/algorithm/sort/?kw=sort, and it says that regarding the comparison method used, "The function shall not modify any of its arguments", yet it does not enforce that they be const.

Is there any documentation that describes this or is it just a case of compiling the code and seeing what different platforms require?
ASKER CERTIFIED SOLUTION
Avatar of jkr
jkr
Flag of Germany image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of mrwad99

ASKER

As has always been the case over the many years you have helped me, thanks :)