Existe-t-il une macro __CLASS__ en C ++?

100

Existe-t-il une __CLASS__macro en C ++ qui donne le nom de la classe similaire à la __FUNCTION__macro qui donne le nom de la fonction

mortel
la source

Réponses:

67

La chose la plus proche est d'appeler typeid(your_class).name()- mais cela produit un nom mutilé spécifique au compilateur.

Pour l'utiliser dans la classe juste typeid(*this).name()

Aleksei Potov
la source
2
typeid (* this) .name () peut être utilisé à l'intérieur des fonctions de classe
Aleksei Potov
2
C'est mieux. En ce qui concerne la connaissance de la classe, définir un tableau de caractères sonne mieux que de le reporter à l'exécution.
Michael Krelin - hacker
5
C'est dommage qu'il ne soit pas défini comme __ CLASS __, cela peut être pratique au stade du préprocesseur! :(
k3a
2
@Max Il ne le fait pas mais pourrait. De la même manière, il connaît les fonctions :-P
k3a
5
@kexik: le préprocesseur ne connaît pas non plus les fonctions, standard __func__et non standard __FUNCTION__ne sont pas des macros. Microsoft documente __FUNCTION__sous forme de macro, mais ce n'est pas vraiment le cas, c'est qu'il n'est pas étendu par le préprocesseur lorsque vous compilez avec /P.
Steve Jessop
77

Le problème avec l'utilisation typeid(*this).name()est qu'il n'y a pas de thispointeur dans un appel de méthode statique. La macro __PRETTY_FUNCTION__signale un nom de classe dans les fonctions statiques ainsi que les appels de méthode. Cependant, cela ne fonctionnera qu'avec gcc.

Voici un exemple d'extraction des informations via une interface de style macro.

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__)

La macro __METHOD_NAME__renverra une chaîne du formulaire <class>::<method>(), découpant le type de retour, les modificateurs et les arguments de ce __PRETTY_FUNCTION__qui vous donne.

Pour quelque chose qui extrait uniquement le nom de la classe, il faut prendre soin de capturer les situations où il n'y a pas de classe:

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
la source
5
Ne devriez-vous pas entourer cela #ifdef __GNU_C__?
einpoklum
1
au lieu d' substr(0,colons).rfind(" ")un pourrait utiliser rfind(' ', colons)pour épargner la création d'une chaîne supplémentaire.
mariusm
1
Je préfère utiliser find_last_of ("::") Sinon, la fonction ne retournera un espace de noms que s'il y en a un
underdoeg
J'ai écrit une version peut-être plus large de la __METHOD_NAME__macro. Vérifiez ici .
Antonio
En C ++ 11, vous pouvez essayer d'en faire une constexprfonction pour l'évaluer au moment de la compilation
Andre Holzner
11

Je voudrais suggérer boost :: typeindex , que j'ai appris de Scott Meyer "Effective Modern C ++" Voici un exemple de base:

Exemple

#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";
}

Compilé avec "g ++ --std = c ++ 14" cela produit ce qui suit

Production

Si vous souhaitez imprimer un type de modèle, rien de plus simple.

T = double

Pour l'obtenir à partir d'une instance d'objet, utilisez simplement decltype:

Le type de fb est: foo_bar

Spacemoose
la source
Est-il possible d'obtenir juste un nom de classe sans espaces de noms avec ça? aka coliru.stacked-crooked.com/a/cf1b1a865bb7ecc7
tower120
10

Pas encore. (Je pense que __class__c'est proposé quelque part). Vous pouvez également essayer d'extraire une partie de classe à partir de __PRETTY_FUNCTION__.

Michael Krelin - hacker
la source
7

Je pense que l'utilisation __PRETTY_FUNCTION__est assez bonne même si elle inclut également un espace de noms, c'est- namespace::classname::functionnameà- dire jusqu'à ce qu'il __CLASS__soit disponible.

vaibhav
la source
4

Si votre compilateur se trouve être g++et que vous le demandez __CLASS__parce que vous voulez un moyen d'obtenir le nom de la méthode actuelle, y compris la classe, cela __PRETTY_FUNCTION__devrait aider (conformément à la info gccsection 5.43 Noms de fonctions sous forme de chaînes ).

ndim
la source
3

Si vous avez besoin de quelque chose qui produira réellement le nom de la classe au moment de la compilation, vous pouvez utiliser C ++ 11 pour le faire:

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

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

Je reconnais que ce n'est pas la même chose que __FUNCTION__mais j'ai trouvé ce post en cherchant une réponse comme celle-ci. :RÉ

Jon Thompson
la source
2

Si vous parlez MS C ++ (Vous devez indiquer, surtout comme __FUNCTION__une extension non standard), il y a __FUNCDNAME__et __FUNCSIG__symboles que vous pouvez parser

Ruben Bartelink
la source
2

Vous pouvez obtenir le nom de la fonction, y compris le nom de la classe. Cela peut traiter des fonctions de type C.

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
la source
1

Ma solution:

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__)

Je travaille pour Visual C ++ 12.

Andrey Epifantsev
la source
1

Voici une solution basée sur les __FUNCTION__modèles de macro et C ++:

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();
}

Voici un exemple de la façon dont cela pourrait être utilisé pour une classe de journalisation

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

La sortie de Example::Runsera alors

Example: 0
Logger<Example>: 0
Sven Vranckx
la source
Notez que cela ne prendra pas en compte le polymorphisme si vous avez un pointeur vers la base (ce qui est probablement très bien).
Courses de légèreté en orbite le
0

Cela fonctionne très bien si vous êtes prêt à payer le coût d'un pointeur.

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
la source
0

Fonctionne aussi avec msvc et 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
la source
0

Toutes les solutions publiées ci-dessus qui reposent sur le __PRETTY_FUNCTION__ont un ou des cas de bord spécifiques où ils ne renvoient pas uniquement le nom de la classe / nom de la classe. Par exemple, considérez la jolie valeur de fonction suivante:

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

L'utilisation de la dernière occurrence de "::"comme délimiteur ne fonctionnera pas car le paramètre de fonction contient également un "::"( std::string). Vous pouvez trouver des cas de bord similaires pour "("comme délimiteur et plus encore. La seule solution que j'ai trouvée prend les macros __FUNCTION__et __PRETTY_FUNCTION__comme paramètres. Voici le code complet:

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
la source
-2

La méthode suivante (basée sur methodName () ci-dessus) peut également gérer une entrée comme "int main (int argc, char ** argv)":

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
la source