A solução utilizada foi adotar um prefixo diferente para cada componente. Isso deve ser feito de forma manual pelo programador para cada identificador (função ou variável) de cada componente, criando muitas situações de possível erro.
3.1 Namespaces
Em C++, podemos contornar esse problema com o uso de namespaces, delimitando uma região do código que providencia um escopo próprio para os identificadores.
É bastante simples criar um namespace, como podemos ver no trecho definido no escopo global:
namespace first_space {
int val = 10;
void foo() {
printf("Inside first_space\n");
}
void bar() {
printf("val = %d\n", val);
}
}
namespace second_space {
void foo() {
printf("Inside second_space\n");
}
}
onde foram criados dois namespaces.
Note que, como cada um delimita um escopo diferente, é possível declarar duas funções com o mesmo nome (foo
), mas com implementações distintas.
Para acessar os identificadores dos namespaces criados, podemos usar o seguinte código:
int main()
{
first_space::foo();
second_space::foo();
first_space::val = 20;
first_space::bar();
return 0;
}
cuja saída deve ser
Inside first_space
Inside second_space
val = 20
Se, dentro de um arquivo .cpp
, acessamos muitas vezes funções de um mesmo namespace, podemos omitir o nome do namespace usando a sintaxe using namespace
.
Assim, no nosso exemplo, obteríamos o mesmo resultado escrevendo:
using namespace first_space;
int main()
{
foo();
second_space::foo();
val = 20;
bar();
return 0;
}
3.2 Utilizando namespaces no componente Timer
Para nossa aplicação "alarme", como desejamos isolar os identificadores de cada componente, podemos criar namespaces para cada um deles.
Dessa maneira, podemos reescrever o arquivo timer.h
para:
#ifndef TIMER_H_INCLUDED
#define TIMER_H_INCLUDED
namespace Timer {
extern void iniciar(int controle);
extern int timeout();
}
#endif // TIMER_H_INCLUDED
e o arquivo timer.cpp
para:
#include <Arduino.h>
#include "definicoes_sistema.h"
#include "timer.h"
#define TEMPO 10
namespace Timer {
int situacao = false;
unsigned long horaInicio;
}
void Timer::iniciar(int controle)
{
situacao = controle;
if (controle)
{
horaInicio = millis();
}
}
int Timer::timeout()
{
if (situacao == false)
{
return false;
}
if(millis() - horaInicio > TEMPO*1000)
{
return true;
}
return false;
}
Assim removemos a necessidade de usar o prefixo tmr
para todas as funções e variáveis.
Note que poderíamos declarar as variáveis situacao
e
horaInicio
no header com o modificador extern
caso necessitássemos que pudessem ser acessadas em outros arquivos.
Para usar o componente no arquivo principal alarme.ino
, temos que modificar o acesso às funções do componente Timer.
Assim, a função executarAcao
fica:
int executarAcao(int codigoAcao)
{
int retval;
retval = NENHUM_EVENTO;
if (codigoAcao == NENHUMA_ACAO)
return retval;
switch(codigoAcao)
{
case A01:
Timer::iniciar(true);
break;
case A02:
sne_bip();
com_notificar("Alarme em alerta");
Timer::iniciar(false);
break;
case A03:
com_notificar("Alarme desacionado");
Timer::iniciar(false);
break;
case A04:
com_notificar("Alarme desacionado");
break;
case A05:
Timer::iniciar(true);
break;
case A06:
sne_acionar(true);
com_notificar("Invasao");
Timer::iniciar(false);
break;
case A07:
com_notificar("Alarme desacionado");
Timer::iniciar(false);
sne_acionar(false);
break;
} // switch
return retval;
} // executarAcao
Para finalizar o aprimoramento do código, idealmente todos os componentes devem possuir o seu próprio namespace. Isto fica como exercício.
No caso de dúvidas, o código final da aplicação pode ser consultado em
https://gitlab.uspdigital.usp.br/andre.kubagawa/alarme_arduino/-/tree/master/alarme_namespaces
Bibliografia para este capítulo
- Código fonte do alarme no Arduino (com namespaces): https://gitlab.uspdigital.usp.br/andre.kubagawa/alarme_arduino/-/tree/master/alarme_namespaces