Forum: Compiler & IDEs static inline in Projektorganisation


von Walter T. (nicolas)


Lesenswert?

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
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.

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

: Bearbeitet durch User
von Carl D. (jcw2)


Lesenswert?

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".

von TestX (Gast)


Lesenswert?

Das Problem gibt es auch bei Templates, Traits etc.. schau dir am besten 
mal boost an wie die das orginaisiert haben...

von Peter D. (peda)


Lesenswert?

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.

von Carl D. (jcw2)


Lesenswert?

@Peda:
Falls ich das oben zu kompliziert geschrieben haben sollte:
Ganz meine Meinung.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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
static uint8_t lookup_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
class foo {
2
  private:
3
    int percent;
4
5
  public:
6
    foo(void);
7
8
    int get_percent { return percent; }
9
    void set_percent(int nval) {
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.

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

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:
1
void inline foo()
2
{
3
...
4
}

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

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! ;-)

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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 …

von Jay (Gast)


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Walter T. (nicolas)


Lesenswert?

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:
1
    /* Einzelnes oder mehrere Bits eines Ports setzen
2
     *
3
     * GPIOx
4
     * Pin: Bitmaske */
5
    static inline __attribute__((always_inline)) void
6
    io_SetBit(GPIO_TypeDef * const pPORTx, const uint8_t Pin) 
7
    {
8
        *pPORTx |= Pin;
9
    }
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.

: Bearbeitet durch User
von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

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.