sábado, 24 de diciembre de 2011

El riesgo de la meta-progamación en C++


El riesgo de abusar de los templates es este: una compilación excesivamente costosa, casi inviable con muchos ordenadores. En la captura se muestra un ejemplo de corbasim donde se recorre varias veces durante la compilación una lista de tipos de 200 elementos. A su vez, cada iteración sobre esos 200 elementos conlleva iteraciones sobre otras listas de tipos derivadas. El resultado es una compilación que llega a provocar paginación, al consumir toda la memoria disponible (y más).

La versión de GCC que utilizo es la 4.4.3. Es probable que en versiones posteriores mejore algo la compilación, pero aún así a día de hoy resulta inviable una compilación de más de media hora para simples herramientas de test. toca optimizar la compilación para generar código eficiente en un tiempo razonable.

domingo, 11 de diciembre de 2011

Boost.Fusion: introspección en tiempo de compilación para estructuras en C++

Una de las librerías que más me gusta de las Boost C++ Libraries es la Boost.Fusion. No voy a entrar en detalle en lo que ofrece esta librería; básicamente permite el manejo de tuplas de elementos de distintos tipos.

Lo bueno de las tuplas que ofrece esta librería son las meta-funciones que ofrecen introspección en tiempo de compilación sobre estas tuplas. El disponer de esta introspección permite escribir código genérico sea cual sea el número y tipo de los elementos de estas tuplas.

La característica que más me gusta de esta librería es que permite poder adaptar estructuras convencionales de C++ para ser utilizadas como lo que realmente son: tuplas de elementos de distintos tipos. De esta forma, podemos utilizar las meta-funciones de esta librería sobre estructuras y así escribir código genérico. Creo que el poder adaptar estructuras para poder tener cierta introspección -siempre en tiempo de compilación- cubre una de las carencias principales del lenguaje C++.

Esta característica la he usado en varios proyectos. Uno de ellos es CORBASIM, donde se utiliza para generar automáticamente -en tiempo de compilación- formularios para introducir datos. También se utiliza para serializar estas estructuras en formato JSON.

Lo malo de esta característica, es que para poder utilizarla hay que definir dos veces la estructura: una -la propia estructura-, y otra con la macro BOOST_FUSION_ADAPT_STRUCT. Es decir, escribir dos veces la misma información semántica. En CORBASIM esto no supone ningún problema, ya que todo el código es generado a partir de ficheros IDL. Pero en otros ámbitos donde no hay un DSL ni código generado de por medio, esta duplicidad es un poco incómoda. La falta de esta introspección de por sí en el lenguaje es -en mi opinión- la mayor de sus carencias.

Para intentar cubrir esta carencia estaría bien disponer de una herramienta que realice esta adaptación de forma automática, generando código C++ adicional a partir de código C++. Esto no sería muy difícil; bastaría recorrer el AST del código en el que se definen las estructuras que queremos adaptar. El AST se puede recorrer por ejemplo con un plugin de GCC, solo hay que echarle tiempo. En una prueba rápida, he conseguido esbozar esta herramienta utilizando Dehydra.

Aunque Dehydra permite recorrer el AST mediante código JavaScript con el fin de implementar validadores de código C++, también se podría utilizar para generar automáticamente este código adicional. El código JavaScript del esbozo que he realizado con Dehydra para generar este código es tan sencillo como el que sigue:

function getHeader(name)
{
    return name.replace('::','_').toUpperCase() + '_ADAPTED';
}

function process_type(type)
{
    if (type.kind == "struct" && !type.typedef 
            && type.members.length > 0)
    {
        header = getHeader(type.name);
        print('#ifndef ' + header);
        print('#define ' + header);

        print('// Generated code for ' + type.name + 
                ' defined in ' + type.loc);
        // Uso de Boost.Fusion
        print('BOOST_FUSION_ADAPT_STRUCT (');
        print('\t' + type.name + ',');
        for each(let m in type.members)
        {
            print('\t(' + m.type.name + ', ' + m.shortName+')');
        }
        print(')');
        print('');

        print('#endif // ' + header);
        print('');
    }
}


Esta herramienta me parece interesante para poder generalizar parte del trabajo realizado en CORBASIM, haciéndolo independiente de CORBA. Extenderé la idea :)

lunes, 18 de abril de 2011

Protoboost: A C++ code generator for boost-serializable classes from .proto files

I've just started this project:

http://code.google.com/p/protoboost/

How to beautify C++ generated with EMF Xpand

Just install "Xpand/Xtext additional support" Eclipse plugin from the update site below:

http://download.eclipse.org/modeling/m2t/xpand/updates/releases/

You must also install the C++ Development Tools (CDT). Then, you can invoke a C++ code beatufier from your mwe2 generation workflow. Just add the packages org.eclipse.xpand.support.cdt and org.eclipse.cdt.core as required plug-in in your Xpand project and add the line below in the outlet section when you invoke the Xpand generator:

postprocessor = org.eclipse.xpand.support.cdt.CppBeautifier {}

sábado, 16 de abril de 2011

A generic JSON serializer

#ifndef JSON_SERIALIZER_HPP
#define JSON_SERIALIZER_HPP

#include <boost/type_traits.hpp> // is_array, is_class, remove_bounds

#include <boost/mpl/eval_if.hpp>
#include <boost/mpl/identity.hpp>
#include <boost/mpl/next_prior.hpp>

#include <boost/fusion/mpl.hpp>
#include <boost/fusion/adapted.hpp> // BOOST_FUSION_ADAPT_STRUCT

// boost::fusion::result_of::value_at
#include <boost/fusion/sequence/intrinsic/value_at.hpp>
#include <boost/fusion/include/value_at.hpp>

// boost::fusion::result_of::size
#include <boost/fusion/sequence/intrinsic/size.hpp>
#include <boost/fusion/include/size.hpp>

// boost::fusion::at
#include <boost/fusion/sequence/intrinsic/at.hpp>
#include <boost/fusion/include/at.hpp>


namespace json
{

// Forward
template < typename T >
struct serializer;

namespace detail
{

namespace iterator
{

template < typename S, typename N >
struct Comma
{
    template < typename Ostream >
    static inline void comma(Ostream& os)
    {
        os << ", ";
    }
};

template < typename S >
struct Comma< S, typename boost::mpl::prior< typename boost::fusion::result_of::size< S >::type >::type >
{
    template < typename Ostream >
    static inline void comma(Ostream& os)
    {
    }
};

// Iteracion sobre una estructura
template < typename S, typename N >
struct StructImpl
{
    // Tipo del campo actual
    typedef typename boost::fusion::result_of::value_at< S, N >::type current_t;
    typedef typename boost::mpl::next< N >::type next_t;
    typedef boost::fusion::extension::struct_member_name< S, N::value > name_t;

    template < typename Ostream >
    static inline void serialize(Ostream& os, const S& s)
    {
        os << "\"" << name_t::call() << "\": ";
        ::json::serializer< current_t >::serialize(os, boost::fusion::at< N >(s));
    
        // Insert comma or not    
        Comma< S, N >::comma(os);

        StructImpl< S, next_t >::serialize(os, s);
    }
};

// Fin de la iteracion sobre estructuras.
template < typename S >
struct StructImpl< S, typename boost::fusion::result_of::size< S >::type >
{
    template < typename Ostream >
    static inline void serialize(Ostream& os, const S& s)
    {
        // Nada que hacer
    }
};

// Iterador sobre una estructura. Template fachada.
template < typename S >
struct Struct : StructImpl< S, boost::mpl::int_< 0 > > {};

} // iterator

template < typename T >
struct array_serializer 
{
    typedef array_serializer< T > type;

    typedef typename boost::remove_bounds< T >::type slice_t;

    static const size_t size = sizeof(T) / sizeof(slice_t);

    template < typename Ostream >
    static inline void serialize(Ostream& os, const T& t)
    {
        os << "[";
        for(size_t idx=0; idx<size; idx++)
        {
            ::json::serializer< slice_t >::serialize(os, t[idx]);
            if (idx != size-1)
                os << ", ";
        }
        os << "]";
    }

};

template < typename T >
struct struct_serializer 
{
    typedef struct_serializer< T > type;

    template < typename Ostream >
    static inline void serialize(Ostream& os, const T& t)
    {
        os << "{";
        iterator::Struct< T >::serialize(os, t);
        os << "}";
    }
};

template < typename T >
struct arithmetic_serializer 
{
    typedef arithmetic_serializer< T > type;

    template < typename Ostream >
    static inline void serialize(Ostream& os, const T& t)
    {
        os << t;
    }
};

template < typename T >
struct calculate_serializer
{
    typedef
        typename boost::mpl::eval_if< boost::is_array< T >,
            boost::mpl::identity< array_serializer < T > >,
        //else
        typename boost::mpl::eval_if< boost::is_class< T >,
            boost::mpl::identity< struct_serializer < T > >,
        //else
            boost::mpl::identity< arithmetic_serializer < T > >
        >
        >::type type;

};

} // detail

template < typename T >
struct serializer : public detail::calculate_serializer < T >::type
{
};


} // json

#endif // JSON_SERIALIZER_HPP

Sample usage:

#include "json.hpp"
#include <iostream>

struct my_other_struct
{
    int my_other_integer;
};

struct my_struct
{
    int my_integer;

    typedef int my_array_t[2];
    my_array_t my_array;

    typedef my_other_struct my_other_structs_t[3];
    my_other_structs_t my_other_structs;
};

BOOST_FUSION_ADAPT_STRUCT(my_struct, (int, my_integer) (my_struct::my_array_t, my_array) (my_struct::my_other_structs_t, my_other_structs))
BOOST_FUSION_ADAPT_STRUCT(my_other_struct, (int, my_other_integer))


int main(int argc, char *argv[])
{
    my_struct s1 = my_struct();

    json::serializer< my_struct >::serialize(std::cout, s1);

    std::cout << std::endl;
}

Result:

{"my_integer": 0, "my_array": [0, 0], "my_other_structs": [{"my_other_integer": 0}, {"my_other_integer": 0}, {"my_other_integer": 0}]}

Boost C++ Libraries 1.45.0 or greater required!

Implementing a JSON serializer using Boost.Fusion and Boost.Serialization (I)

Using Boost.Serialization, we have a generic way to serialize and load data, simply adding a template method "serialize" in our classes/structs.

Using Boost.Fusion (version equal or greater than 1.45.0), we have a generic way to access the fields of a struct and get their names.

The idea: using both we can implement a JSON serializer/deserializer with the usage bellow:

struct my_JSON_struct
{
    int my_integer;
    int my_array[2];
    std::string my_string;

    template < typename Archive >
    void serialize(Archive& ar, unsigned int version)
    {
        ar & my_integer;
        ar & my_array;
        ar & my_string;
    }
};

// ...

my_JSON_struct s1, s2;

// ...

// Saving data (os == ostream)
json_oarchive joa(os);
joa << s1;
// Resulting: {"my_integer" : 10, "my_array" : [1, 4], "my_string" : "nice string"}

// Loading data (is == istream)
json_iarchive jia(is);
jia >> s2;

To be continued...

How to calculate the number of dimensions of an array

Using the same idea of the two previous posts, we can calculate the number of dimensions of an array as follows:

#include <iostream>

template < typename T >
struct calc_dim
{
    static const unsigned int value = 0;
};

template < typename T, unsigned int N >
struct calc_dim< T[N] >
{
    static const unsigned int value = calc_dim< T >::value + 1;
};

int main()
{
    std::cout << calc_dim < int[5][10] >::value  << std::endl;
    std::cout << calc_dim < int[5][10][12] >::value << std::endl;
    std::cout << calc_dim < int[10] >::value << std::endl;
    std::cout << calc_dim < float[10] >::value << std::endl;
    std::cout << calc_dim < float >::value << std::endl;
}

Result:

2
3
1
1
0

viernes, 15 de abril de 2011

Cómo especializar un template para arrays (II)

Siempre se aplica el más restrictivo, o lo que es lo mismo, el más especializado. Ejemplo:

#include <iostream>

template < typename T >
struct Example
{
    static inline void call()
    {
        std::cout << "non-array" << std::endl;
    }
};

template < typename T, unsigned int N >
struct Example< T[N] >
{
    static inline void call()
    {
        std::cout << "array" << std::endl;
    }
};


template < unsigned int N >
struct Example< int[N] >
{
    static inline void call()
    {
        std::cout << "integer array" << std::endl;
    }
};

int main()
{
    Example < int[5][10] >::call();
    Example < int[10] >::call();
    Example < float[10] >::call();
    Example < float >::call();
}

Resultado:

array
integer array
array
non-array

Cómo especializar un template para arrays

#include <iostream>

template < typename T >
struct Example
{
    static inline void call()
    {
        std::cout << "non-array" << std::endl;
    }
};

template < typename T, unsigned int N >
struct Example< T[N] >
{
    static inline void call()
    {
        std::cout << "array" << std::endl;
    }
};

int main()
{
    Example < int[5][10] >::call();
    Example < float[10] >::call();
    Example < float >::call();
}

Resultado:

array
array
non-array

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.

Bienvenida

Con su nombre indica, el objetivo de este blog es romper con el tópico de que C++ es C con clases. Para ello se abordarán cuestiones sobre programación genérica o metaprogramación, patrones de diseño, idioms, DSL embebidos, y cualquier tópico avanzado sobre C++, como el uso de las Boost C++ Libraries.