Hallo zusammen,
mich interessiert, wie ihr eure Projekte organisiert, und um genau zu
sein: Wie ihr "static inline"-Funktionen organisiert.
David Kieras empfiehlt in den "C Header File Guidelines" (siehe hier:
http://www.umich.edu/~eecs381/handouts/CHeaderFileGuidelines.pdf )
niemals Funktionen in *.h-Dateien zu tun, sondern in *.inc-Dateien.
IDEs wie Visual Studio oder Eclipse *) kennen diese Endung allerdings
nicht, was dann dazu führt, daß ihr Komfort deutlich gemindert wird,
weil Funktionen wie "bei Fehlermeldung direkt in die richtige Zeile
springen" oder Refaktoring/Umbenennen fehlen.
Desweiteren kann man entweder das berühmte:
1
staticinline__attribute__((always_inline))
vor jede Funktion schreiben, was der Lesbarkeit nicht wahnsinnig dient,
oder in jede Datei mit solchen Funktionen einen Header einbinden, der
ein Kurzsymbol definiert.
Deswegen: Wie organisiert ihr static inline-Funktionen in einem Projekt?
Viele Grüße
W.T.
*) hier läßt es sich allerdings mit wenigen Handgriffen nachrüsten
Wenn dir alle anderen Regeln bekannt (und begründet) vorkommen, dann ist
diese letzte ehe ein formales Problem. Ist diese "always inline"
Funktion Definition oder Implementierung?
Formal beides, pragmatisch (bei mir) nur ersteres, wenn aber überhaupt,
dann würde ich ".inl" vorziehen. ".inc" ist eher das
Assembler-Äquivalent zum ".h".
Formal gehört alles ins *.h File, was keinen Code oder RAM verbraucht.
Inline Funktionen gehören also ins *.h.
Inline Funktionen können auch in mehreren Objekten benutzt werden und
dazu müssen diese dann das *.h includieren.
Walter T. schrieb:> sondern in *.inc-Dateien.
Nein, das bezieht er ausdrücklich nur auf C-Code, der aus irgendeinem
Grund in anderen C-Code direkt importiert werden soll. Sowas kann
einem beispielsweise dienlich sein, wenn man den Inhalt eines
statischen Arrays extern durch ein Tool generieren lassen will:
1
staticuint8_tlookup_table[LOOKUP_TABLE_SIZE]={
2
# include "lookuptable.inc"
3
};
lookuptable.inc wird dabei durch einen Perl- oder Pythonscript oder
etwas ähnliches (über die Build-Infrastruktur, also Makefile oder
Scons oder was auch immer) generiert. In diesem Falle sollte man
das aber nicht "lookuptable.c" nennen, da es eben keine eigenständig
compilierbare Datei ist.
Über inline-Funktionen hat er sich nicht ausgelassen in seinem
Schriebs. Ich würde sie, obwohl sie bei ihrer Anwendung in der Tat
Code generieren, dennoch im normalen Headerfile lassen. Alles andere
ist meiner Meinung nach widersinnig.
Exkursion in C++: es käme doch auch keiner auf die Idee, in einer
Klasse inline implementierte Methoden in separate Dateien auszulagern:
1
classfoo{
2
private:
3
intpercent;
4
5
public:
6
foo(void);
7
8
intget_percent{returnpercent;}
9
voidset_percent(intnval){
10
if(nval>=0&&nval<=100)
11
percent=nval;
12
}
13
};
Die typischen “getter”- und “setter”-Methoden (get_percent(),
set_percent() in diesem Falle) werden sehr oft in dieser Form als
inline-Implementierungen realisiert.
Jörg W. schrieb:> Exkursion in C++: es käme doch auch keiner auf die Idee, in einer> Klasse inline implementierte Methoden in separate Dateien auszulagern:
Doch, dass kann Sinn machen, wenn Du das template nur für eine
überschaubare Menge von Parameters benötigst. Dann kannst Du die in
einer separaten Übersetzungseinheit explizit instanziieren und nur dort
brauchst Du dann die Funktionsdefinitionen.
@Walter: Vielleicht wäre ein C++ compiler auch eine Lösung für Dich?
Dort kannst Du Funktionen als `inline` deklarieren:
Torsten R. schrieb:> Dort kannst Du Funktionen als `inline` deklarieren:
Was wäre dabei gegenüber einem C-Compiler anders?
Damit der Compiler die Funktion inline erweitern kann, muss er
zwangsläufig ihren Inhalt kennen. Das geht nur, indem man sie in
einem Headerfile komplett hinterlegt.
Jörg W. schrieb:> Torsten R. schrieb:>> Dort kannst Du Funktionen als `inline` deklarieren:>> Was wäre dabei gegenüber einem C-Compiler anders?
Ah, ich dachte für `inline` in C wären Compiler-Erweiterungen nötig.
Ignoriert meinen Vorschlag! ;-)
Torsten R. schrieb:> Ah, ich dachte für `inline` in C wären Compiler-Erweiterungen nötig.
Seit C99 nicht mehr (also seit ca. 15 Jahren).
Deshalb gibt's ja Walters Frage überhaupt erst …
Walter T. schrieb:> IDEs wie Visual Studio oder Eclipse *) kennen diese Endung allerdings> nicht,
Manchmal muss man sich den Realitäten beugen. Dazu gehört auch, dass man
sich nicht-standard Konfigurationen von IDEs gut überlegen sollte. Wenn
man sich den Realitäten beugt, dann bleibt .c oder .h.
.h bietet sich eher an. Bei ganz großen Projekten mit vielen
Inline-Funktionen kann man eine Konvention einführen, dass man normale
Header und Inline-Header nochmal trennt. Entweder durch ein
Unterverzeichnis
1
#include"api.h" // Ein bestimmtes API
2
#include"inline/api.h" // Die Inline-Funktionen des API
oder eine Namenskonvention:
1
#include"api.h" // Ein bestimmtes API
2
#include"api.in.h" // Die Inline-Funktionen des API
Beides aber wirklich nur wenn Inline-Funktionen überhand nehmen und man
sonst den Durchblick verliert. Ansonsten
1
#include"api.h" // API mit Inline-Funktionen
> Desweiteren kann man entweder das berühmte:static inline> __attribute__((always_inline))> vor jede Funktion schreiben, was der Lesbarkeit nicht wahnsinnig dient,> oder in jede Datei mit solchen Funktionen einen Header einbinden, der> ein Kurzsymbol definiert.
Das hängt vom Aufdensackgehfaktor ab. Irgendwann ist der >= 10, dann
gibt es ein #define INLINE ... oder ähnliches.
Ich wäre übrigens mit __attribute__((always_inline)) vorsichtig.
Normalerweise bekommt der Compiler ganz gut raus, ob es sich wirklich
lohnt, die Funktion inline zu erweitern oder nicht.
Danke für die Antworten.
Jörg W. schrieb:> Ich wäre übrigens mit __attribute__((always_inline)) vorsichtig.
Danke, das bin ich :-).
Mein Code ist ein wenig gespickt mit dem Aufruf von Funktionen wie:
die in jedem Bitbanging-Treiber zigfach aufgerufen werden. Sobald es
eine einzelne Stelle gibt, wo die Funktion mit einer Variablen anstelle
einer Konstanten aufgerufen wird, macht mir die Optimierung -S einen
Strich durch die Rechnung und auch an allen anderen Stellen keinen
einfachen SBI/CBI mehr. Diese und ähnliche Funktionen sind allerdings
auch die einzigen "static inline" im ganzen Projekt.
Ich hatte die Aufteilung in .c und .h-Dateien auch immer als Trennung
von Schnittstelle und Implementierung verstanden. Nach dieser Logik
hätten "static inline"-Funktionen eindeutig ihren Platz in der .c-Datei
(was eben nur technisch nicht geht). So hatte ich auch den Vorschlag mit
den .inc-Dateien gelesen.
Aber eure Vorgehensweise ergibt natürlich auch einen Sinn: Man muß
nichts verbiegen, ärgert sich nicht über fehlende Unterstützung in der
Programmierumgebung und es funktioniert einfach.
Ich werde das also wieder zurückändern.
Viele Grüße
W.T.
Walter T. schrieb:> mich interessiert, wie ihr eure Projekte organisiert, und um genau zu> sein: Wie ihr "static inline"-Funktionen organisiert.
Siehe auch:
Beitrag ""Include-Libs" - Teufelswerk?"
ICh persönlich mache da gar nichts mehr in dieser Richtung. static
reicht. Dann kann der Compiler entscheiden, ob er diese inlined oder
nicht. Das kann er selbst meist besser.
> David Kieras empfiehlt in den "C Header File Guidelines" (siehe hier:> http://www.umich.edu/~eecs381/handouts/CHeaderFileGuidelines.pdf )> niemals Funktionen in *.h-Dateien zu tun, sondern in *.inc-Dateien.
Nimm -Os -flto sowohl als Compiler- als auch als Linkerflag. Dann kann
der gcc sogar modulübergreifende Funktionsaufrufe "inlinen", wenn er das
für sinnvoll hält.
Jörg W. schrieb:> Über inline-Funktionen hat er sich nicht ausgelassen in seinem> Schriebs. Ich würde sie, obwohl sie bei ihrer Anwendung in der Tat> Code generieren, dennoch im normalen Headerfile lassen. Alles andere> ist meiner Meinung nach widersinnig.
In einer LTO-Welt nicht unbedingt.
Johann L. schrieb:> In einer LTO-Welt nicht unbedingt.
Dort muss man sie aber auch nicht extra definieren. ;-) Damit ist
es keine Frage der Sourcecode-Organisation mehr.