zig translate-c を試す
zigのお勉強のためにzig言語でRui Ueyamaさんの低レイヤを知りたい人のためのCコンパイラ作成入門をやっています。
エラーメッセージの改良までやってつまりました。
printf("%*s", pos, " ");
こんな感じに、文字列をn回繰り返して表示する方法がわからなかったんです。
ZigにはC言語で書かれたソースファイルをZigに変換する機能もあります。
この機能を使って、 Cの書き方をZigで書いたらどうなるか? を調べてしまいましょう。
以下のソースファイルを変換します。
#include <stdio.h>
int print_n_times(int n){
printf("%*s", n, " ");
}
何も考えずにzig translate-c
コマンドを使ってみましたがエラーになりました。
stdio.hが見つからないようです。
> zig translate-c main.c
src/main.c:1:10: 'stdio.h' file not found
zig libc
コマンドを使うと、zigが参照しているlibcのパスを表示できました。
> zig libc
# The directory that contains `stdlib.h`.
# On POSIX-like systems, include directories be found with: `cc -E -Wp,-v -xc /dev/null`
include_dir=/usr/include
# The system-specific include directory. May be the same as `include_dir`.
# On Windows it's the directory that includes `vcruntime.h`.
# On POSIX it's the directory that includes `sys/errno.h`.
sys_include_dir=/usr/include/x86_64-linux-gnu
# The directory that contains `crt1.o` or `crt2.o`.
# On POSIX, can be found with `cc -print-file-name=crt1.o`.
# Not needed when targeting MacOS.
crt_dir=/usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu
# The directory that contains `vcruntime.lib`.
# Only needed when targeting MSVC on Windows.
msvc_lib_dir=
# The directory that contains `kernel32.lib`.
# Only needed when targeting MSVC on Windows.
kernel32_lib_dir=
# The directory that contains `crtbeginS.o` and `crtendS.o`
# Only needed when targeting Haiku.
gcc_dir=
includeパスがわかったので、もう一度zig translate-cに挑戦します。
コマンドのヘルプをちゃんと読み込んでないのでわかってないんですが、そのままだと標準出力に出るっぽい?
> zig translate-c main.c -I /usr/include -I /usr/include/x86_64-linux-gnu -I /usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu > main.zig
無事変換できました!
長すぎるので、print_n_times関数だけ載せてます。
標準ヘッダの中身を全部出すらしく、出力ファイルはむちゃくちゃ長くなりました。
~~~ 省略 ~~~
pub export fn print_n_times(arg_n: c_int) c_int {
var n = arg_n;
_ = printf("%*s", n, " ");
return 0;
}
~~~ 省略 ~~~
うん…Cのprintfをzigから使っていますね。
zigからはCの関数が呼べちゃいます。あの、Zigとしての書き方は…?(お勉強をしたかったので)。
zigの1.0.0はまだ出ていないんで、機能がまだないのかもしれないですね。
あまり深追いせずに、これは別で調べてみようと思います。
2023/01/30追記
コメントで、文字列と繰り返し回数がコンパイル時に決定できるなら、**
演算子が使えると教えてもらえました。
試しにabcを3回繰り返して出力する関数を書いてみます。
const std = @import("std");
const stdout = std.io.getStdOut().writer();
pub fn print_abc_3_times() !void {
_ = try stdout.print("{s}\n", .{ "abc" ** 3 });
}
test {
try print_abc_3_times();
}
ちゃんと3回繰り返した文字列を出力することができました。
>zig test main.zig
Test [1/1] test_0... abcabcabc
All 1 tests passed.
Discussion
文字列をn回繰り返すのに、その文字列もnもコンパイル時に決まるものならば
**
演算子が使えると思います。コメントありがとうございます!
もともとの記事の動機はnがコンパイル時に決定できなかったので、
**
演算子が使えなく、コンパイル時に決まらない場合の例を調べようとしてました。大事なポイントなのに、サンプルコードで深く考えずに定数で書いてしまったのが良くなかったです…。
文字列も繰り返し回数もコンパイル時に決まる場合の例を追記させてもらいました。