2015-09-07 6 views
7

Ziemlich sicher, ich kenne die Antwort bereits, aber es ist einen Versuch wert.Kann ich eine Vorlagenspezialisierung aus einer Typliste deklarieren?

Also, sagen, ich habe eine Typliste:

template <typename ...Ts> 
struct typelist{}; 

, dass einige Objekte enthält:

struct foo{}; 
struct bar{}; 
struct quux{}; 

using objects = typelist<foo, bar, quux>; 

Jetzt habe ich eine Templat-Klasse (baz), die eines dieser Objekte nehmen. Aber aufgrund der Codebasisgröße und der Kompilierungszeiten möchte ich die Implementierung meiner Vorlagenmethode in einer cpp-Datei haben.

So am unteren Rand des baz.cpp ich habe:

template <> class baz<foo>; 
template <> class baz<bar>; 
template <> class baz<quux>; 

Das Problem ist, ich viele Klassen wie baz haben, und die Liste der Objekte, die sie auch ständig wechselnden mit sich arbeiten. Also ... kann ich meine einzelne Typliste von Objekten trotzdem behalten und diese in der cpp-Datei von jedem baz -ähnlichen Objekt zur Spezialisierung verwenden? Dann muss ich nur meine Typliste aktualisieren, wenn ich ein neues Objekt habe und alle Objektdateien neu erstellt werden.

+0

Sie müssen * nicht spezialisieren *, nur explizit * instanziieren * sie. – Jarod42

+0

Dies könnte definitiv mit einem Makro getan werden. Angesichts der Tatsache, dass dies vom Rest Ihrer Codebasis isoliert sein wird, warum tun Sie das nicht einfach? –

Antwort

9

Die template <> class baz<foo>; Zeile forward-deklariert eine Spezialisierung und keine Template-Instanziierung, die, wie ich annehme, das ist, was Sie wollen.

Ich glaube nicht, dass es einen direkten Weg gibt, dies zu tun, Sie müssen etwas Metaprogrammierung machen. Sie können Boost.Preprocessor verwenden alle benötigten Code zu generieren:

#define TYPES (foo)(bar)(quux) 

using objects = typelist< BOOST_PP_SEQ_ENUM(TYPES) >; 

// Generate extern template declarations in the header 
#define EXTERN_TEMPLATE_BAZ(r, data, arg)\ 
    extern template class baz<arg>; 

BOOST_PP_SEQ_FOR_EACH(EXTERN_TEMPLATE_BAZ, _, TYPES) 

// Generate template instantiations in the .cpp 
#define TEMPLATE_BAZ(r, data, arg)\ 
    template class baz<arg>; 

BOOST_PP_SEQ_FOR_EACH(TEMPLATE_BAZ, _, TYPES) 

es eine Möglichkeit sein, kann dies ohne Präprozessor zu tun, aber dies zu tun zusätzlichen Anforderungen an die baz Art auferlegen würde. Der Punkt besteht darin, den Typ in einem Kontext zu verwenden, in dem er instanziiert werden muss, einschließlich aller seiner Methoden.

+0

Nicht jeder nutzt Boost und es gibt keinen Hinweis auf Boost in der Frage. –

+2

Also? Es ist dennoch eine gültige Lösung. –

+0

Diese Lösung funktioniert, aber vielleicht ist es nicht das, was der Fragesteller wollte. Boost ist kein Teil von C++. –

3

Ich bin mir ziemlich sicher, dass dies ohne den Präprozessor unmöglich ist. Möglicherweise können Sie das Vorlagenargumentpaket aus einem Argument rekonstruieren, aber Sie müssen eine Instanz des Arguments übergeben, die nicht optimal ist. Zweitens sind explizite Schabloneninstanziierungen im Blockbereich (d. H. In einer Schablonenfunktion) nicht erlaubt, so dass es keine Möglichkeit gibt, eine Schablone zu schreiben, die eine andere Schablone explizit instantiiert.

Wie Nir angibt, warum verwenden Sie nicht einfach eine X Macro?

#define MY_FOREACH_TYPES(func, ...) \ 
    func(type1, ##_VA_ARGS__) \ 
    func(type2, ##_VA_ARGS__) \ 

#define MY_INSTANTIATE(Type, Class) \ 
    template <> class Class<Type>; 

MY_FOREACH_TYPES(MY_INSTANTIATE, bar) 

Jetzt aktualisieren Sie MY_FOREACH_TYPES nur, wenn sich Ihre Typenliste ändert.

+0

Vielen Dank für das Erwähnen von X-Macro. Der Wiki Link ist der Beste! Weißt du, was Leute davon abhalten, X Macro zu benutzen? (außer es ist schwieriger zu verfolgen/zu debuggen) – javaLover

2

Eine Version mit einem gewöhnlichen Präprozessor

//Header file 

#define BAZ_OBJS \ 
    BAZ_BEGIN foo \ 
    BAZ_AND  bar \ 
    BAZ_AND  quux \ 
    BAZ_END 

#define BAZ_BEGIN 
#define BAZ_AND , 
#define BAZ_END 
using objects = typelist<BAZ_OBJS>; 
#undef BAZ_BEGIN 
#undef BAZ_AND 
#undef BAZ_END 

#define BAZ_BEGIN BAZ_EXTERN template class baz< 
#define BAZ_END >; 
#define BAZ_AND BAZ_END BAZ_BEGIN 

#ifdef MY_IMPLEMENTATION_CPP //cpp should define it before including the header file 
#define BAZ_EXTERN 
#else 
#define BAZ_EXTERN extern 
#endif 

BAZ_OBJS 
3

Das Wichtigste zuerst zu arbeiten: Korrekte Syntax für explizite Klasse Template-Instantiierung ist

template class baz<foo>; 
template class baz<bar>; 
template class baz<quux>; 

nicht template <> class baz<foo> was explizite Klasse Template-Spezialisierung (forward-Deklaration).

Eine Möglichkeit könnte sein, eine Klasse zu instanziieren, die

template <template <typename> class T, typename... Args> 
class for_each_class : T<Args>... 
{ 
}; 

// Instantiate 
template class for_each_class<baz, foo, bar, quux>; 

sieht wie folgt aus, die implizite Instanziierung baz<foo>, baz<bar>, baz<quux> zwingen würde. Nun, aber Sie möchten dies von typelist erstellen. Die typelist ist eine bereits spezialisierte Vorlage und es gibt in C++ keine Möglichkeit, über Vorlagenparameter in typelist aus der "Außenwelt von typelist" zu iterieren.

Eine andere Möglichkeit könnte sein, Makro zu verwenden, aber selbst im Makro können Sie Ihr Original typelist nicht verwenden. Ich würde schließen, Ihr Problem hat keine Lösung mit gegeben typelist.

Als eine Lösung würde ich die Template Instanziierung auf den Compiler wenn möglich verlassen. Nicht verwendete Vorlagen werden in diesem Fall nicht instanziiert. Die langsame Zusammenstellung ist auf die Art und Weise der meta-programs are specified zurückzuführen.

+0

Das würde 'baz ', 'baz ' oder 'baz ' nicht instanziieren. – Barry

+0

Da Elemente, die Vorlagen sind, nicht instanziiert werden. – Barry

+0

@Barry Bitte lesen Sie meine Post erneut. Ich sage nicht, dass dies 'baz ' etc. instanziiert, aber ich erkläre, warum das ohne Makro nicht möglich ist. –

1

Dies würde den Trick tun. Schließlich mit einer Spezialisierung der Typliste mit nur einem (oder keinem) Typen.

template <typename Head, typename ...Tail> 
struct typelist{ 
    typedef baz<Head> head_t; 
    typedef typelist<Tail...> tail_t; 
}; 
+0

Nein, es instanziiert 'baz' nicht. –

+0

Was wäre, wenn 'baz ' ein Attribut von 'typelist ' wäre? Ich kann es derzeit nicht testen; Der neueste Compiler, den ich bekam, ist gcc4.3. – prgasp77

+0

Welches Attribut? Mir sind keine Attribute bekannt, die die Template-Instanziierung ausführen. –

Verwandte Themen