We work with very old legacy system implemented in C++ with VC6 compiler. Now we are in the process of refactoring the code. We also switched to VC9 compiler.
We use an external proprietary framework, which is also legacy code and not unit testable. In order to make our code unit testable, we introduced interfaces and wrappers for the framework classes (hint: see “Working With Legacy Code” by Martin Fowler):
Now we depend on interfaces. The wrappers call the framework methods and we may happily use mocks in our unit tests.
And here we come to our problem...
The framework classes contain many methods that need to be wrapped and mocked. In order to achieve this goal, our supplier team wrote an API which generates interfaces, wrappers and mocks implementations with use of C++ Macros.
Example of wrapper header file:
class PlanWrapper : public IPlan
{
// ...
WRP_DECLARE_DEFAULTS(FrameworkPlan); // macro
WRP_DECLARE_CSTR_ATTR(FrameworkPlanLabel); // macro
// ...
};
The Macro WRP_DECLARE_CSTR_ATTR is defined like this:
#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 ()
Example of wrapper cpp file:
#include "StdAfx.h"
using namespace SomeNamespace;
WRP_IMPLEMENT_MODDICOM_DEFAULTS(FrameworkPlan)
WRP_IMPLEMENT_W_CSTR_ATTR (FrameworkPlan,FrameworkType1, FrameworkPlanLabel)
// ...
The Macro WRP_IMPLEMENT_W_CSTR_ATTR is defined like this:
#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; \
}
// ...
We have a bunch of even more complicated stuff, but I think you get the idea.
The problem with the API is that it is extremely complicated, not readable, not debuggable and not testable.
We would like to come up with a better mechanism for achieving the same goal. The idea was that we use some of the advanced features that came with new compiler like advanced templates, typelists, traits etc.
With templates we can almost achieve our goal, but we are stuck with the method names. We can generalize for types, but how do we deal with attribute names?
We also thought about creating a tool for automatically generating the wrapper + interfaces + mocks code. However, the API of our external framework is extremely complicated and writing such a tool would be very costly.
What do you think is the best way to solve such a problem? Maybe you already dealt with something like that and can provide good hints? We're looking forward to see your answers!