2012-10-26 42 views
1

我需要編寫一個自動生成將所有參數轉發給另一個(成員)函數的函數的宏。使用c預處理器自動生成函數轉發器

如果您需要知道爲什麼需要它,我需要簡化編寫JNI膠水。 我會省略其他原因,爲什麼我需要這樣做,我只是提到我不能使用boost(儘管我可能會刪除所需的部分並將其從boost轉換爲我自己的宏)。我還檢查了一些其他庫(jace等),但沒有找到任何符合我需要的東西。

總之,這裏的示例JNI功能:

class TestClass 
{ 
    void nativeTest(JNIEnv *, jobject, jint, jboolean) 
    { 
     ... 
    } 

    static TestClass* getPeer(JNIEnv *, jobject obj) 
    { 
     ... 
    } 
} 

JNIEXPORT void JNICALL Java_com_noname_media_TestClass_nativeTest(
    JNIEnv *env, jobject obj, jint i, jboolean b 
) 
{ 
    TestClass* peer = TestClass::getPeer(env, obj, i, b); 
    if(peer) 
     return peer->nativeTest(env, obj, i, b); 
    return; 
} 

現在,我想寫一些JNI_FUNCTION宏,會自動生成所有Java_com_noname_media_TestClass_nativeTest。經過一番思考,我想我能做到這一點是這樣的:

#define JNI_FUNCTION(functionName, functionReturn, functionArgs) \ 
JNIEXPORT functionReturn JNICALL         \ 
    Java_com_noname_media_TestClass##functionName(**WTF**)   \ 
{ 
     TestClass* peer = TestClass::getPeer(**WTF**); 
     if(peer) 
      return peer->functionName(**WTF**); 
     return; 
} 

然後,使用JNI_FUNCTION我可以做這樣的事情:

JNI_FUNCTION(nativeTest, void, (JNIEnv *, jobject, jint, jboolean)); 

的問題是,我不知道如何「破解」函數參數,因爲我需要爲列表functionArgs中的每個條目添加自動編號的參數名稱。

其他陷阱:返回類型可以是某種類型或無效,但對於無效的情況下,我可能會單獨使用JNI_VOID_FUNCTION以防萬一使用常規方式無法輕鬆完成。我所有的jni函數在functionArgs列表中至少有兩個參數,例如它不能爲空列表()。 我沒有使用functionArgs作爲包含多個參數一個參數,我確定這個方式,以及:

#define JNI_FUNCTION(functionName, functionReturn, ...) 

無論工作......也許我需要某種宏觀的,讓我在某個位置提取一些宏,比如ARG_1(...)等,但到目前爲止,我無法將它全部包裝在我的大腦中如何去做。

PS。我記得在使用c-preprocessor時有一些非常酷的例子,在這裏有非常好的解釋,但是現在找不到它們,如果你有書籤,也許我需要的只是看看它們。

編輯: 基本上,關鍵是要自動編號名稱添加到每個參數,然後將它們作爲-是一個成員函數。我需要這樣做的原因是因爲除了預處理器之外,還有其他一些自動生成功能。總之,這個宏實際上將一組類似的宏(在ATL/WTL的東西等)中使用:

JNI_TABLE_BEGIN(ClassName) 
    JNI_FUNCTION(native1, void, (JNIEnv *, jobject, jint)) 
    JNI_FUNCTION(native2, void, (JNIEnv *, jobject, jint)) 
    JNI_FUNCTION(native3, jint, (JNIEnv *, jobject)) 
JNI_TABLE_END() 
+0

您確定這應該被標記爲C嗎? Java或C++中的JNI函數是什麼? –

+0

哦,好的,喬納森,你是最酷的c預處理器樣本,我馬上認出你的名字:)我會檢查你的回覆,也許我會在那裏找到我需要的。是的,我的文章只有c/C++的東西,這裏沒有涉及java。 – Pavel

+0

我的問題的意義在於代碼'class TestClass {...}'不能是C;它只能是C++。所以,你應該把你的問題標記爲C++。另一個標籤很好。在決定可以做什麼和不可以做什麼之前,您應該查看Boost預處理器代碼。 –

回答

3

下面是使用Boost.Preprocessor Sequences解決方案:

#include <boost/preprocessor/cat.hpp> 
#include <boost/preprocessor/seq/size.hpp> 
#include <boost/preprocessor/seq/enum.hpp> 
#include <boost/preprocessor/list/append.hpp> 
#include <boost/preprocessor/seq/for_each_i.hpp> 
#include <boost/preprocessor/control/expr_if.hpp> 
#include <boost/preprocessor/comparison/equal.hpp> 
#include <boost/preprocessor/facilities/expand.hpp> 

#define RET_TYPE_void 1)(1 
#define IS_NOT_VOID(type) BOOST_PP_EQUAL(BOOST_PP_SEQ_SIZE((BOOST_PP_CAT(RET_TYPE_,type))),1) 

#define JNI_PARAMS(r, data, i, elem) (elem p##i) 
#define JNI_ARGS_PASS(r, data, i, elem) (p##i) 

#define JNI_FUNCTION(functionName, functionReturn, PARAMS) \ 
JNIEXPORT functionReturn JNICALL \ 
    Java_com_noname_media_TestClass##functionName(BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_FOR_EACH_I(JNI_PARAMS,_,PARAMS))) \ 
{ \ 
    TestClass* peer = TestClass::getPeer(BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_FOR_EACH_I(JNI_ARGS_PASS,_,PARAMS))); \ 
    if(peer) \ 
     BOOST_PP_EXPR_IF(IS_NOT_VOID(functionReturn),return) peer->functionName(BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_FOR_EACH_I(JNI_ARGS_PASS,_,PARAMS))); \ 
    BOOST_PP_EXPR_IF(IS_NOT_VOID(functionReturn),return functionReturn();) \ 
} \ 
/**/ 

JNI_FUNCTION(nativeTest, void, (jobject)(jint)(jboolean)) 

擴展爲:

JNIEXPORT void JNICALL 
Java_com_noname_media_TestClassnativeTest(jobject p0, jint p1, jboolean p2) 
{ 
    TestClass* peer = TestClass::getPeer(p0, p1, p2); 
    if(peer) 
     peer->nativeTest(p0, p1, p2); 
} 

所以,你的例子會變成:

#define JNI_TABLE_BEGIN(name) class name { public: 
#define JNI_TABLE_END() }; 

JNI_TABLE_BEGIN(ClassName) 
    JNI_FUNCTION(native1, void, (JNIEnv *)(jobject)(jint)) 
    JNI_FUNCTION(native2, void, (JNIEnv *)(jobject)(jint)) 
    JNI_FUNCTION(native3, jint, (JNIEnv *)(jobject)) 
JNI_TABLE_END() 

而我t擴展爲:

class ClassName 
{ 
public: 
    JNIEXPORT void JNICALL 
    Java_com_noname_media_TestClassnative1(JNIEnv * p0, jobject p1, jint p2) 
    { 
     TestClass* peer = TestClass::getPeer(p0, p1, p2); 
     if(peer) 
      peer->native1(p0, p1, p2); 
    } 
    JNIEXPORT void JNICALL 
    Java_com_noname_media_TestClassnative2(JNIEnv * p0, jobject p1, jint p2) 
    { 
     TestClass* peer = TestClass::getPeer(p0, p1, p2); 
     if(peer) 
      peer->native2(p0, p1, p2); 
    } 
    JNIEXPORT jint JNICALL 
    Java_com_noname_media_TestClassnative3(JNIEnv * p0, jobject p1) 
    { 
     TestClass* peer = TestClass::getPeer(p0, p1); 
     if(peer) 
      return peer->native3(p0, p1); 
     return jint(); 
    } 
}; 
+0

這是非常好的結果。但看起來,我設法得到更好的沒有使用提升。通過解釋查看我自己的回覆。唯一需要解決的問題是退貨;實際上不得不返回jint();但是那麼處理void返回類型就有問題了。 – Pavel

+0

使用可變參數宏,對於C++ 98/C++ 03不是標準的,而Boost.Preprocessor序列是可移植的。另外,我爲void返回類型添加了預處理器條件。 –

0

到目前爲止,我有一個想法,可能適合作爲解決方案。透過SO回答我found an example on how to count number of arguments。 使用這個宏,我可以連接從functionArgs參數的計數,並調用一些預定義的宏,例如。 JNI_FUNCTION_5需要參數列表中的5個參數。我所需要的是能夠從列表VA_ARGS中提取一些參數。一些宏,如__VA_ARG_N(num)。

這裏有一個方法來提取一些說法從__VA_ARGS__

#define ARG_REST(arg, ...) __VA_ARGS__ 
#define ARG0(arg0, ...) arg0 
#define ARG1(...) ARG0(ARG_REST(__VA_ARGS__)) 
#define ARG2(...) ARG1(ARG_REST(__VA_ARGS__)) 
... etc. 

然後我寫了只生成列表argumet型對,或參數特殊的宏。

所以,最終,我成功地做到這一點:

JNI_FUNCTION(void, nativeSetOrientation, (JNIEnv *, jobject, jint, jboolean)); 
JNI_FUNCTION(void, nativeStartRecording, (JNIEnv *, jobject, jstring, jint)); 

那將是很好解決的唯一問題是添加特殊處理無效returnType,這樣的事情:

if(peer) 
     IS_NOT_VOID(returnType, return) peer->functionName(**WTF**); 
    IS_NOT_VOID(returnType, return returnType();) 

其中IS_NOT_VOID應該有此動作:

#define IS_NOT_VOID(type, expr) if(type == void) expr 

IS_NOT_VOID(void, return void();) -> expands to nothing 
IS_NOT_VOID(int, return int();) -> expands to return int(); 

任何想法如何正確地做到這一點? 除了迭代所有可能的類型的顯而易見的解決方案,併爲所有可以傳遞給JNI函數的類型創建30個定義。事情是這樣的:

#define _IF_NOT_VOID(type, expr) _IF_NOT_VOID##type(expr) 
#define _IF_NOT_VOIDvoid(expr) //void type... 
#define _IF_NOT_VOIDjboolean(expr) expr 
#define _IF_NOT_VOIDjbyte(expr) expr 
#define _IF_NOT_VOIDjchar(expr) expr 
+1

這是MSVC2008 http://ideone.com/u1SNFw的解決方案。總之 - 想法是使無效的案例擴展到(1,1,0),但其他情況下((東西,0))。之後,我們可以使用可擴展宏來擴展它的第二個參數......等等......我相信也可以爲其他編譯器做它。順便說一句,你在那個項目中使用哪些編譯器?一般來說,預處理器技巧是不可移植的 - 一些編譯器有一個錯誤,其他一些錯誤,這就是爲什麼我更喜歡使用Boost.Preprocessor - 它有很多ifdef,這使得這些東西變得可移植。 –

+1

以下是gcc-4.3.4的版本http://ideone.com/RvjBFH - 您可以在底部看到Ideone輸出。 –

+1

使用Boost的便攜版 - http://ideone.com/IFdllR。適用於MSVC和GCC –