Как я могу выполнить предварительную инициализацию в C / C ++ с помощью avr-gcc? - PullRequest
9 голосов
/ 04 июня 2009

Чтобы обеспечить выполнение некоторого кода инициализации до main (с использованием Arduino / avr-gcc), у меня есть такой код:

class Init {
public:
    Init() { initialize(); }
};

Init init;

В идеале я бы хотел просто написать:

initialize();

но это не компилируется ...

Есть ли менее многословный способ достижения того же эффекта?

Примечание: код является частью эскиза Arduino, поэтому функция main генерируется автоматически и не может быть изменена (например, для вызова initialize перед любым другим кодом).

Обновление: в идеале инициализация должна выполняться в функции setup, но в этом случае есть другой код, зависящий от него, который происходит до main.

Ответы [ 9 ]

13 голосов
/ 04 июня 2009

Вы можете использовать атрибут GCC constructor , чтобы обеспечить его вызов до main():

void Init(void) __attribute__((constructor));
void Init(void) { /* code */ }  // This will always run before main()
4 голосов
/ 04 июня 2009

Вы можете сделать вышеупомянутое немного короче, указав «initialize» тип возвращаемого значения и используя его для инициализации глобальной переменной:

int initialize();
int dummy = initialize();

Тем не менее, вы должны быть осторожны с этим, стандарт не гарантирует, что вышеуказанная инициализация (или инициализация вашего объекта init) произойдет до запуска main (3.6.2 / 3):

Определяется реализацией независимо от того, выполняется ли динамическая инициализация (8.5, 9.4, 12.1, 12.6.1) объекта области имен перед первым оператором main.

Единственное, что гарантировано, это то, что инициализация произойдет до того, как будет использован "фиктивный".

Более навязчивым вариантом (если это возможно) может быть использование «-D main = avr_main» в вашем make-файле. Затем вы можете добавить свой собственный основной файл следующим образом:

// Add a declaration for the main declared by the avr compiler.
int avr_main (int argc, const char * argv[]);  // Needs to match exactly

#undef main
int main (int argc, const char * argv[])
{
  initialize ();
  return avr_main (argc, argv);
}

По крайней мере, здесь вы гарантировано, что инициализация произойдет, когда вы ожидаете.

3 голосов
/ 04 июня 2009

Вот несколько злой метод достижения этого:

#include <stdio.h>

static int bar = 0;

int __real_main(int argc, char **argv);

int __wrap_main(int argc, char **argv)
{
    bar = 1;
    return __real_main(argc, argv);
}

int main(int argc, char **argv)
{
    printf("bar %d\n",bar);
    return 0;
}

Добавьте следующее к флагам компоновщика: --wrap main

например.

gcc -Xlinker --wrap -Xlinker main a.c

Компоновщик заменит все звонки на main звонками на __wrap_main, см. Справочную страницу ld на --wrap

2 голосов
/ 29 сентября 2009

Вы можете использовать разделы ".init *", чтобы добавить код C, который будет запускаться перед main () (и даже средой выполнения C). Эти разделы в конце связаны с исполняемым файлом и вызываются в определенное время во время инициализации программы. Вы можете получить список здесь:

http://www.nongnu.org/avr-libc/user-manual/mem_sections.html

.init1, например, слабо связан с __init (), поэтому, если вы определите __init (), он будет связан и вызван первым делом. Однако стек не был настроен, поэтому вы должны быть осторожны в том, что делаете (используйте только переменную register8_t, а не вызывайте какие-либо функции).

2 голосов
/ 04 июня 2009

Если вы используете среду Arduino, есть ли причина, по которой вы не можете поместить ее в метод установки ?

Конечно, это после установки аппаратного обеспечения для Arduino, так что если у вас есть такие низкоуровневые вещи, которые действительно нужно выполнить до main, тогда вам понадобится немного магии конструктора.

UPDATE:

Хорошо, если это нужно сделать до основного, я думаю, что единственный способ - использовать конструктор, как вы уже это делаете.

Вы всегда можете сделать из него макрос препроцессора:

#define RUN_EARLY(code) \
namespace { \
    class Init { \
        Init() { code; } \
    }; \
    Init init; \
}

Теперь это должно работать:

RUN_EARLY(initialize())

Но на самом деле это не делает вещи короче, а просто перемещает подробный код.

2 голосов
/ 04 июня 2009

Ваше решение просто и чисто. Кроме того, вы можете поместить свой код в анонимное пространство имен. Я не вижу необходимости делать это лучше, чем это:

0 голосов
/ 25 мая 2016

Вот как я выполняю предварительное основное кодирование. Существует несколько разделов init, выполняемых перед main, относится к http://www.nongnu.org/avr-libc/user-manual/mem_sections.html разделам initN.

Во всяком случае, по какой-то причине это работает только при оптимизации -O0. Я все еще пытаюсь выяснить, какая опция «оптимизировала» мой основной код сборки.

static void
__attribute__ ((naked))
__attribute__ ((section (".init8")))    /* run this right before main */
__attribute__ ((unused))    /* Kill the unused function warning */
stack_init(void) {assembly stuff}

Обновление, оказывается, я утверждал, что эта функция не используется, что приводит к оптимизации процедуры. Я должен был убить функцию неиспользованного предупреждения. Вместо этого используется фиксированный используемый атрибут.

0 голосов
/ 04 июня 2009

Использовать статические члены классов. Они инициализируются перед входом на главную. Недостатком является то, что вы не можете контролировать порядок инициализации статических членов класса.

Вот ваш пример преобразился:

class Init {
private:
    // Made the constructor private, so to avoid calling it in other situation
    // than for the initialization of the static member.
    Init() { initialize(); }

private:
    static Init INIT;
};


Init Init::INIT;
0 голосов
/ 04 июня 2009

Конечно, вы помещаете это в один из ваших заголовочных файлов, скажем preinit.h:

class Init { public: Init() { initialize(); } }; Init init;

, а затем в одной ваших единиц компиляции введите:

void initialize(void) {
    // weave your magic here.
}
#include "preinit.h"

Я знаю, что это клочок, но я не знаю любого переносимого способа выполнить предварительную инициализацию без использования конструктора класса, выполняемого в области видимости файла.

Вы также должны быть осторожны с включением более одной из этих функций инициализации, поскольку я не верю, что C ++ определяет порядок - он может быть случайным.

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

awk '{print;if (substr($0,0,11) == "int main (") {print "initialize();"};}'

Вы можете видеть, как это повлияет на вашу программу, потому что:

echo '#include <stdio.h>
int main (void) {
    int x = 1;
    return 0;
}' | awk '{
    print;
    if (substr($0,0,11) == "int main (") {
        print "    initialize();"
    }
}'

генерирует следующее с добавленным вызовом initialize():

#include <stdio.h>
int main (void) {
    initialize();
    int x = 1;
    return 0;
}

Может случиться так, что вы не сможете пост-обработать сгенерированный файл, и в этом случае вам следует игнорировать этот последний вариант, но это то, что я бы посмотрел сначала.

...