Gibt es ein __CLASS__ Makro in C ++?

100

Gibt es __CLASS__in C ++ ein Makro, das den Klassennamen ähnlich dem __FUNCTION__Makro mit dem Funktionsnamen angibt?

sterblich
quelle

Antworten:

67

Das Nächste, was es gibt, ist anzurufen typeid(your_class).name()- aber dies erzeugt einen compilerspezifischen verstümmelten Namen.

Um es nur innerhalb der Klasse zu verwenden typeid(*this).name()

Aleksei Potov
quelle
2
typeid (* this) .name () kann innerhalb von Klassenfunktionen verwendet werden
Aleksei Potov
2
Das ist besser. Um die Klasse zu kennen, klingt das Definieren des char-Arrays besser, als es auf die Laufzeit zu verschieben.
Michael Krelin - Hacker
5
Schade, dass es nicht wie __ KLASSE __ definiert ist, es kann im Präprozessor-Stadium nützlich sein! :(
k3a
2
@ Max Das tut es aber nicht. Ähnlich kennt es sich mit Funktionen aus :-P
k3a
5
@kexik: Der Präprozessor kennt auch keine Funktionen, Standard __func__und Nicht-Standard __FUNCTION__sind keine Makros. Microsoft dokumentiert __FUNCTION__als Makro, aber das Werbegeschenk, dass es nicht wirklich ist, ist, dass es beim Kompilieren nicht vom Präprozessor erweitert wird /P.
Steve Jessop
77

Das Problem bei der Verwendung typeid(*this).name()ist, dass thisein statischer Methodenaufruf keinen Zeiger enthält. Das Makro __PRETTY_FUNCTION__meldet einen Klassennamen in statischen Funktionen sowie in Methodenaufrufen. Dies funktioniert jedoch nur mit gcc.

Hier ist ein Beispiel für das Extrahieren der Informationen über eine Makroschnittstelle.

inline std::string methodName(const std::string& prettyFunction)
{
    size_t colons = prettyFunction.find("::");
    size_t begin = prettyFunction.substr(0,colons).rfind(" ") + 1;
    size_t end = prettyFunction.rfind("(") - begin;

    return prettyFunction.substr(begin,end) + "()";
}

#define __METHOD_NAME__ methodName(__PRETTY_FUNCTION__)

Das Makro __METHOD_NAME__gibt eine Zeichenfolge des Formulars zurück <class>::<method>()und schneidet den Rückgabetyp, die Modifikatoren und die Argumente von what ab__PRETTY_FUNCTION__ Sie erhalten.

Für etwas, das nur den Klassennamen extrahiert, muss sorgfältig darauf geachtet werden, Situationen abzufangen, in denen es keine Klasse gibt:

inline std::string className(const std::string& prettyFunction)
{
    size_t colons = prettyFunction.find("::");
    if (colons == std::string::npos)
        return "::";
    size_t begin = prettyFunction.substr(0,colons).rfind(" ") + 1;
    size_t end = colons - begin;

    return prettyFunction.substr(begin,end);
}

#define __CLASS_NAME__ className(__PRETTY_FUNCTION__)
Andrew Prock
quelle
5
Solltest du das nicht umgeben #ifdef __GNU_C__?
Einpoklum
1
stattdessen substr(0,colons).rfind(" ")könnte man rfind(' ', colons)eine Erstellung eines zusätzlichen Strings ersparen.
Marius
1
Ich würde lieber find_last_of ("::") verwenden. Andernfalls gibt die Funktion nur dann einen Namespace zurück, wenn es einen gibt
underdoeg
Ich habe eine möglicherweise umfassendere Version des __METHOD_NAME__Makros geschrieben. Überprüfen Sie hier .
Antonio
In C ++ 11 könnten Sie versuchen, dies zu einer constexprFunktion zu machen, um es zur Kompilierungszeit auszuwerten
Andre Holzner
11

Ich möchte boost :: typeindex vorschlagen , das ich aus Scott Meyers "Effective Modern C ++" kennengelernt habe. Hier ein grundlegendes Beispiel:

Beispiel

#include <boost/type_index.hpp>

class foo_bar
{
    int whatever;
};

namespace bti =  boost::typeindex;

template <typename T>
void from_type(T t)
{
    std::cout << "\tT = " << bti::type_id_with_cvr<T>().pretty_name() << "\n";
}

int main()
{
    std::cout << "If you want to print a template type, that's easy.\n";
    from_type(1.0);
    std::cout << "To get it from an object instance, just use decltype:\n";
    foo_bar fb;
    std::cout << "\tfb's type is : "
              << bti::type_id_with_cvr<decltype(fb)>().pretty_name() << "\n";
}

Kompiliert mit "g ++ --std = c ++ 14" ergibt dies Folgendes

Ausgabe

Wenn Sie einen Vorlagentyp drucken möchten, ist dies einfach.

T = doppelt

Um es von einer Objektinstanz zu erhalten, verwenden Sie einfach decltype:

Der Typ von fb ist: foo_bar

Spacemoose
quelle
Ist es damit möglich, nur Klassennamen ohne Namespaces zu erhalten? aka coliru.stacked-crooked.com/a/cf1b1a865bb7ecc7
Tower120
9

Noch nicht. (Ich denke __class__, irgendwo vorgeschlagen). Sie können auch versuchen, einen Klassenteil aus zu extrahieren __PRETTY_FUNCTION__.

Michael Krelin - Hacker
quelle
7

Ich denke, die Verwendung __PRETTY_FUNCTION__ist gut genug, obwohl sie auch einen Namespace enthält, dh namespace::classname::functionnamebis __CLASS__verfügbar ist.

Vaibhav
quelle
4

Wenn Ihr Compiler zufällig ist g++und Sie danach fragen, __CLASS__weil Sie eine Möglichkeit suchen, den aktuellen Methodennamen einschließlich der Klasse abzurufen , __PRETTY_FUNCTION__sollte dies helfen (gemäß info gccAbschnitt 5.43 Funktionsnamen als Zeichenfolgen ).

ndim
quelle
3

Wenn Sie etwas benötigen, das beim Kompilieren tatsächlich den Klassennamen erzeugt, können Sie dazu C ++ 11 verwenden:

#define __CLASS__ std::remove_reference<decltype(classMacroImpl(this))>::type

template<class T> T& classMacroImpl(const T* t);

Ich erkenne, dass dies nicht dasselbe ist wie, __FUNCTION__aber ich habe diesen Beitrag gefunden , als ich nach einer Antwort wie dieser gesucht habe. : D.

Jon Thompson
quelle
2

Wenn Sie MS C ++ Rede ist (Sie sollten angeben, besonders als __FUNCTION__ein Nicht-Standard - Erweiterung ist), gibt es __FUNCDNAME__und __FUNCSIG__Symbole , die Sie nicht analysieren

Ruben Bartelink
quelle
2

Sie können den Funktionsnamen einschließlich des Klassennamens abrufen. Dies kann C-Typ-Funktionen verarbeiten.

static std::string methodName(const std::string& prettyFunction)
{
    size_t begin,end;
    end = prettyFunction.find("(");
    begin = prettyFunction.substr(0,end).rfind(" ") + 1;
    end -= begin;
    return prettyFunction.substr(begin,end) + "()";
}
Charles.Lee
quelle
1

Meine Lösung:

std::string getClassName(const char* fullFuncName)
{
    std::string fullFuncNameStr(fullFuncName);
    size_t pos = fullFuncNameStr.find_last_of("::");
    if (pos == std::string::npos)
    {
        return "";
    }
    return fullFuncNameStr.substr(0, pos-1);
}

#define __CLASS__ getClassName(__FUNCTION__)

Ich arbeite für Visual C ++ 12.

Andrey Epifantsev
quelle
1

Hier ist eine Lösung, die auf den __FUNCTION__Makro- und C ++ - Vorlagen basiert :

template <class T>
class ClassName
{
public:
  static std::string Get()
  {
    // Get function name, which is "ClassName<class T>::Get"
    // The template parameter 'T' is the class name we're looking for
    std::string name = __FUNCTION__;
    // Remove "ClassName<class " ("<class " is 7 characters long)
    size_t pos = name.find_first_of('<');
    if (pos != std::string::npos)
      name = name.substr(pos + 7);
    // Remove ">::Get"
    pos = name.find_last_of('>');
    if (pos != std::string::npos)
      name = name.substr(0, pos);
    return name;
  }
};

template <class T>
std::string GetClassName(const T* _this = NULL)
{
  return ClassName<T>::Get();
}

Hier ist ein Beispiel, wie dies für eine Logger-Klasse verwendet werden kann

template <class T>
class Logger
{
public:
  void Log(int value)
  {
    std::cout << GetClassName<T>()  << ": " << value << std::endl;
    std::cout << GetClassName(this) << ": " << value << std::endl;
  }
};

class Example : protected Logger<Example>
{
public:
  void Run()
  {
    Log(0);
  }
}

Die Ausgabe von Example::Runwird dann sein

Example: 0
Logger<Example>: 0
Sven Vranckx
quelle
Beachten Sie, dass dies den Polymorphismus nicht berücksichtigt, wenn Sie einen Zeiger auf die Basis haben (was wahrscheinlich in Ordnung ist).
Leichtigkeitsrennen im Orbit
0

Dies funktioniert ganz gut, wenn Sie bereit sind, die Kosten für einen Zeiger zu bezahlen.

class State 
{
public:
    State( const char* const stateName ) :mStateName( stateName ) {};
    const char* const GetName( void ) { return mStateName; }
private:
    const char * const mStateName;
};

class ClientStateConnected
    : public State
{
public:
    ClientStateConnected( void ) : State( __FUNCTION__ ) {};
};
Robert Basler
quelle
0

Funktioniert auch mit msvc und gcc

#ifdef _MSC_VER
#define __class_func__ __FUNCTION__
#endif

#ifdef __GNUG__
#include <cxxabi.h>
#include <execinfo.h>
char *class_func(const char *c, const char *f)
{
    int status;
    static char buff[100];
    char *demangled = abi::__cxa_demangle(c, NULL, NULL, &status);
    snprintf(buff, sizeof(buff), "%s::%s", demangled, f);
    free(demangled);
    return buff;
}
#define __class_func__ class_func(typeid(*this).name(), __func__)
#endif
Costa
quelle
0

Alle oben aufgeführten Lösungen, die sich auf __PRETTY_FUNCTION__do stützen , haben bestimmte Randfälle, in denen sie nicht nur den Klassennamen / Klassennamen zurückgeben. Betrachten Sie zum Beispiel den folgenden hübschen Funktionswert:

static std::string PrettyFunctionHelper::Test::testMacro(std::string)

Die Verwendung des letzten Vorkommens "::"als Trennzeichen funktioniert nicht, da der Funktionsparameter auch ein "::"( std::string) enthält . Sie können ähnliche Randfälle "("als Trennzeichen und mehr finden. Die einzige Lösung, die ich gefunden habe, verwendet sowohl das __FUNCTION__als auch das __PRETTY_FUNCTION__Makro als Parameter. Hier ist der vollständige Code:

namespace PrettyFunctionHelper{
    static constexpr const auto UNKNOWN_CLASS_NAME="UnknownClassName";
    /**
     * @param prettyFunction as obtained by the macro __PRETTY_FUNCTION__
     * @return a string containing the class name at the end, optionally prefixed by the namespace(s).
     * Example return values: "MyNamespace1::MyNamespace2::MyClassName","MyNamespace1::MyClassName" "MyClassName"
     */
    static std::string namespaceAndClassName(const std::string& function,const std::string& prettyFunction){
        //AndroidLogger(ANDROID_LOG_DEBUG,"NoT")<<prettyFunction;
        // Here I assume that the 'function name' does not appear multiple times. The opposite is highly unlikely
        const size_t len1=prettyFunction.find(function);
        if(len1 == std::string::npos)return UNKNOWN_CLASS_NAME;
        // The substring of len-2 contains the function return type and the "namespaceAndClass" area
        const std::string returnTypeAndNamespaceAndClassName=prettyFunction.substr(0,len1-2);
        // find the last empty space in the substring. The values until the first empty space are the function return type
        // for example "void ","std::optional<std::string> ", "static std::string "
        // See how the 3rd example return type also contains a " ".
        // However, it is guaranteed that the area NamespaceAndClassName does not contain an empty space
        const size_t begin1 = returnTypeAndNamespaceAndClassName.rfind(" ");
        if(begin1 == std::string::npos)return UNKNOWN_CLASS_NAME;
        const std::string namespaceAndClassName=returnTypeAndNamespaceAndClassName.substr(begin1+1);
        return namespaceAndClassName;
    }
    /**
     * @param namespaceAndClassName value obtained by namespaceAndClassName()
     * @return the class name only (without namespace prefix if existing)
     */
    static std::string className(const std::string& namespaceAndClassName){
        const size_t end=namespaceAndClassName.rfind("::");
        if(end!=std::string::npos){
            return namespaceAndClassName.substr(end+2);
        }
        return namespaceAndClassName;
    }
    class Test{
    public:
        static std::string testMacro(std::string exampleParam=""){
            const auto namespaceAndClassName=PrettyFunctionHelper::namespaceAndClassName(__FUNCTION__,__PRETTY_FUNCTION__);
            //AndroidLogger(ANDROID_LOG_DEBUG,"NoT2")<<namespaceAndClassName;
            assert(namespaceAndClassName.compare("PrettyFunctionHelper::Test") == 0);
            const auto className=PrettyFunctionHelper::className(namespaceAndClassName);
            //AndroidLogger(ANDROID_LOG_DEBUG,"NoT2")<<className;
            assert(className.compare("Test") == 0);
            return "";
        }
    };
}
#ifndef __CLASS_NAME__
#define __CLASS_NAME__ PrettyFunctionHelper::namespaceAndClassName(__FUNCTION__,__PRETTY_FUNCTION__)
#endif
Constantin Geier
quelle
-2

Die folgende Methode (basierend auf methodName () oben) kann auch Eingaben wie "int main (int argc, char ** argv)" verarbeiten:

string getMethodName(const string& prettyFunction)
{
    size_t end = prettyFunction.find("(") - 1;
    size_t begin = prettyFunction.substr(0, end).rfind(" ") + 1;

    return prettyFunction.substr(begin, end - begin + 1) + "()";
}
Martin
quelle