2013-04-24 6 views
9

Ich stoße immer wieder auf das Problem, Signalhandler im GTK + -Code einzurichten, ohne mehrere Parameter zu benötigen und die gleiche Funktion wie der Handler für mehrere Signale zu verwenden, deren Handler unterschiedliche Signaturen haben - aber mit dem ersten N Argumente (die mir wichtig sind) gleich.Ist es sicher, eine C-Funktion mit mehr Argumenten aufzurufen, als sie erwartet?

Ist es sicher (in dem Sinne, dass es kein undefiniertes Verhalten ist, eher als der pragmatische Sinn von "funktioniert es auf meinem PC?") Zeiger auf Funktionen an die GObject API zu übergeben, wenn diese Funktionen weniger erwarten Argumente, als sie tatsächlich aus dem Signalemissionsprozess erhalten werden?

Oder, um sich von GTK + zu trennen, ist dieser Code okay?

Für zusätzliche Kredit, diskutieren die Auswirkungen der verschiedenen Rückgabewerttypen zwischen Funktion Signatur und Call-Site.

Edit: Ein almost-identical question Sonden „kompatibel Typ Funktion“ enger und zieht an answer directly addressing my concrete problem - das GObject Signal-Handler von Verkettungs. TL; DR: Ja, es ist undefiniertes Verhalten, aber es ist praktisch idiomatisch (obwohl nicht obligatorisch) in einigen Toolkits.

+0

Ich bin fast sicher, dass die Konvertierung des Funktionszeigers auf diese Weise UB ist. – Flexo

+3

@Flexo Die Konvertierung des Funktionszeigers ist in Ordnung. Verwenden Sie es für etwas anderes als die Konvertierung zurück, auf der anderen Seite, ... –

+0

BTW, keine Notwendigkeit für '(* Fn)' in do_stuff. Rufen Sie einfach 'fn (total, userdata)' auf. Da 'fn' ein Funktionszeiger ist, erfüllt der Funktionsaufrufoperator' (total, userdata) genau das, was Sie erwarten. – Jens

Antwort

8

Es ist ausdrücklich nicht definiertes Verhalten, pro 6.5.2.2, Absatz 9:

Wenn die Funktion mit einem Typ definiert ist, die nicht kompatibel mit dem Typ (des Ausdrucks) ist auf dem durch den Ausdruck, bezeichnet die aufgerufene Funktion, das Verhalten ist nicht definiert.

der Art, die show mit definiert ist,

void show(int x) 

ist mit der Art des Ausdrucks nicht kompatibel, auf den durch den Zeiger, durch die es, genannt

void (*fn)(int, void *) 

auch explizit in 6.3.2.3, Absatz 8:

Ein Punkt äh zu einer Funktion eines Typs kann in einen Zeiger auf eine Funktion eines anderen Typs und wieder umgekehrt werden; Das Ergebnis muss mit dem ursprünglichen Zeiger verglichen werden. Wenn ein konvertierter Zeiger verwendet wird, um eine Funktion aufzurufen, deren Typ nicht mit dem referenzierten Typ kompatibel ist, ist das Verhalten nicht definiert.

Für Funktionen "kompatibeler Typ" in 6.7.6.3 gekennzeichnet ist, (15) [6.7.5.3 (15) in C99]:

Für zwei Funktionstypen kompatibel zu sein, sind beide Spezifizieren Sie kompatible Rückgabetypen. Darüber hinaus müssen die Listen der Parametertypen, wenn beide vorhanden sind, in der Anzahl der Parameter und in der Verwendung des Auslassungsabbrechers übereinstimmen; entsprechende Parameter müssen kompatible Typen haben. Wenn ein Typ eine Parametertypliste und der andere Typ von einem Funktionsdeklarator angegeben wird, der nicht Teil einer Funktionsdefinition ist und eine leere Bezeichnerliste enthält, darf die Parameterliste keinen Auslassungsausschluss und den Typ jedes Parameters enthalten kompatibel mit dem Typ sein, der sich aus der Anwendung der Standard-Argument-Promotions ergibt.Wenn ein Typ eine Parametertypliste hat und der andere Typ ist, der durch eine Funktionsdefinition spezifiziert wird, die eine (möglicherweise leere) Identifikatorliste enthält, müssen beide in der Anzahl der Parameter übereinstimmen, und der Typ jedes Prototypparameters muss mit dem Typ kompatibel sein Typ, der sich aus der Anwendung der Standard-Argument-Promotions auf den Typ des entsprechenden Bezeichners ergibt. (Bei der Bestimmung der Typkompatibilität und eines zusammengesetzten Typs wird jeder mit der Funktion oder dem Array-Typ deklarierte Parameter als angepasster Typ angenommen und jeder mit dem qualifizierten Typ deklarierte Parameter wird als nicht qualifizierte Version des deklarierten Typs betrachtet.)

Der direkteste Teil hier ist, dass die Anzahl der Argumente gleich sein muss.

+0

Howd hast du so schnell gesucht? kennst du den Standard an deinen Fingerspitzen? :-) –

+0

Nicht so gut, aber ich wusste, wo ich in diesem Fall nachschauen sollte. –

+0

Ihre Antwort half mir, die andere Hälfte der Antwort zu finden - die Bedeutung von "kompatiblen Typen" in Bezug auf Funktionen. Möchtest du es hier hinzufügen, bevor ich das akzeptiere? Anscheinend ist es 6.7.5.3 §15. –

-1

lesen http://www.unixwiz.net/techtips/win32-callconv-asm.html und http://www.csee.umbc.edu/~chang/cs313.s02/stack.shtml Scheint, wenn Sie mehr weitergeben, sollte es kein Problem verursachen, weil zuerst die Parameter gedrückt werden. Was immer nicht benötigt wird, bleibt im Aufrufer-Stack. Aber der umgekehrte Weg wird sicherlich Probleme verursachen.

+0

Das kann bei einigen Implementierungen passieren, aber es ändert nicht, dass es ein undefiniertes Verhalten nach dem Standard ist. – Flexo

+0

Es kann in Ordnung sein, wenn der Aufrufer den Stapel räumt, aber was ist mit dem Aufgerufenen - wird er aufgrund des übergroßen Parameterstapelrahmens keine falschen Parameter abrufen? –

+0

Wenn die Ordnungen innerhalb der korrekten Parameter korrekt sind, liegen die restlichen Parameter tatsächlich unterhalb dieser Parameter im Stapelrahmen unter der Rückkehradresse unterhalb des Basiszeigers. Wenn also der Angerufene die Parameter liest, liest er diejenigen vor, die er lesen soll, Ruhe liegt einfach unberührt da. Aber unter denen, die der Angerufene lesen soll, wenn etwas nicht stimmt, wird es ein Problem geben. – abasu

2

Dies ist ein undefiniertes Verhalten im C-Standard, aber der C-Standard erfordert auch die Unterstützung für Funktionen mit variadischen Argumenten, und die einzige Möglichkeit, wie Compiler letztere implementieren, besteht darin, erstere zuzulassen.

Diese spezielle idiomatische Form wird von jedem Compiler und jeder Plattform unterstützt, die von GLib unterstützt werden, und das schon seit vielen Jahren - noch bevor GLib erstellt wurde - also ist es höchst unwahrscheinlich, dass es jemals kaputt gehen wird.

+0

Kontrapunkt: Vor 20 Jahren benutzte ich MS QuickC, was man sagen könnte, um die "pascal" Aufrufkonvention zu verwenden (umgekehrte Reihenfolge der Argumente auf dem Stapel - erstes Argument zuerst und nicht zuletzt). In einem solchen Fall würde die Anzahl der Argumente falsch sein. Natürlich kann ein Toolkit die Plattformen, auf denen dies ein Problem ist, frei definieren und nur diejenigen unterstützen, bei denen dies nicht der Fall ist. –

+0

Also hast du einem * sehr * alten Compiler auf einer Plattform erzählt, die in keiner Weise von GLib unterstützt wird, Jahre bevor GLib überhaupt daran gedacht wurde, etwas zu tun, das eine andere Sprache emuliert und die Dinge kaputt gingen. das wäre ein Problem ... wie? :-) – ebassi

Verwandte Themen