[C++-sig] Reuse PyObject for non python instanciated C++ objects

Goode, Carl R (PG GT LGT EN MT 4 2) carl.goode at siemens.com
Wed Mar 25 20:45:28 CET 2015


I have a use case where we have a C++ framework that is exposed through boost python.  If I use a C++ factory to create objects, I want to always return the same python object once it is exposed.  I've done this through a postcall policy, but I was wondering if there is a better way.  Below is a simple example of my problem and my proposed solution.  This works, with the only drawback that I create a PyObject* that I throw out every time.  I do not want my framework (in this case, struct X) to know anything about python.  Also, I'm familiar with "has_backed_reference" and using a heldType wrapper, but that would only apply to objects instantiated through python.

struct X;
std::map<std::string, boost::shared_ptr<X> > xmap;
std::map<X*, PyObject*> xpymap;

struct X // a container element
{
    std::string id;

    std::string check;
    X(std::string s) : id(s) { }

    void printId() {
        std::cout << "ID is " << id << std::endl;
    }

    void change_string(std::string nstring) {
        check = nstring;
    }

    void print_string() {
        std::cout << "String is now: " << check << std::endl;
    }

    boost::shared_ptr<X> static XFactory(std::string val) {
      std::map<std::string,boost::shared_ptr<X> >::iterator it = xmap.find(val);
      if (it == xmap.end()) {
        boost::shared_ptr<X> nptr = boost::make_shared<X>(val);
        xmap[val] = nptr;
        return nptr;
      }

                return xmap[val];
    }
};


// Custom call policy to overwrite the postcall from C++ back to python //
template <typename BasePolicy = boost::python::default_call_policies>
struct carls_policy
  : BasePolicy
{
  template <typename ArgumentPackage>
  PyObject* postcall(const ArgumentPackage& args, PyObject* result)
  {

    // Chain to base policy.
    result = BasePolicy::postcall(args, result);

    // Extract shared_ptr from result //
    boost::shared_ptr<X> xptr = boost::python::extract< boost::shared_ptr<X> >(result);

    if (!xptr) {
       return result;
    }

    std::map< X*, PyObject* >::iterator it = xpymap.find(xptr.get());
    if (it == xpymap.end()) {
        xpymap[xptr.get()] = result;
        Py_INCREF(xpymap[xptr.get()]); // Increment to store in map
    } else {
        Py_DECREF(result); // Not needed anymore
    }

    Py_INCREF(xpymap[xptr.get()]); // Inremented to return to python
    return xpymap[xptr.get()];
  }
};

BOOST_PYTHON_MODULE(rvp)
{

        boost::python::class_<X, boost::shared_ptr<X> > ("X", boost::python::no_init)
                .def("XFactory", &X::XFactory, carls_policy<>()).staticmethod("XFactory")
                .def("printId", &X::printId)
                .def("change_string", &X::change_string)
                .def("print_string", &X::print_string)
                ;

}

Implementation:

>>> import rvp
>>> x1 = rvp.X.XFactory('id1')
>>> x2 = rvp.X.XFactory('id2')
>>> x3 = rvp.X.XFactory('id1')
>>> x1 is x3
True

This message and any attachments are solely for the use of intended recipients. The information contained herein may include trade secrets, protected health or personal information, privileged or otherwise confidential information. Unauthorized review, forwarding, printing, copying, distributing, or using such information is strictly prohibited and may be unlawful. If you are not an intended recipient, you are hereby notified that you received this email in error, and that any review, dissemination, distribution or copying of this email and any attachment is strictly prohibited. If you have received this email in error, please contact the sender and delete the message and any attachment from your system. Thank you for your cooperation
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/cplusplus-sig/attachments/20150325/e6a44065/attachment.html>


More information about the Cplusplus-sig mailing list