kondoumh のブログ

- とあるソフトウェアエンジニアのめったに更新されないブログ -

C++ REST SDK で null safe な JSON デシリアライズ処理を書く

iEdit では XML 形式のデータエクスポート・インポートをサポートしていますが、今時は XML より JSON だよねってことでこの前対応しました。

blog.kondoumh.com

この対応のために C++ の JSON 処理系ライブラリを探して、Microsoft 製の OSS C++ REST SDK (cpprestsdk) を知りました。

blog.kondoumh.com

iEdit からエクスポートした JSON ファイルはフルセットのデータがあるので何も考えずにインポートして問題ありませんが、ユーザはエディタで作ったり別のプログラムからエクスポートしたりしたデータをインポートしたいもしれません。その場合、全ての値が適切に入ってくると想定するとまずいことになります。例えば、ノードの ID は数値ですが文字列が指定されてしまうかもしれません。ノードの位置を表す座標は何も設定されていないかもしれません。入力データをチェックして不正なデータを弾くようにすれば、ランタイムのエラーは防げますが、インポートデータを作成するのが大変になってしまいます。

ということで、入力されない属性については極力デフォルト値を割り当て、なるべくインポートを成功させる方向で実装しました。JSON の Parse が終わった後、所定のキーで値を取得してノードやリンクの情報を構築していきますが、値が取得できるかを事前に判定し、取得できない場合はデフォルト値を割り当てます。

iEdit で使用するデータ型は、文字列、数値、配列、真偽値 の4つです。cpprestsdk では、JSON value に対して、is_null() / is_string() / is_number() などのチェック用のヘルパーメソッドが用意されています。これを使って HasValue メソッドを書きました。ちょっとダサいですが キーは2つまでとし 値が目的の型で取れるかをチェックします。

bool JsonProcessor::HasValue(json::value v, json::value::value_type type, const wchar_t* key1, const wchar_t* key2)
{
    json::value target = v[key1];
    if (target.is_null()) return false;

    if (key2 != L"") {
        target = v[key1][key2];
        if (target.is_null()) {
            return false;
        }
    }
    switch (type) {
    case json::value::String: return target.is_string();
    case json::value::Number: return target.is_number();
    case json::value::Array: return target.is_array();
    case json::value::Boolean: return target.is_boolean();
    }
    return false;
}

これにより、値の構築コードは以下のように三項演算子でデフォルト値を指定しながら書けます。

auto fillColor = HasValue(v, json::value::String, L"fill_color") ?
                            v[L"fill_color"].as_string() : L"#FFFFFF";
node.SetFillColor(ConvertToRGB(fillColor));

座標など、複数項目にわたる属性もあるので、それらについては個別に値設定ロジックを書きました。

iEdit 2.40 リリース - JSON サポート

f:id:kondoumh:20160410114137p:plain

従来の XML に加え、JSON ファイルのインポート、エクスポート機能を追加しました。

全般的にコードリファクタリングしてちょっと機能を整理・削除したりもしました。

修正内容はリリースノートをご覧ください。

VS Code の ChangeLog メモ用 Extension を Marketplace に公開しました

数ヶ月前に作った VS Code Extention の changelog-support.

blog.kondoumh.com

ずっと ~/.vscode/extensions に手動配置して使ってましたが、せっかくなので Marketplace からインストールできるようにしようと思い立ちました。

VSTS 改め Azure DevOps にアカウントを作成して Publisher 登録とアクセストークン取得が必要です。

dev.azure.com

話がそれますが、この Azure DevOps では、コードリポジトリが作れて、ビルド・テスト・デプロイのパイプラインが構成できます。GitHub のリポジトリとの連携もできますし、 OSS なら無償で使えるそうです。VSTS って使ったことなかったんですが、UI も洗練されていてよい感じですね。

VSTS から UI が変わりましたが、この記事で書かれている通りにいけました。

blog.toshimaru.net

アカウントを作ったら、ひとまず organization を作成します。そして、organization の Settings 画面で公開用のアクセストークンを取得します。

f:id:kondoumh:20180915082528p:plain

Visual Studio Marketplace の publisher 登録画面から Publisher 登録をします。申請の翌日に verify 通知がきました。

以上で、いつでも Marketplace に Plugin を公開できるようになりました。

公開準備として、Extension プロジェクトの package.json に publisher などの情報を追加しました。

  "publisher": "kondoumh",
  "repository": {
    "url": "https://github.com/kondoumh/changelog-support"
  },
  "author": {
    "name": "kondoumh",
    "url": "https://kondoumh.com/"
  },
  "categories": [
    "Other"
  ]

Extension を公開するための CLI ツール vcse をインストールします。

$ npm install -g vsce

Publisher 名でログインします。

$ vcse login kondoumh

この時、事前に取得したアクセストークンを入力します。

あとは Extension プロジェクトディレクトリで公開用コマンドを叩きます。

$ vsce publish

エラーがなければ、パッケージングと公開が数分で完了します。

marketplace.visualstudio.com

最初 アイコンを SVG で作っていたのですが、SVG はダメというエラーが出たので png に差し替えました。アイコンは、draw.io で10分ぐらいで作りました。

VS Code の拡張ビューから検索してインストールできるのはやはり便利です。

f:id:kondoumh:20180915083456p:plain

Marketplace への公開、めんどくさそうと思っていたのですが、意外と簡単にできました。

Express で REST API を TypeScript で書くための boilerplate ジェネレータ

最近 Node.js の Web App Framework である Express で TypeScript を使って REST API を書く機会がありました。

これまでずっとサーバーサイドは Java や .NET で書いてきたので Node.js で書くこと自体が新鮮でしたし TypeScript も久々でした。

作業はもちろん VS Code で行いました。TypeScript への最適化が進んでおり、Eclipse で Java を書いてる (Visual Studio で C# を書いてる)レベルとまでは行かないものの、IDE + 静的型付け言語の開発体験に近づきつつある感じです。コードの見た目も Java や C# にそっくりになります。NPM でなじみのパッケージをインストールして使ったりできるし、型定義ファイルがなくてもコード書けるし、いい感じに緩くてなかなか居心地の良い環境です。

Java ではアプリケーションサーバの起動やモジュールのローディングに時間がかかるため、コード修正 -> 動作確認のサイクルが長くなってしまいますが、Node.js の起動は早いので作業が早く済みます。

Java のサポートが有償化され、Java 離れが進むことが予想される中、Express (および類する Node.js のフレームワーク) も移行先として有力な選択肢になるかもしれません。

ところで、フレームワークを使って開発を始める時は、定型的なコードをたくさん書くのが面倒なので、プロジェクトテンプレートやボイラープレートの類が欲しくなります。もちろん Express でもたくさん提供されていて、すぐに見つかるのが Express Generater というやつです。

www.npmjs.com

これは、TypeScript ではなく JavaScript のコードを生成しますので TypeScript に置き換える作業が面倒です。

ということで見つけたのがこちら。

www.npmjs.com

Yeoman を使用してコード生成するジェネレータです。

$ yo express-no-stress myapp

でアプリが生成されます。

f:id:kondoumh:20180905215250p:plain

Swagger も統合されていていい感じに API 開発できそうです。

Express では ejs というテンプレートエンジンがあり、サーバーサイドレンダリング処理も普通に書けますが、このジェネレータは API にフォーカスしているようです。今回は、API 開発の用途で探してたのでこれ以上追及はしていません。

GitBook + GitHub Pages でレガシードキュメントを移行

iEdit のユーザーズマニュアルはとてもレガシーです。

iEdit ユーザーズマニュアル

以前 Google Sites に引っ越そうと思って、サイト作ってカスタムドメイン設定してたのですが、リリースノートを更新するのみで放置して何年も経ってしまいました。その間 Google Sites もリニューアルされ HTTPS 対応もしましたが、WYSIWYG に寄り過ぎていて構造化されたドキュメントを書くプラットフォームとしてはちょっと・・という気がします。

最近、GitBook でドキュメントを Markdown で記述して静的コンテンツを公開できることを知り移行作業をしてみました。

GitBook の CLI をインストール

$ npm install gitbook-cli -g

プロジェクトを作成

$ mkdir iedit-doc
$ cd iedit-doc
$ gitbook init

空ディレクトリの場合、SUMMARY.md / README.md が生成されます。SUMMARY.md にドキュメントの構造を記述します。

# Summary

* [イントロダクション](README.md)
* [インストール](overview/install.md)
* [ノードの作成と編集](editing/edit_node.md)
 * [ノード作成](editing/edit_node.md#ノード作成)
 * [ノード編集](editing/edit_node.md#ノード編集)
* [リンクの作成と編集](editing/edit_link.md)
 * [リンク作成](editing/edit_link.md)
 * [リンク編集](editing/edit_link.md)
* [インポート・エクスポート](features/import_export.md)
* [検索](features/search.md)
* [メニューコマンド](features/menu.md)
* [各種設定](settings/README.md)
 * [オプション設定](settings/option_settings.md)
 * [ファイルタイプの登録・削除](settings/filetype_registry.md)
* [リリースノート](releasenotes/README.md)

README.md が index.html に変換されます。

SUMMARY.md が決まったら gitbook init を再実行すると、フォルダ構成と必要なファイルを作ってくれます。あとは、書きながら調整していくのがよいでしょう。

book.json を追加して、ドキュメントのタイトルや GitBook のバージョン設定、サイドバーのリンクなどを記述します。plugins に GitBook のプラグイン (実体は NPM モジュール) を指定できます。

{
    "title": "iEdit User's Guide",
    "description": "",
    "plugins": ["expandable-chapters", "ga"],
    "language": "ja",
    "gitbook": ">= 3.0.0",
    "links": {
        "sidebar": {
            "kondoumh.com": "https://kondoumh.com/software/iedit/"
        }
    },
    "pluginsConfig": {
        "ga": {
            "token": "UA-XXXXXXX-X"
        }
    }
}

plugins でプラグインを指定して以下のコマンドでプラグインのインストールが可能です。

$ gitbook install

NPM モジュールなので package.json を記述して npm install でもインストール可能です。その場合は、パッケージ名のプレフィクス gitbook-plugin- が必要になります。

サイドバーの見出しを折りたたむことができる expandable-chapters プラグインと Google Analytics のトラッキングコードを埋め込む ga プラグインを追加してみました。

www.npmjs.com

www.npmjs.com

他にも YouTube の動画を組み込むプラグインなど色々と提供されてます。

ローカルでブラウザでプレビューしながら Markdown を書いていけます。

$ gitbook serve

localhost:3000 にアクセスすると、サイドバー付きのページを確認できます。

この状態で、既存の HTML から内容をコピペ&リライトしながら移行していきました。

ある程度できたところで公開先を検討。最初これまで通り kondoumh.com に配置することも考えましたが、リリースノートも移行して旧 Google Sites サイトを廃止しつつ URL が変らないようにしたいということで独立サイトにすることにしました。GitHub Pages は独自ドメインと HTTPS に対応していて GitBook ドキュメントも簡単に公開できることを知り、渡りに船ということで GitHub Pages を使うことに決定。

ライブリロード中に HTML の変換は随時やってくれますが、シンタックスエラー等でちゃんと反映されてない可能性もありますので、最終的に build します。

$ gitbook build

デフォルトの出力先は、_book ですが、GitHub Pages のお手軽設定だと docs にしておくのがよいです。その場合は、以下のようにします。

$ gitbook build <source dir> ./docs

ついでですが、ライブプレビューでも build と同じ引数で起動した方がいいでしょう。

$ gitbook serve <source dir> ./docs

ここまでくると、package.json を導入して npm script を書いた方がいいですね。ちなみに Markdown ファイルを置く <source dir> は books.json の root キーで指定可能ですが、出力先の指定はできません。Issue が上がっていました。

github.com

ともかく、ドキュメントが書けたら、GitHub に docs フォルダを含めて push します。

github.com

push したリポジトリの Settings タブを開き、GitHub Pages を有効化し設定します。

f:id:kondoumh:20180826123244p:plain

Source で master branch /docs folder を設定します。master への push が即座に反映されるため gh-pages ブランチを作成して push する方法よりも、一人作業だと手軽でよいと思います。

Google Sites のドメイン設定を解除して、Custom Domain に設定しました。Enforce HTTPS も数分で有効化することができました。

リニューアルしたサイト

イントロダクション · iEdit User's Guide

中身はほとんど変えてないし、スクリーンショットも古いのを流用しているので新しい感は薄いかもしれませんが、外側はレスポンシブなモダンな感じになりました。更新も手軽になりましたし。

Jekyll と比べても導入が楽で、手軽に公開・更新でき、PDF も生成できる GitBook はドキュメント作成環境として有力な選択肢だと思います。

vcpkg で OSS のライブラリを導入し Visual C++ プロジェクトに静的リンクする

仕事では久しく C++ のコードを書いていませんが、最近 iEdit で JSON を扱えるようにしようと思って C++ の REST 関連ライブラリを物色していたところ、Microsoft が C++ REST SDK という OSS ライブラリを開発しているのを知りました。

github.com

Java でいうと Jersey と Jackson が一緒になったようなライブラリです。JSON の読み書きしか使いませんが、なかなかよいので採用することにしました。

最初は NuGet でパッケージをインストールして開発してました。必要な C++ のヘッダーファイルと DLL が降ってくるので開発は問題なくできるのですが、配布の際に DLL を同梱する必要があります。利用者の利便性を考えると極力ワンバイナリで提供したいので static リンクする方法を調べました。

昔は C/C++ のライブラリは、個別にソースコードをダウンロードして make / make install してました。Linux だと APT などのパッケージマネージャでバイナリを取得・インストールできますし、Mac の brew のようにソースコードベースのパッケージ管理システムもあります。

今回知ったのですが、Microsoft も近年クロスプラットフォームのソースコードベースパッケージ管理システムを OSS として開発しています。

github.com

ということで、まず vcpkg をビルド (PowerShell で実行します)。

PS> .¥bootstrap-vcpkg.bat

vcpkg.exe が生成されたディレクトリが vcpkg のルートディレクトリとなり、配下にパッケージを取得してビルド・インストールしていきます。

この時点で Visual Studio とのインテグレートをやっておきます。

PS> .¥vcpkg integrate install

あとは、導入したいパッケージを指定してインストールするだけです。

C++ REST SDK を公式 README 通りに以下のコマンドでインストールしました。

PS> .¥vcpkg install cpprestsdk cpprestsdk:x64-windows

C++ REST SDK は Boost C++ ライブラリに依存しているので、2コアの Core i5 マシンだとビルドに48分ぐらいかかりました。

その後、自プロジェクトから NuGet 版を削除してリビルド。エラーなく完了しました。

これは便利。integrate install しているため Visual C++ のプロジェクト設定で include パスの指定や lib の追加をしなくても大丈夫になっています。OSS のライブラリを気軽に試せますね。

ところで、生成されたバイナリのフォルダを確認したところ削除したはずの cpprest_2_10.dll がいました。ダイナミックリンク版になってしまっているようです。これだと時間かけてビルドしたのに NuGet と同じ結果です。

StackOverflow に QA がありました。

stackoverflow.com

static link 版はパッケージ名が違いました。しかも static link 版は integrate install していても自プロジェクト側で include パスなどを設定する必要があるそうです。

ということでまず static link 版をインストールしました。

PS> .¥vcpkg install cpprestsdk:x86-windows-static

その上で自プロジェクトの構成プロパティを設定(後述)。しかし、自プロジェクトをビルドするとやっぱり DLL を取ってきてしまいます。どうも DLL 版のパッケージがインストールされているとそちらを優先するようです。そこで DLL 版パッケージを削除しました。

PS> .¥vcpkg remove cpprestsdk:x64-windows

この状態でビルドしたところ、無事に static link されたバイナリが生成されました。

自プロジェクト側の構成プロパティ設定です。

C/C++ → プリプロセッサ → プリプロセッサの定義に _NO_ASYNCRTIMP を追加します。

f:id:kondoumh:20180822210420p:plain

C/C++ → コード生成 → ランタイムライブラリで マルチスレッド(MT) を選択します。

f:id:kondoumh:20180822205959p:plain

VC++ ディレクトリ → 全般 → ライブラリディレクトリに vcpkg のインストール先の lib ディレクトリを追加します。

f:id:kondoumh:20180822210900p:plain

リンカー → 入力 → 追加の依存ファイルに静的リンクしたいライブラリのファイル名を追加します。

f:id:kondoumh:20180822210937p:plain

C/C++ → 全般 → 追加のインクルードディレクトリに vcpkg のインストール先の include ディレクトリを追加します。

f:id:kondoumh:20180822211147p:plain

ディレクトリは相対パス指定がうまくできなかったので $(HOMEDRIVE)$(HOMEPATH) で逃げました。

ちょっと面倒ですが、NuGet で DLL 取得するのではなく vcpkg でビルドすることで好みの設定で使うことが可能ということですね。

Microsoft Remote Desktop 10 で macOS から Windows 10 ラップトップを使う

会社支給のラップトップが交換時期でリニューアルされました。Let' note CF-SZ6 です。

panasonic.biz

Windows 10 マシンなので開発機として利用することにしました。

自宅では MacBook Pro 13 inch オンリーですので、これまで Windows は VMware Fusion の仮想マシンで運用してきました。メモリ4GB / コア数2という構成で Visual Studio で .NET や C++ 開発はできますがやや非力です。SZ6 は Core i5 / 8GB RAM なので仮想マシンよりかなりキャパがあります。

自宅ワークスペースは狭くて2台のマシンを置いて使うことはできません。それに、外部ディスプレイの WQHD 解像度は SZ6 の HDMI からは出力できません。※ そこで MacBook から Remote Desktop で接続して使うことにしました。

※ 2018.9.17 追記 Intel Graphics の設定でカスタム解像度を追加して選択すると反映されました。

最初は以前にインストールしていた Microsoft Remote Desktop 8 を使いました。ドメインユーザの設定ができず毎回ログインし直しが必要だったり、Mac の 英数 / かな キーをスペースキーと認識してしまい IME のオンオフを別キーに割り当てなければいけなかったりと、かなりイマイチでした。これは常用厳しいなと思って App Store を見るとバージョン10が別アプリとしてリリースされています。

Microsoft Remote Desktop 10

Microsoft Remote Desktop 10

  • Microsoft Corporation
  • ビジネス
  • 無料

インストールして試したところ、上記の問題はすでに解消されており快適に使えるようになりました。レビューにあるような JIS キーボード認識されない問題も発生しませんでした。

さらに 8 ではなかった特徴として、

  • セッションが切れてもキャッシュが残っていれば再接続してくれる
  • macOS の キーショートカットを有効にできる
  • クリップボードのテキストデータをローカルとリモートで相互に共有できる
  • 実行中のリモートデスクトップ をサムネイル表示できる

といった機能まで実装されています。

f:id:kondoumh:20180812023036p:plain

これで仮装マシンから乗り換えられそうです。WiFi 経由でレイテンシーがあるため VMware Fusion で使っているのと体感的に大差ありませんが、実際には 2コア(Hyper-Threading で論理的には4コア) マシンを外部で使っているわけで MacBook のリソースにも余裕が出ます。

仮想マシンではリソースをケチるため Chrome や VS Code もインストールしてませんでしたし WSL も使ってませんでしたが、それらの制約もなくなりました。

VMware Fusion の仮想マシンは予備環境として眠らせておくことにしました。