examples: add example ecl_qt (embedding ECL in QT5)

This commit is contained in:
lexicall 2016-08-13 05:59:32 +00:00 committed by Daniel Kochmański
parent 0fbf2a4c44
commit 4e45a82e2b
19 changed files with 657 additions and 0 deletions

0
examples/ecl_qt/.gitkeep Normal file
View file

13
examples/ecl_qt/Makefile Normal file
View file

@ -0,0 +1,13 @@
all:lisp-envi.a hello-lisp-system--all-systems.fasb
#lisp environment.
lisp-envi.a: lisp-envi.asd lisp-envi.lisp build_static.lisp
ecl -load build_static.lisp
#your lisp system.
hello-lisp-system--all-systems.fasb: hello-lisp-system.asd hello-lisp.lisp \
build_fasl.lisp
ecl -load build_fasl.lisp
clean:
-rm -f hello-lisp-system--all-systems.fasb lisp-envi.a

32
examples/ecl_qt/README.md Normal file
View file

@ -0,0 +1,32 @@
This demo shows how to embed ECL into Qt5 and serve as kernel. This also discuss how to compile ECL with C++(14). You can extend on this demo to form a more complicate and productive project.
# Preparation
Before you build the demo, make sure you have those dependencies installed:
1. ECL, of course. We recommend version 16.1.2.
2. g++/clang compiler with at least C++14 support.
3. make
4. Qt5.x with Qt Creator.
5. Quicklisp installed on your ECL.
We use the external Lisp package :lparallel so you better download that package in advance using (ql:quickload :lparallel).
# Build
## Build CL Library and FASB
Run `make` in current directory and you get two files, if successful. `lisp-envi.a` and `hello-lisp-system--all-systems.fasb`.
## Configure your Qt Project
cd to the directory `qt` and open that Qt project with your Qt Creator. Change the three paths I marked for you, if necessary.
1. `INCLUDEPATH`: The path that contains ecl/ecl.h.
In Linux it may be `/usr/include/`.
2. `LIBS`:The path that leads to the shared library of ECL.
In Linux, it may be `/usr/lib/libecl.so/`.
## Build Qt Project
Build your Qt Project. This will generate an executable file for you.
## Engage `fasb` file
After your Qt project is built, move the `hello-lisp-system--all-systems.fasb` file that generated in build step 1 into the directory containing the executable file.
# Run
After you go through the steps above, go for the executable file and try that demo.
Happy hacking with ECL!
ntr(Lexicall)

View file

@ -0,0 +1,8 @@
;;(require 'asdf)
(push "./" asdf:*central-registry*)
(asdf:make-build :hello-lisp-system
:type :fasl
:monolithic t
:move-here "./")
(quit)

View file

@ -0,0 +1,8 @@
;;(require 'asdf)
(push "./" asdf:*central-registry*)
(asdf:make-build :lisp-envi
:type :static-library
:move-here "./")
(quit)

View file

@ -0,0 +1,4 @@
(defsystem :hello-lisp-system
:depends-on (:lparallel)
:components ((:file "hello-lisp")))

View file

@ -0,0 +1,33 @@
(defpackage :hello-lisp
(:use :cl :lparallel))
(in-package :hello-lisp) ;;package name hello-lisp
(setf lparallel:*kernel* (lparallel:make-kernel 4))
(lparallel:defpun pfib (n)
(if (< n 2)
n
(plet ((a (pfib (- n 1)))
(b (pfib (- n 2))))
(+ a b))))
(defun qsort (seq pred)
(if (null seq) nil
(let* ((pivot (first seq))
(left (remove-if-not (lambda (x)
(funcall pred x pivot))
(cdr seq)))
(right (remove-if (lambda (x)
(funcall pred x pivot))
(cdr seq))))
(append (qsort left pred)
(list pivot)
(qsort right pred)))))
(defun say-hello ()
"Bonjour, lisp!")

View file

@ -0,0 +1,3 @@
(defsystem :lisp-envi
:depends-on ()
:components ((:file "lisp-envi")))

View file

@ -0,0 +1,3 @@
(princ "Lisp Environment Booted.")

View file

View file

@ -0,0 +1,11 @@
#include "cl_bridge_utils.hpp"
cl_object lispfy(string str){
return c_string_to_object(str.data());
}
string __spc_expr(string first){
return first;
}

View file

@ -0,0 +1,103 @@
#ifndef CL_BRIDGE_UTILS_HPP
#define CL_BRIDGE_UTILS_HPP
#include <iostream>
#include <string>
#ifdef slots
#undef slots
#endif
#include <ecl/ecl.h>
using std::string;
using lisp_expr = std::string;
using std::cout;
using std::endl;
//extern string CL_MAIN_FASB ;
//extern string CL_MAIN_PACKAGE_NAME ;
cl_object lispfy(string str); /* convert a std::string to cl_object */
/* add spaces among several strings. */
string __spc_expr(string first);
template <typename ...str>
string __spc_expr (string first, str ... next){
return first+" "+__spc_expr(next...);
}
/* encapsule expressions in parenthesis. */
/* to create lisp expr. */
template<typename ...str>
lisp_expr par_expr(str... all){
return "("+__spc_expr(all...)+")";
}
/* turn the sequence into a lisp list expr. */
/* ex: par_list("hello", "lisp", "world");
* -> '("hello" "lisp" "world") */
template<typename ...str>
lisp_expr par_list(str... all){
return "'"+par_expr(all...);
}
/* an enhanced version of cl_eval */
template<typename ...str>
cl_object cl_eval(str... all){
std::cout<<par_expr(all...)<<std::endl;
return cl_eval(lispfy(par_expr(all...)));
}
auto cl_list_traverse=[](auto& cl_lst, auto fn){
while(!Null(cl_lst))
{
fn(cl_car(cl_lst));
cl_lst=cl_cdr(cl_lst);
}
};
/* enhanced cl_object in c++ */
class cl_obj {
private:
cl_object __obj;
public:
cl_obj(cl_object &&obj){this->__obj=obj;}
cl_obj(const cl_object &obj){this->__obj=obj;}
/* list index */
inline cl_obj car(){return cl_obj(cl_car(this->__obj));}
inline cl_obj cdr(){return cl_obj(cl_cdr(this->__obj));}
inline cl_obj cadr(){return this->cdr().car();}
inline cl_obj caar(){return this->car().car();}
inline cl_obj cddr(){return this->cdr().cdr();}
/* predicates */
inline bool nullp(){return Null(this->__obj);}
inline bool atomp(){return ECL_ATOM(this->__obj);}
inline bool listp(){return ECL_LISTP(this->__obj);}
inline bool symbolp(){return ECL_SYMBOLP(this->__obj);}
inline int to_int(){return ecl_to_int(this->__obj);}
inline char to_char(){return ecl_to_char(this->__obj);}
/* turn the cl_object into string. */
inline std::string to_std_string(){
std::string val;
auto & str=this->__obj->string;
for(unsigned long i=0;i<str.fillp;i+=1)
val+=*(typeof(str.elttype) *)(str.self+i);
return val;
}
/* traverse the cl_object as a list. */
template<typename function>
inline void list_traverse(function fn){cl_list_traverse(this->__obj, fn);}
inline cl_obj operator=(cl_object &&obj){return cl_obj(obj);}
};
#endif // CL_BRIDGE_UTILS_HPP

View file

@ -0,0 +1,35 @@
#-------------------------------------------------
#
# Project created by QtCreator 2016-08-10T18:00:40
#
#-------------------------------------------------
QT += core gui
CONFIG+=c++14
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = ecl_qtdemo
TEMPLATE = app
SOURCES += main.cpp\
hybrid_main.cpp \
cl_bridge_utils.cpp
HEADERS += hybrid_main.h \
cl_bridge_utils.hpp
FORMS += hybrid_main.ui
#The include path that contains ecl/ecl.h
INCLUDEPATH += /usr/local/include
#The ECL shared library directory.
LIBS += /usr/local/lib/libecl.dylib
LIBS += $$_PRO_FILE_PWD_/../lisp-envi.a
RESOURCES += \
resource.qrc

View file

@ -0,0 +1,76 @@
#include "hybrid_main.h"
#include "ui_hybrid_main.h"
#include <sstream>
#include <QMessageBox>
#include "cl_bridge_utils.hpp"
using ss=std::stringstream;
using std::cout;
using std::endl;
hybrid_main::hybrid_main(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::hybrid_main)
{
ui->setupUi(this);
}
hybrid_main::~hybrid_main()
{
delete ui;
}
/* int -> string */
auto itos=[](auto in){
ss s;s<<in;string res;s>>res;
return res;
};
/* when called, an alert dialog shows up */
auto jump_out_alert_window=[](std::string str){
QMessageBox::critical(0 ,
"critical message" , QString::fromStdString(str),
QMessageBox::Ok | QMessageBox::Default ,
QMessageBox::Cancel | QMessageBox::Escape , 0 );
};
/* concurrent fibonacci */
void hybrid_main::on_pushButton_clicked()
{
auto str=ui->edit->text().toStdString();
if(str==""){
jump_out_alert_window("You haven't input anything!");
} else {
cl_obj rtv=cl_eval("pfib", str);
string strt=itos(rtv.to_int());
ui->ans->setText(QString::fromStdString(strt));
}
}
/* quick sort. */
void hybrid_main::on_pushButton_2_clicked()
{
auto str=ui->input->text().toStdString();
if(str=="")
{
jump_out_alert_window("You haven't input anything!");
} else {
cout<<par_list(str)<<endl;
cl_obj rtvl=cl_eval("qsort", par_list(str), "#'<");
cout<<rtvl.car().to_int();
string lab="";
auto read_list_to_string=[&](auto elem){lab=lab+" "+itos(ecl_to_int(elem));};
rtvl.list_traverse(read_list_to_string);
ui->output->setText(QString::fromStdString(lab));
}
}
/* hello lisp */
void hybrid_main::on_pushButton_3_clicked()
{
string s=cl_obj(cl_eval("say-hello")).to_std_string();
jump_out_alert_window(s);
}

View file

@ -0,0 +1,32 @@
#ifndef HYBRID_MAIN_H
#define HYBRID_MAIN_H
#include <QMainWindow>
namespace Ui {
class hybrid_main;
}
class hybrid_main : public QMainWindow
{
Q_OBJECT
public:
explicit hybrid_main(QWidget *parent = 0);
~hybrid_main();
private slots:
void on_pushButton_clicked();
void on_pushButton_2_clicked();
void on_pushButton_3_clicked();
private:
Ui::hybrid_main *ui;
};
#endif // HYBRID_MAIN_H

View file

@ -0,0 +1,248 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>hybrid_main</class>
<widget class="QMainWindow" name="hybrid_main">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>641</width>
<height>430</height>
</rect>
</property>
<property name="windowTitle">
<string>hybrid_main</string>
</property>
<widget class="QWidget" name="centralWidget">
<widget class="QLineEdit" name="edit">
<property name="geometry">
<rect>
<x>112</x>
<y>60</y>
<width>121</width>
<height>21</height>
</rect>
</property>
<property name="inputMask">
<string/>
</property>
<property name="text">
<string/>
</property>
<property name="placeholderText">
<string>Input N here.</string>
</property>
</widget>
<widget class="QPushButton" name="pushButton">
<property name="geometry">
<rect>
<x>240</x>
<y>55</y>
<width>113</width>
<height>32</height>
</rect>
</property>
<property name="text">
<string>calculate!</string>
</property>
</widget>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>30</x>
<y>60</y>
<width>71</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>Fibonacci:</string>
</property>
</widget>
<widget class="QLabel" name="label_2">
<property name="geometry">
<rect>
<x>160</x>
<y>100</y>
<width>241</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>Quick Sort List Processing Test</string>
</property>
</widget>
<widget class="QLabel" name="label_3">
<property name="geometry">
<rect>
<x>30</x>
<y>130</y>
<width>59</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>Input</string>
</property>
</widget>
<widget class="QLabel" name="label_4">
<property name="geometry">
<rect>
<x>30</x>
<y>170</y>
<width>59</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>Output</string>
</property>
</widget>
<widget class="QPushButton" name="pushButton_2">
<property name="geometry">
<rect>
<x>20</x>
<y>200</y>
<width>113</width>
<height>32</height>
</rect>
</property>
<property name="text">
<string>sort!</string>
</property>
</widget>
<widget class="QLineEdit" name="input">
<property name="geometry">
<rect>
<x>110</x>
<y>130</y>
<width>491</width>
<height>21</height>
</rect>
</property>
<property name="placeholderText">
<string>Input a sequence of number, seperate by space.</string>
</property>
</widget>
<widget class="QLineEdit" name="output">
<property name="geometry">
<rect>
<x>110</x>
<y>170</y>
<width>491</width>
<height>21</height>
</rect>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
<property name="placeholderText">
<string>Sorted sequence output</string>
</property>
</widget>
<widget class="Line" name="line">
<property name="geometry">
<rect>
<x>30</x>
<y>80</y>
<width>601</width>
<height>16</height>
</rect>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
<widget class="QLabel" name="label_5">
<property name="geometry">
<rect>
<x>270</x>
<y>240</y>
<width>61</width>
<height>91</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">border-image:url(:/pic/madeinlisp.png)</string>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QLabel" name="label_6">
<property name="geometry">
<rect>
<x>370</x>
<y>300</y>
<width>191</width>
<height>41</height>
</rect>
</property>
<property name="text">
<string>(Core made in Lisp.)</string>
</property>
</widget>
<widget class="QLabel" name="label_7">
<property name="geometry">
<rect>
<x>160</x>
<y>10</y>
<width>301</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>Concurrent Compution Test (lparallel)</string>
</property>
</widget>
<widget class="QPushButton" name="pushButton_3">
<property name="geometry">
<rect>
<x>350</x>
<y>270</y>
<width>181</width>
<height>32</height>
</rect>
</property>
<property name="text">
<string>Hello, Lisp!</string>
</property>
</widget>
<widget class="QLineEdit" name="ans">
<property name="geometry">
<rect>
<x>360</x>
<y>60</y>
<width>113</width>
<height>21</height>
</rect>
</property>
<property name="inputMask">
<string/>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
<property name="placeholderText">
<string>Answer output</string>
</property>
</widget>
<widget class="QLabel" name="label_8">
<property name="geometry">
<rect>
<x>400</x>
<y>240</y>
<width>111</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>String Test</string>
</property>
</widget>
</widget>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections/>
</ui>

Binary file not shown.

After

Width:  |  Height:  |  Size: 431 KiB

View file

@ -0,0 +1,43 @@
#include "hybrid_main.h"
#include <QApplication>
#include "cl_bridge_utils.hpp"
string CL_MAIN_FASB = "\"hello-lisp-system--all-systems.fasb\"";
string CL_MAIN_PACKAGE_NAME = "hello-lisp";
/* Initialization.
* This time we load the fasb file after
* the Lisp Environment is booted.
* */
#define __cl_init_name init_lib_LISP_ENVI
extern "C"{
extern void __cl_init_name(cl_object);
}
void init_cl_env(int argc, char * argv[]){
/* Initialize CL environment */
cl_boot(argc, argv);
ecl_init_module(NULL, __cl_init_name);
/* load fasb */
cl_eval("load", CL_MAIN_FASB);
/* set context to current package */
cl_eval("in-package", CL_MAIN_PACKAGE_NAME);
/* hook for shutting down cl env */
atexit(cl_shutdown);
}
#undef __cl_init_name
int main(int argc, char *argv[]){
QApplication a(argc, argv);
hybrid_main w;
w.show();
init_cl_env(argc, argv); /* init env */
return a.exec();
}

View file

@ -0,0 +1,5 @@
<RCC>
<qresource prefix="/pic">
<file>madeinlisp.png</file>
</qresource>
</RCC>