minisoba
11/29/2016 - 8:22 AM

0: C++ <-> LW bridge.md

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;
}