2012-01-03 12 views
9

Sto cercando di utilizzare la meta-programmazione dei modelli per determinare la classe base. C'è un modo per ottenere automaticamente la classe base senza specializzarsi esplicitamente per ogni classe derivata?Il tipo di classe base può essere ottenuto automaticamente da un tipo di modello?

class foo { public: char * Name() { return "foo"; }; }; 
class bar : public foo { public: char * Name() { return "bar"; }; }; 

template< typename T > struct ClassInfo { typedef T Base; }; 
template<> struct ClassInfo<bar> { typedef foo Base; }; 

int main() 
{ 
    ClassInfo<foo>::Base A; 
    ClassInfo<bar>::Base B; 

    std::cout << A.Name(); //foo 
    std::cout << B.Name(); //foo 
} 

per ora qualsiasi metodo automatico dovrebbe selezionare la prima base dichiarata e fallirebbe per le basi private.

+1

Utilizzare ['std :: is_base_of '] (http://stackoverflow.com/questions/2910979/how-is-base-of-works) – iammilind

+3

@iammilind: Questo è solo per testare se una classe è la base di un altro, devi conoscere la classe base per testare già contro. – Xeo

+4

Per cosa ti serve? Non penso sia possibile, ma forse esiste un approccio diverso per risolvere il problema reale. –

risposta

5

Le mie soluzioni non sono veramente automatiche, ma le migliori che riesco a pensare.

intrusiva C++ soluzione 03:

class B {}; 

class A : public B 
{ 
public: 
    typedef B Base; 
}; 

non intrusiva soluzione C++ 03:

class B {}; 

class A : public B {}; 

template<class T> 
struct TypeInfo; 

template<> 
struct TypeInfo<A> 
{ 
    typedef B Base; 
}; 
+0

che è praticamente esattamente quello che mi è venuto in mente. Speravo che ci fosse qualche trucco modello hardcore di cui non ero a conoscenza che potesse estrapolarlo ... Probabilmente userò il modulo intrusivo per le classi con controllo completo della creazione e il modulo non intrusivo per le classi semi-estranee - Grazie – VonRiphenburger

5

Non sono a conoscenza di alcun modello di selezione di classe base e non sono sicuro che esista o sia addirittura una buona idea. Ci sono molti modi in cui questo rompe l'estensibilità e va contro lo spirito dell'eredità. Quando bar eredita pubblicamente foo, barè unfoo per tutti gli scopi pratici, e il codice cliente non dovrebbe avere bisogno di distinguere classe di base e classe derivata.

Un typedef pubblico nella classe base graffia spesso i pruriti si potrebbe aver bisogno di essere graffiato ed è più chiaro:

class foo { public: typedef foo name_making_type; ... }; 

int main() { 
    Foo::name_making_type a; 
    Bar::name_making_type b; 
} 
+0

Buono. Stavo per scrivere questo. – iammilind

+0

È per un sistema di riflessione C++ molto leggero. Il codice "client" esegue la serializzazione di classe generale e la messaggistica con la minima deformazione possibile e/o aggiunte alla classe originale. – VonRiphenburger

15

E 'possibile con C++ 11 e decltype. Per questo, sfrutteremo il fatto che un puntatore-membro non è un puntatore nella classe derivata quando il membro è ereditato da una classe base.

Ad esempio:

struct base{ 
    void f(){} 
}; 
struct derived : base{}; 

Il tipo di &derived::f sarà void (base::*)(), non void (derived::*)(). Questo era già vero in C++ 03, ma era impossibile ottenere il tipo di classe base senza specificarlo. Con decltype, è facile e necessita solo di questa piccola funzione:

// unimplemented to make sure it's only used 
// in unevaluated contexts (sizeof, decltype, alignof) 
template<class T, class U> 
T base_of(U T::*); 

Usage:

#include <iostream> 

// unimplemented to make sure it's only used 
// in unevaluated contexts (sizeof, decltype, alignof) 
template<class T, class R> 
T base_of(R T::*); 

struct base{ 
    void f(){} 
    void name(){ std::cout << "base::name()\n"; } 
}; 
struct derived : base{ 
    void name(){ std::cout << "derived::name()\n"; } 
}; 

struct not_deducible : base{ 
    void f(){} 
    void name(){ std::cout << "not_deducible::name()\n"; } 
}; 

int main(){ 
    decltype(base_of(&derived::f)) a; 
    decltype(base_of(&base::f)) b; 
    decltype(base_of(&not_deducible::f)) c; 
    a.name(); 
    b.name(); 
    c.name(); 
} 

uscita:

base::name() 
base::name() 
not_deducible::name() 

Come ultimo esempio mostra, è necessario utilizzare un membro che è in realtà un membro ereditato della classe di base che ti interessa.

Il re sono più difetti, però: Il membro deve anche essere inequivocabilmente identificare un membro della classe di base:

struct base2{ void f(){} }; 

struct not_deducible2 : base, base2{}; 

int main(){ 
    decltype(base_of(&not_deducible2::f)) x; // error: 'f' is ambiguous 
} 

Questo è il migliore che si può ottenere, però, senza il supporto del compilatore.

+0

Grazie Xeo, questa è una soluzione parziale molto interessante e molto simile a quello che sto cercando. Lo esaminerò ulteriormente – VonRiphenburger

2

Che cos'è con la classe base? Sei un programmatore .NET o Java?

C++ supporta l'ereditarietà multipla e inoltre non ha una classe di base comune globale. Quindi un tipo C++ può avere zero, uno o più classi di base. L'uso dell'articolo definito è pertanto controindicato.

Dal la classe base non ha senso, non c'è modo di trovarlo.

+0

Ci sono forti motivi di efficienza Java consente solo una classe base e io tendo a ristrutturare il mio codice C++ per gli stessi motivi - quindi ha senso come sottoinsieme utile – VonRiphenburger

+0

@VonRiphenburger: Quindi la domanda deve essere dichiarata : "Per una classe con esattamente una classe base immediata, ereditata pubblicamente e non virtualmente, è possibile scoprire il tipo di classe base?" E non è ancora chiaro se stai cercando la classe base immediata o un altro antenato. E Java che consente solo una classe base non ha nulla a che fare con l'efficienza, e molto a che fare con la progettazione linguistica voluta per forzare i programmatori verso il basso "l'unico vero percorso verso OOP". Idem per mancanza di funzioni libere. –

+0

HO DICHIARATO per la PRIMA base dichiarata. Sembrava ovvio che si cercasse una base immediata, non un antenato casuale, ma quel punto avrebbe potuto essere affermato più chiaramente.Ehm, grazie per il tuo contributo, ben. – VonRiphenburger

0

Cerco di risoluzione portatile per problemi simili per mesi. Ma non lo trovo ancora.

G ++ ha __bases e __direct_bases. Puoi racchiuderli in un elenco di tipi e quindi accedere a uno qualsiasi dei suoi elementi, ad es. a std::tuple con std::tuple_element. Vedi libstdc++'s <tr2/type_traits> per l'uso.

Tuttavia, questo non è portatile. Clang++ currently has no such intrinsics.

1

Con C++ 11, è possibile creare un metodo invasivo per avere sempre un membro base_t, quando la classe eredita da un solo genitore:

template<class base_type> 
struct labeled_base : public base_type 
{ 
    using base_t = base_type; // The original parent type 
    using base::base; // Inherit constructors 

protected: 
    using base = labeled_base; // The real parent type 
}; 

struct A { virtual void f() {} }; 

struct my_class : labeled_base<A> 
{ 
    my_class() : parent_t(required_params) {} 

    void f() override 
    { 
     // do_something_prefix(); 
     base_t::f(); 
     // do_something_postfix(); 
    } 
}; 

Con questa classe, si avrà sempre un parent_t alias, per chiamare i costruttori parent come se fossero i costruttori base con un nome (probabilmente) più breve e un alias base_t, per rendere la classe non consapevole del nome del tipo di classe base se è lunga o fortemente basata su modelli.

L'alias parent_t è protetto per non esporlo al pubblico. Se non si desidera che l'alias base_t sia pubblico, è sempre possibile ereditare labeled_base come protected o private, non è necessario modificare la definizione della classe labeled_base.

Tale base deve avere 0 sovraccarico di runtime o spazio poiché i suoi metodi sono in linea, non fanno nulla e non hanno attributi propri.

Problemi correlati