年末 (2)
2024/12/27
はじめに
年末なので C言語で昔作ったプログラムの改修をすることにした。
来年になったら勉強を兼ねて一から作り直したいと思うが、その気力があるかどうかわからん。
コードを公開しないので、作業中に出てきた問題や理解していなかったことをメモがてら記録していく。
全然まとまりはない。
gcc
gcc のバージョンを気にしていたのは 4
くらいまでで、それ以降は apt install
にお任せだった。
当時の gcc もあまり詳しく覚えていないが、いろいろ変わっているだろう。
$ gcc --version
gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-Werror=deprecated-declarations
数年前に作ったプロジェクトをビルドするとエラーになった。
OpenSSL のライブラリを使っている箇所だった。
error: ‘SHA256_Init’ is deprecated: Since OpenSSL 3.0 [-Werror=deprecated-declarations]
32 | SHA256_Init(p_sha256);
| ^~~~~~~~~~~
CFLAGS=-Wall -Werror
としていたのでエラーになったが、付けなかったら warning が出つつもビルドは成功していただろう。
この “deprecated-declarations” は初見だ。
出さない方のオプション はあるので何もしなかったら warning として出力されるのだろう。
attribute ((deprecated)) を使うそうだ。
OpenSSL ではマクロになっている。
OpenSSL 3
肝心の関数だが OpenSSL 3.0 から deprecated になったそうだ。
SHA256()
みたいに 1 回で済む関数は残っている。
そうでなければ EVP_DigestInit_ex() などを使う。
int EVP_DigestInit_ex2(EVP_MD_CTX *ctx, const EVP_MD *type, const OSSL_PARAM params[]);
int EVP_DigestInit_ex(EVP_MD_CTX *ctx, const EVP_MD *type, ENGINE *impl);
EVP ってなんだかわからなかったが “digital EnVeloPe library” の略らしい。
略称の解説はここのサイトしか出てこなかったが EVP_DigestInit_ex
のページにも “digital envelope” という言葉が 1箇所だけ出てくるので有力そうだ。
GitHub Copilot が自動で補完してくれるのが便利
使い始めたばかりの GitHub Copilot だが EVP_DigestInit_ex()
などに置き換えるときに便利だった。
置き換え自体は自分でやらないといかんが、元がエラーチェックしていなかったので if (...
と追加し、後ろに ) {
と書くとそれっぽいエラー処理を書いてくれた。
戻り値も周辺のコードを見たのか自分で定義していたマクロを使っていた。
こういう細かいところを補ってくれるのも便利だし、これからの人はこういう世界なんだなと思ってしまった。
サービスが終わっていたことを思い出す
テストデータのところにコメントがあって、そのデータの取得に Web API をたたいていた。
が、そのサービスは今ではもう停止していた。
そうだ、あんなに使っていたのに終わっていたのだよ。。。
データの取得は他の方法でもできるので問題は無いのだが、しんみりしてしまった。
C言語のテスト
私は C言語でテストコードを書くときは fff を使うことが多い。
今回のプロジェクトでも fff を使っていた。
個人リポジトリでもサンプルを作っているくらいには使っているのだ。
テストコードは C++ で書いて、実行は Google の GTest だったはずだ。
C 言語だと同じ関数名などを複数実装することはできない。
しかし C++ は namespace
が使えるので、それさえ別になれば同じ名前の関数でも実装できる。
それを利用し、テスト対象のコードは #include
で名前無し namespace
に読み込めば、同じファイルを別のテストコードで #include
してもリンクエラーにならない。
まあ便利!である。
多少癖はあるかもしれないが、C言語で使える単体テストツールでオリジナルのコードを変更しなくて良いというのは非常に助かるのだ。
それまでは関数の static
を別のマクロで定義しておいて、テストの場合は static
ではなくする、みたいなやり方をしていたけど、
今年は ncs のテストで Unity/CMock もちょっとだけ使ったし、
いつか jest みたいに定番のツールが出る・・・ことは多分無いな。
長いことこういう状況だったのが急に変わるとは思えん。
ただ、GitHub Copilot などが上手に自動生成するテストツールが固定化されるなどで定番になるという現象はありそうだ。
名前ルール
今ひとつ私の中でも決め切れていない関数名や変数名のルール。
C言語で書くとき、関数名は小文字のスネークケース記法、const値やマクロは大文字のスネークケース記法にしている。
C++ だと関数名やconst値はキャメルにしていた気がする。たぶん VC++ の影響だと思う。
今回のプロジェクトは C言語。
関数名は小文字スネークなのだが、変数名がキャメルになっていてなんだか落ち着かない。
仮決めしよう。
- 関数名と仮引数は小文字キャメル
- ポインタの場合は
p_
を頭に付けていたが、長くなるし typedef がポインタ型だとどっちなんだってなるので
- ポインタの場合は
- マクロは大文字キャメル
- インクルードガードはファイル名を大文字にしてドットをアンダースコアにする
test_file.h
–>TEST_FILE_H
- 昔は前後にアンダースコアを付けていたが、前はよろしくないし後ろもいらないだろう
- インクルードガードはファイル名を大文字にしてドットをアンダースコアにする
- const値は大文字キャメル
- 変数
- ローカル変数は小文字キャメル
- グローバル変数(static変数含む)はどうしよう(悩み中)
- ローカル変数と同じ見た目だと区別しにくいのを気にしている
- それをいうと static 関数とそうでない関数も区別を付けるべきか?
- struct / union / enum のタグ名は小文字キャメル
- typedefは小文字キャメルで最後に
_t
を付ける(悩み中)- ポインタ変数の
p_
と同じようなことになるんじゃないかという気持ち - 昔は struct / union / enum はタグ名を付けずに typedef していたのだけど、そうしなければ
_t
など付けなくてもなんとかなりそうな気がする - 関数ポインタに名前を付けるときは
_func
を付けたいが、そういうのはまた別の課題だな
- ポインタ変数の
こういうのは Copilot で一括で変更してくれないだろうか。
インデントのスタイルとかだと astyle でできるのだけど中身についてはさすがに無理なのだ。
数字の桁区切り
他の言語を使っていていいなと思ったのが、数値に桁区切りを付ける書き方ができるというものだ。
0x123456789a
–> 0x12_3456_789a
のような書き方だ。
単にアンダースコアを無視するだけだと思うのだが、16進数のように桁数が多い場合は非常にありがたい。
全然関係ないが、数字の桁区切りは地方によって違っていて、小数点としてドット使う国はコンマを使う国よりも少ない。
スペイン語に対応するときにドットとコンマの意味が逆という話が出て、それで調べていて知った。
区切りも 3桁じゃない国もあるし、真面目に対応させると難しいので OS が対応しているならそれを使うのがよいだろう。
ISO 標準は、区切りは 3桁ごとのスペースで小数点はどちらでもありだそうな。
libssl で ripemd160 が使えない
WSL2 で apt install libssl-dev
で OpenSSL のライブラリを組み込んだ。
RIPEMD160 の計算をしようとしたのだがエラーになる。
昔の RIPEMD160()
は deprecated だったので EVP_Q_digest()
にした。
SHA256()
はまだ使えるのに。。。
ドキュメントを見ても何を指定したら良いかわからんので ChatGPT氏に訊く。
よくわからんかった 第1, 3引数は NULL でよいのか。
// RIPEMD-160ハッシュを計算
if (EVP_Q_digest(NULL, "RIPEMD160", NULL,
(unsigned char *)input, strlen(input),
hash, &hash_len) != 1) {
fprintf(stderr, "ハッシュ計算に失敗しました。\n");
return 1;
}
で、これでエラーが返ってくる。
いろいろ調べたが History にこういう記載があった。
The RIPEMD160 digest was added to the default provider in OpenSSL 3.0.7.
All other functionality was added in OpenSSL 3.0.
インストールされているバージョンを確認。
3.0.2
なのでまだ RIPEMD160 はデフォルトで入っていないということか?
$ apt list --installed | grep libssl-dev
WARNING: apt does not have a stable CLI interface. Use with caution in scripts.
libssl-dev/jammy-updates,jammy-security,now 3.0.2-0ubuntu1.18 amd64 [installed]
悩む、悩むね。
libssl で使っているのが SHA256, RIPEMD160、あとは乱数生成くらい。
もうちょっと扱いやすいライブラリがあるだろう。
ChatGPT氏にきくといくつか教えてくれた。
- gpg/libgcrypt: The GNU crypto library. NOTE: Maintainers are not tracking this mirror. Do not make pull requests here, nor comment any commits, submit them usual way to bug tracker (https://www.gnupg.org/documentation/bts.html) or to the mailing list (https://www.gnupg.org/documentation/mailing-lists.html).
- libtom/libtomcrypt: LibTomCrypt is a fairly comprehensive, modular and portable cryptographic toolkit that provides developers with a vast array of well known published block ciphers, one-way hash functions, chaining modes, pseudo-random number generators, public key cryptography and a plethora of other routines.
Star の数では libtomcrypt の方がかなり多い。
GnuPG なので libgcrypt の方が多いだろうと思っていたが、GnuPG だからこそあまり汎用では使わないということかもしれない。
昔はこういうとき MbedTLS を使っていたのだけど、組み込みソフトウェアで使われることが多いからか libssl よりも結構遅かったのだ。
調整すれば速度は上がるのかもしれないが、それならもう libssl でいいや、ということにしたのだ。
組み込みソフトウェアじゃないしね。
WolfSSL も有名どころだけど、これも組み込みソフトウェアで使われることが多いしなー、というだけの理由で使っていない。
今回作っているのは Bitcoin の基礎的なところなので Bitcoin 用のライブラリを探して組み込めばよいのだが、 そうすると勉強として作っているのが意味なくなるのだよ。