通过函数指针从外部文件调用静态函数的不良做法?

时间:2015-06-11 23:19:21

标签: c++ c

请考虑以下代码:

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();
}

虽然我相信上面的例子在技术上是可行的 - 仅通过函数指针调用具有内部链接的函数,这样做是不好的做法吗?是否会出现一些时髦的编译器优化(例如自动内联),这会使这种情况出现问题?

2 个答案:

答案 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); } 。上面的一个变成了一个奇怪的运行时崩溃,一个变成了编译错误。