Problem mit statischen Klassenelementen



  • Senfgurke schrieb:

    Auch wenn ich int privat in der .cpp auserhalb des anonymen Namensbereich deklariere und definiere kann der Linker die Funktion nicht auflösen.
    Hmm...

    Hast du den Namespace in der Cpp datei vor den Funktionsnamen gepackt?



  • Erstens solltest du das "inline" nur angeben, wenn du die Funktion direkt im Header implementierst (damit sagst du dem Compiler, daß er den Funktionsinhalt direkt einkopieren soll statt einen Funktionsaufruf zu erzeugen - also bereitet er auch nichts für einen Funktionsaufruf vor, wenn es nicht nötig ist). Und zweitens ist es vermutlich besser, du öffnest im CPP auch den Namensraum:

    //foo.h
    namespace foo
    {
      void bar();
    }
    
    //foo.cpp
    #include "foo.h"
    namespace foo
    {
      namespace
      {
        int privat;
      }
    
      void bar()
      {
        privat = 4711;
      }
    }
    


  • Danke für die Vorschläge.

    CStoll schrieb:

    Erstens solltest du das "inline" nur angeben, wenn du die Funktion direkt im Header implementierst

    Ich glaube dass ist so nicht richtig.
    Gerade wenn ich die Funktion im Header definiere, wird sie implizit als inline deklariert.
    Wenn ich sie außerhalb definiere, muss ich sie manuell mit inline oder __forceinline deklarieren.

    Also wird bar() im Beispiel nicht als inline-Funktion vom Compiler behandelt.

    Vielleicht sollte ich das einfach in C implementieren.



  • Senfgurke schrieb:

    Danke für die Vorschläge.

    CStoll schrieb:

    Erstens solltest du das "inline" nur angeben, wenn du die Funktion direkt im Header implementierst

    Ich glaube dass ist so nicht richtig.
    Gerade wenn ich die Funktion im Header definiere, wird sie implizit als inline deklariert.

    Das ist (sorry) Bullsh**. Der Compiler sieht überhaupt nichts mehr von Headern, deshalb ist es ihm auch egal, ob du eine Funktion im Header oder im CPP-File definiert hast.

    Wenn du eine Funktion im Header definierst, mußt du sie als inline kennzeichnen, sonst könntest du Probleme von Linker bekommen (Mehrfachdefinitionen). Wenn du die Funktion im Header nur deklarierst und im CPP-File definierst, brauchst du kein inline (und wie du selbst gesehen hast, ist es eher hinderlich).

    PS: Und implizit als inline deklariert werden nur Klassenmethoden, die du innerhalb der Klassendefinition gleich definierst.

    PPS: Egal was du machst, die letzte Entscheidung über "inline oder nicht?" trifft in jedem Fall dein Compiler.



  • Stimmt.
    Ich habe im Delirium von Definition im Header gesprochen, obwohl ich die Definition von Klassenmethoden innerhalb der Klassendefinition meinte.

    Mit __forceinline kann ich doch aber den compiler zwingen (wenn es fehlerfrei möglich ist) die Funktion als inline zu behandeln.
    Nur kann ich dass auch in deinem Beispiel nicht umsetzen.



  • Senfgurke schrieb:

    Mit __forceinline kann ich doch aber den compiler zwingen (wenn es fehlerfrei möglich ist) die Funktion als inline zu behandeln.

    Ja, kannst du. Nur gibt es einige Fälle, wo du eine Funktion NICHT inline definieren kannst (und ein Beispiel davon ist, wenn du die Implementation in eine externe CPP-Datei auslagerst).

    Nur kann ich dass auch in deinem Beispiel nicht umsetzen.

    Gut erkannt 😉



  • CStoll schrieb:

    Wenn du die Funktion im Header nur deklarierst und im CPP-File definierst, brauchst du kein inline (und wie du selbst gesehen hast, ist es eher hinderlich).

    Mir ist es trotz geeigneter Compileroption (/Ob2, /Ot) nicht gelungen dass auch bei kleinsten Funktionsinhalten für diesen Fall inline angewandt wird.

    Ist es überhaupt möglich eine Funktion in einem Header zu deklarieren, im Modul zu definieren und als inline in anderen Modulen zu nutzen?
    Kann man Variablen mit interner Bindung in einer Funktion nutzen, welche mit inline in anderen Modulen genutzt wird?

    Vielen Dank für Eure Geduld! 🙂



  • Senfgurke schrieb:

    Mir ist es trotz geeigneter Compileroption (/Ob2, /Ot) nicht gelungen dass auch bei kleinsten Funktionsinhalten für diesen Fall inline angewandt wird.

    Wenn Du das auf den unten geschilderten Fall beziehst wundert mich das nicht.

    Ist es überhaupt möglich eine Funktion in einem Header zu deklarieren, im Modul zu definieren und als inline in anderen Modulen zu nutzen?

    Nein, wie denn auch? Der Compiler kennt in dem Fall (der Benutzung) die Definition der Funktion nicht, er kennt nur den Funktionskopf. Er kann in diesem Fall keine andere Substitution für diesen Aufruf machen, als den tatsächlichen Aufruf. Der Linker fügt für diesen Aufruf dann die tatsächliche Adresse ins Endprodukt ein, aber der Linker macht kein Inlining.



  • Ich habe es nun wie folgend implementiert:

    //foobar.h
    namespace foo
    {
    	namespace
    	{
    		int privat = 0;
    	}
    
    	inline void bar()
    	{
    		privat = 124;
    	}
    }
    

    Die Variable privat ist zwar global, dafür aber anonym, also so wie ich es haben wollte.
    Gibt es daran etwas wegen der Form auszusetzen?

    Ich weiß jetzt nicht ob es an einer Compileroption liegt, jedenfalls habe ich das Problem, dass der Debugger keine Informationen über die Variable 'privat' ausgiebt.
    Der sollte das doch eigentlich anzeigen können?
    (Nutze VC++ 2003)

    Nochmal vielen Dank.



  • LordJaxom schrieb:

    Ist es überhaupt möglich eine Funktion in einem Header zu deklarieren, im Modul zu definieren und als inline in anderen Modulen zu nutzen?

    Nein, wie denn auch? Der Compiler kennt in dem Fall (der Benutzung) die Definition der Funktion nicht, er kennt nur den Funktionskopf. Er kann in diesem Fall keine andere Substitution für diesen Aufruf machen, als den tatsächlichen Aufruf. Der Linker fügt für diesen Aufruf dann die tatsächliche Adresse ins Endprodukt ein, aber der Linker macht kein Inlining.

    Das ist so nicht ganz richtig. Der MSVC hat nen Schalter für Link-time Code-generation, der genau sowas erlaubt. Auch Optimieren kann der über Übersetzungseinheiten hinweg. Das läuft dort unter dem Namen whole-program-optimization.

    Traditionell ist es natürlich so, wie Du beschrieben hast. Ich kann mir aber gut vorstellen, daß in Zukunft mehr Compiler auf diesen Zug aufspringen. (Oder vielleicht schon aufgesprungen sind?)



  • Nach langem Suchen habe ich das gefunden: http://msdn2.microsoft.com/en-US/library/0888kc6a.aspx

    Scheinbar ist es eine Restriktion des VC++ Debuggers, dass er nichts in anonymen Namensräumen auflösen kann.
    Na toll! Wieso das denn?

    Also wenn das nicht geht, muss ich einen anderen Weg finden, die Datenelemente, die von den inline-Funktionen genutzt werden, nach außen hin unantastbar zu machen.
    Hat da jemand eine Idee wie man dies sonst tun könnte?



  • Naja, unantastbar ist (fast) nix. Wenn du aber Otto-Normal-Programmierer verbieten willst da drauf zuzugreifen, aber gleichzeitig Zugriffe aus inline Funktionen brauchst, ist vielleicht wirklich das Beste wenn du die Daten als "private static" in einen eigene Klasse packst. Die Funktionen kannst du ja trotzdem als free functions implementieren (müssen dann halt alle friend sein). Oder, wenn dir der Aufwand zuviel ist (immer friend Tippen etc.), dann halt wirklich so wie du es ursprünglich geschrieben hast. Die Klasse für diese Daten packst du dann ein einen Detail namespace, und *futsch* sind sie "weg" 🙂

    Je nachdem was du wirklich machst könnte es von der Geschwindigkeit her trotzdem vertretbar sein wenn du einfach ne Klasse machst die mehrfach instanzierbar ist, da alle wichtigen Funktionen (was eben schnell gehen muss) inline implementierst, und überall einen Pointer/eine Referenz auf die zu verwendende Instanz mitgibst. Dass der Compiler Member-Function-Calls nicht "inlinen" kann ist nämlich ein (unwahres) Gerücht, zumindest was MSVC angeht (ab Version 6 kann der das sicher). Das einzige was dann noch an Overhead bleibt ist der "this" Pointer, was aber nahezu vernachlässigbar ist. Das eine Register wird ihm schon nicht SO weh tun 🙂
    Und je nachdem was du machst kann es durchaus Sinn machen diese Kontrollklasse mehrfach instanzierbar zu machen. Man denke an Multithreading. Nix ist lästiger als eine Library die wegen globalem State nicht thread-safe ist. Wenn es allerdings bloss um eine halbwegs überschaubare (Grösse, Komplexität) Applikation geht kann es leicht sein dass jeglicher Aufwand für super-schönes Design vollkommen verschwendet ist, weils eh nie jmd. brauchen wird.

    Oft gilt aber "mutable (global) state" + multithreading = desaster.

    Wenn du etwas genauer beschreiben kannst was diese Klasse/Funktionssammlung eigentlich tun soll könnte vielleicht jmd. mit persönlicher Erfahrung weiterhelfen.

    Und: vergiss nicht das "static initialization order fiasco" - weiss nicht ob es dich betreffen kann, ist aber ein oft ignoriertes Problem bei Singletons (wie auch immer implementiert), das sich manchmal grausam rächt.

    p.S.: das "pointer holen" beim Singleton Pattern kann, je nach Implementierung (z.B. thread-safe oder nicht), sehr teuer werden. Deswegen ziehe ich normalerweise vor den Zeiger (bzw. die Referenz) einfach überall mitzugeben wo man ihn braucht, dann muss man den nicht so oft "neu ermitteln".

    p.p.S.: wenn du MSVC verwendest guck dir mal "#pragma inline_depth()" an - "#pragma inline_depth(255)" wirkt oft Wunder, vor allem bei sehr "schönem" Code (viele Indirections die kaum "echten" Code enthalten). (Default ist 8 und da kommt man schnell mal drüber.) Und ggf. noch "#pragma inline_recursion(on)". Und Link-Time-Code-Generation wurde ja schon erwähnt, wirkt manchmal auch Wunder (wenn auch nicht ganz SO oft). Und ggf. den Buffer-Security-Check abdrehen, checked iterator abdrehen etc. wenns auf Performance ankommt, und der Vergleich zeigt dass es deutlich was bringt.


Anmelden zum Antworten