Here's the API doc, the code is coming. It is like previous proposals
which were well received but more correct. It has *two* conversion
types, pn_borrowed_ptr and pn_given_ptr. This both automates the
refcounting for smart pointers and acts as "hard" documentation of the
ownership rules for plain pointers. Conversion types like this are not
a new idea - CORBA used to have something like it.


/**
@page memory_management

The C++ proton binding offers the following options, in decreasing order of
safety and increasing order of complexity. You don't need to understand the more
complex options to use the simpler ones.

1. memory-safe use with smart pointers (std::shared_ptr, std::unique_ptr, 
boost::shared_ptr, boost::intrusive_ptr)
2. unsafe plain-pointer use to avoid smart pointer overheads.
3. exchange of pointers between C and C++ code.

In this discussion `foo` is a placeholder for any proton C++ class such as
`proton::reactor::link` or `proton::reactor::session`.

## Safe smart-pointer use.

In the headers and API doc you will see `pn_borrowed_ptr<foo>` and
`pn_given_ptr<foo>` as return and argument types. Do *not* use them in your own
code. Just pass or assign to the smart pointer of your choice. You can use
std::shared_ptr, std::unique_ptr, boost::shared_ptr, or boost::intrusive_ptr and
it will Just Work.

If you are stuck with old C++ and no `boost` library you can use
`proton::pn_shared_ptr` in your code. It does not have all the features of
std::shared_ptr (no weak_ptr) but does provide basic memory safety.

## Unsafe plain pointer use.

If you use plain pointers there are no extra reference counts or allocations
compared to using the C API directly. Note that you can use plain pointers in
critical code and smart pointers elsewhere with the usual rules. If you use
smart pointers there are no extra costs except the normal costs associated with
the smart pointer type you choose.

Use plain `foo*` in your own code. As always with old-school memory management
you must arrange to `delete` it (or not) according to proton's rules. However
you can use any mechanism (e.g. std::auto_ptr) that calls `operator delete` to
do so. The rules are clear from the types used in proton function declarations.

If a proton function returns `pn_given_ptr<foo>` then *you own* the returned
pointer (it was "given" to you) so you must arrange to `delete` it. For example:

    // From session.hpp: pn_given_ptr<receiver> session::create_receiver(...)

    proton::reactor::receiver* r1 = session->create_receiver(...);
    std::auto_ptr<proton::reactor::receiver> r2(session->create_receiver(...));
    ...
    delete r1;
    // r2 cleaned up by auto_ptr

If a proton function returns `pn_borrowed_ptr<foo>` then you *do not own* the
pointer (it is "borrowed".) You may use it in some limited scope (see the docs)
but must not delete it, and must assume it is invalid outside that scope. For
example:

    class my_handler : public proton::messaging_handler {
        ...
        void on_link_opened(proton::event &e) {
            proton::link* l = e.link()
            // Use it but don't delete it.
            ...
            // On return, l is invalid. Don't save it for later use. If you
            // need to, use a smart pointer instead.
        }
    }

When you pass `foo*` to a proton function with a `pn_borrowed_ptr` parameter,
there is no change in ownership. If you owned it you still do, if not you still
don't. There are no proton functions taking `pn_given_ptr` parameters (at time
of writing.)

Note: if you do mix plain and smart pointers, it is best to assign to smart
pointers directly from proton function calls. If you assign to a foo*, and then
construct a smart pointer from that, you lose automatic safety. Your code will
still be correct if you follow the ownership rules, but for shared_ptr there is
some loss of efficiency.

## Gory details and mixed C/C++ (you don't have to read this but I know you 
will)

The secrets:

1. The public `C` API contains named but undefind C structs, e.g.  `struct
::pn_foo_t;` You can `reinterpret_cast<>` between `foo*` and `pn_foo_t*`. They
point at the same memory location (the C struct) so you can pass between C and
C++.

2. Given `foo* p`, calling `p->something(...)` is equivalent to the C call
`::pn_foo_something(reinterpret_cast<::pn_foo_t*>(p, ...))`

3. The `C` API has `pn_foo_free()` for traditional "free-style" memory
management, but it *also* has reference counts embedded in the C structs. Use
`pn_object_incref/decref()` to manipulate them. Regular proton C programmers
don't need to know or care, but we are in the basement now...

4. Proton over-rides `operator delete` so `delete p` is equivalent to
`pn_object_decref(reinterpret_cast<pn_foo_t*>(p))`

All polite C++ memory management, from raw `delete` to `shared_ptr`, uses
`operator delete` so proton refcounts are hooked into normal (smart or dumb)
C++ memory management.

When using a C API you must read and follow the documented ownership rules. C++
is better, we can *automate* the ownership rules with `pn_borrowed_ptr` and
`pn_given_ptr`. These are simple conversion wrappers to increase reference
counts (or not) so C++ smart pointers dont "steal" a reference and C++ plain
pointers have the same dumb semantics as C pointers.

OK, smarty-pants at the back is saying "but if you make a new shared_ptr on
every function call that's a new shared_ptr *family*, not just an *instance*!
That's horrible!" There is one more secret:

5. The first time someone wants a shared_ptr to a given proton object we
allocate a weak_ptr and stash it in a context slot on the object. Subsequently
we use that as a shared_ptr factory. Thus there is only one shared_ptr family
per object, and only if you ask for it. 

If you need to know more, read the source!
*/

Reply via email to