Мы работаем с очень старой унаследованной системой, реализованной на C++ с компилятором VC6. Теперь мы находимся в процессе рефакторинга кода. Мы также переключились на компилятор VC9.Как реализовать большое количество сложных оболочек для устаревшего API/framework (C++ Macros vs. C++ Templates против генератора кода)?
Мы используем внешний проприетарный каркас, который также является устаревшим кодом, а не тестируемым модулем. Для того, чтобы сделать наш код блок проверяемых, мы ввели интерфейсы и оберток для каркасных классов (подсказка: см «Работа с унаследованным кодом» Мартина Фаулера):
Теперь мы зависим от интерфейсов. Обертки называют методы структуры, и мы можем с радостью использовать mocks в наших модульных тестах.
И здесь мы подходим к нашей проблеме ...
Каркасные классы содержат много методов, которые должны быть обернуты и издевались. Для достижения этой цели наша команда разработчиков написала API, который генерирует реализации интерфейсов, оболочек и макетов с использованием C++-макросов.
Пример заголовка файла обертка:
class PlanWrapper : public IPlan
{
// ...
WRP_DECLARE_DEFAULTS(FrameworkPlan); // macro
WRP_DECLARE_CSTR_ATTR(FrameworkPlanLabel); // macro
// ...
};
Макро WRP_DECLARE_CSTR_ATTR определяется следующим образом:
#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()
Пример обертки CPP файла:
#include "StdAfx.h"
using namespace SomeNamespace;
WRP_IMPLEMENT_MODDICOM_DEFAULTS(FrameworkPlan)
WRP_IMPLEMENT_W_CSTR_ATTR (FrameworkPlan,FrameworkType1, FrameworkPlanLabel)
// ...
Макро WRP_IMPLEMENT_W_CSTR_ATTR определен например:
#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; \
}
// ...
У нас есть еще более сложный материал, но я думаю, что вы поняли эту идею.
Проблема с API заключается в том, что он чрезвычайно сложный, не читаемый, не отлаживаемый и не проверяемый.
Мы хотели бы предложить лучший механизм для достижения той же цели. Идея заключалась в том, что мы используем некоторые дополнительные функции, которые поставляются с новым компилятором, например, с расширенными шаблонами, списками типов, чертами и т. Д.
С помощью шаблонов мы можем почти достичь своей цели, но мы застряли в именах методов. Мы можем обобщать типы, но как мы имеем дело с именами атрибутов?
Мы также подумали о создании инструмента для автоматического создания кода оболочки + интерфейсов + mocks. Однако API нашей внешней структуры чрезвычайно сложный, и написать такой инструмент будет очень дорого.
Как вы считаете, лучший способ решить эту проблему? Может быть, вы уже справлялись с чем-то подобным и могли дать хорошие советы? Мы с нетерпением ждем ваших ответов!
Thx. Мы обязательно посмотрим на Clang и ctags. – nowaq