Latot February 2016

c++ send arguments to union (variable union)

well i cant find how do this, basically its a variable union with params, basic idea, (writed as function)

Ex1

union Some (int le)
{
  int i[le];
  float f[le];
};

Ex2

union Some
{
  int le;
  int i[le];
  float f[le];
};

obs this don't works D: maybe a way to use an internal variable to set the lenght but don't works too. Thx.

Answers


Bathsheba February 2016

No, this is not possible: le would need to be known at compile-time.

One solution would be to use a templated union:

template <int N> union Some
{
    int i[N];
    float f[N];
};

N, of course, is compile-time evaluable.

Another solution is the arguably more succinct

 typedef std::vector<std::pair<int, float>> Some;

or a similar solution based on std::array.


Simon Kraemer February 2016

Depending on your use case you could try to simulate a union.

struct Some
{
    //Order is important
private:
    char* pData;
public:
    int* const i;
    float* const f;

public:
    Some(size_t len)
        :pData(new char[sizeof(int) < sizeof(float) ? sizeof(float) : sizeof(int)])
        ,i ((int*)pData)
        ,f ((float*)pData)
    {
    }

    ~Some()
    {
        delete[] pData;
    }

    Some(const Some&) = delete;
    Some& operator=(const Some&) = delete;
};

Alternative solution using templates, unique_ptr and explicit casts:

//max_size_of<>: a recursive template struct to evaluate the
// maximum value of the sizeof function of all types passed as
// parameter
//The recursion is done by using the "value" of another
// specialization of max_size_of<> with less parameter types
template <typename T, typename...Args>
struct max_size_of
{
    static const std::size_t value = std::max(sizeof(T), max_size_of<Args...>::value);
};

//Specialication for max_size_of<> as recursion stop
template <typename T>
struct max_size_of<T>
{
    static const std::size_t value = sizeof(T);
};

//dataptr_auto_cast<>:  a recursive template struct that 
// introduces a virtual function "char* const data_ptr()"
// and an additional explicit cast operator for a pointer
// of the first type. Due to the recursion a cast operator 
// for every type passed to the struct is created.
//Attention: types are not allowed to be duplicate    
//The recursion is done by inheriting from of another
// specialization of dataptr_auto_cast<> with less parameter types
template <typename T, typename...Args>
struct dataptr_auto_cast : public dataptr_auto_cast<Args...>
{
    virtual char* const data_ptr() const = 0; //This is needed by the cast operator
    explicit operator T* const() const { return (T*)data_ptr(); } //make it explicit to avoid unwanted side e 


Yakk February 2016

C++ requires that the size of a type be known at compile time.

The size of a block of data need not be known, but all types have known sizes.

There are three ways around it.

I'll ignore the union part for now. Imagine if you wanted:

struct some (int how_many) {
  int data[how_many];
};

as the union part adds complexity which can be dealt with separately.


First, instead of storing the data as part of the type, you can store pointers/references/etc to the data.

struct some {
  std::vector<int> data;

  explicit some( size_t how_many ):data(how_many) {};

  some( some&& ) = default;
  some& operator=( some&& ) = default;
  some( some const& ) = default;
  some& operator=( some const& ) = default;
  some() = default;
  ~some() = default;
};

here we store the data in a std::vector -- a dynamic array. We default copy/move/construct/destruct operations (explicitly -- because it makes it clearer), and the right thing happens.

Instead of a vector we can use a unique_ptr:

struct some {
  std::unique_ptr<int[]> data;

  explicit some( size_t how_many ):data(new int[how_many]) {};

  some( some&& ) = default;
  some& operator=( some&& ) = default;
  some() = default;
  ~some() = default;
};

this blocks copying of the structure, but the structure goes from being size of 3 pointers to being size of 1 in a typical std implementation. We lose the ability to easily resize after the fact, and copy without writing the code ourselves.


The next approach is to template it.

template<std::size_t N>
struct some {
  int data[N];
};

this, however, requires that the size of the structure be known at compile-time, and some<2> and some<3> are 'unrelated types' (barr


PaperBirdMaster February 2016

I would like to suggest a different approach: Instead of tying the number of elements to the union, tie it outside:

union Some
{
  int i;
  float f;
};

Some *get_Some(int le) { return new Some[le]; }

Don't forget to delete[] the return value of get_Some... Or use smart pointers:

std::unique_ptr<Some[]> get_Some(int le)
{ return std::make_unique<Some[]>(le); }

You can even create a Some_Manager:

struct Some_Manager
{
    union Some
    {
      int i;
      float f;
    };

    Some_Manager(int le) :
        m_le{le},
        m_some{std::make_unique<Some[]>(le)}
    {}

    // ... getters and setters...
    int count() const { return m_le; }
    Some &operator[](int le) { return m_some[le]; }

private:
    int m_le{};
    std::unique_ptr<Some[]> m_some;
};

Take a look at the Live example.


kfsone February 2016

It's not possible to declare a structure with dynamic sizes as you are trying to do, the size must be specified at run time or you will have to use higher-level abstractions to manage a dynamic pool of memory at run time.

Also, in your second example, you include le in the union. If what you were trying to do were possible, it would cause le to overlap with the first value of i and f.

As was mentioned before, you could do this with templating if the size is known at compile time:

#include <cstdlib>

template<size_t Sz>
union U {
    int i[Sz];
    float f[Sz];
};

int main() {
    U<30> u;
    u.i[0] = 0;
    u.f[1] = 1.0;
}

http://ideone.com/gG9egD

If you want dynamic size, you're beginning to reach the realm where it would be better to use something like std::vector.

#include <vector>
#include <iostream>

union U {
    int i;
    float f;
};

int main() {
    std::vector<U> vec;

    vec.resize(32);

    vec[0].i = 0;
    vec[1].f = 42.0;

    // But there is no way to tell whether a given element is
    // supposed to be an int or a float:
    // vec[1] was populated via the 'f' option of the union:
    std::cout << "vec[1].i = " << vec[1].i << '\n';
}

http://ideone.com/gjTCuZ

Post Status

Asked in February 2016
Viewed 3,215 times
Voted 9
Answered 5 times

Search




Leave an answer