From: dragoncoder on
Hi all, I have the following code.

// db_operators.h

#ifndef INCLUDED_DB_OPERATORS
#define INCLUDED_DB_OPERATORS

#include <cstring>

struct ecodb2{
unsigned long long id;
char code[21];
};

inline bool operator==(const ecodb2& lhs,
const ecodb2& rhs)
{
std::cout << "Some text" << std::endl;
return ((lhs.id == rhs.id) || !std::strcmp(lhs.code, rhs.code));
}

struct Compare
{
bool operator()(const ecodb2& lhs,
const ecodb2& rhs) const
{
bool retval = false;
if(lhs.id < rhs.id)
retval = true;
else if(lhs.id == rhs.id)
retval = std::strcmp(lhs.code, rhs.code);
return retval;
}
};

#endif

// main.cpp

#include <iostream>
#include <cstring>
#include <set>

#include <db_operators.h>

int main()
{
std::set<ecodb2, Compare> industry_codes;

ecodb2 val;
val.id = 1;

std::strncpy(val.code, "GOV", sizeof(val.code));
industry_codes.insert(val);

std::strncpy(val.code, "ABC", sizeof(val.code));

std::set<ecodb2>::iterator it = industry_codes.find(val);
if(it != industry_codes.end())
std::cout << "Found in set" << std::endl;
else
std::cout << "Not found in set" << std::endl;
}

While running this code, I expect to see Found in set (as either id or
code match meaning a perfect match) and also "Some text" being
printed, becasue set::find should call my custom operator==() function
to find a match but the output I get is "Not found in set" and also
"Some text" is not getting printed.

Please help.

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Bart van Ingen Schenau on
On Feb 3, 12:12 am, dragoncoder <pktiw...(a)gmail.com> wrote:
> Hi all, I have the following code.
>
> // db_operators.h
>
> #ifndef INCLUDED_DB_OPERATORS
> #define INCLUDED_DB_OPERATORS
>
> #include <cstring>
>
> struct ecodb2{
> unsigned long long id;
> char code[21];
>
> };
>
> inline bool operator==(const ecodb2& lhs,
> const ecodb2& rhs)
> {
> std::cout << "Some text" << std::endl;
> return ((lhs.id == rhs.id) || !std::strcmp(lhs.code, rhs.code));
>
> }
>
> struct Compare
> {
> bool operator()(const ecodb2& lhs,
> const ecodb2& rhs) const
> {
> bool retval = false;
> if(lhs.id < rhs.id)
> retval = true;
> else if(lhs.id == rhs.id)
> retval = std::strcmp(lhs.code, rhs.code);
> return retval;
> }
>
> };
>
> #endif
>
> // main.cpp
>
> #include <iostream>
> #include <cstring>
> #include <set>
>
> #include <db_operators.h>
>
> int main()
> {
> std::set<ecodb2, Compare> industry_codes;
>
> ecodb2 val;
> val.id = 1;
>
> std::strncpy(val.code, "GOV", sizeof(val.code));
> industry_codes.insert(val);
>
> std::strncpy(val.code, "ABC", sizeof(val.code));
>
> std::set<ecodb2>::iterator it = industry_codes.find(val);
> if(it != industry_codes.end())
> std::cout << "Found in set" << std::endl;
> else
> std::cout << "Not found in set" << std::endl;
>
> }
>
> While running this code, I expect to see Found in set (as either id or
> code match meaning a perfect match) and also "Some text" being
> printed, becasue set::find should call my custom operator==() function
> to find a match but the output I get is "Not found in set" and also
> "Some text" is not getting printed.

Your custom operator==() will never be called, because all std::set
(and std::map) operations are defined in terms of the Compare object
that you provide (or operator< if you don't specify it).
Equality (or rather, equivalence) of two set elements A and B is
defined as
( not Compare(A,B) && not Compare(B, A) )
or in terms of ordering: neither comes before the other.

>
> Please help.
>
Bart v Ingen Schenau


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: tohava on
On Feb 3, 1:12 am, dragoncoder <pktiw...(a)gmail.com> wrote:
> Hi all, I have the following code.
>
> // db_operators.h
>
> #ifndef INCLUDED_DB_OPERATORS
> #define INCLUDED_DB_OPERATORS
>
> #include <cstring>
>
> struct ecodb2{
> unsigned long long id;
> char code[21];
>
> };
>
> inline bool operator==(const ecodb2& lhs,
> const ecodb2& rhs)
> {
> std::cout << "Some text" << std::endl;
> return ((lhs.id == rhs.id) || !std::strcmp(lhs.code, rhs.code));
>
> }
>
> struct Compare
> {
> bool operator()(const ecodb2& lhs,
> const ecodb2& rhs) const
> {
> bool retval = false;
> if(lhs.id < rhs.id)
> retval = true;
> else if(lhs.id == rhs.id)
> retval = std::strcmp(lhs.code, rhs.code);
> return retval;
> }
>
> };
>
> #endif
>
> // main.cpp
>
> #include <iostream>
> #include <cstring>
> #include <set>
>
> #include <db_operators.h>
>
> int main()
> {
> std::set<ecodb2, Compare> industry_codes;
>
> ecodb2 val;
> val.id = 1;
>
> std::strncpy(val.code, "GOV", sizeof(val.code));
> industry_codes.insert(val);
>
> std::strncpy(val.code, "ABC", sizeof(val.code));
>
> std::set<ecodb2>::iterator it = industry_codes.find(val);
> if(it != industry_codes.end())
> std::cout << "Found in set" << std::endl;
> else
> std::cout << "Not found in set" << std::endl;
>
> }
>
> While running this code, I expect to see Found in set (as either id or
> code match meaning a perfect match) and also "Some text" being
> printed, becasue set::find should call my custom operator==() function
> to find a match but the output I get is "Not found in set" and also
> "Some text" is not getting printed.

I don't think it should be possible for 2 variables
of type ecodb2 to be satisfy both x < y and x == y.
This can happen with your code (in the case
where x and y share an id but not a code). My guess
would be that find checks at some point for some x and y
whether x < y, and if this is the case, it assumes that x != y,
which in your example is NOT the case.

I would recommend implementing this using two maps,
one with id as key, and one with code.


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Goran on
On Feb 3, 12:12 am, dragoncoder <pktiw...(a)gmail.com> wrote:
> Hi all, I have the following code.
>
> // db_operators.h
>
> #ifndef INCLUDED_DB_OPERATORS
> #define INCLUDED_DB_OPERATORS
>
> #include <cstring>
>
> struct ecodb2{
> unsigned long long id;
> char code[21];
>
> };
>
> inline bool operator==(const ecodb2& lhs,
> const ecodb2& rhs)
> {
> std::cout << "Some text" << std::endl;
> return ((lhs.id == rhs.id) || !std::strcmp(lhs.code, rhs.code));
>
> }
>
> struct Compare
> {
> bool operator()(const ecodb2& lhs,
> const ecodb2& rhs) const
> {
> bool retval = false;
> if(lhs.id < rhs.id)
> retval = true;
> else if(lhs.id == rhs.id)
> retval = std::strcmp(lhs.code, rhs.code);
> return retval;
> }
>
> };
>
> #endif
>
> // main.cpp
>
> #include <iostream>
> #include <cstring>
> #include <set>
>
> #include <db_operators.h>
>
> int main()
> {
> std::set<ecodb2, Compare> industry_codes;
>
> ecodb2 val;
> val.id = 1;
>
> std::strncpy(val.code, "GOV", sizeof(val.code));
> industry_codes.insert(val);
>
> std::strncpy(val.code, "ABC", sizeof(val.code));
>
> std::set<ecodb2>::iterator it = industry_codes.find(val);
> if(it != industry_codes.end())
> std::cout << "Found in set" << std::endl;
> else
> std::cout << "Not found in set" << std::endl;
>
> }
>
> While running this code, I expect to see Found in set (as either id or
> code match meaning a perfect match) and also "Some text" being
> printed, becasue set::find should call my custom operator==() function
> to find a match but the output I get is "Not found in set" and also
> "Some text" is not getting printed.

Well, first things first: you should have gotten a warning (and you
should not have ignored it) on line that says

retval = std::strcmp(lhs.code, rhs.code);

That line makes your set ordering wrong. I guess it all goes downhill
form there.

To get correct ordering, use:

retval = std::strcmp(lhs.code, rhs.code)<0;

Goran.


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Paul Bibbings on
dragoncoder <pktiwary(a)gmail.com> writes:

> Hi all, I have the following code.
>
> // db_operators.h
>
> #ifndef INCLUDED_DB_OPERATORS
> #define INCLUDED_DB_OPERATORS
>
> #include <cstring>
>
> struct ecodb2{
> unsigned long long id;
> char code[21];
> };
>
> inline bool operator==(const ecodb2& lhs,
> const ecodb2& rhs)
> {
> std::cout << "Some text" << std::endl;
> return ((lhs.id == rhs.id) || !std::strcmp(lhs.code, rhs.code));
> }
>
> struct Compare
> {
> bool operator()(const ecodb2& lhs,
> const ecodb2& rhs) const
> {
> bool retval = false;
> if(lhs.id < rhs.id)
> retval = true;
> else if(lhs.id == rhs.id)
> retval = std::strcmp(lhs.code, rhs.code);
> return retval;
> }
> };
>
> #endif
>
> // main.cpp
>
> #include <iostream>
> #include <cstring>
> #include <set>
>
> #include <db_operators.h>
>
> int main()
> {
> std::set<ecodb2, Compare> industry_codes;
^^^^^
<snip></snip>

> }
>
> While running this code, I expect to see Found in set (as either id or
> code match meaning a perfect match) and also "Some text" being
> printed, becasue set::find should call my custom operator==() function
> to find a match but the output I get is "Not found in set" and also
> "Some text" is not getting printed.
>
> Please help.

I do not believe that std::set makes comparisons in the way you expect,
though I am not near my copy of the standard to check out the exact
details. Were you not to provide the second template parameter
`Compare', as you do above, then this would default to std::less. I would
suspect that set::find uses the same method as is used also for
ordering of items in the set, probably - where the default is used -
applying some such model of equality as:

!(rhs < lhs && lhs < rhs)

using std::less.

Thus, when leaving set to use the default comparator, std::less, you
would take control by implementing op< and not op==. However, in
providing your Compare class you are effectively saying that you want
this to play the role that std::less would have played otherwise,
effectively translating equality in this instance to something like:

!(aCompare(rhs, lhs) && aCompare(lhs, rhs))

Either way, and whatever the details are, it seems clear that set::find
is not using op== to test equality, and so your custom op== is not not
found because it is not a good match - it is not even looking for it.

Step through your code in a debugger and I am sure you will see that
set::find is using Compare::op() to handle equality testing. What is
more, take out the second template argument to the declaration of your
set, so that this becomes:

std::set<ecodb2> industry_codes;

and I think that you will find that you compiler complains about a
missing op<, not op==.

Try either something along the lines of this model:

#include <iostream>
#include <set>

struct A {
A(int i): i_(i) { }
int i_;
};

bool operator<(const A& lhs, const A& rhs)
{
return lhs.i_ < rhs.i_;
}

int main()
{
std::set<A> my_set;

my_set.insert(3);
my_set.insert(7);
my_set.insert(1);

std::set<A>::const_iterator i = my_set.find(A(3));

if (i != my_set.end())
std::cout << "Gotcha!\n";
else
std::cout << "Hhm...\n";

return 0;
}

or else replace op< with:

struct Compare {
bool operator()(const A& lhs, const A& rhs) {
return lhs.i_ < rhs.i_;
}
};

and use:

std::set<A, Compare> my_set;

which is closer to your original attempt.

I hope this helps, and is even vaguely correct.

Regards

Paul Bibbings

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]