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.