Получить имя std::function

В следующем игрушечном примере я хотел бы получить имя функции. Сама функция была задана как аргумент std::function. Возможно ли в С++ получить имя объекта std::function?

void printName(std::function<void()> func){
    //Need a function name()
    std::cout << func.name();
}

void magic(){};

//somewhere in the code
printName(magic());

output: magic

В противном случае мне пришлось бы указать имя функции в качестве второго параметра.


person hr0m    schedule 31.05.2016    source источник
comment
Является ли этот пример полным? Не могли бы вы просто использовать препроцессор TOSTRING для преобразования магии в строку? printName (TOSTRING (магия))   -  person thorsan    schedule 31.05.2016
comment
Это невозможно. После компиляции вы теряете всю подобную информацию, C++ не имеет никакой интроспекции типов . Единственная возможность зависит от компилятора, используя отладочную информацию, чтобы попытаться выяснить это.   -  person Some programmer dude    schedule 31.05.2016
comment
std::function не нужно ничего инкапсулировать в имя...   -  person Kuba hasn't forgotten Monica    schedule 31.05.2016
comment
Зачем тебе вообще это делать?   -  person KaareZ    schedule 01.06.2016
comment
printName(magic()); не будет компилироваться.   -  person user253751    schedule 01.06.2016
comment
Если бы было решено следующее, мы могли бы использовать .target для решения проблемы: stackoverflow.com/questions/40706805/   -  person Ciro Santilli 新疆再教育营六四事件ۍ    schedule 02.03.2020


Ответы (6)


Нет, нет. Имена функций (как и имена переменных) скомпилированы, поэтому они не видны во время выполнения.

Лучше всего передать имя функции (используйте std::string или const char*), как вы предложили сами. (В качестве альтернативы вы можете основывать решение на __func__, представленном в C++11.)

person Bathsheba    schedule 31.05.2016

Ответ - нет, но вы можете сделать что-то вроде

template<class R, class... Args>
class NamedFunction
{
public:
    std::string name;
    std::function<R(Args...)> func;
    NamedFunction(std::string pname, std::function<R(Args...)> pfunc) : name(pname), func(pfunc)
    {}
    R operator()(Args&&... a)
    {
       return func(std::forward<Args>(a)...);
    }
};

И затем определите препроцессор

#define NAMED_FUNCTION(var, type, x) NamedFunction<type> var(#x,x)
...
NAMED_FUNCTION(f, void(), magic);
person thorsan    schedule 31.05.2016

Учитывая std::function, у него есть функция-член с именем target_type, которая возвращает typeid объекта сохраненной функции. Это означает, что вы можете сделать

void printName(std::function<void()> func){
    //Need a function name()
    std::cout << func.target_type().name();
}

Это возвращает определяемую реализацией строку, уникальную для каждого типа. В Visual Studio эта строка уже удобочитаема. С gcc (или, может быть, это glibc? Я не знаю, кто о чем подробно заботится) вам нужно использовать abi::__cxa_demangle после включения <cxxabi.h>, чтобы получить удобочитаемую версию имени типа.

EDIT
Как заметил Матье М., при наличии указателя на функцию возвращаемый им тип будет просто сигнатурой функции. Например:

int function(){return 0;}
printName(function);

Это выведет (при условии, что вы при необходимости разобрали) int (*)(), которое не является именем функции.

Однако этот метод будет работать с классами:

struct Function
{
    int operator()(){return 0;}
};

printName(Function{});

Это напечатает Function по желанию, но не работает для указателей функций.

person SirGuy    schedule 31.05.2016
comment
а Visual Studio на самом деле MSVC: p - person coyotte508; 01.06.2016
comment
Существует большая разница между именем функции и ее сигнатурой; на gcc я получаю PFvvE в качестве отображения для void magic(), поскольку кодируется только подпись. Не уверен насчет MSVC. - person Matthieu M.; 01.06.2016

Вы также можете иметь свою функцию со строковым параметром для имени, а затем использовать макрос для ее вызова.

void _printName(std::function<void()> func, const std::string& funcName){
    std::cout << funcName;
}
#define printName(f) _printName(f, #f)

void magic(){};

//somewhere in the code
printName(magic);

См. пример

person Urban    schedule 03.06.2016

Поддерживайте свою собственную карту от указателя функции до имени.

template<class Sig>
std::map<Sig*, const char*>& name_map() {
  static std::map<Sig*, const char*> r;
  return r;
}

struct register_name_t {
  template<class Sig>
  register_name_t( Sig* sig, const char* name ) {
    name_map()[sig]=name;
  }
};
#define TO_STRING(A) #A
#define REGISTER_NAME(FUNC) \
   register_name_t FUNC ## _register_helper_() { \
     static register_name_t _{ FUNC, TO_STRING(FUNC) }; \
     return _; \
   } \
   static auto FUNC ## _registered_ = FUNC ## _register_helper_()

Просто выполните REGISTER_NAME(magic);, чтобы зарегистрировать имя magic в функции magic. Это должно быть сделано в области файла, либо в заголовке, либо в файле cpp.

Теперь мы проверяем, есть ли внутри std::function указатель на функцию, соответствующий его сигнатуре. Если это так, мы ищем его в нашем name_map и возвращаем имя, если находим:

template<class Sig>
std::string get_function_name( std::function<Sig> const& f ) {
  auto* ptr = f.target<Sig*>();
  if (!ptr) return {};
  auto it = name_map().find(ptr);
  if (it == name_map().end()) return {};
  return it->second;
}

это вообще плохая идея.

person Yakk - Adam Nevraumont    schedule 31.05.2016

Я думаю, что самое простое решение - использовать typeid(fun).name(), например, так:

#include <typeinfo>

#include <stdio.h>

void foobar( void )
{
}

int main()
{
  printf( "%s\n", typeid( foobar ).name() );
  return 0;
}

Теперь у него много недостатков, и я бы не рекомендовал его использовать. Прежде всего, IIRC показывает символ функции, а не имя, которое вы использовали в исходном коде. Кроме того, имя будет меняться от компилятора к компилятору. И, наконец, RTTI медленный.

[править] Кроме того, я не уверен, как это работает с std::function. Никогда этим не пользовался, честно.

person user3459474    schedule 31.05.2016
comment
Имя будет типа std::function<...>, а не того, что std::function содержит. - person Nicol Bolas; 31.05.2016