3

如果我有一个 function A(),我有兴趣找到一种方便的方法来创建一个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> 创建一个主体。至少 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;
}

如果执行该操作,您将看到打印的两个不同值,反映编译器为这两个调用生成的代码,即使模板参数未使用。nm目标文件上证实了这一点。

于 2011-06-17T01:33:45.260 回答
2

如果这是一次性调试技巧,那为什么不呢:

#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 回答
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 回答
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*);

你可以做类似的事情来做你想做的事。上面的示例向我们展示了使用两种不同的实现,但使用一个通用代码库。对于 GTK 回调。

于 2011-06-17T01:28:43.560 回答
0

有点不清楚你真正想要做什么,但一个非常丑陋的解决方案是将 A 的主体声明为宏,然后你可以在你喜欢的任何函数中“内联”这个宏。

此外,宏是邪恶的。除非你真的必须,否则永远不要使用它们。

于 2011-06-17T01:30:10.063 回答
0

为什么你这么关心内联它?如果你创建一个包装函数,编译器很有可能会内联它。至少,您不太可能构建功能框架。

C++11 也允许你这样做:

void A() {
    ...
}

...

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

您现在可以在语法上使用 B,就好像它是一个包装 A 的函数一样。

于 2012-08-21T09:53:46.467 回答