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.

Reply via email to