I have an array (C language) that should be initialized at compile time.
For example:
DECLARE_CMD(f1, arg);
DECLARE_CMD(f2, arg);
Yes, it is possible.
The usual trick is to have all the DECLARE_CMD(func, args)
lines in one (or more) include files, and to include those in various places with an appropriate definition for the macro.
For example:
In file 'commands.inc':
DECLARE_CMD(f1, args)
DECLARE_CMD(f2, args)
In some source file:
/* function declarations */
#define DECLARE_CMD(func, args) my_func_type func;
#include "commands.inc"
#undef DECLARE_CMD
/* array with poiners */
#define DECLARE_CMD(func, args) &func,
my_func_type* my_funcs[] = {
#include "commands.inc"
NULL
};
You can actually use a single macro to set the function pointers, do the function declaration, set up enums to access the function pointer and strings to use in error messages and later you can use it in a switch()
.
#define X_MACRO(OP) \
OP(addi, int x, int y) \
OP(divi, int x, int y) \
OP(muli, int x, int y) \
OP(subi, int x, int y)
#define AS_FUNC_PTR(x,...) x,
#define AS_FUNC(x,...) int x(__VA_ARGS__);
#define AS_STRINGS(x,...) #x,
#define AS_ENUMS(x,...) ENUM_##x,
X_MACRO(AS_FUNC)
typedef int (*foo_ptr_t)( int, int );
foo_ptr_t foo[] = { X_MACRO(AS_FUNC_PTR) };
char *foo_strings[] = { X_MACRO(AS_STRINGS) };
enum foo_enums { X_MACRO(AS_ENUMS) };
/** example switch()
#define AS_CASE(x,...) ENUM_x : x(i,j);break;
switch (my_foo_enum){
X_MACRO(AS_CASE)
default: do_error();
}
**/
NOTE: in your question there are semicolons at the end of every line. This will seriously interfere with any attempt to use these macros. So it depends on where and how the DECLARE_CMD(...)
lines are found, and whether you can fix the semicolon problem. If they are simply in a dedicated header file all by themselves, you can do:
#define DECLARE_CMD(func, arg) &func,
my_func_type my_funcs [] {
#include "file_with_declare_cmd.h"
};
...which gets turned into:
my_func_type my_funcs [] {
&f1,
&f2,
};
Read The New C: X Macros for a good explanation of this.
If you can't get rid of the semicolons, this will be processed to:
my_func_type my_funcs [] {
&f1,;
&f2,;
};
... which is obviously a syntax error, and so this won't work.
Yes, you can build dynamic arrays at compile time (not at runtime) (and thank's to Mitchel Humpherys), the idea is to declare your callbacks in the same section like this:
EXAMPLE:
Suppose you have three files a.c, b.c main.c and i.h
into i.h
typedef void (*my_func_cb)(void);
typedef struct func_ptr_s {
my_func_cb cb; /* function callback */
} func_ptr_t;
#define ADD_FUNC(func_cb) \
static func_ptr_t ptr_##func_cb \
__attribute((used, section("my_array"))) = { \
.cb = func_cb, \
}
into a.c
#include "i.h"
static void f1(void) {
....
}
ADD_FUNC(f1);
into b.c
#include "i.h"
static void f2(void) {
....
}
ADD_FUNC(f2);
into main.c
#include "i.h"
static void f3(void) {
....
}
ADD_FUNC(f3);
#define section_foreach_entry(section_name, type_t, elem) \
for (type_t *elem = \
({ \
extern type_t __start_##section_name; \
&__start_##section_name; \
}); \
elem != \
({ \
extern type_t __stop_##section_name; \
&__stop_##section_name; \
}); \
++elem)
int main(int argc, char *argv[])
{
section_foreach_entry(my_array, func_ptr_t, entry) {
entry->cb(); /* this will call f1, f2 and f3 */
}
return 0;
}
IMPORTANT
sometimes the compiler optimizes start/end sections variables, it wipes them out, so when you try to use them, you will have a linker error: error LNK2019: unresolved external symbol ...
to fix this problem, i use the following:
Try to print your linker script:
gcc -Wl,-verbose
copy the text between the two:
==================================================
in a file (example lnk.lds), you should see thing like:
/* Script for -z combreloc: combine and sort reloc sections */
OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64","elf64-x86-64")
........
__start_my_array = .;
.my_array :
{
*(.my_array)
}
__stop_my_array = .;
Compile your program with the updated linker script like this:
gcc -O3 -Xlinker -T"lnk.lds" file.c -o program
If you type strings program | grep "__start_my_array" you should find it.