2016-12-28 2 views
1

Lassen Sie uns sagen, dass wir eine Verpackungs Template-Funktion (oder Member-Funktion) wie dieses:Explizite Template-Instantiierung für variadische Vorlagen

template <class... T> void f(T... args) { 
    _f(args...); 
} 
void _f(int a1, ...); // variadic old style function 

Wo die Funktion _f (definiert in einem Drittanbieter-Bibliothek, die wir nicht ändern können) akzeptiert mehrere Kombinationen von Parametertypen und es wäre wünschenswert, mögliche Parameter auf der Verpackungsebene zu begrenzen.
Wenn wir Hunderte solcher Funktionen mit verschiedenen möglichen Parametertypen haben, wäre es zu umfangreich, die überladenen Funktionen mit benannten Argumenten manuell zu definieren. Es wäre besser, solche Funktionen unter Verwendung eines einfachen Makros mit Listen möglicher Typen zu definieren. Boosts Makros, die es erlauben, Argumentlisten zu iterieren, um benannte Argumente zu konstruieren, sehen zu schwer aus.
Gibt es eine elegante Möglichkeit, mögliche Argumente auf Deklarationsebene zu begrenzen?

+1

Oh ... '_f' ist eine altmodische non-template variadic Funktion. Das "..." stellt nicht nur etwas dar, das Sie aus Platzgründen ausgelassen haben, es ist tatsächlich das besondere Merkmal von C++. Ja, dann würde meine Antwort in der Tat nicht funktionieren, also habe ich sie gelöscht. – hvd

+0

Danke trotzdem. Es scheint, ich war nicht zu genau. –

+0

@hvd Ich habe deine Antwort bekommen und sie ein bisschen überarbeitet. Vielleicht klappt es jetzt. – skypjack

Antwort

2

Sie Züge können akzeptiert Parameterlisten auf ihren Typen auf Basis zu definieren.
Es folgt eine minimale, Arbeitsbeispiel:

void _f(int, ...) {} 

template<typename...> struct accepts; 
template<> struct accepts<int, double, char> {}; 
template<> struct accepts<int, char, int> {}; 

template <class... T> 
auto f(T... args) -> decltype(accepts<T...>{}, void()) { 
    _f(args...); 
} 

int main() { 
    f(0, 0., 'c'); 
    f(0, 'c', 0); 
    // f(0, 0, 0); 
} 

Die letzte Invokation werden Sie für <int, int, int> eine Kompilierung Fehler geben ist keine gültige Spezialisierung der Struktur accepts.
Dies liegt daran, dass die primäre Vorlage nicht definiert ist und Sie die akzeptierten Listen steuern können, indem Sie bei Bedarf immer mehr Spezialisierungen einführen.


Hier eine etwas andere Lösung basiert auf std::true_type, std::false_type und static_assert:

#include<type_traits> 

void _f(int, ...) {} 

template<typename...> struct accepts: std::false_type {}; 
template<> struct accepts<int, double, char>: std::true_type {}; 
template<> struct accepts<int, char, int>: std::true_type {}; 

template <class... T> 
void f(T... args) { 
    static_assert(accepts<T...>::value, "!"); 
    _f(args...); 
} 

int main() { 
    f(0, 0., 'c'); 
    f(0, 'c', 0); 
    // f(0, 0, 0); 
} 

Die Vorteile sind:

  • eine aussagekräftigere Fehlermeldung zu Kompilierung-Zeit (gut, wenn Sie ersetzen "!" durch etwas Sinnvolles)
  • die Möglichkeit t o Deaktivieren Sie explizit eine Liste von Parametern durch Erben von std::false_type und dokumentieren Sie es bei Bedarf

Der Nachteil ist, dass es ein wenig ausführlicher ist.

1

Nun, hvd ist genau richtig. Was Sie wirklich wollen, ist SFINAE. Aber ich hatte schon abgegangen es mit Boost.PP zu implementieren, sooo ...

#include <boost/preprocessor/seq/for_each_i.hpp> 
#include <boost/preprocessor/comma_if.hpp> 

#define DECL_PARAM(r, data, i, elem) \ 
    BOOST_PP_COMMA_IF(i) elem _ ## i 

#define FWD_PARAM(r, data, i, elem) \ 
    BOOST_PP_COMMA_IF(i) _ ## i 

#define DEF_FN_WRAPPER(name, delegate, params) \ 
    void name(BOOST_PP_SEQ_FOR_EACH_I(DECL_PARAM, ~, params)) { \ 
     delegate(BOOST_PP_SEQ_FOR_EACH_I(FWD_PARAM, ~, params)); \ 
    } 

expandiert nach:

void f(int _0 , double _1 , std::string _2) { _f(_0 , _1 , _2); } 
+0

Danke, es ist definitiv die mögliche Lösung. –

Verwandte Themen