【问题标题】:Function pointer to class member function problems指向类成员函数问题的函数指针
【发布时间】:2010-10-01 04:07:42
【问题描述】:

首先,我必须承认我的编程技能非常有限,我接手了一个(非常小的)现有 C++ OOP 项目,我试图将自己的东西推入其中。不幸的是,我遇到了一个超出范围的问题我的知识,我希望在这里找到一些帮助。我正在使用第三方库(无法更改)从相机中获取图像,并将在此处使用一些占位符名称。

第三方库有一个函数“ThirdPartyGrab”来启动连续实时抓取,并获取一个指向函数的指针,每次新帧到达时都会调用该函数。所以在一个普通的 C 应用程序中它是这样的:

ThirdPartyGrab (HookFunction);

“HookFunction”需要声明为:

long _stdcall HookFunction (long, long, void*);

或声明为“BUF_HOOK_FUNCTION_PTR”

typedef long (_stdcall *HOOK_FUNCTION_PTR) (long, long, void*);

现在我有一个 C++ 应用程序和一个“MyFrameGrabber”类,它应该封装我所做的一切。所以我把钩子函数作为一个私有成员放入,如下所示:

long _stdcall HookFunction (long, long, void*);

我的班级中还有一个公共 void 函数“StartGrab”应该启动 Grab。在里面我尝试调用:

ThirdPartyGrab (..., HookFunction, ...);

其中(不足为奇)失败了。它说对 MyFrameGrabber::HookFunction 的函数调用错过了参数列表,我应该尝试使用 &MyFrameGrabber::HookFunction 来创建一个指针。但是,传递“&MyFrameGrabber::HookFunction”会导致另一个错误,即无法将其转换为 BUF_HOOK_FUNCTION_PTR。

阅读C++ FAQ function pointers 后,我想我理解了问题但无法提出解决方案。我试图将钩子函数设为静态,但这也会导致转换错误。我也想过将钩子函数放在类之外,但我需要在钩子函数中使用类函数。有没有其他方法或者我需要改变我的整个概念?

编辑 14.01.08: 我测试了单例解决方法,因为我无法更改第三方库,并且 void 指针仅用于钩子函数内部使用的数据。不幸的是,它并没有像我希望的那样开箱即用......我不知道静态函数是否需要放在一个单独的类中,所以我把它放在我的“MyFrameGrabber”类中:

static MyFrameGrabber& instance()
{
        static MyFrameGrabber _instance;
        return _instance;
}
long Hook(long, long, void*); // Implementation is in a separate cpp file

在我的 cpp 文件中,我有 call_hook 函数:

long MFTYPE call_hook(long x, MIL_ID y, void MPTYPE *z)
{
    return MyFrameGrabber::instance().Hook(x,y,z);
}
void
MyFrameGrabber::grab ()
{
    ThirdPartyGrab(..., call_hook, ...);
}

但这在static MatroxFrameGrabber _instance; 中给我一个错误,即找不到匹配的标准构造函数。这是正确的,因为我的 MyFrameGrabber 构造函数如下所示:

MyFrameGrabber (void* x,
                const std::string &y, int z,
                std::string &zz);

我试图放入一个空的构造函数MyFrameGrabber();,但这会导致链接器错误。我应该将空参数传递给单例中的 MyFrameGrabber 构造函数吗?或者我是否需要一个单独的 Hook 类,如果是,我如何访问 MyFrameGrabber 函数?提前致谢。

第二次编辑 15.01.08: 我应用了更改,它现在可以编译和链接。不幸的是,我还不能在运行时测试它,因为它是一个 DLL,而且我还没有 Debug Caller Exe,并且在初始化等过程中还有其他问题。我会将帖子标记为答案,因为我确信这是正确的方法。

【问题讨论】:

    标签: c++ pointers function-pointers


    【解决方案1】:

    您的私有成员方法有一个隐含的this 指针作为第一个参数。如果你把它写出来,很明显函数签名不匹配。

    您需要编写一个静态成员函数,它可以作为回调函数传递给库。 HookFunction 的最后一个参数 void* 在我看来很像一个 cookie,可以将自己的指针传入其中。

    所以,总而言之,应该是这样的:

    我的班级{ 长我的回调(长,长){ // 在这里实现你的回调代码 } 静态长 __stdcall ThirdPartyGrabCallback(long a, long b, void* self) { return reinterpret_cast(self)->MyCallback(a, b); } 民众: 无效开始抓取(){ ThirdPartyGrab(..., &MyClass::ThirdPartyGrabCallback, ..., this, ...); } };

    这当然只有在 void* 参数按照我说的做时才有效。当具有完整的函数签名(包括可用的参数名称)时,thisThirdPartyGrab() 调用中的位置应该很容易找到。

    【讨论】:

      【解决方案2】:

      “&MyFrameGrabber::HookFunction”无法转换为 BUF_HOOK_FUNCTION_PTR 的原因是,作为该类的成员,它隐式地将“this”指针作为第一个参数,因此您无法将成员函数转换为非成员函数:这两个签名看起来一样,但实际上是不同的。

      我会声明一个接口,定义要调用的函数,让您的类实现它并传递对象本身而不是回调(您可以将接口视为函数指针的面向对象的替换):

      class IHookInterface{
      public:
          virtual long HookFunction(long, long, void*) = 0;
      };
       class HookClass : public IHookInterface{
      public:
          virtual long Hook(long, long, void*) {
              // your code here... 
          }
      };
      

      // 新定义: ThirdPartyGrab (..., IHookInterface, ...);

      编辑 - 如果您无法修改库,其他可能的解决方案:使用单例而不是静态函数。

      class HookClass{
      public:
          static HookClass& instance(){
              static HookClass _instance;
              return _instance;
          }
          long Hook(long, long, void*) {
              // your code here... 
          }
      };
      
      long call_hook(long x,long y,void * z){
          return HookClass::instance().Hook(x,y,z);
      }
      

      第二次编辑:您可以使用初始化方法稍微修改单例类,以使用适当的参数调用构造函数,但也许它并不比以下更简单的解决方案更优雅:

      class HookClass{
      public:
      
          HookClass(string x,string y...){
          }
      
          long Hook(long, long, void*) {
              // your code here... 
          }
      };
      
      static HookClass * hook_instance = 0;
      
      long call_hook(long x,long y,void * z){
          if (0 != hook_instance){
              return hook_instance->Hook(x,y,z);
          }
      }
      
      int main(){
          hook_instance = new HookClass("x","y");
          ThirdPartyGrab(..., call_hook, ...);
      }
      

      【讨论】:

      • 我认为对于“第三方库”,OP 意味着他无法更改库。不过,我的假设很可能是错误的。
      • C 回调中 void* 参数的常见用途是作为 C++ 的 this-> 指针。这意味着你会写很长的 (Hook, Hook) 并跳过静态 hook_instance
      猜你喜欢
      • 2017-07-27
      • 2013-06-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-11-18
      相关资源
      最近更新 更多