试图理解围绕dlsym的语法怪癖

时间:2017-07-19 02:37:42

标签: c++ macos

我一直在努力学习一些osx编程知识,以补充我的win32编程经验。我发现GetProcAddress的粗略等价物是dlsym。然后我遇到了一些意想不到的障碍。

我在查看旧论坛帖子和尝试的事情后,我的代码工作正常,但它与我的期望不符,也不符合我在Apple自己的文档中看到的。我希望有人能帮助我理解原因。

Apple documentation I was referring to

文档提供了一个如下所示的示例:

Person_creator* NewPerson = (Person_creator*)dlsym(lib_handle, "NewPerson");

我通过这样做得到了我的代码:

void (*printSome)() = (void(*)())dlsym(handle, "printSomething");

我认为我的代码应该这样做:

void* printSome = (void*)dlsym(handle, "printSomething");

然而,当我这样做并尝试调用printSome()时,我收到此错误消息:

Called object type 'void*' is not a function or function pointer

我有一个愚蠢的想法,它可能是一个无效函数指针的独特之处(即使我的经验没有提到它,我只是抓着试图理解)。

那么,是什么给出的?额外的语法是做什么的?为什么这在官方文档中没有出现?

1 个答案:

答案 0 :(得分:2)

在C中,指向函数的指针的声明可能会令人困惑。 This website, “cdecl”,可以帮助您了解声明的含义。以下是它解释您的工作代码变量声明的方法:

  

输入:void (*printSome)()
  输出:将printSome声明为函数返回void

的指针

以下是解码非工作代码变量声明的方法:

  

输入:void* printSome
  输出:将printSome声明为void

的指针

请注意,第一个声明指向函数的指针,而第二个声明指向函数的指针。所以你不能在第二个上执行函数调用。

使用typedef有助于简化声明,这就是Apple的文档使用typedef Person_creator的原因。以下是如何使用typedef代码。

首先,为与您要调用的函数具有相同签名的函数写出声明:

void PrintFunction(void);

然后将typedef贴在前面:

typedef void PrintFunction(void);

请注意,我们已声明了 function 类型,而不是指向函数的指针类型。

现在你可以声明一个指向这种函数的指针,就像你声明一个指向非函数类型的指针一样:

PrintFunction *printSome;

由于dlsym被声明为返回void *,我们必须转换它的返回值。我们也可以使用typedef

PrintFunction *printSome = (PrintFunction *)dlsym(handle, "printSomething");

另请注意,C允许您直接对指向函数的指针执行函数调用,而无需解除引用。所以你可以这样称呼它:

printSome();

与明确解除引用效果相同:

(*printSome)();

更新

由于您(在评论中)提到您正在观看Handmade Hero,我将解释Casey Muratori宣布函数类型的策略。让我们从第21天win32_handmade.cpp:175开始考虑这个电话:

Result.UpdateAndRender = (game_update_and_render *)
    GetProcAddress(Result.GameCodeDLL, "GameUpdateAndRender");

第{157}行UpdateAndRender字段的声明如下:

game_update_and_render *UpdateAndRender;

所以Casey使用相同的typedef game_update_and_render声明变量并转换GetProcAddress的返回值。 (我猜Windows惯例是使用变量和函数的初始大写,以及类型的初始小写,这与Apple约定相反)。

typedef game_update_and_render位于handmade.h:190

typedef GAME_UPDATE_AND_RENDER(game_update_and_render);

它使用前一行声明的GAME_UPDATE_AND_RENDER宏:

#define GAME_UPDATE_AND_RENDER(name) void name(game_memory *Memory, game_input *Input, game_offscreen_buffer *Buffer)

最终,game_update_and_render是函数类型的typedef,就像Apple示例中的Person_creator或上面的PrintFunction一样。

为什么凯西使用GAME_UPDATE_AND_RENDER宏?我想这是因为他可以使用相同的宏来定义该类型的实际功能,就像他在下一行(handmade.h:190)上所做的那样:

GAME_UPDATE_AND_RENDER(GameUpdateAndRenderStub)
{
}

在这里,他定义了一个名为GameUpdateAndRenderStub的函数,其签名(参数和返回类型)与typedef game_update_and_render相同。没有宏,他必须重复参数并返回如下类型:

void GameUpdateAndRenderStub(game_memory *Memory, game_input *Input, game_offscreen_buffer *Buffer)
{
}