【问题标题】:Is it possible to determine (at runtime) if a function has been implemented?是否可以(在运行时)确定某个功能是否已实现?
【发布时间】:2016-05-27 07:07:31
【问题描述】:

Objective C 的主要功能之一是简单的introspection。此功能的典型用途是能够在调用某个方法(函数)之前检查它是否确实存在。

而下面的代码在运行时抛出一个错误(尽管它编译就好了(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)])

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

有没有办法使用“标准”C (c11) 或 C++ (std=c14) 来做到这一点?

即......

extern void callMeIfYouDare();

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

我想我还应该提到我正在测试/使用它是在 Darwin 运行时环境中。

【问题讨论】:

  • 我想你可以实现自己的动态函数查找/调用代码,比如 Obj-C。
  • 在哪个操作系统上?标准 C 没有任何自省
  • 如果该函数不存在,您将收到链接器错误。就是这样。您的程序永远不需要在运行时进行任何此类检查。

标签: c++ c function runtime conditional


【解决方案1】:

在 GNU gcc / Mingw32 / Cygwin 上你可以使用 Weak symbol:

#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 版本 6.0 (clang-600.0.57)(基于 LLVM 3.5svn)”的 XCode,我设法通过使用以下命令进行编译来解决该问题:

g++ test_undef.cpp -undefined dynamic_lookup -o test_undef

并保留 __attribute__((weak)) 用于其他平台。

【讨论】:

  • Xcode 7.x 的捆绑工具“不行”。我正在灌输最新的gcc,看看它是否做得更好(对于我的平台)......
  • Xcode 在说什么?看看这个:developer.apple.com/library/mac/documentation/MacOSX/Conceptual/… 显然你可以使用 __attribute__((weak_import)) 来代替 __attribute__((weak))
  • 即使是 Apple 自己的示例也无法编译,对我而言。一个完整的非功能示例项目,can be found here
  • 晚上我会在我的mac上试试这个。
  • 好的,我在我的 Mac 上对其进行了测试,似乎已经通过使用以下命令编译解决了该问题:g++ test_undef.cpp -undefined dynamic_lookup -o test_undef 并保留 __attribute__((weak)) 原样。此外 - 我的 XCode 版本不需要指针技巧 - 可以直接检查和调用 callMeIfYouDare()。请确认它是否适合您。
【解决方案2】:

如果您可以看到在源代码中调用了对象(不是指针)的函数并且代码编译成功 - 那么该函数确实存在并且不需要检查。

如果通过指针调用函数,那么您假设您的指针是具有该函数的类的类型。要检查是否如此,请使用强制转换:

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

【讨论】:

  • 这要求YourClass 至少有一个virtual 成员(最好有一个virtual 析构函数)。
  • @BasileStarynkevitch 听起来很合理。但不确定是否 dynamic_cast 仅用于层次结构的上下移动
  • 这确实不包括符号声明但未实现/链接的情况。
【解决方案3】:

C++ 或 C 没有自省功能。您可以在附加层中添加一些(例如,查看 Qt metaobject 或 GTK GObject introspection);您可能会考虑使用MELT 自定义GCC 以进行一些内省...(但这需要数周时间)。您可以使用一些额外的脚本或工具来发出与您的自省需求相关的 C 或 C++ 代码(SWIG 可能会很有启发性)。

在您的特定情况下,您可能希望使用weak symbols(至少在 Linux 上)。也许使用相关的function attribute so 代码。

extern void perhapshere(void) __attribute__((weak));
if (perhapshere) 
   perhapshere();

你甚至可以用一些宏来缩短它。

也许您只是想用dlopen(3) 加载一些插件并使用dlsym(3) 在其中查找符号(或者甚至在您将与-rdynamic 链接的整个程序中,通过将NULL 路径提供给@ 987654334@ 并在获得的句柄上使用dlsym);请注意,C++ 使用name mangling

所以你可以试试

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

【讨论】:

  • 我仍然得到Undefined symbols for architecture x86_64 - 即使在声明函数weak 之后,这本来是我认为它会完成的,只是不太清楚为什么它不起作用。 ?
  • 然后在问题中显示您的确切代码并解释您如何构建程序。上次我使用了弱符号技巧,它在 Linux 上确实有效。
  • 没什么花哨的...代码如上逐字逐句,用clang src -o exe编译
  • 这可能是特定于操作系统的。你真的应该在你的问题提及你的确切代码,你使用的是什么操作系统,你是如何链接你的程序的
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-03-07
  • 2020-03-24
  • 1970-01-01
  • 1970-01-01
  • 2014-07-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多