Como posso executar a inicialização pré-principal em C / C ++ com avr-gcc?
-
09-09-2019 - |
Pergunta
A fim de garantir que algumas corridas de código de inicialização antes main
(usando Arduino / avr-gcc) Eu tenho o código como o seguinte:
class Init {
public:
Init() { initialize(); }
};
Init init;
Idealmente, eu gostaria de ser capaz de simplesmente escreve:
initialize();
Mas isso não compilação ...
Existe uma menos extenso caminho para conseguir o mesmo efeito?
Nota:. o código é parte de um esboço Arduino então a função main
é gerado automaticamente e não podem ser modificados (por exemplo, para initialize
chamada antes de qualquer outro código)
Update:. idealmente a inicialização seria realizada na função setup
, mas neste caso há outro código dependendo isso que ocorre antes main
Solução
Você pode usar o atributo do GCC constructor
para garantir que ele é chamado antes main()
:
void Init(void) __attribute__((constructor));
void Init(void) { /* code */ } // This will always run before main()
Outras dicas
Você pode fazer o acima muito ligeiramente mais curto, dando "inicializar" um tipo de retorno, e usando isso para inicializar uma variável global:
int initialize();
int dummy = initialize();
No entanto, você precisa ter cuidado com isso, o padrão não garante que a inicialização acima (ou um para o seu objeto de inicialização) acontece antes principal é executado (3.6.2 / 3):
É definido implementação-se ou não a inicialização dinâmica (8.5, 9.4, 12.1, 12.6.1) de um objeto do escopo namespace é feito antes da primeira declaração da principal.
A única coisa que é garantido é que a inicialização ocorrerá antes 'fictício' é sempre utilizada.
Uma opção mais intrusiva (se é possível) pode ser usar "-d principais = avr_main" em seu makefile. Você pode então adicionar seus próprios principal da seguinte forma:
// 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);
}
Pelo menos aqui você tem a garantia de que a inicialização ocorrerá quando você espera.
Aqui está um método um tanto mal de conseguir isso:
#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;
}
Adicione o seguinte para as bandeiras vinculador: --wrap main
por exemplo.
gcc -Xlinker --wrap -Xlinker main a.c
O vinculador irá substituir todas as chamadas para main
com chamadas para __wrap_main
, consulte a ld página homem em --wrap
A sua solução em simples e limpo. O que você pode adicionalmente fazer é colocar o seu código no namespace anônimo. Eu não vejo qualquer necessidade de torná-lo melhor do que isso:)
Se você estiver usando o ambiente Arduino, há alguma razão que você não pode colocá-lo na configuração método ?
Claro, isso é, após a configuração de hardware específicos do Arduino, então se você tem essas coisas de baixo nível que ele realmente tem que ir antes main
, então você precisa de um pouco de magia construtor.
UPDATE:
Ok, se ele tem que ser feito antes da principal Acho que a única maneira é usar um construtor como você já faz.
Você pode sempre fazer uma macro pré-processador dele:
#define RUN_EARLY(code) \
namespace { \
class Init { \
Init() { code; } \
}; \
Init init; \
}
Agora, isso deve funcionar:
RUN_EARLY(initialize())
Mas não é realmente fazer as coisas mais curto, apenas movendo o código detalhado ao redor.
Você pode usar o ".init *" seções para adicionar o código C para ser executado antes de main () (e até mesmo o tempo de execução C). Estas secções são ligados no executável no final e chamou-se a uma hora específica durante a inicialização do programa. Você pode obter a lista aqui:
http://www.nongnu.org/avr-libc /user-manual/mem_sections.html
.init1 por exemplo, é fracamente ligada a __init (), então se você definir __init (), ele será ligado e chamado primeira coisa. No entanto, a pilha não foi configurado, então você tem que ter cuidado no que você faz (somente usar variável register8_t, não chamar quaisquer funções).
Utilize membros estáticos de classes. Eles são inicializados antes de entrar para principal. A desvantagem é que você não pode controlar a ordem da inicialização dos membros da classe estática.
Aqui é o seu exemplo transformado:
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;
Claro, você colocar isso em um de seus seus arquivos de cabeçalho, dizem preinit.h:
class Init { public: Init() { initialize(); } }; Init init;
e, em seguida, em um de suas unidades de compilação, colocar:
void initialize(void) {
// weave your magic here.
}
#include "preinit.h"
Eu sei que é um truque, mas eu não estou ciente de qualquer maneira portátil para fazer a inicialização pré-principal sem usar um construtor de classe executado no escopo do arquivo.
Você também deve ter o cuidado de incluir mais de uma dessas funções de inicialização desde que eu não acredito ditames C ++ a ordem -. Que poderia ser aleatória
Eu não tenho certeza disso "esboço" de que você fala, mas seria possível transformar a unidade de compilação principal com um script antes de tê-lo passado para o compilador, algo como:
awk '{print;if (substr($0,0,11) == "int main (") {print "initialize();"};}'
Você pode ver como isso afetaria seu programa porque:
echo '#include <stdio.h>
int main (void) {
int x = 1;
return 0;
}' | awk '{
print;
if (substr($0,0,11) == "int main (") {
print " initialize();"
}
}'
gera a seguinte com a chamada initialize()
acrescentou:
#include <stdio.h>
int main (void) {
initialize();
int x = 1;
return 0;
}
Pode ser que você não pode pós-processo o arquivo gerado caso em que você deve ignorar que a opção final, mas isso é o que eu estaria olhando em primeiro lugar.
Não é como eu executar pré-main codificação. Há seções de inicialização Sever executados antes principal, refere-se a http: // www. nongnu.org/avr-libc/user-manual/mem_sections.html initN seções.
De qualquer forma, isso só funciona em -O0 otimização por algum motivo. Eu ainda tento descobrir qual opção "otimizado" minha pré-main código de montagem de distância.
static void
__attribute__ ((naked))
__attribute__ ((section (".init8"))) /* run this right before main */
__attribute__ ((unused)) /* Kill the unused function warning */
stack_init(void) {assembly stuff}
Update, não é que eu reivindicou esta função não é utilizada, levando a otimizar a rotina de distância. Eu tinha a intenção de matar função de advertência não utilizado. Ele é fixado ao atributo usado em seu lugar.