简体   繁体   English

指向全局函数指针的C ++成员函数指针

[英]C++ member function pointer to global function pointer

I have to solve, at least for me, a tricky problem in C++. 至少对我来说,我必须解决C ++中的一个棘手问题。 There is a dll which i can not modify. 有一个dll,我无法修改。 It gets a function pointer as argument. 它获取一个函数指针作为参数。 If I pass a pointer to a global function everything works fine. 如果我将指针传递给全局函数,则一切正常。 Unfortunatelly there is a list of same class objects to pass to the dll . 不幸的是,存在要传递给dll的相同类对象列表 In C# I solved this by using delegates. 在C#中,我通过使用委托解决了这一问题。 How can this be done in C++? 如何在C ++中完成? Using std::function does not work. 使用std :: function不起作用。 With that there are coding convention errors during runtime. 这样一来,在运行时就会出现编码约定错误。 Further using MSVC2010 would be optimal. 进一步使用MSVC2010将是最佳选择。 I wrote a sample which describes the problem: 我写了一个描述问题的样本:

#include <stdio.h>


// global function which works
void __stdcall task_global(float x, float y) { printf("Called global function with: %f %f\n", x, y); }
typedef void(__stdcall *f_pointer)(float, float);



// try of using a member function
class BaseTask {
public:
    virtual void __stdcall task(float x, float y) = 0;
};


class ListeningTask :public BaseTask {
public:
    void __stdcall task(float x, float y) { printf("Called this member function with: %f %f\n", x, y); }
};

typedef void (BaseTask::*member_f_pointer)(float, float);



// the dll to use
class RegisterTask {
public:
    // no posibility to access or modify!
    void __stdcall subscribe(f_pointer fp) { fp(1.0f, 2.0f); }
    // just for demonstration how to use a member function pointer
    void __stdcall subscribeMemberDemo(member_f_pointer mfp) {  /*how to use mfp?*/};
};



int main() {

    RegisterTask register_task{};

    // use global function
    f_pointer pointer_to_global_task = task_global;
    register_task.subscribe(pointer_to_global_task);


    /*---------------------------------------------------------------*/
    // use member function?
    std::list<ListeningTask> listening_task_list;
    for(int i = 0; i < 10; i++) {
        listening_task_list.push_back(ListeningTask lt);
        member_f_pointer pointer_to_member_task = &listening_task_list.back().task; //error C2276: '&': illegal operation on bound member function expression
        register_task.subscribeMemberDemo(pointer_to_member_task);

        // the tricky and important one to solve
        // how to pass the member function to this subscribe(f_pointer)?
        register_task.subscribe(pointer_to_member_task);
    }

    getchar();
    return 0;
}

The important question is how to pass a member function pointer to the RegisterTask::subscribe(f_pointer) ? 重要的问题是如何将成员函数指针传递给RegisterTask::subscribe(f_pointer)

The parenthetic question is how to pass a member function to the RegisterTask::subscribeMemberDemo(member_f_pointer) ? 括号中的问题是如何将成员函数传递给RegisterTask::subscribeMemberDemo(member_f_pointer)

I hope someone can help me to solve this? 我希望有人可以帮助我解决这个问题吗? I am working on this since days. 从那以后我一直在努力。

Edit: I modified the question to emphasize the problem with the list of ListenerTask . 编辑:我修改了问题,以强调与ListenerTask列表有关的问题。 How to pass a member function pointer is now clear to me through the answers of @pokey909 and @AndyG. 现在,通过@ pokey909和@AndyG的答案,如何传递成员函数指针已经很清楚。 Both of them provide a pointer to one object or rather a list of objects. 它们都提供了指向一个对象或对象列表的指针。 If the callback is called the one ListenerTask or all std::list<*ListenerTask> are called at once. 如果调用该回调,则一次调用所有ListenerTask或所有std::list<*ListenerTask> But how to let only one ListenerTask of the list to be called. 但是如何让列表中只有一个ListenerTask被调用。 Passing more than one callback to the dll. 将多个回调传递给dll。 It ( RegisterTask ) can do that, because the following example with global functions works. 它( RegisterTask )可以做到这一点,因为下面的示例具有全局功能。

void __stdcall task_global_1(float x, float y) { printf("Called global function 1 with: %f %f\n", x, y); }
void __stdcall task_global_2(float x, float y) { printf("Called global function 2 with: %f %f\n", x, y); }
void __stdcall task_global_3(float x, float y) { printf("Called global function 3 with: %f %f\n", x, y); }

typedef void(__stdcall *f_pointer)(float, float);

int main() {
    // give the functions to the dll.
    f_pointer pointer_to_global_task_1 = task_global_1;
    register_task.subscribe(pointer_to_global_task_1);

    f_pointer pointer_to_global_task_2 = task_global_2;
    register_task.subscribe(pointer_to_global_task_2);

    f_pointer pointer_to_global_task_3 = task_global_3;
    register_task.subscribe(pointer_to_global_task_3);
}

There are three global function pointers. 有三个全局函数指针。 They are all given to the dll. 他们都给了dll。 Now, if the dll has a task for task_global_2 it notifies this only! 现在,如果dll具有task_global_2的任务,则仅通知该任务! How to achive this distinction with member function pointer? 如何用成员函数指针达到这种区别?

Note: I got the source of the dll . 注意:我得到了dll来源 Hope this helps. 希望这可以帮助。 Unfortunately modifying, building is not possible. 不幸的是,修改是不可能的。 Here is the callback definition : 这是回调定义

type TCallback = procedure( x : single; y : single; ); stdcall;

procedure subscribe(aCallback: TCallback ); StdCall;
begin
  TaskSocket.addTask( aCallback );
end;

procedure TSocket.addTask( aCallback : TCallback);
var newTask : TTask;
begin
  newTask := TTask.Create(aCallback);
  TaskList.addItem(newTask);
end;

You can use a freestanding function that calls a wrapper which binds your instance to it. 您可以使用一个独立的函数来调用将您的实例绑定到它的包装器。 Here is rough example 这是一个粗糙的例子

#include <iostream>
#include <string>
#include <functional>

// global function which works
std::function<void(float, float)> memberCb;
void task_global(float x, float y) { memberCb(x, y); }
typedef void(*f_pointer)(float, float);


// try of using a member function
class BaseTask {
public:
    virtual void  task(float x, float y) = 0;
};


class ListeningTask :public BaseTask {
public:
    void  task(float x, float y) { printf("Called this member function with: %f %f\n", x, y); }
};

typedef void (BaseTask::*member_f_pointer)(float, float);

void callbackWrapper(BaseTask* t, float x, float y) { t->task(x, y); }

// the dll to use
class RegisterTask {
public:
    // no posibility to access or modify!
    void  subscribe(f_pointer fp) { 
        fp(1.0f, 2.0f); 
    }
    // just for demonstration how to use a member function pointer
    void  subscribeMemberDemo(member_f_pointer mfp) {  /*???*/ };
};



int main() {

    RegisterTask register_task{};

    ListeningTask listening_task{};
    memberCb = std::bind(&callbackWrapper, &listening_task, std::placeholders::_1, std::placeholders::_2);
    register_task.subscribe(task_global);

    return 0;
}

Note 注意

Based on the comment, I'm not sure if all of this works in MSVC2010, since I don't have this compiler version. 基于此评论,由于我没有此编译器版本,因此我不确定所有这些功能是否都能在MSVC2010中工作。 But rudimentary C++11 support should be in there. 但是最基本的C ++ 11支持应该在那里。

Edit 编辑

I'm not sure if thats what you are after, but would this solve your problem? 我不确定这是否就是您的追求,但这是否可以解决您的问题?

void callbackWrapper(const std::list<BaseTask*> &taskList, float x, float y) {
    for (auto t : taskList)
        t->task(x, y); 
}

int main() {

    RegisterTask register_task{};

    std::list<BaseTask*> taskList;
    for (int i = 0; i < 4; ++i)
        taskList.push_back(new ListeningTask);
    memberCb = std::bind(&callbackWrapper, taskList, std::placeholders::_1, std::placeholders::_2);
    register_task.subscribe(task_global);

    return 0;
}

Edit 2 Ok I think I got what you want. 编辑2好吧,我想我得到了你想要的。 The best I can come up with without splattering your code with global functions manually is with template magic. 在不使用全局函数手动填充代码的情况下,我能想到的最好的方法就是模板魔术。 Note however, that it is not as flexible as you might want because you have to bind those methods at compile time. 但是请注意,它并没有您想要的灵活,因为您必须在编译时绑定这些方法。 If you need to add them at runtime, you can probably use the same trick but without templates. 如果需要在运行时添加它们,则可以使用相同的技巧,但无需模板。 Simply put all the std::function objects in a vector and wrap that up in a singleton or something similar. 只需将所有std :: function对象放入向量中,然后将其包装为单例或类似形式。

#include <iostream>
#include <string>
#include <functional>
#include <list>


/* Simulated DLL */
typedef void(*f_pointer)(float, float);
class RegisterTask {
public:
    void  subscribe(f_pointer fp) {
        fp(1.0f, 2.0f);
    }
};



/* Static function generator to ease the pain to define all of them manually */
template<unsigned int T>
std::function<void(float, float)> &getWrapper() {
    static std::function<void(float, float)> fnc;
    return fnc;
}

/* Same here */
template<unsigned int T>
void task_global(float x, float y) { getWrapper<T>()(x, y); }



class BaseTask {
public:
    virtual void  task(float x, float y) = 0;
};
class ListeningTask :public BaseTask {
public:
    ListeningTask(int taskNum) : m_taskNum(taskNum) {}
    void  task(float x, float y) { printf("Called this member of task %d function with: %f %f\n", getTaskNum(), x, y); }
    int getTaskNum() const { return m_taskNum; }
private:
    int m_taskNum;
};


/* Context injector */
void callbackWrapper(BaseTask* t, float x, float y) {
    t->task(x, y);
}

/* Convenience function to bind an instance to a task */
template<unsigned int T>
void bindTask(ListeningTask* t) {
    getWrapper<T>() = std::bind(&callbackWrapper, t, std::placeholders::_1, std::placeholders::_2);
}

int main() {

    RegisterTask register_task{};

    auto task0 = new ListeningTask(1337);
    auto task1 = new ListeningTask(1984);
    auto task2 = new ListeningTask(42);

    bindTask<0>(task0);
    register_task.subscribe(task_global<0>);

    bindTask<1>(task1);
    register_task.subscribe(task_global<1>);

    bindTask<2>(task2);
    register_task.subscribe(task_global<2>);

    return 0;
}

Run Code demo 运行代码演示

pokey909's answer is totally great, but if you don't even have access to std::function and std::bind , we can hack our way around it. pokey909的答案是完全不错的,但是如果您什至没有访问std::functionstd::bind权限,我们可以设法解决它。

The gist of the approach is that we are going to define a template class with an implicit conversion to the desired function type. 该方法的要点是,我们将定义一个模板类,并将其隐式转换为所需的函数类型。 The downside is that each new additional wrapper requires a new type declaration. 缺点是每个新的附加包装器都需要一个新的类型声明。

// assumes two function arguments
template<class Ret, class Mem, class Arg1, class Arg2, int>
struct MemBind
{
    typedef Ret(Mem::*mem_fn_type)(Arg1, Arg2);
    static void Set(mem_fn_type _fn, Mem* _instance)
    {
        fn = _fn;
        instance = _instance;
    }

    static Ret DoTheThing(Arg1 first, Arg2 second)
    {
        return ((*instance).*fn)(first, second);
    }

    typedef Ret(*fn_type)(Arg1, Arg2); 
    operator fn_type ()
    {
        return DoTheThing;
    }

    static mem_fn_type fn;
    static Mem* instance;
};

Given some struct Foo with our desired callback: 给定一些结构Foo并提供所需的回调:

struct Foo
{
    void Bar(float a, float b)
    {
        std::cout << "Foo::Bar(float, float) " << a << " , " << b << std::endl;
    }
};

We have to define our static members: 我们必须定义我们的静态成员:

typedef MemBind<void, Foo, float, float, 0> MemBindZero;
template<> Foo* MemBindZero::instance = nullptr;
template<>  void(Foo::*MemBindZero::fn)(float, float)  = nullptr;

We can have a caller that takes in a function pointer: 我们可以有一个带有函数指针的调用方:

void Caller(void(*_fn)(float, float))
{
    _fn(42.0, 1337.0);
}

The key here is that MemBind has an implicit conversion to the desired function type. 这里的关键是MemBind可以隐式转换为所需的函数类型。 The 0 in the typedef for MemBindZero allows us to re-use the same types for the other arguments, but increment the counter to 1 when used. MemBindZero的typedef中的0允许我们为其他参数重用相同的类型,但是使用时将计数器增加到1 I think you could probably replace it with a __COUNTER__ macro or something like that, but it would be nonstandard so I did it manually. 我认为您可能可以用__COUNTER__宏或类似的东西代替它,但是它不是标准的,所以我手动完成了。

Now the next bit is to create an instance of MemBindZero , then set the static members, and finally pass our instance into Caller : 现在,下一步是创建MemBindZero的实例,然后设置static成员,最后将我们的实例传递给Caller

 Foo f;
 MemBindZero bound;
 bound.Set(&Foo::Bar, &f);
 Caller(bound);

Demo 演示


In the demo I wrapped the static member initialization into a more convenient macro: 在演示中,我将静态成员初始化包装到一个更方便的宏中:

#define MEMBIND(RET, CLASS, ARG1, ARG2, COUNT, ALIAS) \
typedef MemBind<RET, CLASS, ARG1, ARG2, COUNT> ALIAS; \
template<> CLASS * ALIAS::instance = nullptr; \
template<>  RET(CLASS::*ALIAS::fn)(ARG1, ARG2)  = nullptr;

So that I could call it like so: 这样我就可以这样称呼它:

MEMBIND(void, Foo, float, float, 0, MemBindZero)
MEMBIND(void, OtherFoo, float, float, 1, MemBindOne)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM