Forum: Compiler & IDEs dynamische Funktionsaufrufe in c


von Reiner W. (reiner_w)


Lesenswert?

Hallo in die Runde,
ich beschäftige mich seit geraumer Zeit mit den Cypress PSoC Chips, aber 
ich denke, meine Frage ist eher eine allgemeine C-Frage (wo ich noch 
nicht allzulange zu Hause bin), für die ich einfach keine Lösung finde.

Zum konkreten Problem:
Der PSoC Creator erzeugt zur einfachen Ansteuerung von Pins bei der 
Kompilierung eine entsprechende API.
Da gibt es dann diverse Befehle zum schreiben/lesen etc.
Die Syntax ist recht einfach. PinName_Funktion()
Pin1_Write(1) - setze Pin1 auf 1 na usw.
Jetzt hab muss ich das gleiche Bitmuster an verschiedene Pins ausgeben. 
Dafür hab ich Funktionen.
1
void Bitmuster_an_Pin1 (void) {
2
    Pin1_Write(1);   // 1 auf Pin
3
    //tu noch andere Sachen
4
    Pin1_Write(0);   // 0 auf Pin   
5
}

Die gleiche Funktion hab ich dann noch für 3 andere Pins.

Was ich suche ist eine Möglichkeit, den API-Funktionsnahme dynamisch zu 
erzeugen.
Etwa so:
1
//reiner Pseudocode
2
void Bitmuster_an_PinX (char PinNr) {
3
    API_Befehl = "Pin"+PinNr+"_Write  
4
    API_Befehl(1);                   
5
    
6
    //tu noch andere Sachen
7
    
8
    API_Befehl(0);  
9
}
Mit Makroexpansion bekomme ich es nicht hin:

#define Pin(i) Pin##i

expandiert ja nur wenn beim Aufruf i als Zahl benutzt wird.

uint8_t i = 1;
Pin(i)()  -> Fehler
Pin(1)()  -> expandiert zu Pin1()

Natürlich kann ich in eine Funktion mit switch oder case arbeiten, aber 
das wird bei vielen Pins und API_Funktionen
schon sehr unübersichtich.

Ich hoffe, ich habe mich einigermaßen verständlich ausgedrückt.
Vlt. kann mich jemand in die richtige Richtung schupsen.

von Daniel A. (daniel-a)


Lesenswert?

Reiner W. schrieb:

> Der PSoC Creator erzeugt
> Pin1_Write(1) - setze Pin1 auf 1 na usw.
>Was ich suche ist eine Möglichkeit, den API-Funktionsnahme dynamisch zu
>erzeugen.

Wenn dieser PSoC Creator keine funktion zum setzen des n-ten pins, oder 
mehrerer pins hat, würde ich den gleich weglassen und selbst die 
entsprechende funktion schreiben. In C kann man keine funktion über 
einen zur runtime zusammengesetzten namen aufrufen, es sei denn man 
speichert funktionsname und funktionspointer in einer liste. Mit 
preprocessing ist es auch nicht möglich, weil das simple textersetzung 
ist.

Man könnte im notfall Arrays verwenden: (code ungetestet)
1
#define PinMacro(i) Pin ## i ## _Write
2
#define PinListItem(i) &PinMacro(i)
3
#define PinListMacro1() PinListItem(1)
4
#define PinListMacro2() PinListMacro1(), PinListItem(2)
5
#define PinListMacro3() PinListMacro2(), PinListItem(3)
6
#define PinListMacro4() PinListMacro3(), PinListItem(4)
7
#define PinListMacro5() PinListMacro4(), PinListItem(5)
8
#define PinListMacro6() PinListMacro5(), PinListItem(6)
9
#define PinListMacro7() PinListMacro6(), PinListItem(7)
10
#define PinListMacro8() PinListMacro7(), PinListItem(8)
11
#define PinListMacro(n) PinListMacro ## n()
12
#define SetPin(i,b) (*(SetPinList[i-1]))(b)
13
14
typedef void(*PinSetter_t)(char);
15
16
// liste
17
PinSetter_t SetPinList[] = {
18
  PinListMacro(8)
19
};
20
21
// beispiel
22
void doSomething(){
23
  int i = 1; // pin nummer
24
  SetPin(i,1);
25
}

Wobei switch/case sicher effizienter sind...

von Klaus W. (mfgkw)


Lesenswert?

Evtl. helfen dir Funktionszeiger weiter:
1
#include <stdio.h>
2
3
void Pin1_Write( int wert )
4
{
5
  printf( "Schreibe auf Pin   1: %d\n", wert );
6
}
7
8
void Pin2_Write( int wert )
9
{
10
  printf( "Schreibe auf Pin   2: %d\n", wert );
11
}
12
13
// ...
14
15
void Pin999_Write( int wert )
16
{
17
  printf( "Schreibe auf Pin 999: %d\n", wert );
18
}
19
20
21
// Typ PinXXX_Write_t: Zeiger auf Funktion, die eine int bekommt und
22
// nichts zurückliefert
23
typedef void (*PinXXX_Write_t)( int wert );
24
25
// davon ein ganzes Feld: an Position [0] steht ein Zeiger auf die
26
// Funktion, die Pin 1 auf 0 oder 1 setzt, an [1] steht die Funktion,
27
// die selbiges für Pin 2 macht etc.
28
PinXXX_Write_t  PinXXX_Write_array[] =
29
  {
30
    Pin1_Write,
31
    Pin2_Write,
32
    // ...
33
    Pin999_Write,
34
  };
35
36
37
// Gibt auf Pin Nummer PinNr erst eine 1 aus, dann eine 0:
38
void mein_tolles_Bitmuster_an_PinX (char PinNr)
39
{
40
  PinXXX_Write_array[PinNr-1]( 1 ); // PinNr auf 1
41
  // ...
42
  PinXXX_Write_array[PinNr-1]( 0 ); // PinNr auf 0
43
}
44
45
46
47
int main( int nargs, char **args )
48
{
49
  mein_tolles_Bitmuster_an_PinX( 2 ); // Pin 2 auf 1 und dann auf 0 setzen
50
  return 0;
51
}

Das kann man (um Schreibarbeit zu sparen) mglrws. noch mit Makros 
kombinieren:
1
#include <stdio.h>
2
3
#define generate_PinXWrite(i)                             \
4
void Pin##i##_Write( int wert )                           \
5
{                                                         \
6
  printf( "Schreibe auf Pin   " #i ": %d\n", wert );      \
7
}
8
9
generate_PinXWrite(1)
10
generate_PinXWrite(2)
11
//...
12
generate_PinXWrite(999)
13
14
15
16
// Typ PinXXX_Write_t: Zeiger auf Funktion, die eine int bekommt und
17
// nichts zurückliefert
18
typedef void (*PinXXX_Write_t)( int wert );
19
20
// davon ein ganzes Feld: an Position [0] steht ein Zeiger auf die
21
// Funktion, die Pin 1 auf 0 oder 1 setzt, an [1] steht die Funktion,
22
// die selbiges für Pin 2 macht etc.
23
PinXXX_Write_t  PinXXX_Write_array[] =
24
  {
25
    Pin1_Write,
26
    Pin2_Write,
27
    // ...
28
    Pin999_Write,
29
  };
30
31
32
// Gibt auf Pin Nummer PinNr erst eine 1 aus, dann eine 0:
33
void mein_tolles_Bitmuster_an_PinX (char PinNr)
34
{
35
  PinXXX_Write_array[PinNr-1]( 1 ); // PinNr auf 1
36
  // ...
37
  PinXXX_Write_array[PinNr-1]( 0 ); // PinNr auf 0
38
}
39
40
41
42
int main( int nargs, char **args )
43
{
44
  mein_tolles_Bitmuster_an_PinX( 2 ); // Pin 2 auf 1 und dann auf 0 setzen
45
  return 0;
46
}

von Reiner W. (reiner_w)


Lesenswert?

Klaus Wachtler schrieb:
> Evtl. helfen dir Funktionszeiger weiter:

Genauso hab ich es im Prinzip gemacht;-) Auch wenn dein Code 
aufgeräumter aussieht. Aber da feile ich noch dran.
Komme aus alten seligen dBase Zeiten und da konnte man ja zur Laufzeit 
ne Markosubstitution einfach mit

x="test()"
y = &x

machen. Aber man kann nicht alles haben.

Da die Funktionen als API ja alle schon konkret vorliegen, brauch ich ja 
nur noch ein Array mit den Funktionspointern anlegen.
Ist halt blöd, wenn da mal ein Pin dazukommt, da muss man immer dran 
denken, dass array zu erweitern.
Außerdem sind die API-Funktionen noch nicht die ganze Wahrheit, da gibt 
es auch noch Konstanten (z.B. der Pin-Drive-Mode), die nach dem selben 
Schema benannt sind. Das bedeutet schon eine zweite Dimension im Array, 
wenn ich eine solche Konstante in meiner (allgemeinen) Funktion brauche. 
Dennoch ist das besser und sicher auch platzsparender als 8 Funktionen, 
die im Grunde gleich sind.

Daniel A. schrieb:
> #define PinListItem(i) &PinMacro(i)

Die Zeile kann ich noch nicht ganz nachvollziehen. Was macht das & ?
Bin leider noch nicht so firm in der C-Programmierung.

Danke schon mal für eure Hilfe. Ist ja schon mal beruhigend zu wissen, 
dass  ich nicht von vorn anfangen muss, nur weil ich den Wald vor lauter 
Bäumen nicht gesehen habe.

von Klaus W. (mfgkw)


Lesenswert?

Reiner W. schrieb:
> Ist halt blöd, wenn da mal ein Pin dazukommt, da muss man immer dran
> denken, dass array zu erweitern.

In solchen Fällen kann man darüber nachdenken, Quelltext mit einem 
Präprozessor zu generieren (m4, awk, sed, ...).
Wenn man eh über makefiles oder ähnliches kompiliert, ist das leicht 
eingebaut.

Wenn sich etwas ändert, wird eine Konfigurationsdatei angepasst, und 
nach einem make passt alles.

von Reiner W. (reiner_w)


Lesenswert?

Klaus Wachtler schrieb:
> In solchen Fällen kann man darüber nachdenken, Quelltext mit einem
> Präprozessor zu generieren (m4, awk, sed, ...).

Gute Idee, im konkreten Fall lohnt sich aber ein Quellcodegenerator 
sicher nicht. Und in so einem Fall muss man wieder höllisch aufpassen, 
dass die PinNamen einer Regel folgen. Pin1, Pin2..Pinx. Aber das sollten 
sie sowieso tun.

von Daniel A. (daniel-a)


Lesenswert?

Reiner W. schrieb:
> Daniel A. schrieb:
>> #define PinListItem(i) &PinMacro(i)
>
> Die Zeile kann ich noch nicht ganz nachvollziehen. Was macht das & ?

Es gibt den funktions-pointer auf die Funktion zurück. Da eine funktion 
immer auch ein funktionspoiner ist, ist es optional. Ich verwende es aus 
kosmetischen und logik gründen. Wichtig ist, dass wenn man ein x=&func 
verwendet, später (*func)() für den aufruf verwendet wird, und bei 
x=func der aufruf immer mit x() erfolgt.

Funktion und Funktionspointer sind eben rein logisch betrachtet (nicht 
praktisch) nicht das selbe.
1
void x(){} // void(&){}   funktion
2
void(*y)()=&x; // funktionspointer
3
void(**z)()=&y; // objekt-pointer auf funktionspointer

: Bearbeitet durch User
von Reiner W. (reiner_w)


Lesenswert?

Daniel A. schrieb:
> Es gibt den funktions-pointer auf die Funktion zurück

Ja klar, das Brett vorm Kopf ist weg;-)
Danke

von Sven B. (scummos)


Lesenswert?

Wo ist der Vorteil gegenüber dem die Pin-Nummer einfach als Argument zu 
übergeben?

von Reiner W. (reiner_w)


Lesenswert?

Sven B. schrieb:
> Wo ist der Vorteil gegenüber dem die Pin-Nummer einfach als Argument zu
> übergeben?

Ich weis jetzt nicht was du meinst. Das Problem hab ich ja schon bei her 
TE geschildert. Die PinNummer als Argument geht nicht, weil ich über 
dieses Argument nicht mehr auf den Funktion komme, es sei denn, ich 
nehme die PinNummer als Array-Index und im Array stehen die 
Funktionspointer. Das geht ganz gut, erfordert aber, dass im Vorfeld das 
Array mit den Funktionspointern angelegt wurde, wie ja Klaus Wachtler 
perfekt als Beispiel gezeigt hat.
Wenn ich es richtig sehen, läuft der Vorschlag von Daniel letztlich auch 
darauf hinaus, setzt aber den Präprozessor intensiv ein.
Ich weis jetzt nicht, ob du da noch eine andere Möglichkeit siehst und 
wie die aussehen könnte.

von Sven B. (scummos)


Lesenswert?

Ok, sorry, ich habe das Problem falsch verstanden. Vergiss was ich sagte 
:)

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.