Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Modern C++ on Android and beyond (cross platform)

Modern C++ on Android and beyond (cross platform)

C++ is known for its efficiency, but did you know that modern C++ (C++11 and higher) is also fun to work with? For cross platform development, there’s hardly a better choice when it comes to performance sensitive code. And especially on Android with its VM based architecture, C++ outruns any Java/Kotlin based implementation by far.

Cross-platform development with C++ is not restricted to the mobile world. With the right setup, you can boost your productivity by running your code on the desktop with minimal turnaround times. Basically, there’s no Gradle build you need to wait for.

This talk gives a 360° view what it is like to have C++ as part of your app; it covers topics like NDK, CMake, JNI, standard template libraries, native unit testing, and IDEs to give you a comprehensive overview.

greenrobot

June 26, 2018
Tweet

More Decks by greenrobot

Other Decks in Programming

Transcript

  1. Cross platform approaches (a selection) • Web, PWAs • Web

    inside native containers Cordova (PhoneGap), Electron • JavaScript & Native UI React Native • Java, Kotlin, etc. Compiles to JVM, Kotlin: JS & native • Native native, close to the metal C/C++
  2. The case for C/C++ Performance especially on Android… Plus (plus):

    • Runs everywhere • Lots of libraries • Existing code • Close to OS & hardware
  3. Use cases for C++ cross platform • UI: probably not

    System specific look and feel Qt/OpenGL for custom UIs/games • OS specific calls: nope APIs more accessible from default language • App logic / special use cases: yepp Computation intensive; e.g. crypto "Business logic"; optional: networking&storage
  4. Web Assembly (Wasm) • Run native code in the browser

    Chrome, Firefox, Safari, and Edge • Emscriptem C/C++ Toolchain for Wasm (and js.asm) • Sandboxed • Inter-op with JavaScript • Access to browser functionality • Best support for C/C++
  5. CPU / Memory • CPU speaks only machine language CPU

    dependent • Programming languages translated for CPU Compilers or interpreters • CPU registers Used for data operations (, local variables, etc.) • Memory Data accessed by addresses
  6. Under the hood: 1) A class class NamedCounter { String

    name; int count; // Constructor, etc. void increase() { count++; } }
  7. Under the hood: 2) An object NamedCounter bugCounter = new

    NamedCounter("bugs"); bugCounter.increase(); bugCounter name: "bugs" count: 0 bugCounter name: "bugs" count: 1
  8. Under the hood: 3) object memory bugCounter name: "bugs" count:

    1 { Memory View Address Contents 0x12340000 00 00 00 00 0x12340004 44 44 44 44 0x12340008 01 00 00 00 0x1234000B AF FE CA FE … 0x44444444 62 00 74 00 0x44444448 67 00 73 00 0x4444444B 00 00 00 00 'b' 'u' 'g' 's'
  9. Under the hood: 4) machine code (asm) bugCounter name: "bugs"

    count: 1 { Address Contents 0x12340000 00 00 00 00 0x12340004 44 44 44 44 0x12340008 01 00 00 00 0x1234000B AF FE CA FE // High level count++; LOAD32 0x1234008, REG_A ADD32 REG_A, 1 STORE32 REG_A, 0x1234008 02 2
  10. Pointers • Variable pointing into memory (Content is a memory

    address) • Pointer arithmetic Use math to navigate through memory • "Safest" way to get into trouble Bad pointers, pointing to wrong address ~100x times worse than NullPointerException
  11. Allocating C++ objects auto joe = new Person("Joe"); // joe

    is a pointer: Person* // no GC, no auto release pool, etc. // so how do we get rid of joe1? delete joe; // we have to do it ourselves Person* aPerson = findRandomPerson(); // Unclear who should delete aPerson 1) Names are products of the author’s imagination or are used fictitiously. Any resemblance to actual persons, living or dead, is entirely coincidental.
  12. Who's the owner? Who is responsible to delete joe? joe:

    Person jane: Person (parent) BigCoorp: Employer NewYork: City
  13. C++11 Smart Pointers • They define ownership & clean up

    unique_ptr<Person> person = findRandomPerson(); // 1 person = findRandomPerson(); // 2 • Shared ownership with shared_ptr Allow multiple references to an object Object gets cleaned up when no more refs • Fix most pointer problems Safer & more convenient
  14. RAII • "Resource acquisition is initialization" (part of the WAE

    suite – worst acronyms ever) • Best explained by example void helloRaii() { lock_guard<mutex> lock(fileMutex); ofstream file("hello.txt"); file << "Hello world"; // may ex }
  15. Lambdas • "Function pointers deluxe" • Captures variables By reference

    or copy • Can be templated Inlined by the compiler: performance++
  16. Some std::stuff • C++11 STL (Standard Template Library) • Threads

    are super simple Thread creation, joining, locking Atomic types • Containers (collections) List  vector, HashMap  unordered_map And many more • Btw, iterating over collections is quite nice: for (auto& user: userVector) { … }
  17. JNI – Java Native Interface • Call into native C

    methods • C API to access Java world E.g. reflection-like API to access data • Special signature for C methods • javah: creates C headers based on Java src • Pass native resources as a long handle E.g. pointers to C++ objects  Java long
  18. Implementing native methods • C style methods Pass handles and

    cast to C++ objects • JNIEnv always passed: JNI god object Create Java object, get data from J. objects • Method params: Java  C type int  jint String  jstring Object (of any class)  jobject
  19. JNI Helpers using RAII (self-made) class JniString { JniString(JNIEnv* env,

    jstring string) : env_(env), string_(string) { chars_ = env->GetStringUTFChars(string, nullptr); } ~JniString() { env_->ReleaseStringUTFChars(string_, chars_); } operator const char*() const { return chars_; } }
  20. Using our JNI RAII helper void foo(const char* text); //

    simplified void _jni(JNIEnv* env, jstring stringFromJava) { JniString myString(env, stringFromJava); foo(myString); }
  21. JNI Challenges • No fast, C-ish access to Java object

    data Get & set fields using their name • JNI calls are somewhat expensive Must be considered when defining the API • Freeing native resources GC makes no guarantees E.g. close() and finalization; finalizer thread
  22. Exceptions: C++ vs. Java • C++ may throw exceptions Would

    kill the Java process • Catch and raise Java exception • JNI does not throw, but raise an exception Code flow continues(!) JNI method must return regularly Must check if exception already occurred
  23. Fun quizz: JNI corner case static native nativeFoo(long handle); public

    void bar() { nativeFoo(handle); } daObject.bar(); // What could possibly go wrong?
  24. JNI alternatives • Technicaly, JNI is the only bridge to

    C • More convenient solutions based on JNI • JNA (Java Native Access) Define a Java interface for a native library Interface implementation by JNA Automatically converts types • Djinni by Dropbox IDL to define records, interfaces, and enums Generates C++, Java, and Objective-C sources
  25. NDK: What's in it? • JNI • C/C++ compiler: clang

    gcc will be removed in NDK 18 • Cross compiles to supported ABIs ARMv7, ARM64, x86, x64 (discontinued: ARMv5, MIPS) • clang 6.0.2 (NDK 17b) C++17
  26. NDK Native APIs • Only greenlit APIs may be used

    No equivalent for Java APIs • APIs OK for Level 14 Android Logging, Zlib, OpenGL/MAX, Bitmaps • New APIs level 26 and 27 Camera, audio, shared mem., neural networks
  27. CMake • What Gradle is for Android devs, CMake is

    for C++ devs (but without the waits) • Uses make or Ninja E.g. CMake creates makefiles • CMakeLists.txt Specify sources, configuration, outputs
  28. Sharing large data amounts • Passing bytes around is fine

    E.g. image data, compression in/output, etc. • jobject is not cool for C++ • C++ objects/structs not to be touched by Java • String encoded exchange? E.g. JSON/XML? • [Protocol | Flat] Buffers, Cap'n Proto "Objects as bytes" • Or a database… ;-)
  29. Project template: native-lib.cpp #include <jni.h> #include <string> extern "C" JNIEXPORT

    jstring JNICALL Java_io_objectbox_hellojni_MainActivity_stringFromJNI (JNIEnv* env, jobject /* this */) { std::string hello = "Hello world++"; return env->NewStringUTF(hello.c_str()); }
  30. C++17 • C++14 was a smaller release • C++17 with

    lots of language updates, e.g. decomposition tuple<T1,T2,T3> fun() {return {a,b,c}; } auto [x,y,z] = fun(); // T1 x, T2 y, T3 z • Important STL upgrades Primitives: optional, any, string_view File system, parallel algorithms
  31. C++ and iOS (macOS, watchOS, etc.) • No JNI-like interface

    {Insert YES! STRIKE! VICTORY! memes here} • Objective-C(++) simply allows C++ • Swift cannot call C++ (yet?) • Swift can all Objective-C (not ObjC++) ObjC(++) wrappers needed with ObjC protocol
  32. Testing C++ code • Cross platform advantage: desktop tests •

    Unit testing: e.g. Google Test, Catch2 • Usually tests finish before a JVM even starts • ASan (or Valgrind) to avoid memory errors Catches bad pointers etc. before it gets ugly • CI highly recommended • Safety net to keep dev productivity up