Автоматическая генерация функций форвардеров с помощью c-препроцессора

57
4

Мне нужно написать макрос, который автоматически генерирует функцию, которая пересылает все аргументы другой (членной) функции.

Мне нужно, чтобы упростить написание 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.

Другие getchas: тип возвращаемого типа может быть каким-то типом или void, но для случая void у меня может быть отдельный JNI_VOID_FUNCTION если он не может быть легко выполнен обычным способом. Все jni-функции в моем случае всегда будут иметь как минимум два аргумента в списке functionArgs, например, он не может быть пустым списком (). Мне не нужно использовать functionArgs в качестве единственного аргумента, который содержит несколько аргументов, и я в порядке с этим путем:

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

Независимо от того, что работает... возможно, мне нужен какой-то макрос, который позволяет мне извлечь какой-то макрос в какую-то позицию, например ARG_1 (...) и т.д., Но пока я не могу обернуть все, что в моем мозгу, как это сделать,

PS. Я вспоминаю несколько замечательных примеров использования c-препроцессора с очень хорошим объяснением здесь, в SO, но не могу найти их сейчас, если у вас есть закладки, возможно, все, что мне нужно, - это изучить их.

EDIT: В принципе, трюк состоит в том, чтобы добавлять имена с автозапусками к каждому аргументу, а затем передавать их как-есть в функцию-член. Причина, по которой мне это нужно, заключается в том, что, помимо всего прочего, у меня есть еще одна автогенерация с препроцессором. Короче говоря, этот макрос фактически будет использоваться в группе похожих макросов (что-то вроде 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()

спросил(а) 2012-10-27T00:02:00+04:00 7 лет, 11 месяцев назад
1
Решение
82

Вот решение, использующее последовательности Boost.Preprocessor:

#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()

И он расширяется до:

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

ответил(а) 2012-10-27T01:09:00+04:00 7 лет, 11 месяцев назад
41

Пока у меня есть одна идея, которая может быть подходящей в качестве решения. Просматривая ответы SO, я нашел пример того, как подсчитать количество аргументов. С помощью этого макроса можно сцепить кол - арг из 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.

Затем я написал специальный макрос, который генерирует пары пар аргументов списка или только аргументы.

Итак, в конце концов, мне удалось это сделать:

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

Единственная проблема, которую было бы неплохо исправить - добавить специальную обработку для void 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();

Любая идея, как это сделать правильно? кроме очевидного решения для итерации всех возможных типов и создания 30 определений для всех типов, которые могут быть переданы в функции JNI. Что-то вроде этого:

#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

ответил(а) 2012-10-27T01:01:00+04:00 7 лет, 11 месяцев назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема