Caiorss.github. IO/C-Cpr-notes…
Caiors.github. IO /
Release date: February 8, 2021
C language wrapper for C++ libraries and interoperability
1.1 an overview of the
One of the major advantages of C is that libraries written in C by other programming languages can be called through FFI (Foreign Function Interface), and C functions can be called from C shared libraries. Unlike C, it is not feasible to call the C++ library directly through FFI because C++ lacks the standard ABI– the applied binary interface. The solution to this problem is to create a C wrapper for the C++ library using opaque Pointers, incomplete types, and external C comments.
1.2 Calling C++ from C
This section describes how to call statically linked C++ COD from C.
- Complete code. GIST instance – Calls C++ code from C
File: cppcode HPP
- Header file that can be used by C or C++ code. All between
#ifndef __cplusplus ... #edif
Is ignored by the C compiler.
#ifndef _CODECPP_
#define _CODECPP_
#ifdef __cplusplus
#define EXPORT_C extern "C"
#else
#define EXPORT_C
#endif
//============ C++ Only Header =================//
#ifdef __cplusplus // Enabled only for C++ compilers
#include <iostream>
class Runstat
{
/// Sum of sequence processed
double m_sum;
/// Sum of squares processed
double m_sumsq;
/// Size of sequence processed
size_t m_N;
public:
Runstat(a);Runstat(Runstat const&) = delete;
Runstat& operator=(Runstat const&) = delete;
~Runstat(a);void add(double x);
void reset(a);
size_t size(a) const;
double mean(a) const;
/// Standard deviation
double sdev(a) const;
};
#endif //-- End of __cplusplus definition //
// ========== C-interface for std::string container
typedef void* hString;
EXPORT_C hString string_new(a);
EXPORT_C hString string_new1 (const char* text);
EXPORT_C hString string_copy (hString self);
EXPORT_C void string_del (hString self);
EXPORT_C void string_add (hString self, const char* text);
EXPORT_C void string_disp (hString, const char* name);
//============ C-interface for class Runstat ============//
// Opaque pointer type alias for C-lang
typedef void* pStat;
EXPORT_C pStat Runstat_new(a);
EXPORT_C void Runstat_del (pStat self);
EXPORT_C void Runstat_add (pStat self, double x);
EXPORT_C double Runstat_mean(pStat self);
EXPORT_C double Runstat_sdev(pStat self);
EXPORT_C size_t Runstat_size(pStat self);
#endif
Copy the code
File: cppcode. CPP
- Class and C language interface function implementation.
An implementation of a member function of class RunStat.
. . . . Runstat::Runstat()
{
std::cout << " [TRACE] Object created Ok." << std::endl;
m_sum = 0.0;
m_sumsq = 0.0;
m_N = 0;
}
Runstat::~Runstat()
{
std::cout << " [TRACE] Object deleted OK" << std::endl;
}
void
Runstat::add(double x)
{ m_sum += x; m_sumsq += x * x; m_N++; }... . . . . . Runstat::~Runstat()
{
std::cout << " [TRACE] Object deleted OK"<< std::endl; }... . . . . .void
Runstat::add(double x)
{ m_sum += x; m_sumsq += x * x; m_N++; }... . . . . . . .Copy the code
STD :: String C- interface function implementation.
//--------- C-Interface for class std::string ------------------//
hString string_new(a)
{
return new std::string{};
}
hString string_new1(const char* text)
{
return new std::string(text);
}
// Copy constructor
hString string_copy(hString self)
{
std::string* p = reinterpret_cast<std::string*>(self);
return new std::string(*p);
}
void string_del(hString self)
{
delete reinterpret_cast<std::string*>(self);
}
void string_add(hString self, const char* text)
{
auto p = reinterpret_cast<std::string*>(self);
*p = *p + text;
}
void string_disp(hString self, const char* name)
{
auto p = reinterpret_cast<std::string*>(self);
std::cout << name << " / std::string{ " << *p << "}" << std::endl;
}
Copy the code
Implementation of C- interface functions of class RunStat.
//---------- C-Interface for class Runstat ---------------------//
pStat Runstat_new(a)
{
return new (std::nothrow) Runstat(a); }void Runstat_del(pStat self)
{
delete reinterpret_cast<Runstat*>(self);
}
void Runstat_add(pStat self, double x)
{
auto p = reinterpret_cast<Runstat*>(self);
p->add(x);
}
double Runstat_mean(pStat self)
{
Runstat* p = reinterpret_cast<Runstat*>(self);
return p->mean(a); }double Runstat_sdev(pStat self)
{
Runstat* p = reinterpret_cast<Runstat*>(self);
return p->sdev(a); }size_t Runstat_size(pStat self)
{
Runstat* p = reinterpret_cast<Runstat*>(self);
return p->size(a); }Copy the code
Documents: the main. C
- C client code for C interface functions exported using the C++ file cppCode.
#include <stdio.h>
#include <stdlib.h>
#include "codecpp.hpp"
int main(a)
{
printf("\n == EXPERIMENT 1 - std::string C-wrapper ======\n");
hString str = string_new1("A C++ string in C");
string_disp(str, "str");
hString str2 = string_copy(str);
string_add(str, " - hello world");
string_disp(str, "str");
string_disp(str2, "str2");
string_del(str);
string_del(str2);
printf("\n == EXPERIMENT 2 - Class Runstat ======\n");
pStat obj = Runstat_new(a);Runstat_add(obj, 10.0);
Runstat_add(obj, 4.0);
Runstat_add(obj, 25.0);
Runstat_add(obj, 16.0);
printf(" Number of Elements processed = %zu \n".Runstat_size(obj));
printf(" Mean = %.5f \n".Runstat_mean(obj));
printf(" Sdev = %.5f \n".Runstat_sdev(obj));
Runstat_add(obj, 50.0);
Runstat_add(obj, 80.0);
printf(" Mean = %.5f \n".Runstat_mean(obj));
printf(" Sdev = %.5f \n".Runstat_sdev(obj));
// Delete C++ Object
Runstat_del(obj);
return 0;
}
Copy the code
File: CMakeLists. TXT
cmake_minimum_required(VERSION 3.9)
project( CallCppFromC)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_VERBOSE_MAKEFILE ON)
add_executable(main main.c codecpp.cpp codecpp.hpp)
Copy the code
Manual compilation.
# Generate object code codecpp.o
$ g++ codecpp.cpp -o codecpp.o -c -std=c++1z -Wall -g
# Generate object-code main.o
$ gcc main.c -o main.o -c -g
# Link => generate main.bin
$ g++ main.o codecpp.o -o main.bin
Copy the code
Program output.
./main.bin
== EXPERIMENT 1 - std::string C-wrapper ======
str / std::string{ A C++ string in C}
str / std::string{ A C++ string in C - hello world}
str2 / std::string{ A C++ string in C}
== EXPERIMENT 2 - Class Runstat ======
[TRACE] Object created Ok.
Number of Elements processed = 4
Mean = 13.75000
Sdev = 8.95824
Mean = 14.16667
Sdev = 41.69612
[TRACE] Object deleted OK
Copy the code
1.3 Call the Qt5 Widgets library from C, Julia, D-lang, and Lisp.
1.3.1 overview
This sample code provides a C wrapper for the QT5 Widgets GUI library. This C wrapper allows Qt5 to be called from C, Julia, and D.
Functional documentation used in this experiment
Documentation for the Widgets class used in the QT5 sample code.
- The QObject class | Qt Core 5.15.0
- QWidget class | Qt Widgets 5.15.0
- The QApplication class | Qt Widgets 5.15.0
- QPushButton class – Qt Widgets 5.15.0
- QDoubleSpinBox class – Qt Widgets 5.15.0
Julia language
- Call C and Fortran code – Julia language
- Embed Julia–Julia language
- Julia – Wikipedia
- Julia: Come for grammar, stay for speed — Nature.
- An example of Julia
D language.
- D (programming language) – Wikipedia
- Interface between D and C: Getting started – D blog
- Interface to the C-D programming language
- Use the dlopen/ DLSYm -D language discussion area.
- Interface with C++ – D programming language
Lisp – SBCL (Steel Bank Common Lisp):
- www.sbcl.org/ — — Steel Bank Common Lisp
- SBCL (1). Steel Bank Common Lisp – Linux man page
- CFFI User manual
- Interop Mini Series — Calling C and C++ code Lisp with CFFI (Part 1)
- GitHub with CFFI shared C language library – GitHub
- [Common Lisp] Hello CFFI (Common Lisp + C) -github
- Buildapp – Use SBCL or CCL to create executable files
1.3.2 Packing files
- GIST with all sources.
- Gist.github.com/a59d0f1b17d…
C- Wrapper source code
File: CMakeLists. TXT
cmake_minimum_required(VERSION 3.9)
project(Qt5_Widgets_Template)
#====== Global Configurations ==================#
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_VERBOSE_MAKEFILE ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
# Export ALL DLLs symbols on Windows without __declspec(xxxx) annotations.
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS true)
find_package(Qt5 COMPONENTS Core Widgets Network UiTools REQUIRED)
#=============== Target Configurations ============#
# -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- #
add_library( qtwrapper SHARED qtwrapper.cpp )
target_link_libraries( qtwrapper PRIVATE Qt5::Core Qt5::Gui Qt5::Widgets )
add_executable( client1 client1.c )
target_link_libraries( client1 qtwrapper )
Copy the code
File: qtwrapper. CPP
- QT5 C++ GUI library c-wrapper or C-interface.
#include <QtWidgets>
#include <QApplication>
#include <QtUiTools/QtUiTools>
#include <QSysInfo>
#include <QtConcurrent/QtConcurrent>
#include <functional>
#include <iostream>
#include <fstream>
#include <string>
#include <map>
/** extern "C" => Remove C++ name mangling and makes the function * compatible with C. It enforces C linkage to the annotated symbol. **-----------------------------------------------------------------*/
#define EXPORT_C extern "C"
/** Delete any instance of a derived class of QObject * Note: It can be used for deleting instances QWidget, QApplication or QLayout. */
EXPORT_C
void qt_qobject_del(QObject* self)
{
delete self;
}
EXPORT_C
const char* qt_qobject_ClassName(QObject* self)
{
return self->metaObject() - >className(a); }EXPORT_C
void qt_qobject_dumpObjectInfo(QObject* self)
{
self->dumpObjectInfo(a); }EXPORT_C
void qt_qobject_dumpObjectTree(QObject* self)
{
self->dumpObjectTree(a); }EXPORT_C
void qt_qobject_print(QObject* self)
{
qDebug() < <" [QOBjec] " << self;
}
enum class WidgetType
{
Window = 1
, Window_main = 2
, QPushButton = 3
, QLabel = 4
, QLineEdit = 5
, QDoubleSpinBox = 6
};
using CtorFunction = std::function<QWidget* (QWidget* parent)>;
using CtorDatabase = std::map<int, CtorFunction>;
template<typename T> void register_ctor(CtorDatabase& db, int type)
{
db[type] = [](QWidget* parent){ return new (std::nothrow) T(parent); };
}
// Create an object of a QWidget given class, given its name
EXPORT_C
QWidget* qt_widget_new(QWidget* parent, int type)
{
// 'static' => Initialize static object only once.
static const CtorDatabase ctordb = []{
auto db = CtorDatabase{};
register_ctor<QWidget>(db, (int) WidgetType::Window);
register_ctor<QPushButton>(db, (int) WidgetType::QPushButton);
register_ctor<QLabel>(db, (int) WidgetType::QLabel);
register_ctor<QLineEdit>(db, (int) WidgetType::QLineEdit);
register_ctor<QDoubleSpinBox>(db, (int) WidgetType::QDoubleSpinBox);
db[(int) WidgetType::Window_main] = [](QWidget* parent){
QWidget* w = new (std::nothrow) QWidget(parent);
w->resize(500.400);
w->setWindowTitle("MainWindow");
w->show(a);return w;
};
returndb; } ();if(auto it = ctordb.find(type); it ! = ctordb.end())
{ return it->second(parent); }
return nullptr;
}
EXPORT_C
QWidget* qt_window_main(a)
{
QWidget* w = new (std::nothrow) QWidget{};
w->resize(500.400);
w->setWindowTitle("MainWindow");
w->show(a);return w;
}
EXPORT_C
QLayout* qt_layout_new(QWidget* parent, int type)
{
if(type == 1) return new (std::nothrow) QVBoxLayout(parent);
if(type == 2) return new (std::nothrow) QHBoxLayout(parent);
if(type == 3) return new (std::nothrow) QFormLayout(parent);
return nullptr;
}
EXPORT_C
QObject* qt_QFormLayout_addWidgetAndLabel(QFormLayout* self, int type, const char* label)
{
QWidget* wdg = qt_widget_new(nullptr, type);
if(wdg == nullptr) {return nullptr; }
self->addRow(label, wdg);
return wdg;
}
EXPORT_C
void qt_QFormLayout_addRowItem(QFormLayout* self, QWidget* field)
{
self->addRow(field);
}
EXPORT_C
QLabel* qt_QFormLayout_addLabel1(QFormLayout* self, const char* label_text)
{
auto btn = new QLabel(label_text);
self->addRow(btn);
return btn;
}
EXPORT_C
QApplication* qt_app_new(int argc, char** argv)
{
std::cout << " [TRACE] Create QAppliction Object Ok" << std::endl;
return new QApplication(argc, argv);
}
EXPORT_C
QApplication* qt_app_new2(a)
{
std::cout << " [TRACE] Create QAppliction Object Ok" << std::endl;
static int argc = 1;
static const char* argv [] = { "dummy_app" };
return new QApplication(argc, (char**) argv);
}
EXPORT_C
int qt_app_exec(QApplication* self)
{
return self->exec(a); }// -------- Wrappers for QWidget Class ------------------//
EXPORT_C
void qt_widget_show(QWidget* self)
{
self->show(a); }template<typename T>
static bool set_text(QWidget* self, const char* text)
{
auto obj = qobject_cast<T>(self);
// Early return on Error.
if(obj == nullptr) {return false; }
obj->setText(text);
return true;
}
EXPORT_C
void qt_widget_setText(QWidget* self, const char* text)
{
if( set_text<QLabel*>(self, text)) return;
if( set_text<QLineEdit*>(self, text)) return;
if( set_text<QTextEdit*>(self, text)) return;
if( set_text<QMessageBox*>(self, text)) return;
if( set_text<QAbstractButton*>(self, text) ) return;
// logger().log() << " [TRACE] Called function " << __FUNCTION__ << std::endl;
self->setWindowTitle(text);
}
// -------- Wrappers for QPushButton Class ------------------//
// Install event handler (callback) for button clicked event
EXPORT_C
void qt_button_onClicked( // Pointer to button
QAbstractButton* self
// Context of the callback. Note: C does not support closures
// or stateful function pointer. All data needs to be passed
// as arguments
, void* ctx
// Pointer to callback function pointer.
, void (* callback) (void* ctx) )
{
QObject::connect(self, &QPushButton::clicked, [=]{
callback(ctx);
});
}
// Install event handler for
EXPORT_C
void qt_QLineEdit_onTextChanged( QLineEdit* self
, void* ctx
, void (* callback) (void* ctx, QLineEdit* self) )
{
QObject::connect(self, &QLineEdit::textChanged, [=]{
callback(ctx, self);
});
}
EXPORT_C
void qt_msgbox_info(QWidget* parent, const char* title, const char* text)
{
QMessageBox::information(parent, title, text);
}
// Note: The string has to be released after with free() function.
EXPORT_C
const char* qt_QLineEdit_text(QLineEdit* self)
{
return self->text().toLocal8Bit().constData(a); }EXPORT_C
double qt_QDoubleSpinBox_value(QDoubleSpinBox* self)
{
return self->value(a); }EXPORT_C
void qt_QDoubleSpinBox_setValue(QDoubleSpinBox* self, double value)
{
self->setValue(value);
}
EXPORT_C
void qt_QDoubleSpinBox_onValueChanged(
QDoubleSpinBox* self
, void* ctx
, void (* callback)(void* ctx) )
{
QObject::connect( self, qOverload<double>(&QDoubleSpinBox::valueChanged)
, [=](double x){ callback(ctx); }); }Copy the code
File: qtwrapper.h (header for C-wrapper or C-interface)
// Non standard directive, but supported by most compiler to includer the
// header only once
#pragma once
// Type aliases
typedef void ANY;
typedef void QObject;
typedef void QApplication;
typedef void QPushButton;
typedef void QWidget;
typedef void QAbstractButton;
typedef void QLineEdit;
typedef void QLayout;
typedef void QFormLayout;
typedef void QLabel;
void qt_qobject_del(QObject* self);
void qt_qobject_dumpObjectInfo(QObject* self);
void qt_qobject_dumpObjectTree(QObject* self);
void qt_qobject_print(QObject* self);
QApplication* qt_app_new(int argc, char** argv);
QApplication* qt_app_new2(a);
// Instantiate any QT widget by name
QWidget* qt_widget_new(QWidget* parent, int type);
void qt_widget_setText(QWidget* self, const char* text);
// QApplication
int qt_app_exec(QApplication* self);
// Any QtWidget
void qt_QWidget_show(QWidget* self);
void qt_QWidget_setToolTip(QWidget* self, const char* tooltip);
// QPushButton wrappers
QPushButton* qt_QPushButton_new(QWidget* parent, const char* label);
void qt_QPushButton_onClicked_test(QPushButton* self );
// Abstract PushbButton
void qt_button_onClicked(QAbstractButton* self, void* ctx, void (* callback) (void*));
void qt_button_setText(QAbstractButton* self, const char* text);
const char* qt_QLineEdit_text(QLineEdit* self);
QLayout* qt_layout_new(QWidget* parent, int type);
QObject* qt_QFormLayout_addWidgetAndLabel(QFormLayout* self, int type, const char* label);
QLabel* qt_QFormLayout_addLabel1(QFormLayout* self, const char* label_text);
void qt_msgbox_info(QWidget* parent, const char* title, const char* text);
void qt_QLineEdit_onTextChanged( QLineEdit* self
, void* ctx
, void (* callback) (void* ctx, QLineEdit* self) );
Copy the code
Consider the factors.
-
Extern “C”) enforces C linking, in other words, functions with this annotation must have a C-compatible signature and use a C-compatible type.
-
The C++ operator new throws an exception when out of memory, which throws STD ::bad_alloc. Since C does not support exceptions, you need to use the operator new (STD ::nothrow), which returns a null pointer, instead of throwing an exception.
-
Since the C language does not support exceptions, any exception thrown in a function annotated with extern “C “should be returned as an additional function argument. In addition, C++ exceptions from different compilers may even be incompatible due to the lack of a standard C++ ABI.
-
Since C functions do not support closures and cannot hold state, it is necessary to add an extra void pointer argument (void*) to the function pointer argument and the function that takes it as an argument, such as qt_button_onClicked(). This extra (void*) pointer allows the calling code to pass and hold state.
-
Although the technique for creating c-wrappers is for the Qt5 Widgets library, this method can be used for any other C++ library, for example: boost-libraries, wxw.c.c.C.C.C.C. C: Boost-libraries, WxWidgets GUI Library, Windows MFC (Microsoft Foundation Classes), and more.
-
This C-wrapper technique can also be used to create C-libraries in C++, which can be accessed from any programming language (Python, Ruby, Lisp…) via FFI(Foreign Function Interface). Or, in the case of compiled languages such as Golang, Rust, or D-lang, via a link to the C-Wrapper shared library.
-
If Qt has a C-binding like this that ships with Qt on every Linux distribution that uses Qt or KDE, then Qt applications can be created in any programming language other than C++, loaded through the FFI external function interface, or if the programming language is a compiled language, If links to the C ABI (application binary interface) are supported, they can be linked to a C-Binding.
-
Another benefit of C-Wrapper to the C++ library is that C-Wrapper provides a stable ABI(Application-Binary Interface), which allows applications built with different C++ compilers to link to the C- Interface of the library without having to recompile the library. It avoids compiling the library with all the possible C++ compilers that the client code might use.
-
Another way to access C++ libraries from higher-level languages is to use SWIG(SWIG wrapper generator), which can parse interface files and library header files to generate C code for programming language-specific native interface apis. On Unix-like systems, native code libraries can be created in C. It is loaded at run time with a dlopen() call and on Windows with a LoadLibrary() call. The term native Interface API is borrowed from Java’s JNI(Java Native Interface API) because, unlike FFI(Foreign Function Interfaces), There is currently no proper term to describe this functionality. For example, Pythons’ native interface API allows Python modules (that is, libraries) to be created entirely in C. The disadvantage of using SWIG is that it requires access to the library’s source code and generates binding code for each programming language compilation that will generate bindings, whereas C-Wrapper only needs to be compiled once.
1.3.3 Build the wrapper file.
Note: This code was compiled and tested on a Linux–Fedora 32, 64-bit machine that contains the preinstalled Qt5 library.
Get the source code.
$ git clone https://gist.github.com/a59d0f1b17d286dad19842932c931e92 qt5-cwrapper
$ cd qt5-cwrapper
$ ls
client1.c CMakeLists.txt d_client.d loader.jl qtwrapper.cpp qtwrapper.h
Copy the code
Build the project. (Note: Tested on Linux Fedora-32 64-bit x86-64)
$ cmake --config Debug -H. -build
$ cmake --build build --target
Copy the code
Check the binaries.
$ file build/client1
build/client1: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2
, BuildID[sha1]=122650693cf9845a35afeb76bced113f3d92ed1a, forGNU/Linux 3.2.0, not stripped $file build/libqtwrapper.so build/libqtwrapper.so: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), statically linked , BuildID[sha1]=47381d2f687f8cb5f6bf80be737c0baa2b0e2cb1, not strippedCopy the code
Displays symbols exported from the C-Wrapper shared library.
- Note: on Windows, the corresponding tool is dumpbin; on MacOSX, the corresponding tool is otool.
$ nm -D build/libqtwrapper.so ... . . . . . . . . . . . . . 0000000000012d9d T qt_QFormLayout_addRowItem 0000000000012d0c T qt_QFormLayout_addWidgetAndLabel 000000000001315a T qt_QLineEdit_onTextChanged 0000000000013284 T qt_QLineEdit_text 0000000000012741 T qt_qobject_ClassName 000000000001271a T qt_qobject_del 000000000001276a T qt_qobject_dumpObjectInfo 0000000000012785 T qt_qobject_dumpObjectTree 00000000000127a0 T qt_qobject_print U qt_version_tag 0000000000012a47 T qt_widget_new 0000000000012fbb T qt_widget_setText 0000000000012fa0 T qt_widget_show 0000000000012b51 T qt_window_main U strlen U _Unwind_Resume 00000000000157a8 W _Z12qobject_castIP11QMessageBoxET_P7QObject 00000000000157cc W _Z12qobject_castIP15QAbstractButtonET_P7QObject 000000000001573c W _Z12qobject_castIP6QLabelET_P7QObject 0000000000015760 W _Z12qobject_castIP9QLineEditET_P7QObject ... . . . . . . . . . . . . . . .Copy the code
Display exported symbols and decrypt (that is, decode)C++ messy symbols.
$ >> nm -D build/libqtwrapper.so | c++filt ... . . . . . . . . . 000000000001276a T qt_qobject_dumpObjectInfo 0000000000012785 T qt_qobject_dumpObjectTree 00000000000127a0 T qt_qobject_print U qt_version_tag 0000000000012a47 T qt_widget_new 0000000000012fbb T qt_widget_setText 0000000000012fa0 T qt_widget_show 0000000000012b51 T qt_window_main U strlen U _Unwind_Resume 00000000000157a8 W QMessageBox* qobject_cast<QMessageBox*>(QObject*) 00000000000157cc W QAbstractButton* qobject_cast<QAbstractButton*>(QObject*) 000000000001573c W QLabel* qobject_cast<QLabel*>(QObject*) 0000000000015760 W QLineEdit* qobject_cast<QLineEdit*>(QObject*) ... . . . . . . . . .Copy the code
1.3.4C Client code
File: client1.c (C-client code)
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "qtwrapper.h"
typedef struct CallbackContext
{
QLineEdit* lin;
int counter;
} CallbackContext;
// All callback (closure) state should be passed as arguments
void onclicked_callback(void* context)
{
CallbackContext* ctx = (CallbackContext*) context;
ctx->counter += 1;
const char* str = qt_QLineEdit_text(ctx->lin);
printf(" [TRACE] Button clicked => Counter incremented to: %d \n", ctx->counter);
printf(" [TRACE] QLineEdit Value = %s \n", str);
// free(str);
// qt_msgbox_info(NULL, "Message Info", "Button clicked Ok");
}
void lineEdit_Callback(void* ctx, QLineEdit* self)
{
const char* str = qt_QLineEdit_text(self);
QLabel* label = (QLabel*) ctx;
qt_widget_setText(label, str);
printf(" [LineEdit Callback] Text changed to %s \n", str);
}
void onclick_msgbox_callback(void* context)
{
QLineEdit* lin = (QLineEdit*) context;
const char* str = qt_QLineEdit_text(lin);
qt_msgbox_info(NULL."Message Info", str);
// free(str);
}
const int WIDGET_WINDOW = 1;
const int WIDGET_WINDOW_MAIN = 2;
const int WIDGET_QPUSH_BUTTON = 3;
const int WIDGET_QLABEL = 4;
const int WIDGET_QLINE_EDIT = 5;
const int WIDGET_QDOUBLE_SPINBOX = 6;
const int LAYOUT_QFORM_LAYOUT = 3;
int main(int argc, char** argv)
{
// QApplication* qapp = qt_app_new(argc, argv);
QApplication* qapp = qt_app_new2();
// -------- Create GUI - Graphical User Interface ------//
QPushButton* win = qt_widget_new(NULL, WIDGET_WINDOW_MAIN);
qt_widget_setText(win, "Window Title"); assert(win ! =NULL); QFormLayout* form = qt_layout_new(win, LAYOUT_QFORM_LAYOUT); assert(form ! =NULL);
QLineEdit* lin = qt_QFormLayout_addWidgetAndLabel(form, WIDGET_QLINE_EDIT, "Input");
QPushButton* btn1 = qt_QFormLayout_addWidgetAndLabel(form, WIDGET_QPUSH_BUTTON, "");
QPushButton* btn2 = qt_QFormLayout_addWidgetAndLabel(form, WIDGET_QPUSH_BUTTON, "");
QLabel* disp = qt_QFormLayout_addWidgetAndLabel(form, WIDGET_QLABEL, "Display label");
qt_widget_setText(btn1, "Button 1");
qt_widget_setText(btn2 ,"Button 2");
// qt_widget_setToolTip(btn1, "Click at this button to play!" );
qt_qobject_print(win);
// -------- Install Event Handlers --------------//
//
qt_QLineEdit_onTextChanged(lin, disp, lineEdit_Callback);
qt_button_onClicked(btn1, lin, &onclick_msgbox_callback);
CallbackContext ctx;
ctx.counter = 0;
ctx.lin = lin;
qt_button_onClicked(btn2, &ctx, &onclicked_callback);
// ---- Run QT event loop - blocking main thread ---//
qt_app_exec(qapp);
// ----- Dispose objects ---------------------------//
qt_qobject_del(win);
qt_qobject_del(qapp);
puts("\n [TRACE] Terminate application Ok. ");
return 0;
}
Copy the code
Run the executable.
$ build/client1 [TRACE] Create QAppliction Object Ok [LineEdit Callback] Text changed to h [LineEdit Callback] Text changed to h [LineEdit Callback] Text changed to h [LineEdit Callback] Text changed to h ... . . . . . . . . . . . .Copy the code
User interface screenshot:
Figure 1: User interface for C client code.
1.3.5 Julia Client Code (Loader.jl)
File: loader.jl (Julia language client code)
#! /usr/bin/env julia
# Refers to file libqexport.so
#
# On Linux:
# It is assumed that the LD_LIBRARY_PATH environment variable of current
# process contains the directory where libqtexport is located.
#
# On Windows:
# It is made the assumption that the PATH environmenrt variable of current
# process contains the directory where libqtexport is located.
#
#
const shlib = "libqtwrapper"
const QObject = Ptr{Cvoid}
const QWidget = Ptr{Cvoid}
const QLayout = Ptr{Cvoid}
const QApp = Ptr{Cvoid}
const QButton = Ptr{Cvoid}
const QDoubleSpinbox = Ptr{Cvoid}
const WIDGET_WINDOW = 1;
const WIDGET_WINDOW_MAIN = 2;
const WIDGET_QPUSH_BUTTON = 3;
const WIDGET_QLABEL = 4;
const WIDGET_QLINE_EDIT = 5;
const WIDGET_QDOUBLE_SPINBOX = 6;
const LAYOUT_QFORM_LAYOUT = 3;
# Wrapper to function:
# QWidget* qt_widget_new(QWidget* parent, const char* name)
function qt_widget_new(type::Int)::QWidget
return ccall( (:qt_widget_new, shlib)
# Function type signatrue
, QWidget, (QWidget, Cint)
# Function arguments (parent = null and name = name)
, C_NULL, type
)
end
# Delete any instance of QObject (QWidget, QApplictation, QLayout) and so on
function qt_qobject_del(obj::QObject)
ccall( (:qt_qobject_del, shlib)
, Cvoid, ( QObject, )
, obj )
end
function qt_qobject_print(self::QObject)
ccall( (:qt_qobject_print, shlib)
, Cvoid, (QObject,)
, self )
end
## => void qt_widget_setText(QWidget* self, const char* text);
function qt_widget_setText(self::QWidget, text::String)
ccall( (:qt_widget_setText, shlib)
# Function type signatrue
, Cvoid, (QWidget, Cstring)
# Function arguments (parent = null and name = name)
, self, text )
end
# extern QLayout* qt_layout_new(QWidget* parent, const char* name);
function qt_layout_new(type::Int, parent::QWidget)::QLayout
return ccall( (:qt_layout_new, shlib)
# Function type signatrue
, QLayout, (QWidget, Cint)
# Function arguments (parent = null and name = name)
, parent, type )
end
# QPushButton* qt_QFormLayout_addButton(QFormLayout* self, const char* label)
function qt_QFormLayout_addWidgetAndLabel(form::QLayout, type::Int, label::String)::QWidget
return ccall( (:qt_QFormLayout_addWidgetAndLabel, shlib)
# Function type signatrue
, QWidget, (QLayout, Cint.Cstring)
# Function arguments
, form, type, label )
end
function qt_qobject_ClassName(self::QObject)::String
res = ccall( (:qt_qobject_ClassName, shlib)
, Cstring, (QObject, )
, self )
return unsafe_string(res)
end
function QApplication_new()
# return ccall( (:qt_app_new2, shlib), Ptr{Cvoid}, ())
return ccall((:qt_app_new2, shlib), QApp, ())
end
function QApplication_exec(self)
ccall((:qt_app_exec, shlib), Cvoid, ( QApp, ), self )
end
function QPushButton_new(label::String)
# return ccall((:qt_QPushButton_new, shlib), ( Ptr{Cvoid}, Cstring ), C_NULL, label )
return btn = ccall( (:qt_QPushButton_new, shlib)
, Ptr{Cvoid},Ptr{Cvoid}, Cstring),C_NULL, label
)
end
function QWidget_show(self)
return ccall((:qt_QWidget_show, shlib), Cvoid, ( Ptr{Cvoid}, ), self)
end
function QPushButton_onClicked_test(self::QButton)
ccall((:qt_QPushButton_onClicked_test, shlib), Cvoid, ( QButton, ), self)
end
function qt_button_onClicked(self::QButton, handler)
callback = @cfunction $handler Cvoid ( Ptr{Cvoid},)ccall( (:qt_button_onClicked, shlib)
# Function return type signature
, Cvoid
# Function arguments
, ( QButton, Ptr{Cvoid}, Ptr{Cvoid})# Arguments passed to function
, self, C_NULL, callback
)
end
function qt_QDoubleSpinBox_value(self::QDoubleSpinbox)
return ccall( (:qt_QDoubleSpinBox_value, shlib)
, Cdouble, ( QDoubleSpinbox,)
, self)
end
# Wrapper:
# void qt_msgbox_info(QWidget* parent, const char* title, const char* text);
function qt_msgbox_info(title::String, text::String)
ccall( (:qt_msgbox_info, shlib)
, Cvoid, (Ptr{Cvoid}, Cstring.Cstring),C_NULL, title, text
)
end
# void qt_QDoubleSpinBox_onValueChanged(
# QDoubleSpinBox* self
# , void* ctx, void (* callback)(void* ctx))
function qt_QDoubleSpinBox_onValueChanged(self::QDoubleSpinbox, handler)
callback = @cfunction $handler Cvoid ( Ptr{Cvoid},)ccall( (:qt_QDoubleSpinBox_onValueChanged, shlib)
# Function return type signature
, Cvoid
# Function arguments
, ( QButton, Ptr{Cvoid}, Ptr{Cvoid})# Arguments passed to function
, self, C_NULL, callback
)
end
function demo_qt_gui_form()
qapp = QApplication_new()
window = qt_widget_new( WIDGET_WINDOW_MAIN )
qt_widget_setText(window, "Sample QT GUI in Julia Language")
form = qt_layout_new(LAYOUT_QFORM_LAYOUT, window)
entry1 = qt_QFormLayout_addWidgetAndLabel(form, WIDGET_QDOUBLE_SPINBOX, "Speed in m/s")
entry2 = qt_QFormLayout_addWidgetAndLabel(form, WIDGET_QDOUBLE_SPINBOX, "Acceleration m/s^2")
btn_run = qt_QFormLayout_addWidgetAndLabel(form, WIDGET_QPUSH_BUTTON, "")
btn_clean = qt_QFormLayout_addWidgetAndLabel(form, WIDGET_QPUSH_BUTTON, "")
label = qt_QFormLayout_addWidgetAndLabel(form, WIDGET_QLABEL, "")
qt_widget_setText(btn_run, "Run calculations");
qt_widget_setText(btn_clean, "Clean");
println(" [INFO] class of form object = ", qt_qobject_ClassName(form))
println(" [INFO] class of btn_run object = ", qt_qobject_ClassName(btn_run))
println(" [INFO] class of entry1 object = ", qt_qobject_ClassName(entry1))
println(" [INFO] class of entry2 object = ", qt_qobject_ClassName(entry2))
qt_widget_setText(btn_run, "RUN GUI")
qt_widget_setText(btn_clean, "CLEAN GUI")
qt_qobject_print(window)
update_calculations = (ctx) -> begin
speed = qt_QDoubleSpinBox_value(entry1)
accel = qt_QDoubleSpinBox_value(entry2)
z = 4.51 * speed + 9.81 * accel^2 / speed
out = string(" [JULIA] Result = ", round(z, digits = 4))
println(" speed = ", speed, "; accel = ", accel)
println(out)
println("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --")
qt_widget_setText(label, out)
end
qt_QDoubleSpinBox_onValueChanged(entry1, update_calculations)
qt_QDoubleSpinBox_onValueChanged(entry2, update_calculations)
n = 1
qt_button_onClicked(btn_run, (ctx) -> begin
n = n + 1
println(" [ JULIA ] I was clicked Ok. n = ", n, " times. ")
qt_msgbox_info("Notification"."Button was clicked Ok")
end)
qt_button_onClicked(btn_clean, (ctx) -> begin
println(" [TRACE] Button clean clicked Ok")
qt_widget_setText(label, "Button clean clicked")
end)
# --- Block main thread and run QT event loop ---//
QApplication_exec(qapp)
# ----- Dispose objects ----------
# Only the root widget (window) need to be removed
qt_qobject_del(window)
qt_qobject_del(qapp)
end
demo_qt_gui_form()
Copy the code
Run Julia script/try 1-> error.
$ julia loader.jl
ERROR: LoadError: could not load library "libqtwrapper"libqtwrapper.so: cannot open shared object file: No such file or directory Stacktrace: ... . . . . . . . . .Copy the code
Run the Julia script/try 2-> work.
- Note: LD_LIBRARY_PATH only works on Linux. The equivalent of this variable is the PATH environment variable on Windows and DYLD_LIBRARY_PATH on MacOSX.
- The libqtwrapper.so shared library can be installed on Linux by moving the library files to the /lib directory.
$ export LD_LIBRARY_PATH=build:$LD_LIBRARY_PATH$ julia loader.jl [TRACE] Create QAppliction Object Ok [INFO] class of form object = QFormLayout [INFO] class of btn_run object = QPushButton [INFO] class of entry1 object = QDoubleSpinBox [INFO] class of entry2 object = QDoubleSpinBox Speed = 1.0; Accel = 0.0 (JULIA) Result = 4.51 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- speed = 2.0; Accel = 0.0 (JULIA) Result = 9.02 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- speed = 3.0; Accel = 0.0 (JULIA) Result = 13.53 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --... . . .Copy the code
The previous Julia script can also be run using the following method. The advantage of this approach is that the variable LD_LIBRARY_PATH is set only for commands. ‘$ julia loader.jl’.
$ env LD_LIBRARY_PATH=build:$LD_LIBRARY_PATH julia loade.jl
Copy the code
On Linux, libraries can be used by moving the library file to /usr/lib, /usr/local/lib, or /lib without setting the environment variable LD_LIBRARY_PATH.
# Install shared library to /usr/local/lib
$ sudo cp -v build/libqtwrapper.so /usr/lib
# Update library cache
$ sudo ldconfig -v
# Run the Julia script $ julia loader.jl [TRACE] Create QAppliction Object Ok libGL error: MESA-LOADER: failed to open iris (search paths /usr/lib64/dri) libGL error: failed to load driver: iris libGL error: MESA-LOADER: failed to open iris (search paths /usr/lib64/dri) libGL error: failed to load driver: iris libGL error: MESA-LOADER: failed to open swrast (search paths /usr/lib64/dri) libGL error: failed to load driver: swrast [INFO] class of form object = QFormLayout [INFO] class of btn_run object = QPushButton ... . . . . .Copy the code
User interface screenshot.
Figure 2: User interface of Julia language client code.
1.3.6D language –D-lang client code
File: d_client.d (Dlang/D client code)
import core.stdc.stdio;
import std.range: empty;
import str = std.string;
// Import dlopen
import DLL = core.sys.posix.dlfcn;
alias QLayout = void;
alias QObject = void;
alias QWidget = void;
alias QLabel = void;
alias QApplication = void;
alias QPushButton = void;
alias QFormLayout = void;
alias QAbstractButton = void;
const int WIDGET_WINDOW = 1;
const int WIDGET_WINDOW_MAIN = 2;
const int WIDGET_QPUSH_BUTTON = 3;
const int WIDGET_QLABEL = 4;
const int WIDGET_QLINE_EDIT = 5;
const int WIDGET_QDOUBLE_SPINBOX = 6;
const int LAYOUT_QFORM_LAYOUT = 3;
alias qapp_new_t = extern(C) QApplication* function(a);alias qapp_exec_t = extern(C) int function(QApplication* self);
alias qobject_del_t = extern(C) void function (QObject*);
alias qt_widget_new_t = extern(C) QWidget* function(QWidget* parent, int type);
alias qt_window_main_t = extern(C) QWidget* function(a);alias qt_widget_setText_t = extern(C) void function(QWidget* self, const char* text);
alias qt_layout_new_t = extern(C) QLayout* function (QWidget* parent, int type);
alias qt_msgbox_info_t = extern(C) void function(QWidget* parent, const char* title, const char* text);
// QObject* qt_QFormLayout_addWidgetAndLabel(QFormLayout* self, int type, const char* label)
alias qt_QFormLayout_addWidgetAndLabel_t =
extern(C) QPushButton* function (QFormLayout* self, int type, const char* label);
alias qt_button_onClicked_t = extern(C) void function ( QAbstractButton* self
, void* ctx
, void function(void* self) );
struct callback_state
{
int counter = 0;
QLabel* label = null;
qt_widget_setText_t qt_widget_setText = null;
qt_msgbox_info_t qt_msgbox_info = null;
};
extern(C) void button_callback1(void* ctx)
{
import std.conv: to;
auto pstate = cast(callback_state*) ctx;
pstate.counter = pstate.counter + 1;
printf(" [TRACE] Button click event happened => state = %d. \n", pstate.counter);
string text = "Button clicked / counter = ";
text = text ~ to!string(pstate.counter);
pstate.qt_widget_setText(pstate.label, str.toStringz(text));
if(pstate.counter > 20){
pstate.qt_msgbox_info(null."QT Event => Button Clicked", str.toStringz(text)); }}int main()
{
// ------------- Load Symbols from Shared Library -------------------//
void* dll = DLL.dlopen("./build/libqtwrapper.so", DLL.RTLD_GLOBAL | DLL.RTLD_LAZY);
if(! dll) { fprintf(stderr," [ERROR] dlopen error: %s\n", DLL.dlerror());
return 1;
}
auto qapp_new = cast(qapp_new_t) DLL.dlsym(dll, "qt_app_new2");
auto qapp_exec = cast(qapp_exec_t) DLL.dlsym(dll, "qt_app_exec");
auto qt_qobject_del = cast(qobject_del_t) DLL.dlsym(dll, "qt_qobject_del");
auto qt_widget_new = cast(qt_widget_new_t) DLL.dlsym(dll, "qt_widget_new");
auto qt_layout_new = cast(qt_widget_new_t) DLL.dlsym(dll, "qt_layout_new");
auto qt_window_main = cast(qt_window_main_t) DLL.dlsym(dll, "qt_window_main");
auto qt_widget_setText = cast(qt_widget_setText_t) DLL.dlsym(dll, "qt_widget_setText");
auto qt_button_onClicked = cast(qt_button_onClicked_t) DLL.dlsym(dll, "qt_button_onClicked");
auto qt_msgbox_info = cast(qt_msgbox_info_t) DLL.dlsym(dll, "qt_msgbox_info");
auto form_add_item = cast(qt_QFormLayout_addWidgetAndLabel_t) DLL.dlsym(dll, "qt_QFormLayout_addWidgetAndLabel");
assert(form_add_item);
// ---------- Create QT GUI -----------------------------//
//
// Create an instance of class QApplication
auto qapp = qapp_new();
auto window = qt_widget_new(null, WIDGET_WINDOW_MAIN) ;
assert( window );
qt_widget_setText(window, "QT Widgets GUI in D Language");
auto form = qt_layout_new(window, LAYOUT_QFORM_LAYOUT);
auto btn = form_add_item(form, WIDGET_QPUSH_BUTTON, "");
auto label = form_add_item(form, WIDGET_QLABEL, "Display");
qt_widget_setText(btn, "Click ME NOW!!");
callback_state state;
state.counter = 10;
state.label = label;
state.qt_msgbox_info = qt_msgbox_info;
state.qt_widget_setText = qt_widget_setText;
// Install button event handler
qt_button_onClicked(btn, &state, &button_callback1);
// ------ Run QT Event Loop blocking main thread ------//
qapp_exec(qapp);
// ------ Dipose QT Objects ---------------------------//
//
qt_qobject_del(window);
qt_qobject_del(qapp);
return 0;
}
Copy the code
Run the file d_client.d as a script.
$ rdmd d_client.d [TRACE] Create QAppliction Object Ok [TRACE] Button click event happened => state = 11. [TRACE] Button click event happened => state = 12. [TRACE] Button click event happened => state = 13. [TRACE] Button click event happened => state = 14. [TRACE] Button click event happened => state = 15. ... . . . . . . .Copy the code
Build and run the executable.
$ dmd d_client.d -g $ ./d_client [TRACE] Create QAppliction Object Ok [TRACE] Button click event happened => state = 11. [TRACE] Button click event happened => state = 12. [TRACE] Button click event happened => state = 13. ... . . . . . . . . . . . . . . . . . . . . . . .Copy the code
User interface screenshot:
Figure 3: Dlang (D language) user interface
1.3.7 Common Lisp client code
The common Lisp client code, more specifically Stell Bank Common Lisp (SBCL), loads the shared library libqtwrapper.so by using the CFFI external function interface library.
Procedures and dependencies for installing SBCL on Fedora 32.
# Fedora as root
$ dnf install sbcl
# Download and install quicklisp
$ >> curl -O -L http://beta.quicklisp.org/quicklisp.lisp
$ >> rlwrap sbcl --load quicklisp.lisp
# Run sbcl and install quicklisp
$ >> rlwrap sbcl --load quicklisp.lisp
* (quicklisp-quickstart:install)
* (ql:add-to-init-file)
# Install CFFI
* (ql:quickload "cffi")
# Quit SBCL repl
* (quit)
Copy the code
File: sbcl_client. Lisp
;; ---- Experimental binding to Qt5 GUI Framework ----------;;
;;
(require :cffi)
(cffi:define-foreign-library qtw (:unix "libqtwrapper.so"(a))cffi:use-foreign-library qtw )
;; ------- Define bindings ---------------------------..
(cffi:defcfun "qt_app_new2" :pointer)
(cffi:defcfun "qt_widget_new" :pointer (parent :pointer) (type :int))
;; Signature: void qt_widget_show(QWidget* self)
(cffi:defcfun "qt_widget_show" :void (self :pointer))
;; Signature: int qt_app_exec(QApplication* self)
(cffi:defcfun "qt_app_exec" :int (self :pointer))
;; Signature: void qt_qobject_del(QObject* self)
(cffi:defcfun "qt_qobject_del" :void (self :pointer))
;; Signature: void qt_widget_setText(QWidget* self, const char* text)
(cffi:defcfun "qt_widget_setText" :void (self :pointer) (text :string))
;; Signature: QLayout* qt_layout_new(QWidget* parent, int type)
(cffi:defcfun "qt_layout_new" :pointer (self :pointer) (type :int))
;; Signature: QObject* qt_QFormLayout_addWidgetAndLabel(
;; QFormLayout* self
;; , int type, const char* label)
(cffi:defcfun "qt_QFormLayout_addWidgetAndLabel"
:pointer (self :pointer) (type :int))
;; Callback
;; ---------------------------------------------------
;; Signature:
;; -----------------------------------------------------
;; void qt_button_onClicked(
;; QAbstractButton* self
;; , void* ctx
;; , void (* callback) (void* self) )
(cffi:defcfun "qt_button_onClicked"
:void (self :pointer) (ctx :pointer) (callback :pointer))
;; Signature: void qt_msgbox_info( QWidget* parent
;; , const char* title
;; , const char* text )
(cffi:defcfun "qt_msgbox_info"
:void (parent :pointer) (title :string) (text :string(a))defvar WIDGET-WINDOW-MAIN 2)
(defvar WIDGET-BUTTON 3)
(defvar WIDGET-LINE-EDIT 5)
(defvar LAYOUT-QFORM 3)
;; ----- Define variables --------------------------- ;;
;;
;; This function must always be called
;; before creating any widget.
(defvar qapp (qt-app-new2))
;; Create main window
(defvar window (qt-widget-new (cffi:null-pointer) WIDGET-WINDOW-MAIN))
(cffi:with-foreign-string (title "My LISP QT Window")
(qt-widget-settext window title))
(defvar form (qt-layout-new window LAYOUT-QFORM))
;; Note: Common Lisp is case insensitive
(defvar button (qt-QFormLayout-addWidgetAndLabel form WIDGET-BUTTON))
(defvar line-edit (qt-QFormLayout-addWidgetAndLabel form WIDGET-LINE-EDIT))
;; Set button label
(cffi:with-foreign-string (title "click me")
(qt-widget-settext button title))
(defvar counter 0)
;; Define button callback
;; void (* callback) (void* self) )
(cffi:defcallback button-callback
;; Return type of callback
:void
;; List containing callback arguments((ctx :pointer));; Function main body
(progn
;; Increment counter
(setf counter (+ counter 1(a))cffi:with-foreign-string
(text (format nil "counter set to ~a" counter ))
(qt-widget-settext button text))
(cffi:with-foreign-string
(title "User notification")
(cffi:with-foreign-string
(text "Counter clicked")
(qt-msgbox-info window title text)))
(print (format t "Counter set to = ~D \n" counter))
))
;; --- End of button callback() ----- ;;
(qt-button-onClicked button (cffi:null-pointer)
(cffi:callback button-callback))
;; Run QT event loop and blocks current thread.
(qt-app-exec qapp)
;; Dispose C++ objects calling destructors
(qt-qobject-del window)
(qt-qobject-del qapp)
Copy the code
Run the generic Lisp client code.
The $> >export LD_LIBRARY_PATH=$PWD/build $>> SBCL --load sbcl_client.lisp This is SBCL 2.0.1-1.fc32, an implementation of ANSI Common Lisp. More information about SBCL is available at <http://www.sbcl.org/>. SBCL is free software, provided as is, with absolutely no warranty. It is mostlyin the public domain; some portions are provided under
BSD-style licenses. See the CREDITS and COPYING files in the
distribution formore information. [TRACE] Create QAppliction Object Ok ... . . . .Copy the code
User interface screenshot.
Figure 4: User interface for common Lisp (SBCL) client code.
Translation via www.DeepL.com/Translator (free version)