コンパイル時にC/C ++関数を複製します
-
28-10-2019 - |
質問
関数がある場合 A()
, 、機能を作成する便利な方法を見つけることに興味があります B()
それはまったく同じ機能を持っています A()
, 、名前のみが異なります。新しい関数は、1回限りの使用のためです。意図は、やや原始的なサンプリングプロファイラーで同じ関数に呼び出しを区別することであり、重複した関数はこのコンテキストでのみ使用されます。つまり、生産コードに触れることはなく、いじくり回しにのみ使用されます。
最初の推測は、名前のある関数を宣言するマクロです B
インラインドコールを作成します A()
その中。ここでの問題は、任意の関数呼び出しをインラインに強制するためのGCCの方法を知らないことです。すべての挿入オプションは、呼び出しではなく機能宣言用です。
テンプレートでそれを行うための難解な方法があるかもしれません。または、おそらくコンパイラをインラインでつなぐことによって。それが可能であるかどうかはわかりません。何かご意見は?残念ながら、新しいC ++標準は違いを生む場合、利用できません。
解決
テンプレートを使用します
template<int x>
void A()
{
// ..
}
int main()
{
A<0>();
A<1>();
return 0;
}
アップデート
コンパイラはあまりにもスマートで、<0>と<1>のために1つのボディのみを作成できます。少なくともVisual C ++ 2010は、リリースモードでそれを行います。それを防ぐには、logsまたはassertsで関数テンプレート本体内のテンプレートパラメーターを使用してください。例えば、
#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;
}
他のヒント
これはテンプレートを使用して機能します。
#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
オブジェクトファイルでこれが確認されます。
これが1回限りのデバッグハックである場合、なぜ:
#define A_CONTENT \
... // whatever
void A()
{
A_CONTENT
}
void B()
{
A_CONTENT
}
...
A(); // Call to A
B(); // Call to B
マクロは一般的に厳しいですが、ここでは生産コードについて話しているわけではないので、誰が気にしますか?
自分でこの道を進んでいたので、短い答えは、コンパイラが関数の2つの同一の複製を放出してもらったとしても、最適化リンカーはそれらが同一であることに気付くということです。 それらを一緒に折り返します 1つの実装に。 (そして、リンカーの最適化をオフにした場合、プロファイルはAnwyayに有効ではありません)。
サンプリングプロファイラーのコンテキストでは、より簡単なアプローチは、代わりに関数の2つの小さなラッパーを作成することであることがわかりました。
void Func() { .... }
_declspec(noinline)
void A_Func( return Func(); }
void B_Func( return Func(); }
void C_Func( return Func(); }
次に、プロファイラーがCallStackをサンプリングすると、この関数の異なるコールサイトを非常に簡単な方法で区別できます。
たとえば、マクロをいつでも定義できます。 クロム コードを再利用するために以下を行います。
#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コールバック用。
あなたが本当にやろうとしていることは少し不明ですが、本当にugい解決策は、ボディをマクロとして宣言し、あなたが好きな機能の中でこのマクロを「インライン」できることです。
また、マクロは悪です。あなたがいない限りそれらを使用しないでください 本当 した方が良い。
なぜあなたはそれを象徴することをそんなに気にしますか?ラッパー関数を作成すると、とにかくコンパイラがインラインになる可能性がかなりあります。少なくとも、関数フレームが構築される可能性は低いです。
C ++ 11もこれを行うことができます:
void A() {
...
}
...
auto B = [] () -> void { A(); };
これで、bは機能をラッピングしているかのように構文的に使用できます。