Как реализовать большое количество сложных оболочек для устаревших API/фреймворков (С++ Macros vs. С++ Templates vs. Code generator)?

Мы работаем с очень старой унаследованной системой, реализованной на С++ с компилятором VC6. Теперь мы находимся в процессе рефакторинга кода. Мы также переключились на компилятор VC9.

Мы используем внешнюю проприетарную структуру, которая также является устаревшим кодом, а не тестируемой. Чтобы сделать наш блок кода проверенным, мы представили интерфейсы и обертки для классов инфраструктуры (подсказка: см. "Работа с устаревшим кодом" Мартина Фаулера):

enter image description here

Теперь мы зависим от интерфейсов. Обертки называют методы структуры, и мы можем с радостью использовать mocks в наших модульных тестах.

И вот мы приходим к нашей проблеме...

Классы framework содержат множество методов, которые необходимо обернуть и высмеять. Для достижения этой цели наша команда разработчиков написала API, который генерирует реализации интерфейсов, оболочек и макетов с использованием макросов С++.

Пример файла заголовка обертки:

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 заключается в том, что он чрезвычайно сложный, не читаемый, не отлаживаемый и не проверяемый.

Мы хотели бы предложить лучший механизм для достижения той же цели. Идея заключалась в том, что мы используем некоторые из расширенных функций, которые поставляются с новым компилятором, например, с расширенными шаблонами, списками типов, чертами и т.д.

С шаблонами мы можем почти достичь нашей цели, но мы застряли в именах методов. Мы можем обобщать типы, но как мы имеем дело с именами атрибутов?

Мы также подумали о создании инструмента для автоматического создания кода wrapper + interfaces + mocks. Однако API нашей внешней структуры чрезвычайно сложный, и написать такой инструмент будет очень дорого.

Как вы думаете, лучший способ решить такую ​​проблему? Может быть, вы уже справлялись с чем-то подобным и могли дать хорошие советы? Мы с нетерпением ждем ваших ответов!

Ответ 1

Думаю, я бы пошел с инструментом генерации кода. Я бы, вероятно, сделал несколько простых служебных программ: один для создания интерфейса, соответствующего классу вашей устаревшей инфраструктуры, один для создания обертки и один для генерации макетного объекта (или, по крайней мере, скелета).

Это подразумевает некоторый способ анализа кода вашей устаревшей структуры. Я бы посмотрел на Clang или просто запустил ctags в исходном файле и обработать полученные теги.

Ответ 2

Используйте абстрактные factory вместо макросов для решения этой проблемы.

class IApiFactory{
 virtual ISomeApi1* getApi1() =0;
 virtual ISomeApi2* getApi2() =0;
 .....
};

После реализации этого интерфейса для вашего обычного api и moc api и передачи экземпляра вашего factory в вашу систему, например:

MySystem system( new NormalApiFactory );

или

MySystem system( new MocApiFactory );

Ваша система должна быть объявлена ​​как:

class MySystem{
public:
  MySystem( IApiFactory* factory );
};

В factory вы вернете обычные объекты реализации api или moc. Конечно, вы можете вернуть фабрики, "которые возвратят другие фабрики или объекты" из вашего корня factory.

Ответ 3

Если база кода достаточно велика (т.е. несколько сотен тысяч строк С++), и если вы можете скомпилировать ее с помощью GCC ( недавняя версия, то есть 4.6), вы могли бы подумать о создании специального плагина GCC или MELT расширение. (MELT - это высокоуровневый доменный язык для расширения GCC). Однако такая настройка GCC требует значительных усилий (недель, а не часов работы).

Ответ 4

Мне нравится генератор кода. Вы можете создать словарь всех вещей, которые должны быть обернуты, требуется время, а затем используйте простой инструмент для генерации кода. Помимо кода, вы также получаете четкую ссылку на устаревшую систему, руководство для ее замены позже.

Ответ 5

Наш DMS Software Reengineering Toolkit - инструмент преобразования программ, который читает исходный код и выполняет на нем произвольные преобразования.

DMS, с его С++ Front End (VC6 и более современная Visual Studio), возможно, он может быть использован для чтения файлов заголовков для компоненты, которые нужно издеваться, и генерировать mocks.

DMS используется для выполнения массивных преобразований кода С++, особенно с использованием изменений интерфейса и перетасовки интерфейсов различными способами. См. Один из моих технических документов, Реконструкция моделей компонентов С++ с помощью автоматической трансформации программ.

Ответ 6

Существует способ структурирования генерации кода полностью контролируемым образом и по-прежнему поддерживать всю свободу генерации того, что когда-либо было сложным API-интерфейсом, в котором вы нуждаетесь.

Если вы используете Visual Studio (2005, по крайней мере, но желательно 2008 или 2010), вы можете использовать этот T4 Team, упомянутый способ структурированного генерации кода, который основан только на T4 и XML:

http://blogs.msdn.com/b/t4/archive/2011/11/30/some-nice-new-getting-started-with-t4-videos.aspx

Я новатор и ведущий архитектор этой методологии ADM и автор этого блога http://abstractiondev.wordpress.com/

К сожалению, у меня нет конкретного случая для ссылки, чтобы соответствовать вашим требованиям, но я рад помочь вам решить (и в случае, если вы хотите принести пользу сообществу, опубликуйте из этого случая).