[C++] 205 FLTK : Visual C++への移植 その4 clコンパイラでlib, dllファイル作成

[Windows11, MinGW g++ 9.2.0, Visual C++17, FLTK 1.3.8, Visual Studio 2022]

Visual Studioでのlibファイル作成方法にヒントを得て、再度clコンパイラでライブラリ作成を試みたところうまくいきました。静的ライブラリlibと動的ライブラリdllが作成されます。

コマンドは以前のままで、cppファイルとヘッダファイルの関数の先頭に__declspec(dllexport)を付けるだけです。付けないとdllファイルのみ作成になります。

そもそも付けない場合にlibファイル、dllファイル両方作成するのが普通の感覚ではないのかと思ったりしますが、何かとクセが強いMicrosoftですから仕方ないです。

この方法でいけるとなると、Visual Studioでプロジェクトからlibファイル、dllファイルを作成するなんて面倒すぎてやってられません。

日本語まわりの仕様など洗練されているように見えるVisual C++ですが、実際に扱ってみるとなるほど中々の難物でした。まさに隣の芝はなんとやらです。

#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <stdio.h>
#include <ctype.h>
#include <memory>

using std::string; using std::vector;
using std::cout; using std::endl;

class Funcs{

public:
__declspec(dllexport) string capitalizeString(string);
__declspec(dllexport) int getIndex(vector<string>&, string);
};
#include "Funcs.h"

__declspec(dllexport) string Funcs::capitalizeString(string s)
{
    vector<char> s_char(s.begin(), s.end());

    vector<char> s_char_up;
    for (auto && c:s_char){
        char c_up = toupper(c);
        s_char_up.push_back(c_up);
    }

    string str(s_char_up.begin(), s_char_up.end());

    return str;
}

__declspec(dllexport) int Funcs::getIndex(vector<string> &input, string searched) {
    for (int i = 0; i < input.size(); i++) {
        if (input[i] == searched) {
            return i;
        }
    }
    return -1;
}
cl /std:c++17 /I"D:\code\VC++\mylib_vc\include" /I"C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.30.30705\include" ^
/I"C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.30.30705\atlmfc\include" ^
/I"C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\VS\include" ^
/I"C:\Program Files (x86)\Windows Kits\10\Include\10.0.22000.0\ucrt" ^
/I"C:\Program Files (x86)\Windows Kits\10\Include\10.0.22000.0\um" ^
/I"C:\Program Files (x86)\Windows Kits\10\Include\10.0.22000.0\shared" ^
/I"C:\Program Files (x86)\Windows Kits\10\Include\10.0.22000.0\winrt" ^
/I"C:\Program Files (x86)\Windows Kits\10\Include\10.0.22000.0\cppwinrt" ^
/I"C:\Program Files (x86)\Windows Kits\NETFXSDK\4.8\Include\um" ^
"D:\code\VC++\mylib_vc\src\Funcs.cpp" /LD /EHsc /link /LIBPATH:"C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.30.30705\ATLMFC\lib\x64" /LIBPATH:"C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.30.30705\lib\x64" /LIBPATH:"C:\Program Files (x86)\Windows Kits\NETFXSDK\4.8\lib\um\x64" /LIBPATH:"C:\Program Files (x86)\Windows Kits\10\lib\10.0.22000.0\ucrt\x64" /LIBPATH:"C:\Program Files (x86)\Windows Kits\10\\lib\10.0.22000.0\\um\x64" /LIBPATH:"D:\code\VC++\mylib_vc\lib"

[C++] 204 FLTK : Visual C++への移植 その3 VSでlib, dllファイル作成

[Windows11, MinGW g++ 9.2.0, Visual C++17, FLTK 1.3.8, Visual Studio 2022]

ライブラリをclコンパイラではなく、Visual Studioでの作成に切り替えました。

今回は静的ライブラリSplit.libと動的ライブラリSplit.dllを作成しました。2時間ほど掛かりました。やはりIDEは勘弁というのが実感です。

Macならコンソールだけでサクッと作れるんですけどね。Windows(Visual Studio)がプログラミングの入口だったら、C/C++ユーザーに成れていたか微妙です。

これでエラー数は1つ減り23箇所になりました。

[C++] 203 FLTK : Visual C++への移植 その2 dllファイル

[Windows11, MinGW g++ 9.2.0, Visual C++17, FLTK 1.3.8, Visual Studio 2022]

カラーアプリMinGW版のVisual C++への移植が遅々として進みません。

コンパイルはクリアしましたが、案の定ビルドで29箇所のエラーに見舞われました。

clコンパイラで作成した自製dllファイルが全て”未解決の外部シンボル”エラーとなり、ライブラリ化はあきらめてcppファイルとしてプロジェクトに追加しています。

コード編集はVSCode、コンパイル&ビルドはVisual Studioと役割分担しています。Visual Studioはエディタとしては使えないです。

dllファイルの変更が完了すれば、エラーはだいぶ減りそうです。

[C++] 202 Visual C++におけるglob関数の代替方法

[Windows11, Visual C++17]

Visual C++ではPOSIX固有のglob関数が使えないため、std::filesystem::directory_iteratorを使用します。

ソースコード1つなのでコマンドプロンプトでコンパイル&ビルドしました。

#include <iostream>
#include <string>
#include <vector>
#include <filesystem>

int main()
{
	std::string dir_name = "D:\\code\\VC++\\mylib_vc\\src";
	std::vector<std::string> files;
	std::filesystem::path dir_path(dir_name);

	if (!std::filesystem::is_directory(dir_path)) {
		std::cout << "ディレクトリではありません" << std::endl;
		return -1;
	}
	auto dir = std::filesystem::directory_iterator(dir_path);
	for (auto& p : dir) {
		std::string pathname = p.path().string();
		std::cout << p.path().string() << std::endl;
		files.push_back(pathname); // リスト化して利用
	}
}
cl /std:c++17 /I"D:\code\VC++\mylib_vc\include" /I"C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.30.30705\include" ^
/I"C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.30.30705\atlmfc\include" ^
/I"C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\VS\include" ^
/I"C:\Program Files (x86)\Windows Kits\10\Include\10.0.22000.0\ucrt" ^
/I"C:\Program Files (x86)\Windows Kits\10\Include\10.0.22000.0\um" ^
/I"C:\Program Files (x86)\Windows Kits\10\Include\10.0.22000.0\shared" ^
/I"C:\Program Files (x86)\Windows Kits\10\Include\10.0.22000.0\winrt" ^
/I"C:\Program Files (x86)\Windows Kits\10\Include\10.0.22000.0\cppwinrt" ^
/I"C:\Program Files (x86)\Windows Kits\NETFXSDK\4.8\Include\um" ^
"D:\code\VC++\test\test02\FindFile.cpp" /EHsc /link /LIBPATH:"C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.30.30705\ATLMFC\lib\x64" /LIBPATH:"C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.30.30705\lib\x64" /LIBPATH:"C:\Program Files (x86)\Windows Kits\NETFXSDK\4.8\lib\um\x64" /LIBPATH:"C:\Program Files (x86)\Windows Kits\10\lib\10.0.22000.0\ucrt\x64" /LIBPATH:"C:\Program Files (x86)\Windows Kits\10\\lib\10.0.22000.0\\um\x64" 
Microsoft(R) C/C++ Optimizing Compiler Version 19.30.30706 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

FindFile.cpp
Microsoft (R) Incremental Linker Version 14.30.30706.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:FindFile.exe
"/LIBPATH:C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.30.30705\ATLMFC\lib\x64"
"/LIBPATH:C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.30.30705\lib\x64"
"/LIBPATH:C:\Program Files (x86)\Windows Kits\NETFXSDK\4.8\lib\um\x64"
"/LIBPATH:C:\Program Files (x86)\Windows Kits\10\lib\10.0.22000.0\ucrt\x64"
"/LIBPATH:C:\Program Files (x86)\Windows Kits\10\\lib\10.0.22000.0\\um\x64"
FindFile.obj
> FindFile.exe
D:\code\VC++\mylib_vc\src\.DS_Store
D:\code\VC++\mylib_vc\src\CharExtract.cpp
D:\code\VC++\mylib_vc\src\ColorConvert.cpp
D:\code\VC++\mylib_vc\src\csvProcessChar.cpp
D:\code\VC++\mylib_vc\src\csvProcessString.cpp
D:\code\VC++\mylib_vc\src\FindFile.cpp
D:\code\VC++\mylib_vc\src\Funcs.cpp
D:\code\VC++\mylib_vc\src\Split.cpp

[C++] 201 FLTK : Visual C++への移植 その1

[Windows11, MinGW g++ 9.2.0, Visual C++17, FLTK 1.3.8, Visual Studio 2022]

カラーアプリMinGW版をVisual C++に移植しています。

FLTKはYouTube動画を参考に何とか導入できました。

なんだかんだとコンパイルエラーをつぶしていき、あと7箇所(実質2箇所)というところまでたどりつきました。

POSIX固有のヘッダファイルであるglob.hとunistd.hの代替方法を考えるというのがコンパイル時の課題です。まあそれらが解決してもビルドでまた泥沼になる予感しかしませんが。

明日以降手掛けることにします。

参考動画(Visual C++へのFLTK導入)

[C++] 200 Safariのバージョンを取得する info.plist / libxml2

[M1 Mac, Big Sur 11.6.8, clang 13.0.0, NO IDE]

AppleScriptやJavaScript for Automation(JXA)をいじってましたが、言語そのものがあまりにも整備されていないのでC/C++に戻ってきました。

Node.jsへのJXA導入についてはなかなか意欲的な試みに思えました。ただ例とはいえ、やってみせたのがApple純正アプリのバージョン確認というのは少々インパクトに欠けます。そういうことならアプリ内のinfo.plistからC++あたりで直接読み取ればいい、と思ってしまいました。

というわけで公開されていたものを参考にC++のコードを書きました。libxml2ライブラリを使ってSafariのinfo.plist(全797行)を読み込み、CFBundleShortVersionStringキーに対する値を取り出します。意外と手間が掛かりました。

JXAを通してJavaScriptを学ぶきっかけになるかと期待したのですが、また疎遠になりそうです。

しばらくはAppleのネイティブ言語をいじる気にならなさそうです。開発リソースが削られたのかメンテナンスが行き届いていない感じがします。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
--中略--
	<key>CFBundleExecutable</key>
	<string>Safari</string>
	<key>CFBundleGetInfoString</key>
	<string>14.1.2, Copyright © 2003-2021 Apple Inc.</string>
	<key>CFBundleHelpBookFolder</key>
	<string>Safari.help</string>
	<key>CFBundleHelpBookName</key>
	<string>com.apple.Safari.help</string>
	<key>CFBundleIconFile</key>
	<string>AppIcon</string>
	<key>CFBundleIconName</key>
	<string>AppIcon</string>
	<key>CFBundleIdentifier</key>
	<string>com.apple.Safari</string>
	<key>CFBundleInfoDictionaryVersion</key>
	<string>6.0</string>
	<key>CFBundleName</key>
	<string>Safari</string>
	<key>CFBundlePackageType</key>
	<string>APPL</string>
	<key>CFBundleShortVersionString</key>
	<string>14.1.2</string>                     ⇦ ここ
	<key>CFBundleSignature</key>
	<string>sfri</string>
--以下略--
#include <cppstd.h> // 自製ライブラリ
#include <libxml/xmlreader.h>

int flag;
string version_key = "CFBundleShortVersionString";

//  パース中のノード名
typedef enum {
    STATE_NONE,
    STATE_KEY,
    STATE_STRING
} parsingStatus;


//  ノード処理
void processNode(xmlTextReaderPtr reader) 
{
    static parsingStatus state = STATE_NONE;
    int nodeType;
    xmlChar *name, *value;

    //  ノード情報の取得
    nodeType = xmlTextReaderNodeType(reader); 
    name = xmlTextReaderName(reader);
    if (!name) 
        name = xmlStrdup(BAD_CAST "---");

    if (nodeType == XML_READER_TYPE_ELEMENT) { 
        if ( xmlStrcmp(name, BAD_CAST "key") == 0 ) {
            state = STATE_KEY;

        } else if ( xmlStrcmp(name, BAD_CAST "string") == 0 ) {
            state = STATE_STRING;
        }

    } else if (nodeType == XML_READER_TYPE_END_ELEMENT) {
        printf("-----------------------\n"); 
        state = STATE_NONE;

    } else if (nodeType == XML_READER_TYPE_TEXT) {
        //  テキストを取得する
        value = xmlTextReaderValue(reader);
        const char* value_char = (char*) value;
        string value_str = (string) value_char;
        
        if (!value)
            value = xmlStrdup(BAD_CAST "---");

        if ( state == STATE_KEY ) {
            if ( value_str == version_key){
                flag = 1;
            }
        } else if ( state == STATE_STRING ) {
            if (flag == 1){
                printf("Safari version: %s\n", value);
                flag = 2;
            }
        }

        xmlFree(value);
    }

    xmlFree(name);
}

int main(int argc, char *argv[])
{
    xmlTextReaderPtr reader;
    int ret;

    //  Readerの作成
    reader = xmlNewTextReaderFilename("/Applications/Safari.app/Contents/Info.plist");

    if ( !reader ) {
        printf("Failed to open XML file.\n");
        return 1;
    }
    
    //  次のノードに移動 
    ret = xmlTextReaderRead(reader);
    while (ret == 1) {
        //  ノード処理
        processNode(reader);

        if (flag == 2){
            break;
        }

        //  次のノードに移動
        ret = xmlTextReaderRead(reader);
    }
    
    // Readerのすべてのリソースを開放
    xmlFreeTextReader(reader);

    if (ret == -1) {
        printf("Parse error.\n");
        return 1;
    }

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

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

# includeパス(-I)
INCLUDE = -I./include -I/Volumes/DATA_m1/code/cpp/mylib/include \
-I/opt/homebrew/Cellar/libxml2/2.10.3/include/libxml2

# ライブラリパス(-l)
LIBRARY_l = -lxml2

# ライブラリパス(-L)
LIBRARY_L = 

# ソースファイル
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)
Safari version: 14.1.2

参考サイト

[C++] 199 FLTK : コンパイラをGCCへ変更

[M1 Mac, Big Sur 11.6.8, gcc 12.2.0, FLTK 1.3.8, NO IDE]

これまでコンパイラはMacOS標準のClangを使ってきましたが、C++ならGCCでも問題ないので変更してみました。GCCはHomebrewでインストールしました。

Xcodeを使うのであればおのずとLLVM/Clangになります。非IDE派でiOSアプリを作らない者としては特にこだわる必要もありません。

辞典や参考書、公式ドキュメントの類いは情報へのアクセス性や学習効果を考えて紙ベースでも持っておくべきと常々思っていますが、GCCの公式ドキュメントは印刷にも対応できるよう適切なフォントサイズでpdfファイルが用意されています。一方、Clangはフォントサイズがかなり小さく全く配慮していません。

というわけでしばらくGCCで開発を進めていくことにします。

## General Settings
# フラグ設定
CPPFLAGS = -std=c++17 
LDFLAGS = 

# includeパス(-I)
INCLUDE = -I./include -I/opt/homebrew/Cellar/libpng/1.6.37/include -I/usr/local/include \
-I/Volumes/DATA_m1/code/cpp/mylib_gcc/include -I/opt/homebrew/Cellar/gcc/12.2.0/include/c++/12

# ライブラリパス(-l)
LIBRARY_l = /Volumes/DATA_m1/code/cpp/mylib_gcc/lib/Funcs.a \
/Volumes/DATA_m1/code/cpp/mylib_gcc/lib/ColorConvert.a \
/Volumes/DATA_m1/code/cpp/mylib_gcc/lib/CharExtract.a \
/opt/homebrew/Cellar/fltk/1.3.8/lib/libfltk.a \
/Volumes/DATA_m1/code/cpp/mylib_gcc/lib/csvProcessChar.a \
/Volumes/DATA_m1/code/cpp/mylib_gcc/lib/Split.a \
/Volumes/DATA_m1/code/cpp/mylib_gcc/lib/csvProcessString.a

# ライブラリパス(-L)
LIBRARY_L = 

## Compiler and Target Specific Settings
# MacOS [make all]
ifeq ($(TARGET), all)
	COMPILER = g++-12
	CPPFLAGS += 
	LDFLAGS += -framework Cocoa -w -lstdc++ -lgcc
endif
# MacOS(開発モード) [make dev]
ifeq ($(TARGET), dev)
	COMPILER = g++-12
	CPPFLAGS += -g -DDEV
	LDFLAGS += -framework Cocoa -w -lstdc++ -lgcc
endif

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

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

# 実行ファイル
BINDIR = ./bin
APP = ColorSampleJP

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

# appファイル作成関連
POSTBUILD  = fltk-config --post

## Public Targets
.PHONY:	all dev
all dev:
	rm -rf $(OBJS) $(BINDIR)/$(APP) $(APP).app
	$(MAKE) TARGET=$@ $(APP)

## Private Targets
# MacOS(oファイルから実行ファイルとappファイル作成)
.PHONY: $(APP)
$(APP):$(OBJS)
	$(COMPILER) -o $(BINDIR)/$@ $(OBJS) $(LIBRARY_l) $(LIBRARY_L) $(LDFLAGS)
	cp $(BINDIR)/$(APP) $(APP)
	$(POSTBUILD) $(APP)
	mkdir $(APP).app/Contents/Resources
	cp ./images/$(APP).icns $(APP).app/Contents/Resources
	plutil -insert 'CFBundleIconFile' -string $(APP).icns $(APP).app/Contents/Info.plist
	rm -f $(APP)

[C++] 198 FLTK : Makefileの改良 将棋エンジン”技巧2″を参考に

[M1 Mac, Big Sur 11.6.8, clang 13.0.0, FLTK 1.3.8, NO IDE]

将棋エンジン”技巧2″のMakefileを参考に改良しました。とりあえずカラーアプリMac版をビルドできるようにしました。

GNU Makeはクセが強くてまだまだ修得したとは言えないレベルですが、大分マシになったと思います。

## General Settings(MacOSのみ)
# フラグ設定
CPPFLAGS = -std=c++17 
LDFLAGS = 

# includeパス(-I)
INCLUDE = -I./include -I/opt/homebrew/Cellar/libpng/1.6.37/include -I/usr/local/include \
-I/Volumes/DATA_m1/code/cpp/mylib/include

# ライブラリパス(-l)
LIBRARY_l = /Volumes/DATA_m1/code/cpp/mylib/lib/Funcs.a \
/Volumes/DATA_m1/code/cpp/mylib/lib/ColorConvert.a \
/Volumes/DATA_m1/code/cpp/mylib/lib/CharExtract.a \
/opt/homebrew/Cellar/fltk/1.3.8/lib/libfltk.a \
/Volumes/DATA_m1/code/cpp/mylib/lib/csvProcessChar.a \
/Volumes/DATA_m1/code/cpp/mylib/lib/Split.a \
/Volumes/DATA_m1/code/cpp/mylib/lib/csvProcessString.a \
/Volumes/DATA_m1/code/cpp/mylib/lib/csvProcessChar.a

# ライブラリパス(-L)
LIBRARY_L = 

## Compiler and Target Specific Settings
# MacOS [make mac]
ifeq ($(TARGET), mac)
	COMPILER = clang++
	CPPFLAGS += 
	LDFLAGS += -lc++ -framework Cocoa -w
endif
# MacOS(開発モード) [make macdev]
ifeq ($(TARGET), macdev)
	COMPILER = clang++
	CPPFLAGS += -g -DDEV
	LDFLAGS += -lc++ -framework Cocoa -w
endif
# Windows [make win]
ifeq ($(TARGET), win)
	COMPILER = g++
	CPPFLAGS += -DWIN32
	LDFLAGS += -static -lstdc++ -lgcc -lole32 -loleaut32 -luuid -lwsock32 -lgdi32 -lcomctl32 -mwindows
	LIBRARY_L += -L"C:\MinGW\mingw64\lib"
	ICON = .\images\icon.res
endif
# Windows(開発モード) [make windev]
ifeq ($(TARGET), windev)
	COMPILER = g++
	CPPFLAGS += -DWIN32 -Og -g
	LDFLAGS += -static -lstdc++ -lgcc -lole32 -loleaut32 -luuid -lwsock32 -lgdi32 -lcomctl32
	LIBRARY_L += -L"C:\MinGW\mingw64\lib"
	ICON = .\images\icon.res
endif

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

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

# 実行ファイル
BINDIR = ./bin
APP = ColorSampleJP
APPWIN = ColorSampleJP

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

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

## Public Targets
.PHONY:	mac macdev win windev

mac macdev:
	rm -rf $(OBJS) $(BINDIR)/$(APP) $(APP).app
	$(MAKE) TARGET=$@ $(APP)

win windev:
	del \Q $(OBJS) $(BINDIR)\$(APPWIN).exe
	$(MAKE) TARGET=$@ $(APPWIN)

## Private Targets
# MacOS(oファイルから実行ファイルとappファイル作成)
.PHONY: $(APP)
$(APP):$(OBJS)
	$(COMPILER) -o $(BINDIR)/$@ $(OBJS) $(LIBRARY_l) $(LIBRARY_L) $(LDFLAGS)
	cp $(BINDIR)/$(APP) $(APP)
	$(POSTBUILD) $(APP)
	mkdir $(APP).app/Contents/Resources
	cp ./images/$(APP).icns $(APP).app/Contents/Resources
	plutil -insert 'CFBundleIconFile' -string $(APP).icns $(APP).app/Contents/Info.plist
	rm -f $(APP)

# Windows(oファイルからexeファイル作成)
.PHONY: $(APPWIN)
$(APPWIN):$(OBJS)
	$(COMPILER) -o $(BINDIR)\$@ $(OBJS) $(LIBRARY_l) $(LIBRARY_L) $(LDFLAGS) $(ICON)

[C++] 197 FLTK : カラーアプリ移植版のMicrosoft Store登録完了

[Windows11, MinGW g++ 9.2.0, FLTK 1.3.8, NO IDE]

Microsoft Storeに登録していたカラーアプリJava版をC++版に更新しました。前回の更新(申請No.18)が3月上旬ですから7ヶ月半ぶりになります。

アプリサイズは33MBから11MBになり、ウィジェットツールキットにFLTKを採用して高速・軽量アプリに生まれ変わりました。

特に急がず、だらだら取り組んだのでMac C++版からの移植・登録に12日も掛かりました。clang++からMinGW g++へのコンパイラ変更に伴う修正が想像以上にしんどかったです。

申請から公開までは約9時間半でした。審査の経過はMicrosoftパートナーセンターで確認しました。公開されてもメール通知はありません。

今後はタイトルバーのアイコン設定など細かいところをぼちぼち直していきます。

[C++] 196 GDBによるデバッグ例 stoi関数

[Windows11, MinGW g++ 9.2.0, FLTK 1.3.8, NO IDE]

MacOSから移植中のカラーアプリについてWindows環境のみでアプリが落ちる現象が発覚しましたので、早速GDBデバッガで調査しました。

コンパイル時に-Ogオプションを追加しないとオブジェクトファイルのデバッグへの最適化ができないので要注意です。実際に-Ogオプションなしでコンパイルすると関数指定や行数指定のブレークポイント設定が正確にできませんでした。

DEBUG = -Og -g

“b btnAction.cpp:showColor”コマンドでbtnAction.cppのshowColor関数にブレークポイントを設定し、rコマンドでアプリを実行しました。nコマンドでは関数はスキップして次の行に進み、sコマンドでは関数にも入り込んで次の行に進みます。

調査の結果、自製ライブラリColorConvertのZeroToRGB関数にあるstoi関数辺りに原因があることが判明しました。

対策:
invalid argumentエラーを例外処理するようにした。

clang++ではデフォルトで例外処理を包含しているようです。stoi関数の引数がNULLでもエラーになりません。clang++が甘々なのか、MinGWが厳しいのか。

int red;
int green;
int blue;

try{
    red = stoi(red0);
    green = stoi(green0);
    blue = stoi(blue0);
}catch(const std::invalid_argument& e){
    cout << "invalid argument" << endl;
}
>gdb ColorSample.exe
Reading symbols from ColorSample.exe...done.
(gdb) b btnAction.cpp:showColor
Breakpoint 1 at 0x406510: btnAction.cpp:showColor. (2 locations)
(gdb) r
Starting program: ColorSample.exe
[New Thread 6864.0x4334]
[New Thread 6864.0x968]
[New Thread 6864.0x2350]
[New Thread 6864.0x44b4]
locale Japanese_Japan.932
Japan detect
[New Thread 6864.0x43c0]
[New Thread 6864.0x3258]
[New Thread 6864.0x3bd8]

Breakpoint 1, showColor () at C:/FLTK/fltk-1.3.8/include/FL/Fl_Button.H:102
102       char value() const {return value_;}
(gdb) s
516         input_code = code_input->value();
(gdb) s
value (this=0xf485e0) at C:/FLTK/fltk-1.3.8/include/FL/Fl_Input_.H:244
244       const char* value() const {return value_;}
(gdb) n
showColor () at .\src\btnAction.cpp:516
516         input_code = code_input->value();
(gdb) n
517         if (input_code == ""){
(gdb) n
533         if (onoff_zero == 1){
(gdb) s
534             ToZeroConvert();
(gdb) s
ToZeroConvert () at .\src\btnAction.cpp:196
196         const char* input_code0 = code_input->value();
(gdb) p input_code0
$1 = <optimized out>
(gdb) s
value (this=0xf485e0) at C:/FLTK/fltk-1.3.8/include/FL/Fl_Input_.H:244
244       const char* value() const {return value_;}
(gdb) s
ToZeroConvert () at .\src\btnAction.cpp:197
197         input_code = string(input_code0);
(gdb) p input_code
No symbol "input_code" in current context.
(gdb) s
basic_string<> (__a=..., __s=0xf5f498 "0x483D", this=0xdaf334) at .\src\btnAction.cpp:197
197         input_code = string(input_code0);
(gdb) p input_code
No symbol "input_code" in current context.
(gdb) s
_Alloc_hider (__a=..., __dat=0xdaf33c "\375\023爽リ\023爽蝗^ツ\032        \r", this=0xdaf334)
    at c:/mingw/lib/gcc/mingw32/9.2.0/include/c++/ext/new_allocator.h:83
83            new_allocator(const new_allocator&) _GLIBCXX_USE_NOEXCEPT { }
(gdb) s
basic_string<> (__a=..., __s=0xf5f498 "0x483D", this=0xdaf334)
    at c:/mingw/lib/gcc/mingw32/9.2.0/include/c++/bits/basic_string.h:527
527           { _M_construct(__s, __s ? __s + traits_type::length(__s) : __s+npos); }
(gdb) n
ToZeroConvert () at .\src\btnAction.cpp:197
197         input_code = string(input_code0);
(gdb) n
199         if (input_code.find("#") != string::npos){
(gdb) n
230         } else if (input_code.find("0x") != string::npos){
(gdb) n
[New Thread 6864.0x3a8c]
232             code_zero = input_code;
(gdb) s
operator= (__str=..., this=0x52f094 <code_zero[abi:cxx11]>) at .\src\btnAction.cpp:232
232             code_zero = input_code;
(gdb) s
assign (__str=..., this=0x52f094 <code_zero[abi:cxx11]>)
    at c:/mingw/lib/gcc/mingw32/9.2.0/include/c++/bits/basic_string.h:1364
1364          assign(const basic_string& __str)
(gdb) s
_fu21___ZSt4cout () at .\src\btnAction.cpp:235
235             baseColor = CC.ZeroToRGB(input_code);
(gdb) s
basic_string (__str=..., this=0xdaf3c4) at .\src\btnAction.cpp:235
235             baseColor = CC.ZeroToRGB(input_code);
(gdb) s
_Alloc_hider (__a=<optimized out>, __dat=0xdaf3cc "\a", this=0xdaf3c4)
    at c:/mingw/lib/gcc/mingw32/9.2.0/include/c++/ext/new_allocator.h:83
83            new_allocator(const new_allocator&) _GLIBCXX_USE_NOEXCEPT { }
(gdb) s
basic_string (__str=..., this=0xdaf3c4) at c:/mingw/lib/gcc/mingw32/9.2.0/include/c++/bits/basic_string.h:451
451           { _M_construct(__str._M_data(), __str._M_data() + __str.length()); }
(gdb) s
_M_data (this=<optimized out>) at c:/mingw/lib/gcc/mingw32/9.2.0/include/c++/bits/basic_string.h:186
186           _M_data() const
(gdb) s
basic_string (__str=..., this=0xdaf3c4) at c:/mingw/lib/gcc/mingw32/9.2.0/include/c++/bits/basic_string.h:936
936           length() const _GLIBCXX_NOEXCEPT
(gdb) s
451           { _M_construct(__str._M_data(), __str._M_data() + __str.length()); }
(gdb) s
_M_construct<char*> (__end=0x53b0fa <input_code[abi:cxx11]+14> "", __beg=0x53b0f4 <input_code[abi:cxx11]+8> "0x483D",
    this=0xdaf3c4) at c:/mingw/lib/gcc/mingw32/9.2.0/include/c++/bits/basic_string.h:451
451           { _M_construct(__str._M_data(), __str._M_data() + __str.length()); }
(gdb) s
_M_construct_aux<char*> (__end=0x53b0fa <input_code[abi:cxx11]+14> "",
    __beg=0x53b0f4 <input_code[abi:cxx11]+8> "0x483D", this=0xdaf3c4)
    at c:/mingw/lib/gcc/mingw32/9.2.0/include/c++/bits/basic_string.h:263
263             _M_construct(_InIterator __beg, _InIterator __end)
(gdb) n // ここでアプリが落ちた
[New Thread 6864.0x2a54]
[New Thread 6864.0x24c4]
terminate called after throwing an instance of 'std::invalid_argument'
  what():  stoi
[Inferior 1 (process 6864) exited with code 03]
(gdb) quit
GCC公式ドキュメント -Ogオプションに関する記述