簡體   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