scoop でインストールした plantuml と plantuml-mode on Emacs でプレビューできない件

  • Windows
  • scoop でインストールした plantuml
  • Emacs の plantuml-mode
    • http://plantuml.com/ のオンライン変換を使うのではなく、以下のような設定でローカルで変換する
  (setq plantuml-executable-path (executable-find "plantuml"))
  (setq plantuml-default-exec-mode 'executable)

というケースで C-c C-c とかすると文字化けバッファが表示される。

Warning: -headless flag must be the first one in the command line

ってなのが最初についててその後ろに画像データがくっついているので、 Emacs が画像とみなさないようだ。

本質的には、 「画像も warning も同じ標準出力に出している」 と思われる plantuml に非があると推測するが、 Emacs の設定からどうコマンドラインオプションを指定しても どうしてもこの warning が消せない。

プレビュー機能を諦めて2ヶ月くらい放置してたんだが、 今日たまたま

(executable-path "plantuml")

とかしてて plantuml.cmd の中身を見てて原因がわかった。 scoop が作る plantuml.cmd は graphviz の dot のパスをコマンドラインオプションで plantuml.jar へ渡すようになってて、 plantuml.cmd に対して指定したコマンドラインオプションは必ずその後ろに付加される書き方になってる。 だから、この問題は plantuml.cmd の外側 ≒ Emacs plantuml-mode の設定 では解決できないんだ。

なので、plantuml.cmd でこう↓なっているのを

@java -jar "D:\Users\mino\scoop\apps\plantuml\1.2020.19\plantuml.jar" -graphvizdot "D:\Users\mino\scoop\shims\dot.exe" %*

こうしてしまえば warning は出なくなる。

@java -jar "D:\Users\mino\scoop\apps\plantuml\1.2020.19\plantuml.jar" %* -graphvizdot "D:\Users\mino\scoop\shims\dot.exe"

ひとまず解決したのは良かったけど、まぁ釈然とはしないなぁ…w

あ、一応この plantuml.cmd を生成してる scoop extras bucket の修正 PR は出しておきました。 個人的には解決しちゃったので、マージされなくてもちっとも困らないんですが、 今後ハマる人が少しでも少なくなれば。

emacs --daemon + emacsclient で起動したときも、daemon 使わず起動したときも期待通りの設定で動くようにする (途中)

途中なんだけどメモっておかないと忘れるので。

やりたいことは…

  • Emacs の起動高速化したい、 emacs --daemon が使えそう
  • 今すでにある「環境によらず同じ初期化ファイル」というのはキープしたい
  • emacs --daemon + emacsclient も、単に emacs を起動しても動くようにしたい
  • text terminal で emacs -nw で起動した場合も、GUIで起動した場合も動くようにしたいが、それぞれで見た目とか設定を変えたい可能性もある

↑こんな感じ。

で、こんなの↓を作ってみて

github.com

それでわかった?ことは…

  • emacs --daemon で初期化ファイルを読み込んだときは window-systemnil なので一部設定ができない
  • emacsclient で起動したときに設定を遅延すればよくて使えそうな hook が after-make-frame-hook
  • daemon 使用時と非使用時(普通に emacs 起動したとき)で初期化ファイルを共有するためには…
    • GUI 設定を関数にまとめとく
    • その関数を after-make-frame-hook にひっかけておく
    • when (window-system) のときは hook が呼ばれるのを待たずにその関数を呼んじゃう
    • これだけだとフレーム作るたびに関数が呼ばれるので、関数の最後で自分自身を remove-hook しとく
  • after-make-frame-hook は frame 生成後に呼ばれるので default-frame-alist とかをそこで設定しても frame には反映されない?
    • とりあえず (modify-frame-parameters (selected-frame) default-frame-alist) 的なことをすれば解決はできそう

まぁ、何が言いたいかというと、 emacs --daemon がもうちょっと使いやすいと嬉しい、と。

emacsclient でファイルを開かずにフレームを開く

Emacs の起動高速化の関連でちょっと調べてた件。

普通に emacsclient コマンドを使うとファイルを指定しないといけないんだが、

$ emacsclient -c

でフレームだけ開ける。 終了を待ち合わせないで良いなら

$ emacsclient -c -n

で OK。

これを使って 「Ubuntu, macOS, Windows あたりの環境において アイコンクリックで高速にEmacs起動するようにできないかな…」 とか考えをめぐらし中…。 ログイン時に emacs --daemon するか、 初回アイコンクリック時だけ emacs --daemon してから emacsclient 叩くみたいなことしないとダメかなぁ…。

Doxygen で internal な実装群を隠す

隠すやり方がいくつかあるんだけど、 どう隠したいかで使うべきコマンドが違うみたい…。

遭遇したケース

通常、 source file local な class やメソッドに対する Doxygen コメントとかは Doxygen の設定の EXTRACT_〜 を適切にすれば出力されないはず。

私が今回遭遇したケースは

  • constexpr function なのでヘッダーに実装を書いてる
  • Google C++ Style Guide に則るとヘッダーファイルでは無名名前空間が使えないので impl という名前空間に入れてる
  • 勢い余ってせっかく書いた doxygen コメントは残しておきたいが、内部実装なので API リファレンスとしての Doxygen の output からは除外したい
  • そういった実装が複数連続してる存在してるのでまとめて隠したい

この場合は @cond, @endcond を使うとできる。

github.com

Doxygen における条件による出力の出し分け方

いくつか方法がある。以下網羅できてるかは知らない。

#ifndef を使う方法

  • 隠したいところを Doxygen 解釈時以外には絶対に定義されないマクロの #ifndef で括る
#ifndef DOXYGEN_SHOW_CLASS_FOO

/// This class provides so FOO features.
class Foo {
};

#endif  // !defined(DOXYGEN_SHOW_CLASS_FOO)
  • ENABLE_PREPROCESSING = YES にする
  • 出力したいときは PREDEFINED = DOXYGEN_SHOW_CLASS_FOO にする

これは @cond が実装される前に使われてた方法らしい。

  • C / C++ として何か作用したいわけではないのに、 C / C++ としてのソースが汚れる
  • プリプロセッサマクロを解釈するようなツール群が期待しない動作になる or 期待動作のために追加設定が必要になる、かも
    • エディタのコードハイライティング
    • lint など静的解析ツール
    • ...

あたりが欠点かなぁ…。

@cond を使う方法

これは上で書いたので簡単に言うと #ifndef とかの代わりに Doxygen コマンドを使う、と。

/// @cond DOXYGEN_SHOW_CLASS_FOO

/// This class provides so FOO features.
class Foo {
};

/// This class provides so BAR features.
class Bar {
};

/// @endcond
  • 出力したいときは ENABLED_SECTIONS=DOXYGEN_SHOW_CLASS_FOO のようにする

これは使いやすいよね。コードハイライティングがおかしくなったりもしないだろうし。 ちなみに常時出力しないのでよければ @cond のうしろ(「セクションラベル」と呼ぶらしい)は省略できるらしい。

/// @cond

/// This class provides so FOO features.
class Foo {
};

/// This class provides so BAR features.
class Bar {
};

/// @endcond

まぁ、どういう意図で @condで括っているかの表明にもなるから、私だったら省略しないルールにすると思う。

@if を使う方法

これが紛らわしい。 @cond がざっくりまとめて出力・非出力を切り替える目的に使えるのに対して、 @if はコメントブロック内で一部の出力条件を変える目的に使う。 この「コメントブロック内〜」が重要なんだけど私は最初読み飛ばしちゃってて混乱した。 要はクラスなり関数なりの説明のまとまりの中で一部を出力する条件を記述するのに使えるってことらしい。

今回のように目的が「コードを読んだ人には見せるけど Doxygen 出力からは隠す」だと

/// This class provides so FOO features.
///
/// @if CLASS_FOO_SECRET
/// This is not doxygen-documented secret, Ha ha.
/// @endif
class Foo {
};

みたいなことになる。 ENABLED_SECTIONS=CLASS_FOO_SECRET としてやれば括ってあるところも出力される。 ただ、コメントブロックをまたげないので、 今回のように複数関数をまとめて出力する・しないを切り替えたいような用途には使えない。 これだけだと私にはあんまりありがたみが感じられなかったんだが、

/// This class provides so FOO features.
///
/// @if english
/// Details by English.
/// @elseif japanese
/// 日本語での詳細記述。
/// @endif
class Foo {
};

こんな感じで言語を切り替えるのに使ってるのは、 そういえば以前に見たような記憶がかすかにある…。 ENABLED_SECTIONS と出力先を切り替えながら出力すると 複数言語分のドキュメントが生成できる、と。

Dependency Walker のいま

Windows で exe/dll の依存関係を追跡するツール Dependency Walker のいまについて。 ちょっと調べれば情報に行き当たるので、 大して書くこともないんだが…。

ちなみに  Dependency Walker が VS の配布物から外れたのは10年以上前らしく、 私が Windows アプリの本格的な開発から外れて長いのが分かりますw

gcovr や Doxygen の HTML を自動的に deploy する

やりたいこと

  • GitHubリポジトリ上のものを種に生成される静的 HTML サイトを自動的に deploy する
    • Doxygen で生成した HTML
    • gcovr で生成した Coverage report の HTML
    • ...

GitHub Actions で actions/upload-artifact を使えば ディレクトリ単位の成果物を workflow の実行に紐付けてアップロードするのは簡単にできるのだが、 ディレクトリごとの ZIP ファイルになってしまうので いちいちダウンロードして解凍しないとブラウザで確認できない。 これはちとめんどくさい。

どこかのサイトに自動的にアップロードされて、 必要に応じて自動的に更新されると嬉しいなー、と。

道具

  • GitHub Pages
    • deploy 先として使う
    • あらかじめ設定しておいたブランチに push さえすれば、その内容を https://username.github.io/repository_name/ というサイトとして見られるように deploy できる仕組み
  • GitHub Actions
    • GitHub 謹製の CI/CD システム
    • 静的 HTML サイトの構成を作って deploy するのに使う
  • peaceiris/actions-gh-pages
    • 「あらかじめ設定しておいたブランチに push」をやってくれる GitHub Actions の action

下準備

  • リポジトリの Settings で GitHub Pages の設定を gh-pages ブランチを deploy するように設定しておく
    • あとからやってもいいが、 push 時にしか deploy されないみたいなので、あとから設定した場合は設定後一度 push する必要があるっぽい

解説

on:
  push:
    branches:
      - master
  workflow_dispatch:
jobs:
  deploy:
    runs-on: ubuntu-18.04
  • チェックアウトする
    steps:
    - uses: actions/checkout@v1
  • 静的HTMLサイトを生成するのに必要な道具をセットアップする
    • ここは Pelican でも何でもお好みのツールをセットアップすればOK
    • この例では coverage report のための gcovr と Google Test Framework、Doxygen ドキュメントのために DoxygenGraphViz をインストールしてる
    • 最後の rm -rf googletestmake install するために checkout してきたソースコードとビルド生成物を消してる
      • これをやっておかないと googletest/ 以下の Doxygen とか作ろうとしちゃう
      • /tmp/ とかへ checkout するなり、 Doxyfile で exclude するなりすればいいのだが、手を抜いてるだけ
    - name: Install gcovr
      run: |
        sudo pip3 install gcovr
    - name: Install graphviz and doxygen
      run: |
        sudo apt update
        sudo apt install -y --no-install-recommends graphviz doxygen
    - name: Install googletest
      run: |
        git clone https://github.com/google/googletest.git
        cd googletest
        cmake -DBUILD_GMOCK=OFF -DCMAKE_CXX_STANDARD=11 .
        sudo make -k -j all install
        cd ..
        rm -rf googletest
    - name: Build site
      run: |
        make BUILD_TYPE=coverage -k -j site
  • Deploy する、 publish_dir 以下が deploy される
    • 実際にはリポジトリgh-pages というブランチに push されるだけ
    - name: Deploy
      uses: peaceiris/actions-gh-pages@v3
      with:
        github_token: ${{ secrets.GITHUB_TOKEN }}
        publish_dir: ./out/site

できあがったサイト

  • 以上で、 PR マージなどで master ブランチが更新されるたびに静的 HTML サイト(Doxygen ドキュメントと coverage report)が更新される
  • というわけで出来上がったのが minorusekine.github.io

四捨五入の実装って地味に面倒なんじゃないか? という話

C言語だと round() とかが整備されたのって C99 からじゃないかと思うんだが、 まぁまぁ詳しい事情は置いておくとして 二十年くらい前までって四捨五入って当たり前に個々に実装してたような記憶がある。 で、その実装もたいてい 「0.5足して切り捨てればいいじゃん」 という実装だったと記憶している。 別に間違ってないんだけど、これってその型が表現できる最大値から 0.5 以内のところって たぶん正しく動作しないよね??

そのへんはどうしてたんだろうか…

まさに「個々に実装してた」の「個々」の事情で 最大値スレスレにならなきゃ問題にならないからいいってことなんだろうか…。

というのを

GitHub - MinoruSekine/libfixedpointnumber: Library for fixed point number by C++11

の実装を思案してて気になったのでした…。

まぁ「0.5足して切り捨てる」だけだと値が負のときの動作が JIS Z8401 相当になることは諦めてるわけで、 その時点で「個々」の事情で起き得ないところは動作の正しさを諦めてるってことか…。