在运行时是否有可能确定一个函数是否已经被实现?

3
Objective C的主要特点之一是简单的内省。这个功能的典型用途是在调用某个方法(函数)之前,检查它是否确实存在。
虽然以下代码编译通过(Apple LLVM version 7.0.2 (clang-700.1.81)),但在运行时会抛出错误...
@import         Foundation;
@interface      Maybe : NSObject + (void) maybeNot; @end
@implementation Maybe                               @end

int main (){ [Maybe maybeNot]; }

在调用之前添加一个简单的条件...
if ([Maybe respondsToSelector:@selector(maybeNot)])

我们可以在运行时决定是否调用该方法。

在“标准”Cc11)或C++std=c14)中是否有任何方法可以实现这一点?

例如...

extern void callMeIfYouDare();

int main() { /* if (...) */ callMeIfYouDare(); }

我想我也应该提一下,我正在测试/使用这个在一个Darwin运行环境中。


我想你可以像Objective-C一样实现自己的动态函数查找/调用代码。 - Alexander O'Mara
在哪个操作系统上?标准C没有任何内省。 - Basile Starynkevitch
如果函数不存在,你会得到一个链接器错误。就是这样。你的程序在运行时永远不需要进行任何此类检查。 - Lundin
3个回答

4
在GNU gcc / Mingw32 / Cygwin上,您可以使用弱符号
#include <stdio.h>

extern void __attribute__((weak)) callMeIfYouDare();

void (*callMePtr)() = &callMeIfYouDare;

int main() {
        if (callMePtr) {
                printf("Calling...\n");
                callMePtr();
        } else {
                printf("callMeIfYouDare() unresolved\n");
        }
}

编译并运行:

$ g++ test_undef.cpp -o test_undef.exe

$ ./test_undef.exe
callMeIfYouDare() unresolved

如果你将它与定义了 callMeIfYouDare 的库链接起来,它将会调用它。请注意,在至少 Mingw32/Cygwin 中,通过指针进行操作是必要的。直接调用 callMeIfYouDare() 将默认导致截断重定位,除非你想玩链接器脚本,否则无法避免。
在使用 Visual Studio 时,你可能可以使用 __declspec(selectany) 来实现相同的技巧: GCC style weak linking in Visual Studio? 更新 #1:对于 XCode,你可以使用 __attribute__((weak_import)) 来代替,参见:Frameworks and Weak Linking 更新 #2:对于基于“Apple LLVM version 6.0 (clang-600.0.57) (based on LLVM 3.5svn)”的 XCode,我成功解决了这个问题,编译命令如下:
g++ test_undef.cpp -undefined dynamic_lookup -o test_undef

对于其他平台,保留__attribute__((weak))不变。


使用Xcode7.x捆绑工具是不可行的。我将安装最新的GCC来看看是否能改善(我的平台)... - Alex Gray
Xcode 说了什么?看看这个链接:https://developer.apple.com/library/mac/documentation/MacOSX/Conceptual/BPFrameworks/Concepts/WeakLinking.html 。显然,你可以使用__attribute__((weak_import))代替__attribute__((weak))。 - ALGOholic
1
即使是苹果自己的示例,在我的电脑上也无法编译。可以在此处找到一个完整但不可用的示例项目:https://github.com/mralexgray/WeakLinks。 - Alex Gray
1
好的,我在我的Mac上进行了测试,并似乎通过使用以下命令进行编译解决了问题:g++ test_undef.cpp -undefined dynamic_lookup -o test_undef,并将__attribute__((weak))保持不变。此外,使用我的XCode版本不需要指针技巧-可以直接检查和调用callMeIfYouDare()。请确认是否对您有效。 - ALGOholic
您还想了解什么才能接受这个答案呢? - ALGOholic
显示剩余2条评论

1

如果在源代码中看到一个对象(非指针)的函数被调用并且成功编译,则该函数存在且无需检查。

如果通过指针调用函数,则假设你的指针是具有该函数的类的类型。要检查它是否如此,您可以使用转换:

auto* p = dynamic_cast<YourClass*>(somepointer);
if (p != nullptr)
  p->execute();

1
这要求YourClass至少有一个virtual成员函数(最好还要有一个虚析构函数)。 - Basile Starynkevitch
@BasileStarynkevitch 听起来很合理。但不确定 dynamic_cast 是否仅用于层次结构的向上和向下移动。 - Andrey Lyubimov
1
这确实没有涵盖符号已声明但未实现/链接的情况。 - ALGOholic

1
C++或C没有内省。你可以通过添加额外的层来实现(查看Qt元对象或GTK GObject内省作为示例); 你可以考虑使用GCCMELT自定义以获得一些内省...(但这需要几周时间)。您可以有一些额外的脚本或工具,它会发出与您的内省需求相关的C或C++代码(SWIG可能是灵感)。
在您的特定情况下,您可能希望在Linux上使用弱符号。也许使用相关的函数属性来编写代码。
extern void perhapshere(void) __attribute__((weak));
if (perhapshere) 
   perhapshere();

你甚至可以使用一些宏来缩短代码。

也许你只想使用 dlopen(3) 加载一些插件,并使用 dlsym(3) 在其中查找符号(甚至在整个程序中,通过给 -rdynamic 指定 NULL 路径并在获取的句柄上使用 dlsym);请注意,C++ 使用 名称修饰

因此,你可以尝试:

void*mainhdl = dlopen(NULL, RTLD_NOW);
if (!mainhdl) { fprintf(stderr, "dlopen failed %s\n", dlerror());
                exit(EXIT_FAILURE); };

然后稍后:
typedef void voidvoidsig_t (void); // the signature of perhapshere
void* ad = dlsym(mainhdl, "perhapshere"); 
if (ad != NULL) {
   voidvoidsig_t* funptr = (voidvoidsig_t*)ad;
   (*funptr)();
}

即使将函数声明为 "weak",我仍然会收到“Undefined symbols for architecture x86_64”错误信息。这是我最初认为应该完成的方式,只是不太明白为什么它不起作用。 - Alex Gray
然后在问题中展示你的确切代码,并解释你如何构建你的程序。上次我在Linux上使用弱符号技巧它确实起作用了。 - Basile Starynkevitch
将以下与编程有关的内容从英语翻译成中文。只返回翻译后的文本:不需要花哨的格式......代码与上面完全一致,用clang src -o exe编译。 - Alex Gray
这可能与操作系统有关。您应该在问题中提及您的确切代码,使用的操作系统,以及如何链接程序等。 - Basile Starynkevitch

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接