Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Why is 'Variant' used in so many contexts?

11 views
Skip to first unread message

Rune Allnor

unread,
Jul 16, 2007, 12:10:31 PM7/16/07
to
Hi all.

I have seen that the 'variant' type is available in a number
of contexts, including COM, Qt and boost. As I understand it,
the point of 'variant' is to avoid type checking etc.

Doesn't this violate the whole point with C++ and type checking?

The little experience I have with 'variant' is with COM, where
I find that virtually no documentation on data types is available,
and everything is passed in terms of 'variants.' While this
(apparently) works, I have no control about what happens.

So, what is the main benefit with 'variant' which justifies the
damage it apparently does by undermining the otherwise
rigorous C++ type system?

Rune


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Mathias Gaunard

unread,
Jul 18, 2007, 11:57:12 AM7/18/07
to
On 16 juil, 18:10, Rune Allnor <all...@tele.ntnu.no> wrote:

> I have seen that the 'variant' type is available in a number
> of contexts, including COM, Qt and boost. As I understand it,
> the point of 'variant' is to avoid type checking etc.

variant in all of those frameworks are very different things.


> Doesn't this violate the whole point with C++ and type checking?

At least not the boost one.


> The little experience I have with 'variant' is with COM

Well, COM isn't modern C++ to begin with, and represents all of the
practices that are to be avoided.


> So, what is the main benefit with 'variant' which justifies the
> damage it apparently does by undermining the otherwise
> rigorous C++ type system?

Boost variant is a type that can contain an object of any type in a
defined set. You can then visit the variant and do different things
depending on what the type of the underlying object is, by providing
an overloaded functor.
It is perfectly type-safe.

It is quite useful if you want a variable that can either hold an
object of one type or an object of another.
Plus it is highly efficient.

Thant Tessman

unread,
Jul 18, 2007, 3:12:47 PM7/18/07
to
Rune Allnor wrote:

> I have seen that the 'variant' type is available in a number
> of contexts, including COM, Qt and boost. As I understand it,
> the point of 'variant' is to avoid type checking etc.
>
> Doesn't this violate the whole point with C++ and type checking?
>

> The little experience I have with 'variant' is with COM, [...]

I know nothing about what a 'variant' is in COM, but the boost variant
is not about circumventing C++'s type system, but fixing it. A boost
variant is a statically-specified set of types across which a program
can dynamically dispatch.

http://en.wikipedia.org/wiki/Tagged_union

(As a rule, if a computer science term has more than one meaning, the
one that Microsoft adopts is the wrong one.)

-thant

Eugene Gershnik

unread,
Jul 19, 2007, 11:51:24 AM7/19/07
to

Rune Allnor wrote:
> Hi all.
>
> I have seen that the 'variant' type is available in a number
> of contexts, including COM, Qt and boost. As I understand it,
> the point of 'variant' is to avoid type checking etc.

Not really. Its point is to carry many different types together with
type information in the same storage. If the variant is well
implemented it will still do strict type checking.

> The little experience I have with 'variant' is with COM, where
> I find that virtually no documentation on data types is available,

http://msdn2.microsoft.com/en-us/library/ms221627.aspx
http://msdn2.microsoft.com/en-us/library/x295h94e(VS.80).aspx

> So, what is the main benefit with 'variant' which justifies the
> damage it apparently does by undermining the otherwise
> rigorous C++ type system?

There are two main benefits.
One is an ability to interface with languages that have weaker or
different type system. Using COM and variants you can have languages
like JavaScript or VisualBasic call C++ code and vice versa.
Another common usage is when you have an external data source (like
database, XML file etc.). You usually know the set of possible types
you can read from them but you may not know the exact type of the next
item.

--
Eugene

Mitesh

unread,
Jul 19, 2007, 11:57:13 AM7/19/07
to
On Jul 16, 9:10 pm, Rune Allnor <all...@tele.ntnu.no> wrote:
> Hi all.
>
> I have seen that the 'variant' type is available in a number
> of contexts, including COM, Qt and boost. As I understand it,
> the point of 'variant' is to avoid type checking etc.
>
> Doesn't this violate the whole point with C++ and type checking?
>
> The little experience I have with 'variant' is with COM, where
> I find that virtually no documentation on data types is available,
> and everything is passed in terms of 'variants.' While this
> (apparently) works, I have no control about what happens.
>
> So, what is the main benefit with 'variant' which justifies the
> damage it apparently does by undermining the otherwise
> rigorous C++ type system?
>
> Rune
>

I have used variant data type with respect to COM only. From my
experience with COM I infer that if a technology can be implemented in
different languages like C, C++, VB. There should be a common way in
which components created with these different langauges can pass
around data. So thus variant data type was created. If you use a
particular data type from C++ it cannot be understood by C or VB or
some other language.

variant is nothing but a union structure and all the above languages
recognize it. Thus a variant can be used for passing data around these
languages.

Dilip

unread,
Jul 19, 2007, 5:13:15 PM7/19/07
to
On Jul 18, 2:12 pm, Thant Tessman <a...@standarddeviance.com> wrote:
> Rune Allnor wrote:
> > I have seen that the 'variant' type is available in a number
> > of contexts, including COM, Qt and boost. As I understand it,
> > the point of 'variant' is to avoid type checking etc.
>
> > Doesn't this violate the whole point with C++ and type checking?
>
> > The little experience I have with 'variant' is with COM, [...]
>
> I know nothing about what a 'variant' is in COM,

In that case I don't know how you conclude that Microsoft did the
opposite of what a variant usually stands for?

> but the boost variant
> is not about circumventing C++'s type system, but fixing it. A boost
> variant is a statically-specified set of types across which a program
> can dynamically dispatch.
>
> http://en.wikipedia.org/wiki/Tagged_union

This is exactly what a COM variant is too. A discriminated union.

> (As a rule, if a computer science term has more than one meaning, the
> one that Microsoft adopts is the wrong one.)

Or else you could look at this Q&A for VARIANT's motivation:
http://www.microsoft.com/msj/0696/activex0696.aspx

jpal...@web.de

unread,
Jul 19, 2007, 8:31:21 PM7/19/07
to
On Jul 16, 6:10 pm, Rune Allnor <all...@tele.ntnu.no> wrote:
> Hi all.
>
> I have seen that the 'variant' type is available in a number
> of contexts, including COM, Qt and boost. As I understand it,
> the point of 'variant' is to avoid type checking etc.
>
> Doesn't this violate the whole point with C++ and type checking?
>
> The little experience I have with 'variant' is with COM, where
> I find that virtually no documentation on data types is available,
> and everything is passed in terms of 'variants.' While this
> (apparently) works, I have no control about what happens.
>
> So, what is the main benefit with 'variant' which justifies the
> damage it apparently does by undermining the otherwise
> rigorous C++ type system?

I guess in COM, it is used mostly to support VB and similar languages,
which feature untyped variables.

In boost, variant means a discriminated union of given types. So
you should use it when you have an exhaustive list of types that can
appear at some point in your program. It is type-safe, and, if you
use visitors for processing the variants, you can't even omit some
possibility, so you shouldn't be able to do it wrong.

As a real-life example of using it, consider a state with laws
aboud road traffic. They, among other things, classify vehicles
as cars, lorries, bikes, horses, etc. so you would use a variant
of those types. This is superior over deriving them from a common
interface, because

- it lets you, not the programmers of the vehicle classes, control
what a car can/can't, type of driving license needed etc.
- it indicates that the types of cars are exclusive, eg. there is
no vehicle which is a lorry and a motorbike at once
- it indicates that the enumeration is total, eg. there is no vehicle
not falling in one of these categories

boost::variant, if you use it correctly, is safe. If you use a variant
for any type just to avoid type checking... it is your choice.

Regards
Jiri Palecek

Thant Tessman

unread,
Jul 19, 2007, 11:04:31 PM7/19/07
to
Dilip wrote:
> On Jul 18, 2:12 pm, Thant Tessman <a...@standarddeviance.com> wrote:
>> Rune Allnor wrote:

[...]

>> http://en.wikipedia.org/wiki/Tagged_union
>
> This is exactly what a COM variant is too. A discriminated union.
>
>> (As a rule, if a computer science term has more than one meaning, the
>> one that Microsoft adopts is the wrong one.)
>
> Or else you could look at this Q&A for VARIANT's motivation:
> http://www.microsoft.com/msj/0696/activex0696.aspx

>From the article you referenced:

"The type tag's value must be chosen from a limited set of values (see
Figure 2). This list is fixed and is not user-extensible."

This makes them a very different beast than the algebraic data type for
which the boost library uses the term 'variant'. At best, Microsoft's
'variant' is just a particular instance of a variant.

More importantly, the original poster's comments seem to imply that the
Microsoft 'variant' doesn't provide the compiler with the type
information needed to guarantee there won't be a run-time type error.
That's not the case with the Boost variant. (The type-checking
capabilities of the Boost variant is a bit of a Goldberg contraption,
but it does seem to work.)

-thant

Kirit Sælensminde

unread,
Jul 20, 2007, 12:06:29 PM7/20/07
to
On Jul 16, 11:10 pm, Rune Allnor <all...@tele.ntnu.no> wrote:
> Hi all.
>
> I have seen that the 'variant' type is available in a number
> of contexts, including COM, Qt and boost. As I understand it,
> the point of 'variant' is to avoid type checking etc.
>
> Doesn't this violate the whole point with C++ and type checking?

C++ is an odd beast when it comes to type checking. It can be used
anywhere from very weakly typed to extremely strong typed. Variants
can be either a way to use weak typing (COM VARIANT) or strong typing
(Boost.Variant) - I've not used the Qt one so can't speak for it.

>
> The little experience I have with 'variant' is with COM, where
> I find that virtually no documentation on data types is available,
> and everything is passed in terms of 'variants.' While this
> (apparently) works, I have no control about what happens.
>
> So, what is the main benefit with 'variant' which justifies the
> damage it apparently does by undermining the otherwise
> rigorous C++ type system?

The problem with the COM variant is that it is used for several
different purposes which in an ideal world would be separated. For
example it used as a sort of Nullable<> wrapper for some determined
type (which is then lost), an Optional<> wrapper for another types
(which again is a lost), a discriminated union of types (which are not
recoverable) and to store some kinds of data structure (whose nature
is also lost).

Think about a COM VARIANT that might be a string, an int or a double.
Everytime you read the value you'll have something like:

switch ( variant_type( variant ) )
case is_int: do_int( variant );
case is_double: do_double( variant );
case is_string: do_strnig( variant );
case default:
throw "Hands in the air and run screaming";
};

Notice that the check here is done at run time, and the COM variant
could easily be something else.

Compare this with Boost.Variant which is used something like this:

typedef boost::variant< int, double, std::string > my_varaint;

struct process : public boost::static_visitor< void > {
void operator()( int i );
void operator()( double d );
void operator()( const std::string & );
};

// Here is the equivalent of the switch statement
boost::apply_visitor( process(), some_variant );

Notice that we've abstracted out the switch statement, and even better
we've swapped the runtime check for a compile time type check!

This is one of the powerful things you can do with a strongly typed
language which you cannot do with weakly typed languages, but also
notice that the use of the union/variant has nothing at all to do with
what the type system can do for you.


K

Thiruvalluvan M. G

unread,
Jul 20, 2007, 12:05:19 PM7/20/07
to
On Jul 16, 9:10 pm, Rune Allnor <all...@tele.ntnu.no> wrote:
> So, what is the main benefit with 'variant' which justifies the
> damage it apparently does by undermining the otherwise
> rigorous C++ type system?
>

One of the "benefits" of variant (COM and Qt variety, not Boost) is
that it can be used to tunnel objects through interfaces. That is, it
allows an "understanding" between the client and implementation
without changing the interface. It is very convenient to add new
functionality without modifying the interface. This technique is
highly error-prone and hard to document.

I've seen in my experience that once a tunnel is created, people start
misusing it sooner or later. It is hard to bring discipline.

Thiru

Eugene Gershnik

unread,
Jul 21, 2007, 3:33:34 AM7/21/07
to
On Jul 20, 9:06 am, Kirit Sælensminde <kirit.saelensmi...@gmail.com>
wrote:

> On Jul 16, 11:10 pm, Rune Allnor <all...@tele.ntnu.no> wrote:

> Variants
> can be either a way to use weak typing (COM VARIANT) or strong
> typing (Boost.Variant)

Not really. You can easily use COM variant in a strongly typed manner.
The difference is that you can also use it unsafely if you want to.

> Think about a COM VARIANT that might be a string, an int or a double.
> Everytime you read the value you'll have something like:

[...]

No. One time before using COM variant you write a visitor pattern
wrapper around it. It takes about an hour. Then your code looks
identical to the version using boost.

> Notice that we've abstracted out the switch statement, and even better
> we've swapped the runtime check for a compile time type check!

I didn't look at boost::variant implementation but I very strongly
suspect that all you did was to swap runtime check for a virtual
function call. Both are runtime things and impose some overhead.

--
Eugene

Kirit Sælensminde

unread,
Jul 21, 2007, 11:20:39 PM7/21/07
to
On Jul 21, 2:33 pm, Eugene Gershnik <gersh...@hotmail.com> wrote:
> On Jul 20, 9:06 am, Kirit Sflensminde <kirit.saelensmi...@gmail.com>

> wrote:
> > Think about a COM VARIANT that might be a string, an int or a double.
> > Everytime you read the value you'll have something like:
>
> [...]
>
> No. One time before using COM variant you write a visitor pattern
> wrapper around it. It takes about an hour. Then your code looks
> identical to the version using boost.

That still doesn't make it type safe in the same way because it
doesn't stop something else from being put into the variant. What
you're doing with your wrapper is to centralise the location of the
type check, which still occurs at run time. With the boost::variant
the type check occurs within the compiler and it is done on assignment
and when the item is pulled out.

>
> > Notice that we've abstracted out the switch statement, and even better
> > we've swapped the runtime check for a compile time type check!
>
> I didn't look at boost::variant implementation but I very strongly
> suspect that all you did was to swap runtime check for a virtual
> function call. Both are runtime things and impose some overhead.

No it doesn't. There are no virtual methods in the boost::variant code
and the type testing is done within the compiler. If you forget to
supply one of the members for a type wrapped in the variant you get a
compile time error. E.g.

struct process : public boost::static_visitor< void > {
void operator()( int i );
void operator()( double d );

//void operator()( const std::string & );
};

This will now fail with an error when it attempts to compile it.
Uncomment the std::string handler and it will compile again. However
if you comment out the int handler it will work as the int will be
promoted to double (again by the compiler).

It is possible to write a COM variant wrapper that does the same sort
of thing, but as you cannot restrict what is put into a COM variant
(at least if you're taking them from some external source which is the
only real reason to use them) you always have the possibility of a run-
time type failure that is not possible with the boost::variant.

By type tests here I'm not really talking about the sorts of things
that C++ developers think about as type checking. Strongly typed
languages can do much more than that.


K

--

jpal...@web.de

unread,
Jul 21, 2007, 11:35:15 PM7/21/07
to
On Jul 21, 9:33 am, Eugene Gershnik <gersh...@hotmail.com> wrote:
> On Jul 20, 9:06 am, Kirit Sflensminde <kirit.saelensmi...@gmail.com>

> wrote:
>
> > On Jul 16, 11:10 pm, Rune Allnor <all...@tele.ntnu.no> wrote:
> > Variants
> > can be either a way to use weak typing (COM VARIANT) or strong
> > typing (Boost.Variant)
>
> Not really. You can easily use COM variant in a strongly typed manner.
> The difference is that you can also use it unsafely if you want to.

No, you cannot. If you have a method in your interface that takes an
int or float (er, that takes only int), noone can stop the user of
your interface to pass a variant with, say IUnknown byref. This should
be impossible if you could use Variant type-safely.

> > Think about a COM VARIANT that might be a string, an int or a double.
> > Everytime you read the value you'll have something like:
>
> [...]
>
> No. One time before using COM variant you write a visitor pattern
> wrapper around it. It takes about an hour. Then your code looks
> identical to the version using boost.
>
> > Notice that we've abstracted out the switch statement, and even better
> > we've swapped the runtime check for a compile time type check!
>
> I didn't look at boost::variant implementation but I very strongly
> suspect that all you did was to swap runtime check for a virtual
> function call. Both are runtime things and impose some overhead.

No, boost::variant is implemented with ordinary case (or if) clauses,
no virtual functions are involved. The main difference is that the
compiler will complain if you try to pass an unexpected type through
the variant, as well as if you forgot to implement some handler in
your visitor.

Regards
Jiri Palecek

--

Mathias Gaunard

unread,
Jul 21, 2007, 11:49:00 PM7/21/07
to
On Jul 21, 9:33 am, Eugene Gershnik <gersh...@hotmail.com> wrote:

> No. One time before using COM variant you write a visitor pattern
> wrapper around it. It takes about an hour. Then your code looks
> identical to the version using boost.

Oh, so you spend time rewriting the safe boost dispatching system
through overloaded function objects every time you want to visit a COM
variant?


> I didn't look at boost::variant implementation but I very strongly
> suspect that all you did was to swap runtime check for a virtual
> function call. Both are runtime things and impose some overhead.

boost::variant couldn't use a virtual function, because virtual
functions can't be templated.
Since the whole point of boost::variant is that it works for generic
function objects, which can have any type, templates are required.
boost::variant uses switch, which is by the way, way more efficient
than virtual functions unless your optimizer is capable of virtual
function inlining.


--

Tony Delroy

unread,
Jul 23, 2007, 5:19:44 AM7/23/07
to
On Jul 20, 12:51 am, Eugene Gershnik <gersh...@hotmail.com> wrote:
> Another common usage [of variants] is when you have an

> external data source (like database, XML file etc.).
> You usually know the set of possible types you can read
> from them but you may not know the exact type of the
> next item.

This second usage is a key observation that's been only minimally
explained and explored in this thread.

Any program that needs to handle data of a type that won't be known
until run-time might reasonably consider using variants. Such
programs include language interpreters, database implementations,
database-tools that need to read metadata and work on arbitrary tables
that may have been defined after the tool was written.

To understand why variants are useful, ask yourself how you would
write an interpreter to read a script containing "string s; int i;" or
similar. You'd probably want to create a hash/binary-map from
identifiers "s", "i" to the variables, but they are of differing
types. You then end up choosing between:
1) iterating over a map per type, and handling them all differently in
high-level code
2) having one map, but to something that might contain a string or an
int, e.g.
a) store ints in strings? very inefficient!
for binary '+', need a bool to say it's an int
so you can choose between string concatenation and
integer addition
b) generalise the above into an enum for type and
a union
c) use a base class with derived String and Int
classes

Option b) is similar to the boost variant, and contrasted with c) in
the post by Jiri Palecek.

There are also a few variations on the "variant" concept though, known
by various often-conflicting names in different systems:

1) the discriminated union idea, e.g. enum { STRING, INT }
type_stored_; union { std::string* p_string_; int int_; } };
2) a class with a templated constructor/operator=, storing data of an
arbitrary type and keeping RTTI type information (IIRC only guaranteed
to be available for classes using virtual inheritance) for use by
later queries and accessors
3) a class with templated constructor/operator=, that uses the value-
to-store argument to instantiate a template with a non-templated base
class that allows a set of common operations over the stored data
(e.g. instantiating conversion to string via operator<<, or preserving
maths functionality by intantiating operator+ |-|*|/)

All have strengths and weaknesses: 1) works well but only for the
predefined types, 2) requires RTTI and the user may need to implement
the switching thereon, 3) works for any type that can be instantiated
for the set of required operations, but other functionality offered by
only some of the types may be hard to preserve.

There are also considerations re compilation firewalls and locality of
code. For example, option 3) requires a templated "variant" type, but
the base class through which the data is handled is of some known
type, so the user can write code to work on this base class that does
not itself need to be templated. Any new types that can be
instantiated can be handled without changes to or recompilation of the
"client" code. This kind of analysis can be preformed for all the
different options....

HTH

Cheers,

Tony

Eugene Gershnik

unread,
Jul 24, 2007, 3:24:19 PM7/24/07
to
On Jul 21, 8:49 pm, Mathias Gaunard <loufo...@gmail.com> wrote:
> boost::variant uses switch, which is by the way, way more efficient
> than virtual functions unless your optimizer is capable of virtual
> function inlining.

The repeated reference to switch made me curious so I spent some time
looking over the boost::variant code.
It is indeed a[n extremely ugly] switch that relies on preprocessor
tricks of all things. I was indeed deeply ignorant that such
preprocessor things are even possible. I wouldn't pass such code in a
code review or use it but that's just my personal opinion.

In any event now I am confident it is a *runtime* dispatch rather than
compile time thing as was claimed by Kirit.

--
Eugene

Eugene Gershnik

unread,
Jul 24, 2007, 3:20:53 PM7/24/07
to
On Jul 21, 8:49 pm, Mathias Gaunard <loufo...@gmail.com> wrote:
> On Jul 21, 9:33 am, Eugene Gershnik <gersh...@hotmail.com> wrote:
>
> > No. One time before using COM variant you write a visitor pattern
> > wrapper around it. It takes about an hour. Then your code looks
> > identical to the version using boost.
>
> Oh, so you spend time rewriting the safe boost dispatching system
> through overloaded function objects every time you want to visit a COM
> variant?

No I write it once.

> > I didn't look at boost::variant implementation but I very strongly
> > suspect that all you did was to swap runtime check for a virtual
> > function call. Both are runtime things and impose some overhead.
>
> boost::variant couldn't use a virtual function, because virtual
> functions can't be templated.

That's a non sequitur. The part after 'because' is true but it has no
relation to part before it.

> boost::variant uses switch,

This is either a complete nonsense or some deep ignorance on my part.
I don't see a way something like varaint's visitor could be
implemented as a switch. If you can point to a relevant lines in the
source code I would be very grateful.

>From a 10 seconds glance at boost::variant code found by google
(http://www.boost.org/boost/variant/visitor_ptr.hpp) it looks like it
is using function pointers to implement the visitor. If I am correct
my point stands. Instead of using a switch it uses a dispatch through
a function pointer which is a runtime thing. It *may* also be less
efficient as you pointed out.

--
Eugene

Eugene Gershnik

unread,
Jul 24, 2007, 3:22:55 PM7/24/07
to
On Jul 21, 8:20 pm, Kirit Sælensminde <kirit.saelensmi...@gmail.com>
wrote:

> On Jul 21, 2:33 pm, Eugene Gershnik <gersh...@hotmail.com> wrote:
>
> > On Jul 20, 9:06 am, Kirit Sflensminde <kirit.saelensmi...@gmail.com>
> > wrote:
> > > Think about a COM VARIANT that might be a string, an int or a double.
> > > Everytime you read the value you'll have something like:
>
> > [...]
>
> > No. One time before using COM variant you write a visitor pattern
> > wrapper around it. It takes about an hour. Then your code looks
> > identical to the version using boost.
>
> That still doesn't make it type safe in the same way because it
> doesn't stop something else from being put into the variant.

What do you mean by "something else"? The types COM variant supports
are known (see the link I gave elsethread) and that's exactly what you
can put there. If you want a more limited set of types you need to use
a different class.

> What
> you're doing with your wrapper is to centralise the location of
> the type check, which still occurs at run time. With the
> boost::variant the type check occurs within the compiler
> and it is done on assignment and when the item is pulled out.

Exactly the same applies to any discriminated union. I am not saying
that boost::variant is not safer against accidental misuse. All I am
saying is that you can use any discriminated union, including COM
variant in a typesafe manner.


> > I didn't look at boost::variant implementation but I very strongly
> > suspect that all you did was to swap runtime check for a virtual
> > function call. Both are runtime things and impose some overhead.
>
> No it doesn't. There are no virtual methods in the boost::variant code
> and the type testing is done within the compiler.

See my other replies.

> If you forget to
> supply one of the members for a type wrapped in the variant

This is a different matter and has *nothing* to do with when the
dispatch is performed.

> but as you cannot restrict what is put into a COM variant
> (at least if you're taking them from some external source which is the
> only real reason to use them) you always have the possibility of a run-
> time type failure that is not possible with the boost::variant.

I think this is the core of your misunderstanding. You cannot restrict
what you put in COM variant because the types it supports are fixed by
its definition. It is not a general purpose template like
boost::variant is but a specific instance of it.
If you don't like the set of types it requires you to support don't
use it. If you do like this particular set nothing prevents you from
using it in typesafe manner.

--
Eugene

Eugene Gershnik

unread,
Jul 24, 2007, 3:21:35 PM7/24/07
to
On Jul 21, 8:35 pm, jpale...@web.de wrote:
> On Jul 21, 9:33 am, Eugene Gershnik <gersh...@hotmail.com> wrote:
>
> > On Jul 20, 9:06 am, Kirit Sflensminde <kirit.saelensmi...@gmail.com>
> > wrote:
>
> > > On Jul 16, 11:10 pm, Rune Allnor <all...@tele.ntnu.no> wrote:
> > > Variants
> > > can be either a way to use weak typing (COM VARIANT) or strong
> > > typing (Boost.Variant)
>
> > Not really. You can easily use COM variant in a strongly typed manner.
> > The difference is that you can also use it unsafely if you want to.
>
> No, you cannot. If you have a method in your interface that takes an
> int or float (er, that takes only int), noone can stop the user of
> your interface to pass a variant with, say IUnknown byref. This should
> be impossible if you could use Variant type-safely.

Sorry but this is backwards. In C++ if you declare your function to
accept VARIANT you should support all types it is capable of
transferring. Otherwise use a different type.
It is true that other languages like, say, JavaScript frequently
accept VARIANTS that have to carry only specific types but we are not
talking about them or any inter-language code that has by necessity
play by their rules.
My point stands, You *can* use VARIANT safely or you might *choose* to
use it unsafely in order to talk to an unsafe code.


> No, boost::variant is implemented with ordinary case (or if) clauses,
> no virtual functions are involved.

Whether it is a switch (which I cannot believe in until somebody
explains how it is done), ifs, virtual function or a function pointer
(after the second glance it looks I misunderstood the purpose of the
boost::variant code I mentioned in my reply to Mathias) it has to be a
*runtime* thing. There is no way the visitor dispatch over something
that is only know at runtime can be done statically.


--
Eugene

Eugene Gershnik

unread,
Jul 24, 2007, 3:21:53 PM7/24/07
to
Small correction. It looks like I misunderstood the purpose of the
boost::variant code I looked on so please disregard my remark about
the function pointer.

Still, no matter how the dispatch is implemented it has to be a
*runtime* thing, not a compile time one.

--
Eugene

Kirit Sælensminde

unread,
Jul 25, 2007, 2:37:38 PM7/25/07
to
On Jul 25, 2:24 am, Eugene Gershnik <gersh...@hotmail.com> wrote:
> On Jul 21, 8:49 pm, Mathias Gaunard <loufo...@gmail.com> wrote:
>
> > boost::variant uses switch, which is by the way, way more efficient
> > than virtual functions unless your optimizer is capable of virtual
> > function inlining.
>
> The repeated reference to switch made me curious so I spent some time
> looking over the boost::variant code.
> It is indeed a[n extremely ugly] switch that relies on preprocessor
> tricks of all things. I was indeed deeply ignorant that such
> preprocessor things are even possible. I wouldn't pass such code in a
> code review or use it but that's just my personal opinion.
>
> In any event now I am confident it is a *runtime* dispatch rather than
> compile time thing as was claimed by Kirit.

What I was talking about was type safety. Clearly there must be a
mechanism to determine what is in the variant at runtime and execute
the correct function -- I would never claim that the dispatch is done
by the compiler as that would clearly be an impossible feat.

I only said that the dispatch wasn't a virtual. I haven't examined the
Boost variant code to see what is in there, but I can tell from how it
works that it couldn't use a virtual.

The point I was making has to do with type safety, NOT dispatch
mechanisms and when the type check is done.

A COM variant (especially one that arrives through a COM interface)
could contain literally anything. This means that there is always the
possibility of a type check failure at runtime. With the Boost
variant, because an incompatible type cannot be placed in it (a check
enforced by the compiler) it is also impossible to pull an
incompatible type out of it (again enforced by the compiler).

So, although the Boost variant must also work out which code to
execute at runtime, because of the way it works with the compiler it
can guarantee that there will never be a type failure at runtime.


K

Eugene Gershnik

unread,
Jul 25, 2007, 8:47:09 PM7/25/07
to
On Jul 25, 11:37 am, Kirit Sælensminde <kirit.saelensmi...@gmail.com>
wrote:

> A COM variant (especially one that arrives through a COM interface)
> could contain literally anything.

It can contain only the things mentioned its specification. The list
of types is large but it doesn't contain std::vector<std::string> for
example. If this list is not what you want then VARIANT is a wrong
type to use.
COM forces you to use VARIANT where it would not be a natural choice
to support interoperability with other languages. Such use of it is
the real problem (without a good solution).

> This means that there is always the
> possibility of a type check failure at runtime.

COM VARIANT is equivalent to boost::variant<char, int, short, float...
(all types supported by COM)>. If you use such an instantiation of
boost::variant you will have to do something reasonable for all
supported types as well. This includes failing at runtime.

> With the Boost
> variant, because an incompatible type cannot be placed in it (a check
> enforced by the compiler) it is also impossible to pull an
> incompatible type out of it (again enforced by the compiler).

See the instantiation above. It is not the compiler that saves you
from runtime error but your choice as a user to limit your
instantiation to, say, variant<int, float>.
You can create a discriminated union with just two types in it and it
will be as type safe.

> So, although the Boost variant must also work out which code to
> execute at runtime, because of the way it works with the compiler it
> can guarantee that there will never be a type failure at runtime.

So is

struct TinyVARIANT
{
enum { Int, Float } Type;
union
{
int intVal;
float floatVal;
} Data;
};

boost:variant<int, float> has many advantages over the above including
general safety of use but it is no more type-safe than a discriminated
union with the *same* set of types.

--
Eugene

jpal...@web.de

unread,
Jul 25, 2007, 8:51:37 PM7/25/07
to

It uses a switch generated by a preprocessor, I just checked it. You
can see it if you run some program using boost::variant (or maybe
boost/variant.hpp only) through the preprocessor and search for
"switch" in the output, you will recognise it when you'll see it.
I recommend running the output through indent, as preprocessor output
is generally without newlines.

The dispatch is, quite naturally, runtime. However, it is checked
in compile-time that the dispatch cannot fail.

Regards
Jiri Palecek

Kirit Sælensminde

unread,
Jul 26, 2007, 11:06:10 AM7/26/07
to
On Jul 26, 7:47 am, Eugene Gershnik <gersh...@hotmail.com> wrote:
> COM VARIANT is equivalent to boost::variant<char, int, short, float...
> (all types supported by COM)>. If you use such an instantiation of
> boost::variant you will have to do something reasonable for all
> supported types as well. This includes failing at runtime.

Indeed it does.

>
> > With the Boost
> > variant, because an incompatible type cannot be placed in it (a check
> > enforced by the compiler) it is also impossible to pull an
> > incompatible type out of it (again enforced by the compiler).
>
> See the instantiation above. It is not the compiler that saves you
> from runtime error but your choice as a user to limit your
> instantiation to, say, variant<int, float>.
> You can create a discriminated union with just two types in it and it
> will be as type safe.

Yes quite. Of course with the COM variant the choice is made for you,
with the Boost.Variant you can choose types to provide the level of
safety you require.

It's also worth noting that the COM variant supports types explicitly
meant to allow for holes to be punched through the type checking so it
would be possible to pass something like a std::vector< std::string >
through one. For example VT_BLOB or VT_USERDEFINED. The presence of
these means that it is impossible to actually guarantee that there
will never be a type violation at run time.

Of course you could do this too which would have the same problem:

boost::variant< void *, ... >

> > So, although the Boost variant must also work out which code to
> > execute at runtime, because of the way it works with the compiler it
> > can guarantee that there will never be a type failure at runtime.
>
> So is
>
> struct TinyVARIANT
> {
> enum { Int, Float } Type;
> union
> {
> int intVal;
> float floatVal;
> } Data;
>
> };
>
> boost:variant<int, float> has many advantages over the above including
> general safety of use but it is no more type-safe than a discriminated
> union with the *same* set of types.

Exactly. You can of course implement your own version of something
that acts like Boost.Variant and get the same benefits.


K

0 new messages