[C++] 115 CSVファイルから2次元vectorへ変換

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

[C++]114の逆変換です。こちらは少しだけ手間取りました。

ところでstd::stringを使う時は<string>をインクルードしがちですが、<iostream>だけでOKです。

#include <iostream>
#include <fstream>
#include <vector>
#include <utility>
#include <sstream>

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

int main () {
    string eles;
    string ele;
    vector<vector<string>> vec2D;
  
    // csvファイル指定
    std::ifstream file("test.csv");

    while (getline(file, eles)) {
        vector<string> vec;  
        std::istringstream line(eles);

        while (getline(line, ele, ',')) {
            vec.push_back(ele);
        }
        
        vec2D.push_back(vec);
    }

    // vec2Dの内容確認
    int line_num = 1;
    for (const auto &items : vec2D) {
        for (const auto &item : items) {
            cout << line_num << " " << item << endl;
        }
        line_num += 1;
    }
}
--------------------------------------------------
出力例
--------------------------------------------------
1 cadetblue
1 -
1 0x5F9EA0
2 若竹色
2 わかたけいろ
2 0x68BE8D

[C++] 114 2次元vectorからCSVファイル作成

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

2次元vectorからCSVファイルを作成しました。Pythonより簡単で拍子抜けです。

#include <iostream>
#include <fstream>
#include <vector>
#include <utility>

using std::string; using std::vector;

int main()
{
    // ファイル名指定
    std::ofstream file("test.csv");
    vector<vector<string>> vec;

    // vector作成
    vector<string> data1 = {"cadetblue","-","0x5F9EA0"};
    vector<string> data2 = {"若竹色","わかたけいろ","0x68BE8D"};
    vec.push_back(data1);
    vec.push_back(data2);

    // データ取り込み
    for (auto &&data : vec) {
        file << data[0] << ',' << data[1] << ',' << data[2] << '\n';
    }

    // ファイル出力完了
    file.close();
}

[C++] 113 FLTK : メニュー追加 Fl_Menu_Item

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

ここ最近、課題が増えてきて整理しているところです。

カラーアプリについてはWindows11への移植に着手したものの、Explorerのファイラーとしての物足りなさを改めて実感し、秀丸ファイラーClassicを使い始めました。

Macの方もCommander Oneというシェアウェアを導入しました。どちらも複数タブ表示機能があります。秀丸ファイラーClassicにこの機能が搭載されたのが2010年ですから、そんなに新しいものでもないようです。

ここで新たな課題として自分仕様のファイラーを作ってみようと思い、Mac版の言語をObjective-C++、Swift、あるいはC++にするか考えを巡らせています。

一方、カラーアプリで追加したい機能もあり、まずこちらに優先して取り組みます。

繰り返し使う色のカラーコードをファイルで保管するために履歴関連機能を強化します。履歴の保存、読込機能になります。エクスポート、インポート機能のようなものです。ファイルタイプはCSV、JSON、XMLあたりでしょうか。

とりあえずFl_Menu_Itemを書き換えました。開発モードは別メニューに分離させています。

static Fl_Menu_Item	items[] = {
        { "履歴", 0, 0, 0, FL_SUBMENU },
        { "読込", 0, loadHistory, 0, 0 },
        { "保存", 0, saveHistory, 0, 0 },
        { "消去", 0, deleteHistory, 0, 0 },
        { 0 },
#ifdef DEV
        { "開発", 0, 0, 0, FL_SUBMENU },
        { "開発モード", 0, showSubWindow, 0, 0 },
        { 0 }
#else
#endif
};
    
Fl_Sys_Menu_Bar *menubar;
menubar = new Fl_Sys_Menu_Bar(0, 0, 60, 20);
menubar->box(FL_FLAT_BOX);
menubar->menu(items);

[C++] 112 FLTK : 条件付きコンパイル #ifdef

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

通常モードと開発モードによってGUIの内容を変えるため条件付きコンパイルを設定しました。

#ifdefは様々な公開ソースコードで目にする機会が多く、前から使ってみたいと思っていました。共同開発ではメンテナンス性に影響するとのことであまり好まれない現場もあるようです。

これまでFl_Inputなどに隠しコマンドを設定してウィンドウを出現させたりしていましたが、コードの可読性が損なわれるので控えるようにします。

#ifdef DEV
    static Fl_Menu_Item	items[] = {
    { "設定", 0, 0, 0, FL_SUBMENU },
    { "履歴消去", 0, deleteHistory, 0, 0 },
    { "開発モード", 0, showSubWindow, 0, 0 },
    { 0 },
    { 0 }
    };
#else
    static Fl_Menu_Item	items[] = {
    { "設定", 0, 0, 0, FL_SUBMENU },
    { "履歴消去", 0, deleteHistory, 0, 0 },
    { 0 },
    { 0 }
    };
#endif
--------------------------------------------------
#ifdef DEV
    window2 = new Fl_Window(750,50,360,480,"出力用");
    window2->color(fl_rgb_color(238,238,238));

    // output
    output_line2 = new Fl_Multiline_Output(0,0,360,480,"");
    output_line2->textsize(12);
    
    window2->end();
#else
#endif
# オプション設定 -D+マクロ名
CPPFLAGS = -std=c++17 -DDEV

[C++] 111 FLTK:子ウィジェットの座標 その2 モーダルダイアログ

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

“[C++] 31 FLTK:子ウィジェットの座標”の続編です。

親ウィンドウを動かした後に生成したモーダルダイアログが親ウィンドウの相対位置になっていない問題を解決することができました。

親ウィンドウのx_root, y_rootを取得すれば済む話でした。これはすぐに解らないといけない、と反省です。

Fl_Window *window;
modalDialog *dlg;

const char* msg = "該当する色はありません";
dlg = new modalDialog(400, 200, "Attention", msg);
dlg->hotspot(window);

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

dlg->resize(x_win+205, y_win+165,250,150);
dlg->set_modal();
dlg->show();

[C++] 110 FLTKアプリのApple公証 その2

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

“[C++] 08 FLTKアプリのApple公証”の続編です。前編を書いたのは今年3月ですから4ヶ月でC++関連記事を100書いたことになります。短編ばかりとは言え、書きも書いたりといったところです。

製作したアプリを配布するにはApple公証を取得する必要があります。新しいアプリの手続きをするのは昨年12月以来で大分忘れていました。再度この記事にまとめておきます。

Apple公証手続き方法

1.アプリ用パスワードを取得する。公式サイトの”How to generate an app-specific password”に従って作成する。
取得時のサイトの案内がとても分かりにくくてとまどいます。登録したいパスワードの入力を促している様にしか見えないので要注意です。
最初に入力するのは任意の名称です。パスワード自体はAppleが発行します。UIにはこだわるのにローカライズはいい加減なところが散見されます。

公式サイト

2.以下コマンドでパスワードを登録しておくと今後直に入力しなくて済みます。AC_PASSWORDは任意の登録パスワード名です。

xcrun altool --store-password-in-keychain-item "AC_PASSWORD" -u "開発者メールアドレス" -p アプリ用パスワード

3.アプリに署名し、アプリ用パスワードを使ってzipファイルを提出する。
下図の様なエラーになった場合は以下サイトにアクセスして内容に同意する。
https://appstoreconnect.apple.com/

4.”Your Mac software was successfully notarized.”というタイトルのメールが届いたら公証手続きは完了。アプリへの紐付けを行う。ステープラーで留めるとも言います。

[C++]109 FLTK : 静的ライブラリのリンク

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

配布したアプリの導入時にユーザーの方がHomebrewからFLTKをインストールするなどの作業をしなくても済む様、静的ライブラリとリンクさせました。

自製ライブラリも動的ライブラリから静的ライブラリに変更しています。静的ライブラリはビルド時にアプリ内に取り込まれます。

手順

1.コンパイルコマンドをチェックし、最低限必要なライブラリだけに絞り込む。

2.各ライブラリの静的ライブラリ(拡張子 a)を作成する。

# オブジェクトファイルを作成
clang++ -std=c++17 -I/code/cpp/mylib/include -g -o /code/cpp/mylib/lib/Split.o \
-c /code/cpp/mylib/src/Split.cpp

# 静的ライブラリを作成
ar r /code/cpp/mylib/lib/Split.a /code/cpp/mylib/lib/Split.o

3.Makefileでアプリをビルドする。

動的ライブラリの時は612KBだったアプリサイズが1.8MBになりました。単純な合計サイズより小さいのは、必要なクラスや関数だけ取り出しているためでしょう。

なおJava版は45.6MBになりますから、C++版の方がストレージへの負担はかなり小さくそして爆速です。起動が速すぎてちょっとビックリするレベルなので、可能なら少し遅めにしようかとも思っています。

[C++]108 FLTK : 表示色カウンターと履歴消去機能追加 

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

表示色が履歴の何番目かを示したカウンターと履歴消去機能を追加しました。カウンターのFl_Inputを右揃えにできず、やむなく左揃えで妥協しました。

これで搭載したい機能は全て実装完了です。後は配布に耐えうる堅牢性への強化でしょうか。

// counter
counter = new Fl_Input(625, 89, 27, 10,"");
counter->box(FL_FLAT_BOX);
counter->align(FL_ALIGN_INSIDE|FL_ALIGN_RIGHT); // 設定は効かず
counter->color(fl_rgb_color(238,238,238));
counter->textsize(10);

// 色表示関数内
string count = to_string(color_index) + "/" + to_string(selectColorList.size());
counter->value("");
counter->value(count.c_str());

[C++]107 FLTK : 表示色の履歴管理および順逆送り new演算子

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

手掛けてみたら予想通りのややこしさでしたが、運良くすんなり書けました。

C/C++ユーザーの方々には釈迦に説法ですが、変数やポインタのvectorを無処理で作成し履歴データとして蓄積しても勝手に上書きされるので失敗します。

new演算子でメモリを確保し、そこにデータを入れてからvectorを作成します。

確保したメモリをdelete演算子でどのようにどのタイミングで解放するのか、についてはこれから考えます。

vector<vector<const char*>> selectColorList;
const char *name_c, *roma_c, *code_c;
int color_index;

void selectColorListMake(const char* name, const char* roma, const char* code){
    color_index = selectColorList.size();

    vector<const char*> selectColor = {name, roma, code};
    selectColorList.push_back(selectColor);
    color_index += 1;
}

void prevColor(Fl_Widget*, void*){
    if (!(color_index < 2)){
        vector<const char*> prev_color = selectColorList[color_index -1 -1];
        name_c = prev_color[0];
        roma_c = prev_color[1];
        code_c = prev_color[2];
        cout << "name:" << name_c;
        cout << " roma:" << roma_c;
        cout << " code:" << code_c;
        color_index -= 1;
        cout << " index:" << (to_string(color_index)).c_str() << endl;;

        name_input->value("");
        name_input->insert(name_c);
        roma_input->value("");
        roma_input->insert(roma_c);
        code_input->value("");
        code_input->insert(code_c);

        output_line->insert(name_c);
        output_line->insert(" ");

        showColor();
    } else {
        cout << "記録はありません" << endl;
    }
}

void nextColor(Fl_Widget*, void*){
    if (!(color_index >= selectColorList.size())){
        vector<const char*> next_color = selectColorList[color_index -1 + 1];
        name_c = next_color[0];
        roma_c = next_color[1];
        code_c = next_color[2];
        cout << "name:" << name_c;
        cout << " roma:" << roma_c;
        cout << " code:" << code_c;
        color_index += 1;
        cout << " index:" << (to_string(color_index)).c_str() << endl;

        name_input->value("");
        name_input->insert(name_c);
        roma_input->value("");
        roma_input->insert(roma_c);
        code_input->value("");
        code_input->insert(code_c);

        output_line->insert(name_c);
        output_line->insert(" ");

        showColor();
    } else {
        cout << "記録はありません" << endl;
    }
}

色表示する関数内
// selectColor追加
const char* name_c2 = name_input->value();
char* name_c3 = new char[strlen(name_c2) + 1];
strcpy(name_c3, name_c2);

const char* roma_c2 = roma_input->value();
char* roma_c3 = new char[strlen(roma_c2) + 1];
strcpy(roma_c3, roma_c2);

const char* code_c2 = code_input->value();
char* code_c3 = new char[strlen(code_c2) + 1];
strcpy(code_c3, code_c2);

selectColorListMake(name_c3, roma_c3, code_c3);