New content are available at http://tis-method.org/. All articles from this site will be moved to the new location.

воскресенье, 6 марта 2011 г.

Cross-modules Factory method


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.


Pattern
Detailed description of a pattern. In our case the implementation must hide not only the type of a definite instanced class, but also the "location" of a creator-function:
• executable module
• DLLs, "linked" at the assembly time
• delay-loaded DLLs


Recipe (howto)
First of all, define four major elements: interface, string name for the creator function, creator function type, creator function declaration. This quartet will be "visible" as contract for our interface:
// 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 ); }
}

Use
Example below shows how to use template class for three possible integration mechanisms.
#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();

Bonus
Additionally, we can use this solution for "wrapping" functions from dll-modules.
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 ) );



Source code
z3d::util::fmethod

Resources
EN:

RU: