2017-12-13 8 views
2

Ich habe es mit einer C++ - Bibliothek von Drittanbietern zu tun, die umfangreiche Vorlagen verwendet. Das macht es schwierig, eine C-API zu erstellen, um es von meinem Framework zu verwenden.Bridging Templates mit Laufzeitargumenten

das Problem Abstracting an, dass die Bibliothek bietet die Funktion:

template <int i> void foo(); 
template <int i> void zoo(int v); 

ich eine C-API mit dem Funktionskopf erstellen möchten:

extern "C" void c_foo(int i); 
extern "C" void c_zoo(int i, int v); 

Eine offensichtliche Umsetzung sein könnte:

void c_foo(int i) 
{ 
    switch(i) { 
     case 1: foo<1>(); break; 
     case 2: foo<2>(); break; 
     case 3: foo<3>(); break; 
     default: break; 
    }; 
}; 

und das gleiche auch für void zoo(int) tun.

Dies funktioniert, wenn der Bereich der möglichen Werte für i klein ist. Wenn ich alle möglichen Werte von i in [1.100] behandeln will, dann wird es sehr hässlich Code auf diese Art zu schreiben, da es viele Wiederholungen gibt.

Gibt es einen kompakteren Weg das zu tun, d. H. Weniger Codezeilen schreiben? Vielleicht mit rekursiven Preprozessor-Makros?

+0

_ "Vielleicht mit rekursiven Präprozessor-Makros?" _ Es gibt eine Reihe von Bibliotheken, die Hilfsmakros für solche Aufgaben anbieten. Z.B. der Boost-Preprozessor lib. – user0042

Antwort

5

Sie könnten Vorlagen intern verwenden, um den erforderlichen Code zu generieren.

Eine Möglichkeit besteht darin, eine Dispatch-Tabelle mit 100 Funktionszeigern zu erstellen und diese zur Laufzeit zu indizieren. c_foo eine Kompilierung-Sequenz von Indizes erzeugen und einen Helfer nennen:

extern "C" void c_foo(int i) {  
    c_foo_impl(std::make_integer_sequence<int,100>{}, i); 
} 

Dieser Helfer wird die Dispatch-Tabelle erzeugen und den Anruf durchführen:

template <int... Is> 
void c_foo_impl (std::integer_sequence<int,Is...>, int i) { 
    constexpr std::array<void(*)(), sizeof...(Is)> dispatch = { &foo<Is>... }; 

    //assert or some other error handling for i > sizeof...(Is) 
    dispatch[i](); 
} 

Dann können Sie das gleiche tun für zoo :

extern "C" void c_zoo(int i, int v) { 
    c_zoo_impl(std::make_integer_sequence<int,100>{}, i, v); 
} 

template <int... Is> 
void c_zoo_impl (std::integer_sequence<int,Is...>, int i, int v) { 
    constexpr std::array<void(*)(int), sizeof...(Is)> dispatch = { &zoo<Is>... }; 

    //assert or some other error handling for i > sizeof...(Is) 
    dispatch[i](v); 
} 

Wenn Sie, dass Sie in wenigen Orten brauchen diese finden, einige der Details abstrahieren Sie könnten, oder eine Bibliothek verwenden, wie Petra, die eine switch_table bereitstellt, um diese Art von Mapping durchzuführen.

Live demo

+0

Ich mag die Idee, aber ich frage mich, ob die Dispatchtabelle bei jedem Anruf neu generiert wird. Ich denke, ich werde eine leichte Variante versuchen, den Pointer-Vektor in eine Struktur einzubetten, so dass ich dann eine globale Instanz dieser Struktur erstellen und diese für das Dispatching verwenden kann. – Fabio

+0

Sie können 'Dispose'' constexpr' machen, um Regeneration zu vermeiden. – TartanLlama

+0

Der von VS2015 generierte Assembler zeigt, dass das Array selbst bei 'constexpr' bei jedem Aufruf neu initialisiert wird. Mit 'static' geht das Problem weg. – Fabio

0

Ich denke, die Frage ist, was diese Vorlage Zahl? Ist es etwa eine interne Puffergröße oder eher ein Befehlsbyte? Man geht davon aus, dass das durchschnittliche C++ - Programm nur einige wenige Argumentwerte erzeugen würde.

Vielleicht wäre der Bast-Ansatz, einige typische Puffergrößen oder Befehlsnamen aufzulisten und nur diese zu instanziieren. Wenn also der c-Code Ihre enum als Parameter verwendet, wird es funktionieren, andernfalls wird es (scheitert schrecklich).

Es ist ärgerlich, neue Einträge hinzuzufügen, wenn sie gerechtfertigt sind, aber Sie könnten ein Skript dafür schreiben.

Ein anderer Ansatz könnte darin bestehen, verstümmelte benannte Stubs zu erzeugen, d. H. Ein foo__1() foo__2() usw. zu erzeugen, vielleicht mit Hilfe des Boost-erweiterten Präprozessors, um zu helfen. Auch hier ist der C-Programmierer automatisch auf die Methoden und Bereiche beschränkt, die Sie zur Kompilierzeit veröffentlicht haben.