Добавить элементы в массив с макросом, в C - PullRequest
6 голосов
/ 03 сентября 2010

У меня есть массив ( C язык), который должен быть инициализирован во время компиляции.

Например:

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

DECLARE_CMD вызывается из нескольких файлов.

Я хочу, чтобы это было предварительно обработано в.

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

Можно с помощью макроса добавлять элементы в статический массив?

Я использую C99(с расширениями GNU) на gcc4.

Ответы [ 4 ]

3 голосов
/ 07 мая 2016

Да, вы можете создавать динамические массивы во время компиляции (не во время выполнения) (и спасибо Митчелу Хамферису), идея состоит в том, чтобы объявить ваши обратные вызовы в одном и том же разделе следующим образом:

Пример:

Предположим, у вас есть три файла a.c, b.c main.c и i.h

в 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,                           \
    }

в a.c

#include "i.h"

static void f1(void) {
   ....
}

ADD_FUNC(f1);

в б.с.

#include "i.h"

static void f2(void) {
   ....
}

ADD_FUNC(f2);

в 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;
 }

ВАЖНО

иногда компилятор оптимизирует переменные начала / конца секций, стирает их, поэтому при попытке их использования у вас будет ошибка компоновщика: ошибка LNK2019: неразрешенный внешний символ ...

, чтобы исправить эту проблему, я использую следующее:

  1. Попробуйте напечатать скрипт компоновщика:

    gcc -Wl, -verbose

скопируйте текст между двумя:

============================================ ======

в файле (пример lnk.lds ), вы должны увидеть что-то вроде:

/ * Скрипт для -z combreloc: объединять и сортировать разделы перемещений * /

OUTPUT_FORMAT ("elf64-x86-64", "elf64-x86-64", "elf64-x86-64")

........

  1. ДОБАВЬТЕ свой раздел в файл сценария компоновщика lnk.lds после раздела .data примерно так (мой определенный раздел называется my_array, как в примере):

  __start_my_array = .;
  .my_array :
  {
    *(.my_array)      
  }
  __stop_my_array = .;

  1. Скомпилируйте вашу программу с помощью обновленного скрипта компоновщика, например:

    gcc -O3 -Xlinker -T "lnk.lds" file.c -o программа

  2. Если вы наберете строки, программа | grep "__start_my_array" вы должны найти его.

2 голосов
/ 03 сентября 2010

ПРИМЕЧАНИЕ: в вашем вопросе есть точки с запятой в конце каждой строки. Это серьезно помешает любой попытке использовать эти макросы. Так что это зависит от того, где и как находятся строки DECLARE_CMD(...), и можете ли вы решить проблему с точкой с запятой. Если они просто находятся в отдельном заголовочном файле, вы можете сделать:

#define DECLARE_CMD(func, arg) &func,

my_func_type my_funcs [] {
    #include "file_with_declare_cmd.h"
};

... который превращается в:

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

Прочитайте Новые макросы C: X для хорошего объяснения этого.

Если вы не можете избавиться от точек с запятой, это будет обработано следующим образом:

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

... что, очевидно, является синтаксической ошибкой, и поэтому это не сработает.

1 голос
/ 03 сентября 2010

Да, это возможно. Обычный трюк состоит в том, чтобы все DECLARE_CMD(func, args) строки содержались в одном (или более) включаемых файлах и включать их в разных местах с соответствующим определением макроса.

Например:

В файле 'commands.inc':

DECLARE_CMD(f1, args)
DECLARE_CMD(f2, args)

В каком-то исходном файле:

/* 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
};
0 голосов
/ 16 мая 2016

На самом деле вы можете использовать один макрос для установки указателей на функции, объявления функций, установки перечислений для доступа к указателю на функции и строкам для использования в сообщениях об ошибках, а затем вы можете использовать его в 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();
}
**/
...