2016-01-17 5 views
11

Ich habe eine variadic Funktion zoo, die N Argumente übernimmt, wobei N zur Kompilierzeit bekannt ist (es ist ein Vorlagenparameter der Klasse mit der Funktion).So extrahieren Sie einen ausgewählten Satz von Argumenten einer variadic Funktion und verwenden Sie sie zum Aufruf einer anderen Funktion

template <int N> 
struct B 
{ 
    template <typename... Args> 
    static void zoo(Args... args) 
    { 
     static_assert(size of...(args) == N, ""); 
     // do something 
    } 
}; 

I eine andere Variadische Funktion foo haben, die M Argumente verwendet, wobei M> N ist, und zum Zeitpunkt der Kompilierung bekannt ist (es ist ein Template-Parameter der Klasse die Funktion enthält). Ich habe eine statische index_array, die die Indizes der Argumente von foo Ich möchte an zoo übergeben.

aus dem Körper foo Ich möchte Zoo Leiten einer ausgewählten Teilmenge der Argumente von foo nennen.

Was ist der beste Weg, dies zu tun? Idealerweise perfektes Inlining erreichen, d. H. So dass alles in nur eine Anweisung kompiliert wird, ohne Funktionszeiger-Umleitungen?

template<int...I> 
struct indices 
{ 
    static constexpr int N = sizeof...(I); 
}; 

template <int M, typename...X> 
struct A 
{ 
    // here I am simplifying, in reality IS will be built at compile time based on X 
    typedef indices<0,2,3> IS; 

    template <typename... Args> 
    static void foo(Args... args) 
    { 
     static_assert(size of...(args) == M, ""); 
     // do some magic to achieve the function call described in pseudo-code 
     // B<IS::N>::zoo(args(IS(0),IS(1),IS(2))) 
     // ideally this should be perfectly inlined to just have the call above 
    } 
}; 

Bitte beachten Sie, dass der obige Code eine Vereinfachung meines Problems darstellt, die zur Veranschaulichung der Frage entwickelt wurde.

BEARBEITEN: Wie unten beschrieben, beschreibe ich den Anwendungsfall: Ich spiele mit einer Vorlage basierten Bibliothek, um Micro-Controller-Pins zu fahren. Ein Mikrocontroller hat mehrere Ports (zugänglich als Bytes im Speicher) und jeder Port hat bis zu 8 Pins (Bits). Klasse A ist ein Bündel von Pins über das Template-Argument X, wobei jeder Pin als Pin definiert ist. Klasse B manipuliert alle Pins am selben Port. A :: foo ist eine Funktion, um einige der Pins zu modifizieren, wobei die Argumente in der Reihenfolge liegen, in der die Pins im X-Template-Argument-Pack angegeben sind. foo muss die Argumente nach Ports gruppieren und an die B-Klassen senden, die einzelne Ports repräsentieren, wobei alle Argumente fusioniert und in einem einzigen Befehl in den Controller geschrieben werden.

+0

Können Sie einen Anwendungsfall dafür beschreiben? Klingt nach etwas, das beim Debuggen schrecklich wäre, wenn es jemals kaputt geht. Wenn die Anzahl der Fälle foo/Zoo-Paare weniger als etwa zehn ist, würde ich sie einfach codieren. Vielleicht ziehen Sie sie über CRTP in "struct A". – Rumburak

+0

Bitte beachten Sie die der Frage hinzugefügte Anwendungsfallbeschreibung. – Fabio

Antwort

4

Sie einen Helfer erstellen können die nth_arg wie folgt zu extrahieren:

template <int I> 
struct ignore 
{ 
    template <typename T> 
    ignore(T&&) // This constructor accepts anything 
    { 
    } 
}; 

template <typename T> 
struct nth_arg; 

template <size_t... DropIndexes> 
struct nth_arg<std::integer_sequence<size_t, DropIndexes...>> 
{ 
    template <typename Arg, typename... Rest> 
    static decltype(auto) get(ignore<DropIndexes>..., // ignore args 0...n-1 
          Arg&& arg, 
          Rest&&...) // also ignore the rest 
    { 
    return std::forward<Arg>(arg); // return nth arg 
    } 
}; 

Und dann

nennen
template <int... Is, typename... Args> 
static void call_zoo(indices<Is...>, Args&&... args) 
{ 
    B<sizeof...(Is)>::zoo(nth_arg<std::make_index_sequence<Is>>::get(
     std::forward<Args>(args)...)...); 
} 

template <int M> 
struct A 
{ 
    typedef indices<0, 2, 3> IS; 

    template <typename... Args> 
    static void foo(Args... args) 
    { 
    static_assert(sizeof...(args) == M, ""); 
    call_zoo(IS{}, std::forward<Args>(args)...); 
    } 
}; 

Wenn Sie C++ 11 verwenden, können Sie einfach Ihre eigenenrollen.

+0

Elegante Lösung, die keine Tupel benötigt.+1 – Fabio

+1

Ich wünschte, der Standard enthalten einige Zucker-Syntax auf den nth_arg zugreifen, die mir ein häufiger Anwendungsfall scheint. – Fabio

+1

Rumburak: nur um Sie wissen zu lassen, zitierte ich Ihre Antwort [hier] (http://stackoverflow.com/questions/34904049/how-to-create-a-sorted-mapping-integer-index-with-templates) – Fabio

5

Packen Sie die Argumente in ein Tupel von Referenzen und rufen Sie sie dann mit std::get und einer Packerweiterung auf den Indizes ab.

template<class Tuple, int... Is> 
static void magic(Tuple&& args, indices<Is...>){ 
    B<IS::N>::zoo(std::get<Is>(std::forward<Tuple>(args))...); 
} 

template <typename... Args> 
static void foo(Args... args) 
{ 
    static_assert(sizeof...(args) == M, ""); 
    magic(std::forward_as_tuple(args...), IS{}); 
} 

(Möglicherweise möchten Sie foo nehmen Forwarding Referenzen machen.)

+0

Das scheint genau das zu sein, was ich brauche. Ich werde es versuchen. Vielen Dank. – Fabio

Verwandte Themen