Data binding
El Data Binding es una técnica que nos permite registrar las estructuras o clases del programa con el fin de automatizar ciertas tareas, como la sincronización del modelo de datos con la interfaz de usuario.
Funciones
void | dbind (...) |
void | dbind_enum (...) |
type* | dbind_create (...) |
void | dbind_init (...) |
void | dbind_remove (...) |
void | dbind_destroy (...) |
void | dbind_destopt (...) |
type* | dbind_read (...) |
void | dbind_write (...) |
void | dbind_default (...) |
void | dbind_range (...) |
void | dbind_precision (...) |
void | dbind_increment (...) |
void | dbind_suffix (...) |
Entendemos por Data Binding (vinculación de datos o enlace de datos) la posibilidad de sincronizar automáticamente las estructuras del programa con diferentes fuentes de entrada/salida. Partimos del sencillo modelo (Listado 1) que presentamos en Arrays compuesto por un struct
y un enum
.
struct
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
Lo primero que tenemos que hacer, es registrar este modelo en dbind, una especie de "base de datos" general dentro de nuestra aplicación (Listado 2). Únicamente es necesario realizar este proceso una vez al arrancar el programa. De esta forma se crearán unas tablas internas con la descripción de cada estructura del modelo de datos (Figura 1), quedando el programa preparado para automatizar ciertas tareas al trabajar con objetos de dichas clases.
- Utiliza dbind para registrar los campos de una estructura.
- Utiliza dbind_enum para registrar los diferentes valores de tipos enumerados.
1 2 3 4 5 6 7 8 9 |
dbind_enum(type_t, ekCPU, ""); dbind_enum(type_t, ekGPU, ""); dbind_enum(type_t, ekHDD, ""); dbind_enum(type_t, ekSCD, ""); dbind(Product, type_t, type); dbind(Product, String*, code); dbind(Product, String*, description); dbind(Product, Image*, image64); dbind(Product, real32_t, price); |
1. Sincronización con interfaces gráficas
Uno de los usos más extendidos del enlace de datos, es la posibilidad de sincronizar la interfaz gráfica con los objetos que forman el modelo de datos. Este paradigma es conocido como MVVM (Model-View-ViewModel) (Figura 2) y profundizaremos más en él GUI Data binding.
2. Lectura y escritura de JSON
El análisis sintáctico de scripts JSON también se puede automatizar gracias a dbind (Figura 3). En JSON dispondrás de información detallada de como hacerlo.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
{ "code":0, "size":80, "data":[ {"id":0, "code":"i7-8700K", "description":"Intel BX80684I78700K 8th Gen Core i7-8700K Processor", type":0, "price":374.8899999999999863575794734060764312744140625, "image":"cpu_00.jpg", "image64":"\/9j\/4AAQSkZJRgABAQ.... }, ... } |
3. Serialización con DBind
Como ya vimos en Serialización y Unificar la serialización necesitamos definir funciones de lectura y escritura de objetos para enviarlos o recibirlos a través de streams. Afortunadamente, dbind conoce la composición detallada de cada objeto registrado, por lo que puede acceder a la E/S sin que sea necesario programar explícitamente dichas funciones (Listado 3) (Figura 4).
1 2 3 |
ArrPt(Product) *products = dbind_read(stream, ArrPt(Product)); ... dbind_write(stream, products, ArrPt(Product)); |
4. Constructor por defecto
Gracias a dbind también podemos crear objetos inicializados con valores por defecto sin necesidad de crear constructores específicos (Listado 4). También se pueden destruir garantizando la correcta liberación recursiva de la memoria de todos sus campos.
- Utiliza dbind_create para crear un objeto Constructores.
- Utiliza dbind_init para inicializar un objeto.
- Utiliza dbind_destroy para destruir un objetos Destructores.
1 2 3 4 5 6 7 8 9 10 |
ArrSt(Product) *array = dbind_create(ArrSt(Product)); Product *pr1 = dbind_create(Product); Product *pr2 = arrst_new(array, Product); dbind_init(pr2, Product); // Use objects ... dbind_destroy(&pr1, Product); dbind_destroy(&array, ArrSt(Product)); |
Los valores por defecto al inicializar los campos del objeto son 0
para números, FALSE
para booleanos, ""
para Strings y contenedores vacíos en el caso de arrays o sets. Si el objeto contiene sub-objetos anidados, estos también serán creados/inicializados de forma recursiva. Estos valores por defecto se pueden cambiar si fuera necesario (Listado 5).
- Utiliza dbind_default para establecer el valor por defecto.
1 2 3 4 |
dbind_default(Product, type_t, type, ekHDD); dbind_default(Product, String*, code, "Empty-code"); dbind_default(Product, real32_t, price, 5.f); dbind_default(Product, Image*, image64, gui_image(NOIMAGE_PNG)); |
5. Rangos numéricos
Es posible configurar los campos numéricos uint32_t, int8_t, real64_t
para acotar los valores aceptados a un determinado rango (Listado 6). dbind se encargará de validar los datos cada vez que lea valores de cualquier origen de datos (GUI, JSON, Streams, etc).
- Utiliza dbind_range para establecer un máximo y mínimo en valores numéricos.
- Utiliza dbind_precision para establecer la precisión numérica. Por ejemplo
0.01
en valores monetarios. - Utiliza dbind_increment para establecer el valor de incrementos discretos.
- Utiliza dbind_suffix para establecer un sufijo que será añadido al convertir los números a texto.
price
.
1 2 3 4 5 |
dbind_default(Product, real32_t, price, 10f); dbind_range(Product, real32_t, price, .50f, 10000f); dbind_precision(Product, real32_t, price, .01f); dbind_increment(Product, real32_t, price, 5.f); dbind_suffix(Product, real32_t, price, "€"); |
dbind ()
Añade un campo de una estructura/clase a su tabla interna dentro de dbind.
void
dbind(type,
mtype,
name);
type | Tipo de la estructura o clase. |
mtype | Tipo del campo a registrar. |
name | Nombre del campo dentro de la estructura. |
Observaciones
Se generarán errores en tiempo de compilación si el campo indicado no pertenece a la estructura. El método también sirve para clases en C++.
dbind_enum ()
Registra un valor tipo enum
.
void dbind_enum(type, value, const char_t *alias);
type | Tipo del enum. |
value | Valor. |
alias | Alias para el valor. |
Observaciones
dbind_enum(mode_t, ekIMAGE_ANALISYS, "Image Analisys");
utilizará la cadena "Image Analisys" en lugar de "ekIMAGE_ANALISYS" para aquellas operaciones E/S o de interfaz que requieran mostrar los literales del enumerado. Por ejemplo, para rellenar los campos de un PopUp vinculado con un campo de datos.
dbind_create ()
Crea un objeto de tipo registrado, inicializando sus campos con los valores por defecto.
type* dbind_create(type);
type | Tipo de objeto. |
Retorna
Objeto recién creado o NULL
si dbind no reconoce el tipo de dato.
dbind_init ()
Inicializa los campos de un objeto de tipo registrado con los valores por defecto.
void dbind_init(type *obj, type);
obj | Objeto cuya memoria ha sido reservada, pero no inicializada. |
type | Tipo de objeto. |
dbind_remove ()
Destruye la memoria reservada por los campos de un objeto de tipo registrado, pero no destruye el objeto en sí.
void dbind_remove(type *obj, type);
obj | Objeto. |
type | Tipo de objeto. |
dbind_destroy ()
Destruye un objeto de tipo registrado. La memoria asignada a los campos y sub-objetos también será liberada de forma recursiva.
void dbind_destroy(type **obj, type);
obj | Objeto. Será puesto a |
type | Tipo de objeto. |
dbind_destopt ()
Destructor opcional. Igual que dbind_destroy, pero aceptando que el objeto sea NULL
.
void dbind_destopt(type **obj, type);
obj | Objeto a destruir. |
type | Tipo de objeto. |
dbind_read ()
Crea un objeto de tipo registrado a partir de los datos leídos desde un stream.
type* dbind_read(Stream *stm, type);
stm | Stream de lectura. |
type | Tipo del objeto a leer. |
Retorna
Objeto recién creado o NULL
si ha habido algún error.
dbind_write ()
Escribe el contenido de un objeto de tipo registrado en un stream de escritura.
void dbind_write(Stream *stm, const type *data, type);
stm | Stream de escritura. |
data | Objeto a escribir. |
type | Tipo del objeto a escribir. |
dbind_default ()
Establece el valor por defecto de un campo.
void dbind_default(type, mtype, name, mtype value);
type | Tipo de la estructura o clase. |
mtype | Tipo del campo. |
name | Nombre del campo dentro de la estructura. |
value | Valor por defecto a partir de ahora. |
dbind_range ()
Establece el valor máximo y mínimo en campos numéricos.
void dbind_range(type, mtype, name, mtype min, mtype max);
type | Tipo de la estructura o clase. |
mtype | Tipo del campo. |
name | Nombre del campo dentro de la estructura. |
min | Valor mínimo. |
max | Valor máximo. |
Observaciones
Dará error si se utiliza en campos no numéricos.
dbind_precision ()
Establece el salto entre dos valores numéricos consecutivos.
void dbind_precision(type, mtype, name, mtype prec);
type | Tipo de la estructura o clase. |
mtype | Tipo del campo. |
name | Nombre del campo dentro de la estructura. |
prec | Precisión (p.e. |
Observaciones
Dará error si se utiliza en campos no numéricos.
dbind_increment ()
Establece el incremento de un valor numérico al pulsar en un control UpDown.
void dbind_increment(type, mtype, name, mtype incr);
type | Tipo de la estructura o clase. |
mtype | Tipo del campo. |
name | Nombre del campo dentro de la estructura. |
incr | Incremento. |
Observaciones
Dará error si se utiliza en campos no numéricos.
dbind_suffix ()
Establece un sufijo que será añadido al valor numérico al convertirlo a texto.
void dbind_suffix(type, mtype, name, const char_t *suffix);
type | Tipo de la estructura o clase. |
mtype | Tipo del campo. |
name | Nombre del campo dentro de la estructura. |
suffix | Sufijo. |
Observaciones
Dará error si se utiliza en campos no numéricos.