2014-10-28 199 views
4

我需要通過一個標準的函數指針稱爲成員函數,所以我試圖抽象的東西是這樣的:如何從靜態成員函數調用指向成員函數的指針?

class Sample { 
public: 
    virtual void doSomething(void) = 0; 
}; 


class A : public Sample { 
    void doSomething(void);  // details omitted 
}; 

class B : public Sample { 
    void doSomething(void);  // details omitted 
}; 


class Executor { 
public: 
    Executor(Sample *sample) 
    : func(&sample->doSomething) 
    { 
    } 

    static void *execute(void *data) { 
     Executor *pX = data; 

     (pX->*func)();   // error invalid access of func from static function 

     (pX->*pX->func)();  // error pointer to member type 'void (Sample::)()' 
           //  incompatible with object type 'Executor' 
    } 

private: 
    void (Sample::*func)(void); 
}; 



int main(void) { 
    A myA; 
    B myB; 
    Executor x0(&myA); 
    Executor x1(&myB); 

    externallyInvoke(&Executor::execute, &x0); 
    externallyInvoke(&Executor::execute, &x1); 
} 

externallyInvoke是Linux系統調用,這需要一個函數指針和數據指針。 我想使用一個靜態成員函數和一個this-pointer作爲數據。

...我不希望類如AB擁有靜態成員。所以我的想法是創建一個像Sample類的接口,該接口被AB擴展。

我的問題是,我不知道如何從Executor::execute函數內部調用指向成員函數的指針。

+2

無法從靜態函數訪問成員變量 - 什麼不清楚? – 2014-10-28 09:31:59

回答

2

的問題是,你需要內部execute兩個對象 - 一個是Executor的實例,它會提供func,另一個是(從派生的類)Samplefunc將被調用的一個實例。所以,你必須存儲Executor對象,而不是功能:

class Executor { 
public: 
    Executor(Sample *sample) 
    : obj(sample) 
    { 
    } 

    static void *execute(void *data) { 
     Executor *pX = static_cast<Executor*>(data); 

     pX->obj->doSomething(); 
    } 

private: 
    Sample *obj; 
}; 


int main() { // note that `void main()` is not legal C++ 
    A myA; 
    B myB; 
    Executor x0(&myA); 
    Executor x1(&myB); 

    externallyInvoke(&Executor::execute, &x0); 
    externallyInvoke(&Executor::execute, &x1); 
} 

一個成員函數指針(比如你原來void (Sample::*func)())標識類中的一個功能,但不存儲對象。您仍然需要提供一個來調用該函數。

+0

不應該'x0(myA)'是'x0(&myA)'? – wimh 2014-10-28 10:08:29

+0

@Wimmel當然,在問題中解決問題之前,我從OP複製了該問題。謝謝。 – Angew 2014-10-28 10:13:27

+0

@Angew我顯然希望重塑一個仿函數,但是你的解決方案比較乾淨,可愛:) THX – django013 2014-10-30 07:20:56

0

您還需要傳遞一個Sample的實例來調用該函數(因爲它是指向Sample成員的指針)。有幾種方法可以使實例一致。你可以讓它成爲Executor的成員,通過std::pair*作爲data,或者你可以將函數指針和實例組合成一個函子。這是後者的基於lamda的方法。 Lamda具有更多功能的優勢。可以做的不僅僅是調用一個類的一個成員。作爲獎勵,這種方法不會避免可見性規則,儘管這意味着doSomething可能不是私有的(或者它必須通過父指針來調用)。

template<class F> 
class Executor { 
    F f; 
public: 
    Executor(F f): f(f){} 
    static void *execute(void *data) { 
     Executor<F> *pX = static_cast<Executor<F>*>(data); 
     pX->f(); 
     return this; // not quite sure what you intend to return, but just to make this a well formed function... 
    } 
}; 


int main() { 
    A myA; 
    B myB; 
    auto callback0 = [myA]{ 
     myA.doSomething(); 
    }; 
    auto callback1 = [myB]{ 
     myB.doSomething(); 
    }; 
    Executor<decltype(callback0)> x0(callback0); 
    Executor<decltype(callback1)> x1(callback1); 

    externallyInvoke(&Executor::execute, &x0); 
    externallyInvoke(&Executor::execute, &x1); 
} 
+0

'func'是一個成員,它不能在'static'方法中使用。 – user657267 2014-10-28 09:32:54

+0

@ user657267啊,gotcha。修復了答案。 – user2079303 2014-10-28 09:35:35

+0

這怎麼解決答案?他想通過一個'static'方法調用成員函數指針,這根本不可能,使得「public」不會改變。 – user657267 2014-10-28 09:37:35

0

如果您想要與外部系統調用進行交互,您必須自己重新創建std::function。沒問題,在Stack Overflow,我們是重塑現有技術的主人。所以......

首先,接口:

struct FunctionStateBase 
{ 
    virtual ~FunctionStateBase() {} 
    virtual void Invoke() = 0; 
}; 

extern "C" void InvokeAndDelete(void * data) 
{ 
    auto state = static_cast<FunctionStateBase *>(data); 
    state->Invoke(); 
    delete state; 
} 

這裏是你如何使用它:

externallyInvoke(&InvokeAndDelete, MakeFunction(&A::doSomething, &myA)); 

現在我們需要實現MakeFunction

template <typename> struct FunctionState; 

template <typename C, typename R> 
struct FunctionState<R (C::*)()> : FunctionStateBase 
{ 
    R (C::ptmf_*)(); 
    C * obj_; 

    FunctionState(R (C::ptmf*)(), C * obj) : obj_(obj), ptmf_(ptmf) {} 

    virtual void Invoke() { (C->ptmf_)(); } 
}; 

template <typename C, typename R> 
FunctionState<R (C::*)()> MakeFunction(R (C::*ptmf)(), C * obj) 
{ 
    return new FunctionState<R (C::*)()>(ptfm, obj); 
} 

此時我們正在手動管理函數包裝器的生命週期,並注意InvokeAndDelete行爲最終取得了功能狀態的所有權。在合適的C++中,我們會將整個系統調用調用包裝在一個將封裝生命週期管理的內部類中。

您可以爲帶參數的成員函數添加進一步的特化;你只需要在狀態中存儲參數的副本。