背景
- 在C/C++调用Java时,需要写Java方法签名。
- 我写的有点累,还容易写错,错了还只能在运行时发现。
思路
- 我想在写签名的时候,按照Java方法的参数列表来写,方便检查。
- Java方法参数列表不同,所以我需要可变参数函数来实现组装Java方法签名。
- C/C++可变参数函数获取不到参数个数,我又懒得每次去数有几个参数,所以需要想办法计算参数个数。
最终解决方法参考自:c/c++:计算可变参数宏 VA_ARGS 的参数个数
坑
基于当前实现,有2个坑需要注意:
- 可变参数的个数最大为64。可以通过调整FL_ARG_COUNT、FL_INTERNAL_ARG_COUNT_PRIVATE来拓展。
- 最终生成的签名字符串最大长度为8192,可以通过调整MAX_PRINT_SIZE来拓展。
源码
#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <sstream>
#include <string>
#define JNI_void_SIGN "V"
#define JNI_jboolean_SIGN "Z"
#define JNI_jbyte_SIGN "B"
#define JNI_jchar_SIGN "C"
#define JNI_jshort_SIGN "S"
#define JNI_jint_SIGN "I"
#define JNI_jlong_SIGN "J"
#define JNI_jfloat_SIGN "F"
#define JNI_jdouble_SIGN "D"
#define JNI_jstring_SIGN "Ljava/lang/String;"
// 用于计算参数个数
#define FL_ARG_COUNT(...) \
FL_INTERNAL_ARG_COUNT_PRIVATE( \
0, ##__VA_ARGS__, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, \
51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, \
34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, \
17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define FL_INTERNAL_ARG_COUNT_PRIVATE( \
_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, \
_17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, \
_32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, \
_47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, \
_62, _63, _64, N, ...) \
N
// 用于计算参数个数,然后传入GenJavaMethodSign()
#define GEN_JAVA_METHOD_SIGN(return_sign, ...) \
GenJavaMethodSign(return_sign, FL_ARG_COUNT(__VA_ARGS__), ##__VA_ARGS__)
// 组装Java方法签名
std::string GenJavaMethodSign(const char* return_sign, int param_size, ...);
int main() {
std::cout << GEN_JAVA_METHOD_SIGN(JNI_void_SIGN, JNI_jboolean_SIGN,
JNI_jbyte_SIGN, JNI_jchar_SIGN)
<< std::endl;
return 0;
}
/**
* @brief 组装Java方法签名
* @param
* return_sign 返回类型签名
* param_size 参数列表个数
* args 参数列表签名
*/
std::string GenJavaMethodSign(const char* return_sign, int param_size, ...) {
// Note(guol)
// 组装最终签名的格式字符串
// e.g. "(ZLjava/lang/String;)Ljava/lang/String;"
std::stringstream ss;
ss << '(';
for (int i = 0; i < param_size; i++) {
ss << "%s";
}
ss << ')' << return_sign;
static const int MAX_PRINT_SIZE = 8192;
const char* format = ss.str().c_str();
char sign_buf[MAX_PRINT_SIZE];
memset(sign_buf, 0, MAX_PRINT_SIZE);
va_list __va;
va_start(__va, param_size);
int len = vsnprintf(sign_buf, MAX_PRINT_SIZE, format, __va);
va_end(__va);
if (len < 0) {
// Todo(guol)
assert(true);
}
// printf("[%04d] %s\n", len, sign_buf);
return std::string(sign_buf, len);
}