Domanda

Sto usando una libreria che consiste quasi interamente di classi e funzioni basate su modelli nei file di intestazione , in questo modo:

// foo.h
template<class T>
class Foo {
  Foo(){}
  void computeXYZ() { /* heavy code */ }
};
template<class T>
void processFoo(const Foo<T>& foo) { /* more heavy code */ }

Ora questo è negativo perché i tempi di compilazione sono insopportabili ogni volta che includo uno di quei file di intestazione (e in realtà ne includo molti in ciascuna delle mie unità di compilazione).

Dato che come parametro modello uso solo uno o due tipi, intendo creare, per ogni file di intestazione della libreria, un file che contiene solo dichiarazioni , senza il codice pesante, come questo:

// NEW: fwd-foo.h
template<class T>
class Foo {
  Foo();
  void computeXYZ();
};
template<class T>
void processFoo(const Foo<T>& foo);

E poi un file che crea tutte le istanze di cui avrò bisogno. Quel file può essere compilato separatamente una volta per tutte :

// NEW: foo.cpp
#include "foo.h"
template class Foo<int>;
template class Foo<double>;
template void processFoo(const Foo<int>& foo);
template void processFoo(const Foo<double>& foo);

Ora posso solo includere fwd-foo.h nel mio codice e avere brevi tempi di compilazione. Alla fine collegherò contro foo.o .

Il rovescio della medaglia, ovviamente, è che devo creare questi nuovi file fwd-foo.h e foo.cpp . E ovviamente è un problema di manutenzione: quando viene rilasciata una nuova versione della libreria, devo adattarle a quella nuova versione. Ci sono altri aspetti negativi?

E la mia domanda principale è:

È possibile creare questi nuovi file, in particolare fwd-foo.h , automaticamente dall'originale foo.h ? Devo farlo per molti file di intestazione della libreria (forse 20 o giù di lì), e una soluzione automatica sarebbe la migliore soprattutto nel caso in cui una nuova versione della libreria venga rilasciata e devo farlo di nuovo con la nuova versione. Sono disponibili strumenti per questa attività?

MODIFICA:

Domanda aggiuntiva: in che modo la parola chiave extern recentemente supportata può aiutarmi in questo caso?

È stato utile?

Soluzione

Utilizziamo lzz che divide un singolo file in un'intestazione e un'unità di traduzione separate. Per impostazione predefinita, normalmente inserisce anche le definizioni del modello nell'intestazione, tuttavia è possibile specificare che non si desidera che ciò accada.

Per mostrarti come potresti usarlo, considera quanto segue:

// t.cc
#include "b.h"
#include "c.h"

template <typename T> 
class A {
  void foo () {
    C c;
    c.foo ();
    b.foo ();
  }
  B b;
}

Prendi il file sopra e copialo nel file 't.lzz'. Inserisci le direttive #include in blocchi $ hdr e $ src separati, se necessario:

// t.lzz
$hdr
#include "b.h"
$end

$src
#include "c.h"
$end

template <typename T> 
class A {
  void foo () {
    C c;
    c.foo ();
    b.foo ();
  }
  B b;
}

Ora infine, esegui lzz sul file specificando che inserisce le definizioni del modello nel file di origine. Puoi farlo usando un $ pragma nel file sorgente, oppure puoi usare l'opzione della riga di comando " -ts " ;:

Ciò comporterà la generazione dei seguenti file:

// t.h
//

#ifndef LZZ_t_h
#define LZZ_t_h
#include "b.h"
#undef LZZ_INLINE
#ifdef LZZ_ENABLE_INLINE
#define LZZ_INLINE inline
#else
#define LZZ_INLINE       
#endif
template <typename T>
class A
{
  void foo ();
  B b;
};
#undef LZZ_INLINE
#endif

E

// t.cpp
//

#include "t.h"
#include "c.h"
#define LZZ_INLINE inline
template <typename T>
void A <T>::foo ()
          {
    C c;
    c.foo ();
    b.foo ();
  }
#undef LZZ_INLINE

È quindi possibile eseguirli tramite alcuni comandi grep / sed per rimuovere le macro dell'helper LZZ.

Altri suggerimenti

Prova a usare le intestazioni precompilate. So che GCC e MSVC supportano questa funzione. L'utilizzo è specifico del venditore, tuttavia.

Sto lavorando allo stesso problema da un po 'di tempo ormai. Nella soluzione che stai proponendo, stai definendo le tue classi di template due volte. Andrà bene se definisce le stesse cose (nello stesso ordine), ma prima o poi avrai problemi.

Quello che ho escogitato è di considerare il problema al contrario. Finché non stai specializzando la tua implementazione, funziona con grazia.

Utilizza due macro, che devono aggiornare gli argomenti del modello nel file di implementazione (attenzione, tuttavia, se si desidera aggiungere argomenti del modello predefiniti alla classe).

// foo.h
#define FOO_TEMPLATE template<typename T>
#define FOO_CLASS Foo<T>

FOO_TEMPLATE
class Foo {
  Foo();
  void computeXYZ();
};

// foo_impl.h
#include "foo.h"
FOO_TEMPLATE
FOO_CLASS::Foo(){}

FOO_TEMPLATE
void FOO_CLASS::computeXYZ() { /* heavy code */ }

In questo modo, essenzialmente lavori allo stesso modo delle classi non template (ovviamente puoi fare la stessa cosa con le funzioni template).

EDIT: sulla parola chiave extern in c ++ 0x

Credo che la parola chiave extern in c ++ 0x aiuterà, ma non risolverà tutto magicamente!

Da questo articolo ,

  

Modelli esterni

     

Ogni modulo che crea un'istanza a   il modello crea essenzialmente una copia di   nel codice oggetto. Quindi è tutto   al linker per disporre di tutto il file   codice oggetto ridondante all'ultimo   fase, rallentando così la critica   ciclo di modifica-compilazione-collegamento che compone   la giornata di un programmatore (o talvolta   sogni ad occhi aperti). Per cortocircuitare questo   garbage collection del codice oggetto, a   numero di venditori di compilatori   ha già implementato una parola chiave extern   che può andare davanti ai modelli.   Questo è un caso di standardizzazione   codificare le pratiche industriali esistenti   (gioco di parole previsto). In pratica, questo è   implementato inviando un avviso al   compilatore sostanzialmente per "non istanziare   questo qui " ;:

extern template class std::vector;

C ++ 0x risolverà i tuoi problemi di tempo di compilazione con modelli esterni. Tuttavia, non conosco un modo automatico per fare ciò che chiedi.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top