• AVR Freaks

AnsweredHot!Global structs with designated initializes

Author
acharnley
Super Member
  • Total Posts : 517
  • Reward points : 0
  • Joined: 2016/05/01 06:51:28
  • Location: 0
  • Status: offline
2020/02/27 03:25:26 (permalink)
0

Global structs with designated initializes

I'm just attempting to clean some code if possible but haven't yet figured this one out. Running ANSI/99.

Basically I design my code using separation of concern rules so any .c file can include an .h file and get access to a literal of functions and values. As an example, usb.c can include led.h and access all the led functions using led.xxx.

led.h example


struct {
    struct {
        void (*start) (void);
    } events;
    void (*on)(void);
    void (*off)(void);
} led;

void configLed (void);


Every .h file has an initializing function (configLed) which over-writes the default values. These config functions are called by main().

My question is whether I can set these default values directly. I'm aware of designated initializers but I couldn't make them work in the header.

Something like


static void on(void);

struct {
    struct {
        void (*start) (void);
    } events;
    void (*on)(void);
    void (*off)(void);
} led = {
   .on = on
};


The problem here is (using the example above) usb.c has access to the private led members and I don't think it's linked properly anyhow. Warning is:

./led.h:3:13: warning: function 'on' has internal linkage but is not defined [-Wundefined-internal]
static void on (void);
#1
ric
Super Member
  • Total Posts : 26159
  • Reward points : 0
  • Joined: 2003/11/07 12:41:26
  • Location: Australia, Melbourne
  • Status: online
Re: Global structs with designated initializes 2020/02/27 04:08:36 (permalink)
+1 (1)
A header should NEVER initialise values, that should ONLY be done in a C file.
 

I also post at: PicForum
Links to useful PIC information: http://picforum.ric323.co...opic.php?f=59&t=15
NEW USERS: Posting images, links and code - workaround for restrictions.
To get a useful answer, always state which PIC you are using!
#2
LdB_ECM
Super Member
  • Total Posts : 313
  • Reward points : 0
  • Joined: 2019/04/16 22:01:25
  • Location: 0
  • Status: offline
Re: Global structs with designated initializes 2020/02/27 05:44:14 (permalink)
+1 (1)
I agree with Ric  but extending answer
 
The problem is the body of the function "void on(void);"
Does not exist anywhere
 
This is a prototype header
static void on (void);

There is no actual code it just defines what that function looks like
 
This tries to set the function pointer to A BLOCK OF CODE

 
Led = {
 
   .on = on
 
}

The compiler knows what "on" looks like but it has >> no actual code << , so it's complaining that you are very very silly.
 
What it wants is a body you know something with some actual code that would get put to an address
Something exactly like
static void on(void)
 
{
 
   // yipee lets actually put some code in here
 
   // Because defining the prototype header is actually pointless
 
   //  if you don't provide a code body
 
}

 
That block of code will get put somewhere by the compiler and then it can set your function pointer to that address which is what you are so desperately trying to do.
 

post edited by LdB_ECM - 2020/02/27 05:55:19
#3
rpg7
Super Member
  • Total Posts : 1393
  • Reward points : 0
  • Joined: 2003/11/07 12:47:35
  • Status: offline
Re: Global structs with designated initializes 2020/02/27 05:55:59 (permalink)
+1 (1)

static void on(void)
{
}

 
If this is put into a header, it will make separate copies every time the header is included.  Ric has made the point that code should not appear in headers.
#4
du00000001
Just Some Member
  • Total Posts : 3477
  • Reward points : 0
  • Joined: 2016/05/03 13:52:42
  • Location: Germany
  • Status: offline
Re: Global structs with designated initializes 2020/02/27 06:38:22 (permalink)
+1 (1)
Having the initializer in the header imposes the risk to get multiple copies of the variable, resulting in a fancy linker crash.
Having the initializer in some c file prevents the multiple copies.
 
As per the "empty function" discussion:
Basically, an empty function declaration should do. The compiler only needs to know the type of the function plus eventual parameters - the function implementation is not required in this stage. (It's the linker that actually fills-in the address of the function.)
What might raise the issue: the "static" attribute. (Might be, the error message is somewhat misleading.)
This static would require a function implementation for each module that includes this header. The error message you supplied denominates the header's name, but not the c file's name...

PEBKAC / EBKAC / POBCAK / PICNIC (eventually see en.wikipedia.org)
#5
LdB_ECM
Super Member
  • Total Posts : 313
  • Reward points : 0
  • Joined: 2019/04/16 22:01:25
  • Location: 0
  • Status: offline
Re: Global structs with designated initializes 2020/02/27 07:04:11 (permalink)
+1 (1)
I believe he actually wants multiple copies at least that is what he stated that the act of including the header automatically gave the unit including it it's own copy. That unit then overwrites the struct which he is going to change set the function pointers. The fun part comes if any of the units include each other you get a name clash and not sure he thought that thru.
 
I assume he is trying to hack his way around doing it the normal way with a type handle or an opaque pointer to build a device driver context, hence the function pointers.
post edited by LdB_ECM - 2020/02/27 07:10:01
#6
acharnley
Super Member
  • Total Posts : 517
  • Reward points : 0
  • Joined: 2016/05/01 06:51:28
  • Location: 0
  • Status: offline
Re: Global structs with designated initializes 2020/02/27 15:18:26 (permalink)
0
No the struct is initialised, in this case as "led", so any c file that imports this .h file gets access to the shared led literal.

I then have a config function which writes values/pointers into the led struct on startup. It works fine but I wondered if there was a way to initalize the values directly.
 
Some code from led.c ...
 

static void patternEqual (void) {
    
    NCO_MODE = 0;
    patternEnable();
}

void configLed (void) {
    
    led.on = constantOn;
    led.off = off;
    led.events.active = constantOn;
    led.events.idle = patternEqual;
}

post edited by acharnley - 2020/02/27 15:20:35
#7
ric
Super Member
  • Total Posts : 26159
  • Reward points : 0
  • Joined: 2003/11/07 12:41:26
  • Location: Australia, Melbourne
  • Status: online
Re: Global structs with designated initializes 2020/02/27 15:42:41 (permalink)
+1 (1)
If the initialisation is happening in the header file, you're doing it wrong.
Have a read of Organizing Code Files in C and C++ (Ignore the C++ specific bits)

I also post at: PicForum
Links to useful PIC information: http://picforum.ric323.co...opic.php?f=59&t=15
NEW USERS: Posting images, links and code - workaround for restrictions.
To get a useful answer, always state which PIC you are using!
#8
LdB_ECM
Super Member
  • Total Posts : 313
  • Reward points : 0
  • Joined: 2019/04/16 22:01:25
  • Location: 0
  • Status: offline
Re: Global structs with designated initializes 2020/02/27 19:21:33 (permalink) ☼ Best Answerby acharnley 2020/02/28 01:57:02
+2 (2)
Then define the struct in the header with a tag name and expose an extern
 
led.h

 
 
 
struct MyledStruct{
    struct{
        void(*start)(void);
    } events;
    void(*on)(void);
    void(*off)(void);
};

extern struct
MyledStruct led;


Led.c

struct MyledStruct led ={
   .on = on
};


Job done ... led is exposed and the callers can see the function names.

post edited by LdB_ECM - 2020/02/27 23:22:29
#9
acharnley
Super Member
  • Total Posts : 517
  • Reward points : 0
  • Joined: 2016/05/01 06:51:28
  • Location: 0
  • Status: offline
Re: Global structs with designated initializes 2020/02/28 02:09:11 (permalink)
0
Knew there had to be a better way, that's it!

One small issue exists (which I can live with). I put the initialiser after the static functions so that it can pick them up without defining them in a header. It compiles fine but mplabx uses a top down approach so doesn't find it.

Attached Image(s)

#10
moser
Super Member
  • Total Posts : 550
  • Reward points : 0
  • Joined: 2015/06/16 02:53:47
  • Location: Germany
  • Status: offline
Re: Global structs with designated initializes 2020/02/28 02:37:20 (permalink)
+1 (1)
I believe this should do it:

#include "that_header_file_which_contains_type_struct_frequencyProto.h"
 
// declaration of start()
// INSERT HERE DECLARATION OF start IF NOT ALREADY DECLARED IN HEADER FILE
 
// declaration of taskInit()
static void taskInit (void);
 
// declaration of initVoltage()
// INSERT HERE DECLARATION OF initVoltage IF NOT ALREADY DECLARED IN HEADER FILE
 
struct frequencyProto frequency = {
    .unstable = 1,
    .events.start = start,
    .events.tasks = taskInit,
    .events.voltages.ac = initVoltage
};
 
// definition of taskInit
static void taskInit (void) {
    taskCommon();
    frequencyLog[at] = frequency.value;
    at++;
    if (at == LOG_COUNT) {
        at = 0;
        frequency.events.tasks = taskMain;
    }
}

 
PS: Please be aware, that there is a difference between "declaration" and "definition". A good way is to never put a definition of a variable or function into a header file (exception: static inline functions).
post edited by moser - 2020/02/28 02:50:40
#11
acharnley
Super Member
  • Total Posts : 517
  • Reward points : 0
  • Joined: 2016/05/01 06:51:28
  • Location: 0
  • Status: offline
Re: Global structs with designated initializes 2020/02/28 02:52:42 (permalink)
0
Tried that but it's still not picking it up in mplabx.

Saved 100 bytes through unnecessary hops and the code is cleaner. A good outcome.
 
 
post edited by acharnley - 2020/02/28 02:54:08
#12
moser
Super Member
  • Total Posts : 550
  • Reward points : 0
  • Joined: 2015/06/16 02:53:47
  • Location: Germany
  • Status: offline
Re: Global structs with designated initializes 2020/02/28 03:09:12 (permalink)
0
First try: Projects Tab > right click on your project name > Code Assistance > Reparse Project.
 
It might also be worth to try deleting the cache directory of MPLAB X ("var" folder) after closing MPLAB X. You can look up its location in: Help > About > Cache directory. But I believe the chance is pretty low, that this solves the issue in your case.
 
Could you please show the type struct frequencyProto?
Is it located in a header file which is included in that .c file?
 
#13
LdB_ECM
Super Member
  • Total Posts : 313
  • Reward points : 0
  • Joined: 2019/04/16 22:01:25
  • Location: 0
  • Status: offline
Re: Global structs with designated initializes 2020/02/28 04:52:16 (permalink)
+1 (1)
It's a standard circular reference issue.
 
Moser is on the right track but remove the "static" from the prototype header that is scope information which isn't used for a prototype declaration and the compiler is probably freaking out.
 
So
// declaration of taskInit()
void taskInit (void);

 
If that doesn't work again just declare put extern where the static was ... I know it's not external the code is 10 lines down but all the compiler wants is a promise from you that "taskInit" exists somewhere in the code. The fact it will find it 10 lines later and internal rather than finding it thousands of lines later and external doesn't matter.
 
So as last resort this should work ... it seems stupid I know because it isn't extern but you are just trying to get out of the circular reference and that is a promise to the compiler the code exists.
// declaration of taskInit()
extern void taskInit (void);

post edited by LdB_ECM - 2020/02/28 05:09:27
#14
mlp
boots too small
  • Total Posts : 889
  • Reward points : 0
  • Joined: 2012/09/10 15:12:07
  • Location: previously Microchip XC8 team
  • Status: offline
Re: Global structs with designated initializes 2020/02/28 11:07:08 (permalink)
0
Putting on my Pedant hat here:
acharnley
Running ANSI/99.

Choose one.
 
The language commonly known as "ANSI C" is (since 1990) formally known as C90. It does not support designated initializers.
 
C99 supports designated initializers.

Mark (this opinion available for hire)
#15
acharnley
Super Member
  • Total Posts : 517
  • Reward points : 0
  • Joined: 2016/05/01 06:51:28
  • Location: 0
  • Status: offline
Re: Global structs with designated initializes 2020/03/03 05:39:28 (permalink)
0
Due to XC8 C99 not supporting 24 bit floats I'm having to downgrade to C90, which means I've lost the designated initializers.

Any other means to do


struct taskerProto tasker = {
    
    .tasks = tasksConfig
};

 
or is it back to calling init functions in main.c?
#16
du00000001
Just Some Member
  • Total Posts : 3477
  • Reward points : 0
  • Joined: 2016/05/03 13:52:42
  • Location: Germany
  • Status: offline
Re: Global structs with designated initializes 2020/03/03 08:11:32 (permalink)
+1 (1)
C90 allows for complete initialization of structs (just not initializing individual elements).
Using some NULL elements you should be able to achieve what you need without writing initialization functions.

PEBKAC / EBKAC / POBCAK / PICNIC (eventually see en.wikipedia.org)
#17
LdB_ECM
Super Member
  • Total Posts : 313
  • Reward points : 0
  • Joined: 2019/04/16 22:01:25
  • Location: 0
  • Status: offline
Re: Global structs with designated initializes 2020/03/03 08:40:45 (permalink)
+1 (1)
To add to that you can't use the ".name" format just put each entry in order as a value but comma separated.
For your 1 entry it would be
struct taskerProto tasker = { tasksConfig, };

I assume you have more and you need to go in order.
https://www.geeksforgeeks...gnated-initializers-c/
post edited by LdB_ECM - 2020/03/03 08:42:58
#18
Jump to:
© 2020 APG vNext Commercial Version 4.5