Boost logo

Boost :

From: Dietmar Kuehl (dietmar_kuehl_at_[hidden])
Date: 2002-04-19 18:42:54


Andrei Alexandrescu wrote:

> I found many of Fernando's arguments insightful;

Unfortunately, you apparently didn't get his point. Here is another
stab: An example. In an application data is obtained from some
external source (eg. a data base) in form of an 'std::vector<int>'.
The component doing this task is provided by the company DataSouce.
Due to the nature of the application, data is consumed also in the
form of 'std::vector<int>' by a component provided by the company
DataSink (OK, that's somewhat contrieved and I don't want to imply
that compatibility happens by accident - it happens by design; but
bear with me for a moment). Since neither of these companies discloses
their source nor would ship some form of precompiled template stuff
out of fear of reverse enginering we are stuck with a binary
interface.

Just to bring in smart pointers: The vector produced is rather large
and the component processing it is user GUI where the vector has to
be retained for some arbitrary time. Thus, both companies decided to
handle it via a smart pointer. Here are some possible interfaces:

a. The smart pointers used are custom classes, each written by the
    respective company:

      namespace DataSource {
        template <typename T> class smart_ptr { /*...*/ };
        smart_ptr<std::vector<int> > produce(std::string const& query);
      }

      namespace DataSink {
        template <typename T> class smart_ptr { /*...*/ };
        void consume(smart_ptr<std::vector<int> > const& data);
      }

    Obviously, this is the worst choice possible: The functions are
    basically guaranteed not to work together. This is clearly the
    road nobody wants to travel (well, there seem to be companies
    which think this a bright idea but fear ACCU conferences if you
    decide to go there :-) because it basically requires copying of
    on representation to another - just because the pointers mismatch
    while the data is perfectly compatible!

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! Chances are, at least, that DataSource accidentally shipped
    a version which happens to be checked, too, in which case the two
    components would magically fit together with *huge* performance
    benefits.

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!

This is, of course, a contrived example. In reality it is unlikely that
interfaces fit together by coincidence. However, the less choice there
is, the more likely interfaces fit! 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. On the other
hand, if there is a universally available configuration already provided
this is an obvious choice - although possibly suboptimal it is better
than introducing unnecessary incompatibilities.

This is, for the purpose of interfaces, a clear argument against a
policy-based approach (not just for smart pointer but in general). On
the other hand, a fixed approach like 'boost::shared_ptr' is also
inappropriate: This cannot cope with special requirements like eg. the
need to return 'this' from some member function. Also, the complexity
and the runtime characteristics suffer from making it suitable for as
many situations as possible. Thus, some form of configuration is clearly
desirable. This is where policy-based smart pointers again enter the
scene even for interfaces: the "standard" smart pointer is basically
just the name of a preconfigured version of the policy-based smart
pointer. It is still rather inflexible compared to a policy-based
smart pointer but in exchange it is compatible with other uses of the
same name. Since this is the primary goal when interfacing between
different components this is a major advantage.

I entirely agree that a policy-based smart pointer is much more flexible
and necessary, be it to cope with special situations internal to
components or even in special situations in interfaces (if the need for
difference outweight the compatibility issue) or to define the
"standard" smart pointer. It can deal with situations where the
"standard" smart pointer is simply inappropriate. But it is unsuitable
to be used in interfaces directly because it is likely to cause
unnecessary incompatibilities.

Please excuse the confusion I apparently caused with the subject line
which started this thread: It was intended to catch the eye (which it
obviously did :-) and I thought I made clear in the message body what
I ment (actually, the very first paragraph already provided the
disclaimer...).

-- 
<mailto:dietmar_kuehl_at_[hidden]> <http://www.dietmar-kuehl.de/>
Phaidros eaSE - Easy Software Engineering: <http://www.phaidros.com/>

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