error: initializer element is not constant in C [duplicate] - c

This question already has answers here:
Error "initializer element is not constant" when trying to initialize variable with const
(8 answers)
Closed 3 years ago.
Here i am trying to make state machine where FsmHdlr should call a appropriate function based on state and event. What i am getting the above error. How to resolve this.
S16 handleParamReqEvt(void)
{
/* doing something */
RETVALUE(ROK);
}
S16 handleParamRspEvt(param_resp *paramRsp)
{
/* doing something */
RETVALUE(ROK);
}
typedef enum{
IDLE,
CONFIGURED,
MAX_STATE
}STATE;
/* Events in CL */
typedef enum{
PARAM_REQ,
PARAM_RSP
MAX_EVENT
}EVENT;
param_resp *paramMsg;
S16 FsmHdlr[MAX_STATE][MAX_EVENT] =
{
{
/* PHY_STATE_IDLE */
handleParamReqEvt(), //error :initializer element is not constant
handleParamRspEvt(paramMsg) //error: initializer element is not constant
}
};

It sounds like you don't actually want to call the function during initialization (which you can't do for static storage variables anyway, as you've discovered). It sounds like you are building a dispatch table. What you want is called a function pointer.
This how one uses function pointers:
int foo(void) { ... }
int main(void) {
int (*bar)(void) = foo;
bar(); // Calls foo
}
Since the parameters vary based on the type of the event, a 2d array doesn't make much sense. I'd use the following in your case:
S16 fsm_hdrl_idle_param_req(void) { ... }
S16 fsm_hdrl_idle_param_rsp(ParamRsp *param_rsp) { ... }
S16 fsm_hdrl_conf_param_req(void) { ... }
S16 fsm_hdrl_conf_param_rsp(ParamRsp *param_rsp) { ... }
typedef S16 (*FsmReqHdlr)(void);
typedef S16 (*FsmRspHdlr)(ParamRsp*);
typedef struct {
FsmReqHdlr fsm_req_hdlr;
FsmRspHdlr fsm_rsp_hdlr;
} FsmHdlrs;
FsmHdlrs fsm_hdlrs_by_state[MAX_STATE] = {
{ fsm_hdrl_idle_param_req, fsm_hdrl_idle_param_rsp },
{ fsm_hdrl_conf_param_req, fsm_hdrl_conf_param_rsp },
};
Later:
fsm_hdlrs_by_state[state].fsm_req_hdlr();
fsm_hdlrs_by_state[state].fsm_rsp_hdlr(param_rsp);

You could declare pointer to FsmHdlr[MAX_STATE][MAX_EVENT] as a global variable.
S16 (*FsmHdlr)[MAX_STATE][MAX_EVENT] = NULL;
Somewhere in main function allocate the memory to the global pointer as below.
FsmHdlr = malloc(sizeof(S16 [MAX_STATE][MAX_EVENT]));
Then use memcpy to copy array compound literal as below.
memcpy(FsmHdlr,
(S16 [MAX_STATE][MAX_EVENT]) {
{
/* PHY_STATE_IDLE */
handleParamReqEvt(), handleParamRspEvt(paramMsg)
}
},
sizeof((S16 [MAX_STATE][MAX_EVENT]) {
{
/* PHY_STATE_IDLE */
handleParamReqEvt(), handleParamRspEvt(paramMsg)
}
})
);
And dereference as below,
(*FsmHdlr)[0][0];//to access 1st element
(*FsmHdlr)[0][1];//to access 2nd element

You cannot initialise array during declaration using values, what are unknown while compilation.
And, your initialization values are values returned by handleParamReqEvt() and handleParamRspEvt(...), what are unknown.
I suppose that you are thinking about pointers to these functions, nor function values.
So, you should use function names instead of function calls, like below:
S16 FsmHdlr[MAX_STATE][MAX_EVENT] =
{
{
/* PHY_STATE_IDLE */
handleParamReqEvt, //initializer element is NOW constant
handleParamRspEvt //initializer element is NOW constant
}
};
Unfortunately, this will not compile because of wrong array type - now it is not S16[][] (or S16**) like before.
Additionaly, both pointers are pointers of different types:
- first is S16 ( * )();
- second is S16 ( * )(param_resp*);
Fortunately, you can store them both as void* (pointer to anything), but remember that you MUST PROPERLY CAST them before usage.
For cast simplification you can declare the types of these function using typedef directive.
So, the final form of declaration+initialization and usage will be:
// declaration + initialisation of array
void* FsmHdlr[MAX_STATE][MAX_EVENT] =
{
{
/* PHY_STATE_IDLE */
handleParamReqEvt,
handleParamRspEvt
}
};
// declaration of types
typedef S16 (*reqEvt_ptr)(); //signature of handleParamReqEvt()
typedef S16 (*rspEvt_ptr)(param_resp*); //signature of handleParamRspEvt(param_resp*)
// usage:
// handleParamReqEvt
reqEvt_ptr reqEvt = (reqEvt_ptr)FsmHdlr[/* index here */][PARAM_REQ]; // cast
S16 reqResult = reqEvt(); // call
// handleParamRspEvt
rspEvt_ptr rspEvt = (rspEvt_ptr)FsmHdlr[/* index here */][PARAM_RSP]; // cast
S16 rspResult = rspEvt(/* pointer to paramMsg here */); // call

Related

C Unit Test: stub a constant structure (gcc --wrap)

Hi All here is my specific case:
service.h:
typedef struct {
uint8_t (*function1)(void);
uint8_t (*function2)(void);
} const service_struct_t;
extern service_struct_t service_api ;
service.c:
#include "service.h"
static uint8_t foo(void){
return 13+6;
}
static uint8_t bar(void){
return 7*6;
}
service_struct_t service_api = {
.function1 = foo,
.function2 = bar,
};
I need to stub (to mock, to replace) these functions but I have no right to change that original code. I'm using gcc to compile the unit tests. I've failed to:
use the --wrap option of gcc straight on foo and bar since they are static to source.c :
#include "service.h"
#define ENABLE_STUB 1 /* that is actually a variable toggled at runtime */
uint8_t __real_foo(void);
uint8_t __wrap_foo(void){
if(ENABLE_STUB){
return 1;
}else{
return __real_foo();
}
}
/* same for bar */
use the --wrap option of gcc onto the service_api object symbol because it's not a function
#include "service.h"
#define ENABLE_STUB 1 /* that is actually a variable toggled at runtime */
uint8_t __real_service_api ;
uint8_t __wrap_service_api = {
.function1 = foo,
.function2 = bar,
}
static uint8_t foo(void){
if(ENABLE_STUB){
return 1;
}else{
return __real_service_api.function1();
}
}
/* same for bar */
simply reassign the service_api member functions since the structure is constant and already assigned.
#include "service.h"
#define ENABLE_STUB 1 /* that is actually a variable toggled at runtime */
service_struct_t backup_service_api = {
.function1 = service_api.function1;
.function2 = service_api.function2;
}
service_struct_t stub_service_api = {
.function1 = foo;
.function2 = bar;
}
uint8_t foo(void){
if(ENABLE_STUB){
return 1;
}else{
return __real_foo();
}
}/* same for bar */
void service_poke_stub(bool_t enable_stubs){
if(enable_stubs){
service_api.function1 = stub_service_api.function1
service_api.function2 = stub_service_api.function2
}else{
service_api.function1 = backup_service_api.function1
service_api.function2 = backup_service_api.function2
}
}
thanks already for your help
You can't mock the functions in the structure, as you already found out.
So it depends on what you like to test:
If you want to test whether the structure contains the correct functions, the module service.c is your module-under-test and should be used as is. You need to check the correctness by watching what is done by the functions.
If you want to test that the structure is used correctly, you will mock the whole module. Now you are free to put in it whatever you want.
If your source code does not allow this, the design is bad for testing. This is often the case when the architecture is not done with testability in mind.

C arrays of function pointers

I have three function arrays each pointing to a number of functions.
I can call any of those functions form the three tables.
Now I would like to dereference the three arrays into a single array of function pointers but I just can't get it working!
void afunc1(void);
void afunc2(void);
void afunc3(void);
void bfunc1(void);
void bfunc2(void);
void bfunc3(void);
void cfunc1(void);
void cfunc2(void);
void cfunc3(void);
void(*FuncTbla[])(void) = { afunc1, afunc2, afunc3 };
void(*FuncTblb[])(void) = { bfunc1, bfunc2, bfunc3 };
void(*FuncTblc[])(void) = { cfunc1, cfunc2, cfunc3 };
void (*AllFuncTbls[])(void) = { &FuncTbla, &FuncTblb, &FuncTblc };
int TblNo = 1, FuncNo = 1; // tblNo 1 = table b
bFunc2(); // calls bFunc2 directly
FuncTblb[FuncNo](); // Calls Function bFunc2 via function table b
// Call same function using table of function tables
AllFuncTbls[TblNo][FuncNo](); // Does not compile - expression must be a pointer to a complete object type!!!
Two things: First of all remember that arrays naturally decays to pointers to their first element; And secondly it will become so much easier if you use type-aliases for the function types.
Armed with that knowledge you could do it like e.g.
// Type-alias to simplify using function pointers
typedef void (*function_type)(void);
// The three tables
function_type FuncTbla[] = { &afunc1, &afunc2, &afunc3 };
function_type FuncTblb[] = { &bfunc1, &bfunc2, &bfunc3 };
function_type FuncTblc[] = { &cfunc1, &cfunc2, &cfunc3 };
// A table of pointers to the first elements of each array
function_type *AllFuncTbls[] = { FuncTbla, FuncTblb, FuncTblc };
To call a function using AllFuncTbls is as simple as
AllFuncTbls[TblNo][FuncNo]();
If you use typedefs it works:
void afunc1(void);
// ...
typedef void (*funcPtr)(void);
// void(*FuncTbla[])(void) = { afunc1, afunc2, afunc3 };
// ...
funcPtr FuncTbla[] = { afunc1, afunc2, afunc3 };
funcPtr FuncTblb[] = { bfunc1, bfunc2, bfunc3 };
funcPtr FuncTblc[] = { cfunc1, cfunc2, cfunc3 };
//void (*AllFuncTbls[])(void) = { &FuncTbla, &FuncTblb, &FuncTblc };
funcPtr* AllFuncTbls[] = { FuncTbla, FuncTblb, FuncTblc };
// Use an Array of pointers to function pointers here, not an array of function pointers!
// ...
// Call same function using table of function tables
AllFuncTbls[TblNo][FuncNo](); // Compiles now
I commented out the lines that had to be changed.
Using typealiases is better approach, but if you were courious how to do it without it:
void(**AllFuncTbls[])(void) = { FuncTbla, FuncTblb, FuncTblc};

object oriented approach in c program

I don't have much experience in Object oriented programming.I am trying to create an object in c which will have its own methods.
I have declared structure which have pointers to function. All instance of this variable are going to point same function. But currently I need to initialize every instance of variable as in main (Line 1 and Line 2). So is there any method that will initialize its default value when I declare it?
#include <stdio.h>
#include <stdlib.h>
typedef struct serialStr Serial;
struct serialStr
{
void(*init)(Serial*);
void(*open)();
void(*close)();
};
void open()
{
printf("Open Port Success\n");
return;
}
void close()
{
printf("Close port Success\n");
return;
}
void init(Serial* ptr)
{
ptr->open = open;
ptr->close = close;
}
int main()
{
Serial serial,serial_2;
serial.init = init;
serial.init(&serial); // Line1
serial_2.init = init;
serial_2.init(&serial_2); // Line2
serial.open();
//rest of code
serial.close();
serial_2.open();
serial_2.close();
return 0;
}
In C, the standard way would be to declare an initializer macro:
#define SERIAL_INITIALIZER { .init = init, .open = open, /* and others */ }
Serial serial = SERIAL_INITIALIZER;
In most cases in C there is simply no need for dynamic intialization of variables. You only need it for malloced objects.
C++ add some automatization by calling constructor/destructor. In pure C is no way to do so. You should do all steps manually: create and initialize object (call constructor-like function for structure), call functions by pointers from the structure instance, call destructor (it should destroy the instance and free related resources).
If is no polymorphism in your task then use simple way - without pointers to functions, but each function (method) should take pointer to the object.
Common case example:
struct MyStruct
{
// data
};
struct MyStruct* createMyStruct(/* maybe some input */)
{
// create, init and return the structure instance
}
void destoyMyStruct(struct MyStruct* obj)
{
// free resources and delete the instance
}
void doSomeAction(struct MyStruct* obj /* , some other data */)
{
// ...
}
int main()
{
struct MyStruct* object = createMyStruct();
doSomeAction(object);
destoyMyStruct(object);
return 0;
}
Edit 1: macro is only for very simple cases and error-prone way.
Typically, you would do this through "opaque type". Meaning that you declare an object of incomplete type in your header:
typedef struct Serial Serial;
And then in the C file, you place the actual struct definition. This will hide the contents of the struct to the caller (private encapsulation). From your constructor, you could then set up private member functions:
struct Serial
{
void(*init)(void);
void(*open)(void);
void(*close)(void);
};
// private member functions:
static void open (void);
...
// constructor:
Serial* SerialCreate (void)
{
Serial* s = malloc(sizeof (*s));
...
s->open = open;
return s;
}
This means that if you wish to inherit the class, you will only need to change the constructor.
Though of course, if you wish to implement true polymorphism, you don't want to change any code. You could solve this by passing the init function as parameter to the constructor.
header file:
typedef void init_func_t (void);
c file:
// constructor:
Serial* SerialCreate (init_func_t* init)
{
Serial* s = malloc(sizeof (*s));
...
init();
return s;
}
And then from the init function in the inherited class, set all private member functions.

C initializer element is not constant - pointers in struct

Code snippets appear below
/// System Parameters for the gateway
typedef struct {
bool mobile_gateway; // true = GPS receiver used, else false
uint16_t gw_ordinal_number; // Starting at 1, the ordinal gateway number
} CONFIG_SYSTEM_PARMS;
// Data types for the system parameters
typedef enum {
CFGPARM_TYPE_BOOL,
CFGPARM_TYPE_UINT,
CFGPARM_TYPE_INT,
CFGPARM_TYPE_DOUBLE,
CFGPARM_NUM_TYPES
} CONFIG_PARM_DATA_TYPES;
// These mnemonic names and data types correspond to the system parameters
#define SYS_PARM_MOBILE_GATEWAY_MNEM "MOBILEGW"
#define SYS_PARM_GATEWAY_NUMBER_MNEM "GWNUM"
#define SYS_PARM_MOBILE_GATEWAY_TYPE CFGPARM_TYPE_BOOL
#define SYS_PARM_GATEWAY_NUMBER_TYPE CFGPARM_TYPE_UINT
typedef struct {
char FILLER; // TODO - Define User Parms later...
} CONFIG_USER_PARMS;
static const uint16_t TOTAL_CONFIG_PARM_BYTESIZE = sizeof(CONFIG_SYSTEM_PARMS) + sizeof(CONFIG_USER_PARMS);
typedef struct {
const char *sysparm_mnemonic;
CONFIG_PARM_DATA_TYPES sysparm_data_type;
void *sysparm_data_ptr;
} CONFIG_PARM_LOOKUP_TABLE;
// ****************************************
static LT_LOGGER_DATA gateway_log_data;
static CONFIG_SYSTEM_PARMS *config_sys_parms_ptr = &gateway_log_data.config_system_parms;
// **************************************************
// Lookup table for the System Parms
//
// CAA: This is a stinking mess - There is no easy way to define &gateway_log_data.config_system_parms
// outside of the following initialization, so the result is more verbose than it should be
static const CONFIG_PARM_LOOKUP_TABLE config_parm_lookup_table[] = {
{ SYS_PARM_MOBILE_GATEWAY_MNEM, SYS_PARM_MOBILE_GATEWAY_TYPE, &config_sys_parms_ptr->mobile_gateway},
{ SYS_PARM_GATEWAY_NUMBER_MNEM, SYS_PARM_GATEWAY_NUMBER_TYPE, &gateway_log_data.config_system_parms.gw_ordinal_number},
// FILLER to mark end of list
{ "", 0, NULL}
};
The question is this:
Why, am I getting the error : initializer element is not constant (near initialization for 'config_parm_lookup_table[0].sysparm_data_ptr')
when the initializer for config_parm_lookup_table[1].sysparm_data_ptr is effectively using the same construct?
Obviously, I have a work-around, but it seems verbose.

passing argument from incompatible pointer type

static struct dll_wifi_state **dll_states;
enum dll_type {
DLL_UNSUPPORTED,
DLL_ETHERNET,
DLL_WIFI
};
struct dll_state {
enum dll_type type;
union {
struct dll_eth_state *ethernet;
struct dll_wifi_state *wifi;
} data;
};
static struct dll_state *dll_states = NULL;
struct dll_wifi_state {
int link;
// A pointer to the function that is called to pass data up to the next layer.
up_from_dll_fn_ty nl_callback;
bool is_ds;
};
This is the method whose pointer is being passed in the dll_wifi_state struct.
static void up_from_dll(int link, const char *data, size_t length)
{
//some code here
}
In other file, I am calling this method
void reboot_accesspoint()
{
// We require each node to have a different stream of random numbers.
CNET_srand(nodeinfo.time_of_day.sec + nodeinfo.nodenumber);
// Provide the required event handlers.
CHECK(CNET_set_handler(EV_PHYSICALREADY, physical_ready, 0));
// Prepare to talk via our wireless connection.
CHECK(CNET_set_wlan_model(my_WLAN_model));
// Setup our data link layer instances.
dll_states = calloc(nodeinfo.nlinks + 1, sizeof(struct dll_state));
for (int link = 0; link <= nodeinfo.nlinks; ++link) {
switch (linkinfo[link].linktype) {
case LT_LOOPBACK:
dll_states[link].type = DLL_UNSUPPORTED;
break;
case LT_WAN:
dll_states[link].type = DLL_UNSUPPORTED;
break;
case LT_LAN:
dll_states[link].type = DLL_ETHERNET;
dll_states[link].data.ethernet = dll_eth_new_state(link, up_from_dll);
break;
case LT_WLAN:
dll_states[link].type = DLL_WIFI;
dll_states[link].data.wifi = dll_wifi_new_state(link,
up_from_dll,
true /* is_ds */);
break;
}
}
// printf("reboot_accesspoint() complete.\n");
}
It works fine like this, but I want to add another argument i.e. up_from_dll((int link, const char *data, size_t length, int seq). And as soon as I add this argument, following error starts coming up
ap.c:153: warning: passing argument 2 of ‘dll_wifi_new_state’ from incompatible pointer type
Is there a way of adding another argument to that method without getting error ??? I am really bad with pointers :(
Any help would be much appreciated.
Line 153 :
dll_states[link].data.wifi = dll_wifi_new_state(link,
up_from_dll,
true /* is_ds */);
And method
struct dll_wifi_state *dll_wifi_new_state(int link,
up_from_dll_fn_ty callback,
bool is_ds)
{
// Ensure that the given link exists and is a WLAN link.
if (link > nodeinfo.nlinks || linkinfo[link].linktype != LT_WLAN)
return NULL;
// Allocate memory for the state.
struct dll_wifi_state *state = calloc(1, sizeof(struct dll_wifi_state));
// Check whether or not the allocation was successful.
if (state == NULL)
return NULL;
// Initialize the members of the structure.
state->link = link;
state->nl_callback = callback;
state->is_ds = is_ds;
return state;
}
I haven't changed anything else apart from adding the new parameter to up_from_dll.
The second parameter to dll_wifi_new_state is up_from_dll_fn_ty callback.
It's not in your code listing right now, but up_from_dll_fn_ty is a typedef saying that the up_from_dll_fn_ty is a function pointer with specific parameters (which don't include int seq)
When you updated up_from_dll with different parameters, it no longer matches the type specified by up_from_dll_fn_ty and expected as the second parameter for dll_wifi_new_state. You'll need to add the parameter to up_from_dll_fn_ty and you should be good.
If you post the definition of up_from_dll_fn_ty, it would make the question have all the information and allow me to help you more if you still need it.
You're looking for something like:
typedef void (*up_from_dll_fn_ty)(int link, const char *data, size_t length);
and change it to
typedef void (*up_from_dll_fn_ty)(int link, const char *data, size_t length, int seq);
Here's a link to a question that has good information about creating typedefs for function pointers:
Understanding typedefs for function pointers in C

Resources