Boost logo

Boost :

From: Douglas Gregor (gregod_at_[hidden])
Date: 2002-07-01 21:11:10


On Monday 01 July 2002 09:06 pm, David Abrahams wrote:
> Oh, this is spooky. I am just implementing similar functionality in v2 of
> the Boost.Python library. In v1 it was called from_python.
>
> The problem: a Python object (usually managed via a PyObject*, but in
> Boost.Python managed with the wrapper class object) can in principle hold
> any C++ type. A major component of the Boost.Python library's functionality
> is the extraction of C++ objects from Python objects, and the wrapping of
> C++ objects into Python objects.
>
> So, should I call my from_python function variant_cast<> as well? I don't
> think that reads well. In particular, I think of a cast as generally being
> a peer-to-peer type transformation, whereas these "casts" are
> container-to-containee transformations. I was thinking of "extract":
>
> extract<int>(obj)
>
> Now, "extract<>" might be too-general a name to use in namespace boost, but
> I'm not sure that it is. I do think it works better than "variant_cast<>".
>
> Thoughts?
> Dave

Very interesting. I'm most interested in the peer-to-peer vs.
container-to-containee classification, because that really decides whether
"extract" or "variant_cast" is the better name.

How do we classify the relationship between a variant type and the value in
that variant type? Is the variant type just a holder for another type, or is
the variant type one of those other types, but with the decision made at
compile-time? Thinking in object-oriented terms, is a variant like this:

  class Variant { /* ... */ };
  class IntegerValuedVariant : public Variant { /* ... */ };
  class FloatValuedVariant : public Variant { /* ... */ };

Or is a variant more like this:

  class VariantValue { /* ... */ };

  class Variant {
  public:
    VariantValue* value;
    // ...
  };
  class IntegerValuedVariant : public VariantValue { /* ... */ };
  class FloatValuedVariant : public VariantValue { /* ... */ };

With the first view, a downcast from a Variant to one of the values it may be
is a peer-to-peer transformation, so if we think of a variant like this then
I believe variant_cast is the right name.

With the second view, a variant is a container-of-one. The name "extract"
makes perfect sense here, because it is accessing the only value in the
container.

I personally favor the first view, and therefore the name variant_cast. With
the variant type we're describing (I'll get to PyObject* in a moment...), I
think of the favorite expression example:

  class Plus;
  class Minus;
  typedef variant<Plus, Minus, double> expression;

Does the 'expression' type contain an expression or is it an expression? OO
textbooks would tell us to write the object hierarchy like this:

  class Expression { /* ... */ };
  class Plus : public Expression { /* ... */ };
  class Minus : public Expression { /* ... */ };
  class Literal : public Expression { /* ... */ };

Why? Because Plus is-a(n) Expression, and a Literal is-a(n) Expression.

With PyObject*, I think it is the same question. A PyObject* is a just a
placeholder for a type we can't determine statically (like 'Expression' in
the hierarchy above). When we know the type dynamically, we cast to that
type, just like we would dynamic_cast in the OO style.

PyObject*, variant, any -- they're all just union types. Is a union a
container of one?

        Doug


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