Hola a todos.
Me he encontrado una cosa que se escapa de mis conocimientos de C++. Estoy preparando una libreria para una máquina de estados finitos. En dicha libreria creo la máquina de estados; defino dichos estados; las acciones que se realizan tanto dentro, como al entrar y salir del estado; y las transiciones con la condicion en la que se produce y si se ha de ejecutar alguna acción cuando se produce.
De momento y a falta de refinar, estoy probando la libreria en sketch por lo que no hace falta instalarlo y está funcionando como debe. He aquí un ejmplo (está hecho de manera que se pueda enteder, para nada es util...):
typedef bool(*CONDICION)();
typedef void(*ACCION)();
typedef struct {
ACCION AlEntrar;
ACCION Dentro;
ACCION AlSalir;
} ESTADO;
typedef struct {
int EstadoOrigen;
int EstadoDestino;
ACCION Accion;
CONDICION Condicion;
} TRANSICION;
class MAQUINAESTADOS
{
private:
ESTADO *Estados;
int NumEstados;
int EstadoActual;
TRANSICION *Transiciones;
int MaxTransiciones;
int NumTransiciones;
public:
// Un constructor sin parametros, hay que llamar a Crear()...
MAQUINAESTADOS();
// Un constructor con parametros.
MAQUINAESTADOS(int nEstados, int nTransiciones);
// Asigna el numero de estados y transiciones.
void Crear(int nEstados, int nTransiciones);
void PonerAccionAlEntrar(int _estado, ACCION _accion) { Estados[_estado].AlEntrar = _accion; }
void PonerAccionAlSalir(int _estado, ACCION _accion) { Estados[_estado].AlSalir = _accion; }
void PonerAccionDentro(int _estado, ACCION _accion) { Estados[_estado].Dentro = _accion; }
int AnadeTransicion(int _origen, int _destino, CONDICION _condicion, ACCION _accion);
bool Actualizar();
void PonerEstado(int _estado);
int ObtenerEstado() { return EstadoActual; }
};
void
MAQUINAESTADOS::Crear(int nEstados, int nTransiciones)
{
int i;
NumEstados = nEstados;
Estados = new ESTADO[NumEstados];
EstadoActual = 0;
for (i=0; i<NumEstados; i++)
{
Estados[i].AlEntrar=NULL;
Estados[i].Dentro=NULL;
Estados[i].AlSalir=NULL;
}
MaxTransiciones = nTransiciones;
Transiciones = new TRANSICION[MaxTransiciones];
NumTransiciones = 0;
for (i=0; i<MaxTransiciones; i++)
{
Transiciones[i].Accion = NULL;
Transiciones[i].Condicion = NULL;
}
}
void
MAQUINAESTADOS::PonerEstado(int _estado)
{
if ( Estados[EstadoActual].AlSalir!=NULL ) Estados[EstadoActual].AlSalir();
EstadoActual = _estado;
if ( Estados[EstadoActual].AlEntrar!=NULL ) Estados[EstadoActual].AlEntrar;
}
bool
MAQUINAESTADOS::Actualizar()
{
for (int i=0; i<NumTransiciones; i++)
{
if ( Transiciones[i].EstadoOrigen==EstadoActual && Transiciones[i].Condicion!=NULL )
{
if ( Transiciones[i].Condicion() )
{
if ( Transiciones[i].Accion!=NULL ) Transiciones[i].Accion();
PonerEstado(Transiciones[i].EstadoDestino);
return true;
}
}
}
if ( Estados[EstadoActual].Dentro!=NULL ) Estados[EstadoActual].Dentro();
return false;
}
int
MAQUINAESTADOS::AnadeTransicion(int _origen, int _destino, CONDICION _condicion, ACCION _accion=NULL)
{
if ( NumTransiciones >= MaxTransiciones ) return NumTransiciones;
Transiciones[NumTransiciones].EstadoOrigen = _origen;
Transiciones[NumTransiciones].EstadoDestino = _destino;
Transiciones[NumTransiciones].Condicion = _condicion;
Transiciones[NumTransiciones].Accion = _accion;
return NumTransiciones++;
}
MAQUINAESTADOS::MAQUINAESTADOS()
{
NumEstados = 0;
EstadoActual = 0;
Estados = NULL;
MaxTransiciones=0;
NumTransiciones=0;
Transiciones=0;
}
MAQUINAESTADOS::MAQUINAESTADOS(int nEstados, int nTransiciones)
{
int i;
NumEstados = nEstados;
Estados = new ESTADO[NumEstados];
EstadoActual = 0;
for (i=0; i<NumEstados; i++)
{
Estados[i].AlEntrar=NULL;
Estados[i].Dentro=NULL;
Estados[i].AlSalir=NULL;
}
MaxTransiciones = nTransiciones;
Transiciones = new TRANSICION[MaxTransiciones];
NumTransiciones = 0;
for (i=0; i<MaxTransiciones; i++)
{
Transiciones[i].Accion = NULL;
Transiciones[i].Condicion = NULL;
}
}
// Utilizo un enum para definir los "nombres" de los estados.
enum {
MENU_PRINCIPAL,
MENU_2,
MENU_3,
MENU_4,
MAX_MENU
};
void dibujaPantalla()
{
//* Pintar en el LCD.
}
void dibujaPantalla2()
{
//* Pintar en el LCD
}
bool keyAPressed()
{
return digitalRead(3); // Un ejemplo de un boton en el pin 3.
}
// Inicio la máquina de estados.
MAQUINAESTADOS menu(10,10);
void setup()
{
// Pongo alguna accion en los estados.
menu.PonerAccionDentro(MENU_2, dibujaPantalla);
menu.PonerAccionDentro(MENU_3, dibujaPantalla2);
// Creo las transiciones, como es un ejemplo vale.
menu.AnadeTransicion(MENU_2, MENU_3, keyAPressed);
}
void loop()
{
// La funcion principal de la máquina de estados.
menu.Actualizar();
}
El código compila correctamente.
Ahora bien mi idea es usarla de la siguiente forma: habrá objetos, que tendrán un estado propio por lo tanto, tendrá una máquina de estados propia. Cada objeto ademas de su máquina de estados propia tendrá sus propiedades. Y de cada objeto podrá haber varias instancias.
Un ejemplo (reitero, es un ejemplo, nada util):
class Objeto
{
public:
MAQUINAESTADOS me;
Objeto();
int miembro1; // propiedades del objeto... puede ser cualquier cosa...
int miembro2;
void AccionEstado(); // Lo que va a realizar en un estado...
bool CondicionA() { miembro1==miembro2; } // Una condicion para transicion...
};
Objeto::Objeto()
{
me.Crear(2,1);
me.PonerAccionDentro(0,AccionEstado); <-- El problema.
//me.AnadeTransicion(0,1,CondicionA); <-- Tira el mismo error.
}
Pero al compilar obtengo el siguiente error:
prueba_maquina_estados:170: error: invalid use of non-static member function
me.PonerAccionDentro(0,AccionEstado);
^
exit status 1
invalid use of non-static member function
He realizado una prueba con otros compiladores, ya que la libreria pienso usarla también en el pc por lo útil que resulta.
Por ejemplo Borland:
Error E2034 prueba_maquina_estados.cpp 172: Cannot convert 'void (* (_closure )())()' to 'void (*)()' in function Objeto::Objeto()
O en WxDev7
In constructor 'Objeto::Objeto()':
prueba_maquina_estados.cpp:172 no matching function for call to 'MAQUINAESTADOS::PonerAccionDentro(int, <unresolved overloaded function type>)'
candidate is:
void MAQUINAESTADOS::PonerAccionDentro(int, ACCION)
no known conversion for argument 2 from '<unresolved overloaded function type>' to 'ACCION {aka void (*)()}'
En el ejemplo anterior he usado un miembro me como maquina de estados, he probado también utilizando herencia, siendo la máquina de estados la clase madre y con el mismo resultado.
Parece que al compilador no le gusta que se pase como parametro ACCION/CONDICION una función miembro de una clase, aunque este esté definido de la misma forma.
¿Estoy equivocado? ¿Se puede hacer? ¿Tiene solución, que no sea crear las funciones fuera del objeto?
