Boost logo

Boost :

From: Andrei Alexandrescu (andrewalex_at_[hidden])
Date: 2002-04-19 20:30:09


"Dietmar Kuehl" <dietmar_kuehl_at_[hidden]> wrote in message
news:3CC0AB7E.5030509_at_yahoo.com...
> Andrei Alexandrescu wrote:
>
> > I found many of Fernando's arguments insightful;
>
> Unfortunately, you apparently didn't get his point.

Fortunately, according to himself, I did :o).

> Here is another
> stab: An example.

Thanks a lot for the concrete example. I hope it will help clarify a lot of
issues.

[snip -- read the original message to see the example]
> b. The smart pointers used are configurations from some standard (eg.
> boost) library:
>
> namespace DataSource {
> typedef std::smart_ptr<std::vector<int>, unchecked> smart_ptr;
> smart_ptr produce(std::string const& query);
> }
>
> namespace DataSink {
> typedef std::smart_ptr<std::vector<int>, checked> smart_ptr;
> void consume(smart_ptr const& data)
> }
>
> Since DataSource *knows* that it produces the data and has no point
> in checking this, their obvious choice is to use an unchecked smart
> pointer. DataSink, known to defend against user errors, of course
> uses a checked pointer. Although both implementations are based on
> the same smart pointer implementation, we are not better of than in
> case a!

This conjecture is wrong, and I will prove it with facts. We are much better
off than in example (a).

The two smart pointers are of different types, and this is a good thing. It
is a wonderful thing in fact. It gives you everything, without you having to
pay nothing for it.

Let me detail. In this particular example, crossing a checking policy
boundary is much like changing the privilege level in an OS: you want to do
some checks exactly when the crossing is made, not before, not after. This
is exactly what policy-by-policy copy allows you to do. If you cross
checking levels from pointers that can be null to pointers that cannot be
null by design, the stronger checking policy can do that check right then
and there, and throw an exception if the source is null.

At the same time, the storage policy and the ownerchip policies are the same
so the copy will be as efficient as it gets - in the particular case of
refcounting, the refcount will smoothly get incremented, and the underlying
pointer will be copied.

So in the end, the checking level has been smoothly and efficiently passed.
I see this as a very elegant design.

> c. There is a special name for *the* smart pointer to be used in
> interfaces (let's assume there are template typedefs; if there are
> not these can be emulated already):
>
> template <typename T> typedef smart_ptr<T, checked> shared_ptr;
>
> namespace DataSource {
> shared_ptr<std::vector<int> > produce(std::string const& query);
> }
>
> namespace DataSink {
> void consume(shared_ptr<std::vector<int> > const& data);
> }
>
> Although this shared pointer is not necessarily the optimal solution,
> for example DataSource may be unhappy about the pointer being checked
> although a specialization provided by the provider of 'std::vector'
> may actually override this default, it is clearly the right choice.
> ... and suddenly, things just fit together!

Well, when it comes about interfaces, agreement must be made on the data
types are exchenged. I think there's really nothing new here.

> Since some policies are basically
> derived from preferences, a policy-based smart pointer is a bad choice
> to be used in an interface: Different providers may have different
> preferences resulting in unnecessary incompatibilities.

Of course. Policies are an implementation device. In addition, the
policy-by-policy copying mechanism (on which I believe there is still some
confusion) does allow you an elegant method when you do need to cross from a
policy to another. That's pretty much it.

Andrei


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk