This shows how to make a communication bridge between C++ and LispWorks using Foreign Language Interface (FLI).
#|
> lispworks-7-0-0-amd64-darwin -build lisp-to-c.lisp
|#
(require "delivery")
(defvar *mailbox* (mp:make-mailbox))
(fli:define-c-struct event
(num :long)
(msg (:reference :ef-mb-string)))
(fli:define-foreign-callable
("initialize" :result-type :void)
()
(fli:register-module "libc_to_lisp.dylib"))
(fli:define-foreign-callable
("add_number" :result-type :long)
((x :long) (y :long))
(+ x y))
(fli:define-foreign-callable
("spawn_child_process" :result-type :void)
()
(mp:process-run-function
"test"
'(:mailbox t)
#'(lambda ()
(dotimes (x 10)
(format t "Hello ~D~%" x)))))
(fli:define-foreign-callable
("event_listener" :result-type :void)
()
(mp:process-run-function
"test"
'()
#'(lambda ()
(let ((i 0))
(loop for item = (mp:mailbox-read *mailbox*) do
(let ((obj (make-event i item)))
(on-callback-1 item)
(fli:with-foreign-slots (num msg) obj
(on-callback-2 obj))))))))
(fli:define-foreign-callable
("post_event" :result-type :void)
((obj (:reference :ef-mb-string
:lisp-to-foreign-p nil
:foreign-to-lisp-p t)))
(mp:mailbox-send *mailbox* obj))
(fli:define-foreign-function
(make-event "make_event" :source)
((num :long)
(msg (:reference-pass :ef-mb-string)))
:result-type (:pointer event)
:language :ansi-c
:calling-convention :cdecl)
(fli:define-foreign-function
(on-callback-1 "on_callback_1" :source)
((msg (:reference-pass :ef-mb-string)))
:result-type :void
:language :ansi-c
:calling-convention :cdecl)
(fli:define-foreign-function
(on-callback-2 "on_callback_2" :source)
((msg (:pointer (:struct event))))
:result-type :void
:language :ansi-c
:calling-convention :cdecl)
(fli:make-pointer :symbol-name "initialize")
(fli:make-pointer :symbol-name "add_number")
(fli:make-pointer :symbol-name "spawn_child_process")
(fli:make-pointer :symbol-name "event_listener")
(fli:make-pointer :symbol-name "post_event")
(deliver nil "libtest" 0
:dll-exports '("initialize"
"add_number"
"spawn_child_process"
"event_listener"
"post_event")
:multiprocessing t)
#include <iostream>
#include "c_to_lisp.hpp"
using namespace std;
DLLEXPORT Event * make_event(long num, const char * msg) {
return new Event(num, msg);
}
DLLEXPORT void on_callback_1(const char * msg) {
cout << "> " << msg << endl;
}
DLLEXPORT void on_callback_2(const Event * msg) {
cout << "> num: " << msg->num << " msg: " << msg->msg << endl;
}
// c_to_lisp.hpp
//
// > clang -shared -o libc_to_lisp.dylib c_to_lisp.cpp -std=c++11 -lstdc++
#ifndef __C_TO_LISP_HPP_INCLUDED__
#define __C_TO_LISP_HPP_INCLUDED__
#ifdef _WIN32
#define DLLEXPORT __declspec (dllexport)
#else
#define DLLEXPORT __attribute__ ((visibility("default")))
#endif /* _WIN32 */
#ifdef __cplusplus
extern "C" {
#endif
struct Event {
long num;
char * msg;
explicit Event(long num, const char * msg)
: num(num), msg(strdup(msg)) {
}
};
DLLEXPORT Event * make_event(long num, const char * msg);
DLLEXPORT void on_callback_1(const char *);
DLLEXPORT void on_callback_2(const Event *);
#ifdef __cplusplus
}
#endif
#endif /* C_TO_LISP_HPP_INCLUDED */
// main.cpp
//
// > clang main.cpp -std=c++11 -lstdc++
// > DYLD_LIBRARY_PATH=. ./a.out
#include <dlfcn.h>
#include <iostream>
#include <thread>
#include <chrono>
#include "c_to_lisp.hpp"
using namespace std;
typedef long(*add_number)(long, long);
typedef void(*spawn_child_process)();
typedef void(*initialize)();
typedef void(*event_listener)();
typedef void(*post_event)(const char *);
int main(int argc, char * argv[]) {
auto dll_path = "libtest.dylib";
auto handle = dlopen(dll_path, RTLD_LAZY);
if (!handle) {
cout << "Loading error" << endl;
return -1;
}
auto address = dlsym(handle, "initialize");
{
if (!address) {
cout << "failed to load 'initialize' symbol" << endl;
return -1;
}
auto fp = (initialize) address;
fp();
}
{
address = dlsym(handle, "add_number");
if (!address) {
cout << "failed to load 'add_number' symbol" << endl;
return -1;
}
auto fp = (add_number) address;
fp(2, 3);
cout << "2 + 3 = " << fp(2, 3) << endl;
}
{
address = dlsym(handle, "spawn_child_process");
if (!address) {
cout << "failed to load 'spawn_child_process' symbol" << endl;
return -1;
}
auto fp = (spawn_child_process) address;
fp();
this_thread::sleep_for (std::chrono::seconds(1));
}
{
address = dlsym(handle, "event_listener");
if (!address) {
cout << "failed to load 'event_listener' symbol" << endl;
return -1;
}
auto fp1 = (event_listener) address;
fp1();
address = dlsym(handle, "post_event");
if (!address) {
cout << "failed to load 'post_event' symbol" << endl;
return -1;
}
auto fp2 = (post_event) address;
for (auto i = 0; i < 100; i++) {
char buf[10];
sprintf(buf, "msg %d", i);
fp2(buf);
}
}
this_thread::sleep_for (std::chrono::seconds(1));
return 0;
}