2011-12-14 2 views
10

Мы работаем с очень старой унаследованной системой, реализованной на C++ с компилятором VC6. Теперь мы находимся в процессе рефакторинга кода. Мы также переключились на компилятор VC9.Как реализовать большое количество сложных оболочек для устаревшего API/framework (C++ Macros vs. C++ Templates против генератора кода)?

Мы используем внешний проприетарный каркас, который также является устаревшим кодом, а не тестируемым модулем. Для того, чтобы сделать наш код блок проверяемых, мы ввели интерфейсы и оберток для каркасных классов (подсказка: см «Работа с унаследованным кодом» Мартина Фаулера):

enter image description here

Теперь мы зависим от интерфейсов. Обертки называют методы структуры, и мы можем с радостью использовать 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 нашей внешней структуры чрезвычайно сложный, и написать такой инструмент будет очень дорого.

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

ответ

1

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

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

+0

Thx. Мы обязательно посмотрим на Clang и ctags. – nowaq

0

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

+0

Жаль, что мы не с GCC. К сожалению, мы полностью придерживаемся компилятора Visual Studio. – nowaq

+0

+1 для использования обертки для устаревшего кода или неиспользованного исходного кода. – umlcat

1

Используйте абстрактную фабрику вместо макросов, чтобы решить эту проблему.

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

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

MySystem system(new NormalApiFactory); 

или

MySystem system(new MocApiFactory); 

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

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

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

+1

Но проблема в том, как реорганизовать большой пакет кода на этот шаблон .... –

+0

@BasileStarynkevitch «Проблема с API заключается в том, что он чрезвычайно сложный, не читаемый, не отлаживаемый и не проверяемый». «Мы хотели бы предложить лучший механизм для достижения той же цели. Идея заключалась в том, что мы используем некоторые дополнительные функции, которые поставляются с новым компилятором, например, с расширенными шаблонами, списками типов, чертами и т. Д. Я просто предлагаю стандартный механизм для решения этой проблемы. – AlexTheo

+0

@AlexTheo Извините, может быть, описание может быть немного запутанным. Говоря «API», я имел в виду MACROS, которые мы в настоящее время используем для создания Wrappers. Это «проблема», которую мы хотели бы заменить чем-то более хорошим/лучшим/более надежным. – nowaq

0

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

0

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

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

DMS был использован для осуществления массивных преобразований на C++-коде, особенно с использованием изменений интерфейса и перетасовки интерфейсов различными способами. См. Мою техническую документацию, Re-engineering C++ Component Models Via Automatic Program Transformation.

0

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

Если вы используете Visual Studio (2005, по крайней мере, но предпочтительно 2008 или 2010), вы можете использовать упомянутый способ этого T4 Команды из структурированной генерации коды - который основан на Т4 и 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/

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