wxWidgets(Windows)におけるコマンドラインオプションの処理について

## 前提条件
Windows10(64bit)上の、VisualStudio2019とwxWidgets3.2.5、
wxWidgetsのビルド方法は
```
A>cmake -D wxBUILD_SHARED=OFF -D wxBUILD_USE_STATIC_RUNTIME=ON ..
```
で行っている(*なお、wxBUILD_USE_STATIC_RUNTIMEは2024年春現在での公式ドキュメントにはなく、cmakeのキャッシュを見て発見しただけのオプションなので将来のバージョンではリネームor取り扱いが変わるかもしれない*)。

ターゲットのコンパイルオプションは
```
CPPFLAGS = /MT /GR /EHsc /D _UNICODE
```
である。

(ようするに単体実行ファイル原理主義の方針である)

また、コマンドライン処理はwxApp::OnInit()内で行うものとする。

## 実践

### コマンドラインの前処理

ターゲットが以下の形式のコマンドラインを持つ場合、

A>popt [-v] [-f *filename*] [arg ...]

ルールを以下のようにして定義する。
```
  static wxCmdLineEntryDesc desc[] = {
    /* kind, short-name, long-name, usage, type, flags */
    { wxCMD_LINE_SWITCH, "v", "verbose", "エラー表示を饒舌に" }, // 引数なし
    { wxCMD_LINE_OPTION, "f", "file", "設定ファイルのパス" }, // 引数あり
    { wxCMD_LINE_PARAM, NULL, NULL, "引数", wxCMD_LINE_VAL_STRING,
      wxCMD_LINE_PARAM_OPTIONAL |  wxCMD_LINE_PARAM_MULTIPLE },
    { wxCMD_LINE_USAGE_TEXT, NULL, NULL, "コマンドラインのエラー" },
    { wxCMD_LINE_NONE } // 終了マーク
  };
 ```
POSIXのgetoptなどと違い、非オプションの引数(≒optarg)の有無や個数まで定義しないといけないのがポイントである。
wxWidgetsではこれらを「PARAM」の付いた名前で扱い、コード中では「wxCMD_LINE_PARAM」という項目が該当する。この時、フラグとしてwxCMD_LINE_PARAM_OPTIONALを指定しないと引数は省略できない。また、wxCMD_LINE_MULTIPLEを指定しないと複数個を与えられない。

wxCMD_LINE_USAGE_TEXTは後述するが、エラー発生時に自動生成される、usageを表示するウインドウ末尾付近に追加されるコメントを指定できる。
これの定義の仕方は2024年春現在、公式のリファレンスにはないが、ソースコードのtar-ball内の「src/common/cmdline.cpp」のコメント行には書いてある。
```
// wxCMD_LINE_USAGE_TEXT uses only description, shortName and longName is empty
```
 
さて、肝心のコマンドライン引数はどこから引っ張って来るべきなのかwxWidgets3.2.5のtar-ballを確認すると、
- 「include/wx.app.h」内において、wxAppのメンバーとして、
「int argc」と「wxCmdLineArgsArray argv」が定義されている。
- wxCmdLineArgsArrayにはoperator演算子として「(wchar_t **)」が定義されている。

※*具体的には「src/msw/main.cpp」においてWin32APIの「GetCommandLineW」で取得したコマンドラインをシラブル分けして持って来ている(なのでランタイムによるワイルドカードの展開処理は行われない)。*

以上のことから、コマンドラインパースの実行は以下のようにして行う。

 ```
  wxCmdLineParser parser(desc, argc, (wchar_t **)argv);
  //parser.SetSwitchChars("-"); // デフォルトでは「/」と「-」の両方を受け付ける
  if (parser.Parse()) { // エラーが起きれば自動的にウインドウを開いてusageを表示する。
    return false; // エラーが起きたので終了する
  }
 ```
エラーが起きた場合、自動的に表示されるウインドウは以下のようになる
(未定義のオプション「-q」を指定して意図的にエラーを起こしている)。

![popt.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3796207/def64efa-92c2-ed59-5ab3-b1d24c8dc6e7.jpeg)

 ※*デフォルトではオプションスイッチとして「-」と「/」の両方を受付けるので、「-v」と「/v」は全く同じ扱いになるが、ロング形式に関しては「--file path」を受付けるが「//file path」はエラーにされる。そしてparser.SetSwitchChars("-")を実行すると「/」は受け付けなくなる。*

### コマンドラインの参照


- 引数付きのオプションの扱い方
``` 
  wxString fname;
  if (parser.Found("f", &fname)) {
    wxMessageBox(wxString::Format(L"-f=\"%s\"", fname), L"DEBUG", wxOK);
  }
```
- 引数なしのオプションの扱い方
```
  if (parser.Found("v")) {
    wxMessageBox(L"verbose", L"DEBUG", wxOK);
  }
 ```
- 引数(≒optarg)の扱い方 */
 ```
  for (size_t i = 0; i < parser.GetParamCount(); i++) {
    wxMessageBox(parser.GetParam(i), L"DEBUG", wxOK);
  }
 ```

 ## ソースコード全体

### ビルドする方法
```
A>echo CPPFLAGS = /MT /GR /EHsc /D _UNICODE > Makefile
A>nmake popt.exe
```
### ソースコード(popt.cpp)
```
#include <wx/wx.h>
#include <wx/cmdline.h>

class app: public wxApp {
public:
  virtual bool OnInit();
};

wxIMPLEMENT_APP(app);

bool app::OnInit() {
  static wxCmdLineEntryDesc desc[] = {
    { wxCMD_LINE_SWITCH, "v", "verbose", "エラー表示を饒舌に" },
    { wxCMD_LINE_OPTION, "f", "file", "設定ファイルのパス" },
    { wxCMD_LINE_PARAM, NULL, NULL, "引数", wxCMD_LINE_VAL_STRING,
      wxCMD_LINE_PARAM_OPTIONAL |  wxCMD_LINE_PARAM_MULTIPLE },
    { wxCMD_LINE_USAGE_TEXT, NULL, NULL, "コマンドラインのエラー" },
    { wxCMD_LINE_NONE }
  };
  wxCmdLineParser parser(desc, argc, (wchar_t **)argv);
  if (parser.Parse()) {
    return false;
  }
  wxString fname;
  if (parser.Found("f", &fname)) {
    wxMessageBox(wxString::Format(L"-f=\"%s\"", fname), L"DEBUG", wxOK);
  }
  if (parser.Found("v")) {
    wxMessageBox(L"verbose", L"DEBUG", wxOK);
  }
  for (size_t i = 0; i < parser.GetParamCount(); i++) {
    wxMessageBox(parser.GetParam(i), L"DEBUG", wxOK);
  }
  auto frame = new wxFrame(NULL, wxID_ANY, L"タイトル");
  frame->Show();
  return true;
}

```