IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Gérer ses données en C++


précédentsommairesuivant

VI. La dynamisation

Ma première DLL :)

Le gros problème pour le développement côté utilisateur lorsqu'il utilise ce genre de bibliothèque, est qu'en restant statique, ce sera à lui de compiler les classes Template. Nous en possédons un paquet, par conséquent son temps de compilation rendra la production... improductive. En proposant notre gestion via une DLL, notre code sera déjà compilé dedans, mais cela nous pose deux problèmes :

  • Comment ne proposer que des objets "simples", sans Template, dans l'interface de la DLL ?
  • Comment compiler les fonctions Template, de manipulation ?

Pour répondre à la première question, nous avons deux solutions. Soit proposer une interface de donnée, d'où dérivera la donnée concrète, produisant ce genre de code :

 
Sélectionnez
class CBookImpl;
class CAuthor;
class CBook
{
public:
	typedef CBookImpl TImpl;

	virtual unsigned _int64 GetIdent() const = 0;

	virtual const std::string& GetTitle() const = 0;
	virtual void SetTitle(const std::string& rstrTitle) = 0;

	virtual const std::string& GetDescription() const = 0;
	virtual void SetDescription(const std::string& rstrDescription) = 0;

	virtual unsigned int GetYear() const = 0;
	virtual void SetYear(unsigned int uiYear) = 0;

	virtual CAuthor* GetAuthor() = 0;
	virtual void SetAuthor(CAuthor* pAuthor) = 0;
};

class CAuthorImpl;
class CBookImpl : public CBook, public CData<CBookImpl>
{
MAKE_DATA(CBookImpl)

public:
	CBookImpl();
	virtual unsigned _int64 GetIdent() const;

	virtual const std::string& GetTitle() const;
	virtual void SetTitle(const std::string& rstrTitle);

	virtual const std::string& GetDescription() const;
	virtual void SetDescription(const std::string& rstrDescription);

	virtual unsigned int GetYear() const;
	virtual void SetYear(unsigned int uiYear);

	virtual CAuthor* GetAuthor();
	virtual void SetAuthor(CAuthor* pAuthor);

private:
	TUInt64Property m_ulIdent;
	TStringProperty m_strTitle;
	TStringProperty m_strDescription;
	TUInt32Property m_uiYear;
	TUInt64Property m_ulIdentAuthor;

	CSmartDataPtr<CBookImpl, CAuthorImpl> m_pAuthor;
};

const char* SDataTrait<CBookImpl>::CODE = "BOOK";

Soit utiliser le pImpl Idiom. Cette technique définit une implémentation (notre CBook devient CBookImpl) et la donnée encapsule cette implémentation (CBook contient un pointeur vers CBookImpl). Dans le fichier proposé à l'utilisateur (CBook.h), une simple pré déclaration de CBookImpl suffit. Ainsi, le temps de compilation du projet utilisateur s'en trouve réduit.
Nous allons utiliser la première technique, plus adaptée.

Pour répondre à la seconde question, la problématique repose sur "Comment compiler les fonctions GesMgr<MaClass>::XXX(...) ? L'utilisateur ne peut pas connaître GesMgr directement, cela lui imposerait de compiler la hiérarchie éparpillée et donc tous les gestionnaires. Donc nous proposerons des fonctions, toujours Template, et compilerons leur implémentations qui appellerons les fonctions de GesMgr. Voici un exemple :

 
Sélectionnez
#define KIN_EXPORT __declspec(dllexport) /*!< Définition de l'exportation. */
#define KIN_IMPORT __declspec(dllimport) /*!< Définition de l'importation. */

template<class T> KIN_EXPORT unsigned long kinGetCount(); // Fonction exposé dans un fichier d'entête (.h exposé)

template<> KIN_EXPORT Class* kinGetAt<CBook>(unsigned long ulIndex) { \ // Implémentation, caché dans un fichier d'implémentation (.cpp) de la DLL
	return CGesManager::GetInstance().GetAt<CBook::TImpl>(ulIndex); }

Ainsi, l'utilisateur ne manipule qu'une interface, son implémentation est cachée et il n'a pas à la compiler. Puis il appelle GesMgr par une fonction dont seul l'entête est disponible.
Ca paraît fastidieux de devoir déclarer toutes les fonctions qu'offre GesMgr, pour chaque type de donnée. Nous passerons alors par une macro :

 
Sélectionnez
// Des entêtes
	template<class T> KIN_EXPORT unsigned long kinGetCount();
	template<class T> KIN_EXPORT T* kinGetAt(unsigned long ulIndex);
	template<class T> KIN_EXPORT IProperty* kinGetProperty(T* poData, const std::string& rstrPropertyName);
	template<class T> KIN_EXPORT unsigned int kinGetPropertyCount();

// Puis leur implémentation
#define KIN_IMPL_CLASS(Class) \
	template<> KIN_EXPORT Class* kinGetAt<Class>(unsigned long ulIndex) { \
		return CGesManager::GetInstance().GetAt<Class::TImpl>(ulIndex); } \
	template<> KIN_EXPORT unsigned long kinGetCount<Class>() { \
		return CGesManager::GetInstance().GetCount<Class::TImpl>(); } \
	template<> KIN_EXPORT IProperty* kinGetProperty<Class>(Class* poData, const std::string& rstrPropertyName) { \
		return CGesManager::GetInstance().GetProperty<Class::TImpl>((Class::TImpl*)poData, rstrPropertyName); } \
	template<> KIN_EXPORT unsigned int kinGetPropertyCount<Class>() { \
		return CGesManager::GetInstance().GetPropertyCount<Class::TImpl>(); }

Il n'y a plus qu'à appeler la macro sur un type de donnée pour compiler les implémentation des fonctions ainsi que leurs dépendances.

 
Sélectionnez
// Dans un .cpp de la DLL
	KIN_DLL_IMPL_CLASS(CBook)
	KIN_DLL_IMPL_CLASS(CAuthor)

Ainsi, pour chaque type, le travail est minimisé, et le programme utilisateur compile rapidement.

Dans le code source disponible, j'ai exposé la plupart des méthodes du GesMgr, les relations, ainsi que des méthodes non Template comme le positionnement du fichier de configuration IO par exemple.


précédentsommairesuivant

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2009 Aurélien FILEZ Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.