__CLASS__ Makro?



  • Hi leute, ich brauche dringend ein makro, das mir den aktuellen klassen namen zurückgibt, typeid(*this).name() gibt leider immer "Class Klassennamen" zurück, und ein rumgesplitte wäre mir dann doch zu umständlich. Gibt es nicht so etwas wie __FUNCTION__ (also __CLASS__) in c++? Hab gegoogelt, aber nichts gefunden, und weder __class__ noch __CLASS__ scheinen definiert zu sein (zumindest nicht in MSVC++).

    Kennt jemand das passende makro?


  • Mod

    Zum Sprachstandard gehören die alle nicht. Das sind alles Zusatzfeatures bestimmter Compiler. Wenn dein Compiler dies nicht kennt, dann kannst du es auch nicht benutzen.


  • Administrator

    Gibt es nicht im C++ Standard.
    Zudem ist der Rückgabewert von typeid(*this).name() kompilerabhängig. Was da zurückkommt, darauf kannst du dich auch nicht verlassen. Gibt daher grundsätzlich keine vernünftige Methode, wie du zur Laufzeit an den Klassennamen kommst, ausser du speicherst ihn selbst.

    Grüssli



  • Nein, es gibt nichts Standardkonformes in dieser Richtung.

    Klar, mit typeid(*this).name() ist man auch nicht ganz portabel, aber besser als gar nichts. Und weshalb grosses Rumgesplitte?

    std::string a = "class Blub";
    a.erase(0, sizeof("class"));
    


  • hm gibt dann die o.g. function wenigstens immer den Klassennamen richtig aus (wegen der Compilerabhänigkeit)?



  • Wo brauchst du das überhaupt? Je nachdem kann man sich auch eine standardkonforme, garantiert funktionierende Lösung ausdenken.

    #define GET_CLASS_NAME(CLASS)          \
        static std::string GetClassName()  \
        {                                  \
            return #CLASS;                 \
        }
    
    class MyClass
    {
    	public:
    		GET_CLASS_NAME(MyClass)
    };
    

    Zur Optimierung könnte man einen statischen std::string zwischenspeichern und eine Const-Referenz darauf zurückgeben. So spart man sich unnötige Konstruktionen und Kopien.


  • Administrator

    Krauzi schrieb:

    hm gibt dann die o.g. function wenigstens immer den Klassennamen richtig aus (wegen der Compilerabhänigkeit)?

    Was zurückkommt ist ziemlich undefiniert. Es steht nur, soweit ich mich erinnere, dass der Name "human readable" sein muss.

    @Nexus,
    Wozu überhaupt über std::string gehen? Lieber gleich ein Literal verwenden.

    #define CREATE_CLASS_NAME_GETTER(CLASS) \
      static char const* get_class_name()   \
      {                                     \
        return #CLASS;                      \
      }
    

    Falls man trotzdem ein std::string braucht, geht die Konvertierung implizit.

    Grüssli



  • Dravere schrieb:

    @Nexus,
    Wozu überhaupt über std::string gehen?

    Gute Frage. Die Tatsache, dass ich gerade sonst noch mit std::string beschäftigt war, hat meinen Horizont wohl zu stark eingeschränkt. 😉

    Allerdings hat meine Variante einen kleinen Vorteil: Sie ist effizienter, wenn man GetClassName() als const std::string& speichert (z.B. häufig in Schnittstellen anzutreffen). Aber const char* ist generischer, Performance dürfte hier wohl vernachlässigbar sein.



  • Nexus schrieb:

    Dravere schrieb:

    @Nexus,
    Wozu überhaupt über std::string gehen?

    Gute Frage. Die Tatsache, dass ich gerade sonst noch mit std::string beschäftigt war, hat meinen Horizont wohl zu stark eingeschränkt. 😉

    Allerdings hat meine Variante einen kleinen Vorteil: Sie ist effizienter bei Schnittstellen mit const std::string& -Parametern, denen man GetClassName() übergibt. 🕶

    Man könnte die Funktion ja noch entsprechend überladen 😃



  • hmmmmmm schrieb:

    Man könnte die Funktion ja noch entsprechend überladen 😃

    Nein, Überladung aufgrund unterschiedlicher Rückgabetypen funktioniert nicht, da letztere nicht zur Funktionssignatur gehören.

    Man müsste zwei Funktionen mit verschiedenen Namen (oder Parametertypen, was aber weniger Sinn macht) anbieten.


  • Administrator

    Nexus schrieb:

    Allerdings hat meine Variante einen kleinen Vorteil: Sie ist effizienter bei Schnittstellen mit const std::string& -Parametern, denen man GetClassName() übergibt. 🕶

    Daran zweifle ich etwas. Ich denke eher, dass beide Varianten gleich schnell sein werden, weil der Kompiler hier so extrem optimieren kann. Er kann die ganze Funktion verwerfen. Und ob er bei meiner Version dann wirklich einen std::string baut, wenn dieser sowieso nur konstant ist, zweifelhaft. Wenn er wirklich gut ist, kann er diese Kopie auch gleich eliminieren und direkt das Literal als internen Puffer nehmen.

    Aber die Performance dürfte hier sowieso eher vernachlässigbar sein 😃

    Grüssli



  • Ich brauche das, weil ich gerade mit methoden pointern rumexperimentiere. Ich versuche gerade ein Interface für python objekte zu schreiben, dass automatisch alle freigegebenen attribute und methoden in entsprechende python funktionen umsetzt (also c++ instance -> python instance). Dafür wollte ich z.b. ein Makro definieren, dass nach diesem Format arbeitet:
    #define METHOD(r,o,m) (r (o::*)() )(&o::m)
    r = return value
    o = klasse
    m = methode

    ich dachte mir, dass ich vllt. das o weglassen könne, wenn man das mit einem __class__ ersetzten könnte, aber so ein makro gibts leider nicht, also hat sich das ganze erledigt.

    Allerdings könnte mir wegen dem Methoden pointern gleich jemand helfen :P.
    Ich hab jetzt schon viel gesucht, aber ich komme nicht zu einer vernünftigen lösung:
    ich brauche eine methode, (von einer template klasse), die alle methoden abspeichern kann.
    Dazu dachte ich an sowas.
    [cpp]
    template<class Class>
    class MeineKlasse
    {
    ....
    template<typename Type> RegisterMethod( Type *methodenPointer ) {...}
    [cpp]
    das problem ist allerdings, dass ich nicht wirklich auf eine vernünftige lösung komme!
    ich dachte, eine einfügen im plublic bereich mit den methoden pointer als typedef hiermit:
    typedef string ( Class::*stringNoArg )( );
    würde für eine simple methode wie string GetName() { return this->m_Name; } reichen. Hab mich wohl geteuscht.
    Weis jemand, wie das gut und übersichtlich zu lösen wäre?


  • Administrator

    Krauzi schrieb:

    ... die alle methoden abspeichern kann.

    Unmöglich, da die Funktionen jeweils einen anderen Typ haben. Man kann gewisse gleiche Arten von Funktionen speichern, aber das ist auch schon alles. Du müsstest also dieses "alle" genauer spezifizieren. Dann kannst du vielleicht etwas mit boost::function machen.

    Grüssli



  • ich möchte nur ganz bestimmte funktionen registrieren können.
    z.b. welche die kein argument benötigen und einen string zurückgeben.
    Wie könnte ich zumindest diese funktion registrieren?



  • Dravere schrieb:

    Aber die Performance dürfte hier sowieso eher vernachlässigbar sein 😃

    Ja, habe ich ja auch noch geschrieben (2. Edit). 🙂

    Krauzi schrieb:

    ich brauche eine methode, (von einer template klasse), die alle methoden abspeichern kann.
    [...]
    Weis jemand, wie das gut und übersichtlich zu lösen wäre?

    Du kannst unterschiedliche statische Typen nicht in einem einzelnen Container speichern (Typlisten sind hier ungeeignet). Also baust du dir mittels Type Erasure und dynamischer Polymorphie eine Abstraktion, mit der du Methoden einheitlich ansprechen kannst.

    Zum Beispiel sowas:

    // Abstrakte Basisklasse zur Speicherung von Methoden
    class AbstractMethod
    {
    	public:
    		virtual ~AbstractMethod() {}
    		virtual const char* GetName() const = 0;
    };
    
    // Konkrete Implementierung einer Methode
    template <typename MemFnPtr>
    class ConcreteMethod : public AbstractMethod
    {
    	public:
    		ConcreteMethod(const char* name, MemFnPtr pointer)
    		: myName(name)
    		, myPointer(pointer)
    		{
    		}
    
    		virtual const char* GetName() const
    		{
    			return myName;
    		}
    
    	private:
    		const char*		myName;
    		MemFnPtr		myPointer;
    };
    
    // Factory-Funktion (Vorsicht, gibt besitzenden Zeiger zurück)
    template <typename MemFnPtr>
    ConcreteMethod<MemFnPtr>* CreateConcreteMethod(const char* name, MemFnPtr pointer)
    {
    	return new ConcreteMethod<MemFnPtr>(name, pointer);
    }
    
    // Makro zur handlichen Bearbeitung
    #define CREATE_CONCRETE_METHOD(METHOD) CreateConcreteMethod(#METHOD, &METHOD)
    
    // Test-Klasse
    struct MyClass
    {
    	void Test(int) {}
    	bool Test2() { return true; }
    };
    
    // Anwendung
    #include <iostream>
    
    int main()
    {
    	AbstractMethod* a = CREATE_CONCRETE_METHOD(MyClass::Test);
    	AbstractMethod* b = CREATE_CONCRETE_METHOD(MyClass::Test2);
    
    	std::cout << a->GetName() << std::endl;	
    	std::cout << b->GetName() << std::endl;	
    
    	delete a;
    	delete b;
    }
    

    Und jetzt noch einen Container verwenden. Ideal wäre Boost.PtrContainer, da der den Speicher selbst freigibt. Ansonsten musst du eben manuell delete n. Was die eigentlichen Memberfunktionszeiger betrifft, da weiss ich zu wenig über dein Design, aber du kannst dir ja eine entsprechende Schnittstelle überlegen.



  • #include <map>
    #include <string>
    #include <iostream>
    
    typedef std::string (*StrFunc) ();
    typedef std::map<std::string, StrFunc> FuncMap;
    
    class FunctionManager
    {
    private:
    	FuncMap myMap;
    
    public:
    	void add(const std::string& str, StrFunc func)
    	{
    		myMap.insert(std::make_pair(str, func));
    	}
    
    	std::string call(const std::string& str) const
    	{
    		FuncMap::const_iterator it = myMap.find(str);
    
    		if (it != myMap.end())
    		{
    			return it->second();
    		}
    
    		return "<Error>";
    	}
    };
    
    std::string test()
    {
    	return "test";
    }
    
    int main()
    {
    	FunctionManager manager;
    
    	manager.add("lol", test);
    
    	std::cout << manager.call("lol");
    
    	std::cin.get();
    
    	return 0;
    }
    

Anmelden zum Antworten