Есть ли в C ++ макрос __CLASS__?

Есть ли в C ++ макрос __CLASS__, который дает имя класса, подобное макросу __FUNCTION__, которое дает имя функции


person mortal    schedule 03.11.2009    source источник


Ответы (16)


Ближайший вариант - вызвать typeid(your_class).name(), но это дает искореженное имя компилятора.

Чтобы использовать его внутри класса, просто typeid(*this).name()

person Aleksei Potov    schedule 03.11.2009
comment
Вы должны сначала узнать свой класс, чтобы вызвать это ;-) - person Michael Krelin - hacker; 03.11.2009
comment
это для ведения журнала, поэтому я знаю класс, в котором вызываю это. Просто пытаюсь избежать явного определения массива символов, содержащего имя класса - person mortal; 03.11.2009
comment
typeid (* this) .name () можно использовать внутри функций класса - person Aleksei Potov; 03.11.2009
comment
Так-то лучше. Что касается знания класса, определение массива char звучит лучше, чем откладывать его до времени выполнения. - person Michael Krelin - hacker; 03.11.2009
comment
Жаль, что он не определен как __ CLASS __, может пригодиться на этапе препроцессора! :( - person k3a; 23.08.2010
comment
@kexik Препроцессор ничего не знает о классах. - person Max; 10.06.2011
comment
@Max Не может, но может. Аналогичным образом он знает о функциях :-P - person k3a; 03.07.2011
comment
@kexik: препроцессор тоже ничего не знает о функциях, стандартные __func__ и нестандартные __FUNCTION__ не являются макросами. Microsoft документирует __FUNCTION__ как макрос, но на самом деле это не так, потому что он не расширяется препроцессором при компиляции с /P. - person Steve Jessop; 04.10.2011

Проблема с использованием typeid(*this).name() заключается в том, что в вызове статического метода нет указателя this. Макрос __PRETTY_FUNCTION__ сообщает имя класса в статических функциях, а также вызовы методов. Однако это будет работать только с gcc.

Вот пример извлечения информации через интерфейс в стиле макроса.

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

Макрос __METHOD_NAME__ вернет строку формы <class>::<method>(), обрезая возвращаемый тип, модификаторы и аргументы из того, что вам дает __PRETTY_FUNCTION__.

Для чего-то, что извлекает только имя класса, нужно позаботиться о том, чтобы перехватить ситуации, когда класса нет:

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__)
person Andrew Prock    schedule 02.04.2013
comment
Не следует ли окружить это #ifdef __GNU_C__? - person einpoklum; 22.03.2014
comment
вместо substr(0,colons).rfind(" ") можно использовать rfind(' ', colons), чтобы не создавать лишнюю строку. - person mariusm; 13.10.2014
comment
Я бы предпочел использовать find_last_of (: :) В противном случае функция вернет пространство имен, только если оно есть - person underdoeg; 19.12.2014
comment
Я написал, возможно, расширенную версию макроса __METHOD_NAME__. Проверьте здесь. - person Antonio; 24.04.2015
comment
В C ++ 11 вы можете попробовать сделать эту функцию constexpr, чтобы оценить ее во время компиляции. - person Andre Holzner; 20.05.2020
comment
andre-holzner: constexpr версию см. в моем решении. - person darkdragon; 16.10.2020

Я хотел бы предложить boost :: typeindex, который Я узнал об этом от Скотта Мейера «Эффективный современный C ++». Вот простой пример:

Пример

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

Скомпилировано с g ++ --std = c ++ 14, это дает следующее

Вывод

Если вы хотите напечатать тип шаблона, это просто.

T = двойной

Чтобы получить его из экземпляра объекта, просто используйте decltype:

Тип fb: foo_bar

person Spacemoose    schedule 27.08.2015
comment
Можно ли с этим получить только имя класса без пространств имен? также известный как coliru.stacked-crooked.com/a/cf1b1a865bb7ecc7 - person tower120; 18.02.2016

Еще нет. (Думаю, __class__ где-то предлагается). Вы также можете попробовать извлечь часть класса из __PRETTY_FUNCTION__.

person Michael Krelin - hacker    schedule 03.11.2009

Я думаю, что использование __PRETTY_FUNCTION__ достаточно хорошо, хотя оно также включает пространство имен, т.е. namespace::classname::functionname, пока __CLASS__ не станет доступным.

person vaibhav    schedule 29.04.2013

Если вам нужно что-то, что действительно создаст имя класса во время компиляции, вы можете использовать C ++ 11 для этого:

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

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

Я понимаю, что это не то же самое, что __FUNCTION__, но я нашел этот пост, когда искал такой ответ. : D

person Jon Thompson    schedule 27.04.2018
comment
Это лучшее решение, которое я когда-либо видел! Это должно быть во время компиляции, а не во время выполнения! - person DrumM; 12.07.2021

Если ваш компилятор g++, и вы запрашиваете __CLASS__, потому что вам нужен способ получить текущее имя метода, включая класс, __PRETTY_FUNCTION__ должен помочь (согласно info gcc, раздел 5.43 Имена функций как строки) .

person ndim    schedule 03.11.2009

Если вы говорите о MS C ++ (вы должны указать, особенно, поскольку __FUNCTION__ - нестандартное расширение), есть _ 2_ и __FUNCSIG__ символы, которые вы можете разобрать

person Ruben Bartelink    schedule 03.11.2009

Вы можете получить имя функции, включая имя класса. Это может обрабатывать функции 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) + "()";
}
person Charles.Lee    schedule 14.05.2013

Мое решение:

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

Я работаю на Visual C ++ 12.

person Andrey Epifantsev    schedule 14.11.2014

Вот решение, основанное на макросе __FUNCTION__ и шаблонах 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();
}

Вот пример того, как это можно использовать для класса регистратора

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

Тогда результат Example::Run будет

Example: 0
Logger<Example>: 0
person Sven Vranckx    schedule 22.01.2015
comment
Обратите внимание, что это не будет учитывать полиморфизм, если у вас есть указатель на базу (что, вероятно, нормально). - person Lightness Races in Orbit; 22.01.2015

Это прекрасно работает, если вы готовы заплатить стоимость указателя.

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__ ) {};
};
person Robert Basler    schedule 26.04.2015

Также работает с msvc и 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
person Costa    schedule 19.11.2015

Все вышеперечисленные решения, основанные на __PRETTY_FUNCTION__, имеют определенные граничные случаи, когда они не возвращают только имя класса / имя класса. Например, рассмотрим следующее красивое значение функции:

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

Использование последнего появления "::" в качестве разделителя не сработает, поскольку параметр функции также содержит "::" (std::string). Вы можете найти похожие крайние случаи для "(" в качестве разделителя и других. Единственное решение, которое я нашел, принимает в качестве параметров макросы __FUNCTION__ и __PRETTY_FUNCTION__. Вот полный код:

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
person Constantin Geier    schedule 11.07.2020

Я создал функцию, используя __PRETTY_FUNCTION__ и constexpr с методами C ++ 17 constexpr std::string_view. Я также немного обновил алгоритм, чтобы он был более надежным (Благодаря @n. 'Местоимения' m за помощь в 64387023).

constexpr std::string_view method_name(const char* s)
{
    std::string_view prettyFunction(s);
    size_t bracket = prettyFunction.rfind("(");
    size_t space = prettyFunction.rfind(" ", bracket) + 1;
    return prettyFunction.substr(space, bracket-space);
}
#define __METHOD_NAME__ method_name(__PRETTY_FUNCTION__)

В C ++ 20 можно объявить функцию как consteval, заставляя ее выполнять оценку во время компиляции. Кроме того, существует _ 7_ для использования в качестве параметра шаблона.

person darkdragon    schedule 16.10.2020

Следующий метод (на основе метода methodName () выше) также может обрабатывать ввод типа «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) + "()";
}
person Martin    schedule 09.02.2017