On Mon, 2015-08-17 at 10:38 -0400, Andrew Stitcher wrote: > 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.
I'll do you one better - C++ can use *either* (or boost shared/intrusive pointers) seamlessly if we say that all C++ smart pointers own a *proton reference*, not a *proton object*. Will have another proposal out shortly to clarify. > > 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.