From da24262785b2e4cfaaa75654a4c2e2a467587fea Mon Sep 17 00:00:00 2001 From: Jamieson Pryor Date: Thu, 21 Jul 2022 21:43:08 +0000 Subject: [PATCH] Rev JNI Bind release header to v 0.5. PiperOrigin-RevId: 462474343 Change-Id: I7228fa9aac5c963f080ab20ff8c3356034b862f1 --- jni_bind_release.h | 2348 +++++++++++++++++------------------ jni_bind_release_leader.inc | 2 +- 2 files changed, 1160 insertions(+), 1190 deletions(-) diff --git a/jni_bind_release.h b/jni_bind_release.h index dd88cca8..4837b2ea 100644 --- a/jni_bind_release.h +++ b/jni_bind_release.h @@ -15,7 +15,7 @@ */ /******************************************************************************* - * JNI Bind Version 0.1. + * JNI Bind Version 0.5. * Alpha Public Release. ******************************************************************************** * This header is the single header version which you can use to quickly test or @@ -371,6 +371,12 @@ using Any_t = typename Any::template type; template static constexpr bool Any_v = Any_t::value; +template +using Any_Tup = TupleUnroller_t, Ts>; + +template +static constexpr bool Any_Tup_v = TupleUnroller_t, Ts>::value; + } // namespace jni::metaprogramming @@ -415,6 +421,12 @@ class JniEnv { } // namespace jni +namespace jni { + +// Single type that be used as a value when expressing void. +struct Void {}; + +} // namespace jni #include #include @@ -716,6 +728,7 @@ struct Return { template <> struct Return { using Raw = void; + const Void raw_{}; constexpr Return() = default; }; @@ -1908,11 +1921,14 @@ class DoubleLockedValue { namespace jni { -template +template class JniMethodInvoke {}; +//////////////////////////////////////////////////////////////////////////////// +// Rank 0 type (aka void). +//////////////////////////////////////////////////////////////////////////////// template <> -struct JniMethodInvoke { +struct JniMethodInvoke { template static void Invoke(jobject object, jmethodID method_id, Ts&&... ts) { jni::JniEnv::GetEnv()->CallVoidMethod(object, method_id, @@ -1920,8 +1936,11 @@ struct JniMethodInvoke { } }; +//////////////////////////////////////////////////////////////////////////////// +// Rank 1 types, i.e. the primitive type itself (e.g. int). +//////////////////////////////////////////////////////////////////////////////// template <> -struct JniMethodInvoke { +struct JniMethodInvoke { template static jboolean Invoke(jobject object, jmethodID method_id, Ts&&... ts) { return jni::JniEnv::GetEnv()->CallBooleanMethod(object, method_id, @@ -1930,7 +1949,7 @@ struct JniMethodInvoke { }; template <> -struct JniMethodInvoke { +struct JniMethodInvoke { template static jint Invoke(jobject object, jmethodID method_id, Ts&&... ts) { return jni::JniEnv::GetEnv()->CallIntMethod(object, method_id, @@ -1939,7 +1958,7 @@ struct JniMethodInvoke { }; template <> -struct JniMethodInvoke { +struct JniMethodInvoke { template static jlong Invoke(jobject object, jmethodID method_id, Ts&&... ts) { return jni::JniEnv::GetEnv()->CallLongMethod(object, method_id, @@ -1948,7 +1967,7 @@ struct JniMethodInvoke { }; template <> -struct JniMethodInvoke { +struct JniMethodInvoke { template static jfloat Invoke(jobject object, jmethodID method_id, Ts&&... ts) { return jni::JniEnv::GetEnv()->CallFloatMethod(object, method_id, @@ -1957,7 +1976,7 @@ struct JniMethodInvoke { }; template <> -struct JniMethodInvoke { +struct JniMethodInvoke { template static jdouble Invoke(jobject object, jmethodID method_id, Ts&&... ts) { return jni::JniEnv::GetEnv()->CallDoubleMethod(object, method_id, @@ -1966,7 +1985,7 @@ struct JniMethodInvoke { }; template <> -struct JniMethodInvoke { +struct JniMethodInvoke { // This always returns a local reference which should be embedded in type // information wherever this is used. template @@ -1977,7 +1996,7 @@ struct JniMethodInvoke { }; template <> -struct JniMethodInvoke { +struct JniMethodInvoke { template static jobject Invoke(jobject object, jmethodID method_id, Ts&&... ts) { return jni::JniEnv::GetEnv()->CallObjectMethod(object, method_id, @@ -1986,10 +2005,10 @@ struct JniMethodInvoke { }; //////////////////////////////////////////////////////////////////////////////// -// Array Types. +// Rank 2 types, i.e. single dimension arrays (e.g. int[]). //////////////////////////////////////////////////////////////////////////////// -template <> -struct JniMethodInvoke { +template +struct JniMethodInvoke, kRank> { template static jbooleanArray Invoke(jobject object, jmethodID method_id, Ts&&... ts) { return static_cast(jni::JniEnv::GetEnv()->CallObjectMethod( @@ -1997,8 +2016,8 @@ struct JniMethodInvoke { } }; -template <> -struct JniMethodInvoke { +template +struct JniMethodInvoke, kRank> { template static jbyteArray Invoke(jobject object, jmethodID method_id, Ts&&... ts) { return static_cast(jni::JniEnv::GetEnv()->CallObjectMethod( @@ -2006,8 +2025,8 @@ struct JniMethodInvoke { } }; -template <> -struct JniMethodInvoke { +template +struct JniMethodInvoke, kRank> { template static jcharArray Invoke(jobject object, jmethodID method_id, Ts&&... ts) { return static_cast(jni::JniEnv::GetEnv()->CallObjectMethod( @@ -2015,8 +2034,8 @@ struct JniMethodInvoke { } }; -template <> -struct JniMethodInvoke { +template +struct JniMethodInvoke, kRank> { template static jshortArray Invoke(jobject object, jmethodID method_id, Ts&&... ts) { return static_cast(jni::JniEnv::GetEnv()->CallObjectMethod( @@ -2024,8 +2043,8 @@ struct JniMethodInvoke { } }; -template <> -struct JniMethodInvoke { +template +struct JniMethodInvoke, kRank> { template static jintArray Invoke(jobject object, jmethodID method_id, Ts&&... ts) { return static_cast(jni::JniEnv::GetEnv()->CallObjectMethod( @@ -2033,8 +2052,8 @@ struct JniMethodInvoke { } }; -template <> -struct JniMethodInvoke { +template +struct JniMethodInvoke, kRank> { template static jfloatArray Invoke(jobject object, jmethodID method_id, Ts&&... ts) { return static_cast(jni::JniEnv::GetEnv()->CallObjectMethod( @@ -2042,8 +2061,8 @@ struct JniMethodInvoke { } }; -template <> -struct JniMethodInvoke { +template +struct JniMethodInvoke, kRank> { template static jdoubleArray Invoke(jobject object, jmethodID method_id, Ts&&... ts) { return static_cast(jni::JniEnv::GetEnv()->CallObjectMethod( @@ -2051,8 +2070,8 @@ struct JniMethodInvoke { } }; -template <> -struct JniMethodInvoke { +template +struct JniMethodInvoke, kRank> { template static jlongArray Invoke(jobject object, jmethodID method_id, Ts&&... ts) { return static_cast(jni::JniEnv::GetEnv()->CallObjectMethod( @@ -2060,8 +2079,114 @@ struct JniMethodInvoke { } }; -template <> -struct JniMethodInvoke { +template +struct JniMethodInvoke, kRank> { + // Arrays of arrays (which this invoke represents) return object arrays + // (arrays themselves are objects, ergo object arrays). + template + static jobjectArray Invoke(jobject object, jmethodID method_id, Ts&&... ts) { + return static_cast(jni::JniEnv::GetEnv()->CallObjectMethod( + object, method_id, std::forward(ts)...)); + } +}; + +template +struct JniMethodInvoke, kRank> { + template + static jobjectArray Invoke(jobject object, jmethodID method_id, Ts&&... ts) { + return static_cast(jni::JniEnv::GetEnv()->CallObjectMethod( + object, method_id, std::forward(ts)...)); + } +}; + +//////////////////////////////////////////////////////////////////////////////// +// Rank 3+ types, i.e. multi-dimension arrays (e.g. int[][], int[][][]). +//////////////////////////////////////////////////////////////////////////////// +template +struct JniMethodInvoke 2), jboolean>, kRank> { + template + static jobjectArray Invoke(jobject object, jmethodID method_id, Ts&&... ts) { + return static_cast(jni::JniEnv::GetEnv()->CallObjectMethod( + object, method_id, std::forward(ts)...)); + } +}; + +template +struct JniMethodInvoke 2), jbyte>, kRank> { + template + static jobjectArray Invoke(jobject object, jmethodID method_id, Ts&&... ts) { + return static_cast(jni::JniEnv::GetEnv()->CallObjectMethod( + object, method_id, std::forward(ts)...)); + } +}; + +template +struct JniMethodInvoke 2), jchar>, kRank> { + template + static jobjectArray Invoke(jobject object, jmethodID method_id, Ts&&... ts) { + return static_cast(jni::JniEnv::GetEnv()->CallObjectMethod( + object, method_id, std::forward(ts)...)); + } +}; + +template +struct JniMethodInvoke 2), jshort>, kRank> { + template + static jobjectArray Invoke(jobject object, jmethodID method_id, Ts&&... ts) { + return static_cast(jni::JniEnv::GetEnv()->CallObjectMethod( + object, method_id, std::forward(ts)...)); + } +}; + +template +struct JniMethodInvoke 2), jint>, kRank> { + template + static jobjectArray Invoke(jobject object, jmethodID method_id, Ts&&... ts) { + return static_cast(jni::JniEnv::GetEnv()->CallObjectMethod( + object, method_id, std::forward(ts)...)); + } +}; + +template +struct JniMethodInvoke 2), jfloat>, kRank> { + template + static jobjectArray Invoke(jobject object, jmethodID method_id, Ts&&... ts) { + return static_cast(jni::JniEnv::GetEnv()->CallObjectMethod( + object, method_id, std::forward(ts)...)); + } +}; + +template +struct JniMethodInvoke 2), jdouble>, kRank> { + template + static jobjectArray Invoke(jobject object, jmethodID method_id, Ts&&... ts) { + return static_cast(jni::JniEnv::GetEnv()->CallObjectMethod( + object, method_id, std::forward(ts)...)); + } +}; + +template +struct JniMethodInvoke 2), jlong>, kRank> { + template + static jobjectArray Invoke(jobject object, jmethodID method_id, Ts&&... ts) { + return static_cast(jni::JniEnv::GetEnv()->CallObjectMethod( + object, method_id, std::forward(ts)...)); + } +}; + +template +struct JniMethodInvoke 2), jarray>, kRank> { + // Arrays of arrays (which this invoke represents) return object arrays + // (arrays themselves are objects, ergo object arrays). + template + static jobjectArray Invoke(jobject object, jmethodID method_id, Ts&&... ts) { + return static_cast(jni::JniEnv::GetEnv()->CallObjectMethod( + object, method_id, std::forward(ts)...)); + } +}; + +template +struct JniMethodInvoke 2), jobject>, kRank> { template static jobjectArray Invoke(jobject object, jmethodID method_id, Ts&&... ts) { return static_cast(jni::JniEnv::GetEnv()->CallObjectMethod( @@ -2239,10 +2364,6 @@ using PrimitiveKeys = std::tuple; -template -static constexpr bool kIsPrimitiveArrayType = - metaprogramming::TupContains_v; - // Simple type for proxying types used in the API (e.g. jint) to their // corresponding array type (e.g. jintarray). Only use the former type when // using JNI Bind (e.g. LocalArray, not LocalArray). @@ -2420,41 +2541,6 @@ using ArrayFromRank_t = typename ArrayFromRank::type; } // namespace jni -#include -#include - -namespace jni::metaprogramming { - -struct StringConcatenate { - template - struct Helper { - static constexpr auto BuildConcatenation() noexcept { - constexpr std::size_t len = (Vs.size() + ... + 0); - std::array arr{}; - auto append_single_string = - [i = 0, &arr](auto const& string_to_concatenate) mutable { - for (auto c : string_to_concatenate) arr[i++] = c; - }; - (append_single_string(Vs), ...); - arr[len] = 0; - - return arr; - } - - static constexpr auto arr = BuildConcatenation(); - static constexpr std::string_view value{arr.data(), arr.size() - 1}; - }; - - template - static constexpr std::string_view value = Helper::value; -}; - -template -static constexpr auto StringConcatenate_v = StringConcatenate::value; - -} // namespace jni::metaprogramming - - #include namespace jni::metaprogramming { @@ -2562,97 +2648,6 @@ struct NBitSequence...> { } // namespace jni::metaprogramming -#include - -namespace jni { - -// Translates a single JNI term (e.g. jint -> "I") as if it were being used as a -// parameter to a method. -// -// Note, the context a parameter is used on occasion will alter a signature, -// e.g. void in a return is explicit, whereas when used as a parameter, it is -// represented as the omission of any value. -// -// Additionally, Android will obnoxiously fail to compile the standard looking: -// static constexpr char kStr[] = "SomeString"; -// -// Because it is against style to import have a using declaration header wide, -// but these are also template definitions, they must remain in this header, and -// so there are goofy looking "using literal" declarations throughout. -// -// https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/types.html -// -// TODO: Rename to JavaPrimitiveTypeToString -template -constexpr std::string_view JavaTypeToString(); - -template <> -constexpr std::string_view JavaTypeToString() { - // Note: This only applies when used as a return, not as a parameter. This - // could be enforced through type system, but maybe feels excessive to do so. - // For now, enforcing this is unnecesssary, as this function is only called - // for each Param, which, in the case of no params, is 0 times. - using namespace std::literals; - return "V"sv; -} - -template <> -constexpr std::string_view JavaTypeToString() { - using namespace std::literals; - return "Z"sv; -} - -template <> -constexpr std::string_view JavaTypeToString() { - using namespace std::literals; - return "B"sv; -} - -template <> -constexpr std::string_view JavaTypeToString() { - using namespace std::literals; - return "C"sv; -} - -template <> -constexpr std::string_view JavaTypeToString() { - using namespace std::literals; - return "S"sv; -} - -template <> -constexpr std::string_view JavaTypeToString() { - using namespace std::literals; - return "I"sv; -} - -template <> -constexpr std::string_view JavaTypeToString() { - using namespace std::literals; - return "J"sv; -} - -template <> -constexpr std::string_view JavaTypeToString() { - using namespace std::literals; - return "F"sv; -} - -template <> -constexpr std::string_view JavaTypeToString() { - using namespace std::literals; - return "D"sv; -} - -template <> -constexpr std::string_view JavaTypeToString() { - using namespace std::literals; - return "Ljava/lang/String;"sv; -} - -} // namespace jni - - #include #include #include @@ -2711,26 +2706,6 @@ class UtfStringView { } // namespace jni -#include - -namespace jni { - -//////////////////////////////////////////////////////////////////////////////// -// Constants for signature generation. -//////////////////////////////////////////////////////////////////////////////// - -static constexpr std::string_view kLeftBracket{"["}; -static constexpr std::string_view kRightBracket{"]"}; -static constexpr std::string_view kLeftParenthesis{"("}; -static constexpr std::string_view kRightParenthesis{")"}; -static constexpr std::string_view kInit{""}; -static constexpr std::string_view kComma{","}; -static constexpr std::string_view kSemiColon{";"}; -static constexpr std::string_view kLetterL{"L"}; - -} // namespace jni - - #include #include // NOLINT #include @@ -3025,66 +3000,38 @@ using UniqueSet_Tup = TupleUnroller_t; } // namespace jni::metaprogramming - -#include -#include +#include +#include namespace jni::metaprogramming { -template -using T_ = T; - -template -auto TupleFromSize(std::index_sequence) { - return std::tuple...>{}; -} - -// Takes a type and returns a std::tuple of DefaultValues. -template -auto TupleFromSize() { - return TupleFromSize(std::make_index_sequence{}); -} - -template -using TupleFromSize_t = decltype(TupleFromSize()); - -} // namespace jni::metaprogramming - - -#include -#include -#include - -namespace jni::metaprogramming { - -// Returns a null pointer of the type of the two input tuples interleaved. -template -auto Interleave(std::integer_sequence) - -> decltype(std::tuple_cat( - std::make_tuple(std::get(std::declval()), - std::get(std::declval()))...))* { - // This interleave is for *types only*, all values within the tuples are - // completely incidental. In the event there is no default constructor, it - // won't be possible to return a value, so, instead, return a pointer (which - // won't be used) and infer the type by stripping the pointer. - return nullptr; -} +struct StringConcatenate { + template + struct Helper { + static constexpr auto BuildConcatenation() noexcept { + constexpr std::size_t len = (Vs.size() + ... + 0); + std::array arr{}; + auto append_single_string = + [i = 0, &arr](auto const& string_to_concatenate) mutable { + for (auto c : string_to_concatenate) arr[i++] = c; + }; + (append_single_string(Vs), ...); + arr[len] = 0; -template -auto Interleave() { - return Interleave( - std::make_index_sequence::value>()); -} + return arr; + } -template -struct Interleaved; + static constexpr auto arr = BuildConcatenation(); + static constexpr std::string_view value{arr.data(), arr.size() - 1}; + }; -template -struct Interleaved, std::tuple> { - using type = std::remove_pointer_t, std::tuple>())>; + template + static constexpr std::string_view value = Helper::value; }; +template +static constexpr auto StringConcatenate_v = StringConcatenate::value; + } // namespace jni::metaprogramming @@ -3211,99 +3158,93 @@ using CartesianProduct_t = typename CartesianProduct::template type; namespace jni { -// Helper to generate full signature information for a "selected" value, and -// possibly some container information. Here, |Selector| is |MethodSelection|, -// |FieldSelection|, etc. +// Translates a single JNI term (e.g. jint -> "I") as if it were being used as a +// parameter to a method. // -// Unfortunately, the type system does not permit passing subobjects, however, -// types can be used that represent a specific selection upon an object. This -// consolidates all signature info. +// Note, the context a parameter is used on occasion will alter a signature, +// e.g. void in a return is explicit, whereas when used as a parameter, it is +// represented as the omission of any value. // -// |Selector| must express a type alias for RawVal (e.g. jint, Class{...}, etc.) -// and a type alias |RawValT| which is std::decay_t; -template -struct SelectorStaticInfo { - template - struct IthRawTypeMember { - template - static constexpr const auto& Val(const T& val) { - return IthRawTypeMember::Val(val.raw_type_); - } - }; - - template <> - struct IthRawTypeMember<0> { - template - static constexpr const auto& Val(const T& val) { - return val; - } - }; - - // Strangely, the compiler refuses to peer through Val and loses the - // constexpr-ness (i.e std::decay_t; is not a constant - // expression). - static constexpr inline const auto& Val() { - if constexpr (Selector::kRank == 0) { - return Selector::Val(); - } else { - return IthRawTypeMember::Val(Selector::Val()); - } - } - - using RawValT = typename Selector::RawValT; +// Additionally, Android will obnoxiously fail to compile the standard looking: +// static constexpr char kStr[] = "SomeString"; +// +// Because it is against style to import have a using declaration header wide, +// but these are also template definitions, they must remain in this header, and +// so there are goofy looking "using literal" declarations throughout. +// +// https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/types.html +// +// TODO: Rename to JavaPrimitiveTypeToString +template +constexpr std::string_view JavaTypeToString(); - static constexpr inline bool kIsObject = std::is_base_of_v; - static constexpr std::size_t kRank = Selector::kRank; +template <> +constexpr std::string_view JavaTypeToString() { + // Note: This only applies when used as a return, not as a parameter. This + // could be enforced through type system, but maybe feels excessive to do so. + // For now, enforcing this is unnecesssary, as this function is only called + // for each Param, which, in the case of no params, is 0 times. + using namespace std::literals; + return "V"sv; +} - static constexpr std::string_view TypeNameOrNothingIfNotAnObject() { - if constexpr (kIsObject) { - return Val().name_; - } else { - return ""; - } - } +template <> +constexpr std::string_view JavaTypeToString() { + using namespace std::literals; + return "Z"sv; +} - static constexpr std::string_view kTypeNameOrNothingIfNotAnObject = - TypeNameOrNothingIfNotAnObject(); +template <> +constexpr std::string_view JavaTypeToString() { + using namespace std::literals; + return "B"sv; +} - template - struct Repeat { - static constexpr std::string_view val = - metaprogramming::StringConcatenate_v::val>; - }; +template <> +constexpr std::string_view JavaTypeToString() { + using namespace std::literals; + return "C"sv; +} - template <> - struct Repeat<0> { - static constexpr std::string_view val = ""; - }; +template <> +constexpr std::string_view JavaTypeToString() { + using namespace std::literals; + return "S"sv; +} - static constexpr std::string_view kEmptyStr = ""; - static constexpr std::string_view kModifierStr = - (kRank == 0) ? "" : Repeat::val; +template <> +constexpr std::string_view JavaTypeToString() { + using namespace std::literals; + return "I"sv; +} - static constexpr std::string_view UndecoratedTypeName() { - if constexpr (kIsObject) { - return metaprogramming::StringConcatenate_v< - kLetterL, kTypeNameOrNothingIfNotAnObject, kSemiColon>; - } else { - return JavaTypeToString(); - } - } +template <> +constexpr std::string_view JavaTypeToString() { + using namespace std::literals; + return "J"sv; +} - static constexpr std::string_view kUndecoratedTypeName = - UndecoratedTypeName(); +template <> +constexpr std::string_view JavaTypeToString() { + using namespace std::literals; + return "F"sv; +} - static constexpr std::string_view TypeName() { - return metaprogramming::StringConcatenate_v; - } +template <> +constexpr std::string_view JavaTypeToString() { + using namespace std::literals; + return "D"sv; +} - static constexpr std::string_view kTypeName = TypeName(); -}; +template <> +constexpr std::string_view JavaTypeToString() { + using namespace std::literals; + return "Ljava/lang/String;"sv; +} } // namespace jni +#include namespace jni { @@ -3317,8 +3258,8 @@ using Proxy_t = typename ProxyHelper::Proxy_t; template using Index_t = typename ProxyHelper::Index; -template -using CDecl_t = typename ProxyHelper::template CDecl; +template +using CDecl_t = typename ProxyHelper::CDecl; template using Return_t = @@ -3330,6 +3271,40 @@ using Arg_t = typename ProxyHelper::template AsArg_t; template using AsDecl_t = typename ProxyHelper::AsDecl_t; +// Instead of directly searching for the type, convertible types are sought. +// E.g. A string like "Foo" the type will be const char[4] not const char*. +template +struct IsConvertibleKey { + template + static constexpr bool value = + std::is_same_v> || + std::is_base_of_v, std::decay_t> || + std::is_base_of_v, std::decay_t>; +}; + +template +static constexpr bool IsConvertibleKey_v = + IsConvertibleKey::template value; + +} // namespace jni + +#include + +namespace jni { + +//////////////////////////////////////////////////////////////////////////////// +// Constants for signature generation. +//////////////////////////////////////////////////////////////////////////////// + +static constexpr std::string_view kLeftBracket{"["}; +static constexpr std::string_view kRightBracket{"]"}; +static constexpr std::string_view kLeftParenthesis{"("}; +static constexpr std::string_view kRightParenthesis{")"}; +static constexpr std::string_view kInit{""}; +static constexpr std::string_view kComma{","}; +static constexpr std::string_view kSemiColon{";"}; +static constexpr std::string_view kLetterL{"L"}; + } // namespace jni namespace jni { @@ -3394,6 +3369,12 @@ class LocalArray : public ArrayRef; + // TODO(b/143908983): Local arrays only support logic for rank 1. + // This signature exists to satisfy proxy logic returning higher rank arrays. + template && + (kRank > 1)>> + LocalArray(jobjectArray array) : Base(nullptr) {} + LocalArray(RegularToArrayTypeMap_t array) : Base(array) {} LocalArray(LocalArray&& rhs) : Base(rhs.Release()) {} @@ -3507,767 +3488,963 @@ class GlobalString : public StringRefBase { } // namespace jni - -#include -#include +#include #include namespace jni::metaprogramming { -template -class QueryableMapBase {}; - -// This is an interface that can be inherited from to expose an -// operator["name"]. It provides compile time string index lookup with no macros -// although it is dependent on a clang extension. -// -// To use this API, inherit from this class using template types as follows: -// -// |CrtpBase|: The name of the class inheriting from the map. This class -// will inherit an operator[]. It must implement this exact signature: -// -// template -// auto QueryableMapCall(const char* key); -// -// |tup_container_v| is a static instance of an object whose |nameable_member| -// contains a public field called name_. It might seem strange not to -// directly pass a const auto&, however, this prevents accessing subobjects. -// -// The motivation for using inheritance as opposed to a simple member is that -// the the const char cannot be propagated without losing its constexpr-ness, -// and so the clang extension can no longer restrict function candidates. -template -class QueryableMap - : public QueryableMapBase> {}; +template +using T_ = T; -template ::*nameable_member> -using QueryableMap_t = - QueryableMap>, - std::decay_t, nameable_member>; +template +auto TupleFromSize(std::index_sequence) { + return std::tuple...>{}; +} -template -class QueryableMapEntry; +// Takes a type and returns a std::tuple of DefaultValues. +template +auto TupleFromSize() { + return TupleFromSize(std::make_index_sequence{}); +} -template -class QueryableMapBase> - : public QueryableMapEntry... { - public: - using QueryableMapEntry::operator[]...; +template +using TupleFromSize_t = decltype(TupleFromSize()); - using QueryableMapEntry::Contains...; +} // namespace jni::metaprogramming - // Will select subclass specialisations if present. - constexpr bool Contains(const char* key) { return false; } +#include +#include +#include + +namespace jni::metaprogramming { + +// Returns a null pointer of the type of the two input tuples interleaved. +template +auto Interleave(std::integer_sequence) + -> decltype(std::tuple_cat( + std::make_tuple(std::get(std::declval()), + std::get(std::declval()))...))* { + // This interleave is for *types only*, all values within the tuples are + // completely incidental. In the event there is no default constructor, it + // won't be possible to return a value, so, instead, return a pointer (which + // won't be used) and infer the type by stripping the pointer. + return nullptr; +} + +template +auto Interleave() { + return Interleave( + std::make_index_sequence::value>()); +} + +template +struct Interleaved; + +template +struct Interleaved, std::tuple> { + using type = std::remove_pointer_t< + decltype(Interleave, std::tuple>())>; }; -template -class QueryableMapEntry { - public: -#if __clang__ - // This function blurs the distinction between type and value space. The - // clang extension allows the key to be wrapped in a constexpr way. This - // allows for string to string comparison based on the static value the class - // is templated by. - // - // The reason the TypeMap interface requires inheritance as opposed to simply - // holding an instance of this map (like you would with a regular hash map) is - // the constexpr-ness of the string can't be propagated. This essentially - // means you get one shot at defining the function. - constexpr auto operator[](const char* key) __attribute__(( - enable_if(std::string_view(key) == - std::get(tup_container_v.*nameable_member).name_, - ""))) { - static_assert(std::is_base_of_v, - "You must derive from the invocable map."); +} // namespace jni::metaprogramming - return (*static_cast(this)).template QueryableMapCall(key); +#include + +namespace jni { + +// Helper to generate full signature information for a "selected" value, and +// possibly some container information. Here, |Selector| is |MethodSelection|, +// |FieldSelection|, etc. +// +// Unfortunately, the type system does not permit passing subobjects, however, +// types can be used that represent a specific selection upon an object. This +// consolidates all signature info. +// +// |Selector| must express a type alias for RawVal (e.g. jint, Class{...}, etc.) +// and a type alias |RawValT| which is std::decay_t; +template +struct SelectorStaticInfo { + template + struct IthRawTypeMember { + template + static constexpr const auto& Val(const T& val) { + return IthRawTypeMember::Val(val.raw_type_); + } + }; + + template <> + struct IthRawTypeMember<0> { + template + static constexpr const auto& Val(const T& val) { + return val; + } + }; + + // Strangely, the compiler refuses to peer through Val and loses the + // constexpr-ness (i.e std::decay_t; is not a constant + // expression). + static constexpr inline const auto& Val() { + if constexpr (Selector::kRank == 0) { + return Selector::Val(); + } else { + return IthRawTypeMember::Val(Selector::Val()); + } } - constexpr bool Contains(const char* key) __attribute__(( - enable_if(std::string_view(key) == - std::get(tup_container_v.*nameable_member).name_, - ""))) { - return true; + using RawValT = typename Selector::RawValT; + + static constexpr inline bool kIsObject = std::is_base_of_v; + static constexpr inline bool kIsVoid = std::is_same_v; + static constexpr std::size_t kRank = Selector::kRank; + + static constexpr std::string_view TypeNameOrNothingIfNotAnObject() { + if constexpr (kIsObject) { + return Val().name_; + } else { + return ""; + } } -#else - static_assert(false, - "This container requires clang for compile time strings."); -#endif -}; -} // namespace jni::metaprogramming + static constexpr std::string_view kTypeNameOrNothingIfNotAnObject = + TypeNameOrNothingIfNotAnObject(); + template + struct Repeat { + static constexpr std::string_view val = + metaprogramming::StringConcatenate_v::val>; + }; -#include -#include + template <> + struct Repeat<0> { + static constexpr std::string_view val = ""; + }; -namespace jni::metaprogramming { + static constexpr std::string_view kEmptyStr = ""; + static constexpr std::string_view kModifierStr = + (kRank == 0) ? "" : Repeat::val; -template -struct OptionalTup {}; + static constexpr std::string_view UndecoratedTypeName() { + if constexpr (kIsObject) { + return metaprogramming::StringConcatenate_v< + kLetterL, kTypeNameOrNothingIfNotAnObject, kSemiColon>; + } else if constexpr (kIsVoid) { + return JavaTypeToString(); + } else { + return JavaTypeToString(); + } + } -// Takes a _tuple_ of types and returns a tuple with the same types except -// wrapped in std::optional. -template -struct OptionalTup> { - using type = std::tuple...>; -}; + static constexpr std::string_view kUndecoratedTypeName = + UndecoratedTypeName(); -template -using OptionalTup_t = typename OptionalTup::type; + static constexpr std::string_view TypeName() { + return metaprogramming::StringConcatenate_v; + } -} // namespace jni::metaprogramming + static constexpr std::string_view kTypeName = TypeName(); +}; +} // namespace jni +#include #include #include -#include +#include -namespace jni::metaprogramming { +namespace jni { -template -class InvocableMap; +template +class LocalObject; -// This is an interface that can be inherited from to expose an operator(...). -// It provides compile time string index lookup with no macros although it is -// dependent on a clang extension. -// -// To use this API, inherit from this class using template types as follows: -// -// |CrtpBase|: The name of the class inheriting from the map. This class -// will inherit an operator(). It must implement this exact signature: -// -// template -// auto InvocableMapCall(const char* key, Args&&... args); +template +class LocalArray; + +template +struct ArrayHelper; + +template +struct ProxyHelper; + +// Default Proxy, all types and values are pure passthrough. +template +struct ProxyBase { + using Key = Key_; + + using CDecl = Key_; + + template + using AsReturn = Key_; + + using AsArg = std::tuple; + using AsDecl = std::tuple; + + template + static auto ProxyAsArg(T&& t) { + return std::forward(t); + } + + template + static constexpr bool kViable = IsConvertibleKey_v; + + template + static constexpr std::size_t kRank = 1; +}; + +// Proxy is a metafunction that gives useful conversions from +// types and forwards to a corresponding type that's viable as input. // -// If i is the index where |tup_container_v.*nameable_member|.name_ == key, -// then InvocablemapCall will forward the args from operator() with the -// same args. Static memory can be used in this function call and it will -// be unique because of the I non-type template parameter. +// Note, given the context, different types present differently. E.g. a |jint| +// is always a jint, but a |jobject| is declared as a |jni::Class|, passed as a +// |jni::RefBase&| and then converted to a |jobject| to cross the C API. // -// |tup_container_v| is a static instance of an object whose |nameable_member| -// contains a public field called name_. It might seem strange not to -// directly pass a const auto&, however, this prevents accessing subobjects. +// |Proxy_t| will select the correct proxy for any of the above types. To be +// specific, |Proxy_t| of any type in |Arg| or |AsDecl| will return +// the parent Proxy. // -// The motivation for using inheritance as opposed to a simple member is that -// the the const char cannot be propagated without losing its constexpr-ness, -// and so the clang extension can no longer restrict function candidates. -template ::*nameable_member> -using InvocableMap_t = - InvocableMap, nameable_member>; +// Each proxy exports aliases for a given |CDecl|. +// |Index|: A uniquely identifying Key for proxy lookup. This is usually the +// CDecl (e.g. jint => jint), but rich types may differ (Object =>jobject). +// |CDecl|: This is both the unique ID for a given proxy, as well as the +// distinct type (of which there is only one) that is usable when invoking a +// JNI call through the C API (e.g. |jint|, |jobject|). +// |AsArg|: All valid passable types. +// |AsDecl|: The type to be used in a function declaration, either as +// return or as a declared argument. If is templated by |class_v| and +// |class_loader_v| which can allow for additional decoration. +template +struct Proxy : public ProxyBase {}; -template -class InvocableMapEntry; +template +struct Proxy>> + : public ProxyBase {}; -template -class InvocableMapBase {}; +template +struct Proxy>> + : public ProxyBase { + using AsArg = std::tuple; + using AsDecl = std::tuple; -template -class InvocableMapBase> - : public InvocableMapEntry... { - public: - using InvocableMapEntry::operator()...; + template + static constexpr bool kViable = IsConvertibleKey::template value || + IsConvertibleKey::template value; +}; + +template +struct Proxy>> + : public ProxyBase { + using AsArg = std::tuple; + using AsDecl = std::tuple; + + template + static constexpr bool kViable = + IsConvertibleKey::template value || + IsConvertibleKey::template value; +}; + +template +struct Proxy>> + : public ProxyBase { + using AsArg = std::tuple; + using AsDecl = std::tuple; + + template + static constexpr bool kViable = IsConvertibleKey::template value || + IsConvertibleKey::template value; + + static jlong ProxyAsArg(jlong val) { return val; } + + // jlong is a smaller type on ARM than x86. + // When jlong is not equivalent, we upcast to the wider type. + template && + !std::is_same_v>> + static jlong ProxyAsArg(T val) { + return jlong{val}; + } }; -template -class InvocableMapEntry { - public: -#if __clang__ - // This function blurs the distinction between type and value space. The - // clang extension allows the key to be wrapped in a constexpr way. This - // allows for string to string comparison based on the static value the class - // is templated by. - // - // The reason the TypeMap interface requires inheritance as opposed to simply - // holding an instance of this map (like you would with a regular hash map) is - // the constexpr-ness of the string can't be propagated. This essentially - // means you get one shot at defining the function. - template - constexpr auto operator()(const char* key, Args&&... args) __attribute__(( - enable_if(std::string_view(key) == - std::get(tup_container_v.*nameable_member).name_, - ""))) { - static_assert(std::is_base_of_v, - "You must derive from the invocable map."); +template +struct Proxy>> + : public ProxyBase { + using AsArg = + std::tuple; - return (*static_cast(this)) - .template InvocableMapCall(key, - std::forward(args)...); + template + using AsReturn = LocalString; + + template + struct Helper { + static constexpr bool val = true; + }; + + template + static constexpr bool kViable = + IsConvertibleKey::template value || + IsConvertibleKey::template value || + IsConvertibleKey::template value || + IsConvertibleKey::template value || + IsConvertibleKey::template value; + + // These leak local instances of strings. Usually, RAII mechanisms would + // correctly release local instances, but here we are stripping that so it can + // be used in a method. This could be obviated by wrapping the calling scope + // in a local stack frame. + static jstring ProxyAsArg(jstring s) { return s; } + static jstring ProxyAsArg(const char* s) { return LocalString{s}.Release(); } + static jstring ProxyAsArg(std::string s) { return LocalString{s}.Release(); } + static jstring ProxyAsArg(std::string_view s) { + return LocalString{s}.Release(); } -#else - static_assert(false, - "This container requires clang for compile time strings."); -#endif }; -//============================================================================== -template -class InvocableMap - : public InvocableMapBase< - CrtpBase, tup_container_v, TupContainerT, nameable_member, - std::make_index_sequence>>> {}; +//////////////////////////////////////////////////////////////////////////////// +// Object Proxy Definitions. +//////////////////////////////////////////////////////////////////////////////// -} // namespace jni::metaprogramming +template +struct Proxy>> + : public ProxyBase { + using AsDecl = std::tuple; + using AsArg = std::tuple>; + template + struct ContextualViabilityHelper { + // TODO(b/143908983): This is overly permissive, see method_selection_test. + static constexpr bool kViable = std::is_same_v; + }; -namespace jni { + template class Container, + const auto& class_v, const auto& class_loader_v, const auto& jvm_v> + struct ContextualViabilityHelper> { + // TODO(b/174272629): Exclude objects loaded by invalid loaders. + static constexpr bool kViable = + std::string_view(class_v.name_) == + std::string_view(InputParamSelectionT::Val().name_); + }; -template -struct FieldHelper { - static Raw GetValue(const jobject object_ref, const jfieldID field_ref_); + template + static constexpr bool kViable = + ContextualViabilityHelper::kViable; - static void SetValue(const jobject object_ref, const jfieldID field_ref_, - Raw&& value); + template + struct Helper { + // It's illegal to initialise this type with a sub-object of another, + // however, we can construct types with enough validation to guarantee + // correctness. + static constexpr Class kClass{OverloadT::GetReturn().raw_.name_}; + + // TODO(b/174272629): Class loaders should also be enforced. + using type = LocalObject; + }; + + template + using AsReturn = typename Helper::type; + + static jobject ProxyAsArg(jobject obj) { return obj; }; + + // Applies for both local and global. + template + static jobject ProxyAsArg(T& t) { + return jobject{t}; + }; + + // Applies for both local and global. + template + static jobject ProxyAsArg(T&& t) { + return t.Release(); + }; }; -template <> -inline jboolean FieldHelper::GetValue(const jobject object_ref, - const jfieldID field_ref_) { - return jni::JniEnv::GetEnv()->GetBooleanField(object_ref, field_ref_); -} +//////////////////////////////////////////////////////////////////////////////// +// Array Proxy Definitions. +//////////////////////////////////////////////////////////////////////////////// -template <> -inline void FieldHelper::SetValue(const jobject object_ref, - const jfieldID field_ref_, - jboolean&& value) { - jni::JniEnv::GetEnv()->SetBooleanField(object_ref, field_ref_, value); -} +template +struct ArrayRefPrimitiveTag; -template <> -inline jbyte FieldHelper::GetValue(const jobject object_ref, - const jfieldID field_ref_) { - return jni::JniEnv::GetEnv()->GetByteField(object_ref, field_ref_); -} +template +struct Proxy>> + : public ProxyBase { + // Non-array primitive type (e.g. jintArray => jint). + using CDecl = ArrayToRegularTypeMap_t; -template <> -inline void FieldHelper::SetValue(const jobject object_ref, - const jfieldID field_ref_, - jbyte&& value) { - jni::JniEnv::GetEnv()->SetByteField(object_ref, field_ref_, value); -} + template + struct Helper { + static constexpr bool val = std::is_same_v; + }; -template <> -inline jchar FieldHelper::GetValue(const jobject object_ref, - const jfieldID field_ref_) { - return jni::JniEnv::GetEnv()->GetCharField(object_ref, field_ref_); -} + // LocalArray or GlobalArray. + template + struct Helper< + LocalArray> { + static constexpr bool val = + std::is_same_v, JArrayType>; + }; -template <> -inline void FieldHelper::SetValue(const jobject object_ref, - const jfieldID field_ref_, - jchar&& value) { - jni::JniEnv::GetEnv()->SetCharField(object_ref, field_ref_, value); -} + template + static constexpr bool kViable = + std::is_same_v || Helper::val; -template <> -inline jshort FieldHelper::GetValue(const jobject object_ref, - const jfieldID field_ref_) { - return jni::JniEnv::GetEnv()->GetShortField(object_ref, field_ref_); -} + using AsDecl = std::tuple>; + using AsArg = std::tuple, + ArrayTag, ArrayRefPrimitiveTag>; -template <> -inline void FieldHelper::SetValue(const jobject object_ref, - const jfieldID field_ref_, - jshort&& value) { - jni::JniEnv::GetEnv()->SetShortField(object_ref, field_ref_, value); -} + template + using AsReturn = typename ArrayHelper::AsReturn; -template <> -inline jint FieldHelper::GetValue(const jobject object_ref, - const jfieldID field_ref_) { - return jni::JniEnv::GetEnv()->GetIntField(object_ref, field_ref_); -} + template + static constexpr std::size_t kRank = ArrayHelper::kRank; -template <> -inline void FieldHelper::SetValue(const jobject object_ref, - const jfieldID field_ref_, - jint&& value) { - jni::JniEnv::GetEnv()->SetIntField(object_ref, field_ref_, value); -} + static JArrayType ProxyAsArg(JArrayType arr) { return arr; }; -template <> -inline jlong FieldHelper::GetValue(const jobject object_ref, - const jfieldID field_ref_) { - return jni::JniEnv::GetEnv()->GetLongField(object_ref, field_ref_); -} + template + static JArrayType ProxyAsArg(T& t) { + return JArrayType{t}; + }; -template <> -inline void FieldHelper::SetValue(const jobject object_ref, - const jfieldID field_ref_, - jlong&& value) { - jni::JniEnv::GetEnv()->SetLongField(object_ref, field_ref_, value); -} + template , T>>> + static JArrayType ProxyAsArg(T&& t) { + return t.Release(); + }; +}; -template <> -inline jfloat FieldHelper::GetValue(const jobject object_ref, - const jfieldID field_ref_) { - return jni::JniEnv::GetEnv()->GetFloatField(object_ref, field_ref_); -} +// This must be defined outside of Proxy so implicit definition doesn't occur. +template +struct ArrayHelper { + template + struct Helper { + static constexpr auto val = FullArrayStripV(t.raw_type_); -template <> -inline void FieldHelper::SetValue(const jobject object_ref, - const jfieldID field_ref_, - jfloat&& value) { - jni::JniEnv::GetEnv()->SetFloatField(object_ref, field_ref_, value); -} + using StrippedCDecl = CDecl_t>; + using ConvertedCDecl = RegularToArrayTypeMap_t; + }; -template <> -inline jdouble FieldHelper::GetValue(const jobject object_ref, - const jfieldID field_ref_) { - return jni::JniEnv::GetEnv()->GetDoubleField(object_ref, field_ref_); -} + static constexpr auto kVal{Overload::GetReturn().raw_}; -template <> -inline void FieldHelper::SetValue(const jobject object_ref, - const jfieldID field_ref_, - jdouble&& value) { - jni::JniEnv::GetEnv()->SetDoubleField(object_ref, field_ref_, value); -} + static constexpr auto LocalArrayBuildFromArray() { + using RawT = std::decay_t>; -template <> -inline jobject FieldHelper::GetValue(const jobject object_ref, - const jfieldID field_ref_) { - return jni::JniEnv::GetEnv()->GetObjectField(object_ref, field_ref_); -} + constexpr std::size_t kRank = Rankifier::Rank(kVal); -template <> -inline void FieldHelper::SetValue(const jobject object_ref, - const jfieldID field_ref_, - jobject&& new_value) { - jni::JniEnv::GetEnv()->SetObjectField(object_ref, field_ref_, new_value); -} + // TODO(b/143908983): Support multi-dimensional arrays. + if constexpr (!std::is_same_v, jobject>) { + return LocalArray{nullptr}; + } else { + return LocalArray::val>{ + jobjectArray{nullptr}}; + } + } -} // namespace jni + using StrippedCDecl = typename Helper::StrippedCDecl; + using ConvertedCDecl = typename Helper::ConvertedCDecl; + using AsReturn = decltype(LocalArrayBuildFromArray()); +}; +} // namespace jni #include #include -#include #include +#include namespace jni { -template -class LocalObject; - -template -class LocalArray; +template +struct Proxy; -template -struct ArrayHelper; +// Everything you are permitted to declare at method prototypes. +// Note, if the size can reasonably differ, the jtype is enforced by virtue of +// being a different type (i.e. you can't accidentally widen). +using AllKeys = + std::tuple; template -struct ProxyHelper; +struct ProxyHelper { + using T = std::decay_t; -// Default Proxy, all types and values are pure passthrough. -template -struct ProxyBase { - using Key = Key_; + // Metafunction that builds a list of a passable type to all it's possible + // passable types, which may not be the same. E.g. jint => jint, but + // jstring => jstring, const char*, std::string_view, std::string, etc. + struct IndexToKey { + // Proxies can be indexed by their |AsArg|s or their |AsDecl|. + template + using type = metaprogramming::CartesianProduct_t< + std::tuple, + metaprogramming::UniqueSet_Tup::AsArg, typename Proxy::AsDecl>>>; + }; - template - using CDecl = Key_; + // Build a list of two element tuples (in preparation to build a map). e.g. + // { {jint, int}, {jstring, const char*}, {jstring, std::string}, etc. }. + // Note that types may map to 1 or more types, such as jstring above. + using IndexToKeyAsTuples = metaprogramming::Reduce_t< + metaprogramming::Combine, + metaprogramming::InvokePerTupArg_t>; - template - using AsReturn = Key_; + // Collapse this list into a set of keys and values consumable by + // TypeToTypeMap. + using IndexToKeyMap = metaprogramming::TypeToTypeMapFromKeyValuesTup_t< + metaprogramming::Flatten_t>; - using AsArg = std::tuple; - using AsDecl = std::tuple; + // When flipped, a type passed can be reverse indexed to select the same + // Proxy partial specialisation. + using KeyToIndex = metaprogramming::TypeToTypeMap_Invert; - template - static auto ProxyAsArg(T&& t) { - return std::forward(t); - } -}; + using Index = + metaprogramming::TypeToTypeMapQueryWithComparator_t>; -// Proxy is a metafunction that gives useful conversions from -// types and forwards to a corresponding type that's viable as input. -// -// Note, given the context, different types present differently. E.g. a |jint| -// is always a jint, but a |jobject| is declared as a |jni::Class|, passed as a -// |jni::RefBase&| and then converted to a |jobject| to cross the C API. -// -// |Proxy_t| will select the correct proxy for any of the above types. To be -// specific, |Proxy_t| of any type in |Arg| or |AsDecl| will return -// the parent Proxy. -// -// Each proxy exports aliases for a given |CDecl|. -// |Index|: A uniquely identifying Key for proxy lookup. This is usually the -// CDecl (e.g. jint => jint), but rich types may differ (Object =>jobject). -// |CDecl|: This is both the unique ID for a given proxy, as well as the -// distinct type (of which there is only one) that is usable when invoking a -// JNI call through the C API (e.g. |jint|, |jobject|). -// |AsArg|: All valid passable types. -// |AsDecl|: The type to be used in a function declaration, either as -// return or as a declared argument. If is templated by |class_v| and -// |class_loader_v| which can allow for additional decoration. -template -struct Proxy : public ProxyBase {}; + using Proxy_t = Proxy; -template -struct Proxy>> - : public ProxyBase {}; + using CDecl = typename Proxy_t::CDecl; -template -struct Proxy>> - : public ProxyBase { - using AsArg = std::tuple; + template + using AsReturn_t = typename Proxy_t::template AsReturn; - using AsDecl = std::tuple; + template + using AsArg_t = typename Proxy_t::AsArg; + + using AsDecl_t = typename Proxy_t::AsDecl; }; -template -struct Proxy>> - : public ProxyBase { - using AsArg = std::tuple; - using AsDecl = std::tuple; +//////////////////////////////////////////////////////////////////////////////// +// MetaFunction Helpers. +//////////////////////////////////////////////////////////////////////////////// +struct ProxyAsArgMetaFunc { + template + using type = Arg_t; }; -template -struct Proxy>> - : public ProxyBase { - using AsArg = std::tuple; - using AsDecl = std::tuple; +template +struct ProxyAsDeclMetaFunc { + template + using type = AsDecl_t; +}; - static jlong ProxyAsArg(jlong val) { return val; } +} // namespace jni - // jlong is a smaller type on ARM than x86. - // When jlong is not equivalent, we upcast to the wider type. - template && - !std::is_same_v>> - static jlong ProxyAsArg(T val) { - return jlong{val}; - } -}; +// Consumers of Proxy *must* include proxy defininitions after proxy.h. This is +// because Arrays define themselves using the proxies of other types. -template -struct Proxy>> - : public ProxyBase { - using AsArg = std::tuple; +#include +#include +#include - template - using AsReturn = LocalString; +namespace jni::metaprogramming { - // These leak local instances of strings. Usually, RAII mechanisms would - // correctly release local instances, but here we are stripping that so it can - // be used in a method. This could be obviated by wrapping the calling scope - // in a local stack frame. - static jstring ProxyAsArg(jstring s) { return s; } - static jstring ProxyAsArg(const char* s) { return LocalString{s}.Release(); } - static jstring ProxyAsArg(std::string s) { return LocalString{s}.Release(); } - static jstring ProxyAsArg(std::string_view s) { - return LocalString{s}.Release(); - } -}; +template +class QueryableMapBase {}; -template -struct Proxy>> - : public ProxyBase { - using AsDecl = std::tuple; - using AsArg = std::tuple>; +// This is an interface that can be inherited from to expose an +// operator["name"]. It provides compile time string index lookup with no macros +// although it is dependent on a clang extension. +// +// To use this API, inherit from this class using template types as follows: +// +// |CrtpBase|: The name of the class inheriting from the map. This class +// will inherit an operator[]. It must implement this exact signature: +// +// template +// auto QueryableMapCall(const char* key); +// +// |tup_container_v| is a static instance of an object whose |nameable_member| +// contains a public field called name_. It might seem strange not to +// directly pass a const auto&, however, this prevents accessing subobjects. +// +// The motivation for using inheritance as opposed to a simple member is that +// the the const char cannot be propagated without losing its constexpr-ness, +// and so the clang extension can no longer restrict function candidates. +template +class QueryableMap + : public QueryableMapBase> {}; - template - struct Helper { - // It's illegal to initialise this type with a sub-object of another, - // however, we can construct types with enough validation to guarantee - // correctness. - static constexpr Class kClass{OverloadT::GetReturn().raw_.name_}; +template ::*nameable_member> +using QueryableMap_t = + QueryableMap>, + std::decay_t, nameable_member>; + +template +class QueryableMapEntry; + +template +class QueryableMapBase> + : public QueryableMapEntry... { + public: + using QueryableMapEntry::operator[]...; + + using QueryableMapEntry::Contains...; + + // Will select subclass specialisations if present. + constexpr bool Contains(const char* key) { return false; } +}; + +template +class QueryableMapEntry { + public: +#if __clang__ + // This function blurs the distinction between type and value space. The + // clang extension allows the key to be wrapped in a constexpr way. This + // allows for string to string comparison based on the static value the class + // is templated by. + // + // The reason the TypeMap interface requires inheritance as opposed to simply + // holding an instance of this map (like you would with a regular hash map) is + // the constexpr-ness of the string can't be propagated. This essentially + // means you get one shot at defining the function. + constexpr auto operator[](const char* key) __attribute__(( + enable_if(std::string_view(key) == + std::get(tup_container_v.*nameable_member).name_, + ""))) { + static_assert(std::is_base_of_v, + "You must derive from the invocable map."); - // TODO(b/174272629): Class loaders should also be enforced. - using type = LocalObject; - }; + return (*static_cast(this)).template QueryableMapCall(key); + } - template - using AsReturn = typename Helper::type; + constexpr bool Contains(const char* key) __attribute__(( + enable_if(std::string_view(key) == + std::get(tup_container_v.*nameable_member).name_, + ""))) { + return true; + } +#else + static_assert(false, + "This container requires clang for compile time strings."); +#endif +}; - static jobject ProxyAsArg(jobject obj) { return obj; }; +} // namespace jni::metaprogramming - // Applies for both local and global. - template - static jobject ProxyAsArg(T& t) { - return jobject{t}; - }; - // Applies for both local and global. - template - static jobject ProxyAsArg(T&& t) { - return t.Release(); - }; -}; +#include +#include -//////////////////////////////////////////////////////////////////////////////// -// Array Proxy Definitions. -//////////////////////////////////////////////////////////////////////////////// +namespace jni::metaprogramming { -// Proxy for arrays of arrays (e.g. Array{Array{int}}). -template -struct Proxy>> - : public ProxyBase { - using AsDecl = std::tuple>; - using AsArg = std::tuple, RefBaseTag>; +template +struct OptionalTup {}; - template - using CDecl = typename ArrayHelper::StrippedCDecl; +// Takes a _tuple_ of types and returns a tuple with the same types except +// wrapped in std::optional. +template +struct OptionalTup> { + using type = std::tuple...>; +}; - template - using AsReturn = typename ArrayHelper::AsReturn; +template +using OptionalTup_t = typename OptionalTup::type; - static jarray ProxyAsArg(jarray arr) { return arr; }; +} // namespace jni::metaprogramming - template - static jarray ProxyAsArg(T& t) { - return jarray{t}; - }; - template , T>>> - static jarray ProxyAsArg(T&& t) { - return t.Release(); - }; -}; +#include +#include +#include -template -struct ArrayRefPrimitiveTag; +namespace jni::metaprogramming { -// Primitive array like types such as jintArray, jfloatArray. -template -struct Proxy>> - : public ProxyBase { - // Non-array primitive type (e.g. jintArray => jint). - using CDeclOfPrimitiveType = ArrayToRegularTypeMap_t; +template +class InvocableMap; - using AsDecl = std::tuple>; - using AsArg = - std::tuple, ArrayTag, - ArrayRefPrimitiveTag>; +// This is an interface that can be inherited from to expose an operator(...). +// It provides compile time string index lookup with no macros although it is +// dependent on a clang extension. +// +// To use this API, inherit from this class using template types as follows: +// +// |CrtpBase|: The name of the class inheriting from the map. This class +// will inherit an operator(). It must implement this exact signature: +// +// template +// auto InvocableMapCall(const char* key, Args&&... args); +// +// If i is the index where |tup_container_v.*nameable_member|.name_ == key, +// then InvocablemapCall will forward the args from operator() with the +// same args. Static memory can be used in this function call and it will +// be unique because of the I non-type template parameter. +// +// |tup_container_v| is a static instance of an object whose |nameable_member| +// contains a public field called name_. It might seem strange not to +// directly pass a const auto&, however, this prevents accessing subobjects. +// +// The motivation for using inheritance as opposed to a simple member is that +// the the const char cannot be propagated without losing its constexpr-ness, +// and so the clang extension can no longer restrict function candidates. +template ::*nameable_member> +using InvocableMap_t = + InvocableMap, nameable_member>; - template - using CDecl = RegularToArrayTypeMap_t; +template +class InvocableMapEntry; - template - using AsReturn = typename ArrayHelper::AsReturn; +template +class InvocableMapBase {}; - static JArrayType ProxyAsArg(JArrayType arr) { return arr; }; +template +class InvocableMapBase> + : public InvocableMapEntry... { + public: + using InvocableMapEntry::operator()...; +}; - template - static JArrayType ProxyAsArg(T& t) { - return JArrayType{t}; - }; +template +class InvocableMapEntry { + public: +#if __clang__ + // This function blurs the distinction between type and value space. The + // clang extension allows the key to be wrapped in a constexpr way. This + // allows for string to string comparison based on the static value the class + // is templated by. + // + // The reason the TypeMap interface requires inheritance as opposed to simply + // holding an instance of this map (like you would with a regular hash map) is + // the constexpr-ness of the string can't be propagated. This essentially + // means you get one shot at defining the function. + template + constexpr auto operator()(const char* key, Args&&... args) __attribute__(( + enable_if(std::string_view(key) == + std::get(tup_container_v.*nameable_member).name_, + ""))) { + static_assert(std::is_base_of_v, + "You must derive from the invocable map."); - template , T>>> - static JArrayType ProxyAsArg(T&& t) { - return t.Release(); - }; + return (*static_cast(this)) + .template InvocableMapCall(key, + std::forward(args)...); + } +#else + static_assert(false, + "This container requires clang for compile time strings."); +#endif }; -// Arrays of objects. -template -struct Proxy< - JObjectArrayType, - typename std::enable_if_t>> - : public ProxyBase { - using AsDecl = std::tuple>; - using AsArg = std::tuple, - RefBaseTag>; +//============================================================================== +template +class InvocableMap + : public InvocableMapBase< + CrtpBase, tup_container_v, TupContainerT, nameable_member, + std::make_index_sequence>>> {}; - template - using CDecl = jobjectArray; +} // namespace jni::metaprogramming - template - using AsReturn = typename ArrayHelper::AsReturn; - static jobjectArray ProxyAsArg(jobjectArray arr) { return arr; }; +namespace jni { - template - static jobjectArray ProxyAsArg(T& t) { - return jobjectArray{t}; - }; +template +struct FieldHelper { + static Raw GetValue(const jobject object_ref, const jfieldID field_ref_); - template , T>>> - static jobjectArray ProxyAsArg(T&& t) { - return t.Release(); - }; + static void SetValue(const jobject object_ref, const jfieldID field_ref_, + Raw&& value); }; -// This must be defined outside of Proxy so implicit definition doesn't occur. -template -struct ArrayHelper { - template - struct Helper { - static constexpr auto val = FullArrayStripV(t.raw_type_); +template <> +inline jboolean FieldHelper::GetValue(const jobject object_ref, + const jfieldID field_ref_) { + return jni::JniEnv::GetEnv()->GetBooleanField(object_ref, field_ref_); +} - using StrippedCDecl = CDecl_t>; - using ConvertedCDecl = RegularToArrayTypeMap_t; - }; +template <> +inline void FieldHelper::SetValue(const jobject object_ref, + const jfieldID field_ref_, + jboolean&& value) { + jni::JniEnv::GetEnv()->SetBooleanField(object_ref, field_ref_, value); +} - template - static constexpr auto LocalArrayBuildFromArray() { - using RawT = std::decay_t>; - constexpr std::size_t kRank = - Rankifier::Rank(array_val); +template <> +inline jbyte FieldHelper::GetValue(const jobject object_ref, + const jfieldID field_ref_) { + return jni::JniEnv::GetEnv()->GetByteField(object_ref, field_ref_); +} - // TODO(b/143908983): Support multi-dimensional arrays. - if constexpr (!std::is_same_v, jobject>) { - return LocalArray{nullptr}; - } else { - return LocalArray::val>{ - jobjectArray{nullptr}}; - } - } +template <> +inline void FieldHelper::SetValue(const jobject object_ref, + const jfieldID field_ref_, + jbyte&& value) { + jni::JniEnv::GetEnv()->SetByteField(object_ref, field_ref_, value); +} - static constexpr auto kVal{Overload::GetReturn().raw_}; +template <> +inline jchar FieldHelper::GetValue(const jobject object_ref, + const jfieldID field_ref_) { + return jni::JniEnv::GetEnv()->GetCharField(object_ref, field_ref_); +} - using StrippedCDecl = typename Helper::ConvertedCDecl; - using AsReturn = decltype(LocalArrayBuildFromArray()); -}; +template <> +inline void FieldHelper::SetValue(const jobject object_ref, + const jfieldID field_ref_, + jchar&& value) { + jni::JniEnv::GetEnv()->SetCharField(object_ref, field_ref_, value); +} -} // namespace jni +template <> +inline jshort FieldHelper::GetValue(const jobject object_ref, + const jfieldID field_ref_) { + return jni::JniEnv::GetEnv()->GetShortField(object_ref, field_ref_); +} + +template <> +inline void FieldHelper::SetValue(const jobject object_ref, + const jfieldID field_ref_, + jshort&& value) { + jni::JniEnv::GetEnv()->SetShortField(object_ref, field_ref_, value); +} +template <> +inline jint FieldHelper::GetValue(const jobject object_ref, + const jfieldID field_ref_) { + return jni::JniEnv::GetEnv()->GetIntField(object_ref, field_ref_); +} -#include -#include -#include -#include +template <> +inline void FieldHelper::SetValue(const jobject object_ref, + const jfieldID field_ref_, + jint&& value) { + jni::JniEnv::GetEnv()->SetIntField(object_ref, field_ref_, value); +} -namespace jni { +template <> +inline jlong FieldHelper::GetValue(const jobject object_ref, + const jfieldID field_ref_) { + return jni::JniEnv::GetEnv()->GetLongField(object_ref, field_ref_); +} -template -struct Proxy; +template <> +inline void FieldHelper::SetValue(const jobject object_ref, + const jfieldID field_ref_, + jlong&& value) { + jni::JniEnv::GetEnv()->SetLongField(object_ref, field_ref_, value); +} -// Everything you are permitted to declare at method prototypes. -// Note, if the size can reasonably differ, the jtype is enforced by virtue of -// being a different type (i.e. you can't accidentally widen). -using AllKeys = - std::tuple; +template <> +inline jfloat FieldHelper::GetValue(const jobject object_ref, + const jfieldID field_ref_) { + return jni::JniEnv::GetEnv()->GetFloatField(object_ref, field_ref_); +} -// Instead of directly searching for the type, convertible types are sought. -// E.g. A string like "Foo" the type will be const char[4] not const char*. -template -struct IsConvertibleKey { - template - static constexpr bool value = - std::is_same_v> || - std::is_base_of_v, std::decay_t> || - std::is_base_of_v, std::decay_t>; -}; +template <> +inline void FieldHelper::SetValue(const jobject object_ref, + const jfieldID field_ref_, + jfloat&& value) { + jni::JniEnv::GetEnv()->SetFloatField(object_ref, field_ref_, value); +} -template -struct ProxyHelper { - using T = std::decay_t; +template <> +inline jdouble FieldHelper::GetValue(const jobject object_ref, + const jfieldID field_ref_) { + return jni::JniEnv::GetEnv()->GetDoubleField(object_ref, field_ref_); +} - // Metafunction that builds a list of a passable type to all it's possible - // passable types, which may not be the same. E.g. jint => jint, but - // jstring => jstring, const char*, std::string_view, std::string, etc. - struct IndexToKey { - // Proxies can be indexed by their |AsArg|s or their |AsDecl|. - template - using type = metaprogramming::CartesianProduct_t< - std::tuple, - metaprogramming::UniqueSet_Tup::AsArg, typename Proxy::AsDecl>>>; - }; +template <> +inline void FieldHelper::SetValue(const jobject object_ref, + const jfieldID field_ref_, + jdouble&& value) { + jni::JniEnv::GetEnv()->SetDoubleField(object_ref, field_ref_, value); +} - // Build a list of two element tuples (in preparation to build a map). e.g. - // { {jint, int}, {jstring, const char*}, {jstring, std::string}, etc. }. - // Note that types may map to 1 or more types, such as jstring above. - using IndexToKeyAsTuples = metaprogramming::Reduce_t< - metaprogramming::Combine, - metaprogramming::InvokePerTupArg_t>; +template <> +inline jobject FieldHelper::GetValue(const jobject object_ref, + const jfieldID field_ref_) { + return jni::JniEnv::GetEnv()->GetObjectField(object_ref, field_ref_); +} - // Collapse this list into a set of keys and values consumable by - // TypeToTypeMap. - using IndexToKeyMap = metaprogramming::TypeToTypeMapFromKeyValuesTup_t< - metaprogramming::Flatten_t>; +template <> +inline void FieldHelper::SetValue(const jobject object_ref, + const jfieldID field_ref_, + jobject&& new_value) { + jni::JniEnv::GetEnv()->SetObjectField(object_ref, field_ref_, new_value); +} - // When flipped, a type passed can be reverse indexed to select the same - // Proxy partial specialisation. - using KeyToIndex = metaprogramming::TypeToTypeMap_Invert; +} // namespace jni - using Index = - metaprogramming::TypeToTypeMapQueryWithComparator_t>; +#include +#include - using Proxy_t = Proxy; +namespace jni { - template - using CDecl = typename Proxy_t::template CDecl; +// Represents a return value index for |InputParamSelection|. +static constexpr std::size_t kIsReturnIdx = + std::numeric_limits::max(); - template - using AsReturn_t = typename Proxy_t::template AsReturn; +template +struct InputParamSelection { + // If |param_idx| is kIsReturnIdx, this is the return value. + static constexpr bool kIsReturn = (param_idx == kIsReturnIdx); - template - using AsArg_t = typename Proxy_t::AsArg; + static constexpr inline const auto& Val() { + if constexpr (kIsReturn) { + return OverloadSelectionT::GetReturn().raw_; + } else { + return std::get(OverloadSelectionT::GetParams().values_); + } + } - using AsDecl_t = typename Proxy_t::AsDecl; -}; + using RawValT = ArrayStrip_t>; + using UnstrippedRawVal = std::decay_t; -//////////////////////////////////////////////////////////////////////////////// -// MetaFunction Helpers. -//////////////////////////////////////////////////////////////////////////////// -struct ProxyAsArgMetaFunc { - template - using type = Arg_t; -}; + static constexpr std::size_t kRank = Rankifier::Rank(Val()); -template -struct ProxyAsDeclMetaFunc { - template - using type = AsDecl_t; + // Find the appropriate proxy logic for the given argument, and see if that + // parameter is contextually correct given the arguments. + template + static constexpr bool kValid = Proxy_t::template kViable< + InputParamSelection, param_idx, + metaprogramming::TypeOfNthElement_t>; }; } // namespace jni -// Consumers of Proxy *must* include proxy defininitions after proxy.h. This is -// because Arrays define themselves using the proxies of other types. - namespace jni { @@ -4389,6 +4566,8 @@ static inline auto& GetDefaultLoadedMethodList() { template struct OverloadRef { + using ReturnProxied = typename Overload::AsReturn; + static constexpr std::string_view GetMethodSignature() { return Overload::GetOverloadSignature(); } @@ -4406,29 +4585,26 @@ struct OverloadRef { }); } -}; - -template -struct PermutationRef { - using ReturnProxied = typename Overload::AsReturn; - using OverloadRef = OverloadRef; template static ReturnProxied Invoke(jclass clazz, jobject object, Params&&... params) { if constexpr (std::is_same_v) { - JniMethodInvoke::Invoke( + JniMethodInvoke::Invoke( object, OverloadRef::GetMethodID(clazz), Proxy_t::ProxyAsArg(std::forward(params))...); + } else if constexpr (Method::kIsConstructor) { return {JniHelper::NewLocalObject( clazz, OverloadRef::GetMethodID(clazz), Proxy_t::ProxyAsArg(std::forward(params))...)}; } else { - static constexpr bool is_array = - kIsArrayType; + static constexpr std::size_t kRank = + InputParamSelection::kRank; - return {JniMethodInvoke::Invoke( + // For now, adding +1 because the rest of the type system behaves as if + // a type is rank 0, and JniMethodInvoke behaves as if void is rank 0. + return {JniMethodInvoke::Invoke( object, OverloadRef::GetMethodID(clazz), Proxy_t::ProxyAsArg(std::forward(params))...)}; } @@ -4537,43 +4713,24 @@ namespace jni { static constexpr std::size_t kNoSelection = std::numeric_limits::max(); +// The the type of an exact selection parameter in a method as part of the class +// specification (vs. the selection of a parameter in some generated candidate). +template +struct InputParamSelection; + // Represents an indexing into a specific class and method. template struct MethodSelection; -// Represents an overload which itself may be a set of permutations. +// Represents a specific overload selection. template struct OverloadSelection; -// Represents a permutation (e.g. jstring => {std::string, const char*, etc...} -template -struct Permutation; - -// Represents the exact selection of a specific parameter from a permutation. -template -struct ParamSelection; - -// The the type of an exact selection parameter in a method as part of the class -// specification (vs. the selection of a parameter in some generated candidate). -template -struct InputParamSelection; - -// Compares a ParamSelection (the type associated with an exact parameter of an -// exact permutation) and exposes a value if they are equal. -template -struct ParamCompare; - -// The type correlating to the selection of a return type of a method. -template -struct ReturnSelection; - -// Represents the exact permutation selection for a set of arguments. +// Helper to find overloads for a given arg set. template -struct PermutationSelectionForArgs; +struct OverloadSelectionForArgsImpl; //////////////////////////////////////////////////////////////////////////////// // Helper Aliases. @@ -4585,9 +4742,9 @@ using MethodSelection_t = template -using PermutationSelectionForArgs_t = - PermutationSelectionForArgs; +using MethodSelectionForArgs_t = + OverloadSelectionForArgsImpl; //////////////////////////////////////////////////////////////////////////////// // Implementation Details. @@ -4638,13 +4795,9 @@ struct MethodSelection { // kNoSelection is the max of std::size_t, so, this essentially selects any // idx (if a valid one exists), or defaults to kNoSelection. - static constexpr std::pair - overload_permutation_idx_if_valid{ - std::min({OverloadSelection::template OverloadIdxIfViable()...}), - std::min( - {OverloadSelection::template PermutationIdxIfViable()...})}; + static constexpr std::size_t overload_idx_if_valid{std::min( + {OverloadSelection::template OverloadIdxIfViable()...})}; }; template @@ -4653,52 +4806,36 @@ struct MethodSelection { std::decay_t...>::val; } - // The method and permutation overload that is viable for a set of args, or - // {kNoSelection, kNoSelection} if no permutation is valid. + // The overload that is viable for a set of args, or |kNoSelection|. template - static constexpr std::pair IdxPair() { + static constexpr std::size_t IdxForArgs() { return Helper, - std::decay_t...>::overload_permutation_idx_if_valid; + std::decay_t...>::overload_idx_if_valid; } template - using FindOverloadSelection = OverloadSelection().first>; - - template - using FindPermutation = OverloadSelection().second>; -}; - -template -struct ReturnSelection { - using RawValT = ArrayStrip_t< - Raw_t>>; - - static inline constexpr std::size_t ComputeRank() { - if constexpr (Rankifier::kComputableRank) { - return Rankifier::Rank(OverloadSelectionT::GetReturn().raw_); - } else { - return 0; - } - } - - static constexpr std::size_t kRank = ComputeRank(); - - static constexpr inline auto& Val() { - return OverloadSelectionT::GetReturn().raw_; - } + using FindOverloadSelection = + OverloadSelection()>; }; -template -struct InputParamSelection { - static constexpr inline const auto& Val() { - return std::get(OverloadSelectionT::GetParams().values_); - } +template +struct ArgumentValidate { + template + struct Helper { + static constexpr bool kValid = false; + }; - using RawValT = ArrayStrip_t>; + template + struct Helper, Ts...> { + static constexpr bool kValid = + (InputParamSelection::template kValid && + ...); + }; - static constexpr std::size_t kRank = Rankifier::Rank( - std::get(OverloadSelectionT::GetParams().values_)); + template + static constexpr bool kValid = + Helper<(sizeof...(Ts) == OverloadSelectionT::kNumParams), + std::make_index_sequence, Ts...>::kValid; }; template @@ -4724,70 +4861,20 @@ struct OverloadSelection { } } + using ReturnT = std::decay_t; + using ParamsT = std::decay_t; + // CDecl is the type used by the C API for a return type. - using CDecl = - CDecl_t>, OverloadSelection>; + using CDecl = CDecl_t>; // Return type is the richly decorated type returned (e.g LocalArray). - using AsReturn = - Return_t>, OverloadSelection>; - - // Proxy every parameter argument as an argument that can be shown in a - // function prototype. - // - // A void function generates no arguments, so we generate an empty tuple. - using ParamsRaw = typename std::decay_t::ParamsRawTup; - using ParamsProxiedTemp = - metaprogramming::InvokePerTupArg_t; - using ParamsProxied = - std::conditional_t>, - std::tuple>, ParamsProxiedTemp>; - - // Lastly, we create a sequence to iterate all viable permutations. - using NBitSequence = metaprogramming::TupleUnroller_t< - metaprogramming::GenerateBitSequenceFromTupSetsFunc, ParamsProxied>; - - static constexpr const size_t permutation_count = - (NBitSequence::max_representable_size_ == 0 - ? 1 - : NBitSequence::max_representable_size_); - - template - struct Helper; - - // Iterator for Permutation - template - struct Helper, Ts...> { - static_assert(sizeof...(Is) == permutation_count); - - static constexpr bool val = - (Permutation::template PermutationViable() || - ...); - - static constexpr size_t first_valid_permutation = std::min( - {Permutation::template PermutationIdxIfViable()...}); - }; + using AsReturn = Return_t, OverloadSelection>; - static constexpr size_t kNumParams = - std::tuple_size_v; - - template - static constexpr size_t PermutationIdxIfViable() { - constexpr size_t num_params = - std::tuple_size_v; - if constexpr (sizeof...(Ts) != num_params) { - return kNoSelection; - } else { - return Helper, - Ts...>::first_valid_permutation; - } - } + static constexpr size_t kNumParams = std::tuple_size_v; template static constexpr bool OverloadViable() { - return PermutationIdxIfViable() != kNoSelection; + return ArgumentValidate::template kValid; } template @@ -4809,9 +4896,8 @@ struct OverloadSelection { template struct ParamHelper> { static constexpr std::string_view val = - metaprogramming::StringConcatenate_v< - SelectorStaticInfo>::kTypeName...>; + metaprogramming::StringConcatenate_v>::kTypeName...>; }; static constexpr std::string_view kParamSignature = @@ -4825,7 +4911,7 @@ struct OverloadSelection { return "V"; } else { return SelectorStaticInfo< - ReturnSelection>::kTypeName; + InputParamSelection>::kTypeName; } } @@ -4837,133 +4923,18 @@ struct OverloadSelection { } }; -template -struct Permutation { - using NBitSelection = - metaprogramming::Increment_t; - - template - using Param = ParamSelection; - - template - struct Helper; - - template - struct Helper, Ts...> { - static constexpr bool val = (Param::template viable && ...); - }; - - template - static constexpr bool PermutationViable() { - return Helper, Ts...>::val; - } - - template - static constexpr size_t PermutationIdxIfViable() { - return PermutationViable() ? permutation_idx : kNoSelection; - } -}; - -template -struct ParamSelection { - static constexpr size_t selection_idx = - PermutationT::NBitSelection::template GetBit::value_; - - constexpr static auto& GetParam() { - return std::get(OverloadSelectionT::GetParams().values_); - } - - using ParamT = metaprogramming::TypeOfNthTupleElement_t< - selection_idx, metaprogramming::TypeOfNthTupleElement_t< - param_idx, typename OverloadSelectionT::ParamsProxied>>; - - template - static constexpr bool viable = ParamCompare::val; -}; - -template -struct ParamCompare { - using ParamT = typename ParamSelectionT::ParamT; - - template - struct Helper { - static constexpr bool val = false; - }; - - template - struct Helper, - std::remove_cv_t>>> { - static constexpr bool val = true; - }; - - // The partial specialisation to compare a Local or Global object - // against the specific selected permutation. - template