请考虑以下代码:
file_1.hpp:
typedef void (*func_ptr)(void);
func_ptr file1_get_function(void);
file1.cpp:
// file_1.cpp
#include "file_1.hpp"
static void some_func(void)
{
do_stuff();
}
func_ptr file1_get_function(void)
{
return some_func;
}
file2.cpp
#include "file1.hpp"
void file2_func(void)
{
func_ptr function_pointer_to_file1 = file1_get_function();
function_pointer_to_file1();
}
虽然我相信上面的例子在技术上是可行的 - 仅通过函数指针调用具有内部链接的函数,这样做是不好的做法吗?是否会出现一些时髦的编译器优化(例如自动内联),这会使这种情况出现问题?
答案 0 :(得分:1)
没问题,这很好。事实上,恕我直言,这是一个很好的做法,它可以调用你的函数,而不会污染外部可见符号的空间。
在函数查找表的上下文中使用此技术也是合适的,例如,一个计算器,它传入一个表示操作符名称的字符串,并期望返回一个函数指针来执行该操作。
不允许编译器/链接器进行优化,从而破坏正确的代码,这是正确的代码。
历史记录:在C89中,外部可见符号在前6个字符中必须是唯一的;这在C99中是放松的,通常也是编译器扩展。
答案 1 :(得分:0)
为了实现这一点,你必须将它的一部分暴露为外部,这是大多数编译器需要的线索。
是否有可能会有一个破碎的编译器会使这种奇怪的做法变得麻烦,因为他们没有预见到有人会这样做?我无法回答这个问题。
我只能想到想要这样做的错误理由:指纹隐藏,因为你必须在函数指针decl中暴露它而失败,除非你打算绕过东西,在这种情况下,问题是"这会有多严重?#34;
另一个原因是要回调 - 你在模块m中有一些超敏感的静态本地函数,你现在想要在另一个模块中公开这些功能以用于回调目的,但是你想要审计它以便你想要一个外观:
collections.abc.Iterable
但这并没有太多帮助,你真的想要一个执行函数的包装器。
在一天结束时,有一种更简单的方法来公开函数指针。提供访问者功能。
static void voodoo_function() {
}
fnptr get_voodoo_function(const char* file, int line) {
// you tagged the question as C++, so C++ io it is.
std::cout << "requested voodoo function from " << file << ":" << line << "\n";
return voodoo_function;
}
...
// question tagged as c++, so I'm using c++ syntax
auto* fn = get_voodoo_function(__FILE__, __LINE__);
因为在这里你为编译器提供了一个优化机会 - 当你链接时,如果你指定整个程序优化,它可以检测到它是一个它可以消除的外观,因为你让它担心关于函数指针。
但除了不公开其内部名称之外,是否有一个非常令人信服的理由不仅要从static void voodoo_function() {}
void do_voodoo_function() {
// provide external access to voodoo
voodoo_function();
}
的前方移除static
?如果是这样,为什么内部名称如此珍贵,以至于你会花这些时间隐藏它?
voodoo_function
VS
static void ban_account_if_user_is_ugly() {
...;
}
fnptr do_that_thing() {
ban_account_if_user_is_ugly();
}
---编辑---
转换。你的函数指针是void do_that_thing() { // ban account if user is ugly
...
}
,但你的静态函数是int(*)(int)
而你不想要投射它。
再说一次:只提供一个外观函数可以解决问题,它将在以后转换为函数指针。手动将其转换为函数指针只能是编译器整个程序优化的绊脚石。
但如果你正在施法,我们可以考虑一下:
unsigned int(*)(unsigned int)
好的未签约签名,不是什么大不了的事。然后有人出现并将fnptr需要更改为// v1
fnptr get_fn_ptr() {
// brute force cast because otherwise it's 'hassle'
return (fnptr)(static_fn);
}
int facade_fn(int i) {
auto ui = static_cast<unsigned int>(i);
auto result = static_fn(ui);
return static_cast<int>(result);
}
。上面的一个变成了一个奇怪的运行时崩溃,一个变成了编译错误。