[C++] 319 BBS閲覧アプリの製作 その5 スレッドタイトルの取得 libxmlでパース

[M1 Mac, MacOS Ventura 13.3.1, clang 14.0.3]

libxmlを使ったスクレイピングに成功しました。

ただしPythonのBeautifulSoup & パーサー(html.parserなど)によるものとは違い、文字列比較で絞り込んでいく原始的な手法です。

今回の方法ではUTF-8への変換は不要でした。

IDやXPathを指定してパースできるのか、今後調査します。

#include <iostream>
#include <string>
#include <vector>
#include <curl/curl.h>
#include <libxml/HTMLparser.h>

std::vector<std::pair<std::string, std::string>> idTitlePairs;

std::string convertToString(const xmlChar* xmlString) {
    if (xmlString == nullptr) {
        return "";
    }
    return std::string(reinterpret_cast<const char*>(xmlString));
}

void parseAnchorTags(xmlNode* node) {
    for (xmlNode* cur = node; cur; cur = cur->next) {
        if (cur->type == XML_ELEMENT_NODE && xmlStrcmp(cur->name, (const xmlChar*)"small") == 0) {
            xmlChar* id = xmlGetProp(cur, (const xmlChar*)"id");
            if (xmlStrcmp(id, (const xmlChar*)"trad") == 0) {
                for (xmlNode* child = cur->children; child; child = child->next) {
                    if (child->type == XML_ELEMENT_NODE && xmlStrcmp(child->name, (const xmlChar*)"a") == 0) {
                        xmlChar* id0 = xmlGetProp(child, (const xmlChar*)"href");
                        xmlChar* title0 = xmlNodeListGetString(child->doc, child->children, 1);
                        // std::cout << "id: " << id0 << std::endl;
                        // std::cout << "title: " << title0 << std::endl;

                        string id = convertToString(id0);
                        id.erase(id.size() - 4);
                        string title = convertToString(title0);

                        idTitlePairs.push_back(std::make_pair(id, title));

                        xmlFree(id0);
                        xmlFree(title0);
                    }
                }
            }
            xmlFree(id);
        }
        parseAnchorTags(cur->children);
    }
}

// コールバック関数
size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* buffer) {
    size_t totalSize = size * nmemb;
    buffer->append((char*)contents, totalSize);
    return totalSize;
}

int main() {
    // URLからHTMLファイルを取り込む
    std::string url = "HTMLファイルのURL";
    std::string htmlBuffer;
    CURL* curl = curl_easy_init();
    if (curl) {
        curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &htmlBuffer);
        CURLcode res = curl_easy_perform(curl);
        curl_easy_cleanup(curl);
        if (res != CURLE_OK) {
            std::cerr << "Error: Failed to download HTML" << std::endl;
            return 1;
        }
    } else {
        std::cerr << "Error: Failed to initialize CURL" << std::endl;
        return 1;
    }

    // htmlBufferの確認
    // cout << "htmlBuffer: \n" << htmlBuffer << endl;

    xmlDoc* doc = htmlReadMemory(htmlBuffer.c_str(), htmlBuffer.size(), nullptr, nullptr, HTML_PARSE_RECOVER);
    if (doc) {
        xmlNode* root = xmlDocGetRootElement(doc);
        parseAnchorTags(root);
        xmlFreeDoc(doc);
    }

    for (const auto& pair : idTitlePairs) {
        std::cout << "id: " << pair.first << std::endl;
        std::cout << "title: " << pair.second << std::endl;
    }

    return 0;
}

[C++] 318 BBS閲覧アプリの製作 その4 スレッドタイトルの取得 HTMLパーサーを使わず文字列処理

[M1 Mac, MacOS Ventura 13.3.1, clang 14.0.3]

tidy-html5やlibxml2といった有名どころのHTMLパーサーを使おうとしたものの、上手く使いこなせません。

文字列加工関数や正規表現を使ってスレッドIDとスレッドタイトルを2次元vectorにまとめました。

#include <iostream>
#include <string>
#include <vector>
#include <regex>
#include <curl/curl.h>
#include <iconv.h>

// コールバック関数
size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* buffer) {
    size_t totalSize = size * nmemb;
    buffer->append((char*)contents, totalSize);
    return totalSize;
}

std::string ConvertShiftJISToUTF8(const std::string& input) {
    std::string output;

    iconv_t cd = iconv_open("UTF-8", "CP932");
    if (cd == (iconv_t)-1) {
        std::cerr << "Error: Failed to open iconv" << std::endl;
        return output;
    }

    size_t inBytes = input.size();
    size_t outBytes = inBytes * 4; // 変換後の最大バイト数を予測して確保する

    char* inBuf = const_cast<char*>(input.c_str());
    char* outBuf = new char[outBytes];
    char* outPtr = outBuf;

    if (iconv(cd, &inBuf, &inBytes, &outPtr, &outBytes) == (size_t)-1) {
        std::cerr << "Error: Failed to convert encoding: " << strerror(errno) << std::endl;
        delete[] outBuf;
        iconv_close(cd);
        return output;
    }

    output.assign(outBuf, outPtr - outBuf);

    delete[] outBuf;
    iconv_close(cd);

    return output;
}

int main() {
    // URLからHTMLファイルを取り込む
    std::string url = "HTMLファイルのURL";
    std::string htmlBuffer;
    CURL* curl = curl_easy_init();
    if (curl) {
        curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &htmlBuffer);
        CURLcode res = curl_easy_perform(curl);
        curl_easy_cleanup(curl);
        if (res != CURLE_OK) {
            std::cerr << "Error: Failed to download HTML" << std::endl;
            return 1;
        }
    } else {
        std::cerr << "Error: Failed to initialize CURL" << std::endl;
        return 1;
    }

    // 文字コードをCP932(Microsoftの拡張Shift-JIS)からUTF-8へ変換
    htmlBuffer = ConvertShiftJISToUTF8(htmlBuffer);

    // htmlBufferの確認
    // cout << "htmlBuffer: \n" << htmlBuffer << endl;

    // smallタグ部分を抽出
    std::string delimiter = "<small id=\"trad\">";
    htmlBuffer = htmlBuffer.substr(htmlBuffer.find(delimiter) + delimiter.length());
    delimiter = "</small>";
    htmlBuffer = htmlBuffer.substr(0, htmlBuffer.find(delimiter));

    // std::cout << "抽出内容:\n" << htmlBuffer << std::endl;

    // 正規表現を使ってaタグ内のhrefとaタグの内容を抽出 (.*?)部分
    std::regex pattern("<a.*?href=\"(.*?)/l50\".*?>(.*?)</a>");
    std::smatch matches;

    std::string::const_iterator searchStart(htmlBuffer.cbegin());
    std::vector<std::pair<std::string, std::string>> idTitlePairs;
    while (std::regex_search(searchStart, htmlBuffer.cend(), matches, pattern)) {
        std::string id = matches[1];
        std::string title = matches[2];
        idTitlePairs.push_back(std::make_pair(id, title));
        searchStart = matches.suffix().first;
    }

    for (const auto& pair : idTitlePairs) {
        std::cout << "id: " << pair.first << std::endl;
        std::cout << "title: " << pair.second << std::endl;
    }

    return 0;
}

[C++] 317 BBS閲覧アプリの製作 その3 スレッドタイトルの取得 curl

[M1 Mac, MacOS Ventura 13.3.1, clang 14.0.3]

まずcurlライブラリを使ってHTMLファイルを取り込みます。

文字コードがシフトJISであってもMicrosoftの拡張シフトJISの場合はCP932を使わないと文字化けを起こします。

#include <curl/curl.h>
#include <iconv.h>

std::string ConvertShiftJISToUTF8(const std::string& input) {
    std::string output;

    iconv_t cd = iconv_open("UTF-8", "CP932"); // SHIFT-JISではエラーになった
    if (cd == (iconv_t)-1) {
        std::cerr << "Error: Failed to open iconv" << std::endl;
        return output;
    }

    size_t inBytes = input.size();
    size_t outBytes = inBytes * 4; // 変換後の最大バイト数を予測して確保する

    char* inBuf = const_cast<char*>(input.c_str());
    char* outBuf = new char[outBytes];
    char* outPtr = outBuf;

    if (iconv(cd, &inBuf, &inBytes, &outPtr, &outBytes) == (size_t)-1) {
        std::cerr << "Error: Failed to convert encoding: " << strerror(errno) << std::endl;
        delete[] outBuf;
        iconv_close(cd);
        return output;
    }

    output.assign(outBuf, outPtr - outBuf);

    delete[] outBuf;
    iconv_close(cd);

    return output;
}

int main() {
    // URLからHTMLファイルを取り込む
    std::string url = "HTMLファイルのURL";
    std::string htmlBuffer;
    CURL* curl = curl_easy_init();
    if (curl) {
        curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &htmlBuffer);
        CURLcode res = curl_easy_perform(curl);
        curl_easy_cleanup(curl);
        if (res != CURLE_OK) {
            std::cerr << "Error: Failed to download HTML" << std::endl;
            return 1;
        }
    } else {
        std::cerr << "Error: Failed to initialize CURL" << std::endl;
        return 1;
    }

    // 文字コードをCP932(Microsoftの拡張Shift-JIS)からUTF-8へ変換
    htmlBuffer = ConvertShiftJISToUTF8(htmlBuffer);

    // htmlBufferの確認
    cout << "htmlBuffer: \n" << htmlBuffer << endl;

<以下略>

[C++,Python] 316 BBS閲覧アプリの製作 その2 スレッドタイトルの取得 Python編

[M1 Mac, MacOS Ventura 13.3.1, Python 3.10.4]

スレッドタイトルとスレッドIDを取得するPythonスクリプトを書きました。

次はこのスクリプトをC++へ変換したいです。難しくなるようであればモジュール化します。

機能はPythonで下書きしてC++へ変換あるいはモジュール化、GUIはFLTK(C++)でコーディングしていきます。

import requests
import re
from bs4 import BeautifulSoup

# URLからHTMLファイルを取り込む
url = 'スレッドタイトル表示URL(HTMLファイル)'
response = requests.get(url)
response.encoding = response.apparent_encoding
html = response.text

# BeautifulSoupを使用してHTMLを解析する
soup = BeautifulSoup(html, 'html.parser')

# <small id="trad">タグで囲まれた部分の内容を取得する
trad_tags = soup.find_all('small', id='trad')

thread_list = []
for trad_tag in trad_tags:
    content = trad_tag.get_text()

    # <a>タグのhref属性の値を正規表現で取得する
    pattern = r'<a\s+href=[\'"]([^\'"]+)[\'"]'
    href = re.findall(pattern, str(trad_tag))

    # contentをaタグごとに分割してリストにする
    content_list = re.split(r'<a\s+href=[\'"][^\'"]+[\'"]', str(trad_tag))
    
    print(f"hrefの要素数: {len(href)}")
    print(f"content_listの要素数: {len(content_list)}")

    # 辞書型データのリストを作成する
    for i in range(len(href)):
        thread_list.append({'href': (href[i])[:-4], 'content': (content_list[i+1].strip().replace("</a>","").replace("</small>",""))[1:]})

# リストの内容を出力する
for thread in thread_list:
    print(thread['href'])
    print(thread['content'])

[C++,Python] 315 BBS閲覧アプリの製作 その1 DATファイルの保存

[M1 Mac, MacOS Ventura 13.3.1, clang 14.0.3]

とあるBBSのDATファイルへのアクセスが可能になったようなので、早速遊んでみることにしました。

とりあえずDATファイルをダウンロードしてみます。PythonスクリプトをChatGPTに変換してもらったコードがそのまま使えました。

DATファイルの文字コードがシフトJISですから、Macの場合はUTF-8に変換する必要がありますね。

#include <iostream>
#include <fstream>
#include <curl/curl.h>

size_t WriteCallback(void* contents, size_t size, size_t nmemb, void* userp) {
    std::ofstream* file = static_cast<std::ofstream*>(userp);
    file->write(static_cast<char*>(contents), size * nmemb);
    return size * nmemb;
}

int main() {
    std::string url = "DATファイルのurl";
    std::string filename = "保存先DATファイルのパス";

    CURL* curl = curl_easy_init();
    if (curl) {
        std::ofstream file(filename, std::ios::binary);
        curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &file);
        CURLcode res = curl_easy_perform(curl);
        if (res != CURLE_OK) {
            std::cerr << "Error: " << curl_easy_strerror(res) << std::endl;
        }
        curl_easy_cleanup(curl);
    } else {
        std::cerr << "Failed to initialize curl" << std::endl;
    }

    return 0;
}

[C++] 314 画像加工アプリ チャンネル数取得 RGBA

[M1 Mac, MacOS Ventura 13.3.1, clang 14.0.3]

画像加工アプリにpngのチャンネル数を取得する機能を追加しました。libpngを使います。

RGBAであれば4、アルファチャンネルのないRGBであれば3になります。

unsigned int width;
unsigned int height;
unsigned int res_x;
unsigned int res_y;
unsigned int readSize;
unsigned int channel;

std::array<int,5> getInfoPNG(const char* filename){
	fi = fopen(filename, "rb");
	readSize = fread(signature, 1, SIGNATURE_NUM, fi);

	png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
	info = png_create_info_struct(png);

	png_init_io(png, fi);
	png_set_sig_bytes(png, readSize);
	png_read_png(png, info, PNG_TRANSFORM_PACKING | PNG_TRANSFORM_STRIP_16, NULL);
	
	width = png_get_image_width(png, info);
  	height = png_get_image_height(png, info);
	res_x = png_get_x_pixels_per_inch(png,info);
	res_y = png_get_y_pixels_per_inch(png,info);
	channel = png_get_channels(png, info);

	png_destroy_read_struct(&png, &info, NULL);
  	fclose(fi);

	return {(int)width,(int)height,(int)res_x,(int)res_y, (int)channel};
}

[C++] 313 Makefileでのライブラリ設定 MacOS

[M1 Mac, MacOS Ventura 13.3.1, clang 14.0.3]

Linuxはどうなっているのか分かりませんが、MacOSでのライブラリ設定は結構ややこしいです。

ライブラリパスを.bash_profileでDYLD_LIBRARY_PATHとして設定しておかないとビルドはできても実行はできません。

仕組みがよく分からないまま放置していましたが、ようやく明確にできました。

デフォルトのライブラリパスである/usr/local/libなどに配置すれば今回のような設定は不要です。他のライブラリとは分けて管理したかったためライブラリパスを追加した次第です。

# D2XXライブラリの場合
# /usr/local/D2XX/libにはlibftd2xx.aとlibftd2xx.dylibを配置

# includeパス(-I)
INCLUDE = -I/usr/local/D2XX/include

# ライブラリ(-l) 動的ライブラリ優先
LIBS = -lftd2xx
# 静的ライブラリのフルパス(実行ファイルに取り込まれる、ライブラリパス不要)
# LIBS = /usr/local/D2XX/lib/libftd2xx.a
# 動的ライブラリのフルパスは使えない

# ライブラリパス(-L) .bash_profileでDYLD_LIBRARY_PATHとしての設定も必須
LIBPATH = -L/usr/local/D2XX/lib

includeパスは.bash_profileでも設定できる。

# D2XX
export CPLUS_INCLUDE_PATH=$CPLUS_INCLUDE_PATH:/usr/local/D2XX/include

export DYLD_LIBRARY_PATH="/usr/local/D2XX/lib:$DYLD_LIBRARY_PATH"

DYLD_LIBRARY_PATHを設定していない場合の実行時エラーは以下の通り。echo $DYLD_LIBRARY_PATHでは.bash_profileで設定したライブラリパスしか確認できない。デフォルトの確認方法は不明。

~ $ test ; exit;
dyld[17390]: Library not loaded: libftd2xx.dylib
  Referenced from: <E59057E2-6B27-3B0F-9FD2-A568CC6B0F5D> test
  Reason: tried: '/opt/homebrew/Cellar/exiv2/0.27.5_1/lib/libftd2xx.dylib' (no such file), '/libftd2xx.dylib' (no such file), 'libftd2xx.dylib' (no such file), 
'/System/Volumes/Preboot/Cryptexes/OSlibftd2xx.dylib' (no such file), 
'libftd2xx.dylib' (no such file), '/usr/local/lib/libftd2xx.dylib' (no such file), 
'/usr/lib/libftd2xx.dylib' (no such file, not in dyld cache), 
'/opt/homebrew/Cellar/exiv2/0.27.5_1/lib/libftd2xx.dylib' (no such file), 
'/libftd2xx.dylib' (no such file), '/Users/xxx/libftd2xx.dylib' (no such file),
 '/System/Volumes/Preboot/Cryptexes/OS/Users/xxx/libftd2xx.dylib' (no such file), 
'/Users/xxx/libftd2xx.dylib' (no such file), '/usr/local/lib/libftd2xx.dylib' (no such file), 
'/usr/lib/libftd2xx.dylib' (no such file, not in dyld cache)
Abort trap: 6
logout

[C++] 312 FTDI FT-XシリーズをBit Bangモードで使う MacOS

[M1 Mac, MacOS Ventura 13.3.1, clang 14.0.3]

FTDIの技術資料にあるサンプルコードをApple Silicon Macで動くようにしました。いつものように非IDE環境です。

AN_373 Bit-Bang Modes for the FT-X Series

D2XXドライバをダウンロードして、適当な所に配置します。今回は/usr/localディレクトリにしました。

Windows用のコードをApple Siliconにて非IDE環境で使えるようにする、といった類いの作業は自分なりの手法を確立させているので、比較的はかどります。やっていて楽しい作業です。今回はCocoaフレームワークとIOKitフレームワークのビルドオプションへの追加が肝でした。

FT231Xを使ってAVRマイコンに書き込みできるのか検証中です。

ただ、Bit Bangの解説資料ではあるもののフル機能を使えるわけではないような気がします。

// #include <Windows.h> 元はWindows用
#include <stdio.h>
#include <stdint.h>
#include "ftd2xx.h"

FT_STATUS ftStatus;
FT_HANDLE ftHandle;
DWORD w_data_len = 3; //write 3 bytes
DWORD data_written; // number of bytes written
UCHAR Mask = 0x0F;  //00001111 Set D7-D4 input, D3-D0 output [input (0) and output (1)]
UCHAR Mode = 0x04;  //0x04 = synchronous bit-bang
DWORD RxBytes; //number of bytes to be read from the device
DWORD BytesReceived; //number of bytes read from the device
uint8_t RxBuffer[8]; //buffer to receive data from FT-X device
uint8_t data_out[8]; //buffer for data to be sent to FT-X device
unsigned int i;

int main() 
{
    ftStatus = FT_Open(0, &ftHandle);
    ftStatus |= FT_SetUSBParameters(ftHandle, 4096, 4096); // Set USB transfer sizes
    ftStatus |= FT_SetChars(ftHandle, false, 0, false, 0); // Disable event characters
    ftStatus |= FT_SetTimeouts(ftHandle, 5000, 5000); // Set read/write timeouts to 5 sec
    ftStatus |= FT_SetLatencyTimer(ftHandle, 16); // Latency timer at default 16ms
    ftStatus |= FT_SetFlowControl(ftHandle, FT_FLOW_NONE, 0x11, 0x13);
    ftStatus |= FT_SetBaudRate(ftHandle, 62500); //bit rate is x16 this value = 1M

<以下略>
# コンパイラ設定
COMPILER = clang++
DEBUG = -g

# オプション設定
CPPFLAGS = -std=c++20
LDFLAGS = -lc++ -framework Cocoa -framework IOKit

# includeパス(-I)
INCLUDE = -I./include \
-I/usr/local/D2XX

# ライブラリ(-l)
LIBRARY_l = /usr/local/D2XX/build/libftd2xx.a

# ライブラリパス(-L)
LIBRARY_L = -L/usr/local/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

[C++] 311 インクルードパスの確認方法 #include_nextトラブル

[M1 Mac, Ventura 13.3.1, clang 13.0.0]

MacOSをMontereyからVenturaに上げるとコンパイル時に以下のエラーが出るようになりました。

fatal error: 'stdio.h' file not found
#include_next <stdio.h>

Command Line Toolsを入れ直してもダメでXcodeを削除したりしているといつの間にか正常になりました。Xcodeを解決後に再インストールしても問題ありませんでした。MacOSをバージョンアップするたびにこの種のトラブルが必ず起こるので困ったものです。

rm -rf /Library/Developer/CommandLineTools
xcode-select --install

ところで#include_nextはインクルードパスにある別の同名ヘッダファイルを読み込む指示です。インクルードパスは以下のコマンドで確認できます。

clang++ -x c++ -v -E /dev/null
$ clang++ -x c++ -v -E /dev/null
Apple clang version 14.0.3 (clang-1403.0.22.14.1)
Target: arm64-apple-darwin22.4.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin
 "/Library/Developer/CommandLineTools/usr/bin/clang" -cc1 -triple arm64-apple-macosx13.0.0 -Wundef-prefix=TARGET_OS_ -Wdeprecated-objc-isa-usage -Werror=deprecated-objc-isa-usage -Werror=implicit-function-declaration -E -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name null -mrelocation-model pic -pic-level 2 -mframe-pointer=non-leaf -fno-strict-return -ffp-contract=on -fno-rounding-math -funwind-tables=1 -fobjc-msgsend-selector-stubs -target-sdk-version=13.3 -fvisibility-inlines-hidden-static-local-var -target-cpu apple-m1 -target-feature +v8.5a -target-feature +crc -target-feature +lse -target-feature +rdm -target-feature +crypto -target-feature +dotprod -target-feature +fp-armv8 -target-feature +neon -target-feature +fp16fml -target-feature +ras -target-feature +rcpc -target-feature +zcm -target-feature +zcz -target-feature +fullfp16 -target-feature +sm4 -target-feature +sha3 -target-feature +sha2 -target-feature +aes -target-abi darwinpcs -fallow-half-arguments-and-returns -debugger-tuning=lldb -target-linker-version 857.1 -v -fcoverage-compilation-dir=/Users/user -resource-dir /Library/Developer/CommandLineTools/usr/lib/clang/14.0.3 -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -I/usr/local/include -stdlib=libc++ -internal-isystem /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1 -internal-isystem /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/local/include -internal-isystem /Library/Developer/CommandLineTools/usr/lib/clang/14.0.3/include -internal-externc-isystem /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include -internal-externc-isystem /Library/Developer/CommandLineTools/usr/include -Wno-reorder-init-list -Wno-implicit-int-float-conversion -Wno-c99-designator -Wno-final-dtor-non-final-class -Wno-extra-semi-stmt -Wno-misleading-indentation -Wno-quoted-include-in-framework-header -Wno-implicit-fallthrough -Wno-enum-enum-conversion -Wno-enum-float-conversion -Wno-elaborated-enum-base -Wno-reserved-identifier -Wno-gnu-folding-constant -fdeprecated-macro -fdebug-compilation-dir=/Users/user -ferror-limit 19 -stack-protector 1 -fstack-check -mdarwin-stkchk-strong-link -fblocks -fencode-extended-block-signature -fregister-global-dtors-with-atexit -fgnuc-version=4.2.1 -fno-cxx-modules -no-opaque-pointers -fcxx-exceptions -fexceptions -fmax-type-align=16 -fcommon -fcolor-diagnostics -clang-vendor-feature=+disableNonDependentMemberExprInCurrentInstantiation -fno-odr-hash-protocols -clang-vendor-feature=+enableAggressiveVLAFolding -clang-vendor-feature=+revert09abecef7bbf -clang-vendor-feature=+thisNoAlignAttr -clang-vendor-feature=+thisNoNullAttr -mllvm -disable-aligned-alloc-awareness=1 -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o - -x c++ /dev/null
clang -cc1 version 14.0.3 (clang-1403.0.22.14.1) default target arm64-apple-darwin22.4.0
ignoring nonexistent directory "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/local/include"
ignoring nonexistent directory "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/Library/Frameworks"
#include "..." search starts here:
#include <...> search starts here:
 /usr/local/include
 /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1
 /Library/Developer/CommandLineTools/usr/lib/clang/14.0.3/include
 /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include
 /Library/Developer/CommandLineTools/usr/include
 /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks (framework directory)
End of search list.
# 1 "/dev/null"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 428 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "/dev/null" 2
$ clang++ -x c++ -v -E /dev/null
Apple clang version 14.0.3 (clang-1403.0.22.14.1)
Target: arm64-apple-darwin22.4.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
 "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang" -cc1 -triple arm64-apple-macosx13.0.0 -Wundef-prefix=TARGET_OS_ -Wdeprecated-objc-isa-usage -Werror=deprecated-objc-isa-usage -Werror=implicit-function-declaration -E -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name null -mrelocation-model pic -pic-level 2 -mframe-pointer=non-leaf -fno-strict-return -ffp-contract=on -fno-rounding-math -funwind-tables=1 -fobjc-msgsend-selector-stubs -target-sdk-version=13.3 -fvisibility-inlines-hidden-static-local-var -target-cpu apple-m1 -target-feature +v8.5a -target-feature +crc -target-feature +lse -target-feature +rdm -target-feature +crypto -target-feature +dotprod -target-feature +fp-armv8 -target-feature +neon -target-feature +fp16fml -target-feature +ras -target-feature +rcpc -target-feature +zcm -target-feature +zcz -target-feature +fullfp16 -target-feature +sm4 -target-feature +sha3 -target-feature +sha2 -target-feature +aes -target-abi darwinpcs -fallow-half-arguments-and-returns -debugger-tuning=lldb -target-linker-version 857.1 -v -fcoverage-compilation-dir=/Volumes/DATA_m1/code/cpp/projects/SFTPAssist -resource-dir /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/14.0.3 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk -I/usr/local/include -stdlib=libc++ -internal-isystem /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1 -internal-isystem /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/local/include -internal-isystem /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/14.0.3/include -internal-externc-isystem /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include -internal-externc-isystem /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include -Wno-reorder-init-list -Wno-implicit-int-float-conversion -Wno-c99-designator -Wno-final-dtor-non-final-class -Wno-extra-semi-stmt -Wno-misleading-indentation -Wno-quoted-include-in-framework-header -Wno-implicit-fallthrough -Wno-enum-enum-conversion -Wno-enum-float-conversion -Wno-elaborated-enum-base -Wno-reserved-identifier -Wno-gnu-folding-constant -fdeprecated-macro -fdebug-compilation-dir=/Volumes/DATA_m1/code/cpp/projects/SFTPAssist -ferror-limit 19 -stack-protector 1 -fstack-check -mdarwin-stkchk-strong-link -fblocks -fencode-extended-block-signature -fregister-global-dtors-with-atexit -fgnuc-version=4.2.1 -fno-cxx-modules -no-opaque-pointers -fcxx-exceptions -fexceptions -fmax-type-align=16 -fcommon -fcolor-diagnostics -clang-vendor-feature=+disableNonDependentMemberExprInCurrentInstantiation -fno-odr-hash-protocols -clang-vendor-feature=+enableAggressiveVLAFolding -clang-vendor-feature=+revert09abecef7bbf -clang-vendor-feature=+thisNoAlignAttr -clang-vendor-feature=+thisNoNullAttr -mllvm -disable-aligned-alloc-awareness=1 -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o - -x c++ /dev/null
clang -cc1 version 14.0.3 (clang-1403.0.22.14.1) default target arm64-apple-darwin22.4.0
ignoring nonexistent directory "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/local/include"
ignoring nonexistent directory "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/Library/Frameworks"
#include "..." search starts here:
#include <...> search starts here:
 /usr/local/include
 /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1
 /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/14.0.3/include
 /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include
 /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include
 /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks (framework directory)
End of search list.
# 1 "/dev/null"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 428 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "/dev/null" 2

今回のトラブルで、私のような非IDE派はCommand Line Toolsさえあればプログラミングできる、つまりXcodeがなくても問題ないということを再確認しました。Xcodeは20GB超えの重量アプリですから、ストレージ不足になったら真っ先に削除するようにします。

[C++] 310 FLTK : 長整数型を引数とするcallback関数 その2

[M1 Mac, Ventura 13.3.1, clang 13.0.0, FLTK 1.3.8, NO IDE]

callback関数の引数に長整数を使うことで第1引数の動作関数に引数として追加できます。

任意の長整数により動作関数の内容を条件分けできます。例えばFileChooserで選んだファイルパスの表示先を変えることが出来たりします。

使用頻度は高くないですが、たまに使おうとすると殆ど忘れてしまっているので再度記事にしておきます。構造がややこしくて共同開発であれば現場を混乱させそうな内容です。

久しぶりにC++のコードを書きましたが、だいぶPythonになじんでいたので調子が出ないです。

// fileFcBtn
fileFcBtn = new Fl_Button(245,108,30,20,"...");
fileFcBtn->color(fl_rgb_color(112,128,144));
fileFcBtn->labelcolor(fl_rgb_color(255,239,213));
fileFcBtn->labelsize(14);
fileFcBtn->callback(FileChooserCB,1); // 長整数1を渡す

// toFcBtn
toFcBtn = new Fl_Button(245,143,30,20,"...");
toFcBtn->color(fl_rgb_color(112,128,144));
toFcBtn->labelcolor(fl_rgb_color(255,239,213));
toFcBtn->labelsize(14);
toFcBtn->callback(FileChooserCB,2); // 長整数2を渡す
void FileChooserCB(Fl_Widget*, long num) {
    int fc;
    string appdir = "/";
    int onoff_dir = dir_rbtn->value();

    if (onoff_dir == 1){
        fc = 4; // FileChooser::DIRECTORY
    } else {
        fc = 0; // FileChooser::SINGLE
    }

    int x_win = window->x_root();
    int y_win = window->y_root();
    cout<<"x_win "<< x_win <<" y_win "<< y_win <<endl;

    chooser = new FileChooser(appdir.c_str(), 
                        "*.*",
                        fc,
                        "File/Dir Chooser",490,380, num
                        );
    
    chooser->resize(x_win+85,y_win+50,490,380);
    chooser->set_modal();
    chooser->Fl_Window::show();

    while(chooser->Fl_Window::shown()){
        Fl::wait();
    }
}
FileChooser::FileChooser(const char *d, const char *p, 
int t, const char *title,int w,int h, long num)
	:Fl_Window(w,h,title),Fl_File_Chooser(d,p,t,title){

<中略>

btnOK = new Fl_Return_Button(313, 345, 85, 25, "OK");
		if (num==1){
			btnOK->callback((Fl_Callback*)btnOKCB);
		} else {
			btnOK->callback((Fl_Callback*)btnOKCB2);
		}
void btnOKCB(Fl_Return_Button*, void*)
{
	const char* file = inputFileName->value();

	file_input->value("");
	file_input->value(file);
	chooser->Fl_Window::hide();
}

void btnOKCB2(Fl_Return_Button*, void*)
{
	const char* file = inputFileName->value();

	to_input->value("");
	to_input->value(file);
	chooser->Fl_Window::hide();
}
FileChooser
FileChooser選択パスの表示先がFILE/DIR欄に固定されていてTO欄が使えていない
修正後はボタン別に表示先を変えられるようになった
類似記事