Máquina de estados/Instanciación de objetos/Compilación C++

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?

Antes que nada decir que no he mirado a fondo el código de lo que ha hecho. Sólo escribo para advertir que, si bien se pueden usar punteros a funciones miembro de una clase, parece ser algo más complicado que usar punteros a funciones "sueltas". El problema viene por que las funciones miembro de una clase no sólo "dependen de ellas", también "dependen del objeto al que pertenece" (a no ser que sean estáticas). Así que, entre otras cosas, en la llamada se ha de pasar el objeto. Si buscas en Google puntero a funcion miembro c++ encontrarás información sobre ello, por ejemplo C++ Punteros a función miembro (pointer-to-member function) o callbacks con clase - Poesía Binaria

No hagas caso de aquellos que digan que no se puede. Poderse se puede, pero es complicado.

Dicho todo esto, y repito que no he visto a fondo tu código, yo me plantearía resolver el problema usando herencia. Tal vez clases abstractas que sirvan de interfaz. Porque yo no veo mucho "callback" en lo que tú quieres hacer, y esto de los punteros a funciones, aunque no siempre, está más bien pensado para los "callbacks".

Efectivamente, no es lo mismo un puntero a función que un puntero a función miembro. Tampoco entiendo muy bien qué es lo que quieres hacer; pero podrías, por ejemplo, definir AccionEstado en lugar de como miembro, como ACCION:

ACCION AccionEstado;
AccionEstado=función externa...

Posteriormente, podrías definir una función y asignársela en el constructor del objeto.

void mifuncion () {.......}
Objeto(mifuncion);
}

Tampoco entiendo muy bien qué es lo que quieres hacer

A ver si intento explicar lo que quiero hacer que se entienda, así que voy a poner un ejemplo "práctico" sobre la mesa, el ejemplo es para entenderlo no es real.

Supongamos mi habitación con tres ventanas con sus persianas. En cada persiana he puesto dos pulsadores: para abrir y cerrar; dos finales de carrera para abierta y cerrada y un motor que sube y baja.

Las quiero controlar con arduino de modo simple y sin complicaciones, ni Wifi, ni mando a distancia ni nada. Solo llegar dar al boton y que haga lo que tiene sin complicaciones.

Por lo tanto, para cada persiana tendrá lo siguiente: dos entradas con unos pulsadores (abrir y cerrar), dos entradas para los finales de carrera (abierto y cerrado) y dos salidas para controlar un motor, una que le dice subir y otra que le dice bajar.

Cada persiana tiene que tener asignados pines diferentes en el arduino obviamente, y cada persiana se comportará acorde esos pines.

Hago un pequeño diagrama de estados para ver como ha de comportarse:

Diagrama1.png

Vamos a usar mi libreria:

MAQUINAESTADOS Persiana1(4,4);
Persiana1(CERRADA,ABRIENDO, PulsarAbrirPersiana1, SubirMotor1);
Persiana1(ABRIENDO,ABIERTA, CarreraAbierta1, PararMotor1);
Persiana1(ABIERTA,CERRANDO, PulsarCerrarPersiana1, BajarMotor1);
Persiana1(CERRANDO,CERRADA, CarreraCerrada1, ParaMotor1);

Eso sería solo para una persiana. Debo escribir las transiciones para cada una de las persianas, cambiando el "1" por el numero de persiana que sea. Al igual con las funciones de condicion y funciones de accion: una por cada persiana... Y todas IGUALES. Se me ha ido la ley del mínimo esfuerzo al garete ya que tengo que escribir un monton de veces lo mismo...

Pero una ventana es un objeto ¿no? En código sería mas o menos así:

class Persiana 
{
    // Son los pines del arduino.
    int pinAbrir
    int pinCerrar;
    int motorSubir;
    int motorBajar;
    int pinFCAbierta;
    int pinFCCerrada;

    Persiana(int,int,int,int,int,int);
    PulsadoAbrir()   { return digitalRead(pinAbrir)==1; }
    PulsadoCerrar()  { return digitalRead(pinCerrar)==1; }
    SubirMotor()     { digitalWrite(motorSubir,1); digitalWrite(motorBajar,0); }
    BajarMotor()     { digitalWrite(motorSubir,0); digitalWrite(motorBajar,1); }
    PararMotor()     { digitalWrite(motorSubir,0); digitalWrite(motorBajar,0); }
    CarreraAbierta() { digitalRead(pinFCAbierta)==1; }
    CarreraCerrada() { digitalRead(pinFCCerrada)==1; }

}

Persiana::Persiana(int a, int b, int c, int d, int e, int f)
{
   // Cuando creamos el objeto le decimos lo pines que van a controlar la 
   // Persiana.
   pinArir = a;
   pinCerrar = b;
   motorSubir = c;
   motorBajar = d;
   pinFCAbierta = e;
   pinFCCerrada = f;
}

Con esto ya puedo crear tres objetos Persiana, persiana1(pines...), persiana2(pines...) y persiana3(pines...). Como va a funcionar igual, lo unico que cambia son los pines y no he tenido que escribir 3 veces la misma funcion (pulsador o carrera, etc...)

Y ahora lo que quiero es agregar la máquina de estados.

Las condiciones están: PulsadorAbrir, PulsadorCerrar, CarreraAbierta, CarreraCerrada. Y las acciones también: SubirMotor, BajarMotor, PararMotor.

Solo queda encajar la máquina de estados.

He puesto una persina de ejemplo. Pero el objetivo es utilizar la maquina de estados con cualquier objeto (aka tengo las ventanas, pues una puerta...), usando la clase MAQUINADEESTADOS como base.[/b][/b]

Ya tengo la clase Persiana. Con sus funciones mienbro que son las acciones y las transiciones de la máquina de estados.

En el primer post creé un objeto MAQUINA de estados dentro de la clase y daba error, ahora lo hago utilizando herencia (no pongo el código de la máquina de estados):

enum { CERRADA, ABRIENDO, ABIERTA, CERRANDO };

class Persiana : public MAQUINAESTADOS
{
    // Son los pines del arduino.
    int pinAbrir;
    int pinCerrar;
    int motorSubir;
    int motorBajar;
    int pinFCAbierta;
    int pinFCCerrada;

    Persiana(int,int,int,int,int,int);
    PulsadoAbrir()   { return digitalRead(pinAbrir)==1; }
    PulsadoCerrar()  { return digitalRead(pinCerrar)==1; }
    SubirMotor()     { digitalWrite(motorSubir,1); digitalWrite(motorBajar,0); }
    BajarMotor()     { digitalWrite(motorSubir,0); digitalWrite(motorBajar,1); }
    PararMotor()     { digitalWrite(motorSubir,0); digitalWrite(motorBajar,0); }
    CarreraAbierta() { digitalRead(pinFCAbierta)==1; }
    CarreraCerrada() { digitalRead(pinFCCerrada)==1; }
};

Persiana::Persiana(int a, int b, int c, int d, int e, int f)
{
   // Cuando creamos el objeto le decimos lo pines que van a controlar la
   // Persiana.
   pinAbrir = a;
   pinCerrar = b;
   motorSubir = c;
   motorBajar = d;
   pinFCAbierta = e;
   pinFCCerrada = f;

   // Y aqui en el constructor preparar la maquina de estados. De hecho cree esta
   // funcion para poder usarla fuera y creé un constructor sin parametros.
   Crear(4,4);
   // Y a partir de aqui empezar a añadir el código a la inicialización de la 
   // maquina de estados. 
   AnadeTransicion(CERRADA, ABRIENDO, Persiana::PulsadoAbrir, Persiana::SubirMotor);
}

void setup() {}
void loop() {
   Persiana1.Actualizar();
   Persiana2.Actualizar();
   // Todos los objetos...
}

Está es mi idea original, y claro por limitaciones del lenguaje, no se puede usar una funcion miembro, como una funcion normal en otra clase.

Espero que haya quedado más claro lo que quiero hacer.

No podía encontrarla pero viste esto victorjam?

Arduino finite state machine library

Si no te sirve, al menos te dará ideas.
Mirá sus ejemplos.

Le he echado un vistazo asi por encima a dicha libreria, que por cierto no es la primera que veo, pero casi todas funcionan mas o menos igual...

Y el primero "pero" es que empiezo mal al hacer esto:

#include <Fsm.h>

class Persiana
{
    public:
    State abierta (NULL,NULL,NULL);
 
    // Son los pines del arduino.
    int pinAbrir;
    int pinCerrar;
    int motorSubir;
    int motorBajar;
    int pinFCAbierta;
    int pinFCCerrada;   
    
    Persiana(int a, int b,int c,int d,int e, int f);
    
    PulsadoAbrir()   { return digitalRead(pinAbrir)==1; }
    PulsadoCerrar()  { return digitalRead(pinCerrar)==1; }
    SubirMotor()     { digitalWrite(motorSubir,1); digitalWrite(motorBajar,0); }
    BajarMotor()     { digitalWrite(motorSubir,0); digitalWrite(motorBajar,1); }
    PararMotor()     { digitalWrite(motorSubir,0); digitalWrite(motorBajar,0); }
    CarreraAbierta() { digitalRead(pinFCAbierta)==1; }
    CarreraCerrada() { digitalRead(pinFCCerrada)==1; }

};

Persiana::Persiana(int a, int b,int c,int d,int e, int f)
{
   // Cuando creamos el objeto le decimos lo pines que van a controlar la
   // Persiana.
   pinAbrir = a;
   pinCerrar = b;
   motorSubir = c;
   motorBajar = d;
   pinFCAbierta = e;
   pinFCCerrada = f;
}



void setup() {
  // put your setup code here, to run once:

}

void loop() {
  // put your main code here, to run repeatedly:

}

Solo he incluido la libreria y creo el estado "abierta" en el que no hace nada y me tira el error:

sketch_feb28b:8: error: expected identifier before '__null'

     State abierta (NULL,NULL,NULL);

                    ^

sketch_feb28b:8: error: expected ',' or '...' before '__null'

Si el estado abierta se declara fuera de la clase compila sin errores.

Aún así creo que plantea el mismo problema, en cuanto alguna de las acciones a realizar sea una función miembro de una clase dará error.

Eso si... me ha gustado el uso de "transiciones temporizadas" tendré que echarle un mejor vistazo al código.

Bueno mi intención era que te diera IDEAS y por lo visto funcionó.

Que no te sirva o que hayas ido mas alla con tu versión, no significa que puede ser interesante y aporte cosas que no consideraste.

Los archivos principales parecen tener 4 meses de modo que debería funcionar.
Voy a ver.

Compile Multitasking.ino y esto

[SUCCESS] Took 10.75 seconds

[02/28/18 11:58:40]

Volviendo a la inquietud inicial, cambiando

void AccionEstado(); // Lo que va a realizar en un estado...

por

ACCION AccionEstado; // Lo que va a realizar en un estado...

Sí compila; aunque no termino de pillar la intención.

Ufff... siento no haber respondido antes pero llevo un día... Ley de Murphy: Cuando algo se rompe, algo más se romperá y siempre cuando no haga falta que se rompa...

Te refieres a realizar esto:

class Objeto : public MAQUINAESTADOS
{
  public:
  
    Objeto();
    int  miembro1; // propiedades del objeto... puede ser cualquier cosa...
    int  miembro2;
    ACCION AccionEstado; // Lo que va a realizar en un estado...
    bool CondicionA() { miembro1==miembro2; } // Una condicion para transicion...
    void hagoAlgo(); // Lo que va a realizar en el estado.
};

Objeto::Objeto()
{
  Crear(10,10);
  AccionEstado = hagoAlgo; // Asigno a la acción y la pongo en el estado.
  me.PonerAccionDentro(0,AccionEstado);

}

Definir Accion como estado está bien. Pero al asignar de nuevo una funcion miembro es donde falla.

cannot convert 'Objeto::hagoAlgo' from type 'void (Objeto::)()' to type 'ACCION {aka void (*)()}'

La intención en sí es crear un objeto con sus propiedades, con su máquina de estados y no tener que definir todas las acciones/transiciones de un objeto.

A ver si con el ejemplo se ve mejor.

#include <Fsm.h>

enum { CERRADA, ABRIENDO, ABIERTA, CERRADA };

class Persiana : public MAQUINAESTADOS
{
    public:

    // Son los pines del arduino.
    int pinAbrir;
    int pinCerrar;
    int motorSubir;
    int motorBajar;
    int pinFCAbierta;
    int pinFCCerrada;   
   
    Persiana(int a, int b,int c,int d,int e, int f);
   
    PulsadoAbrir()   { return digitalRead(pinAbrir)==1; }
    PulsadoCerrar()  { return digitalRead(pinCerrar)==1; }
    SubirMotor()     { digitalWrite(motorSubir,1); digitalWrite(motorBajar,0); }
    BajarMotor()     { digitalWrite(motorSubir,0); digitalWrite(motorBajar,1); }
    PararMotor()     { digitalWrite(motorSubir,0); digitalWrite(motorBajar,0); }
    CarreraAbierta() { digitalRead(pinFCAbierta)==1; }
    CarreraCerrada() { digitalRead(pinFCCerrada)==1; }

};

Persiana::Persiana(int a, int b,int c,int d,int e, int f)
{
   // Cuando creamos el objeto le decimos lo pines que van a controlar la
   // Persiana.
   pinAbrir = a;
   pinCerrar = b;
   motorSubir = c;
   motorBajar = d;
   pinFCAbierta = e;
   pinFCCerrada = f;
   // Suponemos que no da error de compilacion...
   Crear(4,4);
   // Vamos a poner las transiciones.
   AnadeTransicion(CERRADA, ABRIENDO, PulsadoAbrir, SubirMotor);
   AnadeTransicion(ABRIENDO, ABIERTA, CarreraAbierta, PararMotor);
   AnadeTransicion(ABIERTA, CERRANDO, PulsadoCerrar, BajarMotor);
   AnadeTransicion(CERRANDO, CERRADA, CarreraCerrada, ParaMotor);
}

Persiana persiana1(...), persiana2(...), etc.

void setup() {
  // put your setup code here, to run once:

}

void loop() {
  // put your main code here, to run repeatedly:
  persiana1.actualizar();
  persiana2.actualizar();
}

Si se pudiera hacer (que seguro que si), tendríamos la ventaja de no tener que definir para cada objeto una funcion ( PulsadoAbrir, PulsadoCerrar, ParaMotor, ... ) externa, teniendo que repetir el mismo proceso para cada pin, con lo cual es engorroso...

De momento como no deja, hay que escribir cada funcion, por ejemplo: pulsadoAbrir para la persiana 1, lo mismo para la dos. El codigo es el mismo, lo que cambia son los pines...

No sé como explicarlo mejor.... :smiley:

Hola de nuevo.
No termino de entender la relación entre estados/transiciones que planteas.
Efectivamente, me refería a eso. De esa forma puedes asignar funciones absolutas. Creo que también puedes conseguir asignar métodos de clase cambiando la definición y la relación entre las estructuras/clases; pero no ambas. Y en el primer ejemplo, en tu objeto menu estás utilizando la asignación de funciones absolutas. Para que fuera equivalente a lo que quieres hacer con tu clase Objeto, también debería ser una clase (y derivar de MAQUINAESTADOS). Vamos; que si lo arreglamos para Objeto dejaría de funcionar para menú.
¿Podrías poner un ejemplo simple y completo de lo que quieres hacer funcionar, aunque no compile? Pero sin mezclar funciones absolutas y métodos de clase.

No termino de entender la relación entre estados/transiciones que planteas.

La máquina de estados se compone de estados y transiciones.

Cuando está en un estado, se realiza una acción definida por la función (en este caso un puntero a una función) Dentro. Cuando se produce un cambio de estado, pueden ocurrir dos cosas, que haya que hacer algo al entrar o al salir de él, para ello están las funciones (punteros) AlEntrar y AlSalir. Quizás no haya que relaizar ninguna función en ese estado y por lo tanto Dentro será NULL. Igualmente ocurre con lo que haya que hacer AlEntrar o AlSalir que pueden ser NULL.

En algunas implementaciones que he visto, solo se limitan a la acción que se realiza dentro, y otras, solo al entrar/salir; y en otras ninguna de las dos. Por lo que decidí crear mi propia libreria.

En la máquina de estados se crea un array donde para cada estado tenemos el puntero a la accion dentro, al salir y al entrar.

Las transiciones implican un cambio de un estado a otro, siempre que se cumpla una condición, que se define en este caso también como puntero, y quizás haya que hacer algo cuando se cumple la transición o no, que es la acción de la transición.

En cada transición debemos haber un estado origen y destino que serán los indices del array de estados.

Las transiciones se guardan en otro array que recorreremos en la función Actualizar.

Así cuando llamamos a Actualizar, recorremos el array de transiciones, cuando llegamos a alguna cuyo estado origen coincida con el estado actual, comprobamos la condicion, si la condicion existe y se cumple, significa que hemos de cambiar de estado, pero antes de cambiar de estado comprobamos si esta transicion tiene una acción (puntero distinto de NULL) y la ejecutamos.

El siguiente paso es cambiar el estado. Comprobamos si el estado actual (origen en la transicion) tiene que realizar una acción al salir (AlSalir!=NULL) y la ejecutamos. Ahora si, cambiamos el estado actual por el destino que nos indica la transición y comprobamos si tiene que realizar la acción al entrar (AlEntrar!=NULL).

Si hemos recorrido el array de transiciones y no hemos entrado una transición que se cumpla, no hemos cambiado de estado por que se realiza la acción de ese estado si la huebiera (Dentro!=NULL).

El uso de tanta acción viene condicionado por la máquina de estados:

Por ejemplo en la maquina de estados de la persiana, no se realiza ninguna accion, ni al entrar, ni al salir, ni dentro del estado, por lo que los punteros deben ser NULL. En cambio si se realiza una acción cada vez que cambio de estados (poner en marcha el motor y pararlo).

En cambio, en otra máquina como la del menú, dentro del estado haya que hacer algo (mostrar una variable, por ejemplo) y realizar alguna que otra acción al salir o entrar... y en las transiciones quizás no haya que hacer nada, simplemente cambiar de estado.

Todo dependerá del menú...

La máquina de estados con funciones absolutas (para mi globales), tiene pinta de funcionar bien aunque no la he probado lo bastante y quizas se pueda mejorar mas en rendimiento y/o eficiencia.

Para usarla con objetos... hay está el problema. Estoy viendo que por un lado ganaré legibilidad de codigo, pero perderé eficacia en el lado de que solo podré usar objetos.

He estado ojeando un libro sobre máquina de estados en los que se realizan varias implementaciones. Una de ellas es usando herencia y funciones miembro pero no me queda claro el código.

En ella se define:

class MAQUIANESTADOS
{
    public:
 typedef void (MAQUINAESTADOS::*Accion)();
 struct transicion
 {
    Accion accion;
    unsigned int next;
 };
 transicion const *tabla;
};

Pero dicha implementación es un poco fea, ya que no hay condiciones para pasar de un estado a otro, y solo hay acciones en la transicion; Y tampoco me queda claro como se hace uso del puntero a funciones miembro. Y la tabla de transiciones es constante aunque eso no es problema, la maquina de estados no debe de cambiar en tiempo de ejecución. Luego crea una tabla de transiciones usando como principio:

static_cast<MAQUINAESTADOS::Accion>(&CHija::funcionmiembro)

Cuando tenga un rato seguiré investigando esa implementación.

Si alguien quiere echarle un vistazo el libro se llama: "Practical StateChars in C/C++ Quantum Programming for Embedded System, Miro Samek, Ph.D."

El ejemplo de lo que me gustaria poder hacer ya lo puse aqui. Hay no utilizo funciones globales, solo funciones miembro, aunque claro ya no puedo utilizar la clase maquinaestados para funciones globales. De momento me conformo en saber como poder utilizar funciones a puntero miembro que es lo que se me escapa a mis conocimientos de C++.

En el ejemplo, vereis que uso la clase MAQUINAESTADOS como madre de la clase PERSIANA. Y luego utilizo las funciones mienbro de la Persiana como las acciones/condiciones de la maquina de estados. Por último en el loop actualizo la maquina de estados de la persiana llamando a Actualizar.

Bueno. Mucho más entendible lo que deseas hacer con tu última explicación. Gracias.
La implementación que expones usando herencia es la óptima a mi entender, ya que desde las funciones miembro (o métodos) también dispones de acceso a los miembros de la clase derivada. Con funciones globales, si necesitas activar o leer el estado de un determinado pin, por ejemplo, deberías recibirlos por parámetro; mientras que si son métodos de clase, puedes utilizar los miembros directamente. Para ello es necesario que la clase herede de la máquina de estados, no que contenga una máquina de estados, que es lo que hacías en tu primer código con tu Class Objeto (no es lo mismo tener una máquina de estados que ser una máquina de estados).
En cuanto a las condiciones para pasar de un estado a otro en la implementación que expones, tal vez se hallen como parte de la propia acción.
En todo caso, siguiendo el mismo método de implementación de acción/transición y tabla de transiciones, puedes establecer la de condiciones. Una de las principales diferencias, como habrás visto, es que tanto la definición a función miembro (typedef void (MAQUINAESTADOS::*Accion)() ) como la estructura que lo usa están DENTRO de la clase.