1*65c59e02SInna Palant# Quick Reference 2*65c59e02SInna Palant- [JavaClass definition and method registration](#javaclass-definition-and-method-registration) 3*65c59e02SInna Palant- [Wrapping constructors](#wrapping-constructors) 4*65c59e02SInna Palant- [Basic method usage (Java to C++ and C++ to Java)](#basic-method-usage-java-to-c-and-c-to-java) 5*65c59e02SInna Palant- [Methods using Java primitives](#methods-using-java-primitives) 6*65c59e02SInna Palant- [Methods using Java Strings](#methods-using-java-strings) 7*65c59e02SInna Palant- [Working with arrays of primitives](#working-with-arrays-of-primitives) 8*65c59e02SInna Palant- [Working with arrays of objects](#working-with-arrays-of-objects) 9*65c59e02SInna Palant- [References](#references) 10*65c59e02SInna Palant- [Defining a nested class](#defining-a-nested-class) 11*65c59e02SInna Palant- [Defining a child class](#defining-a-child-class) 12*65c59e02SInna Palant- [Getting and setting fields](#getting-and-setting-fields) 13*65c59e02SInna Palant- [Working with JObject and JClass](#working-with-jobject-and-jclass) 14*65c59e02SInna Palant- [Catching and throwing exceptions](#catching-and-throwing-exceptions) 15*65c59e02SInna Palant- [Working with boxed primitives](#working-with-boxed-primitives) 16*65c59e02SInna Palant- [Working with Iterables](#working-with-iterables) 17*65c59e02SInna Palant- [Building Collections](#building-collections) 18*65c59e02SInna Palant- [Transferring data with direct ByteBuffer](#transferring-data-with-direct-bytebuffer) 19*65c59e02SInna Palant## JavaClass definition and method registration 20*65c59e02SInna Palant```cpp 21*65c59e02SInna Palant#include <fbjni/fbjni.h> 22*65c59e02SInna Palantusing namespace facebook::jni; 23*65c59e02SInna Palant``` 24*65c59e02SInna Palant```cpp 25*65c59e02SInna Palant// Standard declaration for a normal class (no C++ fields). 26*65c59e02SInna Palantstruct DocTests : JavaClass<DocTests> { 27*65c59e02SInna Palant static constexpr auto kJavaDescriptor = "Lcom/facebook/jni/DocTests;"; 28*65c59e02SInna Palant``` 29*65c59e02SInna Palant```cpp 30*65c59e02SInna Palant // NOTE: The name of this method doesn't matter. 31*65c59e02SInna Palant static void registerNatives() { 32*65c59e02SInna Palant javaClassStatic()->registerNatives({ 33*65c59e02SInna Palant makeNativeMethod("nativeVoidMethod", DocTests::nativeVoidMethod), 34*65c59e02SInna Palant makeNativeMethod("staticNativeVoidMethod", DocTests::staticNativeVoidMethod), 35*65c59e02SInna Palant``` 36*65c59e02SInna Palant```cpp 37*65c59e02SInna Palantjint JNI_OnLoad(JavaVM* vm, void*) { 38*65c59e02SInna Palant return facebook::jni::initialize(vm, [] { 39*65c59e02SInna Palant DocTests::registerNatives(); 40*65c59e02SInna Palant }); 41*65c59e02SInna Palant} 42*65c59e02SInna Palant``` 43*65c59e02SInna Palant 44*65c59e02SInna Palant 45*65c59e02SInna Palant## Wrapping constructors 46*65c59e02SInna Palant```java 47*65c59e02SInna Palantclass DataHolder { 48*65c59e02SInna Palant int i; 49*65c59e02SInna Palant String s; 50*65c59e02SInna Palant DataHolder(int i, String s) { 51*65c59e02SInna Palant this.i = i; 52*65c59e02SInna Palant this.s = s; 53*65c59e02SInna Palant } 54*65c59e02SInna Palant static DataHolder someInstance; 55*65c59e02SInna Palant} 56*65c59e02SInna Palant``` 57*65c59e02SInna Palant```cpp 58*65c59e02SInna Palantstruct JDataHolder : JavaClass<JDataHolder> { 59*65c59e02SInna Palant static constexpr auto kJavaDescriptor = "Lcom/facebook/jni/DataHolder;"; 60*65c59e02SInna Palant // newInstance should be wrapped to ensure compile-time checking of call sites. 61*65c59e02SInna Palant static local_ref<JDataHolder> create(int i, std::string const& s) { 62*65c59e02SInna Palant // Constructor is looked up by argument types at *runtime*. 63*65c59e02SInna Palant return newInstance(i, s); 64*65c59e02SInna Palant } 65*65c59e02SInna Palant``` 66*65c59e02SInna Palant```cpp 67*65c59e02SInna Palant // Call-site in another file. 68*65c59e02SInna Palant static local_ref<JDataHolder> runConstructor( 69*65c59e02SInna Palant alias_ref<JClass> clazz) { 70*65c59e02SInna Palant // Call to ordinatry C++ function is checked at *compile time*. 71*65c59e02SInna Palant return JDataHolder::create(1, "hi"); 72*65c59e02SInna Palant } 73*65c59e02SInna Palant``` 74*65c59e02SInna Palant 75*65c59e02SInna Palant 76*65c59e02SInna Palant## Basic method usage (Java to C++ and C++ to Java) 77*65c59e02SInna Palant```java 78*65c59e02SInna Palant native void nativeVoidMethod(); 79*65c59e02SInna Palant static native void staticNativeVoidMethod(); 80*65c59e02SInna Palant void voidMethod() {} 81*65c59e02SInna Palant static void staticVoidMethod() {} 82*65c59e02SInna Palant``` 83*65c59e02SInna Palant```cpp 84*65c59e02SInna Palant public: 85*65c59e02SInna Palant // Java methods should usually be wrapped by C++ methods for ease-of-use. 86*65c59e02SInna Palant // (Most other examples in this document will inline these for brevity.) 87*65c59e02SInna Palant void callVoidMethod() { 88*65c59e02SInna Palant static const auto method = getClass()->getMethod<void()>("voidMethod"); 89*65c59e02SInna Palant // self() returns the raw JNI reference to this object. 90*65c59e02SInna Palant method(self()); 91*65c59e02SInna Palant } 92*65c59e02SInna Palant static void callStaticVoidMethod() { 93*65c59e02SInna Palant static const auto cls = javaClassStatic(); 94*65c59e02SInna Palant static const auto method = cls->getStaticMethod<void()>("staticVoidMethod"); 95*65c59e02SInna Palant method(cls); 96*65c59e02SInna Palant } 97*65c59e02SInna Palant 98*65c59e02SInna Palant // Native implementations of Java methods can be private. 99*65c59e02SInna Palant private: 100*65c59e02SInna Palant // For non-Hybrid objects, all JNI methods must be static on the C++ side 101*65c59e02SInna Palant // because only Hybrid objects can have C++ state. 102*65c59e02SInna Palant static void nativeVoidMethod( 103*65c59e02SInna Palant // All non-static methods receive "this" as a first argument. 104*65c59e02SInna Palant alias_ref<DocTests> thiz) { 105*65c59e02SInna Palant // Make sure we got the right object. 106*65c59e02SInna Palant assert(thiz->toString() == "instance of DocTests"); 107*65c59e02SInna Palant thiz->callVoidMethod(); 108*65c59e02SInna Palant } 109*65c59e02SInna Palant static void staticNativeVoidMethod( 110*65c59e02SInna Palant // All static methods receive the class as a first argument. 111*65c59e02SInna Palant alias_ref<JClass> clazz) { 112*65c59e02SInna Palant assert(clazz->toString() == "class com.facebook.jni.DocTests"); 113*65c59e02SInna Palant DocTests::callStaticVoidMethod(); 114*65c59e02SInna Palant } 115*65c59e02SInna Palant``` 116*65c59e02SInna Palant 117*65c59e02SInna Palant 118*65c59e02SInna Palant## Methods using Java primitives 119*65c59e02SInna Palant```java 120*65c59e02SInna Palant static native long addSomeNumbers(byte b, short s, int i); 121*65c59e02SInna Palant static long doubler(int i) { return i + i; } 122*65c59e02SInna Palant``` 123*65c59e02SInna Palant```cpp 124*65c59e02SInna Palant static jlong addSomeNumbers(alias_ref<JClass> clazz, jbyte b, jshort s, jint i) { 125*65c59e02SInna Palant static const auto doubler = clazz->getStaticMethod<jlong(jint)>("doubler"); 126*65c59e02SInna Palant jlong l = doubler(clazz, 4); 127*65c59e02SInna Palant return b + s + i + l; 128*65c59e02SInna Palant } 129*65c59e02SInna Palant 130*65c59e02SInna Palant``` 131*65c59e02SInna Palant Argument and return types can be in either JNI style or C++ style. 132*65c59e02SInna Palant 133*65c59e02SInna Palant | Java type | JNI types | 134*65c59e02SInna Palant | --- | --- | 135*65c59e02SInna Palant | `boolean` | `jboolean`, `bool` | 136*65c59e02SInna Palant | `byte` | `jbyte`, `int8_t` | 137*65c59e02SInna Palant | `char` | `jchar` | 138*65c59e02SInna Palant | `short` | `jshort`, `short`, `int16_t` | 139*65c59e02SInna Palant | `int` | `jint`, `int`, `int32_t` | 140*65c59e02SInna Palant | `long` | `jlong`, `int64_t` | 141*65c59e02SInna Palant | `float` | `jfloat`, `float` | 142*65c59e02SInna Palant | `double` | `jdouble`, `double` | 143*65c59e02SInna Palant 144*65c59e02SInna Palant 145*65c59e02SInna Palant## Methods using Java Strings 146*65c59e02SInna Palant```java 147*65c59e02SInna Palant // Java methods used by the C++ code below. 148*65c59e02SInna Palant static native String fancyCat(String s1, String s2); 149*65c59e02SInna Palant static native String getCString(); 150*65c59e02SInna Palant static String doubler(String s) { return s + s; } 151*65c59e02SInna Palant``` 152*65c59e02SInna Palant```cpp 153*65c59e02SInna Palant static std::string fancyCat( 154*65c59e02SInna Palant alias_ref<JClass> clazz, 155*65c59e02SInna Palant // Native methods can receive strings as JString (direct JNI reference) ... 156*65c59e02SInna Palant alias_ref<JString> s1, 157*65c59e02SInna Palant // or as std::string (converted to real UTF-8). 158*65c59e02SInna Palant std::string s2) { 159*65c59e02SInna Palant // Convert JString to std::string. 160*65c59e02SInna Palant std::string result = s1->toStdString(); 161*65c59e02SInna Palant // Java methods can receive and return JString ... 162*65c59e02SInna Palant static const auto doubler_java = clazz->getStaticMethod<JString(JString)>("doubler"); 163*65c59e02SInna Palant result += doubler_java(clazz, *s1)->toStdString(); 164*65c59e02SInna Palant // and also std::string (converted from real UTF-8). 165*65c59e02SInna Palant static const auto doubler_std = clazz->getStaticMethod<std::string(std::string)>("doubler"); 166*65c59e02SInna Palant result += doubler_std(clazz, s2)->toStdString(); 167*65c59e02SInna Palant // They can also receive const char*, but not return it. 168*65c59e02SInna Palant static const auto doubler_char = clazz->getStaticMethod<std::string(const char*)>("doubler"); 169*65c59e02SInna Palant result += doubler_char(clazz, s2.c_str())->toStdString(); 170*65c59e02SInna Palant // All 3 formats can be returned (std::string shown here, const char* below). 171*65c59e02SInna Palant return result; 172*65c59e02SInna Palant } 173*65c59e02SInna Palant 174*65c59e02SInna Palant static const char* getCString(alias_ref<JClass>) { 175*65c59e02SInna Palant // This string is converted to JString *after* getCString returns. 176*65c59e02SInna Palant // Watch your memory lifetimes. 177*65c59e02SInna Palant return "Watch your memory."; 178*65c59e02SInna Palant } 179*65c59e02SInna Palant``` 180*65c59e02SInna Palant 181*65c59e02SInna Palant 182*65c59e02SInna Palant## Working with arrays of primitives 183*65c59e02SInna Palant```java 184*65c59e02SInna Palant static native int[] primitiveArrays(int[] arr); 185*65c59e02SInna Palant``` 186*65c59e02SInna Palant```cpp 187*65c59e02SInna Palant static local_ref<JArrayInt> primitiveArrays( 188*65c59e02SInna Palant alias_ref<JClass> clazz, 189*65c59e02SInna Palant // JArrayX is available for all primitives. 190*65c59e02SInna Palant alias_ref<JArrayInt> arr) { 191*65c59e02SInna Palant size_t size = arr->size(); 192*65c59e02SInna Palant std::vector<jint> buffer(size + 1L); 193*65c59e02SInna Palant // Copy elements into native memory. 194*65c59e02SInna Palant arr->getRegion(0, size, buffer.data()); 195*65c59e02SInna Palant // Copy elements into fresh memory (returns unique_ptr<int[]>). 196*65c59e02SInna Palant auto elements = arr->getRegion(0, size); 197*65c59e02SInna Palant // Pin can eliminate the need for a copy. 198*65c59e02SInna Palant { 199*65c59e02SInna Palant auto pin = arr->pin(); 200*65c59e02SInna Palant for (size_t i = 0; i < pin.size(); i++) { 201*65c59e02SInna Palant // Can read and/or write pin[i]. 202*65c59e02SInna Palant buffer[size] += pin[i]; 203*65c59e02SInna Palant } 204*65c59e02SInna Palant } 205*65c59e02SInna Palant // Allocating a new array and copying data in. 206*65c59e02SInna Palant // (Data can also be assigned by writing to a pin.) 207*65c59e02SInna Palant auto ret = JArrayInt::newArray(size + 1); 208*65c59e02SInna Palant ret->setRegion(0, size + 1, buffer.data()); 209*65c59e02SInna Palant return ret; 210*65c59e02SInna Palant } 211*65c59e02SInna Palant``` 212*65c59e02SInna Palant 213*65c59e02SInna Palant 214*65c59e02SInna Palant## Working with arrays of objects 215*65c59e02SInna Palant```cpp 216*65c59e02SInna Palant static local_ref<JArrayClass<JString>> classArrays( 217*65c59e02SInna Palant alias_ref<JClass> clazz, 218*65c59e02SInna Palant alias_ref<JArrayClass<JDataHolder>> arr) { 219*65c59e02SInna Palant size_t size = arr->size(); 220*65c59e02SInna Palant local_ref<JArrayClass<JString>> ret = JArrayClass<JString>::newArray(size); 221*65c59e02SInna Palant for (int i = 0; i < size; ++i) { 222*65c59e02SInna Palant local_ref<JString> str = arr->getElement(i)->getStr(); 223*65c59e02SInna Palant ret->setElement(i, *str); 224*65c59e02SInna Palant } 225*65c59e02SInna Palant return ret; 226*65c59e02SInna Palant } 227*65c59e02SInna Palant``` 228*65c59e02SInna Palant 229*65c59e02SInna Palant 230*65c59e02SInna Palant## References 231*65c59e02SInna Palant 232*65c59e02SInna Palant ### `alias_ref<JFoo>` 233*65c59e02SInna Palant `alias_ref` is a non-owning reference, like a bare pointer. 234*65c59e02SInna Palant It is used almost exclusively for function arguments. 235*65c59e02SInna Palant 236*65c59e02SInna Palant ### `local_ref<JFoo>` 237*65c59e02SInna Palant `local_ref` is a ref-counted thread-specific pointer that is invalidated upon returning to Java. 238*65c59e02SInna Palant For variables used within a function, use `local_ref`. 239*65c59e02SInna Palant Most functions should return `local_ref` (and let the caller convert to a `global_ref` if necessary). 240*65c59e02SInna Palant 241*65c59e02SInna Palant ### `global_ref<JFoo>` 242*65c59e02SInna Palant `global_ref` is a ref-counted pointer. 243*65c59e02SInna Palant Use this for storing a reference to a Java object that may 244*65c59e02SInna Palant outlive the current call from Java into C++ 245*65c59e02SInna Palant (e.g. class member fields are usually global refs). 246*65c59e02SInna Palant You can create a new `global_ref` (from an `alias_ref`/`local_ref`) by calling `make_global`. 247*65c59e02SInna Palant```cpp 248*65c59e02SInna Palant static local_ref<JObject> convertReferences( 249*65c59e02SInna Palant alias_ref<JClass> clazz, 250*65c59e02SInna Palant alias_ref<JMyDerivedClass> derived) { 251*65c59e02SInna Palant local_ref<JMyDerivedClass> local_derived = make_local(derived); 252*65c59e02SInna Palant global_ref<JMyDerivedClass> global_derived = make_global(derived); 253*65c59e02SInna Palant // Store global_derived somewhere. 254*65c59e02SInna Palant return local_derived; 255*65c59e02SInna Palant } 256*65c59e02SInna Palant``` 257*65c59e02SInna Palant 258*65c59e02SInna Palant 259*65c59e02SInna Palant## Defining a nested class 260*65c59e02SInna Palant```java 261*65c59e02SInna Palantclass Outer { 262*65c59e02SInna Palant class Nested {} 263*65c59e02SInna Palant} 264*65c59e02SInna Palant``` 265*65c59e02SInna Palant```cpp 266*65c59e02SInna Palantstruct JNested : JavaClass<JNested> { 267*65c59e02SInna Palant static constexpr auto kJavaDescriptor = "Lcom/facebook/jni/Outer$Nested;"; 268*65c59e02SInna Palant static local_ref<JNested> create() { 269*65c59e02SInna Palant return newInstance(); 270*65c59e02SInna Palant } 271*65c59e02SInna Palant}; 272*65c59e02SInna Palant``` 273*65c59e02SInna Palant 274*65c59e02SInna Palant 275*65c59e02SInna Palant## Defining a child class 276*65c59e02SInna Palant```java 277*65c59e02SInna Palantclass MyBaseClass {} 278*65c59e02SInna Palantclass MyDerivedClass extends MyBaseClass {} 279*65c59e02SInna Palant``` 280*65c59e02SInna Palant```cpp 281*65c59e02SInna Palantstruct JMyBaseClass : JavaClass<JMyBaseClass> { 282*65c59e02SInna Palant static constexpr auto kJavaDescriptor = "Lcom/facebook/jni/MyBaseClass;"; 283*65c59e02SInna Palant}; 284*65c59e02SInna Palant 285*65c59e02SInna Palantstruct JMyDerivedClass : JavaClass<JMyDerivedClass, JMyBaseClass> { 286*65c59e02SInna Palant static constexpr auto kJavaDescriptor = "Lcom/facebook/jni/MyDerivedClass;"; 287*65c59e02SInna Palant}; 288*65c59e02SInna Palant 289*65c59e02SInna Palant``` 290*65c59e02SInna PalantThis will allow implicit casts from Derived to Base and explicit downcasts. 291*65c59e02SInna PalantWhen no base class is given, JObject will be used as the base. 292*65c59e02SInna Palant```cpp 293*65c59e02SInna Palant static void castReferences( 294*65c59e02SInna Palant alias_ref<JClass> clazz, 295*65c59e02SInna Palant alias_ref<JMyBaseClass> base) { 296*65c59e02SInna Palant // Just like raw pointers, upcasting is implicit. 297*65c59e02SInna Palant alias_ref<JObject> obj = base; 298*65c59e02SInna Palant // static_ref_cast is like C++ static_cast. No runtime checking is done. 299*65c59e02SInna Palant alias_ref<JMyDerivedClass> derived_1 = static_ref_cast<JMyDerivedClass>(base); 300*65c59e02SInna Palant // dynamic_ref_cast is like C++ dynamic_cast. 301*65c59e02SInna Palant // It will check that the runtime Java type is actually derived from the target type. 302*65c59e02SInna Palant try { 303*65c59e02SInna Palant alias_ref<JMyDerivedClass> derived_2 = dynamic_ref_cast<JMyDerivedClass>(base); 304*65c59e02SInna Palant (void)derived_2; 305*65c59e02SInna Palant } catch (const JniException& exn) { 306*65c59e02SInna Palant // Throws ClassCastException if the cast fails. 307*65c59e02SInna Palant throw; 308*65c59e02SInna Palant } 309*65c59e02SInna Palant``` 310*65c59e02SInna Palant 311*65c59e02SInna Palant 312*65c59e02SInna Palant## Getting and setting fields 313*65c59e02SInna Palant```cpp 314*65c59e02SInna Palant void getAndSetFields() { 315*65c59e02SInna Palant static const auto cls = javaClassStatic(); 316*65c59e02SInna Palant // Primitive fields. 317*65c59e02SInna Palant static const auto iField = cls->getField<jint>("i"); 318*65c59e02SInna Palant jint i = this->getFieldValue(iField); 319*65c59e02SInna Palant this->setFieldValue(iField, i + 1); 320*65c59e02SInna Palant // Object fields work for standard classes and your own JavaObject classes. 321*65c59e02SInna Palant static const auto sField = cls->getField<JString>("s"); 322*65c59e02SInna Palant // Object are returned as local refs ... 323*65c59e02SInna Palant local_ref<JString> s = this->getFieldValue(sField); 324*65c59e02SInna Palant // and can be set from any ref. 325*65c59e02SInna Palant this->setFieldValue(sField, make_jstring(s->toStdString() + "1").get()); 326*65c59e02SInna Palant // Static fields work the same, but getStaticField, getStaticFieldValue, 327*65c59e02SInna Palant // and setStaticFieldValue must all be called on the class object. 328*65c59e02SInna Palant static const auto someInstanceField = cls->getStaticField<JDataHolder>("someInstance"); 329*65c59e02SInna Palant auto inst = cls->getStaticFieldValue(someInstanceField); 330*65c59e02SInna Palant if (!inst) { 331*65c59e02SInna Palant // NOTE: Can't use cls here because it is declared const. 332*65c59e02SInna Palant getClass()->setStaticFieldValue(someInstanceField, self()); 333*65c59e02SInna Palant } 334*65c59e02SInna Palant } 335*65c59e02SInna Palant``` 336*65c59e02SInna Palant 337*65c59e02SInna Palant 338*65c59e02SInna Palant## Working with JObject and JClass 339*65c59e02SInna Palant```cpp 340*65c59e02SInna Palant static std::string showJObject( 341*65c59e02SInna Palant alias_ref<JClass> clazz, 342*65c59e02SInna Palant // JObject is the base class of all fbjni types. It corresponds to java.lang.Object. 343*65c59e02SInna Palant alias_ref<JObject> obj, 344*65c59e02SInna Palant alias_ref<JDataHolder> data) { 345*65c59e02SInna Palant local_ref<JClass> objClass = obj->getClass(); 346*65c59e02SInna Palant local_ref<JClass> dataClass = data->getClass(); 347*65c59e02SInna Palant local_ref<JClass> parent = dataClass->getSuperclass(); 348*65c59e02SInna Palant assert(isSameObject(parent, objClass)); 349*65c59e02SInna Palant assert(data->isInstanceOf(parent)); 350*65c59e02SInna Palant assert(objClass->isAssignableFrom(clazz)); 351*65c59e02SInna Palant std::string str = "data="; 352*65c59e02SInna Palant { 353*65c59e02SInna Palant // Acquires the object lock until this object goes out of scope. 354*65c59e02SInna Palant auto lock = data->lock(); 355*65c59e02SInna Palant // Calls Object.toString and converts to std::string. 356*65c59e02SInna Palant str += data->toString(); 357*65c59e02SInna Palant } 358*65c59e02SInna Palant // All JavaClass types have a `javaobject` typedef, which is their raw JNI type. 359*65c59e02SInna Palant static_assert(std::is_same<JObject::javaobject, jobject>::value, ""); 360*65c59e02SInna Palant static_assert(std::is_same<JClass::javaobject, jclass>::value, ""); 361*65c59e02SInna Palant static_assert(!std::is_same<JDataHolder::javaobject, jobject>::value, ""); 362*65c59e02SInna Palant static_assert(std::is_convertible<JDataHolder::javaobject, jobject>::value, ""); 363*65c59e02SInna Palant return str; 364*65c59e02SInna Palant } 365*65c59e02SInna Palant``` 366*65c59e02SInna Palant 367*65c59e02SInna Palant 368*65c59e02SInna Palant## Catching and throwing exceptions 369*65c59e02SInna Palant```cpp 370*65c59e02SInna Palant static void catchAndThrow( 371*65c59e02SInna Palant alias_ref<JClass> clazz) { 372*65c59e02SInna Palant try { 373*65c59e02SInna Palant clazz->getStaticMethod<void()>("doesNotExist"); 374*65c59e02SInna Palant assert(!"Exception wasn't thrown."); 375*65c59e02SInna Palant } catch (JniException& exn) { 376*65c59e02SInna Palant // JniException extends std::exception, so "catch (std::exception& exn)" also works. 377*65c59e02SInna Palant local_ref<JThrowable> underlying = exn.getThrowable(); 378*65c59e02SInna Palant const char* msg = exn.what(); 379*65c59e02SInna Palant // Throwing exceptions from C++ is fine. 380*65c59e02SInna Palant // They will be translated to an appropriate Java exception type. 381*65c59e02SInna Palant throw std::runtime_error(std::string() + "Caught '" + msg + "'"); 382*65c59e02SInna Palant } 383*65c59e02SInna Palant } 384*65c59e02SInna Palant``` 385*65c59e02SInna Palant 386*65c59e02SInna Palant 387*65c59e02SInna Palant## Working with boxed primitives 388*65c59e02SInna Palant```java 389*65c59e02SInna Palant static native Double scaleUp(Integer number); 390*65c59e02SInna Palant``` 391*65c59e02SInna Palant```cpp 392*65c59e02SInna Palant static local_ref<JDouble> scaleUp( 393*65c59e02SInna Palant alias_ref<JClass> clazz, 394*65c59e02SInna Palant alias_ref<JInteger> number) { 395*65c59e02SInna Palant // Boxed types exist for all Java primitive types. 396*65c59e02SInna Palant // Unbox with ->value() or ->intValue. 397*65c59e02SInna Palant jint unboxed = number->value(); 398*65c59e02SInna Palant jdouble scaled = unboxed * 1.5; 399*65c59e02SInna Palant // Box with autobox() or JDouble::valueOf. 400*65c59e02SInna Palant local_ref<JDouble> ret = autobox(scaled); 401*65c59e02SInna Palant return ret; 402*65c59e02SInna Palant } 403*65c59e02SInna Palant``` 404*65c59e02SInna Palant 405*65c59e02SInna Palant 406*65c59e02SInna Palant## Working with Iterables 407*65c59e02SInna Palant```java 408*65c59e02SInna Palant static native String concatMatches(List<Integer> values, Map<String, Integer> names); 409*65c59e02SInna Palant``` 410*65c59e02SInna Palant```cpp 411*65c59e02SInna Palant static std::string concatMatches( 412*65c59e02SInna Palant alias_ref<JClass> clazz, 413*65c59e02SInna Palant // Note that generic types are *not* checked against Java declarations. 414*65c59e02SInna Palant alias_ref<JList<JInteger>> values, 415*65c59e02SInna Palant alias_ref<JMap<JString, JInteger>> names) { 416*65c59e02SInna Palant int sum = 0; 417*65c59e02SInna Palant std::string ret; 418*65c59e02SInna Palant // Iterator and Iterable support C++ iteration. 419*65c59e02SInna Palant // Collection, List, and Set support iteration and ->size(). 420*65c59e02SInna Palant for (const auto& elem : *values) { 421*65c59e02SInna Palant sum += elem->value(); 422*65c59e02SInna Palant } 423*65c59e02SInna Palant // Maps iterate like C++ maps. 424*65c59e02SInna Palant for (const auto& entry : *names) { 425*65c59e02SInna Palant if (entry.second->value() == sum) { 426*65c59e02SInna Palant ret += entry.first->toStdString(); 427*65c59e02SInna Palant } 428*65c59e02SInna Palant } 429*65c59e02SInna Palant // This works if you build with C++17. 430*65c59e02SInna Palant // for (const auto& [key, value] : *names) { 431*65c59e02SInna Palant return ret; 432*65c59e02SInna Palant } 433*65c59e02SInna Palant``` 434*65c59e02SInna Palant 435*65c59e02SInna Palant 436*65c59e02SInna Palant## Building Collections 437*65c59e02SInna Palant```java 438*65c59e02SInna Palant static native Map<String, List<Integer>> buildCollections(); 439*65c59e02SInna Palant``` 440*65c59e02SInna Palant```cpp 441*65c59e02SInna Palant static local_ref<JMap<JString, JList<JInteger>>> buildCollections( 442*65c59e02SInna Palant alias_ref<JClass> clazz) { 443*65c59e02SInna Palant auto primes = JArrayList<JInteger>::create(); 444*65c59e02SInna Palant primes->add(autobox(2)); 445*65c59e02SInna Palant primes->add(autobox(3)); 446*65c59e02SInna Palant auto wrapper = JHashMap<JString, JList<JInteger>>::create(); 447*65c59e02SInna Palant wrapper->put(make_jstring("primes"), primes); 448*65c59e02SInna Palant return wrapper; 449*65c59e02SInna Palant } 450*65c59e02SInna Palant``` 451*65c59e02SInna Palant 452*65c59e02SInna Palant 453*65c59e02SInna Palant## Transferring data with direct ByteBuffer 454*65c59e02SInna Palant```java 455*65c59e02SInna Palant static native ByteBuffer transformBuffer(ByteBuffer data); 456*65c59e02SInna Palant static void receiveBuffer(ByteBuffer buffer) { 457*65c59e02SInna Palant assertThat(buffer.capacity()).isEqualTo(2); 458*65c59e02SInna Palant assertThat(buffer.get(0)).isEqualTo((byte)2); 459*65c59e02SInna Palant assertThat(buffer.get(1)).isEqualTo((byte)3); 460*65c59e02SInna Palant } 461*65c59e02SInna Palant @Test 462*65c59e02SInna Palant public void testByteBuffers() { 463*65c59e02SInna Palant ByteBuffer data = ByteBuffer.allocateDirect(2); 464*65c59e02SInna Palant data.put(new byte[] {1, 2}); 465*65c59e02SInna Palant ByteBuffer transformed = transformBuffer(data); 466*65c59e02SInna Palant receiveBuffer(transformed); 467*65c59e02SInna Palant } 468*65c59e02SInna Palant``` 469*65c59e02SInna Palant```cpp 470*65c59e02SInna Palant#include <fbjni/ByteBuffer.h> 471*65c59e02SInna Palant``` 472*65c59e02SInna Palant```cpp 473*65c59e02SInna Palant static local_ref<JByteBuffer> transformBuffer( 474*65c59e02SInna Palant alias_ref<JClass> clazz, 475*65c59e02SInna Palant alias_ref<JByteBuffer> data) { 476*65c59e02SInna Palant // Direct ByteBuffers are an efficient way to transfer bulk data between Java and C++. 477*65c59e02SInna Palant if (!data->isDirect()) { 478*65c59e02SInna Palant throw std::runtime_error("Argument is not a direct buffer."); 479*65c59e02SInna Palant } 480*65c59e02SInna Palant // Transform data into a local buffer. 481*65c59e02SInna Palant std::vector<uint8_t> buffer(data->getDirectSize()); 482*65c59e02SInna Palant uint8_t* raw_data = data->getDirectBytes(); 483*65c59e02SInna Palant for (size_t i = 0; i < buffer.size(); ++i) { 484*65c59e02SInna Palant buffer[i] = raw_data[i] + 1; 485*65c59e02SInna Palant } 486*65c59e02SInna Palant // Wrap our data in a buffer and pass to Java. 487*65c59e02SInna Palant // Note that the buffer *directly* references our memory. 488*65c59e02SInna Palant local_ref<JByteBuffer> wrapper = JByteBuffer::wrapBytes(buffer.data(), buffer.size()); 489*65c59e02SInna Palant static const auto receiver = clazz->getStaticMethod<void(alias_ref<JByteBuffer>)>("receiveBuffer"); 490*65c59e02SInna Palant receiver(clazz, wrapper); 491*65c59e02SInna Palant // We can create a new buffer that owns its own memory and safely return it. 492*65c59e02SInna Palant local_ref<JByteBuffer> ret = JByteBuffer::allocateDirect(buffer.size()); 493*65c59e02SInna Palant std::memcpy(ret->getDirectBytes(), buffer.data(), buffer.size()); 494*65c59e02SInna Palant return ret; 495*65c59e02SInna Palant } 496*65c59e02SInna Palant``` 497*65c59e02SInna Palant 498*65c59e02SInna Palant 499