2009-06-04 7 views
8

W celu zapewnienia, że ​​niektóre kod inicjalizacji biegnie przed main (przy użyciu Arduino/AVR-gcc) mam kod, takich jak:Jak przeprowadzić wstępną inicjalizację w C/C++ za pomocą avr-gcc?

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

Init init; 

Idealnie chciałabym móc po prostu napisać:

initialize(); 

ale to nie skompilować ...

Czy istnieje mniej gadatliwy sposób, aby osiągnąć ten sam efekt?

Uwaga: kod jest częścią szkicu Arduino więc funkcja main jest generowany automatycznie i nie mogą być zmieniane (na przykład wywołać initialize przed jakimkolwiek innym kodem).

Aktualizacja: idealnie inicjalizacji byłyby wykonywane w funkcji setup, ale w tym przypadku jest inny kod w zależności od tego, który występuje przed main.

Odpowiedz

11

Można użyć GCC constructor attribute celu zapewnienia, że ​​zostanie wezwany przed main():

void Init(void) __attribute__((constructor)); 
void Init(void) { /* code */ } // This will always run before main() 
+0

+1 Proste i zadbane. Czy to też działa na avr-gcc? –

+0

Nie wiem na pewno, ponieważ nigdy nie używałem avr-gcc, ale według tej strony http://www.nongnu.org/avr-libc/user-manual/porting.html, obsługuje avr-gcc inne rodzaje atrybutów. –

+1

Próbowałem tego przy użyciu oprogramowania Arduino (które jest wspierane przez avr-gcc) i działa. Przyjmuję tę odpowiedź, dziękuję. –

2

Twoje rozwiązanie w prosty i czysty. Możesz dodatkowo umieścić kod w anonimowym obszarze nazw. Nie widzę żadnej potrzeby, aby to lepiej :)

+0

Zawinięcie w anonimowym obszarze nazw prawdopodobnie uniemożliwi nazwę klasy i jej instancji zanieczyszczającej przestrzeń nazw (co jest dobre), ale to naprawdę nie pomoże uczynić kodu mniej szczegółowym ... –

0

Jasne, można umieścić to w jednym z Twoich plików nagłówkowych, powiedzmy preinit.h:

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

a następnie w jednej swoich jednostek kompilacji, umieścić:

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

wiem, że to kludge, ale nie jestem świadomy dowolny przenośny sposób to zrobić pre-main inicjowania bez użycia konstruktora klasy wykonywany w zakresie plików.

Powinieneś także uważać na włączenie więcej niż jednej z tych funkcji inicjalizacji, ponieważ nie wierzę, że C++ dyktuje kolejność - może być losowa.

nie jestem tego pewien „szkic”, o której mówisz, ale mając przed przeszedł do kompilatora byłoby możliwe, aby przekształcić jednostkę główną z kompilacji skryptu, coś jak:

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

można zobaczyć, jak wpłynie to na swój program, ponieważ:

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

generuje następujące z wezwaniem initialize() dodania:

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

Może się zdarzyć, że nie będziesz w stanie przetworzyć wygenerowanego pliku, w takim przypadku powinieneś zignorować tę ostateczną opcję, ale na tym właśnie bym się najpierw przyjrzał.

+0

Rozwiązanie nie działa ". t musi być przenośny. Musi działać tylko przy użyciu avr-gcc. –

0

Użyj statycznych członków klas. Są one inicjowane przed wejściem do głównej. Wadą jest to, że nie można kontrolować kolejności inicjalizacji statycznych członków klasy.

tutaj jest przykład przekształcone:

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; 
+3

To jest bardziej szczegółowe, nie mniej. –

+0

Ten pomysł sprawia, że ​​zastanawiam się, czy kolejność inicjowania statycznych członków w hierarchii * klasy * jest zdefiniowana w C++? Jeśli tak, to byłby sposób na rozwiązanie "statycznego niepowodzenia zlecenia inicjalizacji". –

+0

@Piotr: Kolejność inicjowania w jednostce tłumaczeniowej jest zdefiniowana jako "kolejność, w której pojawiają się definicje". Pomiędzy jednostkami tłumaczeniowymi jest niezdefiniowana. –

2

Jeśli używasz środowiska Arduino, czy jest jakiś powód, dla którego nie można umieścić go w setup method?

Oczywiście jest to po konfiguracji sprzętowej specyficznej dla Arduino, więc jeśli masz tak niskopoziomowe rzeczy, które naprawdę muszą przejść przed main, potrzebujesz magii konstruktora.

UPDATE:

Ok, jeśli ma to być wykonane przed głównym Myślę, że jedynym sposobem jest użycie konstruktora jak już zrobić.

Zawsze można zrobić makro preprocesora go:

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

Teraz powinno działać:

RUN_EARLY(initialize()) 

Ale to nie jest tak naprawdę czyni rzeczy krótsze, tylko przeniesienie kodu gadatliwe dookoła.

+0

+1, ale niestety nie mogę użyć setup() do tego. Zaktualizuję pytanie, aby to wyjaśnić. –

4

Można dokonać powyższego bardzo nieznacznie krótsza dając „zainicjować” typ zwrotny, a przy użyciu to, aby zainicjować zmienną globalną:

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

Jednak trzeba być ostrożnym z tym, średnia robi nie gwarantuje, że powyższa inicjalizacja (lub ta dla obiektu init) ma miejsce przed uruchomieniem głównego (3.6.2/3):

Jest zdefiniowana w ramach implementacji niezależnie od tego, czy dynamiczna inicjalizacja (8.5, 9.4 , 12.1, 12.6.1) obiektu zakresu przestrzeni nazw jest wykonywane przed pierwszym stwierdzeniem mai n.

Gwarantowane jest tylko to, że inicjalizacja nastąpi przed użyciem "dummy".

Bardziej uciążliwym wariantem (jeśli to możliwe) może być użycie "-D main = avr_main" w pliku makefile. Następnie można dodać własną główną w następujący sposób:

// 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); 
} 

Przynajmniej tutaj masz gwarancję, że inicjalizacja nastąpi, gdy się tego spodziewamy.

+0

Niestety środowisko Arduino kontroluje wykonanie kompilatora, więc nie ma pliku Makefile, który można zmienić. –

3

Oto nieco zła metoda osiągnięcia tego celu:

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

Dodaj poniższe linie do flagi linkera: --wrap main

np.

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

Łącznik zastąpi wszystkie połączenia do main z połączeń do __wrap_main, zobacz ld man page na --wrap

+0

Niestety nie mam wpływu na linię poleceń gcc (faktycznie avr-gcc), ponieważ jest ona obsługiwana automatycznie przez środowisko Arduino. –

2

Możesz użyć sekcji ".init *", aby dodać kod C, który będzie uruchamiany przed main() (i nawet w środowisku wykonawczym C). Sekcje te są połączone w plik wykonywalny na końcu i wywoływane w określonym czasie podczas inicjowania programu. Można uzyskać listę tutaj:

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

.init1 na przykład jest słabo związany __init(), więc jeśli zdefiniować __init(), to zostanie połączone i nazwał pierwszą rzeczą. Jednak stos nie został skonfigurowany, więc musisz być ostrożny w tym, co robisz (używaj tylko zmiennej register8_t, nie wywołuj żadnych funkcji).

0

Oto sposób wykonywania pre-główne kodowania. Istnieją sever init sekcje wykonywane przed głównym, odnosi się do sekcji initN http://www.nongnu.org/avr-libc/user-manual/mem_sections.html.

W każdym razie działa to tylko z optymalizacją -O0 z jakiegoś powodu. Nadal próbuję dowiedzieć się, która opcja "zoptymalizowała" mój kod pre-main.

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

Aktualizacja, okazuje się, że twierdziłem, że ta funkcja nie jest używana, co pozwala zoptymalizować rutynę. Miałem zabić funkcję nieużywanego ostrzeżenia. Zamiast tego jest ustawiony na używany używany atrybut.

Powiązane problemy