カテゴリー: post

uta8alibという競技プログラミング用ライブラリの環境構築について書きます。
今回環境を作るに当たって、ngtkana さんngtlibを参考にした部分が大きいです。ありがとうございました。

やりたいこと

今後の展望

開発の流れ

ディレクトリ構成を決める

ディレクトリ構成は以下のようになっています。
src の下に cpp と rust があり、その下にディレクトリが存在していてexampleなどがライブラリの名前になります。コードはそのディレクトリの中のmain.[rs|hpp]に書きます。
test はテストコードです。src と同様の構成になっています。

❯ tree -I 'Catch2|chan|cpp-test|rust-test'
.
├── CMakeLists.txt
├── Dockerfile
├── libtest.sh
├── README.md
├── src
│   ├── cpp
│   │   ├── example
│   │   │   └── main.hpp
│   │   └── include
│   │       └── lib.hpp
│   └── rust
│       └── example
│           └── main.rs
├── test
│   ├── cpp
│   │   ├── CMakeLists.txt
│   │   ├── example
│   │   │   └── main.cpp
│   │   └── main.cpp
│   └── rust
│       └── example
│           └── main.rs
└── todo.md

重視したことは、少しくらい深さのある構成になっても自分が好きな方を選ぶということです。lib-name.hppというような名前のファイルをフラットにsrc直下に置く方も多いですが、僕はlib-name/main.hppの方が好きなのでこうしました。好みの問題ですね。
深さができて GitHub で見るには面倒、という弱点のために、document を自動生成してブラウザで見る、というようにしたいです。コードブロックの右上にコピペボタンを用意すれば捗りそうです。

自分用の競プロ CLI ツール、chan を使う

競プロをするときは焦りがちなので、使うコマンドは最小限にしたいです。あと、打ちやすいほうがいい。僕はchanというコマンドでコンパイルして実行ファイル生成を行っていましたが、このコマンドにライブラリのテンプレート作成機能をもたせることにしました。

chan main.rs # rustc main.rsが走る
chan main.cpp # g++ が走る
/path/to/uta8alib$ chan generate cpp example # exampleという名前のフォルダで、C++のsrcとtestのテンプレートファイル作成。C++の場合、include/lib.hppにパスを追加する。

引数の数で判別しています。単純な機能ですが、Rust で 4 時間くらいかけて書いて、Python3 とかの方がこういうのは向いてるなぁと感じました。外部 crate は使いませんでしたが、clap-rsを使うと高機能にできそうです。ソースコードの本体はこちらです。
重視したことは、色付きで出力させることです。はじめ Option なしでは色がなく、読みづらく感じていました。調べたところ、強制的に coloring を ON にする Option をつければよいと分かりました。Rust は rustc --color=always を、C++は g++ -fdiagnostics-color=always を指定しています。

git submodule を使う

ngtlibを見て git submodule に興味が湧いていました。また、競プロに関わることは普段はなるべくひとつにまとめておきたいものです。というわけで chan を submodule として uta8alib に取り込みます。

やること

[submodule "chan"]
	path = chan
	url = git@github.com:uta8a/chan.git

読むべき

困った時のコマンド

注意

Rust のテスト方法を考える

今回、cargo で外部 crate を入れるつもりはなかったので、cargo を使わないディレクトリ構成をしていました。しかしテストをするには cargo がよいです。そこで、テストのときにcargo newしてcargo testするという方法に決めました。
libtest.sh# rust test の箇所を見ると分かるのですが、cat で src と test のファイルを結合したものをcargo newで生成したディレクトリの中に入れて、lib.rsに追記しています。
cargoを完全にテストツールとして使っていて強引な気がしますがうまく行っています。一つのファイルにまとめているので private method test ができ、わざわざ pub をつけずにライブラリを書くことが出来ます。

C++のテスト方法を考える

git submodule を使うと同じ手順でCatch2を導入しました。

やること

細かく分けるとこんな感じでした。テストフレームワークの選定についてはwikipedia のユニットテストフレームワークの記事の C++の欄を参考にしました。

ヘッダ・ファイルのみからなり、外部に依存しない。(wikipedia の記事より引用)

たくさんあるので google test と Catch2 しか見れませんでしたが、違いがいまいち分からなかったのでngtlibでも使われている Catch2 を選択しました。
CMakeLists.tst についてはngtlibを参考にしたいのであまり理解できていません。以下の記事が参考になりました。

参考

make が通った時は嬉しかったです。

Git の開発手順を見直す

ここまでずっと master に push してきましたが、そろそろ CI を導入するとなると、master にプルリク送ったときのみ CI が回るようにしたいです。現在は develop と master の 2 つですが、ライブラリを作る時はfeature-xxxというブランチを作り、Rust と C++が揃った段階で develop に取り込み、master へマージして CI 回すという形にしたいです。

参考

CI でテストを回す

以前 GitHub Actions を使ったことがあったので、今回は Circle CI を使ってみました。結論のconfig.ymlこちらです。

version: 2.0

jobs:
  build_and_test:
    docker:
      - image: uta8a/circleci-rust-cmake:0.0.1
    environment:
      USER: uta8a
    working_directory: ~/work
    steps:
      - checkout
      - run: git submodule sync
      - run: git submodule update --init
      - run: ./libtest.sh

workflows:
  version: 2
  build:
    jobs:
      - build_and_test:
          filters:
            branches:
              only: master

注意

status badge を GitHub につける

公式の documentを読むと簡単に導入できます。Different Style の方が好みだったのでそうしました。

LICENSE を加える

加えました。MIT。

今後の展望

Logo を作ってやる気を高める

Verify してみる

snippet の導入

競プロで使うエディタの選定

どこでも競プロできるようにシェルスクリプトを書く

ライブラリの種類をどうやって増やすか(アルゴリズムを学ぶ)

この記事を書くに当たって

[article](#toc) で見出しへの anchor link が張れるのですが、これは一癖あって、hugo で生成されたものの id をみながらそれと一致するようにやっていくとよいです。例えば、 ##C++でのテスト方法を考える#c--でのテスト方法を考える に変化します。

終わりに

今回環境構築がメインでもう力尽きそうですがここからがスタートラインです。今年は競プロのレートに興味を持たずに、アルゴリズムに興味を持つことを目標にしていきたいです。