2014-11-06 26 views
1

我的目的是從我的C++代碼調用一些C函數並傳遞一些C++對象。 其實我使用從GSL libray(用C語言編寫)一個集成常規,看到這個link這個C回調是否安全使用C++對象?

我的代碼片段:

// main.cpp 

#include <stdio.h> 
#include <gsl/gsl_integration.h> 
#include <myclass.h> 

/* my test function. */ 
double testfunction (double x , void *param) { 
    myclass *bar=static_cast<myclass*>(param); 

    /*** do something with x and bar***/ 

    return val; 

    } 

int main (int argc , char *argv[]) { 

    gsl_function F; // defined in GSL: double (* function) (double x, void * params) 

    /* initialize.*/ 
    gsl_integration_cquad_workspace *ws = 
    gsl_integration_cquad_workspace_alloc(200) ;  

    /* Prepare test function. */ 
    myclass foo{}; // call myclass constructor 
    F.function = &testfunction; 
    F.params = &foo; 


    /* Call the routine. */ 
    gsl_integration_cquad(&F, 0.0,1.0,1.0e-10,1.0e-10,ws, &res,&abserr,&neval); 


    /* Free the workspace. */ 
    gsl_integration_cquad_workspace_free(ws); 

    return 0; 

    } 

在我的情況下,直接調用gsl_integration_cquad看起來不錯,但所提供的頭文件包含諸如「ifdef __cplusplus」,我的擔心是關於回調F,最初在C中定義的,我是否允許以這種方式通過測試函數和C++ foo對象? 。

還是有沒有更好的方法來做這種東西,也許重載和使用函數?

P.S.我是否允許在回調函數中執行exeption處理? (使用try catch在「testfunction」中)。它適用於我的情況,但不確定它是否合法。

+0

您方式足夠好 - 沒有限制讓事情變得不必要的複雜。你可以以任何方式混合使用c和C++,只要你的鏈接器管理來構建最終的可執行文件或共享對象。 – lowtech 2014-11-06 19:12:51

+0

簡短的回答是肯定的,這很好。但是,我會在'testfunction'的聲明中加入'__cdecl',以防萬一它不是給定平臺的默認值。 – Cameron 2014-11-06 19:18:03

+0

@lowtech謝謝,無論如何,我經常聽說混合C和C++導致問題... – lorniper 2014-11-06 19:18:20

回答

6

我不熟悉的問題庫,但在一般情況下, 指針傳遞到回調和void*到 一個C程序,該程序會調用回調回來的void*時, 有兩個你需要做的事情,使之安全:

  • 的功能,其地址傳遞必須聲明的extern「C」。 你不會用大量的編譯器做這件事,但 這是不合法的,一個好的編譯器會抱怨。

  • 您轉換爲void*的類型必須與您在回調中將其重新轉回的類型完全相同 類型。 的經典錯誤是將類似new Derived的東西傳遞給 C函數,並在回調中將其轉換回Base*。 往返Derived*void*Base*未定義 行爲。它會在一段時間內工作,但在其他時間,它可能會崩潰,或導致任何其他問題。

  • 而cdhowie在評論中指出,您不希望 允許異常傳播到C代碼。再次,它可能工作。但它可能不會。

爲您發佈確切的例子,你需要做的 的唯一一件事就是宣佈testfunctionextern "C",和你所有的 權。如果你以後開始使用多態對象,然而,請注意第二點。

+0

我一直在嘗試[Ideone C++ 11](http ://ideone.com/3pxksi)並感到驚訝。 – 2014-11-06 19:24:22

+0

謝謝,我使用g ++,似乎evenif我沒有指定testfunction爲「extern C」,沒有問題,只要gsl_integration_cquad頭包含「ifdef __cplusplus extern C」。 – lorniper 2014-11-06 19:29:59

+0

值得指出的是,C++異常可能導致堆棧放鬆C代碼,這可能不會爲這種可能性做好準備。也就是說,回調上下文中的C++異常可能會使工作空間對象處於不良狀態。 – cdhowie 2014-11-06 19:35:23

2

You can use

myclass *bar=static_cast<myclass*>(param); 

void*

如果你的意思是像通過C回調的void*指針運送C++類的指針,是的,它是安全static_cast<>

當通過c代碼傳遞時,沒有一種會丟失此類指針的C++特定屬性。雖然傳遞派生類指針,並將靜態轉換回基類,但不能正常工作,因爲@James Kanze pointed out

+0

很多C庫提供了這樣的void *接口,而且我經常使用靜態強制轉換,在我看來相當安全。也許你可以解釋更多,因爲我根本無法將接口更改爲C庫函數,那麼我該如何用C++ objs提供C回調? – lorniper 2014-11-06 19:13:20

+0

我會downvote你的答案,如果你不提供解釋 – lowtech 2014-11-06 19:14:45

+0

@lowtech冷靜下來... – 2014-11-06 19:18:00

1

void*很可能只是被C庫忽略而沒有查看指向數據,所以如果它包含一個C++類,這不是問題。正如您將void*正確投射到日誌中一樣,不應該有任何問題。

要確保回調函數本身是兼容的,您可以聲明它爲extern "C"。另外,你應該確保沒有任何異常從回調函數中拋出,因爲調用回調的C代碼不會期望這些異常。

總之我會分裂的代碼放到一個函數,它的實際工作,被用作回調並處理與C庫接口其他功能,例如像這樣:

#include <math.h> 

double testfunction (double x ,myclass *param) { 
    /*** do something with x and bar***/ 
    return val; 
} 

extern "C" double testfunction_callback (double x , void *param) { 
    try { 
     myclass *bar=reinterpret_cast<myclass*>(param); 
     return testfunction(x, bar); 
    } 
    catch(...) { 
     std::cerr << "Noooo..." << std::endl; 
     return NAN; 
    } 
} 
+0

感謝您的代碼示例! – lorniper 2014-11-06 19:35:32

相關問題