Elegir struct a partir de un String en C/C++

Buenas!

Soy novato en este foro y digamos que en el mundo del C y C++.

Escribo para ver si alguien me puede ayudar en un problema que me esta dando dolores de cabeza.

Os voy a poner un ejemplo de mi problema con 2 estructuras, no me serviría ninguna opcion que compare con las estructuras que tengo ya que en mi proyecto real tengo cerca de mil.

Vamos allá:

Tengos 2 structs

struct
{
int rueda;
int puerta;
} coche;

struct
{
int volante;
int chapa;
} furgoneta;

estas dos structs y aunque las dos son vehiculos no necesito que esten enlazadas de ningun tipo. simplemente lo siguiente.

si yo tengo una variable estructura que cada posicion del vector es por ejemplo:

estructura[0] = "c"
estructura[1] = "o"
estructura[2] = "c"
estructura[3] = "h"
estructura[4] = "e"

como en mi array pone coche quiero que mi programa cree una variable de tipo coche.

ej: coche cocheverde.

Se puede hacer lo que digo o es muy complicado?

Muchas gracias!!

Un saludo
 
No, no se puede hacer en lenguaje C.

Mejor dicho: se puede pero no de forma directa. Me explico: tu programa, cuando se ejecuta, ya está compilado, por lo que en ese momento debe conocer el tamaño exacto de las estructuras que debe reservar en memoria (con excepción de los "array de longitud variable").

Entonces, la solución sería algo así:
* una parte del programa debe descubrir si es un "coche" o una "furgoneta" lo que contiene la estructura[]
* sabiendo cuál es, sabe entonces qué tamaño darle a malloc() para que nos reserve espacio suficiente para el almacenamiento de una estructura de ese tamaño
* el resto del programa debe tener en cuenta que el acceso (por el puntero devuelto por malloc) debe ser de forma diferente según el valor original de estructura[]. Eso quiere decir que el programa estará lleno de if() o de switch()

Si, en lugar de usar C, usases un lenguaje dinámico (Perl, Python, Ruby...) entonces sería mucho más fácil.

Ejemplo:
PHP:
my $clase = <>;                        # leemos si "coche" o "furgoneta"
chomp $clase;                          # quitamos retorno de carro

my $vehículo = $clase->new();          # creamos una instancia de esa $clase
 
Última edición por un moderador:
Gracias por tu respuesta Joaquin!

Entonces... si yo creo un puntero usando el malloc y le reservo digamos un 1000bytes, sabiendo que ninguna posible estructura de las que tengo superará esa cantidad, puedo crear variables de mis structs a partir del string?? Como dato decir que las variables me interesa que esten creadas de una en una, osea que cuando acabe la variable cambie de tipo de struct.

Osea.

estructura[] = "coche"
coche vehiculo;
.....
....
hace lo que tenga que hacer con la variable vehiculo de tipo coche.
...
...
estructura []="furgoneta";
furgoneta vehiculo;
......
....
lo mismo, hace lo que tenga que hacer y luego se irá a la siguiente struct, que diga mi array estructura.

Gracias!!
 
Tengo que repasar los últimos estándares de C, el C99 y el C11, pero me temo que no, que no se puede declarar vehículo como de dos estructuras distintas, dentro del mismo contexto (llaves).

Lo que sí puede funcionar es:

* con un strcmp() averiguas qué es lo que contiene estructura
* con un switch()/case, llamas a cada una de las subrutinas que tratan cada vehículo de forma diferenciada.

El problema está en que muestras que cada estructura tiene miembros distintos, por lo que son estructuras completamente distintas, con distinto tamaño y seguro que con distintos métodos y subrutinas.

Lo mejor es eso: crear subrutinas propias para cada tipo de vehículo.
 
El problema de esto es la automatización, ya que en el ejemplo que te expongo solo tengo dos tipos de structs, pero en mi problema real tengo cerca de 1000.

Otra posibilidad que veo sería crear una variable para cada tipo de struct, pero claro que no tuviese que ser picado por mi previamente, ya que estas structs las tengo en un header que a veces serán esas structs y otras veces sera otro header con otras structs diferentes y de diferente nombre.

Hay alguna manera de decir en mi codigo... lee mi header y crea una variable para cada struct k leas?

Aún así si eso pudiera hacerse, como podría comparar lo que tengo en mi string de mi estructura[] con los diferentes tipos de mis variables para saber cual utilizar.

Muchas gracias por tus respuestas y siento las molestias!!
 
La pregunta es: Si tienes 1000 estructuras diferentes, ¿también tienes 1000 procedimientos diferentes? ¿O las puedes agrupar?

Por ejemplo, en vez de tener las estructuras coche y furgoneta, podrías tener uno así:
PHP:
struct {
    int ruedas;
    int puerta;
    int volante;
    int chapa;
} vehiculo_B1;
Luego, en el programa, según el tipo de vehículo, accederemos a unos miembros de la estructura, obviando otros.

Tener 1000 estructuras diferentes es algo muy complicado, si no consigues encontrar un mínimo común denominador a todas ellas. En el caso de vehículos, todos ellos tienen ruedas, motorizado o no, un sistema de navegación o control, puertas... y luego cada uno tendrá una particularidad: en el caso de la furgoneta, volumen y peso máximo de la carga.
 
No la verdad es que no puedo juntar estructuras.

A ver te cuento...Mis estructuras son mensajes CAN de un coche.
En mi header tengo una struct de un posible mensaje de CAN

Ej.

struct
{
int ebd.lamp
int ebd.luz
}brake

cada variable de dentro de la estructura es una de las señales, que tiene dentro de la zona de datos de un mensaje de CAN, pero el software que uso me transforma cada mensaje de una base de datos en structs y no los puedo ni tocar ni mezclar entre ellas ya que cada vez que creo un mensaje y pongo las señales en el valor que quiero lo envio por un bus de comunicaciones.

Por eso la variable que busco es una variable general que sea mensaje, y como quiero que los mensajes se envien de 1 en 1 y comprobar que esté todo bien, esa misma variable buscaba ir cambiandole el tipo pasando por las estructuras que necesito segun me diga mi array estructura [].

Es un poco rollo lo sé pero es una parte bastante importante de mi proyecto final de carrera y llevo dias estancado con esto.
Muchas gracias por la ayuda, lleguemos a una solucion o no siempre es de agradecer.

Un saludo!
 
Hay una forma de reunir la misma definición de muchas estructuras compartiendo el mismo espacio de memoria: union
PHP:
union mensajes {
    struct { int ruedas; int puerta; } coche;
    struct { int volante; int chapa; } furgoneta;
    struct { int bateria; int angulo; int kilometros; } segway;
} mensaje_CAN;
Ahora ya tienes una variable llamada mensaje_CAN, que ocupa el máximo necesario para guardar uno de sus miembros. Podrás acceder cómodamente a los miembros de las estructuras, con tan solo respetar la jerarquía:
PHP:
mensaje_CAN.coche.ruedas = 2;
mensaje_CAN.coche.puerta = 1;
Creo que es lo más cómodo: metemos todas las definiciones de los mensajes en un mismo sitio, creamos una variable para acceder a ese sitio, y luego, según el valor de estructura[] accedemos a un miembro u otro.

El problema viene por si algún miembro es de tamaño variable.
 
Última edición por un moderador:
Entonces con esto que me dices no haría falta que creara una variable para cada struct interna? solo una externa que valga para todos, osea crearia mensajes_CAN mensaje y dsd ahi puedo entrar a todo?

Y si eso fuera así... como sería lo de escoger esa struct segun el valor de mi array estructura[]?

Pk no puedo hacer la comparación de si mi array es = a coche pk estaríamos en las mismas de ir comparandola con todas mis structs.

La idea sería que vea que mi array vale coche y escoja esa struct dentro de la union que creamos.

Buff muchas gracias lo de la union si es así como pienso creo k me va a servir muchisimo.

Un saludo!
 
Pues... a ver... hay una forma de hacerlo, pero entramos en la parte de C "avanzado" :cool:

Se trataría de crear un array, cuyos elementos sean punteros a funciones, cada una de las cuales sería capaz de entender la estructura que debe atender.

Entonces, el procedimiento sería: transformar el valor de estructura[] a un valor índice, para acceder dentro del array.

Te pongo un código de ejemplo:
PHP:
/*
  Prueba de llamadas a subrutinas indexadas en array
  
  Joaquín Ferrero, 2015/01/19

  C99
*/

#include <stdio.h>
#include <string.h>

#define COCHE		0
#define FURGONETA	1
#define SEGWAY		2

//// Declaraciones
int g_coche(void);
int g_furgoneta(void);
int g_segway(void);


//// Definiciones
// Estructura de datos y uniones
union mensajes_CAN {

  struct {
    int ruedas;
    int puerta;
  } coche;

  struct {
    int volante;
    int chapa;
  } furgoneta;

  struct {
    int bateria;
    int angulo;
    int kilometros;
  } segway;

};

union mensajes_CAN mensaje_CAN;			// Almacena el mensaje a analizar

//// Constantes
const char * TIPOS[] = {
  "coche",
  "furgoneta",
  "segway"
};
const int MAXTIPOS = sizeof(TIPOS) / sizeof(TIPOS[0]);

int (*VECTOR_PTR[]) (void) = {			// Punteros a subrutinas
  &g_coche,		// COCHE
  &g_furgoneta,		// FURGONETA
  &g_segway		// SEGWAY
};

//// Variables
char estructura[80];				// Tipo de mensaje recibido

//// Programa principal
void
main (void) {

// DEMO
  int i;
  for (i = 0; i < MAXTIPOS; i++) {
    printf("DEMO Mensaje: %s\n", TIPOS[i]);

    // Código de prueba: según el tipo, ponemos algo en el mensaje_CAN
    switch (i) {
      case COCHE:
        mensaje_CAN.coche.ruedas = 4;
        break;
      case FURGONETA:
        mensaje_CAN.furgoneta.chapa = 3;
        break;
      case SEGWAY:
        mensaje_CAN.segway.bateria = 99;
        break;
    }

    // Inicializamos el mensaje
    strcpy(estructura, TIPOS[i]);

// FIN DEMO

    // Comienzo del proceso
    // Ver qué tipo de mensaje hemos recibido
    int j;
    for (j = 0; j < MAXTIPOS; j++) {
    
      if (0 == strcmp(estructura, TIPOS[i])) {
        printf("Encontrado %s\n", estructura);
        
        // Ejecutar la función asociada
        (VECTOR_PTR[i])();
        
        break;					// el for termina inmediatamente
      }
    }
    // Fin del proceso
  }
}

// Definiciones de subrutinas
int
g_coche(void) {
  printf("%d\n", mensaje_CAN.coche.ruedas);
}

int
g_furgoneta(void) {
  printf("%d\n", mensaje_CAN.furgoneta.chapa + 2);
}

int
g_segway(void) {
  printf("%d %%\n", mensaje_CAN.segway.bateria);
}
En el ejemplo, fíjate en el main(), cómo somos capaces de atender a cada tipo de vehículo, con solo analizar el contenido de estructura[].

La salida es:
PHP:
DEMO Mensaje: coche
Encontrado coche
Ruedas: 4
DEMO Mensaje: furgoneta
Encontrado furgoneta
Espesar de chapa: 5
DEMO Mensaje: segway
Encontrado segway
Bateria: 99 %
Entonces, de cada prueba, el proceso distingue qué tipo de vehículo tenemos, y ejecuta la subrutina correspondiente. Dentro de cada subrutina, hacemos lo que queramos con la información que viene almacenada en mensaje_CAN.

Fíjate además: tenemos que escribir una serie de líneas con puntero a las funciones, en el mismo orden en que están los TIPOS de mensajes. Son un montón de líneas... pero el fruto del trabajo es que el procesamiento dentro de main() queda reducido a muy pocas líneas.

La creación de los punteros y de los identificadores de mensaje se podría automatizar, ya que dijiste que los tenías definidos en archivos cabecera (.h). Con un pequeño programa en Perl o Python se puede hacer rápido.

Tienes más ejemplos de arrays a funciones aquí y aquí.
 
Muchas gracias Joaquín!

Voy a probar con todo lo que me has puesto por aquí, no obstante creo que la clave es lo de automatizar la deteccion de mi string, sin tener que poner todos los posibles casos que pudiera tener, la idea que tengo es tirar por que sé seguro que lo que ponga en mi estructura [], es un mensaje de CAN existente si o sí, x lo tanto sería algo como...

ej

mensaje_CAN."VALOR DE ESTRUCTURA []"."VALOR DE LA SEÑAL QUE TENDRE EN OTRO ARRAY []" = 3;

la cosa es situar el valor de mi array en la frase anterior.

Muchas gracias acabo de ponerme a estudiar la jugada a ver si hoy saco algo ya por fin!

Un saludo!


EDIT:

cuanod hacia coche coche1 para enviar un mensaje usaba este comando:

SendMsg(*((STCAN_MSG *)&coche1));

ahora con la union tendría que ser

SendMsg(*((STCAN_MSG *)&mensaje_CAN.coche)); ???

porque así no me funciona, ni con el punto ni con "->"
 
Última edición:
mensaje_CAN."VALOR DE ESTRUCTURA []"."VALOR DE LA SEÑAL QUE TENDRE EN OTRO ARRAY []" = 3;

la cosa es situar el valor de mi array en la frase anterior.
Si eres capaz de sustituir el valor que recibes en índices numéricos, la expresión de arriba se convierte en una matriz bidimensional...

Hay otra opción: usar hash (diccionarios), con lo que la indexación se hace por cadenas de caracteres. Pero... me temo que tus mensajes CAN no son cadenas de caracteres, sino bytes sueltos.

Creo que... desde C++ sí que se podría hacer, con templates o macros... no sé mucho de esa parte de C++, así que no sé decirte.

Sería interesante ver un ejemplo real, sin tanto coche ni furgoneta. Algo así como un ejemplo de mensaje CAN real y los valores que recibes en estructura[]. Quiero decir que si el tipo de mensaje CAN viene indicado en el propio mensaje, y se compone de unos cuantos bytes, ese valor puedes usarlo como índice al arreglo o a lo que quieras.

cuando hacia coche coche1 para enviar un mensaje usaba este comando:

SendMsg(*((STCAN_MSG *)&coche1));

ahora con la union tendría que ser

SendMsg(*((STCAN_MSG *)&mensaje_CAN.coche)); ???

porque así no me funciona, ni con el punto ni con "->"
Tienes que poner solo mensaje_CAN, que es el valor de inicio de ese espacio de memoria.
 
Ok te pongo un caso real de los que tengo.

Una de las structs que tengo en el .h es:

#ifndef MESSAGENAME_VehicleDyn
#define MESSAGENAME_VehicleDyn
typedef union
{
unsigned char m_aucData[64];
unsigned short int m_auwData[4];
unsigned long int m_aulData[2];

struct
{
unsigned int : 12 ;
unsigned int LONG_ACC_02 : 12 ;
unsigned int TRANS_ACC_02 : 12 ;
unsigned int : 20 ;
unsigned int PSS_02 : 8 ;
unsigned int MVP_COUNT : 2 ;
unsigned int : 6 ;

} __attribute__((__packed__));
}VehicleDyn;

los unsigned int son diferentes señales del mensaje CAN y su longitud de datos en bits.

y cuando quisiera usar esta struct mi array estructura[] = "VehicleDyn"

pero como de structs tengo ésta y mil más, lo que necesitaria es que mi programa leyera VehicleDyn de mi array estructura[] y se fuese automaticamente a la struct puesta arriba. Si tuviera 3 o 4 podría hacerlo con if porque sabria los 4 posibles valores de estructura[], pero en este caso tengo mil y no siempre son los mismos.

Gracias de nuevo!
 
Lo siento, pero en C no se puede usar una cadena como índice dentro de un array. Se podría pensar en usar una estructura hash, pero el trabajo de escribirla es casi igual a la solución que te he comentado antes: usar strcmp() para buscar en una tabla, que hace la traducción a un índice, y de ahí, acceder a la rutina de gestión de la estructura.

Si tienes mil estructuras, lo más cómodo es hacer un programa que extraiga la información de los archivos h. Por ejemplo, con el programa en Perl
PHP:
#!/usr/bin/perl
use v5.14;
my $msg;

while (<>) {

    if (/^\}(.+?);/) {
        $msg = $1;
        $msg =~ s/(^\s+|\s+$)//g;
        say "[$msg]";
    }
}
se pueden extraer los nombres de todas esas estructuras. Y modificándolo un poco más, hasta puede crear directamente el código C de las estructuras que almacenan los nombres (TIPOS en mi código) y los punteros a funciones (el arreglo VECTOR_PTR de mi código).

Vamos, en 5 minutos lo tienes resuelto.

En C++, sí que puedes indexar un arreglo con una cadena de caracteres.
 
El Perl no lo he usado nunca la verdad así que no sabría ni por donde empezar, mi programa puede ser en C o C++, así que me decantare por la opcion de C++ a ver si extraigo algo en claro. LLevo unos días atascado con esto y lo que hago es adelantar el trabajo de extaer los datos del excel que lo tengo casi acabado.

A ver si me aclaro con lo que me pones, gracias!!!
 
Si eres capaz de sustituir el valor que recibes en índices numéricos, la expresión de arriba se convierte en una matriz bidimensional...

Hay otra opción: usar hash (diccionarios), con lo que la indexación se hace por cadenas de caracteres. Pero... me temo que tus mensajes CAN no son cadenas de caracteres, sino bytes sueltos.

Creo que... desde C++ sí que se podría hacer, con templates o macros... no sé mucho de esa parte de C++, así que no sé decirte.

Sería interesante ver un ejemplo real, sin tanto coche ni furgoneta. Algo así como un ejemplo de mensaje CAN real y los valores que recibes en estructura[]. Quiero decir que si el tipo de mensaje CAN viene indicado en el propio mensaje, y se compone de unos cuantos bytes, ese valor puedes usarlo como índice al arreglo o a lo que quieras.

Tienes que poner solo mensaje_CAN, que es el valor de inicio de ese espacio de memoria.
Aqui si uso solo mensaje_CAN que es mi union, no estoy diciendo cual de mis estructuras internas a la union estoy usando, entiendo que la union guarda el espacio de memoria de la struct mas grande pero en algun sitio tendré que decirle que struct es no??? Enviarlo me deja enviarlo pero me da todo 0

EDIT: Me refiero a lo que te decia de :

SendMsg(*((STCAN_MSG *)&mensaje_CAN))
 
Tienes que rellenar mensaje_CAN rellenando los miembros de la estructura del mensaje que quieres enviar. Por ejemplo:

strcpy(mensaje_CAN.VehicleDyn.m_aucData, buffer);
 
Usar estructuras de esta forma es... complicado.

Muchas veces es más práctico usar buffers organizados como se quiera y manejarse con direcciones (o punteros).

Una forma de hacer eso con estructuras es algo así (C en linux):

Código:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define TIPO_DE_VEHICULOS   2

struct coche
{
    int rueda;
    int puerta;
};

struct furgoneta
{
    int volante;
    int chapa;
};

int GetIndiceIngreso(char *string)
{
    char referencia[TIPO_DE_VEHICULOS][20]={"Coche\n","Furgoneta\n"};
    unsigned int cont;

    for(cont=0; cont<TIPO_DE_VEHICULOS; cont++)
    {
        if(!strcmp(string, referencia[cont]))
            return cont;
    }

    return -1;
}

int main()
{
    struct coche tutu={0,0};
    struct furgoneta camioneta={0,0};
    void *ptr[TIPO_DE_VEHICULOS]={&tutu, &camioneta};
    int ret;
    char string[20];

    fgets( string, 20, stdin );

    ret=GetIndiceIngreso(string);
    if(ret>=0)
    {
        *(int*)(ptr[ret])=4;        //Rueda o volante!
        *(int*)(ptr[ret]+4)=2;      //Puerta o chapa

        printf("Rueda: %d\nPuerta: %d\n", tutu.rueda, tutu.puerta);
        printf("Volante: %d\nChapa: %d\n", camioneta.rueda, camioneta.puerta);
    }
    else
        printf("Ingreso incorrecto!\n");

    return 0;
}

La función:

Código:
int GetIndiceIngreso(char *string);

Simplemente transforma el string ingresado a un índice, en caso de no existir el elemento, devuelve -1.

La "magia" está en usar la dirección de la variable de estructura para evitar llamar al nombre de la varible y al mismo tiempo, acceder a memoria a los distintos elementos de la variable de esa estructura usando un offset.

Es decir, "&tutu" es la dirección de la variable de tipo estructura coche, dicha dirección la almaceno en un array de punteros "ptr". Por lo tanto ptr[0] = &tutu y al mismo tiempo a la dirección de la variable "rueda" que se encuentra dentro de la estructura coche. Para acceder a la variable "puerta" es necesario desplazarse el "int" que ocupa en memoria "rueda", como el gcc en arquitectura intel toma a la variable "int" como si fuera de 4bytes, es necesario desplazarse 4 posiciones de memoria, por lo tanto "ptr[0]+4" será la dirección de la variable "rueda" .

La única ventaja de usar estructura en este caso, es que el ordenamiento en memoria es más sencillo de armar, ya que se hace en forma automática, pero a la hora de acceder en forma dinámica (es decir, evitando los nombre definidos), es mediante el acceso a memoria.

En Consola los resultados son los siguientes:

Consola (ingresando el string "Coche" mediante teclado) dijo:
....> ./Estructuras
Coche
Rueda: 4
Puerta: 2
Volante: 0
Chapa: 0

Consola (ingresando el string "Furgoneta" mediante teclado) dijo:
....> ./Estructuras
Furgoneta
Rueda: 0
Puerta: 0
Volante: 4
Chapa: 2

Consola (ingresando el string "Pepe" mediante teclado) dijo:
....> ./Estructuras
Pepe
Ingreso incorrecto!
 
Última edición:
Atrás
Arriba