Skip to content

Latest commit

 

History

History
399 lines (274 loc) · 7.64 KB

File metadata and controls

399 lines (274 loc) · 7.64 KB

%title: Writing C interfaces in Julia - JuliaCon 2019 %author: kdheepak %date: 2019-07-24

-> Why writing C interfaces in Julia is so easy* <-

-> Dheepak Krishnamurthy <-

-> July 24th <-

-> JuliaCon 2019, Baltimore, MD <-

-> Slides <-


-> About Me <-

  • Python, Julia
  • Written interfaces in Python and Julia using the C ABI

-> What this talk is about <-

  • What C interfaces are and how they work
  • How to use them in Julia using ccall
  • Best practices for:
    • for writing/building C interfaces
    • for writing Julia code

-> What are C interfaces? <-


What is a shared library?

Technique for placing library functions into a single unit that can be shared by multiple processes at run time

  • A shared library or shared object
    • .so on Linux
    • .dylib on Apple
    • .dll on Windows

Note: -fPIC allows code to be located at any virtual address at run time.


Shared library source code

int GLOBAL_INTEGER;

int getInteger() {
  return GLOBAL_INTEGER;
}

void setInteger(int integer) {
  GLOBAL_INTEGER = integer;
}

int add(int integer) {
  return GLOBAL_INTEGER + integer;
}
clang -dynamiclib -fPIC c/example1.c -o o/liblibrary_default_cc_example1c.dylib
gcc -shared -fPIC c/example1.c -o o/liblibrary_default_cc_example1c.so
nm o/liblibrary_default_cc_example1c.dylib
0000000000001000 S _GLOBAL_INTEGER
0000000000000fa0 T _add
0000000000000f70 T _getInteger
0000000000000f80 T _setInteger
                 U dyld_stub_binder

Shared libraries

  • Native machine code
  • Typically loaded at run-time
  • Provides an Application Binary Interface (ABI)

Run time loading

#include <dlfcn.h>
#include <stdio.h>

int main() {

    void * lib;
    lib = dlopen("o/liblibrary_default_cc_example1c.dylib", RTLD_GLOBAL);

    int (*funcp)(void);

    funcp = dlsym(lib, "getInteger");

    if (funcp != NULL) {
        int x = (*funcp)();
        printf("x = %d\n", x);
    }

    dlclose(lib);
}
cc c/main o/main
julia julia/main.jl

Load library into Julia

import Libdl

lib = Libdl.dlopen("libexample1c.dylib") # must be called during initialization phase

Call function from Julia

@show ccall(:getInteger, Cint, ())

Application Binary Interfaces (ABI)

  • interface between two binary program modules
  • defines how data structures or computational routines are accessed in machine code
  • low-level
  • hardware-dependent format

Application Binary Interfaces (ABI)

  • ABI implemented for each platform
  • Various calling conventions implemented in almost every language out there
    • Order to put parameters on the call stack
    • cdecl, stdcall, syscall, pascal and more
  • dlopen API contains dlopen, dlsym, dlclose, dlerror functions

-> API and ABI <-


API and ABI

#include "user.h"

struct User * createUser(char *);

int main() {

    struct User * user = createUser("kdheepak");
    free(user);

}


API changes [1]

struct User {
  char * username;
};

struct User * createUser(char * username) {
    struct User * user = malloc(sizeof(struct User));
    user->username = username;
    return user;
}

API changes [2]

struct User {
  char * username;
  int id;
};

struct User * createUser(char * username, int id) {
    struct User * user = malloc(sizeof(struct User));
    user->username = username;
    user->id = id;
    return user;
}

ABI changes [1]

struct User {
  char * username;
};

struct User * createUser(char * username) {
    struct User * user = malloc(sizeof(struct User));
    user->username = username;
    return user;
}

ABI changes [2]

struct User {
  int id;
  char * username;
};

struct User * createUser(char * username) {
    struct User * user = malloc(sizeof(struct User));
    user->id = 0;
    user->username = username;
    return user;
}

-> Tips for writing C code <-


Initialization and clean up

void __attribute__ ((constructor)) initLibrary(void) {
    //
    // Function that is called when the library is loaded
    //
    printf("Library is initialized\n");
    GLOBAL_INTEGER = 0;
}
void __attribute__ ((destructor)) cleanUpLibrary(void) {
    //
    // Function that is called when the library is closed.
    //
    printf("Library is exited\n");
}
make example2.dylib

Difference between C++ compilers

void print_hello(std::string arg) {

    std::cout << "hello " << arg << std::endl;

}

Guards

#ifdef __cplusplus

extern "C" {

#endif

...

#ifdef __cplusplus

}

#endif

Visibility

// Define EXPORTED for any platform
#if defined _WIN32 || defined __CYGWIN__
  #ifdef WIN_EXPORT
    // Exporting...
    #ifdef __GNUC__
      #define EXPORTED __attribute__ ((dllexport))
    #else
      #define EXPORTED __declspec(dllexport) // Note: actually gcc seems to also supports this syntax.
    #endif
  #else
    #ifdef __GNUC__
      #define EXPORTED __attribute__ ((dllimport))
    #else
      #define EXPORTED __declspec(dllimport) // Note: actually gcc seems to also supports this syntax.
    #endif
  #endif
#else
  #if __GNUC__ >= 4
    #define EXPORTED __attribute__ ((visibility ("default")))
  #else
    #define EXPORTED
  #endif
#endif

-> Tips for writing Julia code <-


Julia and C


Memory management


Clang.jl

  • Clang.jl can parse C and C++ header files
  • Can be used to generate Julia ccall code.

Two levels of wrapping in Julia

  • Wrap functions generated in Julia
  • Wrapping functions allows for more Julian interface

Julia Package Compiler

juliac.jl -vas hello.jl

-> Questions? <-

Thanks to @staticfloat for reviewing my slides!