web-dev-qa-db-ja.com

std :: functionの名前を取得する

次のおもちゃの例では、関数の名前を取得したいと思います。関数自体がstd::function引数として渡されました。 C++では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

そうでなければ、関数の名前を2番目のパラメーターとして指定する必要があります。

22
hr0m

いいえ、ありません。関数名(変数名など)はコンパイルされ、実行時に表示されません。

あなたの最善の策は、関数の名前を渡すことです(std::stringまたはconst char*)あなたが提案したとおり。 (あるいは、あなたcould__func__はC++ 11で導入されました。)

27
Bathsheba

答えはノーですが、あなたは次のようなものを作ることができます

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);
13
thorsan

std::functionを指定すると、格納されている関数オブジェクトのtypeidを返すtarget_typeというメンバー関数があります。それはあなたができることを意味します

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>を使用して、人間が読めるバージョンの型名を取得する必要があります。

[〜#〜]編集[〜#〜]
Matthieu M.が指摘したように、関数ポインタが与えられた場合、これによって返される型は関数のシグネチャになります。例えば:

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

これは(必要に応じて解体されたと想定して)int (*)()を出力しますが、これは関数名ではありません。

ただし、このメソッドはクラスで機能します。

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

printName(Function{});

これは、必要に応じてFunctionを出力しますが、関数ポインターでは機能しません。

7
SirGuy

名前の文字列パラメータを関数に設定し、マクロを使用してそれを呼び出すこともできます

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

例を参照してください

2
Urban

関数ポインタから名前への独自のマップを維持します。

_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_()
_

名前magicを関数magicに登録するには、単にREGISTER_NAME(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;
}
_

これは一般に悪い考えです。