Funktionen gruppieren; Namespace oder Klasse?



  • Hallo Leute,

    Ich habe eine gewisse Anzahl von Funktionen, die ich gerne gruppieren möchte. Da diese Funktionen auf keine externe Variablen zugreifen, ist nun die Frage ob ich diese Funktionen in einer Klasse zusammenfasse oder in einem Namensraum.

    Beispiel:

    namespace myFunctions
    {
    	int funktion1(...)
    	{
    	}
    
    	int funktion2(...)
    	{
    		...
    		hilfsfunktion(...);
    		...
    	}
    
    	// leider sichtbar :(
    	int hilfsfunktion(...)
    	{
    	}
    }
    
    class myFunctions
    {
    public:
    	myFunctions(){};
    	~myFunctions(){};
    
    	int funktion1(...)
    	{
    	}
    
    	int funktion2(...)
    	{
    		...
    		hilfsfunktion(...);
    		...
    	}
    
    private:
    	int hilfsfunktion(...)
    	{
    	}
    };
    
    void main()
    {
    	int var1;
    
    	// Namespacevariante
    	var1 = myFunctions::funktion1(...);
    
    	// Klassenvariante
    	var1 = myFunctions().funktion1(...);
    
    }
    

    Mein Problem mit dem Namensraum ist, dass ich einige Hilfsfunktionen nach außen hin unsichtbar machen möchte (ähnlich wie private bei Klassen). Ist sowas irgendwie möglich?



  • Namespace!

    Wenn du die Funktionen in einer separaten cpp definierst, können die Hilfsfunktionen unsichtbar sein. Wenn nicht, müssen die Funktionen inline definiert werden. Die Hilfsfunktionen können in einem verschachtelt namespace liegen (mit entsprechendem Kommentar).



  • Mit einem "unnamed namespace" kann man sie "privat" machen:

    http://en.cppreference.com/w/cpp/language/namespace#Unnamed_namespaces



  • Stumpelrielzchen schrieb:

    Mit einem "unnamed namespace" kann man sie "privat" machen:

    http://en.cppreference.com/w/cpp/language/namespace#Unnamed_namespaces

    Nein, wie du es auch im verlinkten Beispiel sehen kannst.



  • manni66 schrieb:

    Stumpelrielzchen schrieb:

    Mit einem "unnamed namespace" kann man sie "privat" machen:

    http://en.cppreference.com/w/cpp/language/namespace#Unnamed_namespaces

    Nein, wie du es auch im verlinkten Beispiel sehen kannst.

    Doch, Du musst es aber in eine eigene CPP/H auslagern:

    myfunctions.h

    #pragma once
    
    namespace myFunctions
    {
    	int funktion1();
    	int funktion2();
    }
    

    myfunctions.cpp

    #include "stdafx.h"
    #include "myfunctions.h"
    
    namespace
    {
    	int hilfsfunktion()
    	{
    		return 0;
    	}
    }
    
    int myFunctions::funktion1()
    {
    	return hilfsfunktion();
    }
    
    int myFunctions::funktion2()
    {
    	return hilfsfunktion();
    }
    

    main.cpp

    #include "stdafx.h"
    #include "myfunctions.h"
    
    int main()
    {
    	int i = myFunctions::funktion1();
    	int j = myFunctions::funktion2();
    	int k = hilfsfunktion(); // <-- error: undefined
        return 0;
    }
    


  • ja danke 😃

    "Anonyme Namensräume" waren genau das was ich gesucht habe. 🙂
    vielen Dank euch allen.



  • Hab noch mal eine Ergänzungsfrage dazu:

    Ab und zu werfen alle meine Funktionen, die im anonymous namespace sind solche Warnungen:

    ‘std::string {anonymous}::convertAsn1Time(ASN1_TIME*)’ declared ‘static’ but never defined [-Wunused-function]

    Kann jemand damit was anfangen? Soweit funktioniert alles und seltsamer Weise werden diese Warnungen nicht immer angezeigt. Ich weiß nicht was der Auslöser ist und auch nicht was mir diese Warnung mit 'declared ‘static’ but never defined' genau sagen will 😕



  • D.h. der Compiler ist der Meinung du hättest irgendwo stehen

    namespace {
    //...
    std::string convertAsn1Time(ASN1_TIME*); // <- declared
    // <-- not defined here -->
    }
    


  • ja das ist der Fall. Allerdings ist die definition und deklaration getrennt.

    So ist mein Aufbau:
    Header:

    namespace XXX
    {
       ...funktionsdeklarationen...
    }
    
    namespace YYY
    {
       ...funktionsdeklarationen...
    }
    
    namespace
    {
        std::string convertAsn1Time(ASN1_TIME *asn1Time);
       ...sonstige funktionsdeklarationen...
    }
    

    Cpp-Datei:

    namespace XXX
    {
       ...funktionsdefinitionen...
    }
    
    namespace YYY
    {
       void setIrgendwas(...)
        {
            ...code...
            meinStr = convertAsn1Time(...);
            ...code...
        }
    
       ...weitere funktionsdefinitionen...
    }
    
    namespace
    {
        std::string convertAsn1Time(ASN1_TIME *asn1Time)
        {
            ...code...
        }
    
       ...sonstige funktionsdefinitionen...
    }
    

    wenn ich die deklaration von convertAsn1Time in der Header auskommentiere, dann bekomme ich beim kompilieren einen fehler, daher dachte ich eigentlich dass der anonymous namespace im Header und in der CPP für den Compiler identisch sind. Oder generiert der Compiler intern verschiedene Namen für die namespaces, um die Eindeutigkeit zu sichern?



  • Einfach die Deklaration der Funktion im anonymen NS in der Header-Datei weglassen - zugegriffen wird ja eh nur innerhalb der Source-Datei.



  • theta schrieb:

    Einfach die Deklaration der Funktion im anonymen NS in der Header-Datei weglassen - zugegriffen wird ja eh nur innerhalb der Source-Datei.

    Also ich kann die Definition des anonymen NS (mit impliziter Deklaration) ganz oben in der cpp machen. Funktionen die unterhalb definiert wurden können dann auf die Funktionen im NS zugreifen.

    Was mich wundert: Warum führt das Weglassen der Deklaration im Header zu einem Fehler beim Kompilieren? Könnte es ein Namenskonflikt sein? Ich stelle es mir so vor: der anonyme NS im Header und der anonyme NS in der CPP sind für den Compiler 2 verschiedene NS, die jeweils irgendwie ein eindeutige Kennung im Compiler bekommen. Funktionen in einem definierten NS, die auf eine Funktion im dem anonymen NS zugreifen sollen, sehen zuerst diese Deklaration im Header. Dort ist diese aber nichts Definiert. Nehmen die dann das nächstbeste (die Definition in der Cpp)?

    ...ist das verwirrend 😮



  • SBond schrieb:

    Ich stelle es mir so vor: der anonyme NS im Header und der anonyme NS in der CPP sind für den Compiler 2 verschiedene NS, die jeweils irgendwie ein eindeutige Kennung im Compiler bekommen. Funktionen in einem definierten NS, die auf eine Funktion im dem anonymen NS zugreifen sollen, sehen zuerst diese Deklaration im Header. Dort ist diese aber nichts Definiert. Nehmen die dann das nächstbeste (die Definition in der Cpp)?

    Ne.

    Aber die anonymen Namespaces in A.cpp und B.cpp haben unterschiedliche "Namen". Was ja genau der Sinn von anonymen Namespaces ist. Sonst würde ja die Namen von zwei Klassen [anonym]::MyHelper in verschiedenen .cpp Files kollidieren. (Dinge in anonymen Namespaces können ja trotzdem external linkage haben!)

    Guck mal...

    // --- Foo.h -----------------
    
    namespace {
        void FooImpl();
    }
    
    ...
    
    // --- Foo.cpp ---------------
    
    #include "Foo.h" // -> fwd. decl von  void [anon_namespace_for_Foo_cpp]::FooImpl()
    
    namespace {
        void FooImpl() { ... } // Definition von void [anon_namespace_for_Foo_cpp]::FooImpl()
    }
    
    // --- Bar.cpp ---------------
    
    #include "Foo.h" // -> fwd. decl von  void [anon_namespace_for_Bar_cpp]::FooImpl()
    
    ...
    

    Jetzt wird hoffentlich klar:
    Die Funktion void [anon_namespace_for_Foo_cpp]::FooImpl() wird deklariert und definiert.
    Die Funktion void [anon_namespace_for_Bar_cpp]::FooImpl() wird nur deklariert aber nirgends definiert.



  • Ja!

    Du hast recht. Da liegt der Wurm begraben. 😃
    Aber kann ich da was machen? 😕

    Warnungen möchte ich nicht deaktivieren. Diese anonymen namespaces, die in der Foo.h deklariert und in der Foo.cpp definiert werden, werden ja auch nur in der Foo.cpp verwendet. Es war auch das Ziel, das diese nicht von anderen .cpp-Dateien verwendet werden können (quasi ein private-Bereich im namespace). Aber wenn nun jede beliebige .cpp-Datei, die Foo.h inkludiert mehrere Warnungen schmeißt, dann ist es auch nicht so ganz das wahre. Könnte ich was dagegen tun?



  • SBond schrieb:

    Aber kann ich da was machen? 😕

    Das hat dir theta doch schon beantwortet:

    theta schrieb:

    Einfach die Deklaration der Funktion im anonymen NS in der Header-Datei weglassen - zugegriffen wird ja eh nur innerhalb der Source-Datei.

    Aus meinem Beispiel wird dann

    // --- Foo.h -----------------
    
    // namespace {
    //     void FooImpl();  // *** brauchen wir nicht, und wollen wir hier nichtmal haben => weg damit
    // }
    
    void Foo();
    
    ...
    
    // --- Foo.cpp ---------------
    
    #include "Foo.h"
    
    namespace {
        void FooImpl() { ... } // Definition von void [anon_namespace_for_Foo_cpp]::FooImpl()
                               //  *** hier muss sich nix ändern, die Definition ist ja zuglich auch ne Deklaration
    }
    
    void Foo()
    {
        FooImpl();
    }
    
    // --- Bar.cpp ---------------
    
    #include "Foo.h" //  ***  keine fwd. decl von FooImpl mehr => kein Problem mehr
    
    ...
    

    Das wäre dann auch die Variante so wie ich das kenne. Also anonyme Namespaces nur in der .cpp und die enthalten dann halt keinerlei forward declarations sondern nur Funktionen und Klassen wo alles "inline" implementiert ist.



  • ok 🙂

    habe zuvor noch nicht groß mit namespaces gearbeitet. Vielen Dank für die Hilfe. ...mal wieder 😃



  • Weiss nicht was dieser Teil jetzt mit Namespaces zu tun hat.

    Bis auf dass die Funktion FooImpl ohne anomynem Namespace "kollisionsgefähret" wäre (=Problem wenn ein anderes File auch eine Funktion namens FooImpl enthält) ändert sich nichts wenn du den anomynem Namespace weg lässt.


Log in to reply