OOP vs Inline com Arduino

8

Estou programando há um bom tempo, mas sou novo no Arduino e na Programação AVR. A principal pergunta que tenho sobre a programação desses microcontroladores é que existem grandes diferenças no design de código em Classes orientadas a objetos versus a programação em linha mais tradicional que já vi em muitos exemplos?

Em outras palavras, no mundo dos controladores Arduino / AVR, há alguma economia com memória e desempenho utilizando classes ou vice-versa?

Digamos, por exemplo, que temos uma classe:

class SomeClass(){

private:
   int x;
   int y;

public:
   void foo();
   void bar();
}

SomeClass thisClass;
thisClass.foo();
thisClass.bar();

Haveria algum ganho de desempenho ou memória projetando o programa de maneira mais integrada, como:

int x;
int y;

void foo(){ /*** Do something ***/};
void bar(){ /*** Do more stuff ***/};

Tentei fazer algumas pesquisas no Stack Exchange e no Google, mas não consegui encontrar a resposta certa. Estou procurando a coisa mais próxima que pude encontrar foi essa questão do Stack Exchange.

A razão pela qual estou perguntando isso é que tenho um projeto que precisa ser o mais leve possível e não estou claro como devo projetar meu programa nesse ambiente.


Editar

Obrigado pelas respostas, isso esclareceu as coisas. Há uma coisa sobre a qual não estou muito claro.

Digamos que você tenha uma classe que você está projetando que utiliza o u8glib da seguinte maneira:

class UserInterface{
private:
   U8GLIB_ST7920_128X64 Display;

public:
   UserInterface();
}

Como você usaria a "Memória dinâmica" como:

UserInterface::UserInterface(){
   UserInterface::Display = U8GLIB_ST7920_128X64(LCD_E_PIN, LCD_RW_PIN, LCD_RS_PIN, U8G_PIN_NONE);
}
Andy Braham
fonte

Respostas:

2

A principal pergunta que tenho sobre a programação desses microcontroladores é que existem grandes diferenças no design de código em Classes orientadas a objetos versus a programação em linha mais tradicional que já vi em muitos exemplos?

...

Em outras palavras, no mundo dos controladores Arduino / AVR, há alguma economia com memória e desempenho utilizando classes ou vice-versa?

Sim, há uma grande diferença entre o uso de C ou C ++ para sistemas embarcados de pequena escala, como o Arduino / AVR. O C ++ permite que mais informações sejam fornecidas para otimizações do compilador.

Se você estiver implementando uma estrutura OOP, a plataforma ou o tempo de execução C ++ e as classes também poderão ajudar na arquitetura e na reutilização do software. Em Cosa, vários padrões de design de OOP são usados ​​para obter interfaces para programadores de aplicativos e programadores de driver de dispositivo. O mais comum é a delegação .

O uso de classes abstratas, funções-membro virtuais, embutimento e modelos pode ajudar a obter menor pegada e desempenho superior às implementações tradicionais. Como exemplo, as classes Cosa Pin são X5-X10 mais rápidas que o núcleo do Arduino e, ao mesmo tempo, menores em pegadas. Por favor, veja os benchmarks .

Uma coisa para "desaprender" da programação C ++ tradicional é o uso de new / delete (malloc / free). Com o tamanho da SRAM de apenas alguns Kbyte, usar um heap é um risco. A resposta são classes estáticas e dados baseados em pilha.

Há muito mais a ser dito sobre a arquitetura da estrutura OOP, mas espero que isso ajude a responder às suas perguntas iniciais.

Felicidades!

Mikael Patel
fonte
Boa resposta! Atualizei minha pergunta perguntando sobre como contornar a memória dinâmica (novo / delete / malloc / free). Você tem alguma opinião sobre o uso não alocado de memória dinâmica? Tudo o que precisa ser compartilhado ao longo das aulas deve ser global? Isso não parece certo para mim, sempre fui ensinado a não usar globals se você puder ajudá-lo.
Andy Braham
Um breve comentário no seu exemplo UserInterface acima. O Display é na verdade composição do objeto (não referência / ponteiro), assim você não precisa de novo. Você precisa iniciar a exibição. A construção UserInterface deve se parecer com isso. UserInterface::UserInterface() : Display(LCD_E_PIN, LCD_RW_PIN, LCD_RS_PIN, U8G_PIN_NONE) { ... }. Os parâmetros necessários do construtor Display devem ser passados ​​para o construtor UserInterface.
Mikael Patel
OOP é tudo sobre encapsulamento, ocultação de dados, portanto, o compartilhamento deve ser no mínimo (zero). O objetivo é ter mais ou menos apenas um número de objetos estáticos globais que interagem. Eles mantêm e ocultam o estado global. Para os objetos, os dados do membro são um estado local não dinâmico. Para atingir a meta, você precisa novamente de um conjunto de truques; transformações de programa.
Mikael Patel
Um exemplo; Considere o design de uma classe BitSet que pode ter um número variável de membros. A solução óbvia é calcular o número de bytes necessários e usar o novo / malloc. Uma alternativa é passar o armazenamento como um parâmetro para o construtor BitSet. Uma terceira alternativa é uma classe de modelo com o número de elementos como parâmetro. Isso permite que uma variável de membro seja o número necessário de bytes. Consulte Cosa / BitSet.hh para obter mais detalhes sobre essa variante de transformação de programa. Há mais transformações.
Mikael Patel
Atualizei meu exemplo UserInterface, é mais ou menos a abordagem correta? Acho que agora tenho um entendimento muito bom de como implementar o que preciso.
Andy Braham
4

A razão pela qual você não consegue encontrar a resposta é porque a resposta é Sim e Não.

Para coisas básicas da classe - definindo sua classe com métodos etc. e instanciando objetos a partir dela - há pouca diferença no resultado final em comparação com o "vanilla" C. As otimizações do compilador são tão boas agora que o desempenho é o mesmo. Sim, pode haver pequenos aumentos no uso da memória, já que você passa um ponteiro extra a cada chamada de método (em vez de foo(int x)o fez foo(MyClass *this, int x)), mas é tão pequeno que não é perceptível.

As grandes diferenças surgem quando você começa a brincar com o polimorfismo e outros tópicos avançados. Ao começar a executar esses programas complexos, o compilador nem sempre é capaz de descobrir quais funções são necessárias e quais não são, e não é capaz de eliminar as funções não utilizadas ( "coleta de lixo" ). Então você pode acabar com um código maior.

Isso não significa código mais lento, apenas pedaços de código que estão por aí que nunca fazem nada.

De maior importância é gerenciar sua memória dinâmica melhor do que você está acostumado. Como existe uma quantidade tão pequena de memória, o heap é muito pequeno e, como resultado, é fragmentado com muita facilidade. Criação e destruição de objetos (Dinâmico new myClass, delete myClassObject, etc) é muito ruim. Objetos de classe realmente precisam ser definidos estaticamente (no escopo global é o mais comum) ou alocados temporariamente na pilha (instâncias locais). Caso contrário, você está pedindo problemas - e o primeiro a saber é que coisas estranhas estão acontecendo (sem relatórios de erro ou exceções, veja ...).

Majenko
fonte
Tudo bem, se eu entendi isso corretamente neste mundo da programação, as classes são usadas para mais um papel organizacional e de portabilidade do que o verdadeiro OOP e que as coisas devem ser alocadas estaticamente e explicitamente, em vez de dinamicamente. Muito obrigado, isso faz muito sentido e explica por que tenho visto exemplos e o que não está escrito do jeito que estão.
precisa