我想根据模板参数的变量数量的类型生成一个字符串文字。应将每种类型转换为字符串文字(1 个或多个字符),然后将文字连接起来。例如:
const char* sig1 = make_sig<void, int>();
assert(strcmp("VI", sig1) == 0); // with void=>"V", int=>"I"
const char* sig2 = make_sig<void, int, bool>();
assert(strcmp("VIZ", sig2) == 0); // with bool=>"Z"
用例:在使用 JNI 运行时注册本机方法时,自动从 C/C++ 函数声明生成 JNI(Java 本机接口)签名,而不是手写签名,因为它们会变得复杂:
typedef struct {
const char *signature;
// ...
} JNINativeMethod;
JNINativeMethod methods[] = {
{ sig1 }, // I want string literals to assign to a const char*
{ sig2 },
};
jniEnv->RegisterMethods(methods, sizeof(methods)/sizeof(methods[0]);
如何在 C++17 中实现这个
make_sig()
模板函数?
到目前为止我对编译时字符串的了解:
template<char...s>
struct string_holder {
static constexpr char value[] = { s..., '\0' };
};
template <typename...Ts>
struct concat;
template <char...s>
struct concat<string_holder<s...>> : public string_holder<s...> {};
template <char...s1, char...s2>
struct concat<string_holder<s1...>, string_holder<s2...>> : public string_holder<s1..., s2...> {};
template <typename T> struct as_jni;
template <> struct as_jni<void> { using type = string_holder<'V'>; };
template <> struct as_jni<int> { using type = string_holder<'I'>; };
template <typename...Ts>
const char* make_sig() {
return concat<typename as_jni<Ts>::type...>::value;
}
assert(strcmp(make_sig<int, void>(), "IV") == 0);
但这并不能扩展到任意数量的模板参数:
make_sig<int, void, int>(); // compile error: implicit instantiation of undefined template
我有一个将 JNI 签名生成为
std::string
的解决方案,但我更喜欢文字。
struct jstring {};
template <typename T> const char* to_jni_type();
template <> const char* to_jni_type<void>() { return "V"; }
template <> const char* to_jni_type<int>() { return "I"; }
template <> const char* to_jni_type<jstring>() { return "Ljava/lang/String;"; }
template <typename Last = void>
std::string type_sig() {
return to_jni_type<Last>();
}
template <typename First, typename Second, typename ...Rest>
std::string type_sig() {
return type_sig<First>() + type_sig<Second, Rest...>();
}
template <typename Ret, typename ...Args>
std::string make_sig(Ret(*)(Args...)) {
return '(' + type_sig<Args...>() + ')' + type_sig<Ret>();
}
void native(int, jstring);
assert("(ILjava/lang/String;)V" == make_sig(native));
对我没有帮助的相关问题:
std::integer_sequence
字符。