1

For the following two classes:

class A {
};

class B {
public:

    B()
    : a_(std::make_shared<A>())
    {} 

    std::shared_ptr<A> a_;
};

there is a following simple boost.python wrapping

A& get_a(const B& b) {
    return *b.a_; // intentionally, no check here; doesn't matter for the example
}

boost::python::class_<A, std::shared_ptr<A>>("A");

boost::python::class_<B>("B")
   .def("get_a", &get_a, boost::python::return_internal_reference<>());

In Python there is simple members' retrieval:

import ext
b = ext.B()
a1 = b.get_a()
a2 = b.get_a()

What I expect is

id(a1) == id(a2)

which doesn't hold for the above case which means that two different PyObject as A wrappers are created. Why? Doesn't return_internal_reference policy prevents from creating multiple temporary objects?

If I return std::shared_ptr<A> from get_a, the programme fails to compile.

This question has similarities to this one. However, in the latter one there are temporary objects which are likely not to be kept track of. Here, on the other hand, member variables' wrappers are stored in python variables.

Community
  • 1
  • 1
Alexandra B.
  • 263
  • 2
  • 10

1 Answers1

3

The return_internal_reference policy allows returning pointers or references to internally held objects without having Boost.Python make a copy of the referent. It does not imply that subsequent calls will return identical Python objects.


Here is an example demonstrating that the C++ object are not being copied when using return_internal_reference, and that multiple discrete Python objects may embed a reference to the same C++ object:

#include <boost/python.hpp>
#include <cstdint> // std::uintptr_t

struct spam
{
  uintptr_t id() { return reinterpret_cast<uintptr_t>(this); }
};

struct egg {};

spam& get_spam(const egg&)
{
  static spam instance;
  return instance;
}

BOOST_PYTHON_MODULE(example)
{
  namespace python = boost::python;

  python::class_<spam>("Spam")
    .def("id", &spam::id)
    ;

  python::class_<egg>("Egg")
    .def("get_spam", get_spam, python::return_internal_reference<>())
    ;
}

Interactive usage:

import example
egg = example.Egg()
spam1 = egg.get_spam()
spam2 = egg.get_spam()
assert(spam1.id() == spam2.id()) # reference same C++ object
assert(id(spam1) != id(spam2))   # non-identical Python objects
Tanner Sansbury
  • 51,153
  • 9
  • 112
  • 169