Initializing union member in initializer list without narrowing - c++

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.

Related

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.

Array of floats throws "incomplete type is not allowed"

I'm still fairly new to C++, so maybe its a very simple error I made here.
I wanted to initialize a private float array in one of my classes, like this.
float firstVect[] = {0.0f,0.0f,0.0f};
but firstVect is underlined with the explanation of incomplete type.
On the other hand two lines below the float array i have an int array looking like this:
int normalNumberOfDigits[]= {0,0,0};
The compile does not complain about the int array but only about the float array. Why?
Full code of my class:
class DataFilter
{
public:
int filterMovement(float vect3[3])
{
//TBD
}
private:
float firstVect[] = {0.0f,0.0f,0.0f};
int initialized = 0;
int normalNumberOfDigits[]= {0,0,0};
int determinNumberOfDigits(float testValue)
{
//TBD
}
};
Compiling with a modern version of clang++ makes your mistake obvious:
error: array bound cannot be deduced from an in-class initializer
You need to explicitly specify the size of the arrays (if you want to use a C-style array) in your class initialization:
class DataFilter
{
// ...
float firstVect[3] = {0.0f,0.0f,0.0f};
int initialized = 0;
int normalNumberOfDigits[3]= {0,0,0};
// ...
};
"The compile does not complain about the int array but only about the float array. Why?"
Compiling with a modern version of g++ makes this obvious as well:
error: flexible array member DataFilter::normalNumberOfDigits not at end of class DataFilter
When you declare an array without an explicit size, the compiler thinks that it is a flexible array, which is a C-only feature. It seems to be allowed in C++ as a non-standard g++ extension.
Non-static data members must have complete type, i.e. if they are arrays they must contain the array size. The size cannot be inferred from any initializers, because initializers are a) optional and b) not really part of the class definition, but rather implicitly part of all constructor function definitions.
Simple fix:
float firstVect[3] = {0.0f,0.0f,0.0f};
// ^^^
To clarify the explanation a bit: A class definition with default member initializer like this:
struct Foo
{
int a[3] = {1, 2, 3};
};
is conceptually the same as:
struct Foo
{
Foo();
int a[3];
};
Foo::Foo() : a{1, 2, 3} {}
You see now that the initializer is not really a part of the class definition, and thus cannot be used to deduce the array size.
A more extreme example is this:
struct Bar
{
Bar(int) : a{1, 2, 3} {}
Bar(float): a{1, 1} {}
Bar() = default;
int a[???] = {4, 4, 4, 4, 4};
};
What should the type of Bar::a be here?
Clang reports a clearer error:
fatal error: array bound cannot be deduced from an in-class initializer
You have to explicitly size your arrays:
class DataFilter
{
// ...
private:
float firstVect[3] = {0.0f,0.0f,0.0f};
int normalNumberOfDigits[3]= {0,0,0};
};

How can I pass a default argument of other type to a parameter?

Lets say I have this prog.cc file:
struct MyStruct
{
unsigned int X : 2;
unsigned int Y : 2;
unsigned int Spare : 28;
};
void MyFunc (int x, MyStruct myStruct = 0);
int main ()
{
MyStruct myStr;
myStr.X = 1;
myStr.Y = 0;
MyFunc(6);
}
void MyFunc (int x, MyStruct myStruct)
{
x += 10;
}
When compiled with GCC 4.4.7, I got this error:
prog.cc:7: error: default argument for 'MyStruct myStruct' has type 'int'
Now, I understand the error, but still - how can I resolve this?
I know I need somehow to cast from int to MyStruct, but both
MyStruct myStruct = (MyStruct)0
and
MyStruct myStruct = static_cast<MyStruct>0
has failed with:
prog.cc:7: error: no matching function for call to 'MyStruct::MyStruct(int)'
prog.cc:1: note: candidates are: MyStruct::MyStruct()
prog.cc:1: note: MyStruct::MyStruct(const MyStruct&)
How can I simply initialize my user-defined struct, passed as default argument on function parameter?
(Here is the sample code:
http://melpon.org/wandbox/permlink/izWCnBXl9PGnmm0l)
You can default-initialise PODs, so:
void MyFunc (int x, MyStruct myStruct = MyStruct());
I guess you're trying to use 0 because, as well as being an integer, that's a null pointer constant. But you're not using pointers here. The use of 0 is the wrong approach.
Just add a conversion constructor to your MyStruct:
struct MyStruct
{
unsigned int X : 2;
unsigned int Y : 2;
unsigned int Spare : 28;
MyStruct(int n) : X(n&3), Y(n&12), Spare(0) {}
};
It's implementation depends on your semantics, but you get the technical point. Enclosing your struct in an union could be a good solution too if you want to make it convertible from/to an int.

Member array with size defined by template parameter, but why no warning for zero-size array?

I was trying to write a templated base class to store a fixed number of data types, each with varying length. Here is a simplified version of much what I was trying to do:
template< int NINT, int NR0 >
class EncapsulatedObjectBase
{
public:
EncapsulatedObjectBase();
~EncapsulatedObjectBase();
double m_real[NR0];
int m_int[NINT];
}
Yeah...so the template parameters can be zero, thus declaring a zero-length array of objects. There will be multiple derived classes for this base, each defining their own number of variables. I have two questions:
1) Is this approach fundamentally flawed?
2) If so...why doesn't icc13 or gcc4.7.2 give me warnings about this when I instantiate a zero-length array? For gcc I use -wall and -wextra -wabi. The lack of warnings made me think that this sort of thing was OK.
EDIT:
Here is the contents of a file that show what I am talking about:
#include <iostream>
template< int NINT, int NR0 >
class EncapsulatedObjectBase
{
public:
EncapsulatedObjectBase(){}
~EncapsulatedObjectBase(){}
double m_real[NR0];
int m_int[NINT];
};
class DerivedDataObject1 : public EncapsulatedObjectBase<2,0>
{
public:
DerivedDataObject1(){}
~DerivedDataObject1(){}
inline int& intvar1() { return this->m_int[0]; }
inline int& intvar2() { return this->m_int[1]; }
};
class DerivedDataObject2 : public EncapsulatedObjectBase<0,2>
{
public:
DerivedDataObject2(){}
~DerivedDataObject2(){}
inline double& realvar1() { return this->m_real[0]; }
inline double& realvar2() { return this->m_real[1]; }
};
int main()
{
DerivedDataObject1 obj1;
DerivedDataObject2 obj2;
obj1.intvar1() = 12;
obj1.intvar2() = 5;
obj2.realvar1() = 1.0e5;
obj2.realvar2() = 1.0e6;
std::cout<<"obj1.intvar1() = "<<obj1.intvar1()<<std::endl;
std::cout<<"obj1.intvar2() = "<<obj1.intvar2()<<std::endl;
std::cout<<"obj2.realvar1() = "<<obj2.realvar1()<<std::endl;
std::cout<<"obj2.realvar2() = "<<obj2.realvar2()<<std::endl;
}
If I compile this with "g++ -Wall -Wextra -Wabi main.cpp" I get no warnings. I have to use the -pedantic flag to get warnings. So I still don't know how unsafe this is. In retrospect, I feel as though it must not be a very good idea...although it would be pretty useful if I could get away with it.
Zero-sized arrays are actually illegal in C++:
[C++11: 8.3.4/1]: [..] If the constant-expression (5.19) is present, it shall be an integral constant expression and its value shall be greater than zero. The constant expression specifies the bound of (number of elements in) the array. If the value of the constant expression is N, the array has N elements numbered 0 to N-1, and the type of the identifier of D is “derived-declarator-type-list array of N T”. [..]
For this reason, your class template cannot be instantiated with arguments 0,0 in GCC 4.1.2 nor in GCC 4.7.2 with reasonable flags:
template< int NINT, int NR0 >
class EncapsulatedObjectBase
{
public:
EncapsulatedObjectBase();
~EncapsulatedObjectBase();
double m_real[NR0];
int m_int[NINT];
};
int main()
{
EncapsulatedObjectBase<0,0> obj;
}
t.cpp: In instantiation of 'EncapsulatedObjectBase<0, 0>':
t.cpp:17: instantiated from here
Line 10: error: ISO C++ forbids zero-size array
compilation terminated due to -Wfatal-errors.
clang 3.2 says:
source.cpp:10:17: warning: zero size arrays are an extension [-Wzero-length-array]
(Note that, in any case, you won't get any error until you do try to instantiate such a class.)
So, is it a good idea? No, not really. I'd recommend prohibiting instantiation for your class template when either argument is 0. I'd also look at why you want to have zero-length arrays and consider adjusting your design.
In C using a zero-sized array as the last member of a struct is actually legal and is commonly used when the struct is going to end up with some sort of dynamically-created inline data that's not known at compile-time. In other words, I might have something like
struct MyData {
size_t size;
char data[0];
};
struct MyData *newData(size_t size) {
struct MyData *myData = (struct MyData *)malloc(sizeof(struct MyData) + size);
myData->size = size;
bzero(myData->data, size);
return myData;
}
and now the myData->data field can be accessed as a pointer to the dynamically-sized data
That said, I don't know how applicable this technique is to C++. But it's probably fine as long as you never subclass your class.
1) Add to declaration of your class C++11 static_assert or BOOST_STATIC_ASSERT and you will have compile-time diagnostic for zero length array:
....
BOOST_STATIC_ASSERT(NR0 > 0);
BOOST_STATIC_ASSERT(NINT > 0);
double m_real[NR0];
int m_int[NINT];
};
2) Use std::array or boost::array and you will have run-time diagnostic (in debug mode) for index overflow problem in such code:
BOOST_STATIC_ASSERT(NR0 > 0);
BOOST_STATIC_ASSERT(NINT > 0);
boost::array<double, NR> m_real; //double m_real[NR0];
boost::array<int, NINT> m_int; //int m_int[NINT];
};
Remark:
class boost::array has specialisation for zero-size array
3) Use size_t but not int for size of array.
Your design is quite dangerous:
DerivedDataObject1 a;
a.m_real[2] = 1; // size of m_real == 0 !!!
I think it will better to change design of your class EncapsulatedObjectBase. May be it will better to use:
template<typename T, size_t N> class EncapsulatedObjectBase
{
....
};
class DerivedDataObject1 : public EncapsulatedObjectBase<int,2>
{
....
};
class DerivedDataObject2 : public EncapsulatedObjectBase<double,2>
{
....
};
class DerivedDataObject3 : public EncapsulatedObjectBase<double,2>
, public EncapsulatedObjectBase<int,2>
{
....
};