New place for this article is
here
RU
In the process of software development it is often necessary to solve the following problem: provide a transparent mechanism for binding of interacting interfaces from various binary modules.
Let's consider the target structure "from a side".
Diagram can be divided into two levels: source codes level and binary representation level.
Architecture level (source codes) contains four components, which are abstractions of the key entities («environment», «application», «editor», «engine»). The interaction between them is done through definite interfaces («ienv», «iappl», «ieditor», «iengine»).
For binary representation it is necessary to provide three configurations:
• «monolith assembly» — executable application that is used for viewing and editing data by implementing WYSIWYG
• «browser plug-in assembly»- dynamically loadable module for embedding into browsers
• «utility assembly» — utility for testing of a browser plug-in that duplicates the modular representation of a system browser-plug-in
Each level has its own key requirements:
• At the architectural level we want to see "continuous field" of interacting entities. Orthogonality of interfaces, code reuse, etc. are important here.
• At the level of binary modules the minimization of the need to recompile / build, ease of deployment, versioning and correct updates on the user side, etc come to the foreground.
The conflict of interests is obvious, on the one hand we want to get a jointless interaction between entities, on the other hand- we want to be able to assemble them in any way in the binary representation.
Each level has its own requirements and limitations. To reduce the complexity of the general task let us divide it into two parts, defining a clear interaction contract between them. On the one hand , such contract limits the combinatorial field of system types, but on the other hand it allows to minimize the "influence"of one system on another one.
• executable module
• DLLs, "linked" at the assembly time
• delay-loaded DLLs
// iappl.hpp
#define EXPORT __declspec( dllexport )
//
//interface definition
class iappl
{
public:
virtual bool init() = 0;
virtual ~iappl(){}
};
// creator function name
static char const * const create_appl_name = "create_appl";
// creator function type
typedef iappl*( *create_appl_t )( int );
extern "C"
{
// creator function declaration
EXPORT iappl* create_appl( int );
}
Second part is interface implementation, that consists of interface implementation and creator function definition. This implementation can be placed anywhere in the executable module or dll modules. z3d::util:fmethod will uniform interaction with interface.
// appl.hpp
//
// interface implementation
class appl : public iappl
{
virtual bool init() { return true; }
};
// appl.cpp
//
extern "C"
{
// creator function definition
EXPORT iappl* create_appl( int ) { return ( new appl ); }
}
#ifdef USE_DELAYED_LOADED_DLL
//dll loading/unloading
z3d::util::fmethod creator( "core" );
#elif USE_LINKED_DLL
// use of loaded dll
z3d::util::fmethod creator( "core", 0 );
#else
// use of export sections of exe module
z3d::util::fmethod creator;
#endif
// receive interface
boost::scoped_ptr a( creator.lookup( create_appl_name )( 0 ) );
a->init();
SYSTEM_INFO si;
z3d::util::fmethod kernel32( "kernel32.dll", 0 );
kernel32.lookup< void (WINAPI *)(LPSYSTEM_INFO) >( "GetNativeSystemInfo" )( &si );
kernel32.lookup< void (WINAPI *)(DWORD, DWORD) >( "Beep" )( 750, 300 );
If you don't have or don't need define function type, you could generate it "on the fly" using
Boost.FunctionType.
namespace bft = boost::function_types;
kernel32.call< bft::tag< bft::non_const, bft::stdcall_cc >, void >( "GetNativeSystemInfo", &si );
kernel32.call< bft::tag< bft::non_const, bft::stdcall_cc >, void >( "Beep", static_cast< dword >( 750 ), static_cast< dword >( 300 ) );
RU: