2008-09-28 4 views
4

En suivant les techniques de 'Modern C++ Design', j'implémente une bibliothèque de persistance avec diverses optimisations à la compilation. Je voudrais que la possibilité d'envoyer une fonction à une variable de membre si cette variable basé sur un modèle dérive d'une classe donnée:Envoi basé sur le type à la compilation

template<class T, template <class> class Manager = DefaultManager> class Data 
{ 
private: 
    T *data_; 

public: 
    void Dispatch() 
    { 
     if(SUPERSUBCLASS(Container, T)) 
     { 
     data_->IKnowThisIsHere(); 
     } 
     else 
     { 
     Manager<T>::SomeGenericFunction(data_); 
     } 
    } 
} 

Où SUPERSUBCLASS est une macro de compilation pour déterminer l'héritage de l'objet. Bien sûr, cela échoue dans tous les cas où T hérite de Container (ou T est un type intrinsèque etc etc) car le compilateur se plaint à juste titre que IKnowThisIsHere() n'est pas un membre de données, même si ce chemin de code ne sera jamais suivi. comme indiqué ci-après avec prétraiter T = int:

private: 
    int *data_; 

public: 
    void Dispatch() 
    { 
     if(false) 
     { 
     data_->IKnowThisIsHere(); 

compilateur se plaint clairement à ce code, même si elle ne sera jamais exécuté. Une suggestion d'utiliser un dynamic_cast ne fonctionne pas non plus, comme encore une conversion de type est tentée au moment de la compilation qui n'est pas possible (par exemple avec T = double, std :: string):

void Dispatch() 
    { 
     if(false) 
     { 
     dynamic_cast<Container*>(data_)->IKnowThisIsHere(); 

error: cannot dynamic_cast '((const Data<double, DefaultManager>*)this)->Data<double, DefaultManager>::data_' (of type 'double* const') to type 'class Container*' (source is not a pointer to class) 
error: cannot dynamic_cast '((const Data<std::string, DefaultManager>*)this)->Da<sttad::string, DefaultManager>::data_' (of type 'struct std::string* const') to type 'class Container*' (source type is not polymorphic) 

Je dois vraiment émuler (ou même persuader!) ayant le compilateur émettant un ensemble de code si T hérite de Container, et un autre si ce n'est pas le cas.

Des suggestions?

Répondre

3

Surcharge peut être utile de mettre en œuvre dis de compilation patcher, tel que proposé par Alexandrescu dans son livre "Modern C++ Design".

Vous pouvez utiliser une classe comme celui-ci pour transformer au moment de la compilation d'un booléen ou entier dans un type:

template <bool n> 
struct int2type 
{ enum { value = n}; }; 

Le code source ci-dessous montre une application possible:

#include <iostream> 

#define MACRO() true // <- macro used to dispatch 

template <bool n> 
struct int2type 
{ enum { value = n }; }; 

void method(int2type<false>) 
{ std::cout << __PRETTY_FUNCTION__ << std::endl; } 

void method(int2type<true>) 
{ std::cout << __PRETTY_FUNCTION__ << std::endl; } 

int 
main(int argc, char *argv[]) 
{ 
    // MACRO() determines which function to call 
    // 

    method(int2type<MACRO()>()); 

    return 0; 
} 

Bien sûr, ce fait vraiment le travail est le MACRO() ou une meilleure mise en œuvre comme une métafonction

+0

Merci muchly - c'est exactement ce que je recherchais. – user23167

1

traits Boost a quelque chose pour que: is_base_of

0

Regardez dans le modèle de boost bibliothèque de programmation méta. En outre, en fonction de ce que vous essayez d'accomplir, regardez la bibliothèque de sérialisation boost, car elle peut déjà avoir ce dont vous avez besoin.

0

Je suis intéressé à faire cela 'par les premiers principes' comme une curiosité éducative. Cependant, je vais regarder les bibliothèques Boost.

Dans tous les cas, je ne pense pas is_base_of est une aide - il fait exactement la même chose que la macro SUPERSUBCLASS ...

0

Malheureusement, je suis passé par cela aussi (et il est, aussi, un appel d'exécution;)) Le compilateur se plaint si vous passez non types polymorphes ou de classe, d'une manière similaire à avant:

error: cannot dynamic_cast '((const Data<double, DefaultManager>*)this)->Data<double, RawManager>::data_' (of type 'double* const') to type 'class Container*' (source is not a pointer to class) 

ou

error: cannot dynamic_cast '((const Data<std::string, DefaultRawManager>*)this)->Data<std::string, DefaultManager>::data_' (of type 'struct std::string* const') to type 'class Container*' (source type is not polymorphic) 
2

Vous avez besoin d'une sorte de compilation if. Cela appelle alors une fonction selon le cas true.De cette façon, le compilateur ne tombera pas sur du code qu'il ne peut pas compiler (car il est stocké en toute sécurité dans un autre modèle de fonction qui n'est jamais instancié). Il existe plusieurs façons de réaliser un tel temps de compilation if. Le plus commun est d'utiliser l'idiome SFINAE: substitution failure is not an error. Le is_base_of de Boost est en fait une instance de cet idiome. Pour l'utiliser correctement, vous ne l'écrivez pas dans une expression if mais l'utilisez plutôt comme le type de retour de votre fonction.

code non testé:

void Dispatch() 
{ 
    myfunc(data_); 
} 

private: 

// EDIT: disabled the default case where the specialisation matched 
template <typename U> 
typename enable_if_c<is_base_of<Container, U>::value, U>::type myfunc(U& data_) { 
    data_->IKnowThisIsHere(); 
} 

template <typename U> 
typename disable_if_c<is_base_of<Container, U>::value, U>::type myfunc(U& data_) { // default case 
    Manager<U>::SomeGenericFunction(data_); 
} 
+0

Cette méthode entraînera une surcharge ambiguë. Vous devriez laisser le compilateur supprimer la seconde méthode (le cas par défaut) quand is_base_of <> metafunction réussit. –

+0

Je dans le code de laisser tomber le cas par défaut lorsque le premier correspondait. Je ne sais pas si c'est juste –

Questions connexes