Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Append items to an array with a macro, in C

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.

like image 247
cojocar Avatar asked Sep 03 '10 07:09

cojocar


People also ask

Can you append to array C?

An array is a contiguous block of memory and if you want to append an element, you have to write it to the position following the last occupied position, provided the array is large enough.

Can you define an array as a macro in C?

In C programming language we can also use Macro to define a constant. As we know that, while declaring an array we need to pass maximum number of elements, for example, if you want to declare an array for 10 elements. You need to pass 10 while declaring. Example: int arr[10];


2 Answers

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.

like image 63
elhadi dp ıpɐɥןǝ Avatar answered Nov 16 '22 01:11

elhadi dp ıpɐɥןǝ


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.

like image 21
detly Avatar answered Nov 16 '22 00:11

detly