3

関数 がある場合、名前だけが異なるだけで、とまったく同じ機能を持つA()関数を作成する便利な方法を見つけることに興味があります。新しい機能は 1 回限りの使用です。その意図は、やや原始的なサンプリング プロファイラーで同じ関数の呼び出しを区別することであり、複製された関数はこのコンテキストでのみ使用されます。つまり、本番コードに触れることはなく、いじくり回しにのみ使用されます。B()A()

最初に推測されるのは、という名前の関数を宣言し、その内部Bへのインライン呼び出しを作成するマクロです。A()ここでの問題は、インラインへの任意の関数呼び出しを強制する GCC のメソッドを認識していないことです。すべてのインライン化オプションは、呼び出しではなく関数宣言用のようです。

テンプレートを使用するか、コンパイラをだましてインライン化することによって、それを行う難解な方法があるかもしれません。可能かどうかわかりません。何かご意見は?残念ながら、違いがあるとしても、新しい C++ 標準は利用できません。

4

7 に答える 7

7

テンプレートの使用

template<int x>
void A()
{
    // ..
}

int main()
{
    A<0>();
    A<1>();
    return 0;
}

アップデート

コンパイラが賢すぎて、A<0> と A<1> に対して 1 つの本体しか作成しないことがあります。少なくとも Visual C++ 2010 はリリース モードでそれを行います。これを防ぐには、ログまたはアサートの関数テンプレート本体内でテンプレート パラメーターを使用します。例えば、

#include <iostream>

template<int x>
void A()
{
    ::std::cout << x << std::endl;
    // ..
}

int main()
{
    A<0>();
    A<1>();
    auto v0 = A<0>;
    auto v1 = A<1>;
    ::std::cout << v0 << std::endl;
    ::std::cout << v1 << std::endl;
    ::std::cout << (v0 == v1) << std::endl;
    return 0;
}
于 2011-06-17T01:33:43.807 に答える
3

これはテンプレートを使用して機能します。

#include <iostream>                                                             

template<typename T>
void foo() {
    static int x = 0;
    std::cout << &x << std::endl;
}

int main(int argc, char **argv) {
    foo<int>();
    foo<float>();
    return 0;
}

これを実行すると、テンプレート パラメーターが使用されていない場合でも、両方の呼び出しに対してコンパイラが生成したコードを反映して、2 つの異なる値が出力されます。nmオブジェクトファイルでこれを確認します。

于 2011-06-17T01:33:45.260 に答える
2

私自身この道を歩んできたので、短い答えは、コンパイラーに関数の2つの同一の複製を発行させたとしても、最適化リンカーはそれらが同一であることに気づき、それらを1つの実装にまとめるということです。(また、リンカーで最適化をオフにしている場合、プロファイルは有効ではありません)。

サンプリング プロファイラーのコンテキストでは、関数の代わりに 2 つの小さなラッパーを作成する方が簡単な方法であることがわかりました。

void Func() { .... }

_declspec(noinline) 
void A_Func( return Func(); }
void B_Func( return Func(); }
void C_Func( return Func(); }

次に、プロファイラーがコールスタックをサンプリングすると、この関数のさまざまな呼び出しサイトを非常に簡単な方法で区別できます..

于 2011-06-17T01:34:39.017 に答える
2

これが 1 回限りのデバッグ ハックである場合、その理由は次のとおりです。

#define A_CONTENT \
    ... // whatever

void A()
{
    A_CONTENT
}

void B()
{
    A_CONTENT
}

...

A();  // Call to A
B();  // Call to B  

マクロは一般的に厳しいですが、ここでは製品コードについて話しているわけではないので、誰が気にしますか?

于 2011-06-17T01:28:42.140 に答える
1

いつでもマクロを定義できます。たとえば、Chromiumではコードを再利用するために次のことを行います。

#define CHROMEG_CALLBACK_1(CLASS, RETURN, METHOD, SENDER, ARG1)     \
  static RETURN METHOD ## Thunk(SENDER sender, ARG1 one,            \
                                gpointer userdata) {                \
    return reinterpret_cast<CLASS*>(userdata)->METHOD(sender, one); \
  }                                                                 \
                                                                    \
  virtual RETURN METHOD(SENDER, ARG1);

そして、私たちはそれらを次のように呼びます:

 CHROMEGTK_CALLBACK_1(PageActionViewGtk, gboolean, OnExposeEvent, GdkEventExpose*);

 CHROMEGTK_CALLBACK_1(PageActionViewGtk, gboolean, OnButtonPressed, GdkEventButton*);

やりたいことと同じようなことをすることができます。上記の例では、2 つの異なる実装を使用していますが、1 つの共通コード ベースを使用しています。GTK コールバック用。

于 2011-06-17T01:28:43.560 に答える
0

なぜあなたはそれをインライン化することにそれほど気を配っていますか?ラッパー関数を作成すると、コンパイラがそれをインライン化する可能性がかなり高くなります。少なくとも、関数フレームが構築される可能性はほとんどありません。

C++11 では、次のこともできます。

void A() {
    ...
}

...

auto B = [] () -> void { A(); };

これで、A をラップする関数であるかのように B を構文的に使用できるようになりました。

于 2012-08-21T09:53:46.467 に答える
0

あなたが実際に何をしようとしているのかは少し不明ですが、本当に醜い解決策は、 A の本体をマクロとして宣言し、このマクロを好きな関数内に「インライン化」することです。

また、マクロは悪です。本当に必要な場合を除き、決して使用しないでください。

于 2011-06-17T01:30:10.063 に答える