The open source OpenXR runtime
at main 337 lines 9.4 kB view raw
1// Copyright 2020-2021, Collabora, Ltd. 2// SPDX-License-Identifier: BSL-1.0 3// Author: Rylie Pavlik <rylie.pavlik@collabora.com> 4 5#pragma once 6#include <assert.h> 7#include <jni.h> 8#include <jnipp.h> 9 10namespace wrap { 11 12/*! 13 * Base class for types wrapping Java types. 14 * 15 * Derived types are encouraged to have a nested `struct Meta`, inheriting 16 * publicly from MetaBaseDroppable or MetaBase, with a singleton accessor named 17 * `data()`, and a private constructor (implemented in a .cpp file, not the 18 * header) that populates jni::method_t, jni::field_t, etc. members for each 19 * method, etc. of interest. 20 */ 21class ObjectWrapperBase { 22 public: 23 /*! 24 * Default constructor. 25 */ 26 ObjectWrapperBase() = default; 27 28 /*! 29 * Construct from a jni::Object. 30 */ 31 explicit ObjectWrapperBase(jni::Object obj) : obj_(std::move(obj)) {} 32 33 /*! 34 * Construct from a nullptr. 35 */ 36 explicit ObjectWrapperBase(std::nullptr_t const &) : obj_() {} 37 38 /*! 39 * Evaluate if this is non-null 40 */ 41 explicit operator bool() const noexcept { return !obj_.isNull(); } 42 43 /*! 44 * Is this object null? 45 */ 46 bool isNull() const noexcept { return obj_.isNull(); } 47 48 /*! 49 * Get the wrapped jni::Object 50 */ 51 jni::Object &object() noexcept { return obj_; } 52 53 /*! 54 * Get the wrapped jni::Object (const overload) 55 */ 56 jni::Object const &object() const noexcept { return obj_; } 57 58 private: 59 jni::Object obj_; 60}; 61 62/*! 63 * Equality comparison for a wrapped Java object. 64 */ 65static inline bool operator==(ObjectWrapperBase const &lhs, 66 ObjectWrapperBase const &rhs) noexcept { 67 return lhs.object() == rhs.object(); 68} 69 70/*! 71 * Inequality comparison for a wrapped Java object. 72 */ 73static inline bool operator!=(ObjectWrapperBase const &lhs, 74 ObjectWrapperBase const &rhs) noexcept { 75 return lhs.object() != rhs.object(); 76} 77 78/*! 79 * Equality comparison between a wrapped Java object and @p nullptr. 80 */ 81static inline bool operator==(ObjectWrapperBase const &obj, 82 std::nullptr_t) noexcept { 83 return obj.isNull(); 84} 85 86/*! 87 * Equality comparison between a wrapped Java object and @p nullptr. 88 */ 89static inline bool operator==(std::nullptr_t, 90 ObjectWrapperBase const &obj) noexcept { 91 return obj.isNull(); 92} 93 94/*! 95 * Inequality comparison between a wrapped Java object and @p nullptr. 96 */ 97static inline bool operator!=(ObjectWrapperBase const &obj, 98 std::nullptr_t) noexcept { 99 return !(obj == nullptr); 100} 101 102/*! 103 * Inequality comparison between a wrapped Java object and @p nullptr. 104 */ 105static inline bool operator!=(std::nullptr_t, 106 ObjectWrapperBase const &obj) noexcept { 107 return !(obj == nullptr); 108} 109 110/*! 111 * Base class for Meta structs where you want the reference to the Class object 112 * to persist (indefinitely). 113 * 114 * Mostly for classes that would stick around anyway (e.g. 115 * @p java.lang.ClassLoader ) where many operations are on static 116 * methods/fields. Use of a non-static method or field does not require such a 117 * reference, use MetaBaseDroppable in those cases. 118 */ 119class MetaBase { 120 public: 121 /*! 122 * Gets a reference to the class object. 123 * 124 * Unlike MetaBaseDroppable, here we know that the class object ref is 125 * alive. 126 */ 127 jni::Class const &clazz() const noexcept { return clazz_; } 128 /*! 129 * Gets a reference to the class object. 130 * 131 * Provided here for parallel API to MetaBaseDroppable, despite being 132 * synonymous with clazz() here. 133 */ 134 jni::Class const &classRef() const noexcept { return clazz_; } 135 136 /*! 137 * Get the class name, with namespaces delimited by `/`. 138 */ 139 const char *className() const noexcept { return classname_; } 140 141 protected: 142 /*! 143 * Construct. 144 * 145 * @param classname The class name, fully qualified, with namespaces 146 * delimited by `/`. 147 * @param clazz The jclass object for the class in question, if known. 148 */ 149 explicit MetaBase(const char *classname, jni::jclass clazz = nullptr) 150 : classname_(classname), clazz_() { 151 if (clazz != nullptr) { 152 // The 0 makes it a global ref. 153 clazz_ = jni::Class{clazz, 0}; 154 } else { 155 clazz_ = jni::Class{classname}; 156 } 157 } 158 159 private: 160 const char *classname_; 161 jni::Class clazz_; 162}; 163 164/*! 165 * Base class for Meta structs where you don't need the reference to the Class 166 * object to persist. (This is most uses.) 167 */ 168class MetaBaseDroppable { 169 public: 170 /*! 171 * Gets the class object. 172 * 173 * Works regardless of whether dropClassRef() has been called - it's just 174 * slower if it has. 175 */ 176 jni::Class clazz() const { 177 if (clazz_.isNull()) { 178 return {classname_}; 179 } 180 return clazz_; 181 } 182 183 /*! 184 * Get the class name, with namespaces delimited by `/`. 185 */ 186 const char *className() const noexcept { return classname_; } 187 188 /*! 189 * May be called in/after the derived constructor, to drop the reference to 190 * the class object if it's no longer needed. 191 */ 192 void dropClassRef() { clazz_ = jni::Class{}; } 193 194 protected: 195 /*! 196 * Construct. 197 * 198 * Once you are done constructing your derived struct, you may call 199 * dropClassRef() and still safely use non-static method and field IDs 200 * retrieved. 201 * 202 * @param classname The class name, fully qualified, with namespaces 203 * delimited by `/`. 204 * @param clazz The jclass object for the class in question, if known. 205 */ 206 explicit MetaBaseDroppable(const char *classname, 207 jni::jclass clazz = nullptr) 208 : classname_(classname), clazz_() { 209 if (clazz != nullptr) { 210 // The 0 makes it a global ref. 211 clazz_ = jni::Class{clazz, 0}; 212 } else { 213 clazz_ = jni::Class{classname}; 214 } 215 } 216 217 /*! 218 * Gets a reference to the class object, but is non-null only if it's still 219 * cached. 220 * 221 * Only for used in derived constructors/initializers, where you know you 222 * haven't dropped this yet. 223 */ 224 jni::Class const &classRef() const { return clazz_; } 225 226 private: 227 const char *classname_; 228 jni::Class clazz_; 229}; 230 231/*! 232 * Implementation namespace for these JNI wrappers. 233 * 234 * They can be ignored if you aren't adding/extending wrappers. 235 */ 236namespace impl { 237/*! 238 * Type-aware wrapper for a field ID. 239 * 240 * This is a smarter alternative to just using jni::field_t since it avoids 241 * having to repeat the type in the accessor, without using any more storage. 242 * 243 * @see StaticFieldId for the equivalent for static fields. 244 * @see WrappedFieldId for the equivalent for structures that we wrap. 245 */ 246template <typename T> struct FieldId { 247 public: 248 FieldId(jni::Class const &clazz, const char *name) 249 : id(clazz.getField<T>(name)) {} 250 251 const jni::field_t id; 252}; 253 254/*! 255 * Get the value of field @p field in Java object @p obj. 256 * 257 * This is found by argument-dependent lookup and can be used unqualified. 258 * 259 * @relates FieldId 260 */ 261template <typename T> 262static inline T get(FieldId<T> const &field, jni::Object const &obj) { 263 assert(!obj.isNull()); 264 return obj.get<T>(field.id); 265} 266/*! 267 * Type-aware wrapper for a static field ID. 268 * 269 * This is a smarter alternative to just using jni::field_t since it avoids 270 * having to repeat the type in the accessor, without using any more storage. 271 * 272 * @see FieldId 273 */ 274template <typename T> struct StaticFieldId { 275 public: 276 StaticFieldId(jni::Class const &clazz, const char *name) 277 : id(clazz.getStaticField<T>(name)) {} 278 279 const jni::field_t id; 280}; 281 282/*! 283 * Get the value of static field @p field in Java type @p clazz. 284 * 285 * This is found by argument-dependent lookup and can be used unqualified. 286 * 287 * @relates FieldId 288 */ 289template <typename T> 290static inline T get(StaticFieldId<T> const &field, jni::Class const &clazz) { 291 assert(!clazz.isNull()); 292 return clazz.get<T>(field.id); 293} 294 295/*! 296 * Type-aware wrapper for a field ID of a wrapped structure type. 297 * 298 * This is a smarter alternative to just using jni::field_t since it avoids 299 * having to repeat the type in the accessor, without using any more storage. 300 * 301 * Requires that the structure wrapper provides 302 * `static constexpr const char *getTypeName() noexcept;` 303 * 304 * @see FieldId 305 */ 306template <typename T> struct WrappedFieldId { 307 public: 308 WrappedFieldId(jni::Class const &clazz, const char *name) 309 : id(lookupField(clazz, name)) {} 310 311 const jni::field_t id; 312 313 private: 314 /*! 315 * Helper for field ID lookup, mostly to avoid calling c_str() on a string 316 * temporary. 317 */ 318 static jni::field_t lookupField(jni::Class const &clazz, const char *name) { 319 std::string fullType = std::string("L") + T::getTypeName() + ";"; 320 return clazz.getField(name, fullType.c_str()); 321 } 322}; 323 324/*! 325 * Get the value of field @p field in Java object @p obj. 326 * 327 * This is found by argument-dependent lookup and can be used unqualified. 328 * 329 * @relates WrappedFieldId 330 */ 331template <typename T> 332static inline T get(WrappedFieldId<T> const &field, jni::Object const &obj) { 333 assert(!obj.isNull()); 334 return T{obj.get<jni::Object>(field.id)}; 335} 336} // namespace impl 337} // namespace wrap