buntan-pc #4
(0)
2025.05.07 Wed #0
- 今日はacl3.cを仕上げて、kuas.cをacl3.cに対応させたいです。
- ソースコードのサイズ比較(kuas04a→kuas05a):
| aclmini.c/acl3.c | 5125バイト → 9563バイト |
| kuas.c | 11106バイト → 9552バイト |
- ライブラリは結構いろいろ追加したので、増えたのはしょうがないかなー。
- kuas.cが1554バイト減ったのはうれしいです。それだけacl3.cはうまく開発をアシストできているということだと思います。
- このライブラリを使っただけで、アセンブラが10KB未満で書けるのなら、それは十分に楽しいと私は思うのです。
2025.05.07 Wed #1
- 実はAX_printf()っていうのを作りたい。これがあれば事前に十分な大きさのバッファを用意するとかしなくてよくなるから、プログラムがすごく気楽に作れるはずだ。そのためにはAX_printfの中でvsnprintf()を使えばよさそうだけど、サイズが足りなかったらバッファサイズを2倍とかにして何回かやり直すのだろうか。うーん、それはちょっと遅そうだなあ。
- と思ったら、snprintf族は、バッファに収まらなかった場合でも「仮にバッファが十分にあったとしたら何文字出力したか」を返すらしい。それはいい!これならバッファを拡大しながら呼び直すなんていうループはいらない。2回目で必ず成功できる。
2025.05.07 Wed #2
2025.05.08 Thu #0
- とりあえず、includeは問題なくできるようになった気がする。入れ子になっていても動いているし、循環参照になっても止まれるようにしてみた。
- 次はdefineかな。
2025.05.09 Fri #0
- こんな実験をしました。WSL2上でUbuntu 24.04.2 LTSを使って、そこでgcc 13.3.0を使って試しました。
- 以下のプログラムを作って、gccの-Sでアセンブラソースを出力させています。
#include <stdio.h>
static char msg[] = "not use this message";
static int func(int i)
{
puts(msg);
return i * i;
}
int main(int argc, const char **argv)
{
puts("hello, world");
return 0;
}
- 要点は、mainからfuncは一切呼ばれていないし、msg[]を使っているのはfuncだけだということです。
| gcc -S -O0 | msgとfuncもアセンブラソースに含まれる |
| gcc -S -O1 | msgとfuncはアセンブラソースに含まれない |
| gcc -S -O2 | msgとfuncはアセンブラソースに含まれない |
| gcc -S -O3 | msgとfuncはアセンブラソースに含まれない |
- このように、-O1以上では、使わない関数やグローバル変数があっても、それが実行ファイルに反映されることはなく、無視されます(static属性がない場合は、リンク時にほかのオブジェクトファイルから参照される可能性があるので、勝手には消さなくなります)。
- 今回は明示的には確認していませんが、ほかのコンパイラでも同様の挙動を示します。
- 昔からライブラリはクラスとプロトタイプ宣言のみのヘッダファイルに書き、関数の実体は別のソースコードに書いて分割コンパイルして、リンク時に必要なものだけ(参照されるものだけ)リンクされます。
- でもこの方法だと、コンパイラによる高度な最適化は適用されません。別々にコンパイルして、リンクするかしないかしか選べません。
- 一方で、ヘッダファイルに関数の実体も含めて全部書いてしまえば、コンパイル時間はかなり長くなるものの、コンパイラは大局的な最適ができるようになり、必要に応じて関数のインライン展開などもするようになります。そしてstatic属性さえつけておけば、使わない関数は出力されないので、いくら書いても問題ありません。
- 近年ではコンピュータの性能が上がってきて、小さなライブラリであれば分割しない方法でも十分に現実的な速度でコンパイルできるようになってきました。だから最近のKはヘッダファイルではなく関数本体をincludeするタイプのライブラリばかり作っています。
- でも一つだけ問題があります。gccとかで警告を多めにすると、未使用関数(記述したのに使わなかった関数)に対して、いちいち警告を出すのです。これがめんどくさいのです。
- まあ普通は関数を書いたら使われる方が当たり前で、そうなってないということは「あれ?何か書き忘れがあるんじゃないですか?」ってことで教えてくれるのは親切なのですが、私のような使い方だと「おせっかい」で「ありがた迷惑」なのです。
- gccの場合「-Wno-unused-function」とか「-Wno-unused-variable」を指定すれば、この手の警告だけ抑制できるので、私はいつも指定しています。
2025.05.10 Sat #0
- buntan-pcでは、平凡な仕様よりもちょっと変な仕様の方が望ましいので、#defineについてもちょっとだけ冒険することにします。
- [1]#defineマクロは、引数の有無、引数の数で区別されます。
- [2]#ifdefやdefined(...)は引数有り無し含めてとにかくどれか一つでも定義されていれば真とします。
- [3]既に定義済みのマクロに対してさらに#defineすることを許します。その場合、新しい定義が有効になります。そして#undefすると元に戻ります。
- [4]#undefでカッコのつかないマクロ名を指定した場合、まずはカッコのつかないマクロのundefを試みますが、もし該当するものが見つからなければ、同名の引数あり版を探してundefします。
2025.05.12 Mon #0
- すごく適当に#defineを作ってみたら、メモリ効率が良くなかったので、もうちょっとちゃんと作ることにします。
2025.05.17 Sat #0
- 単純に作れるか作れないかで言えば作れるのですが、しかし「なんでこの程度の処理がすっきり書けないのか?」と思うと、要するにライブラリの不備のせいだということになり、じゃあライブラリをどうすればいいのかというと、考え込んでしまいます。まあでもそれが楽しいんです。がんばります。
2025.05.19 Mon #0
- defineとundefもできるようになりました。あとは#if類だけです。
- acl3.c : 498行(14.9KB)
- kcpp0.c : 277行(6.8KB)
- もちろん、 ## や # も使えます。
#define ACat_Helper(x, y) x##y
#define ACat(x, y) ACat_Helper(x, y)
#define PutsNum(i) puts(#i)
ACat(Puts, Num)(123); → puts("123");になる
- 私はこの成果にとても驚いています。まだincludeとdefine類だけではありますが、結構プリプロセッサらしく動いています。
- 2000年くらいにOSASKを作っていたころ、私はASKAでプリプロセッサを使いたくて、gccのcpp0を利用していました。あの時はプリプロセッサは「きっと作るのが大変だろう」という存在でした。こんな簡単に作れると知っていたら、きっと自作したでしょう。
- 今回こんなに小さく作れたのは自作ライブラリであるacl3.cがあるからです。自分の開発力が底上げされているなととても実感します。
- 可変長メモリ構造を楽に扱える関数とか、メモリリークをすぐに確認できる機能をとてもよく使っています。
2025.05.19 Mon #1
- 調べてみてびっくり、いつの間にか#elifdefと#elifndefというものが追加されたらしいです。
- とりあえず、内部処理としては、
- #ifdef ... → #if defined(...)
- #ifndef ... → #if !defined(...)
- #elifdef ... → #elif defined(...)
- #elifndef ... → #elif !defined(...)
- と読み替えるだけにしたので、コスト的には7行の追加で済みました。
- これであとは#if、#else、#elif、#endifを何とかすれば完成ということになります。