Question

Suivant les techniques de 'Modern C ++ Design', j'implémente une bibliothèque de persistance avec diverses optimisations à la compilation. J'aimerais pouvoir envoyer une fonction à une variable membre basée sur un modèle si cette variable dérive d'une classe donnée:

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 au moment de la compilation pour déterminer l'héritage des objets. Bien sûr, cela échoue dans tous les cas où T hérite de Container (ou T est un type intrinsèque, 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é ici après le prétraitement avec T = int:

private:
   int *data_;

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

Le compilateur se plaint clairement de ce code, même s'il ne sera jamais exécuté. Une suggestion d'utilisation de dynamic_cast ne fonctionne pas non plus, car une nouvelle conversion de type est tentée au moment de la compilation, ce 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)

J'ai vraiment besoin d'émuler (ou même de persuader!) que le compilateur émette un ensemble de code si T hérite de Container et un autre s'il ne le fait pas.

Des suggestions?

Était-ce utile?

La solution

La surcharge peut être utile pour implémenter la répartition au moment de la compilation, comme proposé par Alexandrescu dans son livre "Modern C ++ Design".

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

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

Le code source suivant 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 qui fait vraiment le travail, c’est la MACRO () ou une meilleure implémentation en tant que métafonction

Autres conseils

Vous avez besoin d'un type de si au moment de la compilation. Ceci appelle ensuite une fonction en fonction de la casse 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 if au moment de la compilation. Le plus courant consiste à utiliser l'idiome SFINAE: l'échec de la substitution n'est pas une erreur . Le is_base_of de Boost est en réalité une instance de cet idiome. Pour l'utiliser correctement, ne l'écrivez pas dans une expression if , mais utilisez-le plutôt comme 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_);
}

Boost traits a quelque chose pour cela: is_base_of

Regardez dans la bibliothèque de méta-programmation du modèle de renforcement. En outre, en fonction de vos objectifs, consultez la bibliothèque de sérialisation boost, car elle contient peut-être déjà ce dont vous avez besoin.

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

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

Malheureusement, je suis également passé par là (et il s'agit également d'un appel d'exécution;)). Le compilateur se plaint si vous transmettez des types non polymorphes ou de classe, de la même manière qu'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)
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top