I like the way you're thinking - I expect to have real time to look at your code Tomorrow/Wednesday.
One point that occurred to me over the weekend (that I think is probably incorporated in what you've done here). Is that C++ code never needs to use a shared_ptr to any Proton struct because Proton ref counts by itself. In other words the C++ ref count could only ever by 0 or 1. All the C++ code ever needs is a unique_ptr. I suspect this point doesn't really affect your proposal here though. Andrew On Sat, 2015-08-15 at 06:09 -0400, aconway wrote: > In case you spotted the bug in the previous proposal here is a much > better one. This one doesn't have code yet but you can imagine how it > would work based on the previous code. I'll post updated code > shortly. > > Updated proposal to integrate C++ and proton C memory management. > > - use refcounting consistently, no pn_free. Fixes bug in the previous > proposal. > - added pn_shared_ptr for portable refcounting in C++11 and C++03. > - better integration with std:: and boost:: smart pointers in C++11 > and > C++03. > > The idea is that every ::pn_foo_t type has a corresponding C++ > proton::foo class with member functions so you can do `foo* p=...; p > ->something()` in C++ and it will call `::pn_foo_something()` on the > underlying `::pn_foo_t`. > > The first trick: the foo class is *empty and never instantiated*. A > foo* returned from the C++ API points directly to the raw C `struct > pn_foo_t`. You can reinterpret_cast between the two if you want to > mix > C and C++ APIs (you don't really want to.) Doing `foo* p=...; delete > p` > will actually call pn_object_decref(reinterpret_cast<void*>(p)). > > The next trick: proton:: functions return proton::pn_shared_ptr<foo>, > a > smart pointer using proton refcounts directly > (pn_object_incref/decref) > It is portable and useful as-is in c++03 and c++11, but is not as > featureful as the std:: and boost:: smart pointers. > > However it can be converted to any of std::unique_ptr, > std::shared_ptr, > std::auto_ptr, boost::shared_ptr and boost::intrusive_ptr safely with > correct refcounting. Each unique_ptr, auto_ptr or shared_ptr family > owns a proton refcount (not the actual proton object) so it is safe > to > have multiple unique/shared_ptr to the same underlying proton object. > > So some examples, given: > > class foo { pn_shared_ptr<foo> create(); ... } > class event { pn_shared_ptr<foo> foo(); } > event e; > > This works in C++11: > > - std::shared_ptr<foo> p = e.foo(); // shared_ptr refcounts > integrated > with proton > - std::unique_ptr<foo> p = foo::create(); > > These are all safe and portable in C++03 or C++11: > > - e.foo()->somefunc(); // call direct, no > refcounting. > - pn_shared_ptr<foo> p = e.foo(); // use pn_shared_ptr > directly. > - std::auto_ptr<foo> p = foo::create(); // portable but deprecated in > C++11 > - boost::intrusive_ptr<foo> p = e.foo() // use proton refcount > directly. > - boost::shared_ptr<foo> p = e.foo() // boost refcounts integrated > with proton > > The following are *unsafe* but legal in all C++ versions: > > - foo* p = e.foo(); // unsafe, p may be invalidated by later > proton actions. > - foo* p = foo::create(); // unsafe, p will not automatically be > freed. > > There is almost no overhead compared to using the raw C interface. > If > you use boost|std::shared_ptr it will allocate an internal counter > per > pointer *family* (not instance) which is not much overhead, otherwise > there are 0 extra allocations. The the template magic will evaporate > at > compile time. > > NOTE: proton::functions will take foo& as a parameter so you can > always > pass *p for any pointer type. > > On Fri, 2015-08-14 at 20:52 -0400, aconway wrote: > > I have a proposal to integrate C++ and proton C memory management, > > I > > need a sanity check. > > > > Attached is an executable C++ sketch and test (pn_ptr.cpp) and a > > script > > (test.sh) that runs the combinations of g++/clang++ and > > c++11/c++03, > > as > > well as some tests to verify that we get compile errors to prevent > > mistakes. > > > > The idea is that every pn_foo_t type has a corresponding C++ foo > > class > > with member functions to make it easy to call pn_foo_* functions in > > C++ > > (converting std::string/char* etc.) > > > > The first trick: the foo class is empty and never instantiated. A > > foo* > > actually points to the same memory location as the pn_foo_t*. You > > can > > reinterpret_cast between the two, and deleting the foo* will > > actually > > call pn_foo_free(pn_foo_t*). > > > > The next trick: proton::event accessor functions return a > > pn_ptr<foo>, > > which is an internal class that users cannot instantiate. What they > > can > > do is convert it by assignment or construction to any of: foo*, > > std::auto_ptr<foo>, std::unique_ptr<foo> or std::shared_ptr<foo>. > > In > > the shared_ptr case the conversion automatically does a > > pn_incref(). > > > > The upshot of this is that you can use plain foo* or any of the > > std::smart pointers to point to a foo and it Just Works. If you > > don't > > use shared_ptr you need to understand the proton C API lifecycle > > rules, > > but with shared_ptr it is all fully automatic. > > > > Moreover if you don't use shared_ptr there is almost no overhead > > over > > using pn_foo_t* directly in the C API, as the compiler should > > optimise > > away all the inline template magic. > > > > This works with c++11 (everything works) or c++03 (just foo* and > > auto_ptr). It will be trivial to add support for boost::shared_ptr > > so > > nice memory management will work with c++03 and boost.