[C++] 361 BBS閲覧アプリの製作 その34 FLTK内部ソースコード混在によるビルドエラー 原因判明 #define命令

[Mac M2 Pro 12CPU, Sonoma 14.3.1, clang++ 15.0.0, FLTK 1.3.9]

前回記事のビルドエラーについて原因が判明しました。

Fl_WebView.cppで定義していたプリプロセッサの#define命令が悪さをしていました。

#define命令 FL_INTERNALSによりmac.Hのif文で誤った分岐に誘導されていました。なぜ急にプリプロセッサが機能し始めたのか、については不明です。

とりあえずプリプロセッサは無効にしました。libfltk_webview.aファイルに静的ライブラリ化しなくても、これまで通りビルドできます。

// #define FL_INTERNALS // mac.Hのif文で誤った分岐に誘導していたため無効化

#include "Fl_WebView.H"
#include "webview.h"
#include <FL/Fl.H>
#include <FL/platform.H>
#include <stdexcept>

extern "C" void my_close_win(void *win);
extern "C" void make_delegate(void *child, void *parent);

void close_cb(Fl_Widget *w, void *win) {
  my_close_win(win);
  w->hide();
}

void Fl_WebView::navigate(const char *addr) { webview_navigate(wv, addr); }

Fl_WebView::Fl_WebView(int x, int y, int w, int h, const char *title)
    : Fl_Double_Window(x, y, w, h, title) {
  Fl_Double_Window::end();
}

void Fl_WebView::draw() {
  if (wv)
    webview_set_size(wv, w(), h(), 0);
}

void Fl_WebView::init() {
    if (!shown())
        throw std::runtime_error("The window needs to be shown.");
    
    auto handle = fl_xid(this);
    wv = webview_create(false, (void *)handle);
    make_delegate((void *)webview_get_window(wv), (void *)handle);
    // Fl::add_idle(webview_run, wv); // この行をコメントアウトする
    this->top_window()->callback(close_cb, (void *)webview_get_window(wv));
}
#if !(defined(FL_LIBRARY) || defined(FL_INTERNALS)) // this part is used when compiling an application program
#  include <FL/Fl_Widget.H>

typedef struct flCocoaRegion* Fl_Region;
typedef struct CGContext* CGContextRef;
typedef struct OpaquePMPrintSettings*   PMPrintSettings;
typedef struct OpaquePMPageFormat*      PMPageFormat;
typedef struct OpaquePMPrintSession*    PMPrintSession;
typedef struct CGImage* CGImageRef;
typedef struct __CFData* CFMutableDataRef; // used in Fl_Copy_Surface.H
typedef CGContextRef Fl_Offscreen;

#else // this part must be compiled when building the FLTK libraries

※ mac.Hのif文でelseの方に誘導されていた

[C++] 360 BBS閲覧アプリの製作 その33 FLTK内部ソースコード混在によるビルドエラー

[Mac M2 Pro 12CPU, Sonoma 14.3.1, clang++ 15.0.0, FLTK 1.4.0ベータ版]

BBS閲覧アプリが急にビルドできなくなりました。

FLTKライブラリ作成用のFl_WebView.cppをプロジェクトに混在させていた問題が何らかのきっかけで顕在化したようです。これまではマグレでたまたまビルドできていました。

In file included from src/Fl_WebView.cpp:6:
In file included from /usr/local/include/FL/platform.H:26:
In file included from /usr/local/include/FL/x.H:32:
/usr/local/include/FL/mac.H:118:12: fatal error: '../src/Fl_Font.H' file not found
#  include "../src/Fl_Font.H"
           ^~~~~~~~~~~~~~~~~~
1 error generated.
make: *** [Makefile:36: obj/Fl_WebView.o] Error 1

GitHubにあるFL_WebViewプロジェクトからビルドし、生成したlibfltk_webview.aファイルを使うようにすると正常化しました。ビルドの際、GitHubのFLTK 1.4.0 最新ベータ版を使っているため、このアプリのビルドにも同バージョンを使用しました。

FLTK最新ベータ版 libファイル:fltk-build/libにあるものを使用
FLTK最新ベータ版 ヘッダファイル:fltk-src/FLにあるものを使用
libfltk_webview.aファイル:libファイルと同じディレクトリへコピーする

GUI右側中央のFl_Boxにスレッドタイトルが表示できていない不具合についてはこれから対処します。

GitHubリンク

[C++] 359 Makefileでワイルドカード使用 FLTKアプリ

[Mac M2 Pro 12CPU, Sonoma 14.3.1, clang++ 15.0.0, FLTK 1.3.9]

Homebrewのライブラリをアップデートする度にMakefileにあるバージョン番号を更新しなくて済むようワイルドカードに置き換えました。

さらにライブラリへのリンクを意味する-lを正常に動作させるため、-Lオプションに/opt/homebrew/libを追加しました。

これまでは/opt/homebrew/Cellarにある各ライブラリのlibパスをわざわざ記入していました。/opt/homebrew/libにライブラリファイルがまとめられているとは知らなかったです。

今回の修正で大分洗練されたMakefileになったように思います。

# コンパイラ
COMPILER = clang++
DEBUG = -g

# ビルドオプション
CPPFLAGS = $(shell fltk-config --use-gl --use-images --cxxflags ) -std=c++11
CPPFLAGS2 = $(shell pkg-config --cflags --libs opencv4 )
LDFLAGS = $(shell fltk-config --use-gl --use-images --ldflags ) -lc++

# includeパス(-I)
INCLUDE = -I./include -I/opt/homebrew/Cellar/libpng/*/include -I/opt/homebrew/Cellar/fltk/*/include \
-I/opt/homebrew/Cellar/opencv/*/include -I/opt/homebrew/Cellar/opencv/*/include/opencv4 \
-I/Library/Frameworks/Python.framework/Versions/3.10/include/python3.10 \
-I/Volumes/DATA_m1/code/cpp/mylib/include

# リンク(-l)
LINK = -lz -lpng -lopencv_core -lopencv_imgcodecs -lopencv_highgui -lopencv_imgproc -lpython3.10

# ライブラリパス(-L)
LIBRARY = -L/usr/local/lib -L/opt/homebrew/lib \
-L/Library/Frameworks/Python.framework/Versions/3.10/lib \
/Volumes/DATA_m1/code/cpp/mylib/lib/Split.a

# ソースファイル
SRCDIR = ./src
SRCS = $(shell find $(SRCDIR) -type f)

# オブジェクトファイル
OBJDIR = ./obj
OBJS = $(addprefix $(OBJDIR), $(patsubst ./src/%.cpp,/%.o,$(SRCS)))

# 実行ファイル
TARGETDIR = ./bin
TARGET = ImageInspector
	
# cppファイルからoファイル作成 $<:依存ファイル
$(OBJDIR)/%.o: $(SRCDIR)/%.cpp
	$(COMPILER) $(CPPFLAGS) $(CPPFLAGS2) $(INCLUDE) $(DEBUG) -o $@ -c $<

# アプリファイル作成関連
POSTBUILD  = fltk-config --post

# oファイルから実行ファイルとappファイル作成
$(TARGET):$(OBJS)
	$(COMPILER) -o $(TARGETDIR)/$@ $(OBJS) $(LINK) $(LIBRARY) $(LDFLAGS)
	cp $(TARGETDIR)/$(TARGET) $(TARGET)
	$(POSTBUILD) $(TARGET)
	mkdir $(TARGET).app/Contents/Resources
	cp ./images/$(TARGET).icns $(TARGET).app/Contents/Resources
	plutil -insert 'CFBundleIconFile' -string $(TARGET).icns $(TARGET).app/Contents/Info.plist
	rm -f $(TARGET)

# 全ソース強制コンパイル
.PHONY:all
all: clean $(TARGET)

# 全ファイル削除
.PHONY:clean
clean:
	rm -rf $(OBJS) $(TARGETDIR)/$(TARGET) $(TARGET).app

[C++] 358 FLTKアプリ ビルド時のトラブル png.h

[Mac M2 Pro 12CPU, Sonoma 14.3.1, clang++ 15.0.0, FLTK 1.3.9]

アイコン画像作成アプリを久しぶりにビルドすると、png関連のシンボルが見つからずエラーを吐くようになりました。

これまでlibpngのpng.hへアクセスしていたのが、FL/images/png.hを読み込むようになってしまったために起こったトラブルでした。

ヘッダファイルのincludeを絶対パスにすると直りました。

Homebrewのライブラリをアップデートするとバージョン番号が変わるのでMakefileを更新する必要がありますが、それとは関係なく起こったレアなトラブルだったため原因究明に時間が掛かりました。

ld: Undefined symbols:
  _fltk_png_create_info_struct, referenced from:
      getInfoPNG(char const*) in processImage.o
  _fltk_png_create_read_struct, referenced from:
      getInfoPNG(char const*) in processImage.o
  _fltk_png_destroy_read_struct, referenced from:
      getInfoPNG(char const*) in processImage.o
  _fltk_png_get_channels, referenced from:
      getInfoPNG(char const*) in processImage.o
  _fltk_png_get_image_height, referenced from:
      getInfoPNG(char const*) in processImage.o
  _fltk_png_get_image_width, referenced from:
      getInfoPNG(char const*) in processImage.o
  _fltk_png_get_x_pixels_per_inch, referenced from:
      getInfoPNG(char const*) in processImage.o
  _fltk_png_get_y_pixels_per_inch, referenced from:
      getInfoPNG(char const*) in processImage.o
  _fltk_png_init_io, referenced from:
      getInfoPNG(char const*) in processImage.o
  _fltk_png_read_png, referenced from:
      getInfoPNG(char const*) in processImage.o
  _fltk_png_set_sig_bytes, referenced from:
      getInfoPNG(char const*) in processImage.o
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [Makefile:53: ImageInspector] Error 1
#ifndef PROCESSIMAGE_H
#define PROCESSIMAGE_H

#include <array>
// FL/imagesのpng.hを避けるため絶対パスに変更
#include "/opt/homebrew/Cellar/libpng/1.6.42/include/libpng16/png.h"

std::vector<std::string> split(std::string, char);
std::string join(const std::vector<std::string>& , const char*);

int checkImage(const char*);
std::array<int,5> getInfoPNG(const char*);

#define SIGNATURE_NUM 8

#endif

[C++] 357 ChatGPTアプリの製作 その42 “libcurlによるHTTP通信”の中断 curl_easy_cleanup 

[Mac M2 Pro 12CPU, Sonoma 14.3.1, clang++ 15.0.0, FLTK 1.3.8]

中断ボタンでlibcurlによるHTTP通信をabortしようとしましたが、かなり手こずりました。

C++では難しいためSwiftへの移植も考えたものの、相当な時間を掛けることになるのでもう一粘りしたところ、あっさり解決しました。

CURL* curl = curl_easy_init();のcurlをグローバル変数にして、中断コールバック関数内でcurl_easy_cleanup(curl);とすれば中断できるようになりました。

シングルスレッドであるFLTKにて送信ボタン押下状態で中断ボタンを押せるようにするにはstd::threadを用い送信コールバック関数(HTTP通信)を別スレッド化する必要があります。

最初は別スレッド化したHTTP通信のプロセスID(PID)をpgrepコマンドで探し、killコマンドで中断しようとしました。しかしstd::threadでは同じプロセスIDをメインスレッドと共用しているため、その方法は使えませんでした。

昨年2023/3/2にChatGPTアプリ開発に着手してから、ずっと抱えていた懸案をようやく解決できました。

libcurlでHTTP通信が容易に中断できない問題はStackOverFlowサイトでも暗礁に乗り上げていたので意外な結末でした。ChatGPTに聞いてもサンプルコードが少ないためか、珍しく迷回答頻発でした。

#include <thread>
#include <future>
#include <curl/curl.h>

extern CURL* curl;

void sendCBWrapper(Fl_Widget* w, void* v) {
    // promiseとfutureを作成
    std::promise<void> p;
    std::future<void> f = p.get_future();
    
    // sendCBを新しいスレッドで実行し、promiseを渡す
    std::thread([w, v, p = std::move(p)]() mutable {
        sendCB(w, v, std::move(p));
    }).detach();
    
    // 別のスレッドでfutureの結果を待機
    std::thread([f = std::move(f)]() mutable {
        f.wait(); // sendCBの終了を待機
        std::system("afplay /System/Library/Sounds/Submarine.aiff"); // システム音を鳴らす
        cout << "sendCB終了" << endl;
    }).detach();
}

void abortCB(Fl_Widget*, void*){
    if (curl){
        curl_easy_cleanup(curl);
        statusBox -> changeColor(YELLOW);
    } else {
        cout << "送信していません" << endl;
    }
}

[C++] 356 ChatGPTアプリの製作 その41 FLTKマルチスレッド化 promise, future

[Mac M2 Pro 12CPU, Sonoma 14.3.1, clang++ 15.0.0, FLTK 1.3.8]

ChatGPTアプリのアップデートは久々です。

cURL通信を中断する機能を実装するため、マルチスレッド対応に着手しました。

まずはリクエストの送信を別スレッドにしてデタッチ(切り離し)するようにしました。

これまでは受信があるまでGUIが固まっていたのが、他のボタンを押したりできるようになります。

ただし別スレッドの動作が終わってもその信号をGUIが受け取ることはできないため、システム音で知らせるようにしました。システム音が聞こえると自分でカーソルを少し動かして、メインスレッドに戻します。メインスレッドに戻るとリクエストの結果がGUIに表示されます。

次は中断ボタンを押すとコールバック関数で受信をabortするようにします。

#include <thread>
#include <future>

void sendCB(Fl_Widget*, void* data, std::promise<void>&& p) {

    <中略>

    p.set_value();
}

void sendCBWrapper(Fl_Widget* w, void* v) {
    // promiseとfutureを作成
    std::promise<void> p;
    std::future<void> f = p.get_future();
    
    // sendCBを新しいスレッドで実行し、promiseを渡す
    std::thread([w, v, p = std::move(p)]() mutable {
        sendCB(w, v, std::move(p));
    }).detach();
    
    // 別のスレッドでfutureの結果を待機
    std::thread([f = std::move(f)]() mutable {
        f.wait(); // sendCBの終了を待機
        std::system("afplay /System/Library/Sounds/Submarine.aiff"); // システム音を鳴らす
        cout << "sendCB終了" << endl;
    }).detach();
}

[C++] 355 ChatGPTアプリの製作 その40 GPT-4 Turbo with visionへの対応

[Mac M2 Pro 12CPU, MacOS Ventura 13.6, clang 15.0.0]

自製ChatGPTアプリをGPT-4 Turbo with visionに対応させました。

画像のURLを送ると内容を読み取り、プロンプトに対応します。

漢字、ひらがな、カタカナなど非ラテン文字は読めません。これらが画像に含まれているとレスポンスしなくなるので、モザイクを掛けるなり前処理が必要です。

プログラミングでUIについて質問する際に画像を使うとやりやすいです。特に見てほしいところを色線で囲むなど、工夫を入れるとより深い分析をしてくれます。ここまでできるとプログラミングスクールにとどまらず教育業界全体が相当な危機でしょう。

GPT-4までは心強いパートナーという感じでしたが、with visionになって凄まじい眼力を身に付け、いささか脅威を覚えるようになりました。

今のAIがどのような状況になっているのか、もっと周知しないとヤバい気がします。

[C++] 354 ChatGPTアプリの製作 その39 GPT-4 Turboへの対応

[Mac M2 Pro 12CPU, MacOS Ventura 13.6, clang 15.0.0]

自製ChatGPTアプリを本日11/7未明よりサービス開始となったGPT-4 Turboに対応させました。

たまに英語で回答が返ってくるため、最初の役割設定(role : sys)で日本語回答を必須にしました。

トレーニングデータは23年4月までですから、AppleのvisionOSに関する情報はカバーしていません。

トークンの上限がGPT-4の8,192から128,000になりました。ChatGPTでのやり取りが途切れることも大分減りそうです。また料金はGPT-4の3分の1なので、ある程度気にせず利用できます。

[C++] 353 Pythonスクリプトの実行(再) / Rustの現状

[Mac M2 Pro 12CPU, MacOS Ventura 13.6, clang 15.0.0]

以前にも扱いましたが、おさらいしておきます。

Rustでも簡単にPythonスクリプトを実行できます。ただRustは安全性が担保されない限りコーディングがなかなか先に進まないので、趣味としては楽しめないです。Javaのぬるぽ地獄に似たような窮屈さを感じます。

昨年2022年あたりがピークだったのか、今現在Rustブームは落ち着いているようです。プロダクト責任者に近い上級プログラマだけが熱を上げている印象です。Pythonのようなお手軽さが全くない言語ですから、ユーザーの新規参入が先細りにならないか心配です。

私としては、もしビジネスが絡んだら仕方なく使うというスタンスです。

#define PY_SSIZE_T_CLEAN
#include </Library/Frameworks/Python.framework/Versions/3.10/include/python3.10/Python.h>

void sheetNaming() {
    Py_Initialize();

    // pyファイルの指定(OutingCosts.py)
    PyObject* myModuleString = PyUnicode_FromString((char*)"OutingCosts");

    // pyファイルのモジュール化
    PyObject* myModule = PyImport_Import(myModuleString);

    // pyファイル内の関数を指定
    PyObject* myFunction = PyObject_GetAttrString(myModule,(char*)"outingcosts");

    // 関数の引数を設定
    PyObject* args = NULL;
    // 引数2つの場合
    // PyObject* args = PyTuple_Pack(2,PyUnicode_FromString(path),PyUnicode_FromString(path2)))

    // 関数を実行
    PyObject* myResult = PyObject_CallObject(myFunction, args);

    Py_Finalize();

    return ;

}

int main() {
    sheetNaming();

    return 0;
}
# コンパイラ設定
COMPILER = clang++
DEBUG = -g

# フラグ設定
CPPFLAGS = -std=c++20
LDFLAGS = -lc++

# includeパス(-I)
INCLUDE = -I./include \
-I/Library/Frameworks/Python.framework/Versions/3.10/include/python3.10

# ライブラリ(-l)
LIBRARY_l = -lpython3.10

# ライブラリパス(-L)
LIBRARY_L = -L/usr/local/lib -L/Library/Frameworks/Python.framework/Versions/3.10/lib

# ソースファイル
SRCDIR = ./src
SRCS = $(shell find $(SRCDIR) -type f)

# オブジェクトファイル
OBJDIR = ./obj
OBJS = $(addprefix $(OBJDIR), $(patsubst ./src/%.cpp,/%.o,$(SRCS)))

# 実行ファイル
TARGETDIR = ./bin
TARGET = test

# cppファイルからoファイル作成 $<:依存ファイル
$(OBJDIR)/%.o: $(SRCDIR)/%.cpp
	$(COMPILER) $(CPPFLAGS) $(INCLUDE) $(DEBUG) -o $@ -c $<

# oファイルから実行ファイル作成
$(TARGET):$(OBJS)
	$(COMPILER) -o $(TARGETDIR)/$@ $(OBJS) $(LIBRARY_l) $(LIBRARY_L) $(LDFLAGS)

# コンパイル&ビルド
.PHONY:all
all: clean $(OBJS) $(TARGET)

# oファイル・実行ファイル削除
.PHONY:clean
clean:
	rm -rf $(OBJS) $(TARGETDIR)/$(TARGET) $(TARGET).app
# 導入するPythonスクリプトのあるディレクトリ設定
export PYTHONPATH="/code/Python/library/python_module"

# importする自製ライブラリのあるディレクトリ設定
export PYTHONPATH="/code/Python/library"
RustでのPythonスクリプト実行例
Objective-CでのPythonスクリプト実行例