Trabajamos con un sistema heredado muy antiguo implementado en C++ con el compilador VC6. Ahora estamos en el proceso de refactorizar el código. También cambiamos al compilador VC9.¿Cómo implementar una gran cantidad de envoltorios complejos para API/framework heredado (C++ Macros vs. C++ Templates vs. Code generator)?
Usamos un marco de propiedad externo, que también es código heredado y no es comprobable por unidad. Con el fin de hacer que nuestra unidad de código comprobable, introdujimos interfaces y contenedores para las clases del framework (pista: ver “Utilización del código de legado” por Martin Fowler):
Ahora dependemos de interfaces. Los wrappers llaman a los métodos de framework y podemos felizmente usar mocks en nuestras pruebas unitarias.
Y aquí llegamos a nuestro problema ...
Las clases del framework contienen muchos métodos que deben ser envueltos y burlado. Para lograr este objetivo, nuestro equipo de proveedores escribió una API que genera interfaces, envolturas y implementaciones de burlas con el uso de Macros C++.
Ejemplo de archivo de cabecera envoltura:
class PlanWrapper : public IPlan
{
// ...
WRP_DECLARE_DEFAULTS(FrameworkPlan); // macro
WRP_DECLARE_CSTR_ATTR(FrameworkPlanLabel); // macro
// ...
};
El WRP_DECLARE_CSTR_ATTR Macro se define así:
#define WRP_DECLARE_CSTR_ATTR(AttrName) \
virtual bool set##AttrName (LPCTSTR Value_in); \
virtual bool get##AttrName (CString& Value_out); \
virtual bool unset##AttrName(); \
virtual bool isSet##AttrName()
Ejemplo de archivo de envoltura CPP:
#include "StdAfx.h"
using namespace SomeNamespace;
WRP_IMPLEMENT_MODDICOM_DEFAULTS(FrameworkPlan)
WRP_IMPLEMENT_W_CSTR_ATTR (FrameworkPlan,FrameworkType1, FrameworkPlanLabel)
// ...
se define La WRP_IMPLEMENT_W_CSTR_ATTR Macro como este:
#define WRP_IMPLEMENT_W_CSTR_ATTR(ClassName,AtrTypeObj,AttrName) \
bool ClassName##Wrapper::set##AttrName (LPCTSTR Value_in) { \
AtrTypeObj aValue = Value_in; \
FrameworkLink<ClassName> convertedObj = NULL_LINK; \
framework_cast(convertedObj, m_Object); \
return convertedObj != NULL_LINK ? \
convertedObj->set##AttrName (aValue) : false; \
}
// ...
Tenemos un montón de cosas aún más complicadas, pero creo que entiendes la idea.
El problema con la API es que es extremadamente complicado, no legible, no se puede depurar y no se puede probar.
Nos gustaría encontrar un mecanismo mejor para lograr el mismo objetivo. La idea era que usamos algunas de las características avanzadas que vienen con el nuevo compilador, como plantillas avanzadas, listas de tipos, rasgos, etc.
Con las plantillas casi podemos lograr nuestro objetivo, pero estamos atascados con los nombres de los métodos. Podemos generalizar para los tipos, pero ¿cómo manejamos los nombres de los atributos?
También pensamos en crear una herramienta para generar automáticamente el código wrapper + interfaces + mocks. Sin embargo, la API de nuestro marco externo es extremadamente complicada y escribir dicha herramienta sería muy costoso.
¿Cuál crees que es la mejor manera de resolver este problema? ¿Tal vez ya ha tratado con algo así y puede proporcionar buenos consejos? ¡Esperamos ver sus respuestas!
Thx. Definitivamente vamos a echar un vistazo a Clang y ctags. – nowaq