lunes, 11 de abril de 2011

Asociar un widget de Qt para introducir valores de un tipo concreto (I)

Actualmente estamos desarrollando un generador de interfaces gráficas en Qt a partir de interfaces en lenguaje IDL. Se trata, en resumidas cuentas, de generar formularios para rellenar instancias de los tipos definidos en un fichero IDL. Por ejemplo, para un entero generar un QSpinBox, para una estructura generar QGroupBox con widgets hijos para los campos de la estructura, etc...

En una primera versión generábamos cantidades ingentes de código creando widgets de Qt, mucho código repetido, etc... Además, si se quería modificar el widget asociado a un tipo concreto había que modificar bastante el código generado. Por ejemplo, para asociar a una estructura "Date" un widget más natural que un conjunto de QSpinBox.

Para delimitar un poco el problema, limitaremos el conjunto de tipos definibles en los ficheros IDL a arrays, estructuras y tipos aritméticos primitivos.

Una forma de resolver este problema mediante programación genérica es definir un template widget que dado un tipo le asocie un widget de Qt. Si el usuario del generador de código desea cambiar la representación de un tipo concreto bastará con que especialice el template para ese tipo concreto.

template< typename T >
struct widget;

Este template tendrá un método create_widget para crear el widget concreto asociado a ese tipo. El generador ya no generará cantidades ingentes de código inicializando widgets de Qt, si no que, para un tipo T, se instanciaría a widget< T >::create_widget para crear el widget Qt asociado a ese tipo. Este template tendrá distinto comportamiento en función del tipo de tipo que sea T.

Como se ha comentado antes se ha limitado los tipos de tipos a tres: aritméticos primitivos, arrays y estructuras. Para cada uno de ellos el widget Qt asociado es distinto, por lo que es necesario un comportamiento del template widget en función del tipo de tipo que sea T. Para esto utilizamos una meta-función calculate_default_widget, a la cual se invocará al instanciar el template widget para un tipo concreto.

template< typename T >
struct widget : public detail::calculate_default_widget< T >::type
{
};

La implementación de esta meta-función, para los tipos de tipos posibles sería como sigue:

namespace detail {
template < typename T >
struct calculate_default_widget
{
    typedef
        typename boost::mpl::eval_if< boost::is_array< T >,
            boost::mpl::identity< default_array_widget < T > >,
        //else
        typename boost::mpl::eval_if< boost::is_class< T >,
            boost::mpl::identity< default_struct_widget < T > >,
        //else
            boost::mpl::identity< default_arithmetic_widget < T > >
        >
        >::type type;

};
}

Donde default_*_widget son templates con el comportamiento por defecto para cada uno de los tipos de tipos posibles (en post posteriores abordaremos la implementación de estos templates).

Esta meta-función hace uso de las meta-funciones eval_if e identity de la Boost Metaprogramming Library, así como de los Boost Type Traits is_array e is_class. Básicamente lo que hace esta meta-función es un if-else en tiempo de compilación en función del tipo T, calculando el tipo correspondiente para T.

En sucesivos posts iré completando la implementación de este ejemplo mediante programación genérica.

No hay comentarios:

Publicar un comentario