Initializing aggregate unions - c++

I've got a union :
union my_union
{ short int Int16; float Float; };
I'd like to create :
const my_union u1 = ???;
const my_union u2 = ???;
and initialize their values to be of different types respectively :
u1 -> int16
u2 -> float
How do I do that ? If the above is not possible, are there any workarounds?

union can have any number of constructors - this will work for any datatypes without constructor, so your example is well if exclude string (or make pointer to string)
#include <string>
using namespace std;
union my_union
{
my_union(short i16):
Int16(i16){}
my_union(float f):
Float(f){}
my_union(const string *s):
str(s){}
short int Int16; float Float; const string *str;
};
int main()
{
const my_union u1 = (short)5;
const my_union u2 = (float)7.;
static const string refstr= "asdf";
const my_union u3 = &refstr;
}
There is more complicated way to create class, that owns by union, class must have a selector (scalar or vector datatype used) - to correctly destroy string.

Notwithstanding the ban on non-POD member data (as elaborated above) the Standard says:
at 8.5.1.15: When a union is initialized with a brace-enclosed
initializer, the braces shall only contain an initializer for
the first member of the union.
so
const my_union u1 = {1};
should work but this form cannot be used for the second (and subsequent) members.

Unions cannot contain non-POD data types such as string, so your question is meaningless.

Related

Is it possible to make a C++ function for a data type that is accessible using a dot operator?

I want to make a function associated to a data type say int that can be accessed using the dot operator such that:
int x = 9;
std::string s = x.ToString();
I know how to do so with std::string and the likes but not the int data type. Is that possible?
No, this is not possible. An int is a primitive type whereas a std::string is a class type that contains methods.
However, you could create your own struct/class in order to implement this functionality. The struct Int type has a constructor which takes in an integer and uses an initializer list :a(value) to assign the internal integer with a given value.
#include <string>
struct Int
{
int a;
Int(int value)
: a(value)
{}
std::string ToString() const { return std::to_string(a); }
};
int main()
{
Int a = 20;
std::string s = a.ToString();
}

Do constant pointer arrays even exist in c++03?

I'm fairly new to c++, and am trying to create a const pointer array to hold shortcuts to a few structures.
The issue I'm having is that C++03 (or the compiler I'm using - gcc 4.4.7) apparently doesn't support constant pointer arrays? Or at least you can't create them for existing objects?
To clarify, the pointers themselves are to be constant, but the objects they point to are variable. And the array structure is crucial because it greatly simplifies my code if I can access these objects by index later on.
This is for work with spacecraft hardware that has flight heritage or something, so it's not feasible to use a newer compiler :/
struct Type1 {
unsigned short thing1;
};
struct Type2 {
Type1 thing2;
};
struct Type3 {
Type2 thing3;
};
class A {
Type3 Array[4];
Type1 *const pArray[4] = {
&Array[0].thing3.thing2,
&Array[1].thing3.thing2,
&Array[2].thing3.thing2,
&Array[3].thing3.thing2
};
};
error: a brace-enclosed initializer is not allowed here before ‘{’ token
error: ISO C++ forbids initialization of member ‘pArray’
error: making ‘pArray’ static
error: invalid in-class initialization of static data member of non-integral type ‘MyType* const [4]’
So is it even possible to do what I'm trying to do given the compiler I'm using?
Because pArray is const, it requires an initializer. Because it is a non-static member variable, it can only be initialized from the initializer list of a constructor. Because it is an array, there is no syntax for this in C++03.
One possible workaround is to make it a non-array:
#include <cstddef>
struct Type1 {
unsigned short thing1;
};
struct Type2 {
Type1 thing2;
};
struct Type3 {
Type2 thing3;
};
class A {
struct xarray4 {
Type1 *data[4];
xarray4(Type1 *p0, Type1 *p1, Type1 *p2, Type1 *p3) {
data[0] = p0;
data[1] = p1;
data[2] = p2;
data[3] = p3;
}
Type1 *&operator[](std::size_t n) {
return data[n];
}
Type1 *const &operator[](std::size_t n) const {
return data[n];
}
};
Type3 Array[4];
const xarray4 pArray;
A() : pArray(
&Array[0].thing3.thing2,
&Array[1].thing3.thing2,
&Array[2].thing3.thing2,
&Array[3].thing3.thing2
) {
}
};
Here pArray is not an array, but an object with overloaded operator[]. Because it is an object, we can give it a custom constructor that lets us initialize it the way we want.
With the overloaded [] operator we can still access the pointers by index.

Access fields in a named union

I want to have a named union in the following struct so that I can memcpy it without knowing what field is "active".
struct Literal {
enum class Type : size_t {
INT = 1,
LONG,
FLOAT,
DOUBLE
} type;
union {
int li;
long ll;
float lf;
double ld;
} v;
constexpr Literal(int li): type{Type::INT}, v.li{li} {}
constexpr Literal(long ll): type{Type::LONG}, v.ll{ll} {}
constexpr Literal(float lf): type{Type::FLOAT}, v.lf{lf} {}
constexpr Literal(double ld): type{Type::DOUBLE}, v.ld{ld} {}
};
How can I initialize the fields in the constructors? Neither v.li{li} nor li{li} are working.
I also tried v{li} but it work only for the first constructor because it cast the 3 others to int.
EDIT: From #StoryTeller answer and comment:
struct Literal {
enum class Type : size_t {
INT = 1,
LONG,
FLOAT,
DOUBLE
} type;
union {
#define UNION_FIELDS int li; long ll; float lf; double ld;
union { UNION_FIELDS } value;
union { UNION_FIELDS };
};
};
You can only initialize direct members of Literal in its c'tors member initializer list. Aggregate initialization of the union member won't work due to narrowing conversions. So your options are to:
Name the union member type, and add appropriate c'tors to it.
Recurse in order to force the union fields into being treated as fields of the Literal class. Have a union of unions, and rely on the common initial sequence guarantee:
union {
union {
int li;
long ll;
float lf;
double ld;
} v;
union {
int li;
long ll;
float lf;
double ld;
};
};
constexpr Literal(int li): type{Type::INT}, li{li} {}
constexpr Literal(long ll): type{Type::LONG}, ll{ll} {}
constexpr Literal(float lf): type{Type::FLOAT}, lf{lf} {}
constexpr Literal(double ld): type{Type::DOUBLE}, ld{ld} {}
The above allows you to refer to each field by name, on account of the anonnymous union member, as well as lumping them together using the named v member. But I'll be the first to admit, it's ugly.

Initialiser, specific member of a union

I have a feeling the answer to this question is no, but is it possible to initialise a specific member of a union? For example the following:
#include <cassert>
#include <Windows.h>
int _tmain(int argc, _TCHAR* argv[])
{
auto time = 20090520145024798ull;
auto large = ULARGE_INTEGER() = {
{ time }
};
assert(large.QuadPart == time);
return 0;
}
(Visual Studio 2013, Windows 10), produces a compiler "conversion from 'unsigned __int64' to 'DWORD'", implying it's going to try to shoe-horn the uint64_t into the DWORD.
ULARGE_INTEGER is the union:
typedef union _ULARGE_INTEGER {
struct {
DWORD LowPart;
DWORD HighPart;
} u;
ULONGLONG QuadPart;
} ULARGE_INTEGER;
What does the standard say about order of initialisation in cases like this? I had hoped the compiler would see that QuadPart was the appropriate member to assign.
With unions, you can use list-initialization to initialize the first member only, as per [dcl.init.aggr]:
When a union is initialized with a brace-enclosed initializer, the braces shall only contain an initializer-clause
for the first non-static data member of the union. [ Example:
union u { int a; const char* b; };
u a = { 1 };
u b = a;
u c = 1; // error
u d = { 0, "asdf" }; // error
u e = { "asdf" }; // error
—end example ]
So, in your example, the following:
ULARGE_INTEGER large{time};
would initialize u.LowPart, not QuadPart, regardless of what the types of the various members are.
If you want to do anything else, you'll have to be explicit about it:
ULARGE_INTEGER large;
large.QuadPart = time;
Or write a better union type that actually has a constructor.
Keeping it simple:
ULARGE_INTEGER large;
large.QuadPart = time;
Or if you want to use auto and initialize it in one line, make a little helper perhaps?
inline auto MAKE_ULARGE_INTEGER(ULONGLONG t)
{
ULARGE_INTEGER result;
result.QuadPart = t;
return result;
}
auto large = MAKE_ULARGE_INTEGER(time);

Initializing union member in initializer list without narrowing

In the following code,
typedef unsigned long col24;
inline col24 MakeRGB24(int R, int G, int B) { return ...; }
struct blitdata
{
union
{
int Flags, Stretch;
col24 Luminance;
};
// (other members)
};
int main()
{
blitdata BlitData =
{
MakeRGB24(0, 0, 0),
// ...
};
}
why does the first initializer in the initializer list of BlitData give the following error:
Non-constant-expression cannot be narrowed from type col24 (aka unsigned long) to int in initializer list
Why is the compiler trying to initialize the int member of the union with the initializer whose type is col24 instead of using it to initialize the col24 member?
The compiler suggests that I static_cast the result of MakeRGB24 to int, but this could result in an unwanted narrowing.
How can the Luminance member be correctly initialized using the result of MakeRGB24 in the initializer list?
Edit: blitdata should stay POD.
This is apparently a nonstandard gcc extension, but this could be what you're after:
blitdata BlitData =
{
Luminance: MakeRgb24(0,0,0),
};
If that's no good for you, I suspect assigning it afterwards is the only way.