Un día de trabajo con mi tesis
Quiero poner desde hace tiempo cosas de mi tesis. Sobre todo porque así me animo a escribir y a explicar lo que hago, de cara a empezar a escribirla. Básicamente traba de componentes en CORBA. Cada componente ofrece un conjunto de interfaces como interfaces «ofrecidos». Estos interfaces son como «vistas» de un mismo componente.
La mayoría del código de los componentes se genera automáticamente, salvo el código propio del componentes, que lo tiene que programar el programador. En particular, se debe generar el código que retorna valores adecuados para cada uno de los interfaces. Por ahora estoy con el generador de código. A grandes rasgos, y ya que no puedo explicar todo el diseño aquí, diré que he aumentado el IR (_Interface Repository_, que describe cómo son los interfaces de los objetos) de CORBA para añadir componentes e interfaces ofrecidos. El IR de CORBA estándar sólo contiene definiciones de interfaces normales, y no componentes, por lo que he tenido que aumentarlo.
El generador de código sigue una estrategia de _visitor_ del árbol aumentado del IR (árbol semántico), en la que va generando el fichero de salida. La generación se realiza de manera que el _visitor_ de cada nodo del árbol genera una salida en forma de una variable de texto. Así, por ejemplo, el nodo para «Componente» genera una variable llamada «Component». Imaginemos que una parte de esa variable de texto incluye una función como la que sigue:
...
corbalc::UserArtifact_ptr
%(qualified_component_name)s_impl::queryIfaceArtifact (const char* name)
{
ServantMapType::const_iterator it;
if ((it = ServantMap_.find( name )) == ServantMap_.end())
return NULL;
%(SearchForArtifacts)s
return NULL;
}
. . .
Este trozo de texto generará la función queryIfaceArtifact. Nótese que los elementos entre «%(» y «)s» son variables que se sustituyen al recorrer elementos más internos en el árbol. En el caso de los componentes, hay varios elementos más internos: operaciones, atributos, interfaces requeridos e interfaces ofrecidos.
Nótese también que esta función depende del valor de una variable llamada «SearchForArtifacts». Esta variable, tal y como lo tengo implementado, debe contener el código que retorne un objeto del tipo adecuado según el valor de la variable «name» de la función. Así, se genera una comprobación para cada uno de los interfaces ofrecidos. El código del visitador que genera el valor necesario para la variable «SearchForArtifacts» es el siguiente:
class SearchForArtifactsVisitor: public generic::ProvidedIfaceVisitor
{
public:
SearchForArtifactsVisitor(codegen::util::Context *c)
: generic::ProvidedIfaceVisitor(c)
{
}
const char*
variable() const
{
return "SearchForArtifacts";
}
const char*
pattern_file() const
{
return "CppImplTemplates/SearchForArtifacts";
}
void
traverse (corbalc::AIR::ProvidedIfaceDef_ptr i)
{
CORBA::String_var id = i->id();
IRID irid (id);
ctx_->vm_ ["idl_iface_name"] = id;
ctx_->vm_ ["qualified_iface_name"] = irid.cppFQN(false);
stringstream lengthstr;
lengthstr << strlen (id);
ctx_->vm_ ["idl_iface_name_length"] = lengthstr.str();
ctx_->vm_[variable()] +=
pattern().substitute (ctx_->vm_);
}
};
Sé que este código por sí solo es difícil de entender, pero creo que uno se puede hacer una idea con las siguientes aclaraciones:
1. Esta clase hereda de «ProvidedIfaceVisitor» esto es, es capaz de visitar los interfaces que ofrece el componente.
2. Por tanto, la función «traverse» se ejecutará para cada interface ofrecido por el componente.
3. La función «variable» retorna la variable de texto que será el resultado de este _visitor_ (SearchForArtifacts).
4. La función «pattern_file» especifica el fichero de patrón que se utilizará para hacer de esqueleto de esa variable. Como pasaba en el caso del componente, se utiliza un patrón en el que se sustituyen valores de variables que son a su vez cadenas de texto (lo veremos después).
5. La función «traverse», como puede verse, genera el valor para unas cuantas variables que se utilizarán dentro del patrón, como puede ser «qualified _interface _name», que después veremos que forma parte del patrón para generar esta variable.
6. El valor de la variable «SearchForArtifacts» se añade a lo que ya haya en esa misma variable al final de la función «traverse», con lo que se generará código para todos los interfaces ofrecidos. Ese código se dejará en la variable citada, que a su vez forma parte de la variable «Component», que a su vez forma parte de la variable «Fichero», que genera el fichero final.
(Sé que es un poco complicado, pero creo que expresa la idea).
¿Cómo es el patrón para generar la variable «SearchForArtifacts»? Aquí está:
// Iface %(idl_iface_name)s
if (!strncasecmp (name, "%(idl_iface_name)s", %(idl_iface_name_length)s))
{
%(qualified_iface_name)s_impl* i =
dynamic_cast<%(qualified_iface_name)s_impl*>((*it).second);
return corbalc::UserArtifact::_duplicate (i->executor_);
}
Nótese que contiene las variables a rellenar por el _visitor_, como «qualified _iface _name». Este patrón, una vez relleno, se sustituye en el primero de los patrones mostrados para construir la variable «Component», y así sucesivamente hasta el fichero a generar.
Quizá esto no haya explicado bien lo que hago, pero sí que ha dado una idea de lo penoso del proceso. Supongamos que quiero probar el código generado. Lo que hago es modificar el generador de código. Este generador lo uso para generar código para los componentes de prueba. Los pruebo, y si dan errores (de compilación o funcionamiento) tengo que volver atrás el proceso para revisar los patrones de generación, que contienen los ficheros, pero por partes, lo que complica la cosa, modificarlos acorde con los errores que se hayan producido y repetir el proceso de construir el generador, generar el código y compilar.
La única buena noticia es que al estar basado en patrones, muchas de las veces sólo modificando los patrones de generación es suficiente para arreglar los errores que se hubieran producido o añadir nueva funcionalidad. Así sólo tengo que generar de nuevo el código de los componentes, pero no recompilar el generador.
Si lo hubiera diseñado ahora, quizá habría diseñado un lenguaje específico del dominio, como están ahora tan de moda (y no por casualidad), lo que habría hecho casi innecesario recompilar el generador de código, a costa de que el generador sería más complejo. Este es un tema muy complejo y extenso, que no puedo tratar ahora, y que quizá deje para otro post, pero en general, el lenguaje dedicado en cuestión estaría en concordancia con el orden de visita de los nodos del árbol, y permitiría especificar de forma no funcional qué hacer en cada uno de los nodos. Eso lo dejaré para una posterior entrada.

Hola, Diego
Soy un estudiante mexicano en busca de un buen tutorial de programación con CCM, en particular para aplicarlo con OpenCCM. En mi proyecto de tesis de licenciatura necesito programar algunos servicios que representen aspectos no-funcionales para aplicaciones basadas en componentes. (espero haber sido claro
Podrías ofrecerme alguna guía al respecto?
Saludos, y gracias!
Comment by Programación con OpenCCM — 7/2/2006 @ 2:00
Por cierto,
Mi nombre es Alan y no Programación con OpenCCM
Comment by alan.gustavo — 7/2/2006 @ 2:03
Hola Diego
Soy una estudiante de Ingenieria de Sistemas de Colombia. En estos momentos me encuentro realizando mi tesis sobre la realizacion de un metamodelo quisiera saber si tu me puedes colaborar con algo que llamamos el estado del arte y que hace referencia a la existencia de estos generadores de codigo a nivel mundial. Espero que me puedas colaborar.
Gracias.
Deborah.
Comment by deborah — 4/9/2006 @ 14:54