%title: Writing C interfaces in Julia - JuliaCon 2019 %author: kdheepak %date: 2019-07-24
-> Dheepak Krishnamurthy <-
-> July 24th <-
-> JuliaCon 2019, Baltimore, MD <-
-> Slides <-
- Python, Julia
- Written interfaces in Python and Julia using the C ABI
- 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
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.
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
- Native machine code
- Typically loaded at run-time
- Provides an Application Binary Interface (ABI)
#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
import Libdl
lib = Libdl.dlopen("libexample1c.dylib") # must be called during initialization phase
@show ccall(:getInteger, Cint, ())
- interface between two binary program modules
- defines how data structures or computational routines are accessed in machine code
- low-level
- hardware-dependent format
- 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 containsdlopen
,dlsym
,dlclose
,dlerror
functions
#include "user.h"
struct User * createUser(char *);
int main() {
struct User * user = createUser("kdheepak");
free(user);
}
struct User {
char * username;
};
struct User * createUser(char * username) {
struct User * user = malloc(sizeof(struct User));
user->username = username;
return user;
}
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;
}
struct User {
char * username;
};
struct User * createUser(char * username) {
struct User * user = malloc(sizeof(struct User));
user->username = username;
return user;
}
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;
}
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
void print_hello(std::string arg) {
std::cout << "hello " << arg << std::endl;
}
#ifdef __cplusplus
extern "C" {
#endif
...
#ifdef __cplusplus
}
#endif
// 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
- Let
ccall
handle converts - The Julia manual documentation is great!
- Clang.jl can parse C and C++ header files
- Can be used to generate Julia
ccall
code.
- Wrap functions generated in Julia
- Wrapping functions allows for more Julian interface
juliac.jl -vas hello.jl
Thanks to @staticfloat for reviewing my slides!