如何将类成员函数作为回调传递?
我使用的 API 要求我传递函数指针作为回调。 我正在尝试在我的类中使用此 API,但出现编译错误。
这是我在构造函数中所做的:
m_cRedundencyManager->Init(this->RedundencyManagerCallBack);
这无法编译 - 我收到以下错误:
错误 8 错误 C3867:“CLoggersInfra::RedundencyManagerCallBack”:函数调用缺少参数列表; 使用“&CLoggersInfra::RedundencyManagerCallBack”创建指向成员的指针
我尝试了使用 &CLoggersInfra::RedundencyManagerCallBack
的建议 - 对我来说不起作用。
对此有什么建议/解释吗?
我用的是VS2008。
谢谢!!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(14)
这是一个简单的问题,但答案却出奇地复杂。 简而言之,您可以使用
std::bind1st
或boost::bind
执行您想要执行的操作。 较长的答案如下。编译器正确地建议您使用
&CLoggersInfra::RedundencyManagerCallBack
。 首先,如果 RedundencyManagerCallBack 是成员函数,则该函数本身不属于 CLoggersInfra 类的任何特定实例。 它属于类本身。 如果您以前曾经调用过静态类函数,您可能已经注意到您使用了相同的SomeClass::SomeMemberFunction
语法。 由于函数本身是“静态”的,因为它属于类而不是特定实例,因此您可以使用相同的语法。 '&' 是必要的,因为从技术上讲,您不直接传递函数——函数不是 C++ 中的真实对象。 相反,从技术上讲,您传递的是函数的内存地址,即指向函数指令在内存中开始位置的指针。 结果是相同的,您实际上是“传递函数”作为参数。但这只是本例中问题的一半。 正如我所说,RedundencyManagerCallBack 函数不“属于”任何特定实例。 但听起来您想将其作为回调传递,并考虑到特定的实例。 要了解如何执行此操作,您需要了解成员函数的真正含义:带有额外隐藏参数的常规未在任何类中定义的函数。
例如:
A::foo
有多少个参数? 通常我们会说 1。但在幕后, foo 实际上需要 2。看看 A::foo 的定义,它需要 A 的特定实例才能使“this”指针有意义(编译器需要知道“这是)。 通常指定“this”的方式是通过语法MyObject.MyMemberFunction()
。 但这只是将MyObject
的地址作为第一个参数传递给MyMemberFunction
的语法糖。 类似地,当我们在类定义中声明成员函数时,我们不会将“this”放入参数列表中,但这只是语言设计者为节省打字而提供的礼物。 相反,您必须指定成员函数是静态的,才能选择不自动获取额外的“this”参数。 如果 C++ 编译器将上面的示例翻译为 C 代码(原始 C++ 编译器实际上是这样工作的),它可能会写出如下内容:回到您的示例,现在有一个明显的问题。 “Init”需要一个指向带有一个参数的函数的指针。 但是
&CLoggersInfra::RedundencyManagerCallBack
是一个指向带有两个参数的函数的指针,它是普通参数和秘密的“this”参数。 这就是为什么你仍然会收到编译器错误(附带说明:如果你曾经使用过 Python,这种混乱就是为什么所有成员函数都需要“self”参数)。处理此问题的详细方法是创建一个特殊对象,该对象保存指向所需实例的指针,并具有一个名为“run”或“execute”(或重载“()”运算符)的成员函数,该函数接受参数对于成员函数,只需在存储的实例上使用这些参数调用成员函数即可。 但这需要您更改“Init”以获取特殊对象而不是原始函数指针,而且听起来 Init 是其他人的代码。 每次出现这个问题时都创建一个特殊的类会导致代码膨胀。
现在,终于有了一个好的解决方案,
boost::bind
和boost::function
,您可以在这里找到每个解决方案的文档:boost::bind 文档,
boost::function 文档
boost ::bind
将允许您获取一个函数以及该函数的一个参数,并创建一个新函数,其中该参数被“锁定”到位。 因此,如果我有一个将两个整数相加的函数,我可以使用 boost::bind 来创建一个新函数,其中一个参数被锁定为 5。这个新函数将仅采用一个整数参数,并且总是会专门为其添加 5。 使用这种技术,您可以将隐藏的“this”参数“锁定”为特定的类实例,并生成一个仅采用一个参数的新函数,就像您想要的那样(请注意,隐藏参数始终是 第一个参数,普通参数按顺序排列在它之后)。 查看 boost::bind 文档中的示例,他们甚至专门讨论了将其用于成员函数。 从技术上讲,您也可以使用一个名为[std::bind1st][3]
的标准函数,但boost::bind
更为通用。当然,还有一个问题。
boost::bind
将为您提供一个不错的 boost::function ,但从技术上讲,这仍然不是像 Init 可能想要的原始函数指针。 值得庆幸的是,boost 提供了一种将 boost::function 转换为原始指针的方法,如 StackOverflow如果这看起来非常困难,请不要担心——您的问题涉及 C++ 的几个阴暗角落,并且一旦您学会了
boost::bind
就会非常有用。C++11 更新:您现在可以使用捕获“this”的 lambda 函数,而不是
boost::bind
。 这基本上是让编译器为您生成相同的东西。This is a simple question but the answer is surprisingly complex. The short answer is you can do what you're trying to do with
std::bind1st
orboost::bind
. The longer answer is below.The compiler is correct to suggest you use
&CLoggersInfra::RedundencyManagerCallBack
. First, ifRedundencyManagerCallBack
is a member function, the function itself doesn't belong to any particular instance of the classCLoggersInfra
. It belongs to the class itself. If you've ever called a static class function before, you may have noticed you use the sameSomeClass::SomeMemberFunction
syntax. Since the function itself is 'static' in the sense that it belongs to the class rather than a particular instance, you use the same syntax. The '&' is necessary because technically speaking you don't pass functions directly -- functions are not real objects in C++. Instead you're technically passing the memory address for the function, that is, a pointer to where the function's instructions begin in memory. The consequence is the same though, you're effectively 'passing a function' as a parameter.But that's only half the problem in this instance. As I said,
RedundencyManagerCallBack
the function doesn't 'belong' to any particular instance. But it sounds like you want to pass it as a callback with a particular instance in mind. To understand how to do this you need to understand what member functions really are: regular not-defined-in-any-class functions with an extra hidden parameter.For example:
How many parameters does
A::foo
take? Normally we would say 1. But under the hood, foo really takes 2. Looking at A::foo's definition, it needs a specific instance of A in order for the 'this' pointer to be meaningful (the compiler needs to know what 'this' is). The way you usually specify what you want 'this' to be is through the syntaxMyObject.MyMemberFunction()
. But this is just syntactic sugar for passing the address ofMyObject
as the first parameter toMyMemberFunction
. Similarly, when we declare member functions inside class definitions we don't put 'this' in the parameter list, but this is just a gift from the language designers to save typing. Instead you have to specify that a member function is static to opt out of it automatically getting the extra 'this' parameter. If the C++ compiler translated the above example to C code (the original C++ compiler actually worked that way), it would probably write something like this:Returning to your example, there is now an obvious problem. 'Init' wants a pointer to a function that takes one parameter. But
&CLoggersInfra::RedundencyManagerCallBack
is a pointer to a function that takes two parameters, it's normal parameter and the secret 'this' parameter. That's why you're still getting a compiler error (as a side note: If you've ever used Python, this kind of confusion is why a 'self' parameter is required for all member functions).The verbose way to handle this is to create a special object that holds a pointer to the instance you want and has a member function called something like 'run' or 'execute' (or overloads the '()' operator) that takes the parameters for the member function, and simply calls the member function with those parameters on the stored instance. But this would require you to change 'Init' to take your special object rather than a raw function pointer, and it sounds like Init is someone else's code. And making a special class for every time this problem comes up will lead to code bloat.
So now, finally, the good solution,
boost::bind
andboost::function
, the documentation for each you can find here:boost::bind docs,
boost::function docs
boost::bind
will let you take a function, and a parameter to that function, and make a new function where that parameter is 'locked' in place. So if I have a function that adds two integers, I can useboost::bind
to make a new function where one of the parameters is locked to say 5. This new function will only take one integer parameter, and will always add 5 specifically to it. Using this technique, you can 'lock in' the hidden 'this' parameter to be a particular class instance, and generate a new function that only takes one parameter, just like you want (note that the hidden parameter is always the first parameter, and the normal parameters come in order after it). Look at theboost::bind
docs for examples, they even specifically discuss using it for member functions. Technically there is a standard function called[std::bind1st][3]
that you could use as well, butboost::bind
is more general.Of course, there's just one more catch.
boost::bind
will make a nice boost::function for you, but this is still technically not a raw function pointer like Init probably wants. Thankfully, boost provides a way to convert boost::function's to raw pointers, as documented on StackOverflow here. How it implements this is beyond the scope of this answer, though it's interesting too.Don't worry if this seems ludicrously hard -- your question intersects several of C++'s darker corners, and
boost::bind
is incredibly useful once you learn it.C++11 update: Instead of
boost::bind
you can now use a lambda function that captures 'this'. This is basically having the compiler generate the same thing for you.这是行不通的,因为成员函数指针不能像普通函数指针一样处理,因为它需要一个“this”对象参数。
相反,您可以按如下方式传递静态成员函数,这在这方面就像普通的非成员函数:
该函数可以定义如下
That doesn't work because a member function pointer cannot be handled like a normal function pointer, because it expects a "this" object argument.
Instead you can pass a static member function as follows, which are like normal non-member functions in this regard:
The function can be defined as follows
这个答案是对上面评论的回复,不适用于 VisualStudio 2008,但应首选更新的编译器。
同时,您不必再使用 void 指针,也不需要 boost,因为
std::bind
和 < code>std::function 可用。 一个优点(与 void 指针相比)是类型安全,因为返回类型和参数是使用std::function
显式声明的:然后您可以使用以下命令创建函数指针
std::bind
并将其传递给 Init:完整示例将
std::bind
与成员、静态成员和非成员函数一起使用:可能的输出:
此外使用
std::placeholders
您可以动态地将参数传递给回调(例如,这可以使用return f("MyString");如果 f 有一个字符串参数,则在
。Init
中执行This answer is a reply to a comment above and does not work with VisualStudio 2008 but should be preferred with more recent compilers.
Meanwhile you don't have to use a void pointer anymore and there is also no need for boost since
std::bind
andstd::function
are available. One advantage (in comparison to void pointers) is type safety since the return type and the arguments are explicitly stated usingstd::function
:Then you can create the function pointer with
std::bind
and pass it to Init:Complete example for using
std::bind
with member, static members and non member functions:Possible output:
Furthermore using
std::placeholders
you can dynamically pass arguments to the callback (e.g. this enables the usage ofreturn f("MyString");
inInit
if f has a string parameter).将 C 风格的回调函数与 C++ 类实例连接起来仍然很困难。 我想重新表述一下原来的问题:
您正在使用的某些库需要从该库回调 C 风格的函数。 更改库 API 是不可能的,因为它不是您的 API。
您希望在您自己的 C++ 代码中的成员方法中处理回调
由于您没有(确切地)提到您想要处理什么回调,我将使用 GLFW 按键输入回调。 (旁注:我知道 GLFW 提供了一些其他机制将用户数据附加到他们的 API,但这不是这里的主题。)
我不知道这个问题的任何解决方案,不包括使用某种静态对象。 让我们看看我们的选择:
简单方法:使用 C 风格的全局对象
由于我们总是在类和实例中思考,我们有时会忘记,在 C++ 中,我们仍然拥有 C 的整个库。 所以有时这个非常简单的解决方案不会浮现在脑海中。
假设我们有一个类 Presentation 应该处理键盘输入。 这可能看起来像这样:
我们有一个全局对象KeyInputGlobal,它应该接收按下的键。 函数 globalKeyHandler 具有 GLFW 库调用我们的代码所需的 C 风格 API 签名。 它在我们的成员方法 initGLFW 上激活。 如果我们代码中的任何地方对当前按下的键感兴趣,我们可以调用另一个成员方法 Presentation::getCurrentKey
这种方法有什么问题吗?
也许一切都很好。 完全取决于您的用例。 也许您完全可以在应用程序代码中读取最后按下的键。 您不关心错过按键事件。 您所需要的就是简单的方法。
概括来说:如果您能够在 C 风格代码中完全处理回调,计算一些结果并将其存储在全局对象中以便稍后从代码的其他部分读取,那么使用这种简单的方法可能确实有意义。 好的一面是:它非常容易理解。 不足之处? 这感觉有点像作弊,因为你并没有真正在 C++ 代码中处理回调,你只是使用了结果。 如果您将回调视为一个事件,并希望在您的成员方法中正确处理每个事件,则此方法还不够。
另一种简单的方法:使用 C++ 静态对象
我想我们中的许多人已经这样做了。 我当然有。 思考:等等,我们有一个全局变量的 C++ 概念,即使用静态。 但我们可以在这里简短地讨论一下:它可能比使用前面示例中的 C 风格更具 C++ 风格,但问题是相同的 - 我们仍然有全局变量,很难与非静态、常规成员方法结合在一起。 为了完整起见,它在我们的类声明中看起来像这样:
激活我们的回调看起来是一样的,但我们还必须定义在我们的实现中接收按下的键的静态结构:
您可能倾向于只删除 我们的回调方法 globalKeyHandler 中的 static 关键字:编译器会立即告诉您,您不能再在 glfwSetKeyCallback() 中将其传递给 GLFW。 现在,如果我们只能以某种方式将静态方法与常规方法连接起来...
C++11 使用静态和 lambda 的事件驱动方法
我能找到的最佳解决方案如下。 它有效并且有点优雅,但我仍然不认为它是完美的。 让我们看一下并讨论:
callback_static 的定义是我们将静态对象与实例数据连接起来的地方,在本例中 this 是我们Presentation<的一个实例/em> 类。 您可以如下阅读该定义:如果在此定义之后的任何时间调用callback_static,则所有参数都将传递给在Presentationkey_callbackMember em> 刚刚使用的实例。 这个定义与 GLFW 库无关——它只是为下一步做准备。
现在,我们使用第二个 lambda 在 glfwSetKeyCallback() 中向库注册我们的回调。 同样,如果callback_static没有被定义为static,我们就无法将它传递给GLFW。
这是在所有初始化之后运行时 GLFW 调用我们的代码时发生的情况:
好处:我们已经实现了我们想要的目标,无需在初始化方法 initGLFW 之外定义全局对象。 不需要 C 风格的全局变量。
坏处:不要仅仅因为所有内容都整齐地打包到一种方法中而被愚弄。 我们仍然有静态对象。 全局对象所面临的所有问题也随之而来。 例如,多次调用我们的初始化方法(使用不同的Presentation实例)可能不会达到您想要的效果。
摘要
可以将现有库的 C 风格回调连接到您自己的代码中的类实例。 您可以尝试通过在代码的成员方法中定义必要的对象来尽量减少管理代码。 但每个回调仍然需要一个静态对象。 如果您想使用 C 样式回调连接 C++ 代码的多个实例,请准备好引入比上面示例更复杂的静态对象管理。
希望这对某人有帮助。 快乐编码。
It is still difficult to connect C style callback functions with C++ class instances. I want to kind of rephrase the original question:
Some library you are using requires a C style function to be called back from that library. Changing the library API is out of the question since it is not your API.
You want the callback to be handled in your own C++ code in member methods
As you did not mention (exactly) what callback you want to handle I will give an example using GLFW callbacks for key input. (On a side note: I know GLFW offers some other mechanism to attach user data to their API, but that is not the topic here.)
I don't know any solution to this problem that doesn't include usage of some kind of static object. Let's look at our options:
Simple approach: Use C style global objects
As we always think in classes and instances we sometimes forget that in C++ we still have the whole arsenal of C at our hands. So sometimes this very simple solution does not come to mind.
Let's assume we have a class Presentation that should handle keyboard input. This could look like this:
We have a global Object KeyInputGlobal that should receive the key pressed. The function globalKeyHandler has exactly the C style API signature needed by the GLFW library to be able to call our code. It is activated on our member method initGLFW. If anywhere in our code we are interested in the currently pressed key we can just call the other member method Presentation::getCurrentKey
What is wrong with this approach?
Maybe it is all fine. Depends entirely on your use case. Maybe you are totally fine to just read the last pressed key somwhere in your application code. You don't care to have missed key pressed events. The simple approach is all you need.
To generalize: If you are able to fully process the callback in C style code, calculate some result and store it in a global object to be read later from other parts of your code, then it may indeed make sense to use this simple approach. On the plus side: It is very simple to understand. The downside? It feels a little bit like cheating, because you didn't really process the callback in your C++ code, you just used the results. If you think of the callback as an event and want each event to be properly processed in your member methods this approch won't be enough.
Another simple approach: Use C++ static objects
I guess many of us have already done this. Certainly I have. Thinking: Wait, we have a C++ concept of globals, that is using static. But we can keep the discussion short here: It may be more C++ style than using the C style from previous example, but the problems are the same - we still have globals, that are hard to bring together with non-static, regular member methods. For completeness, it would look like this in our class declaration:
Activating our callback would look the same, but we also have to define the static struct that receives the key pressed in our implementation:
You might be inclined to just remove the static keyword from our callback method globalKeyHandler: The compiler will immediately tell you that you can no longer pass this to GLFW in glfwSetKeyCallback(). Now, if we only could connect static methods with regular methods somehow...
C++11 Event driven approach with statics and lambdas
The best solution I could find out is the following. It works and is somewhat elegant, but I still do not consider it perfect. Let's look at it and discuss:
The definition of callback_static is where we connect a static object with instance data, in this case this is an instance of our Presentation class. You can read the definition as follows: If callback_static is called anytime after this definition, all parameters will be passed to the member method key_callbackMember called at the Presentation instance just used. This definition has nothing to do with the GLFW library yet - it is just the preparation for the next step.
We now use a second lambda to register our callback with the library in glfwSetKeyCallback(). Again, if callback_static would not have been defined as static we could not pass it to GLFW here.
This is what happens at runtime after all the initializations, when GLFW calls our code:
The good: We have achieved what we wanted with no need to define global objects outside our initialization method initGLFW. No need for C style globals.
The bad: Don't be fooled just because everything is neatly packed into one method. We still have static objects. And with them all the problems global objects have. E.g. multiple calls to our initialization method (with different instances of Presentation) would probably not have the effect you intended.
Summary
It is possible to connect C style callbacks of existing libraries to instances of classes in your own code. You can try to minimize houeskeeping code by defining the necessary objects in member methods of your code. But you still need one static object for each callback. If you want to connect several instances of your C++ code with a C style callback be prepared to introduce a more complicated management of your static objects than in the example above.
Hope this helps someone. Happy coding.
在 c++14 或更高版本中,有一种令人惊讶的简单方法:
**假设
subscribeToEvent
获取std::function
There's a surprisingly simple way to do so in c++14 or above:
**assuming
subscribeToEvent
getsstd::function<void()>
Init
采用什么参数? 新的错误消息是什么?C++ 中的方法指针有点难以使用。 除了方法指针本身之外,您还需要提供一个实例指针(在您的情况下为
this
)。 也许Init
希望它作为一个单独的参数?What argument does
Init
take? What is the new error message?Method pointers in C++ are a bit difficult to use. Besides the method pointer itself, you also need to provide an instance pointer (in your case
this
). MaybeInit
expects it as a separate argument?指向类成员函数的指针与指向函数的指针不同。 类成员采用隐式额外参数(this 指针),并使用不同的调用约定。
如果您的 API 需要非成员回调函数,那么您必须将其传递给它。
A pointer to a class member function is not the same as a pointer to a function. A class member takes an implicit extra argument (the this pointer), and uses a different calling convention.
If your API expects a nonmember callback function, that's what you have to pass to it.
m_cRedundencyManager
能够使用成员函数吗? 大多数回调都设置为使用常规函数或静态成员函数。 请查看 C++ FAQ Lite 中的此页面了解更多信息。更新:您提供的函数声明显示
m_cRedundencyManager
需要以下形式的函数:void yourCallbackFunction(int, void *)
。 因此,在这种情况下,成员函数作为回调是不可接受的。 静态成员函数可能可以工作,但如果这在您的情况下是不可接受的,则以下代码也可以工作。 请注意,它使用了void *
的邪恶转换。Is
m_cRedundencyManager
able to use member functions? Most callbacks are set up to use regular functions or static member functions. Take a look at this page at C++ FAQ Lite for more information.Update: The function declaration you provided shows that
m_cRedundencyManager
is expecting a function of the form:void yourCallbackFunction(int, void *)
. Member functions are therefore unacceptable as callbacks in this case. A static member function may work, but if that is unacceptable in your case, the following code would also work. Note that it uses an evil cast fromvoid *
.死灵术。
我认为迄今为止的答案还有些不清楚。
让我们举个例子:
假设你有一个像素数组(ARGB int8_t 值的数组),
现在你想生成一个 PNG。
为此,您调用函数 toJpeg
,其中 writeByte 是回调函数
这里的问题:FILE* 输出必须是全局变量。
如果您处于多线程环境(例如 http 服务器)中,则非常糟糕。
因此,您需要某种方法使输出成为非全局变量,同时保留回调签名。
立即想到的解决方案是闭包,我们可以使用带有成员函数的类来模拟它。
然后做
然而,事与愿违,这行不通。
原因是,C++ 成员函数的实现有点像 C# 扩展函数。
所以
当你想调用
writeByte 时,你不仅需要传递 writeByte 的地址,还需要传递 BadIdea 实例的地址。
因此,当您有 writeByte 过程的 typedef,并且它看起来像这样
并且您有一个看起来像这样的 writeJpeg 签名时,从
根本上不可能将两地址成员函数传递给一地址函数指针(不修改 writeJpeg),没有办法解决这个问题。
在 C++ 中你可以做的下一个最好的事情是使用 lambda 函数:
但是,因为 lambda 没有做任何不同的事情,而不是将实例传递给隐藏类(例如“BadIdea”类),所以你需要修改writeJpeg 的签名。
与手动类相比,lambda 的优点是您只需将一个 typedef 更改
为
然后您可以保留其他所有内容不变。
您也可以使用 std::bind
但这在幕后只是创建了一个 lambda 函数,然后该函数还需要更改 typedef。
所以不,没有办法将成员函数传递给需要静态函数指针的方法。
但 lambda 是一种简单的方法,只要您能够控制源代码。
否则,你就不走运了。
你对 C++ 无能为力。
注意:
std::function 需要
#include
但是,由于 C++ 也允许您使用 C,因此您可以使用 libffcall 用纯 C 语言编写,如果您不介意链接依赖项。
从 GNU 下载 libffcall(至少在 ubuntu 上,不要使用发行版提供的软件包 - 它已损坏),解压。
main.c:
如果您使用 CMake,请在 add_executable 之后添加链接器库
,或者您可以动态链接 libffcall
注意:
您可能想要包含 libffcall 标头和库,或者使用 libffcall 的内容创建一个 cmake 项目。
Necromancing.
I think the answers to date are a little unclear.
Let's make an example:
Supposed you have an array of pixels (array of ARGB int8_t values)
Now you want to generate a PNG.
To do so, you call the function toJpeg
where writeByte is a callback-function
The problem here: FILE* output has to be a global variable.
Very bad if you're in a multithreaded environment (e.g. a http-server).
So you need some way to make output a non-global variable, while retaining the callback signature.
The immediate solution that springs into mind is a closure, which we can emulate using a class with a member function.
And then do
However, contrary to expectations, this does not work.
The reason is, C++ member functions are kinda implemented like C# extension functions.
So you have
and
So when you want to call writeByte, you need pass not only the address of writeByte, but also the address of the BadIdea-instance.
So when you have a typedef for the writeByte procedure, and it looks like this
And you have a writeJpeg signature that looks like this
it's fundamentally impossible to pass a two-address member function to a one-address function pointer (without modifying writeJpeg), and there's no way around it.
The next best thing that you can do in C++, is using a lambda-function:
However, because lambda is doing nothing different, than passing an instance to a hidden class (such as the "BadIdea"-class), you need to modify the signature of writeJpeg.
The advantage of lambda over a manual class, is that you just need to change one typedef
to
And then you can leave everything else untouched.
You could also use std::bind
But this, behind the scene, just creates a lambda function, which then also needs the change in typedef.
So no, there is no way to pass a member function to a method that requires a static function-pointer.
But lambdas are the easy way around, provided that you have control over the source.
Otherwise, you're out of luck.
There's nothing you can do with C++.
Note:
std::function requires
#include <functional>
However, since C++ allows you to use C as well, you can do this with libffcall in plain C, if you don't mind linking a dependency.
Download libffcall from GNU (at least on ubuntu, don't use the distro-provided package - it is broken), unzip.
main.c:
And if you use CMake, add the linker library after add_executable
or you could just dynamically link libffcall
Note:
You might want to include the libffcall headers and libraries, or create a cmake project with the contents of libffcall.
一个简单的解决方案“解决方法”仍然是创建一个虚拟函数“接口”类并在调用者类中继承它。 然后将其作为参数“可以在您想要回调调用者类的另一个类的构造函数中”传递。
DEFINE Interface:
这是你要回调的类:
这是将被回调的类。
A simple solution "workaround" still is to create a class of virtual functions "interface" and inherit it in the caller class. Then pass it as a parameter "could be in the constructor" of the other class that you want to call your caller class back.
DEFINE Interface:
This is the class that you want to call you back:
And this is the class that will be called back.
我可以看到 init 具有以下覆盖:
其中
CALLBACK_FUNC_EX
是I can see that the init has the following override:
where
CALLBACK_FUNC_EX
is指向非静态成员函数的指针的类型与指向普通函数的指针不同。
如果它是普通或静态成员函数,则类型为
void(*)(int)
。如果它是一个非静态成员函数,则类型为
void(CLoggersInfra::*)(int)
。因此,如果非静态成员函数需要普通函数指针,则不能将指针传递给该函数。
此外,非静态成员函数具有对象的隐式/隐藏参数。
this
指针作为参数隐式传递给成员函数调用。 因此,只能通过提供对象来调用成员函数。如果 API
Init
无法更改,则可以使用调用该成员的包装函数(普通函数或类静态成员函数)。 在最坏的情况下,该对象将是包装函数要访问的全局对象。如果 API
Init
可以更改,则有许多替代方案 - 对象非静态成员函数指针、函数对象、std::function
或接口功能。请参阅关于 回调 的帖子,了解 C++ 工作示例。
The type of pointer to non-static member function is different from pointer to ordinary function.
Type is
void(*)(int)
if it’s an ordinary or static member function.Type is
void(CLoggersInfra::*)(int)
if it’s a non-static member function.So you cannot pass a pointer to a non-static member function if it is expecting an ordinary function pointer.
Furthermore, a non-static member function has an implicit/hidden parameter to the object. The
this
pointer is implicitly passed as an argument to the member function call. So the member functions can be invoked only by providing an object.If the API
Init
cannot be changed, a wrapper function (ordinary function or a class static member function) that invokes the member can be used. In the worst case, the object would be a global for the wrapper function to access.If the API
Init
can be changed, there are many alternatives - Object non-static member function pointer, Function Object,std::function
or Interface Function.See the post on callbacks for the different variations with C++ working examples.
此问题与解答来自C++ FAQ Lite 很好地涵盖了您的问题以及答案中涉及的注意事项,我认为。 我链接的网页的简短片段:
This question and answer from the C++ FAQ Lite covers your question and the considerations involved in the answer quite nicely I think. Short snippet from the web page I linked:
看起来像
std::mem_fn
( C++11) 正是您所需要的:Looks like
std::mem_fn
(C++11) does exactly what you need: