keep your arms and legs inside the platform
TRANSCRIPT
![Page 1: Keep Your Arms and Legs Inside the Platform](https://reader036.vdocuments.us/reader036/viewer/2022070601/588a5b171a28ab7a768b54f1/html5/thumbnails/1.jpg)
Keep Your Hands and Feet Inside the Platform
James BordenSoftware Engineer, Couchbase
![Page 2: Keep Your Arms and Legs Inside the Platform](https://reader036.vdocuments.us/reader036/viewer/2022070601/588a5b171a28ab7a768b54f1/html5/thumbnails/2.jpg)
I AM JAMES BORDENI am on the Internet as @borrrden. I develop Couchbase Lite .NET
![Page 3: Keep Your Arms and Legs Inside the Platform](https://reader036.vdocuments.us/reader036/viewer/2022070601/588a5b171a28ab7a768b54f1/html5/thumbnails/3.jpg)
WHAT IS ON THE AGENDA◇What is P/Invoke?◇Example I am working on◇Best Practices I have learned
![Page 4: Keep Your Arms and Legs Inside the Platform](https://reader036.vdocuments.us/reader036/viewer/2022070601/588a5b171a28ab7a768b54f1/html5/thumbnails/4.jpg)
WHAT IS P/INVOKE?Let’s start with that…
![Page 5: Keep Your Arms and Legs Inside the Platform](https://reader036.vdocuments.us/reader036/viewer/2022070601/588a5b171a28ab7a768b54f1/html5/thumbnails/5.jpg)
Platform Invocation Services, commonly referred to as P/Invoke, is a feature of Common Language Infrastructure implementations, like Microsoft's Common Language Runtime, that enables managed code to call native code.
![Page 6: Keep Your Arms and Legs Inside the Platform](https://reader036.vdocuments.us/reader036/viewer/2022070601/588a5b171a28ab7a768b54f1/html5/thumbnails/6.jpg)
WELL WHY DO THAT?◇You have a bazillion lines of legacy code
◇You want to share code between languages (as opposed to architectures)
◇You love pointers (just kidding)
![Page 7: Keep Your Arms and Legs Inside the Platform](https://reader036.vdocuments.us/reader036/viewer/2022070601/588a5b171a28ab7a768b54f1/html5/thumbnails/7.jpg)
ELABORATE ON POINT 2◇At Couchbase, we have three versions of our Lite product (Objective-C, Java, .NET)
◇Historically we have developed features for all of them side-by-side
◇That’s a pretty big waste of time
![Page 8: Keep Your Arms and Legs Inside the Platform](https://reader036.vdocuments.us/reader036/viewer/2022070601/588a5b171a28ab7a768b54f1/html5/thumbnails/8.jpg)
WELCOME TO THE JUNGLE…I MEAN FORESTEnter into ForestDB and CBForest
![Page 9: Keep Your Arms and Legs Inside the Platform](https://reader036.vdocuments.us/reader036/viewer/2022070601/588a5b171a28ab7a768b54f1/html5/thumbnails/9.jpg)
FOREST WHAT?◇ForestDB: Couchbase’s own Key-Value storage engine written in C
◇CBForest: A C++ layer adding Couchbase Lite specific functionality to ForestDB (document creation, view indexing & querying, etc)
![Page 10: Keep Your Arms and Legs Inside the Platform](https://reader036.vdocuments.us/reader036/viewer/2022070601/588a5b171a28ab7a768b54f1/html5/thumbnails/10.jpg)
THIS PRESENTS A PROBLEM
We now have unmanaged code being developed by a separate team that need to be utilized by our product. Initially it was easily done with Objective-C++, but what about Java and .NET?
![Page 11: Keep Your Arms and Legs Inside the Platform](https://reader036.vdocuments.us/reader036/viewer/2022070601/588a5b171a28ab7a768b54f1/html5/thumbnails/11.jpg)
HOW CAN I DO IT?Let’s start our journey
![Page 12: Keep Your Arms and Legs Inside the Platform](https://reader036.vdocuments.us/reader036/viewer/2022070601/588a5b171a28ab7a768b54f1/html5/thumbnails/12.jpg)
NO C++ ALLOWED
*C++ has no standard ABI (Application Binary Interface) and so every function signature changes based on which compiler is used to compile it. This makes C++ an insane and counterproductive idea for P/Invoke. Can you read this small text in the back? No? Good :-p
![Page 13: Keep Your Arms and Legs Inside the Platform](https://reader036.vdocuments.us/reader036/viewer/2022070601/588a5b171a28ab7a768b54f1/html5/thumbnails/13.jpg)
HERE IS AN API CALL IN C/** Opaque handle to an opened database. */typedef struct c4Database C4Database;
enum C4DatabaseFlags : uint32_t { kC4DB_Create = 1, /**< Create the file if it doesn't exist */ kC4DB_ReadOnly = 2, /**< Open file read-only */ kC4DB_AutoCompact = 4 /**< Enable auto-compaction */}
typedef struct { const void *buf; size_t size;} C4Slice;
typedef struct { int32_t domain; int32_t code;} C4Error;
C4Database* c4db_open(C4Slice path, C4DatabaseFlags flags, C4Error *outError);
![Page 14: Keep Your Arms and Legs Inside the Platform](https://reader036.vdocuments.us/reader036/viewer/2022070601/588a5b171a28ab7a768b54f1/html5/thumbnails/14.jpg)
HERE IS ONE WAY TO DO IT
IntPtr c4db_open(IntPtr path, int flags, out IntPtr outError);
![Page 15: Keep Your Arms and Legs Inside the Platform](https://reader036.vdocuments.us/reader036/viewer/2022070601/588a5b171a28ab7a768b54f1/html5/thumbnails/15.jpg)
DON’T DO THAT
*You will spend your entire life with bugs related to putting the wrong type into your method. FOR SHAME!
![Page 16: Keep Your Arms and Legs Inside the Platform](https://reader036.vdocuments.us/reader036/viewer/2022070601/588a5b171a28ab7a768b54f1/html5/thumbnails/16.jpg)
OK, WHAT SHOULD I DO?◇Mock the structure layout in C#
◇Use ‘unsafe’ code blocks◇The code will look almost identical to its C counterpart
![Page 17: Keep Your Arms and Legs Inside the Platform](https://reader036.vdocuments.us/reader036/viewer/2022070601/588a5b171a28ab7a768b54f1/html5/thumbnails/17.jpg)
WHAT WILL HAPPEN?◇Everything must be copied between managed and unmanaged*
◇The only inherently copyable types are primitives and one dimensional arrays of primitives
*This is also true of passing things to functions in C, and is precisely why pointers exist in the first place.
![Page 18: Keep Your Arms and Legs Inside the Platform](https://reader036.vdocuments.us/reader036/viewer/2022070601/588a5b171a28ab7a768b54f1/html5/thumbnails/18.jpg)
HERE IS THE PREVIOUS API IN C#/** Opaque handle to an opened database. */public struct C4Database { }
[Flags]public enum C4DatabaseFlags : uint { Create = 1, ReadOnly = 2, AutoCompact = 4}public unsafe struct C4Slice { public UIntPtr size; // Because size_t is not a fixed size public void* buf; // ZOMG, C# pointer!!}public struct C4Error { public int domain; public int code;} C4Error;
public static extern C4Database* c4db_open(C4Slice path, C4DatabaseFlags flags, C4Error *outError);
![Page 19: Keep Your Arms and Legs Inside the Platform](https://reader036.vdocuments.us/reader036/viewer/2022070601/588a5b171a28ab7a768b54f1/html5/thumbnails/19.jpg)
SOME NOTES◇All types are made up of purely blittable types (pointers are simply 32 or 64-bit integers)
◇The runtime is smart enough to figure out what you want (compare to Java)
![Page 20: Keep Your Arms and Legs Inside the Platform](https://reader036.vdocuments.us/reader036/viewer/2022070601/588a5b171a28ab7a768b54f1/html5/thumbnails/20.jpg)
WHAT ABOUT SENDING THINGS TO C?◇The garbage collector is non-deterministic and can run at any given time
◇When a garbage collection occurs, all pointers to managed objects are potentially invalidated
![Page 21: Keep Your Arms and Legs Inside the Platform](https://reader036.vdocuments.us/reader036/viewer/2022070601/588a5b171a28ab7a768b54f1/html5/thumbnails/21.jpg)
PINNING TO THE RESCUEpublic struct C4String : IDisposable { private GCHandle _handle; // Stores the UTF-8 bytes in a pinned location
public C4String(string s) { _handle = new GCHandle(); if(s != null) { var bytes = Encoding.UTF8.GetBytes(s); _handle = GCHandle.Alloc(bytes, GCHandleType.Pinned); } }}
public unsafe void SendBytes(byte[] bytes) { fixed(byte* ptr = bytes) { SendBytesNative(ptr); // ptr guaranteed not to move }}
![Page 22: Keep Your Arms and Legs Inside the Platform](https://reader036.vdocuments.us/reader036/viewer/2022070601/588a5b171a28ab7a768b54f1/html5/thumbnails/22.jpg)
WHAT ABOUT SENDING THINGS TO C#?
◇C# APIs expect C# managed objects
◇C knows nothing about C#
![Page 23: Keep Your Arms and Legs Inside the Platform](https://reader036.vdocuments.us/reader036/viewer/2022070601/588a5b171a28ab7a768b54f1/html5/thumbnails/23.jpg)
CATCH THINGS AT THE BOUNDARY
public unsafe string GetString(){ byte *nativeObject = GetNativeObject(); int size = GetNativeSize(); return new string((sbyte*)nativeObject, size, Encoding.UTF8);}
![Page 24: Keep Your Arms and Legs Inside the Platform](https://reader036.vdocuments.us/reader036/viewer/2022070601/588a5b171a28ab7a768b54f1/html5/thumbnails/24.jpg)
WHERE TO LOOK FOR NATIVE CODE?
◇The DllImport (DLL Import) attribute and extern keyword indicate native functions
◇The runtime searches a standardized search path for a given DLL
![Page 25: Keep Your Arms and Legs Inside the Platform](https://reader036.vdocuments.us/reader036/viewer/2022070601/588a5b171a28ab7a768b54f1/html5/thumbnails/25.jpg)
DLL? YOU MEAN WINDOWS ONLY?
◇Mono, and by extension Xamarin, provides DllMapping functionality to allow you to specify an alternate native library in place of a .dll filename
![Page 26: Keep Your Arms and Legs Inside the Platform](https://reader036.vdocuments.us/reader036/viewer/2022070601/588a5b171a28ab7a768b54f1/html5/thumbnails/26.jpg)
EXAMPLE
<configuration> <dllmap dll=”msvcrt.dll" target="libc.dylib" os="osx" /></configuration>
// If the names do not match, you need to manually specify the entry point// This function will look for msvcrt.dll on Windows, and libc.dylib// on OS X[DllImport(“msvcrt.dll”, CallingConvention=CallingConvention.Cdecl, EntryPoint=“memcpy”]public static extern int MemoryCopy(void* dest, void* src, UIntPtr count);
![Page 28: Keep Your Arms and Legs Inside the Platform](https://reader036.vdocuments.us/reader036/viewer/2022070601/588a5b171a28ab7a768b54f1/html5/thumbnails/28.jpg)
CREDITS
Special thanks to all the people who made and released these awesome resources for free:◇ Presentation template by SlidesCarnival◇ Photographs by Unsplash &
Death to the Stock Photo (license)