2010-12-06 54 views
0

Je voudrais construire une API qui fournit une interface de classe à la fonctionnalité (fichier d'en-tête) et cacher l'implémentation pour toutes les bonnes raisons standard. Je prévois d'utiliser une fabrique d'objets pour renvoyer des pointeurs d'objet dérivés d'objets "nouveaux" qui se conforment à l'interface.C++ Construire une API en utilisant des classes d'interfaces, des templates et des fabriques d'objets

Ma classe d'API de base varie en fonction des vecteurs std :: de types numériques intégrés (char, uchar, short, ushort, int, uint, float et double). Donc, un modèle semble être un ajustement naturel. Je vais mettre un modèle de classe d'interface à la disposition des utilisateurs de mon API et j'en dériverai dans le modèle de classe d'implémentation qui sera caché. Comme mon modèle de classe visible par l'utilisateur est une classe d'interface, je voudrais déclarer toutes les méthodes purement virtuelles, mais je comprends qu'il peut y avoir des problèmes avec les instanciations/exportations de modèles requis dans les DLL, les objets partagés, etc. Je vais juste les définir virtuellement et leur donner des corps de méthodes vides dans la classe d'interface de base. Quelque part le long de la ligne, j'ai besoin de créer une méthode d'usine statique de modèle (ou une fonction de modèle) qui va créer les objets des classes dérivées et les renvoyer à l'appelant. Le problème est que je ne peux pas mettre l'implémentation de la méthode d'usine d'objet statique dans le fichier d'en-tête de l'interface, car elle doit créer des objets des classes dérivées qui doivent être cachées. Donc, je voudrais mettre ces usines d'objets statiques dans l'en-tête de l'implémentation ou dans un fichier source.

Voici un en-tête de la mise en œuvre conceptuelle

#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 

Voici une classe de mise en œuvre dérivée conceptuelle

#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 someMethod(T aParam) { /*...*/ } 
private: 
std::vector<T> _dataVector; 

};

Note: En réalité, je vais utiliser TR1 :: shared_ptr au lieu de pointeurs bruts.

Mes questions sont les suivantes:

1) Où puis-je définir la méthode statique "factoryMethod()" (ou de implementation.h implementation.cpp)?

2) À quoi ressemble cette implémentation de méthode?

3) Y a-t-il d'autres problèmes que je dois connaître pour que mes utilisateurs API ou moi-même n'obtenions pas de temps de liaison ou d'erreurs d'exécution?

merci d'avance!

+0

Je l'ai eu pour fonctionner sous Visual Studio 2008 – intensifi

Répondre

0

Vous avez un problème avec ceci:

1) Les modèles ne peuvent pas être exposés par DLLs comme celui-ci comme ils sont « remplis » au moment de la compilation. Autrement dit, si vous avez DerivedClass<int>, le compilateur remplit réellement "int" partout où vous avez "T" pour définir l'objet. En essayant de l'exposer via une DLL, le code client (l'application qui tentera de lier avec votre objet) pourrait essayer de créer un objet DerivedClass<int> et vous obtiendrez une erreur de l'éditeur de liens parce qu'elle n'existe pas. 2) Bien que la norme C++ permette techniquement de scinder les classes modèles en fichiers d'implémentation et en en-tête, aucun compilateur n'a pris en charge cette fonctionnalité (car c'est une peine royale à tenter de mettre en œuvre). Cela inclut le compilateur C++ de Microsoft.Il y a deux façons de contourner ceci: écrire la classe inline dans le fichier header (ce que font la plupart des implémentations STL), ou #include le fichier d'implémentation en bas du fichier header (qui fait essentiellement la même chose, mais n'est pas vraiment pratique standard).

+1

Un compilateur, Comeau C++, a pris en charge la division des classes de modèles. L'option sera presque certainement retirée de la norme, et Comeau est en faveur de le faire. –

0

Je l'ai à travailler sous Visual Studio 2008.

Voici les détails:

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; 
} 

Cela peut être non-standard, mais il est très utile.