Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing comparison using a generic interface in C++

Let's say I'm working on a library that works on items of type Item. The main entry point is a class like:

class Worker
{
private:
  SomeContainer _c;
public:
    void add( const Item &i );
    void doSomething();
};

The doSomething() method looks at the added items, compares them, etc. and does something with them. So the Item class needs an operator==.

Now, I want to use this library in different environments and in each environment the implementation of the Item class is different. Therefore, Item needs a virtual comparison function:

class Item
{
protected:
    virtual bool _equals( Item &other ) = 0;
public:
    bool operator==( Item &other ) { return _equals( other ); };
};

And each environment has its own Item implementation. The library only knows about the Item class and the specific item classes are defined and implemented in the platform-specific applications using the library. In environment A it might be:

class AItem: public Item
{
private:
    bool _equals( Item &other );
    std::string _member;
...
};

and in environment B:

class BItem: public Item
{
private:
    bool _equals( Item &other );
    int _member;
...
};

What is now the best way to, for each environment, implement the comparison for use by the library? _equals is specified in the Item class , so its specific item implementations need to cast other to their own type.

In a given environment, different item types will not be used at the same time, so given that assumption, the following would be sort-of safe:

bool AItem::_equals( Item &other )
{
    return this->_member == static_cast<AItem &>(other)._member;
}

But it seems like a nasty solution because it allows a programmer using the library to implement a new environment to break things if he adds items of different types to the worker.

Other solutions I can think of are:

  • Use dynamic_cast.
  • Implement my own sort-of-RTTI by adding a member to the base Item class indicating the environment. This can then be used in the comparison function to see if other is of the same type. Not very elegant.
  • Implement all the types in the library and select the right one based on a #define at compile-time. This means the library itself must be extended for each environment.
  • Use a templatized worker.

But I feel there must be a more elegant solution. Maybe something completely different. What would you do?

like image 245
Marten Avatar asked Oct 15 '25 14:10

Marten


1 Answers

  • Wouldn't it better if Worker was templatized on Item instead of relying on inheritance? In general value semantic (and equality is at the earth of value semantic) doesn't work well with inheritance. In your specific example, I also fear truncation if SomeContainer does copy what it stored in it.

  • If you really need an operation which depend on the dynamic type of two objects, you should do search about "multiple dispatch" (or look in "Modern C++ Design", it has a chapter on this subject). There are several known techniques for this with different trade-off. One of the best known one is often associated with the Visitor Pattern. The simplest variant depend on knowing beforhand all the descendants of Item. (If you look at a description of the visitor pattern, take into account that both hierarchies of that pattern would be unified for your application).

Edit: here is the sketch of an example:

class ItemA;
class ItemB;

class Item
{
public:
    virtual bool equal(Item const&) const = 0;
protected:
    virtual bool doesEqual(ItemA const*) const { return false; }
    virtual bool doesEqual(ItemB const*) const { return false; }
};

class ItemA: public Item
{
public:
    virtual bool equal(Item const& other) const
    {
        return other.doesEqual(this);
    }
protected:
    virtual bool doesEqual(ItemA const* other) {
        return do_the_check;
    }
};

class ItemB: public Item
{
public:
    virtual bool equal(Item const& other) const
    {
        return other.doesEqual(this);
    }
protected:
    virtual bool doesEqual(ItemB const* other) {
        return do_the_check;
    }
};

bool operator==(Item const& lhs, Item const& rhs)
{
    return lhs.equal(rhs);
}
like image 167
AProgrammer Avatar answered Oct 18 '25 15:10

AProgrammer