Templates in C möglich machen?
-
Hallo allerseits,
ich würde mich gerne informieren was ich für eine Möglichkeit habe C++ Templates
in C nutzbar zu machen.*\ Ich habe einen funktionsfähigen Code der von jemandem anderen programmiert wurde und der über C kompiliert wird. Also rein C ist. Nennen wir diesen CODE_OTHER
*\ Dann habe ich meinen Code der C++ basiert ist und Templates verwendet.
Nennen wir diesen CODE_MY*\ Jetzt möchte ich innerhalb CODE_OTHER meinen code also CODE_MY nutzbar machen.
Ich habe angenfangen in CODE_OTHER funktionen zu schreiben die ein Mapping zwischen den Datenstrukturen schafft. Jetzt komme ich aber an eine Stelle wo ich
eine Template-basierte klasse aus CODE_MY instantiieren will/muss.Ich weiß hier nicht was ich tun kann/soll: Ich habe mich informiert es aber nicht genau verstanden:
-> Irgendwie gibt es eine Möglichkeit in CODE_MY über extern C interface funktionen an CODE_MY zu bieten. Dann CODE_MY als shared library zu kompilieren
und anschließend das Object-file von CODE_OTHER gegen das Object-file von CODE_MY zu linken.Klappt das so oder kann mir jemand noch genauere angaben geben? Ich finde auch keine Minimalbeispiele diesbezüglich. ich würde mich sehr freuen wenn mir hier jemand helfen könnte.
Danke
-
*\ Jetzt möchte ich innerhalb CODE_OTHER meinen code also CODE_MY nutzbar machen.
Schreibe einen C-Wrapper von CODE_MY, kompiliere ihn mit einem C++ Compiler und linke ihn zum C-Projekt dazu. Soweit die Theorie. Das Problem sind natuerlich Datentypen, Objekte und Klassen. Templates muessen ja erstmal instanziiert werden. Deswegen bricht man komplexe Daten auf Standardtypen wie int, char (oder Arrays von diesen) herunter, um einen konfliktfreien Austausch zu ermoeglichen.
Dann CODE_MY als shared library zu kompilieren
Muss keine shared library sein.
in CODE_OTHER funktionen zu schreiben
Wenn du sowieso den Code zur Verfuegung hast, warum nutzt du nicht die weitreichende Kompatibilitaet von C und C++ aus und uebersetzt den C Code mit einem C++ Kompiler?
-
Schreibe einen C-Wrapper von CODE_MY, kompiliere ihn mit einem C++ Compiler und linke ihn zum C-Projekt dazu.
Bitte nicht böse sein wegen meiner dummen fragerei:
* Ist das die einzige Möglichkeit?
* Wo finde ich gute Links um so etwas zu tun bzw. wie kann ich so etwas bewerkstelligen?
-
Danke für die weitere Antwort
Das Problem sind natuerlich Datentypen, Objekte und Klassen. Templates muessen ja erstmal instanziiert werden. Deswegen bricht man komplexe Daten auf Standardtypen wie int, char (oder Arrays von diesen) herunter, um einen konfliktfreien Austausch zu ermoeglichen.
Ja. ich habe jetzt in CODE_OTHER schon viel arbeit reingesteckt und die ganzen umrechnungen der daten hinbekommen. Jetzt fehlt nur noch eine methode in der dann sozusagen der ganze transfer stattfindet bzw. z.B. eine templatebasiertes Objekt aufgerufen wird. War das alles jetzt umsonst?
Wenn du sowieso den Code zur Verfuegung hast, warum nutzt du nicht die weitestgehende Kompatibilitaet von C und C++ aus und uebersetzt den C Code mit einem C++ Kompiler.
Naja...weil CODE_OTHER eben von jedem runterladbar ist und standardmässig einen c-compiler drin hat. Ich würde gerne denjenigen dann anbieten meine integration zu nutzen will ihnen aber nicht aufzwingen auch ihre makefiles umzuschreiben....
oder wäre das gar kein aufwand?
-
du müsstest so oder so in der DLL das Template instantiieren, und zwar auf jede art, die du in dem anderen Code benutzen willst. Im Grunde erzeugst du dadurch aus dem Klassentemplate den Code für die Instantiierungen, d.h. für die einzelnen Templateklassen.
Da es in C keine Klassen gibt, müsstest du zusätzlich C-Funktionen bereitstellen, die die Benutzung der Klassen kapseln. Wie genau das dann erfolgt ist dir überlassen. Eine möglichkeit wäre was in der Art://foo.hpp, das C++-Template template <class T> class Foo { public: Foo f(T const& i) : i(i) {} int bar() {return i;} private: T i; }; //foowrap.h, C/C++-Header #ifdef __cplusplus extern "C" { #endif #define FOOWRAP(type) \ \ typedef struct { void* ptr; } FOO_##type; \ \ FOO_##type create_foo_##type( const type* pi); \ type call_bar_foo_##type(FOO_##type foo); \ void destroy_foo_##type(FOO_##type foo) FOOWRAP(int); FOOWRAP(short); FOOWRAP(float); #undef FOOWRAP #ifdef __cplusplus } /*end extern "C"*/ #endif //foowrap.cpp - implementierung auf C++-Seite #include "foowrap.h" #include "foo.hpp" #define FOOWRAPIMPL(type) \ \ FOO_##type create_foo_##type( const type* pi) \ { \ FOO_##type t; \ /*hiermit wird das temlate auch instantiiert:*/\ t.ptr = new Foo<type>(*pi); \ return t; \ } \ \ type call_bar_foo_##type(FOO_##type foo) \ { \ Foo<type>* pf \ = static_cast<Foo<type>*>(foo.ptr); \ return pf->bar(); \ } \ \ void destroy_foo_##type(FOO_##type foo) \ { \ Foo<type>* pf \ = static_cast<Foo<type>*>(foo.ptr); \ delete pf; pf = NULL; \ } FOOWRAPIMPL(int); FOOWRAPIMPL(float); FOOWRAPIMPL(short); #undef FOOWRAPIMPL //main.c - C-Datei! #include "foowrap.h" int main() { FOO_int fi = create_foo_int(14); int i = call_bar_foo_int(fi); printf("Foos i: %d\n", i); destroy_foo_int(fi); }ungetestet und ohne gewähr, aber templates gegen C-Code ist ne ziemliche schweinigelei, wie man sieht

-
Probleme hast du sowieso nur zu erwarten, wenn der C Teil Funktionen aus dem C++ Teil aufrufen willst.
Dazu kannst du im C++ Teil einfach 'extern "C"' Wrapper schreiben.
Das können natürlich nur einfache Funktionen sein, keine Templates. Du kannst aber natürlich diese einfachen Funktionen so implementieren, dass sie nichts weiter tun als ein Template aufzurufen.Beispiel:
extern"C" int MyFunctionChar(char c) { return MyNamespace::MyFunction(c); } extern"C" int MyFunctionInt(int i) { return MyNamespace::MyFunction(i); } // etc.Dazu schreibst du ein passendes C-kompatibles Header-File, damit du aus dem C-Teil auch an die Prototypen drankommst.
Das kann man z.B. so machen:
#ifdef __cplusplus #define MY_FOO_EXTERN_C extern "C" #else #define MY_FOO_EXTERN_C #endif MY_FOO_EXTERN_C int MyFunctionChar(char c); MY_FOO_EXTERN_C int MyFunctionInt(int i);So ein Header-File kann dann sowohl von C als auch von C++ Programmen verwendet werden.
-
Testo schrieb:
Naja...weil CODE_OTHER eben von jedem runterladbar ist und standardmässig einen c-compiler drin hat.
ich bezweifle dass der Code einen eingebauten COmpiler hat

Ich würde gerne denjenigen dann anbieten meine integration zu nutzen will ihnen aber nicht aufzwingen auch ihre makefiles umzuschreiben....
oder wäre das gar kein aufwand?wenn die Makefiles ordentlich gemacht wurden steht da an einer Stelle drin wie der Compiler aufzurufen ist. Das wäre also eine Zeile zu ändern.
-
puh...also erstmal danke für eure zahlreichen antworten.
Ich bin mir nicht sicher ob ich euch verstehe. Vielleicht könnte ich mal ein Minimalbeispiel aufsetzen:Hier mal meine main.c
/* * This is my C-Program * */ #include "stdlib.h" int main() { double * mydata = NULL; /* Here i want to use the MyB.foo(MyC<double>* myC) but * i dont have any myC object because i cannot create it * here because its template based*/ /* ?????????? */ MyC<double>* mtx = new MyC<double>(); }Und hier mein main.cpp
// This is my C++ Programm #include <iostream> #include <string.h> template<class T> class MyC { public: MyC<T>() { data = NULL; memset(data, 0, 10*sizeof(T)); } ~MyC<T>() { delete [] data; } T* data; }; template<class T> class MyB { public: MyB<T>() { mydata = NULL; } void foo(MyC<T>* mtx) { memcpy(mtx->data,mydata,10*sizeof(T)); } T* mydata; }; int main() { MyC<int>* mtx = new MyC<int>(); MyB<int>* myB = new MyB<int>(); myB->foo(mtx); delete mtx; delete myB; }Das main.c programm ist CODE_OTHER und main.cpp ist CODE_MY. Mein Problem ist ich habe in CODE_MY eigene datenstrukturen die templatebasiert sind. Wenn in CODE_OTHER jetzt wieder andere datenstrukturen sind muss ich ja irgendwie ein mapping machen oder? Ich meine ich muss doch dann CODE_MY ein wenig abändern weil ich ja ganz andere datenstrukturen einspeisen werde...
oder soll ich jetzt für jede template-klasse einen wrapper schreiben?
-
oder anders ausgedrückt in worten:
CODE_OTHER hat eine stuktur *data z.B.
in CODE_MY steht dann
int main() { MyC<T>* mtx = NULL; mtx = readMatrix(); Algorithm(mtx); }Wie schaffe ich es Algorithm statt auf mtx auf *data laufen zu lassen?
Ich muss doch dazu in CODE_MY eine funktion schreiben die *data entgegennimmt und die umwandlung macht? und dann ncoh eine zusätzliche funktion um Algorithm aufzurufen. Aber dazu musss ich ja die main() in CODE_MY verändern oder nicht?
-
Und nochmal code :
Hier mal CODE_MY wo ich eben templatebasiert bin.
// This is my C++ Programm #include <iostream> #include <string.h> template<class T> class MyC { public: MyC<T>() { data = new T[10]; memset(data, 0, 10*sizeof(T)); } ~MyC<T>() { delete [] data; } void print(MyC<int>* mtx); T* data; }; template <> void MyC<int>::print(MyC<int>* mtx) { // Hier z.B. mtx ausgeben oder andere // Dinge mit mtx tun... for (int i = 0; i <10; i++) std::cout << i << std::endl; } template<class T> class MyB { public: MyB<T>() { mydata = new T[10]; } void Algorithm(MyC<T>* mtx) { // do something with mtx mtx->print(mtx); memcpy(mtx->data,mydata,10*sizeof(T)); } T* mydata; }; int main() { MyC<int>* mtx = new MyC<int>(); MyB<int>().Algorithm(mtx); delete mtx; }Hier mal das C programm CODE_OTHER welches eben z.B. die data struktur hat.
/* * This is my C-Program * */ #include "stdlib.h" int main() { double* data = calloc(10, sizeof(double)); free(mydata); }Und wie schaffe ich es jetzt eine verbindung zwischen beiden herzustellen.
Also data über
MyC<int>::print(MyC<int> mtx)
auszugeben?
-
Puh....
seh ich das richtig dass ich diesen code via c++-compiler übersetze://foo.hpp, das C++-Template template <class T> class Foo { public: Foo f(T const& i) : i(i) {} int bar() {return i;} private: T i; }; //foowrap.h, C/C++-Header #ifdef __cplusplus extern "C" { #endif #define FOOWRAP(type) \ \ typedef struct { void* ptr; } FOO_##type; \ \ FOO_##type create_foo_##type( const type* pi); \ type call_bar_foo_##type(FOO_##type foo); \ void destroy_foo_##type(FOO_##type foo) FOOWRAP(int); FOOWRAP(short); FOOWRAP(float); #undef FOOWRAP #ifdef __cplusplus } /*end extern "C"*/ #endif //foowrap.cpp - implementierung auf C++-Seite #include "foowrap.h" #include "foo.hpp" #define FOOWRAPIMPL(type) \ \ FOO_##type create_foo_##type( const type* pi) \ { \ FOO_##type t; \ /*hiermit wird das temlate auch instantiiert:*/\ t.ptr = new Foo<type>(*pi); \ return t; \ } \ \ type call_bar_foo_##type(FOO_##type foo) \ { \ Foo<type>* pf \ = static_cast<Foo<type>*>(foo.ptr); \ return pf->bar(); \ } \ \ void destroy_foo_##type(FOO_##type foo) \ { \ Foo<type>* pf \ = static_cast<Foo<type>*>(foo.ptr); \ delete pf; pf = NULL; \ } FOOWRAPIMPL(int); FOOWRAPIMPL(float); FOOWRAPIMPL(short); #undef FOOWRAPIMPLund diesen code nur mit c - wie sieht dann aber das linking aus??
//main.c - C-Datei! #include "foowrap.h" int main() { FOO_int fi = create_foo_int(14); int i = call_bar_foo_int(fi); printf("Foos i: %d\n", i); destroy_foo_int(fi); }
-
Freund Testo, dir ist schon klar dass es in C auch keine Klassen gibt, ja?
Was du willst hat erstmal mit Templates nichts zu tun, sondern damit, dass es eben in C keine Klassen gibt.
Ob es sich dabei C++ seitig um Templates handelt oder um "normale" Klassen, ist C seitig komplett egal.
-
Danke erneut.
@hustbaer. Ja du hast recht - ich sehe jetzt mein Problem etwas klarer.
Das Beispiel von pumuckl ist ein wenig "zu schwer" für mich im Moment.
Ich würde mich freuen wenn mir jemand mit meinem minimalbeispiel weiterhelfen könnte - in diesem möchte ich den c++ code in dem c-code verwenden. Ich hoffe sehr dass ich ein korrektes minimalbeispiel aufzeige dass mein problem beschreibt. Ich bin über andere Hilfe jedesmal dankbar....:Der C++ code
// This is my C++ Programm #include <iostream> #include <string.h> template<class T> class MyC { public: MyC<T>() { data = new T[10]; memset(data, 0.0, 10*sizeof(T)); } ~MyC<T>() { delete [] data; } void print(MyC<int>* mtx); T* data; }; template <> void MyC<int>::print(MyC<int>* mtx) { // Hier z.B. mtx ausgeben oder andere // Dinge mit mtx tun... int a; for (int i = 0; i <10; i++) std::cout << mtx->data[i] << std::endl; } template<class T> class MyB { public: MyB<T>() { mydata = new T[10]; } // do something with mtx void Algorithm(MyC<T>*& mtx) { for (int i= 0; i < 10; i++) mtx->data[i] = i; mtx->print(mtx); memcpy(mtx->data,mydata,10*sizeof(T)); } T* mydata; }; int main() { MyC<int>* mtx = new MyC<int>(); MyB<int>().Algorithm(mtx); delete mtx; }Der C - Code
/* * This is my C-Program * */ #include "stdlib.h" int main() { int* data = calloc(10, sizeof(int)); for (int i = 10; i > 0; i--) data[10-i] = i; // Hier würde ich gern data über die C++ Klasse // so ändern dass in data in aufsteigender // art die i's gespeichert sind. for (int i = 0; i < 10; i++) printf("data[%d] = %d\n", i, data[i]); free(data); }
-
Die erste Sache ist schonmal: entscheide dich in welchem Codeteil die main() überhaupt sein soll. Schreib mal deinen Code komplett so wie du ihn schreiben würdest wenn du in C die Teplates benutzen könntest. Was du da hast, nämlich zwei main()-Funktionen ist sinnfrei und machts extrem schwer zu erkennen was du eigentlich willst.
-
Entschuldigung. Ich möchte die main von C benutzen also main.c.
Also main soll im letzteren code-teil sein.Ich versuche mal (wie vorgeschlagen) den code so zu schreiben wie wenn ich die templates in c verwenden könnte:
/* * This is my C-Program * */ #include "stdlib.h" int main() { int* data = calloc(10, sizeof(int)); for (int i = 10; i > 0; i--) data[10-i] = i; // Hier würde ich gern data über die C++ Klasse // so ändern dass in data in aufsteigender // art die i's gespeichert sind. // Ich habe schwierigkeiten mich richtig auszudrücken: // Ich habe hier in meinem C-code eine struktur data // (vereinfacht ein array) die eben anders ist als // Objekte vom Typ MyC. Ich möchte aber über Algorithm // mein hieriges data so ändern wie wenn ich MyB.Algorithm() // aufrufen würde. Dazu brauche ich wohl erstmal sowas wie // eine methode die mir mein data in ein MyC Objekt verändert // oder? ALso so ein Mapping zwischen den Datenstrukturen. // // Im Prinzip sind es zwei dinge die ich nicht ganz verstehe. // 1. Wie verbinde ich beide codes so das ich aus c die c++ // sachen aufrufen kann. das wäre dann so ein aufruf wie: MyC<int> * mtx = new MyC<int>(); MyB<int>().Algorithm(mtx); // UND 2. habe ich hier in meinem c-programm ja andere datenstrukturen // (hier ist es jetzt nur vereinfacht ein array aber es könnte // ja auch eine komplexe structure sein). Und hier verstehe ich // eben auch nicht wie ich denn ausgehend von meinen hierigen // datenstrukturen (data) den c++ code nutzen könnte also so in // etwa: MyC<int>* mtx = convert_data_to_MyC(data); MyB<int>().Algorithm(mtx); *data = convert_MyC_to_data(mtx); // Es wäre sehr schön wenn letzteres gehen würde. Im Prinzip // sollte dann ja die folgende ausgabe wieder aufsteigende // i's ausgeben innerhalb data. for (int i = 0; i < 10; i++) printf("data[%d] = %d\n", i, data[i]); free(data); }
-
dann mal (beispielsweise) hier den Code, wie du ihn schreiben könntest:
//main.c #include "myc_wrapper.h" #include "myb_wrapper.h" int main() { /*...*/ //MyC<int> * mtx = new MyC<int>(); WrapMyC_int mtx = myc_int_create(); //konstruktor //MyB<int>().Algorithm(mtx); WrapMyB_int tmp = myb_int_create(); //konstruktor myb_int_algorithm(tmp, mtx); //methode myc_int_delete(mtx); //destruktor myb_int_delete(tmp); //destruktor /*...*/ //MyC<int>* mtx = convert_data_to_MyC(data); WrapMyC_int mtx = myc_int_create_1(data); //anderer konstruktor //MyB<int>().Algorithm(mtx); WrapMyB_int tmp = myb_int_create(); //konstruktor myb_int_algorithm(tmp, mtx); //methode myc_int_writetobuffer(mtx, data); myc_int_delete(mtx); //destruktor myb_int_delete(tmp); //destruktor }C/C++-Header, ich hab mal beide zusammengeschmissen:
//myc_wrapper.h, myb_wrapper.h #ifdef __cplusplus extern "C" { #endif typedef struct WrapMyB_int_ { void* ptr; } WrapMyB_int; typedef struct WrapMyC_int_ { void* ptr; } WrapMyC_int; WrapMyB_int myb_int_create(void); void myb_int_algorithm(WrapMyB_int this_, WrapMyC_int arg); void myb_int_delete(WrapMyB_int this_); WrapMyC_int myc_int_create(void); WrapMyC_int myc_int_create_1(int* data); void myc_int_writetobuffer(WrapMyC_int this_, int* buffer); void myc_int_delete(WrapMyC_int this_); #ifdef __cplusplus } /*end extern "C"*/ #endifC++-Definition:
#include "myc_wrapper.h" #include "myb_wrapper.h" #include "myc.hpp" //MyC<T> #include "myb.hpp" //MyB<T> WrapMyB_int myb_int_create(void) { WrapMyB_int tmp; tmp.ptr = new MyB<int>(); return tmp; } void myb_int_algorithm(WrapMyB_int this_, WrapMyC_int arg) { MyB<int>* mb = static_cast<MyB<int>*> this_.ptr; mb->Algorithm(arg); } void myb_int_delete(WrapMyB_int this_) { MyB<int>* mb = static_cast<MyB<int>*> this_.ptr; delete mb; this_.ptr = NULL; } //der Rest sei dir zur Übung überlassen ;) WrapMyC_int myc_int_create(void) { /* ... */ } WrapMyC_int myc_int_create_1(int* data) { /* ... */ } void myc_int_writetobuffer(WrapMyC_int this_, int* buffer) { /* ... */ } void myc_int_delete(WrapMyC_int this_) { /* ... */ }Der Clou ist: du kanns in C keine Klassen oder Poitner darauf rumreichen, weil C nicht weiß was das ist. Du reichst stattdessen einen voidpointer rum und nur der C++-Code weiß dass er das Ding auf eine Klasse umcasten kann und wie der zu benutzen ist.
Aus jedem Konstruktor machst du eine Funktion, die ein entsprechendes wrapper-struct zurückliefert.
Aus allen anderen Methoden inklusive Destruktoren machst du Funktionen, die die gleichen Rückgabewerte haben und zusätzlich zu den normalen Parametern noch das Wrapperstruct als this-Parameter übernehmen.Da Templates keine Klassen sind sondern nur ihre Instantiierungen Klassen sind, musst du das Ganze für jede Template-Instantiierung machen, die du vom C-Code aus benutzen willst. Oben habe ich das bei MyC und MyB jeweils nur für int gemacht.
Da das immer wieder das selbe ist, hab ich in meinem ersten Beispiel ein langes Makro draus gemacht, das mir die Funktionsdeklarationen udn -definitionen jeweils für int, short und float generiert hat.
-
Wobei mir immer noch schleierhaft ist, WARUM du das machst.
Sobald du in der main von CODE_OTHER rumfummelst, ist das nicht mehr das, was jeder User runterlädt.
Andererseits hast du auch ne main in CODE_MY, den du ohne weiteres auch mit C-Code füttern kannst und vor allem abändern.Was ist eigentlich der Hintergrund, dass du dich genötigt siehst fremden Code in C zu verwenden und zu ändern, nur damit du deinen eigenen C++-Code einfügen kannst?
-
Was ist eigentlich der Hintergrund, dass du dich genötigt siehst fremden Code in C zu verwenden und zu ändern, nur damit du deinen eigenen C++-Code einfügen kannst?
Naja: Wenn ich meinen Code nutzbar machen will in CODE_OTHER geht das doch nicht anders. Ich will dass die algorithmen innerhalb CODE_OTHER auf CODE_MY zurückgreifen können. Wäre das so nicht der richtige weg?
@ Pumuckl:
Herzlichen Dank für das Beispiel. Mir wird vieles klarer. Ich versuche gerade das Beispiel zu kompilieren. Sehe ich das richtig dass ich aus dem C++ code erstmal eine library (static) erstellen muss. Ich mache das unter linux im moment so:ar rcs libmaincpp.a MyB.o MyC.oAnschließend gehe ich zu dem "neuen" C-code der dne c++ code nutzen. Sehe ich das richtig dass ich hier dann gegn die library linken muss:
gcc -o mainnew mainnew.c -L lmaincpp.aoder so ählnich?
Aufgrund der undefined refeerences vermute ich dass beim linken etwas faul wird...
-
Ich bin weiterhin verwirrt wie ich eine LIbrary erstellen soll die nur aus headern besteht !? Bei der benutzung von templates kann ich doch nur header erstellen...ich habe keine *.cpp file für MyC.h ?
-
Testo schrieb:
Ich bin weiterhin verwirrt wie ich eine LIbrary erstellen soll die nur aus headern besteht !? Bei der benutzung von templates kann ich doch nur header erstellen...ich habe keine *.cpp file für MyC.h ?
Dann hast du pumuckl & Co nicht verstanden.
Du musst für deinen C++ Code einen Wrapper schreiben, und dort musst du die Templates auch auf konkrete Typen festlegen. Anders geht es nicht.
-
Jetzt versteh ich nur noch Bahnhof.
Ich dachte den wrapper hat pumuckl mir schon geschrieben. Da sind doch die cpp wrapper oder nicht? MIr istklar dass ich da in den leeren wappern noch was füllen muss...sehe ihc das falsch?