Zeiger auf Funktionen mit versch. Parametern
-
Hallo,
ich arbeite im Moment an einem Programm, welches viele Funktionen mit unterschiedlichen Parametertypen und unterschiedlicher Parameteranzahl beinhaltet. Im Pronix C-Kurs habe ich für ANSI C eine Variante mit einem Zeiger auf Funktionen gefunden:
int (*tag_func[])() = {func1,func2,func3,func4,func5};Mich würde jetzt interessieren ob diese Form eigentlich "schön" respektive konform ist und weiters, ob es eine bessere Lösung in C++ gibt?
Vielen Dank schonmal für die Hilfe

-
SeniiX schrieb:
...Funktionen mit unterschiedlichen Parametertypen und unterschiedlicher Parameteranzahl ...
Sehe da jetzt gerade keinen Zusammenhang zu dem Array von int-Funktionspointern ...

SeniiX schrieb:
int (*tag_func[])() = {func1,func2,func3,func4,func5};Was willst'n machen ?
So ins Blaue hinaus (und das ist der Fall, in dem solche Ellipsen fast immer gefragt werden) würde ich auf den Übergang von printf() -> ostream verweisen:
int i = 0; string s = "1"; double d = 2.3; // Ausgabe in C printf("%d%s%lf", i, s.c_str(), d); // Ausgabe in C++ cout << i << s << d;Alternativ kann man bei variabler Parameterzahl (aber identischer Schnittstelle der Parametertypen) noch einen vector<base_class*> ... übergeben.
Gruß,
Simon2.
-
@SeniiX: Wo steht da was von unterschiedlichen Parametern und unterschiedlicher Parameteranzahl in deinem C Beispiel? Schau dir mal boost::function an (oder tr1::function).
-
Danke erstmal euch zwei für eure Hilfe.
Zum besseren Verständnis hier mal der ganze Code aus dem Pronix C-Kurs:
#include <stdio.h> typedef struct MISC { int tag; /*Tagwert siehe enum*/ void *obj; /*Adresse des Objektes*/ int (*func)(); /*Adresse der Funktion die Ausgeführt wird*/ }MISC; /*Kennung : ZS_TAG für Zeichen&Sring | SL_TAG für String&Long DSD_Tag für Double&String&Double | IS_TAG für Integer&String*/ enum{ INVALID_TAG,ZS_TAG,SL_TAG,DSD_TAG,IS_TAG}TAG; /*ZS für Zeichen & String*/ typedef struct ZS { char data; char zs[80]; }ZS; /*SL für String & Long*/ typedef struct SL { char sl[80]; long data; }SL; /*DSD für Double & String & Dounble*/ typedef struct DSD { double data_a; char dsd[80]; double data_b; }DSD; /*IS für Integer & String*/ typedef struct IS { int data; char is[80]; }IS; /*Packt verschieden Datenobjekte zusammen. MISC *bag -> Struktur unseres Datenobjektes int nr -> Indexzähler für gemischtes Array int Tag -> Tagkennung (siehe enum) void *adress -> Adresse des gemischten Objektes int (*function)() -> Adresse der Funktion */ void pack(MISC *bag, int nr, int Tag, void *adress, int (*function)()) { bag[nr].tag = Tag; bag[nr].obj = adress; bag[nr].func = function; } int zs(ZS *char_string) { printf("Funktion für Zeichen und String aufgerufen\n"); printf("Zeichen : %c , String : %s\n",char_string->data,char_string->zs); return 0; } int sl(SL *str_long) { printf("Funktion für String und Long aufgerufen\n"); printf("String : %s , Long : %d\n",str_long->sl,str_long->data); return 0; } int dsd(DSD *dstrd) { printf("Funktion für Double String Double aufgerufen\n"); printf("Double : %f , String : %s , Double : %f\n", dstrd->data_a,dstrd->dsd,dstrd->data_b); return 0; } int is(IS *intstr) { printf("Funktion für Integer und String aufgerufen\n"); printf("Integer : %d , String : %s\n",intstr->data,intstr->is); return 0; } int dummy() { return 0; } int main() { ZS za = {'x', "String für ZS" }; SL sa = {"String für SL" , 123456 }; DSD da = {1.2345, "String für DSD", 6.54321}; IS ia = {55 , "String für IS"}; int (*tag_func[])() = {dummy,zs,sl,dsd,is}; MISC Pack[4] ={0}; int i; pack(Pack, 0, ZS_TAG, &za, tag_func[ZS_TAG]); pack(Pack, 1, SL_TAG, &sa, tag_func[SL_TAG]); pack(Pack, 2, DSD_TAG,&da, tag_func[DSD_TAG]); pack(Pack, 3, IS_TAG, &ia, tag_func[IS_TAG]); for(i=0; i<4; i++) (*Pack[i].func)(Pack[i].obj); return 0; }Hoffe es hilft euch und ihr könnt dann mir helfen

-
Argh, das ist ein Hack.
Das "geht" deswegen weil alle Funktionen ein "T*" Parameter nehmen, bis auf eine, die garkein Parameter nimmt.In C++ könntest du das schon auch machen, bloss wäre die "saubere" Version dann gleich allen Funktionen die gleiche Signatur zu verpassen, also z.B. "int f(void*)".
Den void* müssen sich die Funktionen intern dann einfach auf das casten was sie als Parameter erwarten.
-
@hustbaer:
Könntest du mir bitte einen Beispiel-Code posten? Wäre dir sehr verbunden.Mein Problem ist nämlich, dass ich eine DLL dynamisch einbinde (über LoadLibrary und Aufruf der DLL-Funktionen per Pointer).
Das heißt, ich hab bei jeder Funktion fast komplett identischen Code nur andere Typen.
Hier ein Beispiel:// DPR_STRING ... char // DP_ERROR_T ... Error-Structure // DPR_DWORD .... unsigned long typedef DPR_DWORD DLLFUNC_DP_START_CP(DPR_STRING*, DPR_STRING*, DP_ERROR_T*); typedef DPR_DWORD DLLFUNC_DP_OPEN(DPR_STRING*, DPR_DWORD*, DP_ERROR_T*); int __fastcall dll_dp_start_cp( char *dll_name_path, char *database, DP_ERROR_T *str_probuserr, long *err) { HINSTANCE hInstance; DLLFUNC_DP_START_CP *p_start_cp = NULL; if(dll_name_path == 0) { return (-1); } hInstance = LoadLibrary(dll_name_path); if(hInstance == 0) { return (-2); } else { #ifdef USED_IN_FIRMWARE p_start_cp = (DLLFUNC_DP_START_CP*)::GetProcAddress(hInstance, "_DP_start_cp"); #else p_start_cp = (DLLFUNC_DP_START_CP*)::GetProcAddress(hInstance, "DP_start_cp"); #endif if(p_start_cp != NULL) { (*err) = p_start_cp(CP_NAME, database, str_probuserr); FreeLibrary(hInstance); return (0); } FreeLibrary(hInstance); return (-3); } } int __fastcall dll_dp_open( char *dll_name_path, DPR_DWORD *glb_dpdUserHandle, DP_ERROR_T *str_probuserr, long *err) { HINSTANCE hInstance; DLLFUNC_DP_OPEN *p_open = NULL; if(dll_name_path == 0) { return (-1); } hInstance = LoadLibrary(dll_name_path); if(hInstance == 0) { return (-2); } else { #ifdef USED_IN_FIRMWARE p_open = (DLLFUNC_DP_OPEN*)::GetProcAddress(hInstance, "_DP_open"); #else p_open = (DLLFUNC_DP_OPEN*)::GetProcAddress(hInstance, "DP_open"); #endif if(p_open != NULL) { (*err) = p_open(CP_NAME, glb_dpdUserHandle, str_probuserr); FreeLibrary(hInstance); return (0); } FreeLibrary(hInstance); return (-3); } }Und ich will dieses Prozedere nicht 28 mal (oder zukünftig vllt. noch öfter, sollten mehr hinzukommen) machen, sondern ich einer Funktion zusammenfassen. Hoffentlich kann mir jemand helfen

-
Kannst du das nicht mit Hilfe einer Template-Wrapper-Funktion (für den GetProcAdress Aufruf und den Code drumherum) lösen?
Ungefähr so:
template< typename FuncDeclaration, typename Arg1_t, typename Arg2_t > int __fastcall dll_dp_routine( char* dll_name_path, char* func_name Arg1_t* arg1, Arg2_t* arg2, long* err ) { HINSTANCE hInstance; DLLFUNC_DP *p_func = NULL; if(dll_name_path == 0) { return (-1); } hInstance = LoadLibrary(dll_name_path); if(hInstance == 0) { return (-2); } else { #ifdef USED_IN_FIRMWARE p_func = (FuncDeclaration*)::GetProcAddress(hInstance, func_name); #else p_func = (FuncDeclaration*)::GetProcAddress(hInstance, func_name); #endif if(p_func != NULL) { (*err) = p_func(CP_NAME, arg1, arg2); FreeLibrary(hInstance); return (0); } FreeLibrary(hInstance); return (-3); } }Ist jetzt nur kurz zusammengehacked, aber so in etwa könntest du das machen und als ersten parameter den Prototyp angeben, z.B. so:
dll_dp_routine< int(char*,char*,long* >(/.../);
-
Vielen Dank für die Hilfe. So denke ich würd es gehn, hab zwar noch nie ein Template-Wrapper-Funktion benutzt aber naja. Hoffe das klappt.
Muss ich dann bei dieser Template-Wrapper-Funktion soviele Typen angeben wie ich Funktionen habe (28) und auch genauso viele beim Funktionsaufruf übergeben?
//EDIT
Ich habe jetzt ein wenig herumprobiert aber bin nicht wirklich auf ein Ergebnis gekommen
Kann mir irgendjemand einen genaueren Code posten, der sich an meinen geposteten Code anlehnt? Und wenn möglich vllt. noch einen Link zu diesen Template-Wrapper.
Danke schonmal!
-
Ich habe mich inzwischen ein wenig mit Templates befasst und verstehe jetzt den Code großteils. Hier nochmal der Code:
typedef unsigned long DPR_DWORD; typedef char DPR_STRING; typedef struct DP_ERROR_T { int dummy; }DP_ERROR_T; typedef DPR_DWORD DLLFUNC_DP_START_CP(DPR_STRING*, DPR_STRING*, DP_ERROR_T*); typedef DPR_DWORD DLLFUNC_DP_OPEN(DPR_STRING*, DPR_DWORD*, DP_ERROR_T*); template<typename FunctionDeclaration, typename arg1_t, typename arg2_t> int __fastcall dll_dp_function( char *dll_name_path, char *func_name, arg1_t *arg1, arg2_t *arg2 ) { HINSTANCE hInstance; DLLFUNC_DP *p_func = NULL; if(dll_name_path == 0) { return (-1); } hInstance = LoadLibrary(dll_name_path); if(hInstance == 0) { return (-2); } else { #ifdef USED_IN_FIRMWARE p_func = (FunctionDeclaration*)::GetProcAddress(hInstance, func_name); #else p_func = (FunctionDeclaration*)::GetProcAddress(hInstance, func_name); #endif if(p_func != NULL) { (*err) = p_func(CP_NAME, arg1, arg2); FreeLibrary(hInstance); return (0); } FreeLibrary(hInstance); return (-3); } }Was ich nicht verstehe:
- DLLFUNC_DP (welcher Typ ist das?)
- Mir ist nicht ganz klar, wie das mein Problem lösen soll? (mehrere Funktion mit untersch. Paremetern und untersch. Parameteranzahl)Hoffe es ist noch jemand da, der mir helfen kann.
-
[cynicism]
Äh. 28 mal? Soooooo oft?
[/cynicism]Wieso lädst du die DLL jedes mal neu?
Und wenn es schon sein muss, dann verwende vielleicht eine RAII Klasse, nen?BTW: DLLFUNC_DP ist IMO Blödsinn und muss FunctionDeclaration heissen in dem Code.
-
Ich hab mir gedacht, dass es besser wäre, wenn ich die DLL immer wieder neu lade und dann wieder freigebe (speichermäßig betrachtet). Ich kann sie natürlich auch nur einmal laden - bin auf diesem Sektor nicht so erfahren, was ist denn besser?
Und was mir immer noch nicht ganz klar ist, wie deine Lösung das Problem der untersch. Anzahl der Parameter löst?
-
Welche Lösung?
Ich hatte anhand deiner nicht-auf-dein-Problem-passenden Beschreibung angenommen dass du etwas ganz anderes machen willst als du machen willst.Aber zurück zum DLL laden: es ist besser wenn du die nur 1x lädst und nichtmehr freigibst, das wird das Working Set (den "Speicherverbrauch") deiner Applikation sogar vermutlich etwas kleiner halten.
Was den Rest angeht... das kann man wie gesagt relativ einfach über Templates lösen. Wenn du mit Templates nicht gut genug umgehen kannst, kannst du es aber auch mit typedefs machen:
typedef int (*BlahFunctionPtr)(int, char*, Foo*); void f() { HMODULE dllHandle = ...; BlahFunctionPtr bp = reinterpret_cast<BlahFunctionPtr>(::GetProcAddress(dllHandle, "Blah")); (*bp)(...); // "Blah" aufrufen }Musst du auch nur 1x den ganzen Funktionstypen Schmarren tippen, und 1x brauchst du den sowieso-unbedingt-geht-nicht-ohne.
Die Funktionszeiger holst du dir dann üblicherweise auch nur 1x beim Laden der DLL, und verwendest die ansonsten bloss.Wenn du die DLL selbst schreibst ist die wesentlich einfachere Lösung aber eine einzige Funktion zu exportieren die einen Zeiger auf eine rein abstrakte Klasse (=nur virtual pure Funktionen) zurückgibt. Über die kannst du die Funktionen dann aufrufen ohne immer blöd mit Funktionszeigern rumhantieren zu müssen.