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
_______________________________________________
Cplusplus-sig mailing list
Cplusplus-sig@python.org
https://mail.python.org/mailman/listinfo/cplusplus-sig

Reply via email to