Pergunta

Eu estou trabalhando em um projeto sênior do grupo para minha universidade e eu ter executado em um grande obstáculo na tentativa de obter o meu código de trabalho.

O compilador que temos para o nosso Atmel microcontrolador de 8 bits não suporta os novos ou excluir operadores, e não suporta a STL C ++. Eu poderia programar em C, mas eu tenho que implementar um algoritmo A * que eu nunca fiz antes. Embora eu tenha tentado C inicialmente logo percebi que eu nunca fiz pura C antes. Tentando objetos de modelo com estruturas e funções é me atrasando desde que eu estou tão acostumado com o muito mais limpo C ++ sintaxe.

De qualquer maneira, as palavras exatas para os meus compiladores deficiências pode ser encontrada aqui: http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_cplusplus

Para superá-los e ainda usar C ++ Eu considerei as seguintes possibilidades. 1) Não alocar qualquer coisa, é só usar modelos para gerar matrizes fixos na pilha. 2) Atribuir e encontrar algum truque para chamar o construtor para objetos uma vez que eu tenha atribuído o espaço para eles. Colocação de novo não é uma opção desde novo não é um operador. 3) Basta usar C e chupa-lo, é um microcontrolador por que estou recebendo fantasia? 4) Encontre um melhor compilador que provavelmente vai custar $$$.

A segunda opção é a mais difícil, mas que teria o maior pagamento fora em termos de como eu posso escrever esse código. No entanto, eu imagino que a depuração pode ser uma dor enorme se eu entendi errado. Estou pensando em criar objetos na pilha, copiando seus bits para o espaço alocado, e depois de zerar os bits no objeto para que ele não chamar seu destruidor. Para fazer isso eu acessar os bits diretamente com um ponteiro de char não assinado eo operador sizeof para obter a contagem de bytes.

Isso soa terrível e eu não sei se isso poderia funcionar de forma confiável, mas eu estou considerando isso. Eu sei vtables pode ser um problema, mas eu não pretende ter qualquer vtables uma vez que é apenas um microcontrolador de 8 bits.

Foi útil?

Solução

Apenas para o registro, zerando os bits em um objeto não afetará se o destruidor é chamado (a menos que o compilador tem uma peculiaridade especial que permite que este comportamento). Basta escrever algumas declarações de registro em seu destruidor para testar isto.

Estruturação seu programa não atribuir qualquer coisa é provavelmente a forma como o sistema foi concebido. Eu não tenho trabalhado com sistemas embarcados antes, no entanto eu tenho ler alguns experientes lojas embutidos que desencorajam o uso de memória dinâmica porque o ambiente de tempo de execução tem quantidades escassos do mesmo.


No entanto, se você tem, você ainda pode usar posicionamento novo. Se você não tem o cabeçalho <new>, aqui estão as linhas relevantes diretamente dele na minha versão do GCC:

// Default placement versions of operator new.
inline void* operator new(std::size_t, void* __p) throw() { return __p; }
inline void* operator new[](std::size_t, void* __p) throw() { return __p; }

// Default placement versions of operator delete.
inline void  operator delete  (void*, void*) throw() { }
inline void  operator delete[](void*, void*) throw() { }

vara que em algum lugar em um arquivo de cabeçalho incluídos por cada arquivo de origem que a colocação de usos new / delete.

Amostra de arquivo que testa o seguinte:

#include <cstdio>
#include <new>

int
main(int argc, char** argv)
{
    typedef char const* cstr;
    char foobar[16];
    cstr* str = new (&foobar) cstr(argc > 1 ? argv[1] : "Hello, world!");
    std::puts(*str);
    str->~cstr();
}

Em minha versão do GCC, este não usa libstdc++ em tudo (se -fno-exceptions é usado).


Agora, se você quiser combinar isso com malloc (se a sua plataforma oferece isso), então você pode fazer isso:

#include <cstdio>
#include <cstdlib>

inline void* operator new  (std::size_t n) {return std::malloc(n);}
inline void* operator new[](std::size_t n) {return std::malloc(n);}
inline void  operator delete  (void* p) {std::free(p);}
inline void  operator delete[](void* p) {std::free(p);}

int
main(int argc, char** argv)
{
    typedef char const* cstr;
    cstr* str = new cstr(argc > 1 ? argv[1] : "Hello, world!");
    std::puts(*str);
    delete str;
}

Isso permite que você use o padrão new / delete que você está familiarizado, sem a necessidade de uso de libstdc++.

Boa sorte!

Outras dicas

Não lute contra suas ferramentas. Se o único compilador que você tem para o seu sistema embarcado é um compilador C, aprender C - não é difícil. Tentando produzir uma versão bastardised das duas línguas apenas para resolver um problema de programação bastante simples só irá acabar em lágrimas.

Para olhar de outra forma, se a sua plataforma embarcada nem sequer apoiar um compilador C, mas apenas uma montadora, seria o seu primeiro impulso seja para sentar e escrever um compilador C ++ em assembler? Espero que não, eu espero que você, ao invés, sentar-se e aprender a usar a montadora para completar a sua missão -. escrevendo um compilador C ++ (ou mesmo um compilador C) seria o uso totalmente inadequado de seu tempo, e quase certamente resultar em falha

Eu acho que você está se aproximando o problema a partir de um ponto de vista que é inferior a ideal.

Você está focalizando sobre o compilador (ou falta dela) em vez de se concentrar no hardware.

A resposta mais provável para seus principais perguntas é "porque o hardware não suporta todas as coisas que C ++". hardware embarcado (microcontroladores) estão marcados para a personalização do design de hardware -. mapas de memória, manipuladores de interrupção, I / O, etc

Na minha opinião, você deve primeiro passar algum tempo com o livro hardware para o microcontrolador, aprender os meandros do dispositivo - ou seja, como ele foi projetado e com que finalidade primária. Alguns foram projetados para manipulação de memória rápida, alguns para rápido I / manuseio O, alguns para A / D Tipo de trabalho, alguns para processamento de sinais. O tipo de ditames de microcontroladores as instruções assembler que eles escreveram para ele, e que dita que qualquer compilador de nível superior pode fazer de forma eficiente.

Se isto é importante, gastar algum tempo para olhar para a montador bem - ele vai lhe dizer o que os designers considerado importante. Ele também irá lhe dizer muito sobre o quanto você pode obter de um compilador de alto nível.

Geralmente, os microcontroladores não suportam C ++ porque o projeto realmente não se preocupa com objetos, ou manipulação de memória fantasia (a partir da perspectiva C ++). Isso pode ser feito, mas que são muitas vezes tentando libra um prego redondo em um buraco quadrado para obter construtores e destruidores (e 'novo' e 'delete') para trabalhar no micro ambiente.

Se você tem um compilador C para esta unidade, considerá-lo uma bênção. Um compilador bom C é muitas vezes "mais que suficiente" para criar excelente software incorporado.

Cheers,

-Richard

Só porque ele não tem essas ferramentas não significa que você não pode beneficiar de C ++. Se o projeto for grande o suficiente, o acesso a objetos de design orientado por si só poderia ser motivação suficiente.

Se ele não suporta 'novo', em seguida, provavelmente é porque não faz sentido fazer uma distinção automática entre uma pilha e a pilha. Isso pode ser por causa de sua configuração de memória. Ele também pode ser porque os recursos de memória são tão limitados apenas a alocação muito cuidado faz sentido. Se você absolutamente tem que implementar seu próprio operador 'novo', você pode olhar para adaptação Doug Lea de malloc . Eu acredito que ele começou sua alocador em uma circunstância similar (reimplementar C ++ 's novo).

Eu amo o STL, mas ainda é possível fazer coisas úteis sem ele. Dependendo do escopo do projeto que você pode ser melhor fora apenas usando uma matriz.

Eu tive um compilador similar que implementou uma versão bizarra do Incorporado-C ++ padrão. Tivemos operator new que iria chamar construtores para nós e destruidores foram chamados na maioria dos casos . O fornecedor de compilador / runtime foi e implementado try e catch usando setjmp e longjmp como um conveniência para o engenheiro . O problema era que eles nunca mencionou que um throw que não causa destruidores de objetos locais para ser invocado!

De qualquer forma, o nosso grupo herdou a base de código depois que alguém escreveu um ator aplicação como se fosse C ++ padrão: usando técnicas RAII e todos os outros bens. Nós acabamos reescrevendo-no que um número de nós chamamos de orientada a objeto C em seu lugar. Você pode querer considerar apenas morder a bala e escrever em C. reta Em vez de construtores, tem um método de inicialização explicitamente chamado. Destruidores se tornar um método de terminação explicitamente chamado. Não há muito de C ++ que você não pode imitar em C muito rapidamente. Sim, MI é uma dor no ... mas herança simples é muito fácil. Dê uma olhada na este PDF para algumas idéias. É quase descreve a abordagem que tomamos. Eu realmente desejo que eu tinha escrito o nosso método em algum lugar ...

Você pode encontrar algum código útil no meu A * website tutorial . Embora o href="http://code.google.com/p/a-star-algorithm-implementation/" rel="nofollow código eu escrevi para apoiar esta STL usos no deve ser fácil de retirar o apoio a STL. Além disso, há um alocador de piscina incluído com ele (fsa.h) que eu escrevi para acelerar STL em consolas de jogos. É código C ++, mas eu portou originalmente de C e eu não acho que seria difícil fazê-lo para o outro lado. O código é testado por mais de 10.000 pessoas por isso é uma boa base para começar.

Substituir as estruturas STL que estou usando não é problema, uma vez que se limita a vetores. I usar um dos vectores como uma fila de prioridade utilizando as funções de pilha (make_heap e push_heap). Você pode substituir isso com o meu código velho C que tem uma fila de prioridade implementado em C que deve apenas cair em seu código. (Que só faz uma alocação, para que possa substituir esse com um ponteiro para uma área reservada de sua memória.

Como você pode ver neste fragmento de código do cabeçalho, a principal diferença em código C é que não há é este ponteiro, nenhum objeto, então seu código normalmente leva um ponteiro de objeto como o primeiro argumento.

void PQueueInitialise( PQUEUE *pq, int32 MaxElements, uint32 MaxRating, bool32 bIsAscending );
void PQueueFree( PQUEUE *pq );
int8 PQueuePush( PQUEUE *pq, void *item,  uint32 (*PGetRating) ( void * ) );
int32 PQueueIsFull( PQUEUE *pq );
int32 PQueueIsEmpty( PQUEUE *pq );
void *PQueuePop( PQUEUE *pq, uint32 (*PGetRating) ( void * ) );

Por que não escrever pela primeira vez no seu computador desktop, levando em consideração as limitações do compilador, depurá-lo, verifique se ele funciona perfeitamente e só então passar para o ambiente incorporado?

ao fazer o trabalho incorporado, uma vez eu não conseguia nem ligar o tempo de execução C para restrições de memória, mas o hardware tinha um DMA (alocador de memória dinâmica) instruções para que eu escrevi o meu próprio malloc com esse hardware, o hardware provavelmente tem um similar recurso, então você poderia escrever um malloc e, em seguida, um novo baseado no malloc.

De qualquer forma, no final, eu usei 99 alocações% pilha, e alguns limites conjuntos OS objetos estáticos que gostaria de reciclar, por builiding no lugar. Este poder me uma boa solução.

scroll top