开发者

C++ Building an API using Interface classes, Templates and Object Factories

I'd like to build an API that provides a class interface to functionality (header file) and hide the implementation for all the standard good reasons. I plan on using an object factory to return object pointers of derived "newed" objects which conform to the interface.

My core API class varies based on std::vectors of built-in numeric types(char, uchar, short, ushort, int, uint, float and double). So a template seems like a natural fit. I'll make an interface class template available to the users of my API and derive from it in the implementation class template which will be hidden.

Since my user visible class template is an interface class I'd like to declare all the methods pure virtual but I understand there may problems with doing this given required template instantiations/exports in dlls, shared objects, etc. So I'll just define them virtual and give them empty method bodies in the base interface class. Somewhere along the line I need to create a template static factory method (or template function) that will create the objects of the derived classes and return pointers to them to the caller.

The issue is I cannot put the implementation of the static object factory method in the interface header file either as it has to create objects of the derived classes which are to be hidden. So I'd like to put these static object factories in the implementation header or a source file.

Here is a conceptual implementation header

#ifndef INTERFACE_H
#define INTERFACE_H

#ifdef DLL_EXPORTS
#define DLL_API __declspec(dllexport)
#else
#define DLL_API __declspec(dllimport)
#endif

// This interface class is exported from the dll.

template < typename T >
class DLL_API InterfaceClass {
public:
 static InterfaceClass* factoryMethod( );
 virtual ~InterfaceClass ( ) { }

 virtual void someMethod( T aParam ){ };

protected:
 InterfaceClass ( ) { }

private:
 InterfaceClass ( const InterfaceClass & );
 InterfaceClass& operator=( const InterfaceClass & );
};

#endif

Here is a conceptual derived implementation class

#ifndef IMPLEMENTATION_H
#define IMPLEMENTATION_H

#include <vector>
#include "interface.h"

template < typename T >
class DerivedClass : public InterfaceClass< T > {
public:
 DerivedClass( const T& aDataVector ) : InterfaceClass< T >( ) { /*...*/ }

 virtual ~DerivedClass( ) { /*...*/ }

 virtual void someMeth开发者_如何转开发od( T aParam ) { /*...*/ }
private:
 std::vector< T > _dataVector;

};

Note: In reality I'll be using TR1::shared_ptr instead of raw pointers.

My questions are:

1) Where do I define the static "factoryMethod( )" method (implementation.h or implementation.cpp)?

2) What does this method implementation look like?

3) Are there any other issues I need to be aware of so I or my API users don't get link time or run-time errors?

thanks in advance!


You will run into problems with this:

1) Templates cannot be exposed by DLLs like this as they are "filled in" at compile time. That is, if you have DerivedClass<int>, the compiler actually fills in "int" everywhere you have "T" to define the object. Trying to expose it via a DLL, the client code (the application that will try to link with your object) could try to create a DerivedClass<int> object and you will get a linker error because it doesn't exist.

2) While the C++ standard technically allows for template classes to be split into implementation and header files, no compiler has supported that feature (as it is a royal pain to attempt to implement). This includes Microsoft's C++ compiler. There are a couple ways around this: write the class inline in the header file (which is what most STL implementations do), or #include the implementation file at the bottom of the header file (which essentially does the same thing, but is not really standard practice).


I got it to work under Visual studio 2008.

Below are the details:

interface.h

#ifndef INTERFACE_H
#define INTERFACE_H

#if defined( WIN32 ) || defined ( WIN64 )

#ifdef DLL_EXPORTS
#define DECLSPECIFIER __declspec( dllexport )
#define EXPIMP_TEMPLATE 
#else // DLL_EXPORTS
#define DECLSPECIFIER __declspec( dllimport )
#define EXPIMP_TEMPLATE extern 
#endif // DLL_EXPORTS

#else // defined( WIN32 ) || defined ( WIN64 )
#define DECLSPECIFIER 
#define EXPIMP_TEMPLATE 
#endif // defined( WIN32 ) || defined ( WIN64 )

// This class is exported from the dll.

template < typename T >
class DECLSPECIFIER InterfaceClass {
public:
 static InterfaceClass* factoryMethod( );
 virtual ~InterfaceClass( ) { }

 virtual void someMethod( ) { }

protected:
 InterfaceClass( ) { }

private:
 InterfaceClass( const InterfaceClass& );
 InterfaceClass& operator=( const InterfaceClass& );
};

#if defined( WIN32 ) || defined ( WIN64 ) 
#pragma warning( push )
#pragma warning( disable: 4231 ) // "nonstandard extension used : 'extern'
                                 // ok per [link text][1]
#endif

#include <vector>

EXPIMP_TEMPLATE template class DECLSPECIFIER InterfaceClass < std::vector< char > >;
EXPIMP_TEMPLATE template class DECLSPECIFIER InterfaceClass < std::vector< unsigned char > >;
EXPIMP_TEMPLATE template class DECLSPECIFIER InterfaceClass < std::vector< short > >;
EXPIMP_TEMPLATE template class DECLSPECIFIER InterfaceClass < std::vector< unsigned short > >;
EXPIMP_TEMPLATE template class DECLSPECIFIER InterfaceClass < std::vector< int > >;
EXPIMP_TEMPLATE template class DECLSPECIFIER InterfaceClass < std::vector< unsigned int > >;
EXPIMP_TEMPLATE template class DECLSPECIFIER InterfaceClass < std::vector< float > >;
EXPIMP_TEMPLATE template class DECLSPECIFIER InterfaceClass < std::vector< double > >;

#if defined( WIN32 ) || defined ( WIN64 ) 
#pragma warning( pop )
#endif

#endif

derived.h

#ifndef DERIVED_H
#define DERIVED_H

#include <iostream>

#include "interface.h"

template < typename T >
class DerivedClass : public InterfaceClass< T > {
public:
 DerivedClass( ) { 
    std::cout << "constructing derived class" << std::endl;

 }

 virtual ~DerivedClass( ) {
    std::cout << "destructing derived class" << std::endl;
 }

 virtual void someMethod( ) {
    std::cout << "hello" << std::endl;
 }
private:
 T _data;
};

#endif

interface.cpp

#include "interface.h"
#include "derived.h"

template < typename T >
DECLSPECIFIER
InterfaceClass<T>* InterfaceClass< T >::factoryMethod( ) {
  return new DerivedClass< T >( );
}

client.cpp

#include <exception>

#include "interface.h"

typedef InterfaceClass < std::vector< int > > IntVectorType;

int main(int argc, char* argv[])
{
 IntVectorType* ptrTest = NULL;
 try {
    ptrTest = IntVectorType::factoryMethod( );
 } 
 catch ( std::bad_alloc& ) {
    return 1;
 }

 ptrTest->someMethod( );

 delete ptrTest;

 return 0;
}

This may be non-standard but it is very useful.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜