简体   繁体   中英

Boost Python overriding equality operator

I'm trying to override the python equality operator for a class which I expose via boost python.

So my code looks something like:

   class_<MyClass, boost::noncopyable, boost::shared_ptr<MyClass> >("MyClass", no_init)
    .def("foo", &MyClass::foo)                                    
     .
     .
     .
    .def("__eq__", &MyClass::operator==) 
    .def("__ne__", &MyClass::operator!=)

Yet in python when I take 2 instances of the object which represent the same C++ object, but have arrived from different python objects, they are never equal...

Hence:

from myPackage import myClass

v1 = myClass.get("abc")
v2 = myClass.get("abc")
if v1 == v2:
   print "true"
else:
   print "false"

Always prints false. (I omitted the get function definition from the object for simplicity sake)

Any ideas?

Consider writing a C++ test case for MyClass::operator==() to verify its implementation. The Boost.Python code exposing the operators is correct.


Here is an example demonstrating exposing a C++ class' comparison operators as the equality and inequality rich-comparison methods:

#include <iostream>
#include <string>
#include <boost/make_shared.hpp>
#include <boost/python.hpp>
#include <boost/shared_ptr.hpp>

class foo
{
public:
  foo(const char* value) : value_(value) {}
  foo(const foo&) = delete;
  foo& operator=(const foo&) = delete;

  bool operator==(const foo& rhs) 
  {
    std::cout << "foo::operator==()" << std::endl;
    return value_ == rhs.value_;
  }

  bool operator!=(const foo& rhs)
  {
    std::cout << "foo::operator!=()" << std::endl;  
    return !(*this == rhs);
  }

  std::string get_value() const     { return value_;  }
  void set_value(std::string value) { value_ = value; }

private:
  std::string value_;
};

boost::shared_ptr<foo> make_foo(const char* value)
{
  return boost::make_shared<foo>(value);
}

BOOST_PYTHON_MODULE(example)
{
  namespace python = boost::python;
  python::class_<foo, boost::shared_ptr<foo>, boost::noncopyable>(
      "Foo", python::no_init)
    .def("__init__", python::make_constructor(&make_foo))
    .def("__eq__", &foo::operator==)
    .def("__ne__", &foo::operator!=)
    .add_property("value", &foo::get_value, &foo::set_value)
    ;
}

Interactive usage:

>>> import example
>>> foo1 = example.Foo("abc")
>>> foo2 = example.Foo("abc")
>>> foo3 = example.Foo("def")
>>> assert(foo1 == foo1)
foo::operator==()
>>> assert(foo1 == foo2)
foo::operator==()
>>> assert(foo1 is not foo2)
>>> assert(foo1 != foo3)
foo::operator!=()
foo::operator==()
>>> foo1.value = foo3.value
>>> assert(foo1 != foo2)
foo::operator!=()
foo::operator==()
>>> assert(foo1 == foo3)
foo::operator==()

As observed in the above output, the C++ comparison operators were called from Python.


In the example, the make_foo() factory function creates unique C++ foo instances. Hence, I have opted to hide the implementation detail of the factory method to Python by wrapping make_foo() as a constructor and exposing it as the __init__ method. As demonstrated here , equality can still be checked if object creation occurs through a static method. On the other hand, if a static factory method like get() can return handles to existing foo instances, then one may expect that both equality and identity comparisons work on Foo Python objects (ie assert(Foo.get("abc") is Foo.get("abc")) . To return a reference to the same Python object, one would need to manage the associated PyObject .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM