开发者

Append items to an array with a macro, in C

开发者 https://www.devze.com 2023-01-14 02:11 出处:网络
I have an array (C language) that should be initialized at compile 开发者_开发知识库time. For example:

I have an array (C language) that should be initialized at compile 开发者_开发知识库time.

For example:

DECLARE_CMD(f1, arg);
DECLARE_CMD(f2, arg);

The DECLARE_CMD is called from multiple files.

I want this to be preprocessed in.

my_func_type my_funcs [] = {
   &f1,
   &f2
}

It is possible, with a macro, to append items to an static array?

I am using C99 (with GNU extensions) on gcc4.


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:

  1. 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")

........

  1. ADD your section to the linker script file lnk.lds after the section .data like this (my defined section is called my_array as in the example):
  __start_my_array = .;
  .my_array :
  {
    *(.my_array)      
  }
  __stop_my_array = .;
  1. Compile your program with the updated linker script like this:

    gcc -O3 -Xlinker -T"lnk.lds" file.c -o program

  2. If you type strings program | grep "__start_my_array" you should find it.


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, 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();
}
**/
0

精彩评论

暂无评论...
验证码 换一张
取 消