在编译时复制C/C++函数
Duplicating C/C++ functions at compile time
如果我有一个函数A()
,我有兴趣找到一种方便的方法来创建一个与A()
功能完全相同、只是名称不同的函数B()
。新功能将一次性使用。其目的是在某种程度上原始的采样探查器中区分对同一函数的调用,并且重复的函数只能在该上下文中使用。也就是说,它永远不会触及生产代码,只能用于修补。
首先猜测的是一个宏,它声明了一个名为B
的函数,并在其中创建了一个对A()
的内联调用。这里的问题是,我不知道GCC中有什么方法可以强制任意函数调用内联;似乎所有的内联选项都是用于函数声明而不是调用的。
使用模板可能有一些深奥的方法,或者可能通过欺骗编译器进行内联。我不确定这是否可能。有什么想法吗?不幸的是,新的C++标准不可用,如果它能带来不同的话。
使用模板
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;
}
这使用模板工作:
#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
证实了这一点。
如果这是一次性调试破解,那么为什么不呢:
#define A_CONTENT
... // whatever
void A()
{
A_CONTENT
}
void B()
{
A_CONTENT
}
...
A(); // Call to A
B(); // Call to B
宏通常很可怕,但我们在这里谈论的不是生产代码,所以谁在乎呢?
我自己也经历过这条路,简单的答案是,即使你让编译器发出一个函数的两个相同副本,优化链接器也会注意到它们是相同的,并将它们重新组合到一个实现中。(如果你关闭了链接器中的优化,那么你的配置文件也无效)。
在采样探查器的上下文中,我发现更简单的方法是为函数制作两个小包装器:
void Func() { .... }
_declspec(noinline)
void A_Func( return Func(); }
void B_Func( return Func(); }
void C_Func( return Func(); }
然后,当您的探查器对调用堆栈进行采样时,您将能够以一种非常简单的方式区分该函数的不同调用位置。。
您可以始终定义一个宏,例如在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回调。
目前还不清楚你真正想做什么,但一个非常糟糕的解决方案是将a的主体声明为宏,然后你可以在任何你喜欢的函数中"内联"这个宏。
此外,宏也是邪恶的。除非你真的必须使用,否则永远不要使用它们。
为什么如此关心内联?如果您创建了一个包装函数,那么编译器很有可能会内联它。至少,你不太可能构建一个函数框架。
C++11也可以让你做到这一点:
void A() {
...
}
...
auto B = [] () -> void { A(); };
现在,您可以在语法上使用B,就好像它是一个包装a的函数一样。
- "error: no matching function for call to"构造函数错误
- 什么时候调用组成单元对象的析构函数
- 继承函数的重载解析
- 为什么随机数生成器不在void函数中随机化数字,而在main函数中随机化
- C++模板来检查友元函数的存在
- 递归函数计算序列中的平方和(并输出过程)
- 对RValue对象调用的LValue ref限定成员函数
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 将数组作为参数传递给函数安全吗?作为第三方职能部门,可以探索他们想要的之外的其他元素
- 在C++STL中是否有Polyval(Matlab函数)等价物?
- 为什么使用 "this" 指针调用派生成员函数?
- 将对象数组的引用传递给函数
- 函数调用中参数的顺序重要吗
- 函数向量_指针有不同的原型,我可以构建一个吗
- 使用不带参数的函数访问结构元素
- 代码在main()中运行,但在函数中出现错误
- 内置函数可查看CPP中的成员变量
- 如何获取std::result_of函数的返回类型
- 如何在c++中为模板函数实例创建快捷方式
- 如果C++类在类方法中具有动态分配,但没有构造函数/析构函数或任何非静态成员,那么它仍然是POD类型吗