2

In the past, I have used a macro for initializing an Struct with this scheme:

node.h

typedef struct Node_TypeDef
{
    uint16_t netId;
    const uint8_t UID[6];
}Node_TypeDef

#define NODE_INIT                                       \
{                                                         \
    0x0002,                                 /*NetID*/     \
    {0x03, 0x44, 0x03, 0x00, 0xA0, 0x08},   /*UID*/       \
}

main.c

#include "node.h"
 
int main(void) {

    Node_TypeDef node = NODE_INIT;
    

/*More Code*/

}

Now I want to put the initialization code in a function that receives a pointer to the struct, then populates it depending on the second parameter using the macro directives. Something like this:

node.h

typedef struct Node_TypeDef
{
    uint16_t netId;
    const uint8_t UID[6];
}Node_TypeDef

#define NODE_INIT_1                                      \
{                                                         \
    0x0001,                                 /*NetID*/     \
    {0x03, 0x44, 0x03, 0x00, 0xA0, 0x08}    /*UID*/       \
}

#define NODE_INIT_2                                       \
{                                                         \
    0x0001,                                 /*NetID*/     \
    {0x03, 0x44, 0x03, 0x00, 0xA0, 0x08}    /*UID*/       \
}

.
.
.
.

#define NODE_INIT_N                                          \
{                                                            \
    0x0001,                                 /*NetID*/        \
    {0x03, 0x75, 0x03, 0x00, 0xD0, 0x0A}    /*UID*/          \
}

void initNode (Node_TypeDef *node, uint8_t mode);

node.c

#include node.h

Node_TypeDef ptrNode;
 
void initNode (Node_TypeDef *node, uint8_t mode)
{
    switch (mode) {
    case 1:
        *node = NODE_INIT_1; 
        break;
    case 2:
        *node = NODE_INIT_2;
        break;
     .
     .
     .
    default:
        break;
    }
    ptrNode = node; 
}

main.c

#include "node.h"
 
int main(void) {

    Node_TypeDef node;
    initNode (&node,2);
    

/*More Code*/

}

Nevertheless, as a macro, it's not a variable. It is replaced in compilation time; the deference operator can not just assign the value. Furthermore, as is a struct, I should use a memcpy() for assigning it, but I need the memory address where this constant was placed.

In Summary:

How can I assign a value to my struct that was passed by reference to a constant value defined in a macro?

I appreciate any help with this.

2 Answers2

4

For this, you need compound literal:

#define NODE_INIT           \                                   
    ((struct Node_TypeDef){ \                                                        
    0x0002,  /*NetID*/      \
    {0x03, 0x44, 0x03, 0x00, 0xA0, 0x08}, /*UID*/ })

After that, this should work:

*node = NODE_INIT; 
abelenky
  • 63,815
  • 23
  • 109
  • 159
hyde
  • 60,639
  • 21
  • 115
  • 176
  • basically, all you need is to put the type in front of the `{ }`. – abelenky May 23 '22 at 19:19
  • I am getting an `error: assignment of read-only location` – BorbonJuggler May 23 '22 at 19:35
  • @BorbonJuggler Did you consider `Node_TypeDef *ptrNode;`? And does the compact example in my answer work for you? – Franck May 23 '22 at 19:40
  • @Franck, I checked your example and worked in the online environment. Then I tried in my IDE and it's giving me the reported error -I am Using Simplicity IDE and GNU ARM Compiler- – BorbonJuggler May 23 '22 at 19:46
  • @BorbonJuggler On my machine (Apple M1) it works, too. $ gcc --version Configured with: --prefix=/Library/Developer/CommandLineTools/usr --with-gxx-include-dir=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/4.2.1 Apple clang version 12.0.5 (clang-1205.0.22.11) Target: arm64-apple-darwin21.3.0 Thread model: posix InstalledDir: /Library/Developer/CommandLineTools/usr/bin – Franck May 23 '22 at 19:49
  • 1
    @BorbonJuggler For an embedded target there are special rules regarding memory layout. You might have to use macros to put the variables into the proper sections. – Franck May 23 '22 at 19:52
  • 1
    @BorbonJuggler Side note: When your variable is located in eeprom or flash memory then it can only be written into via a write function. – Franck May 23 '22 at 19:59
  • @Franck, Thanks for your advice and side note. I will look for another ways to do it. – BorbonJuggler May 23 '22 at 20:22
1

Only the (type) information was missing. Kudos to @hyde for the link: compound literal

#include <stdint.h>
#include <stdio.h>

typedef struct Node_TypeDef
{
    uint16_t netId;
    uint8_t UID[6];
} Node_TypeDef;

#define NODE_INIT_1 (Node_TypeDef)                        \
{                                                         \
    0x0001,                                 /*NetID*/     \
    {0x03, 0x44, 0x03, 0x00, 0xA0, 0x08}    /*UID*/       \
}
void initNode (Node_TypeDef *node, uint8_t mode);

Node_TypeDef *ptrNode;
 
void initNode (Node_TypeDef *node, uint8_t mode)
{
    switch (mode) {
    case 1:
        *node = NODE_INIT_1; 
        break;
    default:
        break;
    }
    ptrNode = node; 
}
 
int main(void) {

    Node_TypeDef node;
    initNode (&node,1);
    printf("netId: %d\n", node.netId);

/*More Code*/
    return 0;
}
netId: 1

Test at online environment

Franck
  • 1,340
  • 2
  • 3
  • 15