// Copyright 2020-2021, Collabora, Ltd. // SPDX-License-Identifier: BSL-1.0 // Author: Rylie Pavlik #pragma once #include #include #include namespace wrap { /*! * Base class for types wrapping Java types. * * Derived types are encouraged to have a nested `struct Meta`, inheriting * publicly from MetaBaseDroppable or MetaBase, with a singleton accessor named * `data()`, and a private constructor (implemented in a .cpp file, not the * header) that populates jni::method_t, jni::field_t, etc. members for each * method, etc. of interest. */ class ObjectWrapperBase { public: /*! * Default constructor. */ ObjectWrapperBase() = default; /*! * Construct from a jni::Object. */ explicit ObjectWrapperBase(jni::Object obj) : obj_(std::move(obj)) {} /*! * Construct from a nullptr. */ explicit ObjectWrapperBase(std::nullptr_t const &) : obj_() {} /*! * Evaluate if this is non-null */ explicit operator bool() const noexcept { return !obj_.isNull(); } /*! * Is this object null? */ bool isNull() const noexcept { return obj_.isNull(); } /*! * Get the wrapped jni::Object */ jni::Object &object() noexcept { return obj_; } /*! * Get the wrapped jni::Object (const overload) */ jni::Object const &object() const noexcept { return obj_; } private: jni::Object obj_; }; /*! * Equality comparison for a wrapped Java object. */ static inline bool operator==(ObjectWrapperBase const &lhs, ObjectWrapperBase const &rhs) noexcept { return lhs.object() == rhs.object(); } /*! * Inequality comparison for a wrapped Java object. */ static inline bool operator!=(ObjectWrapperBase const &lhs, ObjectWrapperBase const &rhs) noexcept { return lhs.object() != rhs.object(); } /*! * Equality comparison between a wrapped Java object and @p nullptr. */ static inline bool operator==(ObjectWrapperBase const &obj, std::nullptr_t) noexcept { return obj.isNull(); } /*! * Equality comparison between a wrapped Java object and @p nullptr. */ static inline bool operator==(std::nullptr_t, ObjectWrapperBase const &obj) noexcept { return obj.isNull(); } /*! * Inequality comparison between a wrapped Java object and @p nullptr. */ static inline bool operator!=(ObjectWrapperBase const &obj, std::nullptr_t) noexcept { return !(obj == nullptr); } /*! * Inequality comparison between a wrapped Java object and @p nullptr. */ static inline bool operator!=(std::nullptr_t, ObjectWrapperBase const &obj) noexcept { return !(obj == nullptr); } /*! * Base class for Meta structs where you want the reference to the Class object * to persist (indefinitely). * * Mostly for classes that would stick around anyway (e.g. * @p java.lang.ClassLoader ) where many operations are on static * methods/fields. Use of a non-static method or field does not require such a * reference, use MetaBaseDroppable in those cases. */ class MetaBase { public: /*! * Gets a reference to the class object. * * Unlike MetaBaseDroppable, here we know that the class object ref is * alive. */ jni::Class const &clazz() const noexcept { return clazz_; } /*! * Gets a reference to the class object. * * Provided here for parallel API to MetaBaseDroppable, despite being * synonymous with clazz() here. */ jni::Class const &classRef() const noexcept { return clazz_; } /*! * Get the class name, with namespaces delimited by `/`. */ const char *className() const noexcept { return classname_; } protected: /*! * Construct. * * @param classname The class name, fully qualified, with namespaces * delimited by `/`. * @param clazz The jclass object for the class in question, if known. */ explicit MetaBase(const char *classname, jni::jclass clazz = nullptr) : classname_(classname), clazz_() { if (clazz != nullptr) { // The 0 makes it a global ref. clazz_ = jni::Class{clazz, 0}; } else { clazz_ = jni::Class{classname}; } } private: const char *classname_; jni::Class clazz_; }; /*! * Base class for Meta structs where you don't need the reference to the Class * object to persist. (This is most uses.) */ class MetaBaseDroppable { public: /*! * Gets the class object. * * Works regardless of whether dropClassRef() has been called - it's just * slower if it has. */ jni::Class clazz() const { if (clazz_.isNull()) { return {classname_}; } return clazz_; } /*! * Get the class name, with namespaces delimited by `/`. */ const char *className() const noexcept { return classname_; } /*! * May be called in/after the derived constructor, to drop the reference to * the class object if it's no longer needed. */ void dropClassRef() { clazz_ = jni::Class{}; } protected: /*! * Construct. * * Once you are done constructing your derived struct, you may call * dropClassRef() and still safely use non-static method and field IDs * retrieved. * * @param classname The class name, fully qualified, with namespaces * delimited by `/`. * @param clazz The jclass object for the class in question, if known. */ explicit MetaBaseDroppable(const char *classname, jni::jclass clazz = nullptr) : classname_(classname), clazz_() { if (clazz != nullptr) { // The 0 makes it a global ref. clazz_ = jni::Class{clazz, 0}; } else { clazz_ = jni::Class{classname}; } } /*! * Gets a reference to the class object, but is non-null only if it's still * cached. * * Only for used in derived constructors/initializers, where you know you * haven't dropped this yet. */ jni::Class const &classRef() const { return clazz_; } private: const char *classname_; jni::Class clazz_; }; /*! * Implementation namespace for these JNI wrappers. * * They can be ignored if you aren't adding/extending wrappers. */ namespace impl { /*! * Type-aware wrapper for a field ID. * * This is a smarter alternative to just using jni::field_t since it avoids * having to repeat the type in the accessor, without using any more storage. * * @see StaticFieldId for the equivalent for static fields. * @see WrappedFieldId for the equivalent for structures that we wrap. */ template struct FieldId { public: FieldId(jni::Class const &clazz, const char *name) : id(clazz.getField(name)) {} const jni::field_t id; }; /*! * Get the value of field @p field in Java object @p obj. * * This is found by argument-dependent lookup and can be used unqualified. * * @relates FieldId */ template static inline T get(FieldId const &field, jni::Object const &obj) { assert(!obj.isNull()); return obj.get(field.id); } /*! * Type-aware wrapper for a static field ID. * * This is a smarter alternative to just using jni::field_t since it avoids * having to repeat the type in the accessor, without using any more storage. * * @see FieldId */ template struct StaticFieldId { public: StaticFieldId(jni::Class const &clazz, const char *name) : id(clazz.getStaticField(name)) {} const jni::field_t id; }; /*! * Get the value of static field @p field in Java type @p clazz. * * This is found by argument-dependent lookup and can be used unqualified. * * @relates FieldId */ template static inline T get(StaticFieldId const &field, jni::Class const &clazz) { assert(!clazz.isNull()); return clazz.get(field.id); } /*! * Type-aware wrapper for a field ID of a wrapped structure type. * * This is a smarter alternative to just using jni::field_t since it avoids * having to repeat the type in the accessor, without using any more storage. * * Requires that the structure wrapper provides * `static constexpr const char *getTypeName() noexcept;` * * @see FieldId */ template struct WrappedFieldId { public: WrappedFieldId(jni::Class const &clazz, const char *name) : id(lookupField(clazz, name)) {} const jni::field_t id; private: /*! * Helper for field ID lookup, mostly to avoid calling c_str() on a string * temporary. */ static jni::field_t lookupField(jni::Class const &clazz, const char *name) { std::string fullType = std::string("L") + T::getTypeName() + ";"; return clazz.getField(name, fullType.c_str()); } }; /*! * Get the value of field @p field in Java object @p obj. * * This is found by argument-dependent lookup and can be used unqualified. * * @relates WrappedFieldId */ template static inline T get(WrappedFieldId const &field, jni::Object const &obj) { assert(!obj.isNull()); return T{obj.get(field.id)}; } } // namespace impl } // namespace wrap