🏣
C言語のソースコードをZigに変換する機能を試す(その2)
この続き。
今回は実際にgolangのプログラムからCGOで使っていたCの部分を題材にしてみた。webcamの設定値を読み書きする
webcam_ctrl.h
#ifndef _WEBCAM_CTRL_H_
#define _WEBCAM_CTRL_H_
#include <linux/v4l2-controls.h>
int get_control(int fd, int id, int *val);
int set_control(int fd, int id, int val);
#endif /* _WEBCAM_CTRL_H_ */
webcam_ctrl.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/v4l2-controls.h>
#include <linux/videodev2.h>
#include <stdlib.h>
#include "webcam_ctrl.h"
int set_control(int fd, int cid, int val)
{
struct v4l2_control v4l2_ctrl = {
.id = cid,
.value = val
};
if(ioctl(fd, VIDIOC_S_CTRL, &v4l2_ctrl)) {
return -1;
}
return 0;
}
int get_control(int fd, int cid, int* val)
{
struct v4l2_control v4l2_ctrl = { .id = cid };
if(ioctl(fd, VIDIOC_G_CTRL, &v4l2_ctrl)) {
return -1;
}
*val = v4l2_ctrl.value;
return 0;
}
#ifndef CGO
int main(int argc, char** argv)
{
char *fname;
int fd;
int cid;
int ret;
int val;
int val0;
if (argc < 2) {
fprintf(stderr, "usage: %s val\n", argv[0]);
return 1;
}
val = atoi(argv[1]);
fname = "/dev/video0";
fd = open(fname, O_RDWR);
if (fd < 0) {
fprintf(stderr, "failed to open: %s\n", fname);
}
ret = get_control(fd, V4L2_CID_BRIGHTNESS, &val0);
if (ret != 0) {
fprintf(stderr, "failed to get_control: ret=%d\n", ret);
}
printf("Brightness=%d\n", val0);
ret = set_control(fd, V4L2_CID_BRIGHTNESS, val0 + val);
return ret;
}
#endif
zig cc
でビルドできる。
$ zig cc webcam_ctrl.c
$ file a.out
a.out: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, for GNU/Linux 2.0.0, stripped
CのソースコードをZigに変換する
test用のmainは除いてzigに変換する。
$ zig translate-c -DCGO \
-I /usr/local/zig-linux-aarch64-0.9.1/lib/libc/include/aarch64-linux-gnu \
-I /usr/local/zig-linux-aarch64-0.9.1/lib/libc/include/generic-glibc \
-I /usr/local/zig-linux-aarch64-0.9.1/lib/libc/include/any-linux-any \
webcam_ctrl.c > webcam_ctrl.zig
$ wc webcam_ctrl.zig
5303 31035 300798 webcam_ctrl.zig
できた。5000行ある。
共有ライブラリ作ってみた。
$ zig build-lib -dynamic -lc webcam_ctrl.zig
$ ldd libwebcam_ctrl.so
linux-vdso.so.1 (0x0000ffffa2f28000)
libpthread.so.0 => /lib/aarch64-linux-gnu/libpthread.so.0 (0x0000ffffa2e46000)
libc.so.6 => /lib/aarch64-linux-gnu/libc.so.6 (0x0000ffffa2cd3000)
/lib/ld-linux-aarch64.so.1 (0x0000ffffa2ef8000)
Zigでライブラリビルド環境を整える
$ zig init-lib
info: Created build.zig
info: Created src/main.zig
info: Next, try `zig build --help` or `zig build test`
$ mv webcam_ctrl.zig src/
build.zigを編集。
build.zig
const std = @import("std");
pub fn build(b: *std.build.Builder) void {
// Standard release options allow the person running `zig build` to select
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
const mode = b.standardReleaseOptions();
const lib = b.addSharedLibrary("webcam_ctrl", "src/webcam_ctrl.zig", b.version(0,1,0));
lib.linkLibC();
lib.setBuildMode(mode);
lib.install();
const exe = b.addExecutable("test", null);
exe.addCSourceFile("test.c", &[_][]const u8{});
exe.linkLibrary(lib);
exe.linkSystemLibrary("c");
b.default_step.dependOn(&exe.step);
const run_cmd = exe.run();
const test_step = b.step("test", "Test the program");
test_step.dependOn(&run_cmd.step);
}
ライブラリをCでテストするコード。
test.c
#include <stdio.h>
#include <fcntl.h>
#include <linux/v4l2-controls.h>
#include "webcam_ctrl.h"
int main(int argc, char** argv)
{
char *fname;
int fd;
int ret;
int val;
int val_org;
fname = "/dev/video0";
fd = open(fname, O_RDWR);
if (fd < 0) {
fprintf(stderr, "failed to open: %s\n", fname);
}
ret = get_control(fd, V4L2_CID_BRIGHTNESS, &val);
if (ret != 0) {
fprintf(stderr, "failed to get_control: ret=%d\n", ret);
}
printf("Brightness=%d\n", val);
val_org = val;
ret = set_control(fd, V4L2_CID_BRIGHTNESS, val + 10);
printf("set Brightness=%d\n", val + 10);
ret = get_control(fd, V4L2_CID_BRIGHTNESS, &val);
if (ret != 0) {
fprintf(stderr, "failed to get_control: ret=%d\n", ret);
}
printf("Brightness=%d\n", val);
ret = set_control(fd, V4L2_CID_BRIGHTNESS, val_org);
printf("set Brightness=%d\n", val_org);
ret = get_control(fd, V4L2_CID_BRIGHTNESS, &val);
if (ret != 0) {
fprintf(stderr, "failed to get_control: ret=%d\n", ret);
}
printf("Brightness=%d\n", val);
return ret;
}
$ zig build
$ ls -l zig-out/lib/libwebcam_ctrl.so*
lrwxrwxrwx 1 koba koba 19 7月 20 17:57 zig-out/lib/libwebcam_ctrl.so -> libwebcam_ctrl.so.0
lrwxrwxrwx 1 koba koba 23 7月 20 17:57 zig-out/lib/libwebcam_ctrl.so.0 -> libwebcam_ctrl.so.0.1.0
-rwxrwxr-x 1 koba koba 566864 7月 20 17:20 zig-out/lib/libwebcam_ctrl.so.0.1.0
ライブラリをビルドできた。
$ zig build test
Brightness=20
set Brightness=30
Brightness=30
set Brightness=20
Brightness=20
想定通りの結果。
自動生成したzigのソースコードを手で修正
自動生成されたものは5000行ちかくあったが、@cInclude
をすることで大幅に削減できた。
さらにいくつか修正して、すっきりさせたものがこちら。
webcam_ctrl.zig
const c = @cImport({
@cInclude("sys/ioctl.h");
@cInclude("linux/v4l2-controls.h");
@cInclude("linux/videodev2.h");
});
pub export fn get_control(fd: c_int, cid: c_int, val: [*c]c_int) c_int {
var v4l2_ctrl: c.struct_v4l2_control = .{
.id = @bitCast(c.__u32, cid),
.value = 0,
};
if (c.ioctl(fd, c.VIDIOC_G_CTRL, &v4l2_ctrl) != 0) {
return -@as(c_int, 1);
}
val.* = v4l2_ctrl.value;
return 0;
}
pub export fn set_control(fd: c_int, cid: c_int, val: c_int) c_int {
var v4l2_ctrl: c.struct_v4l2_control = .{
.id = @bitCast(c.__u32, cid),
.value = val,
};
if (c.ioctl(fd, c.VIDIOC_S_CTRL, &v4l2_ctrl) != 0) {
return -@as(c_int, 1);
}
return 0;
}
C言語で書かれた共有ライブラリをそっくりそのままZig言語に置き換えることができた。
Discussion