我一直在尝试获取一些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


我愚蠢地认为这可能是void函数指针所特有的(尽管根据我的经验,它什么也没有暗示,但我只是在试图理解)。

那么,有什么用呢?额外的语法在做什么?为什么在官方文档中什么也没有出现?

最佳答案

在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);


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

现在,您可以声明指向此类函数的指针,就像声明非函数类型的指针一样:

PrintFunction *printSome;


由于声明dlsym返回void *,因此我们必须转换其返回值。我们也可以在那里使用typedef

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


还要注意,C允许您直接对指向函数的指针执行函数调用,而无需取消引用。因此,您可以这样称呼它:

printSome();


与显式取消引用具有相同的作用:

(*printSome)();




更新

由于您提到(在评论中)您正在观看,因此,我将解释Casey Muratori声明函数类型的策略。让我们考虑从第21天起在win32_handmade.cpp:175处进行的调用:

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


UpdateAndRender字段在第157行中声明为:

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一样。

Casey为什么使用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)
{
}

关于c++ - 试图了解dlsym的语法怪异,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/45180020/

10-15 05:30