在编译时复制C / C ++函数

时间:2011-06-17 01:16:51

标签: c++ templates c-preprocessor inlining

如果我有一个函数A(),我有兴趣找到一个方便的方法来创建一个函数B(),它具有与A()完全相同的功能,仅在名称上有所不同。新功能将是一次性使用。目的是在一个有点原始的抽样分析器中区分对相同函数的调用,并且复制函数仅在此上下文中使用。也就是说,它永远不会触及生产代码,只能用于修补。

首先猜测是一个宏,它声明一个名为B的函数,并在其内部创建一个对A()的内联调用。这里的问题是我不知道GCC中强制任意函数调用内联的方法;似乎所有内联选项都是函数声明而不是调用。

使用模板可能有一些深奥的方法,或者可能通过欺骗编译器进行内联。我不确定这是可能的。有什么想法吗?不幸的是,新的C ++标准不可用,如果它会有所作为。

7 个答案:

答案 0 :(得分:6)

使用模板

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

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

<强>更新

编译器可能太聪明并且仅为A&lt; 0&gt;创建一个主体。和A&lt; 1&gt;。至少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;
}

答案 1 :(得分: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确认了这一点。

答案 2 :(得分:2)

如果这是一次性调试黑客,那么为什么不:

#define A_CONTENT \
    ... // whatever

void A()
{
    A_CONTENT
}

void B()
{
    A_CONTENT
}

...

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

宏一般都很严峻,但我们不是在谈论生产代码,所以谁在乎呢?

答案 3 :(得分:2)

我自己走了这条路,简而言之就是即使你让编译器发出两个相同的函数副本,优化链接器也会注意到它们是相同的并且fold them back together成为一个实现。 (如果您已关闭链接器中的优化,那么您的配置文件无效anwyay)。

在采样分析器的上下文中,我发现更简单的方法是为函数制作两个小包装器:

void Func() { .... }

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

然后,当您的探查器对callstack进行采样时,您将能够以非常直接的方式区分此函数的不同调用...

答案 4 :(得分: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回调。

答案 5 :(得分:0)

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

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

答案 6 :(得分:0)

为什么你如此关心内联呢?如果你创建一个包装器函数,编译器很可能无论如何都会内联它。至少,你不太可能得到一个功能框架。

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

void A() {
    ...
}

...

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

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