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
Réponses:
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()
__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
.
Le problème avec l'utilisation typeid(*this).name()
est qu'il n'y a pas de this
pointeur 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__)
#ifdef __GNU_C__
?
substr(0,colons).rfind(" ")
un pourrait utiliser rfind(' ', colons)
pour épargner la création d'une chaîne supplémentaire.
constexpr
fonction pour l'évaluer au moment de la compilation
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
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__
.
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É
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
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) + "()";
}
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.
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::Run
sera alors
Example: 0
Logger<Example>: 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__ ) {};
};
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
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
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) + "()";
}