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 ... #edifIs 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.

  1. Extern “C”) enforces C linking, in other words, functions with this annotation must have a C-compatible signature and use a C-compatible type.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  6. 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.

  7. 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.

  8. 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.

  9. 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)