82

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
};
Ivan
  • 7,448
  • 14
  • 69
  • 134
  • 1
    I think you are using g++ to compile this code. If yes, remove the "." i.e. .port becomes port, .ib_port becomes ib_port. It should compile after that. – Anon Jul 04 '15 at 01:40
  • 1
    I am using g++ 4.9.2 (with c++11 enabled), but removing the . does not work? "port was not declared in this scope" – Ivan Jul 04 '15 at 01:42
  • Oh. Forgot to mention, use ":" instead of "=" e.g. port: 18515 and do this for other variables too. – Anon Jul 04 '15 at 01:44
  • Ok, the colon seems to pass the compiler, but it still complains with the same error. I think I have to have ALL fields initialized in the exact order. But there is a struct inside the struct, "local_connection", not a struct pointer? In plain C it works. In C++ I get this error. – Ivan Jul 04 '15 at 01:47
  • The error is the title of this thread. Also in the body of the post. – Ivan Jul 04 '15 at 01:49
  • I showed you how to initialized the ib_connection struct. Note that order matters. Tested on g++. Was able to compile and link. – Anon Jul 04 '15 at 01:57
  • @Ivan make sure the order of fields in the constructor matches the order of fields in the struct declaration – Justin Raymond Jan 23 '16 at 18:32
  • I think you are missing field *local_connection* in the assigment? – flodis Jul 29 '17 at 09:15
  • 5
    This question is a manifest of how broken and unaligned the C and C++ standards are still in 2018. – Johannes Overmann Dec 17 '18 at 11:37

7 Answers7

107

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.

Parallel Universe
  • 1,626
  • 2
  • 13
  • 5
  • 7
    Contrary to the accepted answer's statement that this doesn't work in g++, I am successfully using g++ (5.3.0) with the solution given in this answer (and not using "-X"). – Thomas Tempelmann Sep 25 '17 at 17:47
  • 6
    This answer worked for me. I could initialize by field name, but it had to be in the order that I declared them. –  Nov 02 '17 at 05:15
  • 1
    I wish i could upvote your answer more than once. A BILLION THANKS!! – Himanshu Mittal Oct 30 '19 at 13:44
34

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]

Anon
  • 2,608
  • 6
  • 26
  • 38
  • Also note this code is not portable (but will work with g++). What is the reason you are using C constructs with C++? Doing some kernel/driver implementation? – Anon Jul 04 '15 at 01:59
  • 6
    Thanks that works. BTW, the dots work in C++. Not sure why you suggested ":" instead. As to usage of C, it is mostly that I have examples that are in C and I am trying to port it to C++. One thing at a time. – Ivan Jul 04 '15 at 03:32
  • @Ivan Thanks, +1 for you. I did not know that. Tried ".port = 6000" and it worked! – Anon Jul 05 '15 at 01:01
  • 4
    `extern "C"` does not change the language, just the linkage. I.e. it tells the C++ compiler to compile C++ code such that it can be linked with cod from a C compiler. – MSalters Sep 27 '16 at 15:07
  • 3
    As @MSalters said, I get the same error even if in `extern "C"`. – BeeOnRope Feb 12 '17 at 18:49
  • @BeeOnRope, did the first method work for you? If not, can you paste your code? I can take a look. – Anon Feb 19 '17 at 01:35
  • @Anon - yes, the first method probably works, but I needed code which worked in both C and C++ in my case. I worked around it by moving the initializer out of the header. – BeeOnRope Feb 19 '17 at 01:39
  • 5
    This was not the correct answer for me with g++. Instead, the solution turned out to be missing and out-of-order fields in the initializer. It seems that both gcc and clang are more lenient, and it's just g++ that has this limitation. – Chris Dolan Sep 20 '17 at 02:10
  • [Here are the docs about the aggregate initialization in C++](https://en.cppreference.com/w/cpp/language/aggregate_initialization), it clearly shows the `. = ` as one of the possible ways to initialize your structures (since C++20, though...) – Alexis Wilke Apr 16 '19 at 19:40
  • It's kinda weird but this answer didn't work for me. but @parallel-universe answer works – Behnam Ghiaseddin Apr 23 '20 at 17:54
  • @BehnamGhiaseddin This answer doesn't work for me either. When I change `.member = initval` to `member : initval`, g++ 4.9.2 emits the same error: non-trivial designated initializers not supported. It looks like the two are just syntactic sugars for the same underlying mechanism, which is not complete in that compiler. – Kaz Feb 25 '22 at 02:28
16

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:

  1. Assign to .name=value fields
  2. Assign in the order they where declared
  3. Include all fields in the assigment
flodis
  • 1,123
  • 13
  • 9
  • All fields are not required, but they have to be in order. Missing fields will be set to zero or their default value (i.e. if you have `struct T { int a; int b = 3 }; T t = { .a = 55 };` then `b` is set to `3` (since C++11.) In C++20, the order and whether fields are missing will not matter. – Alexis Wilke May 20 '19 at 02:56
  • 1
    I had this issue with g++ / C++14 but all I had were missing fields (none out of order). I had to add the missing fields (assigning them to _something_) to get past the compiler error. – davidA Jul 26 '19 at 07:22
7

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;
Falko
  • 17,076
  • 13
  • 60
  • 105
  • Which version of g++ was that? 3.x? 4.x? – Alexis Wilke May 20 '19 at 03:00
  • I just accomplished initializing a struct in Arduino IDE (1.8.12). I literally copied the typedef from the .h file to my code and initialized it. Order and number of members must match. – Krista K May 27 '20 at 16:06
4

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.

Léo Lam
  • 3,870
  • 4
  • 34
  • 44
  • 2
    For me it compiled using C++11, g++ 4.8.4, just with `static` instead of `constexpr` – Oren Kishon Feb 25 '19 at 20:40
  • 1
    @OrenKishon the point of this is that it is evaluated at compile time, which might not happen if `fuse_ops` is declared `static` (instead then it would be left up to optimizations by the compiler). – Keeley Hoek Feb 09 '20 at 03:03
1

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.

d33tah
  • 10,999
  • 13
  • 68
  • 158
-1

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

Guz
  • 187
  • 1
  • 6