繁体   English   中英

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

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

至少对我来说,我必须解决C ++中的一个棘手问题。 有一个dll,我无法修改。 它获取一个函数指针作为参数。 如果我将指针传递给全局函数,则一切正常。 不幸的是,存在要传递给dll的相同类对象列表 在C#中,我通过使用委托解决了这一问题。 如何在C ++中完成? 使用std :: function不起作用。 这样一来,在运行时就会出现编码约定错误。 进一步使用MSVC2010将是最佳选择。 我写了一个描述问题的样本:

#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;
}

重要的问题是如何将成员函数指针传递给RegisterTask::subscribe(f_pointer)

括号中的问题是如何将成员函数传递给RegisterTask::subscribeMemberDemo(member_f_pointer)

我希望有人可以帮助我解决这个问题吗? 从那以后我一直在努力。

编辑:我修改了问题,以强调与ListenerTask列表有关的问题。 现在,通过@ pokey909和@AndyG的答案,如何传递成员函数指针已经很清楚。 它们都提供了指向一个对象或对象列表的指针。 如果调用该回调,则一次调用所有ListenerTask或所有std::list<*ListenerTask> 但是如何让列表中只有一个ListenerTask被调用。 将多个回调传递给dll。 它( 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);
}

有三个全局函数指针。 他们都给了dll。 现在,如果dll具有task_global_2的任务,则仅通知该任务! 如何用成员函数指针达到这种区别?

注意:我得到了dll来源 希望这可以帮助。 不幸的是,修改是不可能的。 这是回调定义

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;

您可以使用一个独立的函数来调用将您的实例绑定到它的包装器。 这是一个粗糙的例子

#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;
}

注意

基于此评论,由于我没有此编译器版本,因此我不确定所有这些功能是否都能在MSVC2010中工作。 但是最基本的C ++ 11支持应该在那里。

编辑

我不确定这是否就是您的追求,但这是否可以解决您的问题?

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;
}

编辑2好吧,我想我得到了你想要的。 在不使用全局函数手动填充代码的情况下,我能想到的最好的方法就是模板魔术。 但是请注意,它并没有您想要的灵活,因为您必须在编译时绑定这些方法。 如果需要在运行时添加它们,则可以使用相同的技巧,但无需模板。 只需将所有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;
}

运行代码演示

pokey909的答案是完全不错的,但是如果您什至没有访问std::functionstd::bind权限,我们可以设法解决它。

该方法的要点是,我们将定义一个模板类,并将其隐式转换为所需的函数类型。 缺点是每个新的附加包装器都需要一个新的类型声明。

// 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;
};

给定一些结构Foo并提供所需的回调:

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

我们必须定义我们的静态成员:

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

我们可以有一个带有函数指针的调用方:

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

这里的关键是MemBind可以隐式转换为所需的函数类型。 MemBindZero的typedef中的0允许我们为其他参数重用相同的类型,但是使用时将计数器增加到1 我认为您可能可以用__COUNTER__宏或类似的东西代替它,但是它不是标准的,所以我手动完成了。

现在,下一步是创建MemBindZero的实例,然后设置static成员,最后将我们的实例传递给Caller

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

演示


在演示中,我将静态成员初始化包装到一个更方便的宏中:

#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;

这样我就可以这样称呼它:

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