Domanda

Seguendo le tecniche di "Modern C ++ Design", sto implementando una libreria di persistenza con varie ottimizzazioni in fase di compilazione. Vorrei la possibilità di inviare una funzione a una variabile membro basata su modelli se tale variabile deriva da una determinata classe:

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

Dove SUPERSUBCLASS è una macro in fase di compilazione per determinare l'ereditarietà degli oggetti. Naturalmente, ciò non riesce in tutti i casi in cui T eredita dal contenitore (o T è un tipo intrinseco ecc. Ecc.) Poiché il compilatore si lamenta giustamente che IKnowThisIsHere () non è un membro di dati, anche se questo percorso di codice non verrà mai seguito, come mostrato qui dopo la preelaborazione con T = int:

private:
   int *data_;

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

Il compilatore si lamenta chiaramente di questo codice, anche se non verrà mai eseguito. Anche un suggerimento sull'uso di dynamic_cast non funziona, poiché in questo momento viene tentata una conversione del tipo che non è possibile (ad esempio con 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)

Ho davvero bisogno di emulare (o addirittura persuadere!) che il compilatore emetta un set di codice se T eredita dal contenitore e un altro se non lo fa.

Qualche suggerimento?

È stato utile?

Soluzione

Il sovraccarico può essere utile per implementare l'invio in fase di compilazione, come proposto da Alexandrescu nel suo libro "Modern C ++ Design".

Puoi usare una classe come questa per trasformare in fase di compilazione un valore booleano o intero in un tipo:

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

Il seguente codice sorgente mostra una possibile applicazione:

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

Naturalmente ciò che rende davvero il lavoro è il MACRO () o una migliore implementazione come metafunzione

Altri suggerimenti

È necessario un tipo di in fase di compilazione se . Questo quindi chiama una funzione a seconda del caso true . In questo modo, il compilatore non si imbatterà in codice che non può essere compilato (perché è archiviato in modo sicuro in un altro modello di funzione che non viene mai istanziato).

Esistono diversi modi per realizzare un in fase di compilazione se . Il più comune è utilizzare il linguaggio SFINAE: il fallimento della sostituzione non è un errore . Il is_base_of di Boost è in realtà un'istanza di questo idioma. Per utilizzarlo correttamente, non dovresti scriverlo in un'espressione if ma piuttosto utilizzarlo come tipo di ritorno della tua funzione.

Codice non testato:

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

I tratti boost hanno qualcosa per questo: is_base_of

Cerca nella libreria di meta-programmazione del modello boost. Inoltre, a seconda di ciò che stai cercando di ottenere, guarda la libreria di serializzazione boost, poiché potrebbe già avere ciò di cui hai bisogno.

Sono interessato a fare questo "dai primi principi" come una curiosità educativa. Tuttavia, esaminerò le librerie Boost.

In ogni caso, non credo che is_base_of sia di alcun aiuto - fa esattamente lo stesso della macro SUPERSUBCLASS ...

Sfortunatamente ho passato anche quello (ed è anche una chiamata di runtime;)) Il compilatore si lamenta se passi in tipi non polimorfici o di classe, in modo simile a prima:

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)

o

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)
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top