I have already read struct in union initialize and it does not address my issue
In my legacy code, I have tons of struct-with-union like
typedef struct st
{
union
{
struct
{
uint8 sign ;
uint8 cells;
}s;
int data;
}u;
int extra;
} st;
Under C, below compiles:
static const st someval = {
.sign = 0, .cells ={ 0 },
.cells = { 11 }
};
Is there a way to minimally change the compilation method (or code) to make it compiler under g++?
static const st someval = {
.sign = 0, .cells ={ 0 },
// ^
.cells = { 11 } };
// ^
You initialize cells twice, which makes this ill-formed in C++.
Also, neither sign nor cell are direct members of st. The declaration is ill formed in C as well.
Following would be correct use of designated initializers in C++20 (a future C++ standard which is proposed to introduce designated initializers to the language):
static const st someval = {
.u{.s{.sign = 0, .cells = 11}},
// NOTE .extra left zero-initialized
};
or following, which is correct in both C++20 and C:
static const st someval = {
.u = {.s = {.sign = 0, .cells = 11}},
};
The current C++ standard doesn't have designated initializers however. You can write a function that creates a local non-const zero-initialized st object, then use member accessors to initialize the specific members that you want to initialize, and then return the object. Then you can initialize the const st object with a call to that function. That initialization function might not be useful anywhere else, so you might want to use a lambda to avoid needlessly polluting the namespace.:
static const st someval = []() {
st o {};
o.u.s.sign = 0;
o.u.s.cells = 11;
return o;
}();
Related
This question is specifically about options in C++17. Assuming following declaration in C library (I cannot change them):
typedef enum {
TYPEA = 0,
TYPEB = 2,
TYPEC = 4
} SPECIFIC_TYPE_t;
typedef struct {
uint16_t Method : 1;
uint16_t Access : 2;
uint16_t VendorSpecific : 1;
uint16_t Direction : 1;
uint16_t Persistent : 1;
uint16_t Internal : 4;
uint16_t Reserved : 6;
} PROPERTY_t;
typedef RESULT_t (*CB_DataPointRead_t) (void *Service, uint8_t Pinpoint, bool VendorSpecific,
uint16_t GroupID, uint16_t ElementID, void *Data,
uint8_t *DataLengthInOut);
typedef RESULT_t (*CB_DataPointWrite_t) (void *service, uint8_t Pinpoint, bool VendorSpecific,
uint16_t GroupID, uint16_t ElementID, void *Data,
uint8_t DataLength);
typedef struct {
uint16_t GroupID;
uint16_t ElementID;
uint8_t Pinpoint;
SPECIFIC_TYPE_t Type;
uint8_t Size;
PROPERTY_t Property;
union {
struct {
CB_DataPointRead_t Read;
CB_DataPointWrite_t Write;
} Callback;
struct {
void *Data;
} DirectAccess;
} AccessType;
} DataPoint_t;
Status in pure C
To initialize the last element designator initializers work well in C:
uint16t dataPointValue = 0;
const DataPoint_t firstDatapointConfig = {
/*...*/,
.DirectAccess = { (void*)&dataPointValue; }
};
Designator initializers in C++
They appear in C++20 and aren't compatible with C in many aspects.
Problem
I'd like to initialize variable like firstDatapointConfig as const qualified in C++17. So far I don't see a way other than write a function in C (compiled as C code) and return the initialized structure to a variable before use. I tried various ways, including gnuc++17 which handles designator initializers, except it tells me:
error: 'const DataPoint_t' has no non-static data member named 'DirectAccess'
and MSVC don't digest this method of initialization at all without C++20 enabled.
Addressing the last element outside initializer, don't work either:
datapoint.DirectAccess = { &value };
results in the following error:
error: 'struct DataPoint_t' has no member named 'DirectAccess'
Comment
It was much easier to use these structures in Rust after processing them through bindgen :-)
Question
Is there a way to initialize the variable of DataPoint_t type in C++17 with DirectAccess element filled with the right value?
Is there a way to initialize the variable of DataPoint_t type in C++17 with DirectAccess element filled with the right value?
Yes, even though my way of doing it is a little cumbersome. There may be easier ways:
// Make a usable type of that anonymous entity:
using AccessType_t = decltype(DataPoint_t::AccessType);
// Use the new type and prepare what you need:
AccessType_t at;
at.DirectAccess.Data = nullptr;
// initialize your DataPoint_t
const DataPoint_t firstDatapointConfig{1,2,3,TYPEA,4, PROPERTY_t{}, at};
If you do this a lot you could make a helper function:
using AccessType_t = decltype(DataPoint_t::AccessType);
using Callback_t = decltype(AccessType_t::Callback);
using DirectAccess_t = decltype(AccessType_t::DirectAccess);
template<class U>
constexpr auto init_AccessType(U u) {
AccessType_t at;
if constexpr (std::is_same_v<U,Callback_t>) {
at.Callback = u;
} else if constexpr (std::is_same_v<U,DirectAccess_t>) {
at.DirectAccess = u;
} else {
// uninitialized
}
return at;
}
const DataPoint_t firstDatapointConfig{1,2,3,TYPEA,4, PROPERTY_t{},
init_AccessType(DirectAccess_t{nullptr})};
I'd like to initialize variable like firstDatapointConfig as const qualified in C++17
With all that C++ has to offer:
constexpr DataPoint_t create_DataPoint_t(uint16_t *v) {
DataPoint_t r{};
r.AccessType.DirectAccess.Data = v;
return r;
}
const DataPoint_t firstDatapointConfig = create_DataPoint_t(&dataPointValue);
with designated initializers:
const DataPoint_t firstDatapointConfig2 = {
.AccessType = {
.DirectAccess = {
.Data = &dataPointValue
}
}
};
Addressing the last element outside initializer, don't work either:
datapoint.DirectAccess = { &value };
Because there is no such element, there is datapoint.AccessType.DirectAccess.Data.
To initialize the last element designator initializers work well in C:
uint16t dataPointValue = 0;
const DataPoint_t firstDatapointConfig = {
/*...*/,
.DirectAccess = { (void*)&dataPointValue; }
};
The presented code is invalid - uint16t is meant to be uint16_t and ; is a typo. And still after fixing the typos, no, the presented code is invalid in "pure C" and "does not work well" godbolt link. There is no such thing as DirectAccess in DataPoint_t - there is such member in the unnamed union declared inside DataPoint_t. You can do in C:
const DataPoint_t firstDatapointConfig3_in_C = {
.AccessType = {
.DirectAccess = { (void*)&dataPointValue }
}
};
or
const DataPoint_t firstDatapointConfig4_in_C = {
.AccessType.DirectAccess = { (void*)&dataPointValue }
};
or
const DataPoint_t firstDatapointConfig4_in_C = {
.AccessType.DirectAccess.Data = (void*)&dataPointValue
};
The cast to void* is superfluous - all pointers are implicitly converted to void*. Note that the following:
const DataPoint_t firstDatapointConfig5_in_C = {
.AccessType = { (void*)&dataPointValue }
};
would be equal to:
const DataPoint_t firstDatapointConfig6_in_C = {
.AccessType.Callback.Read = (void*)&dataPointValue
};
Most probably you are coding under -fms-extensions with GNU gcc or with MSVC, in which case you should be aware that you are using an extension that imports unnamed structure members to parent structure. The code is invalid in "pure C", it's using an extension to C.
I was trying to create a clever class containing a font style. Before this consisted of 3 enums with bit-wise compatible values (each set of values did not had overlapping bits with the other enums) so you could do FontStyle::LEFT | FontStyle::TOP
But clang warned me about combining unrelated enums and yes I've seen possible bugs here: FontStyle::LEFT | FontStyle::RIGHT did set both bits. So I reworked the class using a helper class for the former enums and templates to match correct values. But now I'm getting linker errors for clang on Debug builds about undefined reference to my static constexpr members.
Looking at Undefined reference error for static constexpr member suggests, that the value is ODR-used, but I'm not using any references.
When does a static constexpr class member need an out-of-class definition? this then pointed me to the implicit copy constructor of my helper class which is the issue.
Is there any chance I can avoid the out-of-class definitions in C++14 (C++17 already allows to omit them) and Debug builds (Ctors are optimized away in Release and hence no undefined references)?
Related code:
#include <array>
#include <cstdint>
namespace detail {
template<unsigned T_index>
struct FontStylePart
{
constexpr FontStylePart(uint8_t val) : value(val) {}
uint8_t value;
};
} // namespace detail
class FontStyle
{
static constexpr unsigned AlignH = 0;
static constexpr unsigned AlignV = 1;
public:
constexpr FontStyle() = default;
template<unsigned T_index>
constexpr FontStyle(detail::FontStylePart<T_index> style) : FontStyle()
{
value[T_index] = style.value;
}
/// Horizontal align
static constexpr detail::FontStylePart<AlignH> LEFT = 0;
static constexpr detail::FontStylePart<AlignH> RIGHT = 1;
static constexpr detail::FontStylePart<AlignH> CENTER = 2;
/// Vertical align
static constexpr detail::FontStylePart<AlignV> TOP = 0;
static constexpr detail::FontStylePart<AlignV> BOTTOM = 1;
static constexpr detail::FontStylePart<AlignV> VCENTER = 2;
private:
std::array<uint8_t, 3> value = {{0, 0, 0}};
};
int main() {
FontStyle style = FontStyle::CENTER;
return 0;
}
The line
FontStyle style = FontStyle::CENTER;
is a ODR use of FontStyle::CENTER.
I tried using
constexpr FontStyle style = FontStyle::CENTER;
but I ran into problems in the constructor. The following works although it's not clear to me whether that is acceptable for your needs.
int main() {
constexpr auto v = FontStyle::CENTER;
FontStyle style = v;
return 0;
}
This transfers the onus of ODR use to constexpr auto v.
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);
I have a structure as follows:
struct app_data
{
int port;
int ib_port;
unsigned size;
int tx_depth;
int sockfd;
char *servername;
struct ib_connection local_connection;
struct ib_connection *remote_connection;
struct ibv_device *ib_dev;
};
When I try to initialize it thus:
struct app_data data =
{
.port = 18515,
.ib_port = 1,
.size = 65536,
.tx_depth = 100,
.sockfd = -1,
.servername = NULL,
.remote_connection = NULL,
.ib_dev = NULL
};
I get this error:
sorry, unimplemented: non-trivial designated initializers not supported
I think it wants the order of initialization exactly as it is declared, and local_connection is missing. I don't need to initialize it though, and setting it to NULL doesn't work.
If I change it to this for g++, still get the same error:
struct app_data data =
{
port : 18515,
ib_port : 1,
size : 65536,
tx_depth : 100,
sockfd : -1,
servername : NULL,
remote_connection : NULL,
ib_dev : NULL
};
the order of initialization needs to be in the exact order of declaration.
typedef struct FOO
{
int a;
int b;
int c;
}FOO;
FOO foo = {.a = 1, .b = 2}; // OK
FOO foo1 = {.a = 1}; // OK
FOO foo2 = {.b = 2, .a = 1}; // Error sorry, unimplemented: non-trivial designated initializers not supported
FOO foo3 = {.a = 1, .c = 2}; // Error sorry, unimplemented: non-trivial designated initializers not supported
I understand that this means that the compiler has no support for name-oriented, out-of-order, member initialization.
Need to initialize the struct in the old fashioned way. I keep the variable names for clarity, but I have to initialize them in order, and not skip a variable.
I can stop the initialization at any variable, but can't initialize variables that come of that.
This does not work with g++. You are essentially using C constructs with C++. Couple of ways to get around it.
1) Remove the "." and change "=" to ":" when initializing.
#include <iostream>
using namespace std;
struct ib_connection
{
int x;
};
struct ibv_device
{
int y;
};
struct app_data
{
int port;
int ib_port;
unsigned size;
int tx_depth;
int sockfd;
char *servername;
struct ib_connection local_connection;
struct ib_connection *remote_connection;
struct ibv_device *ib_dev;
};
int main()
{
struct app_data data =
{
port : 18515,
ib_port : 1,
size : 65536,
tx_depth : 100,
sockfd : -1,
servername : NULL,
local_connection : {5},
remote_connection : NULL,
ib_dev : NULL
};
cout << "Hello World" << endl;
return 0;
}
2) Use g++ -X c. (Not recommended) or put this code in extern C [Disclaimer, I have not tested this]
I have noticed my GCC compiler has some trick to accept .fieldname=value assigments but will only compile if fields come in the same order they are declared in the struct.
I was able to init this struct in two ways. The one with names improves readability and reduces the risk of assigning the wrong data if the struct field order is later changed.
//Declare struct
typedef struct
{
uint32_t const * p_start_addr;
uint32_t const * p_end_addr;
fs_cb_t const callback;
uint8_t const num_pages;
uint8_t const priority;
} fs_config_t;
//Assign unnamed
fs_config_t fs_config
{
(uint32_t*)0x00030000, // uint32_t const * p_start_addr;
(uint32_t*)0x00038000, // uint32_t const * p_end_addr;
fs_evt_handler, // fs_cb_t const callback;
8, // uint8_t const num_pages;
0xFE // uint8_t const priority;
};
//Assign to named fields
static fs_config_t fs_config1
{
.p_start_addr = (uint32_t*)0x00030000,
.p_end_addr = (uint32_t*)0x00038000,
.callback = fs_evt_handler,
.num_pages = 8,
.priority = 0xFE
};
The rule of thumb is:
Assign to .name=value fields
Assign in the order they where declared
Include all fields in the assigment
Since none of the other approaches worked for me with the Arduino IDE, I decided to simply set each field separately:
struct app_data data;
data.port = 18515;
data.ib_port = 1;
data.size = 65536;
data.tx_depth = 100;
data.sockfd = -1;
data.servername = NULL;
data.remote_connection = NULL;
data.ib_dev = NULL;
Unfortunately, C++ doesn't support designated initialisers. GCC still lets you use them (as an extension) but you must initialise members in the same order as they are listed in the struct.
Another workaround is to use an immediately invoked lambda:
constexpr fuse_operations fuse_ops = []{
fuse_operations ops{};
ops.destroy = wiifs_destroy;
ops.getattr = wiifs_getattr;
ops.access = wiifs_access;
// ...
return ops;
}();
I personally prefer this solution because it is perfectly standard C++, it lets you initialise fields in the order you want, skip the ones you don't need and default initialise the rest. And the compiler is still able to optimise this. Do note that this will only work with C++17 or newer.
I was hit by a variant of this. Consider this broken code:
enum {
S_START,
S_ANOTHER,
S_LAST
} STATES;
const char* STATE_NAMES[] = {
[S_START] = "S_START",
[S_LAST] = "S_LAST",
};
int main() {
}
Here's the error message:
a.cpp:10:1: sorry, unimplemented: non-trivial designated initializers not supported
10 | };
The problem was that I forgot to define S_ANOTHER entry in STATE_NAMES.
Also note that, as the original question stated, the order of the member expressions matter. I noticed that if I would like to only initialize "size" in the previous example, I need to put expressions for .port and .ib_port before. Otherwise I get the error "sorry, unimplemented: non-trivial designated initializers not supported" Not that intuitive...
Say I have a class/struct with a single union in it... something like:
struct Box { union { AS128 intr; struct { AS32 a, b, c, d; }; }; };
What's the proper way to do an initializer list for this kind of data type?
Box iMyBoxA = { 10, 10, 100, 100 };
Box iMyBoxB = ( Box ){ 10, 10, 100, 100 };
The 'A' option above works in many cases, but isn't completely portable... giving the error "no known conversion for argument 1 from 'brace-enclosed initializer list'". And the second doesn't compile with "Cannot omit braces around initialization of subobject..."
I've tried a few other cases and can't seem to get it to compile across multiple platforms.
A union holds only one member at a time, so you initialize it with only one value.
You may use Box iMyBoxA = { value }; to initialize the first-listed member of the union or Box iMyBoxA = { .name = value }; to initialize a particular member by name.
Some notes:
Your code is missing a semicolon after the union declaration; it should be:
struct Box { union { AS128 intr; AS32 a, b, c, d; }; };
Since you have a union inside a struct, you ought to use two sets of braces for the initializers:
Box iMyBoxA = { { value } };
The syntax with one set of braces is permitted but may elicit warnings from your compiler.
Designated initializers (member names in initializers) are a C 1999 feature. This question is tagged with C and C++. A C++ compiler might or might not accept a designated initializer. In C, you would also need to declare Box as a type using typedef or change Box iMyBoxA… to struct Box iMyBoxA….
In C, anonymous unions are new in C 2011.
The original version of the structure and union definition in the question was:
struct Box { union { AS128 intr; AS32 a, b, c, d; } };
This is a little unusual. Assuming the types AS128 and AS32 are defined, then it is equivalent to:
struct Box1 // Renamed for convenience of testing
{
union
{
AS128 intr;
AS32 a;
AS32 b;
AS32 c;
AS32 d;
};
};
That is, it is a union of an AS128 called intr and 4 separate values of type AS32 called a, b, c and d (with the latter 4 all occupying the same space).
Presumably, what you have in mind is:
struct Box2
{
union
{
AS128 intr;
struct
{
AS32 a;
AS32 b;
AS32 c;
AS32 d;
} s;
};
};
This uses the anonymous members feature of C11 (listed in the Foreword of ISO/IEC 9899:2011 as a new feature compared to C99). [The question has now been fixed to reflect more or less this notation (using an anonymous structure). Note that you can't directly designate an anonymous union member, so the s above is a good idea, at least. The structure without the s can be used, but I don't think you can initialize the structure unless you can designate it — you can only designate named elements of a union.]
With the first variant (struct Box1), you can initialize the first member (the AS128 intr; member) with no name:
struct Box1 b1 = { { 1234 } };
The outer braces are for the structure; the inner braces are for the (anonymous) union.
Or you can specify which member to initialize using a designated initializer:
struct Box1 b2 = { { .a = 1234 } };
Note that you can't do:
struct Box1 b3 = { { .a = 1234, .b = 2341 } };
The compiler gives a warning (see the example code below).
With the second variant (struct Box2), with the nested structure, you can still use the undesignated initializer or the designated initializers, but this time, you could specify all 4 of the members of the structure:
struct Box2 b4 = { { .s = { .a = 32, .b = 65, .c = 48, .d = 97 } } };
Example code
Using funny types for the AS128 and AS32 type names.
typedef long AS128;
typedef char AS32;
struct Box1
{
union
{
AS128 intr;
AS32 a;
AS32 b;
AS32 c;
AS32 d;
};
};
struct Box2
{
union
{
AS128 intr;
struct
{
AS32 a;
AS32 b;
AS32 c;
AS32 d;
} s;
};
};
struct Box1 b1a = { { 1234 } };
struct Box1 b1b = { { .intr = 1234 } };
struct Box1 b1c = { { .a = 32 } };
//struct Box1 b1d = { { .b = 29, .c = 31 } }; // Invalid double initialization
//error: initialized field overwritten [-Werror=override-init]
struct Box2 b2a = { { 1234 } };
struct Box2 b2b = { { .intr = 1234 } };
struct Box2 b2c = { { .s = { .a = 29, .b = 30, .c = 31, .d = 32 } } };