boost::optional<> in a union?
Asked Answered
T

2

6

I have an optional POD struct that will be contained inside a union.
boost::optional<> holds its type by value, so I thought this could work:

union helper
{
    int foo;
    struct 
    {
        char basic_info;
        struct details {
            //...
        };

        boost::optional<details> extended_info;
    } bar;
    //  ...
};

helper x = make_bar();

if( x.bar.extended_info )
{
    // use x.bar.extended_info->elements
}

but VS2008 complained that my bar struct now had a copy constructor due to the boost::optional<details> element.

As a replacement, I've added a boolean flag to indicate whether the optional parameter is valid, but it's clunky:

union helper
{
    int foo;
    struct 
    {
        char basic;
        struct details {
            bool valid;
            //...
        } extended;
    } bar;
    //  ...
};

I considered implementing details::operator bool() to return the details::valid variable, but that's obscure and a disservice to humanity.
boost::optional<> clearly documents the syntax and intent and doesn't require detective work.

Finally, the helper union needs to be POD, so I can't do any dynamic allocation - otherwise I would use a pointer.

Any suggestions for something syntactically similar to boost::optional<> that's usable in a union?

Tayyebeb answered 12/1, 2011 at 20:53 Comment(4)
Why can't you use a pointer? You are allowed to dynamically allocate POD; it just isn't initialized automatically. Or does your helper union need to account for the size of your bar struct?Forthcoming
The union is copied by value and sent as bytes to another process, so I can't share the memory.Tayyebeb
Converting non-POD types into bytes is undefined behavior. You have to use POD types for binary serialization. Actually most do not use binary communication protocols but use XML-based communication protocols these days.Shackleford
You are correct. Unfortunately, this is a legacy product and cannot be easily changed. I'm eating this elephant one bite at a time.Tayyebeb
T
1

As others have mentioned, the ideal thing to do is to change from a union to a boost::variant<>.

However, if this isn't possible, you can implement a POD approximation of boost::optional<> as follows:

Implementation

template <typename T>
class Optional
{
    T value;
    bool valid;

public:

    // for the if(var) test
    operator bool() const  {  return valid;  }

    //  for assigning a value
    Optional<T> &operator=(T rhs)   
    {  
        value = rhs;  
        valid = true;  
        return *this;  
    }

    //  for assigning "empty"
    Optional<T> &operator=(void *)  
    {  
        valid = false;  
        return *this;  
    }

    // non-const accessors
    T &operator*()   {  return  value;  }
    T *operator->()  {  return &value;  }

    // const accessors
    const T &operator*()  const  {  return  value;  }
    const T *operator->() const  {  return &value;  }
};

The const accessors are necessary if you are holding a const instance of Optional<>.

Usage

Like a pointer, Optional<T> has no default state and must be initialized before you can rely on it (null or not).
Unlike boost::optional<T>, Optional<T> cannot be constructed from its T value type, and can only be constructed from another Optional<T>.
If you really want to value- or null-initialize it at construction, you could make a helper class with an operator Optional<T>(). I chose not to.

Construction

Optional<details> additional_info;
Optional<details> more_info(additional_info);

Assignment

// if there's no additional info
additional_info = 0;

// if there is extended info
details x;
//  ...populate x...
additional_info = x;

Data access

if( extended_info )
{
    extended_info->member;
    // - or -
    details &info = *extended_info;
}

So - it didn't turn out to be too bad. It doesn't make me feel quite warm and fuzzy, but it gets the job done.

Tayyebeb answered 13/1, 2011 at 20:33 Comment(0)
H
14

You can not use non-POD types as fields in union. Use boost::variant or something like it in C++ instead of union. Leave union only for compatibility with modules written in C.

Hexad answered 12/1, 2011 at 20:58 Comment(4)
Pretty much this- use a boost::variant.Glister
I have a lot of legacy code that serializes the union into bytes. I would like to avoid that refactoring right now.Tayyebeb
You have to use the "clunky" helper you described for serialization and convert the variant with optional to and from it. Memory layout of non-POD objects may be optimized by next version/service pack/compiling option of compiler differently, nothing to do.Shackleford
mskfisher, take a look at boost:apply_vistor. I use this in my code to serialize boost variants.Lancet
T
1

As others have mentioned, the ideal thing to do is to change from a union to a boost::variant<>.

However, if this isn't possible, you can implement a POD approximation of boost::optional<> as follows:

Implementation

template <typename T>
class Optional
{
    T value;
    bool valid;

public:

    // for the if(var) test
    operator bool() const  {  return valid;  }

    //  for assigning a value
    Optional<T> &operator=(T rhs)   
    {  
        value = rhs;  
        valid = true;  
        return *this;  
    }

    //  for assigning "empty"
    Optional<T> &operator=(void *)  
    {  
        valid = false;  
        return *this;  
    }

    // non-const accessors
    T &operator*()   {  return  value;  }
    T *operator->()  {  return &value;  }

    // const accessors
    const T &operator*()  const  {  return  value;  }
    const T *operator->() const  {  return &value;  }
};

The const accessors are necessary if you are holding a const instance of Optional<>.

Usage

Like a pointer, Optional<T> has no default state and must be initialized before you can rely on it (null or not).
Unlike boost::optional<T>, Optional<T> cannot be constructed from its T value type, and can only be constructed from another Optional<T>.
If you really want to value- or null-initialize it at construction, you could make a helper class with an operator Optional<T>(). I chose not to.

Construction

Optional<details> additional_info;
Optional<details> more_info(additional_info);

Assignment

// if there's no additional info
additional_info = 0;

// if there is extended info
details x;
//  ...populate x...
additional_info = x;

Data access

if( extended_info )
{
    extended_info->member;
    // - or -
    details &info = *extended_info;
}

So - it didn't turn out to be too bad. It doesn't make me feel quite warm and fuzzy, but it gets the job done.

Tayyebeb answered 13/1, 2011 at 20:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.