Initializing C struct with unions declared in C++17 - c++

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.

Related

Using an already-defined struct as an anonymous member of a union

Let's say I have a 32-bit hardware register Reg that I want to be able to access either as a 32-bit value (e.g. Reg = 0x12345678) or as bitfields (e.g. Reg.lsw = 0xABCD). I can achieve this by declaring a union with anonymous struct member, and declaring assignment and conversion operators to/from uint32_t. In a little-endian environment, the code might look like this:
#include <cstdint>
#include <cstdio>
typedef union
{
uint32_t val ;
struct
{
uint32_t lsw : 16 ;
uint32_t msw : 16 ;
} ;
operator = (uint32_t n) { val = n ; }
operator uint32_t() const { return val ; }
} HWR ;
int main()
{
HWR Reg ;
Reg = 0x12345678 ;
Reg.lsw = 0xABCD ;
printf ("%X\n", uint32_t(Reg)) ;
}
But now let's say I have a whole bunch of these registers, each with its own bitfield layout, and I have a header file FieldDefs.h that declares these bitfield layouts as named structures. How can I use these named structures in the above code, so that I can access the 32-bit value and also the individual bitfields? I could do it like this:
#include "FieldDefs.h" // Defines struct MyHWR
typedef union
{
uint32_t val ;
struct MyHWR field ;
operator = (uint32_t n) { val = n ; }
operator uint32_t() const { return val ; }
} MyHWRUnion ;
But now instead of Reg.lsw =..., I need to type Reg.field.lsw =...
Is there any way (in C++17) to declare an already defined struct as an anonymous member of a union? I am using g++ version 7.3.0 if it matters.
union
{
// ...
struct
{
// ...
};
This is an anonymous struct. Anonymous structs are ill-formed in C++. Only unions may be anonyous. This is different from C where anonymous structs are allowed (since C11).
Is there any way (in C++17) to declare an already defined struct as an anonymous member of a union?
No. Unnamed members cannot have a named type.
You'll need to make a choice between the unnamed member and the pre-declared class. Given that the anonymous struct is non-standard in the first place, I recommend going with the named member and pre-defined class. Maybe give it a short name to minimise verbosity.
I suppose none will like this answer, neither OP (since requires g++ 9.1), neither C++ gurus (UB smells?), but I am still a little proud of tinkering it.
There is [[no_unique_address]] attribute coming in C++20 and g++ 9.1 already supports it (even without -std=c++2a flag).
How can it be utilized here?
By test and trials it seems that if we create proxy member val marked with it it will take address of object1.
Thus we can create Proxy class which has operator=(uint32_t) and operator uint32_t that treats this as uint32_t. The proxy object has no address, does not increase size of struct that utilizes it.
Bitfields names have to be added by inheritance, which got wrapped in simple template, for consistency named HWR.
Voilà, we have HWR<bitfield> object which can be assigned to uint32_t directly, by val member and gives access to bitfields names.
https://godbolt.org/z/N2xEmz
#include <bits/stdint-uintn.h>
#include <cstddef>
#include <cstdint>
#include <cstdio>
// Example bifields, I assumed you have such in "FieldDefs.h"
struct bitfield {
uint32_t lsw : 16;
uint32_t msw : 16;
};
struct ThisProxy {
uint32_t& operator=(uint32_t n) {
auto& uint = *reinterpret_cast<uint32_t*>(this);
uint = n;
return uint;
}
operator uint32_t() const { return *reinterpret_cast<const uint32_t*>(this); }
};
template <typename Bitfield>
struct HWR : Bitfield {
static_assert(sizeof(Bitfield) == 4, "Bad things would happen");
HWR& operator=(uint32_t n) {
this->val = n;
return *this;
}
operator uint32_t() const { return this->val; }
[[no_unique_address]] ThisProxy val;
};
int main() {
HWR<bitfield> Reg;
// Sanity check that proxy points at &Reg and does not increase size
static_assert(offsetof(HWR<bitfield>, val) == 0, "");
static_assert(sizeof(HWR<bitfield>) == 4, "");
Reg = 0x12345678;
Reg.val = 0x8765432A;
Reg.lsw = 0xABCA;
printf("%X\n%ld\n", uint32_t(Reg), sizeof(Reg));
return 0;
}
Edit:
As it turned out that access by Reg.val is not mandatory the trick with inheritance + reinterpret_cast can be reused in pre-C++20 code.
template <typename Bitfield> struct HWR : Bitfield {
static_assert(sizeof(Bitfield) == 4, "Bad things would happen");
HWR &operator=(uint32_t n) {
*reinterpret_cast<uint32_t *>(this) = n;
return *this;
}
operator uint32_t() const {
return *reinterpret_cast<const uint32_t *>(this);
}
};
There is still smell of reinterpret_cast and I need to find out oine thing to fully recommend this code. Whenever bitfield can be interpreted by underlying type uint32_t.
1 I am not sure whenever offset of 0 is guaranteed by P0840R2.
PS. g++ complains with warning: offsetof within non-standard-layout type ‘HWR<bitfield>’ is conditionally-supported [-Winvalid-offsetof], but I didn't try to find workaround for it.
PPS. No anonymous structs!

union inside a struct -- how to do designated initializer?

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;
}();

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);

non-trivial designated initializers not supported

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...

Initialize static const multidimensional array with inferred dimensions inside class definition

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;
}