Safe union of std::vectors

S

Simon Elliott

I'd like to do something along these lines:

struct foo
{
int i1_;
int i2_;
};

struct bar
{
double d1_;
double d2_;
};

typedef std::vector<foo> vectorFoo;
typedef std::vector<bar> vectorBar;

union vectorAll
{
vectorFoo foo_;
vectorBar bar_;
};

static vectorAll myVectorAll;
....
foo myFoo;
myVectorAll.foo_.push_back(myFoo);
myVectorAll.foo_.clear();
....
bar myBar;
myVectorAll.bar_.push_back(myBar);
myVectorAll.bar_.clear();



But a) I'm fairly sure that this isn't valid C++ because std::vector
must have a constructor, b) it seems very unsafe. and c) it precludes
sensible requirements such as clearing myVectorAll without knowing what
type is in it. (Intuitively one might want to be able to do a
myVectorAll.clear());

Given that one of my target compilers is too uncompliant to compile
boost, I can't use boost::any for this. What are my other options?
 
M

Mike Wahler

Simon Elliott said:
I'd like to do something along these lines:

struct foo
{
int i1_;
int i2_;
};

struct bar
{
double d1_;
double d2_;
};

typedef std::vector<foo> vectorFoo;
typedef std::vector<bar> vectorBar;

union vectorAll
{
vectorFoo foo_;
vectorBar bar_;
};

static vectorAll myVectorAll;
...
foo myFoo;
myVectorAll.foo_.push_back(myFoo);
myVectorAll.foo_.clear();
...
bar myBar;
myVectorAll.bar_.push_back(myBar);
myVectorAll.bar_.clear();



But a) I'm fairly sure that this isn't valid C++ because std::vector
must have a constructor,

It's invalid because std::vector *does* have a (non-trivial)
constructor (as well as other precluded member functions).
(C&V below)
b) it seems very unsafe.

Well, I suppose it you were able to convince a compiler
to create code for it, it would very likely be 'unsafe'. :)
and c) it precludes
sensible requirements such as

Such as violating the standard's rules. :)
clearing myVectorAll without knowing what
type is in it. (Intuitively one might want to be able to do a
myVectorAll.clear());

I wouldn't intiut that because the your union has no member
functions.
Given that one of my target compilers is too uncompliant to compile
boost, I can't use boost::any for this. What are my other options?

Don't use a union. :) How about a function template?
(Of course that's just a guess, without knowing more
about what you're trying to do.)

ISO/IEC 14882:1998(E)

9.5 Unions

1 In a union, at most one of the data members can be active
at any time, that is, the value of at most one of the data
members can be stored in a union at any time. [Note: one
special guarantee is made in order to simplify the use of
unions: If a POD­-union contains several POD­-structs that
share a common initial sequence (9.2), and if an object of
this POD­-union type contains one of the POD­-structs, it is
permitted to inspect the common initial sequence of any of
POD­struct members; see 9.2. ] The size of a union is suffi­-
cient to contain the largest of its data members. Each data
member is allocated as if it were the sole member of a struct.
A union can have member functions (including constructors
and destructors), but not virtual (10.3) functions. A union
shall not have base classes. A union shall not be used as a
base class. An object of a class with a non­-trivial constructor
(12.1), a non-­trivial copy constructor (12.8), a non­-trivial
destructor (12.4), or a non-­trivial copy assignment operator
(13.5.3, 12.8) cannot be a member of a union, nor can an
array of such objects. If a union contains a static data
member, or a member of reference type, the program is ill-­formed.


-Mike
 
M

msalters

Simon said:
I'd like to do something along these lines:

struct foo {...}
struct bar {...}
typedef std::vector<foo> vectorFoo;
typedef std::vector<bar> vectorBar;

union vectorAll
{
vectorFoo foo_;
vectorBar bar_;
};

static vectorAll myVectorAll;
...
foo myFoo;
myVectorAll.foo_.push_back(myFoo);
myVectorAll.foo_.clear();
...
bar myBar;
myVectorAll.bar_.push_back(myBar);
myVectorAll.bar_.clear();

But a) I'm fairly sure that this isn't valid C++ because std::vector
must have a constructor, b) it seems very unsafe. and c) it precludes
sensible requirements such as clearing myVectorAll without knowing what
type is in it. (Intuitively one might want to be able to do a
myVectorAll.clear());

Given that one of my target compilers is too uncompliant to compile
boost, I can't use boost::any for this. What are my other options?

The logical solution is probably to wrap your own vector class
around a union of two pointers, a Foo* and a Bar*. You can use a
shared size member, so you can implement empty() without
differentiating which elements there aren't ;) For most operations
you will have to use a tag, though.

You'd have to add your own push_back overloads, your own iterators,
a foo_begin() and a bar_begin() but it's not too difficult.

BTW, if you're targetting multiple platforms and one is very out
of date, you might want to consider Comeau.

HTH,
Michiel Salters
 
M

Martin Stettner

Simon said:
I'd like to do something along these lines:

struct foo
{
int i1_;
int i2_;
};

struct bar
{
double d1_;
double d2_;
};

typedef std::vector<foo> vectorFoo;
typedef std::vector<bar> vectorBar;

union vectorAll
{
vectorFoo foo_;
vectorBar bar_;
};

static vectorAll myVectorAll;
...
foo myFoo;
myVectorAll.foo_.push_back(myFoo);
myVectorAll.foo_.clear();
...
bar myBar;
myVectorAll.bar_.push_back(myBar);
myVectorAll.bar_.clear();

[...]
Given that one of my target compilers is too uncompliant to compile
boost, I can't use boost::any for this. What are my other options?

Perhaps something like the following might do the job:

class FooBarVector
{
enum VectorType { NONE, FOO, BAR };
public:
FooBarVector() : type_(NONE) { };

~FooBarVector() {
switch(type_) {
case FOO: delete v_.foo_; break;
case BAR: delete v_.bar_; break;
}
type_ = NONE;
}

vectorFoo& foo(){
if (type_ == BAR)
delete v_.bar_;
if (type_ != FOO) {
v_.foo_ = new vectorFoo();
type_ = FOO;
}
return *(v_.foo_);
}

vectorBar& bar(){
if (type_ == FOO)
delete v_.foo_;
if (type_ != BAR) {
v_.bar_ = new vectorBar();
type_ = BAR;
}
return *(v_.bar_);
}
private:
union {
vectorFoo *foo_;
vectorBar *bar_;
} v_;

VectorType type_;
};

// ...
static FooBarVector v;
// ...
Foo myFoo;
v.foo().push_back(myFoo);
v.foo().clear();
// ...
Bar myBar;
v.bar().push_back(myBar);
v.bar().clear();
// ...

As an alternative, you can throw an exception, if foo() is called with
an non-empty bar-Vector (and vice versa).

greetings
Martin
 
S

Simon Elliott

The logical solution is probably to wrap your own vector class
around a union of two pointers, a Foo* and a Bar*. You can use a
shared size member, so you can implement empty() without
differentiating which elements there aren't ;) For most operations
you will have to use a tag, though.

You'd have to add your own push_back overloads, your own iterators,
a foo_begin() and a bar_begin() but it's not too difficult.

Yes. I was hoping for a solution that didn't involve vectors of
pointers though.
BTW, if you're targetting multiple platforms and one is very out
of date, you might want to consider Comeau.

I'd like to drop support for BCB3 but can't at the moment. I'm quite
interested in adding Comeau to the list of supported compilers at some
point.
 
M

msalters

Simon said:
Yes. I was hoping for a solution that didn't involve vectors of
pointers though.

It's not a vector of pointers, just

template< typename T1, typename T2 >
class bi_vector
{
union {
T1* t1_array;
T2* t2_array;
} data;
size_t size;
enum { t1_used, t2_used } type_in_data;
public:
// ...
};
If you want to save memory, you could use a signed integer type
for size, with size > 0 meaning size T1 objects and size < 0
meaning -size T2 objects. Not worth the price if these vectors
are rare, of course.

HTH,
Michiel Salters
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,276
Messages
2,571,384
Members
48,073
Latest member
ImogenePal

Latest Threads

Top