Extending Python with C++ singleton pattern using boost python lib

Alex Martelli aleaxit at yahoo.com
Thu Apr 26 08:27:16 EDT 2001


"Benjamin Geer" <benjamin.geer at btinternet.com> wrote in message
news:9c7uic$7vb$1 at uranium.btinternet.com...
> In article <9c0tdd02sa7 at news2.newsguy.com>, "Alex Martelli"
> <aleaxit at yahoo.com> wrote:
> > It's hard to avoid that: in general, it's VERY hard work to
> > wrap/expose/handle non-canonical C++ objects (ones that can't be
> > default-constructed, destructed, copied, assigned). Not just in/to
> > Python or in/to Boost Python specifically: non canonical objects are Bad
> > News in any substantial C++ system.
>
> It seems to me that this might be true if you pass objects by value and/or
> reference, but where's the difficulty if you only pass pointers?  A

Many "substantial C++ systems" are based on using _smart_ pointers
of some description, for example -- all the way between the standard
std::auto_ptr<> up to much more sophisticated versions.  In some
cases it's not a problem to translate between bare pointers and
smart versions, back and forth, but that's not always true.

Pointers may not be meaningfully transferable between subsystems, if
those subsystems might be running in different address spaces -- more
generally, persistence and 'streaming' issues may be source of several
kinds of problems here.


> singleton can surely make itself available exactly as described in Gamma
et. al,
> via a static method that returns a pointer to the instance, with very
> little effort required, either in the singleton or in its clients.
> Copying and assigning pointers is considerably easier than writing copy
> constructors and copy assignment methods...

Yes, but this is generally offset by the boilerplate needed to use
pointers effectively with C++'s generics.  Making a std::set of
pointers or a std::map from pointers to anything is often a bother,
for example (depending on how your std::less<> is implemented when
instantiated on pointers -- I _hear_ it said that Standard C++ does
mandate a std::less<> that will work universally, special-casing
all pointers as needed, but have seen no conforming implementation
on that point).  A canonical proxy is pretty much _standardized_
boilerplate (I think Boost offers such stuff all ready for reuse),
so the ease of implementation issue isn't really one.


> If you want polymorphism, you're forced to use pointers or references,

Smart-pointers allow pretty good polymorphism in today's C++, actually.

At the same time they can *STOP* the kind of polymorphism that may
well be thought of as C++'s single most destructive feature -- the
fact that an array-of-X can be passed where an array-of-Y is wanted
if X is-a Y (because of array->pointer transformation), but then
trying to _use_ elements of the alleged "array of Y" from within
the receiving function can cause unbounded amounts of destruction
(if your program just crashes, count yourself pretty lucky...).

If no "bare pointers" (nor "bare arrays" of course) are ever exposed
at the interface of any subsystem towards others, but only 'smart'
pointers (polymorphic but NOT allowing 'pointer arithmetic') or
containers (NOT polymorphic -- unless they are containers of [smart]
pointers...), you may manage to finesse this issue (without losing
polymorphism where it matters AND it's safe to use -- it's sad that
container-of-X IS-A container-of-Y does NOT follow from X IS-A Y,
but it _is_ generally true, and making believe otherwise doesn't
fix things).

> but references have to refer to something that lives either on the heap
> or on the stack.  If it lives on the stack, you have all the problems of
> copy constructors, etc.  If it lives on the heap, you have to use
> pointers anyway...

It's pretty easy to have references exposed at the interface of
subsystems rather than pointers -- look at the specifications for
the standard C++ library: std::vector::operator[], for example,
returns a reference.  What mechanisms are used inside a subsystem
to implement its published interfaces is a less important issue
than what ARE the interfaces between subsystems -- particularly
since you may always fix an inferior implementation within a
subsystem, if the interfaces were designed to allow that, while
changing the interfaces themselves may well be unfeasible (client
code not under your control may depend on the previously published
interfaces, so you're stuck with them).


> If you use garbage collection, there's no way to avoid pointers.

I never said you can avoid pointers completely when programming
in C++.  You _can_, at best, avoid *bare* pointers exposed on
interfaces between subsystems -- and, in your implementation of
those interfaces, use the many excellent tools provided by the
standard C++ libraries and others (including Boost) to keep
the pointers well-wrapped and under as much control as possible
(or, roll your own smarts -- only cost-justified for very large
projects, with special portability constraints, etc, etc).


> Finally, if you're using SWIG to wrap your Python extension, providing
> pointers to your objects is the the most straighforward approach, unless
> you want to write a lot of glue code by hand.

I do believe SWIG has no support for stuff in the standard C++
library (and other templates), so at the interface _to SWIG_ you
may have to restrict yourself to a C-flavoured C++ subset, in
which case no doubt pointers WILL have to abound, whether one
likes that or not (templates can often be seen as avoiding the
"by hand" part of that "write a lot of glue code by hand").  This
IS part of why some of us find Boost Python (or CXX, etc) to be
preferable tools for exposing to Python subsystems that were in
fact developed _in C++_: the "interface _to SWIG_" would otherwise
have to be a differently developed "glue code by hand", exposing
as bare pointers what is normally exposed in standard C++ library
terms (or via other kinds of smarts).

> So why not make all your copy constructors private, use pointers for
> everything, write singleton classes the way the Gang of Four suggest, and
> make a singleton available in a Python module?

I'm not saying it can't be done.  Having that pointer-to-singleton
exposed as a SWIG'ged opaque type is surely feasible, and the Python
side of things will not expose you to the language-specific risks
connected with C++ bare pointers anyway, of course.  But you're
still likely to be paying several other sorts of typical costs,
e.g. the "when does it go away" issue -- when you give out pointers
rather than proxies or other smart things to other C++-coded
subsystems, you lose the ability to keep track of who is using
your singleton where... you may not need that ability _today_,
but by exposing bare pointers on your interface you may be locking
yourself out of a typical enhancement-request that is not unlikely
to come tomorrow (yeah, yeah, I know all about not overdesigning
today, as tomorrow may never come, but, when thinking of interfaces
to major C++-coded subsystems, the future-change inertia may well
be predicted to be SO high as to make a _little_ "overdesign today"
to cover the most likely directions for change requests less of
a heresy than my liking for XP would like it to be:-).


Alex






More information about the Python-list mailing list