operator==

A

Andrea Crotti

Is it possible in theory to force the subclasses to have an operator==?

Now I wanted to define a generic == operator, and supposing Packet is
the superclass and fields:
std::vector<Serializable *> fields;

In serializable I also have defined
virtual bool operator==(const Serializable& other) { return false; }

So here it is

bool Packet::eek:perator==(const Packet& other) const
{
for (size_t i=0; i < fields.size(); ++i) {
if (! ((*fields) == (*other.fields))) {
return false;
}
}
return true;
}

and it doesn't work unfortunately.
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7ba6830 in Packet::eek:perator== (this=0x7fffffffd070, other=...) at src/Packet.cpp:22
22 if (! ((*fields) == (*other.fields))) {

the fields of the other object should be also set to correct memory
addresses, and thus it should be fine.
What could be wrong in this?
 
A

Andrea Crotti

Andrea Crotti said:
Is it possible in theory to force the subclasses to have an operator==?

Now I wanted to define a generic == operator, and supposing Packet is
the superclass and fields:
std::vector<Serializable *> fields;

In serializable I also have defined
virtual bool operator==(const Serializable& other) { return false; }

So here it is

bool Packet::eek:perator==(const Packet& other) const
{
for (size_t i=0; i < fields.size(); ++i) {
if (! ((*fields) == (*other.fields))) {
return false;
}
}
return true;
}

and it doesn't work unfortunately.
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7ba6830 in Packet::eek:perator== (this=0x7fffffffd070, other=...) at src/Packet.cpp:22
22 if (! ((*fields) == (*other.fields))) {

the fields of the other object should be also set to correct memory
addresses, and thus it should be fine.
What could be wrong in this?


Ok well I guess there is something wrong with this
bool CoordAns::eek:perator==(const CoordAns& other) const
{
for (size_t i=0; i < fields.size(); ++i) {
if (! ((*fields) == (*other.fields))) {
return false;
}
}
return true;
}

because if I try to use this function also in a specific class instead
of the superclass it still segfaults.
But why then?
Maybe hte pointers in other.fields don't point to something reachable in the
memory or something like this?
 
F

Fred Zwarts

Andrea Crotti said:
Is it possible in theory to force the subclasses to have an operator==?

Now I wanted to define a generic == operator, and supposing Packet is
the superclass and fields:
std::vector<Serializable *> fields;

In serializable I also have defined
virtual bool operator==(const Serializable& other) { return false; }

So here it is

bool Packet::eek:perator==(const Packet& other) const
{
for (size_t i=0; i < fields.size(); ++i) {
if (! ((*fields) == (*other.fields))) {
return false;
}
}
return true;
}

and it doesn't work unfortunately.
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7ba6830 in Packet::eek:perator== (this=0x7fffffffd070, other=...) at src/Packet.cpp:22
22 if (! ((*fields) == (*other.fields))) {

the fields of the other object should be also set to correct memory
addresses, and thus it should be fine.
What could be wrong in this?


Many things could be wrong. In particular in the code that you do not show.
Please, reduce your problem to a small program that exhibits the problem,
that we can compile and try out. Probably, in the process of reducing it, you
will already discover yourself where the problem is located.
Now we have to guess. Probably, one of the pointers in fields is invalid,
but you don't show us how you populate this vector.
 
A

Andrea Crotti

Fred Zwarts said:
Andrea Crotti said:
Is it possible in theory to force the subclasses to have an operator==?

Now I wanted to define a generic == operator, and supposing Packet is
the superclass and fields:
std::vector<Serializable *> fields;

In serializable I also have defined
virtual bool operator==(const Serializable& other) { return false; }

So here it is

bool Packet::eek:perator==(const Packet& other) const
{
for (size_t i=0; i < fields.size(); ++i) {
if (! ((*fields) == (*other.fields))) {
return false;
}
}
return true;
}

and it doesn't work unfortunately.
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7ba6830 in Packet::eek:perator== (this=0x7fffffffd070, other=...) at src/Packet.cpp:22
22 if (! ((*fields) == (*other.fields))) {

the fields of the other object should be also set to correct memory
addresses, and thus it should be fine.
What could be wrong in this?


Many things could be wrong. In particular in the code that you do not show.
Please, reduce your problem to a small program that exhibits the problem,
that we can compile and try out. Probably, in the process of reducing it, you
will already discover yourself where the problem is located.
Now we have to guess. Probably, one of the pointers in fields is invalid,
but you don't show us how you populate this vector.


Ok I wrote a small example
#include <vector>

class Interface;

class Base
{
protected:
std::vector<Interface *> fields;

public:
Base();
virtual bool operator==(const Base& other) {
for (size_t i=0; i < fields.size(); ++i)
if (! ((*fields) == (*other.fields)))
return false;

return true;
}
};

class Interface
{
public:
Interface();
// just an example, should be overloaded of course
virtual bool operator==(const Interface& other) { return false; }
};

class Ext : public Base
{
private:
Interface x;
Interface y;

public:
Ext() {
fields.push_back(&x);
fields.push_back(&y);
}
};

int main() {

return 0;
}

this is basically what I'm doing, and now it doesn't compile though,
because I guess it doesn't find operator== as a possible method of Ext
(even if it's virtual).

Any idea on how to do this?
 
A

Andrea Crotti

Daniel T. said:
Yes. If the base class doesn't have an op== or that op== is otherwise
inaccessible, then in order for users of the derived classes to compare
objects, an op== will need to be defined.

It is not possible to force them to have an op== if one is not needed
however.

Yes ok that's what I meant, thanks.
An important note, assuming that "Packet" is a subclass of
"Serializable," the function below is not an override of the function
above.


I didn't want to override it, I wanted to make it available for the
subclasses...
 
A

Andrea Crotti

Daniel T. said:
Below is a version of your example that compiles. The reason it didn't
compile was because you were trying to use operator==(const Interface&)
before it was declared.

The code below does not exhibit the problem you are having with your
actual code. That should tell you that the Base::eek:p==(const Base&)
function is not where your problem lies.

Chances are that somewhere in your code (not shown to us so far,) you
are putting an object into the vector, then destroying it without
removing it from the vector. Although, your problem could be more
pernicious than that.

class Interface;

class Base {
protected:
std::vector<Interface*> fields;
virtual bool operator==(const Base& other);
public:
Base();
};

class Interface {
public:
Interface();
// just an example, should be overloaded of course
virtual bool operator==(const Interface& other) { return false; }
};

bool Base::eek:perator==(const Base& other) {
for (size_t i=0; i < fields.size(); ++i)
if (! ((*fields) == (*other.fields)))
return false;
return true;
}

class Ext : public Base {
private:
Interface x;
Interface y;

public:
Ext() {
fields.push_back(&x);
fields.push_back(&y);
}
};

int main() {
}

There are also problems with the above conceptually. For example:

class Derived1 : public Interface {
bool operator==(const Interface&);
}

class Derived2 : public Interface {
bool operator==(const Interface&);
};

int main() {
Derived1 d1;
Derived2 d2;
Interface* i1 = &d1;
Interface* i2 = &d2;
*i1 == *i2; // this will call a different function than
*i2 == *i1; // this.
}

All of your op==(const Interface&) functions will have invariants tying
them together, but they are in different classes. This is not a good
thing. My guess is, that at a minimum, you want the base class op== to
ensure that the two objects passed in are of the same type, returning
true if they are, false otherwise. Then each derived class will need to
call it's base op== and only do it's own checks if the base's op==
returns true.


Thanks right I always forget this detail when I write small examples...

Anyway so yes I get your point, but the thing is that I will never try
to say

PacketType1 obj1;
PacketType2 obj2;
obj1 == obj2

I can even have an assertion that the type of the things I'm checking
are exactly the same.

If I can write a generic == method for every packet than I spare a lot
of code and possible mistakes.
 
J

James Kanze

Andrea Crotti <[email protected]> wrote:

[...]
All of your op==(const Interface&) functions will have invariants tying
them together, but they are in different classes. This is not a good
thing. My guess is, that at a minimum, you want the base class op== to
ensure that the two objects passed in are of the same type, returning
true if they are, false otherwise. Then each derived class will need to
call it's base op== and only do it's own checks if the base's op==
returns true.

I'd do just the opposite:

class Base
{
virtual bool isEqual(Base const& other) const = 0;
public:
bool operator==(Base const& other) const
{
return typeid(*this) == typeid(other)
&& isEqual(other);
}
};

Then override isEqual in all of the derived classes, converting
other to the correct type and doing the comparison.
 
A

Andrea Crotti

James Kanze said:
I'd do just the opposite:

class Base
{
virtual bool isEqual(Base const& other) const = 0;
public:
bool operator==(Base const& other) const
{
return typeid(*this) == typeid(other)
&& isEqual(other);
}
};

Then override isEqual in all of the derived classes, converting
other to the correct type and doing the comparison.

I don't see the advantage, then I still have to implement the op== in
all my subclasses, which is very redundant and useless since I already
know that they're are equal if all the fields are equals...
 
A

Andrea Crotti

Obviously, if there is a batch of code that all op== functions have in
common, then you should write a function that takes care of that, but it
need not be an op==.

Why shouldn't it be op==?
Anyway now my code doesn't segfault anymore, the only problem I have is
that it calls
operator== from

class Serializable
{
public:
Serializable() {}
virtual Stream toStream() const = 0;
virtual bool operator==(const Serializable& other) { return false; };
};


and the thing is that if I make it pure then I have to change all the
operator== in the subclasses of Serializable, otherwise the signature is
different.

Given something from
std::vector<Serializable *>

is it not possible to call the more specialized method of the object?
I mean if that pointer points to an object of a subclass of
Serializable, if I was able to call that operator== I would be fine and
everything would work...
 
J

James Kanze

I don't see the advantage, then I still have to implement the
op== in all my subclasses, which is very redundant and useless
since I already know that they're are equal if all the fields
are equals...

But how do you know that all the fields are equal?

If you want to support comparison for equality in all of the
derived classes, you have to implement some function which
defines equality for those classes. There is no other way;
there is no implicit definition of equality in C++.

If you want to compare for equality over a hierarchy, you also
need to implement the case where the two classes are not the
same. Most of the time (but not always), you can get by with
something like the above, and assume that if the two objects
have different types, they aren't equal. (When you can't, you
need something more complex; some form of double dispatch.)
 
A

Andrea Crotti

James Kanze said:
But how do you know that all the fields are equal?

If you want to support comparison for equality in all of the
derived classes, you have to implement some function which
defines equality for those classes. There is no other way;
there is no implicit definition of equality in C++.

If you want to compare for equality over a hierarchy, you also
need to implement the case where the two classes are not the
same. Most of the time (but not always), you can get by with
something like the above, and assume that if the two objects
have different types, they aren't equal. (When you can't, you
need something more complex; some form of double dispatch.)

Yes sure that I know, in theory every subclass of Serializable also
redefines operator==.
The problem is here that the signatures are different so unless I change
all of them with

bool operator==(const Serializable& other)
it doesn't work.

I was trying with a templated function maybe that works...
template<typename T>
bool packetEqualTo(const T& first, const T& second) {
assert(first.fields.size() > 0);
assert(first.fields.size() == second.fields.size());

for (size_t i=0; i < first.fields.size(); ++i) {
if (! ((*first.fields) == (*second.fields))) {
return false;
}
}
return true;
}

and then I call packetEqualTo in all the subclasses...
 
A

Andrea Crotti

A couple of important questions. Does your Base class contain data
members? Does at least one derived class contain data members? Are there
any derived classes of derived classes?

Ein moment I think I'm almost there, but just a simple question
I get this error:
/root/pad_mobility/include/Serializable.hpp:16: undefined reference to `vtable for Serializable'
/root/pad_mobility/include/Serializable.hpp:14: undefined reference to `vtable for Serializable'
mobpad-test.o:(.rodata._ZTV10RingBufferIP6PacketE[vtable for RingBuffer<Packet*>]+0x18): undefined reference to `Serializable::eek:perator==(Serializable const&) const'
mobpad-test.o:(.rodata._ZTV9BasicTypeIiE[vtable for
BasicType<int>]+0x18): undefined reference to
`Serializable::eek:perator==(Serializable const&) const'

but only for two classes, which are templated and all defined in the
header file.
So the question is if that could be the problem or maybe also the other
classes would not work?

I also moved the operator== out of the declaration now but it's the
same...

For example here is one of the two classes that generates the error:

template<class T>
class BasicType : public Serializable
{
private:
T value;

public:
BasicType() {}
BasicType(T _value) : value(_value) {}

BasicType& operator=(const T &rhs) {
// Check for self-assignment!
if (this == &rhs)
return *this;

value = rhs;
return *this;
}

Stream toStream() const {
return Stream::fromBasic<T>(value);
}

static BasicType<T> parseStream(Stream& raw) {
BasicType<T> res;
res.value = raw.toBasic<T>();
return res;
}

bool operator==(const BasicType<T>&) const;
T getValue() const { return value; }

// see here about why we need the double template declaration
// http://stackoverflow.com/questions/4039817/friend-declaration-declares-a-non-template-function
template<typename X>
friend std::eek:stream& operator<<(std::eek:stream&, const BasicType<X>&);
};

template<typename T>
std::eek:stream& operator<<(std::eek:stream& s, const BasicType<T>& basic)
{
s << basic.value;
return s;
}

template<typename T>
bool BasicType<T>::eek:perator==(const BasicType<T>& other) const
{
return (value == other.value);
}
 
J

James Kanze

Yes sure that I know, in theory every subclass of Serializable also
redefines operator==.
The problem is here that the signatures are different so unless I change
all of them with
bool operator==(const Serializable& other)
it doesn't work.

That's why I used a separate function.

But if you support == in the base class, why do you want to
support it in the derived class?
I was trying with a templated function maybe that works...
template<typename T>
bool packetEqualTo(const T& first, const T& second) {
assert(first.fields.size() > 0);
assert(first.fields.size() == second.fields.size());

for (size_t i=0; i < first.fields.size(); ++i) {
if (! ((*first.fields) == (*second.fields))) {
return false;
}
}
return true;
}

and then I call packetEqualTo in all the subclasses...

Which supposes that you have some mechanism to get the fields,
and that you only use the static type of the object. (But if
you're only using the static type, what's the point of
deriving?)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,968
Messages
2,570,154
Members
46,702
Latest member
LukasConde

Latest Threads

Top