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...
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 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;
}();
How to initialize two or more structures with the same data? This has to be done at compile time to be as default data for const structures that are non-member global variables.
EDIT:
And what about C?
Works for me:
// header
struct Foo {
int a;
int b;
};
extern Foo const x;
extern Foo const y;
// cpp file
Foo const x{2, 3};
Foo const y = x;
Edit: reinterpreted the question a little.
One way is:
#define DATA { bla,bla,bla,bla }
var a = DATA;
var b = DATA;
You can use the copy constructor to initialize the structs. (This requires the use of a helper function to ensure correct order of initialization.) If the structures are POD, this should be as efficient as regular compile-time initialization:
struct A {
int a;
double c;
};
A initial_data() {
return { 1, 2.0 };
}
A a = initial_data();
A b = initial_data();
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);
Since C++11, one can initialize static const built-in types inside a class definition, like so:
class A {
public:
static const unsigned int val = 0; //allowed
};
However, doing this in Visual C++ 2013 with an array gives me an error telling me that this is not allowed:
class B {
public:
static const unsigned int val[][2] = { { 0, 1 } }; //not allowed
};
The error message simply reads "a member of type const unsigned int [][2] cannot have an in-class initializer." Instead, I'm forced to do the following:
class C {
public:
static const unsigned int val[][2];
};
const unsigned int C::val[][2] = { { 0, 1 } };
This is unfortunate because I have code which relies on the size of val, and I want to be able to change the contents of val without having to remember to go back and change a constant somewhere. Is there a different way of doing this that allows me to use sizeof on val from any point in the file below the declaration?
Your array must be a constexpr (clang and gcc specify it in their error messages) :
class B {
public:
static constexpr const unsigned int val[][2] = { { 0, 1 } };
// ^^^^^^^^
};
See it working here.
Visual Studio CTP 2013 (a "beta" version, that should be avoided for production) provides support for constepxr, which should be available in future releases, too.
EDIT :
If your compiler does not support constexpr (hopefully for you, not for too long), then you cant do in-class initialization of your static array, and must do the old way :
class C {
public:
static const unsigned int val[][2];
};
const unsigned int C::val[][2] = { { 0, 1 } };
If your array type is complete (if you declare all the array dimensions), then sizeof can be applied (the compiler knows how many elements to expect) :
;
class C {
public:
static const unsigned int val[2][2]; // Specify all dimensions.
void foo() { cout << sizeof(C::val); } // OK
};
const unsigned int C::val[][2] = { { 0, 1 } , { 2, 3 } };
int main() {
C c;
c.foo();
return 0;
}