冬休みの自由研究に AppKitをC言語
でフルスクラッチ記述している jimon/osx_app_in_plain_c をzig言語で写経してたのですが、ハマりどころをメモとして残しておきます。
事象
Objective-C
のメッセージ送信は最終的にobjc/message.h
で宣言されているobjc_msgSend
に行き着くが、このプロトタイプ宣言が
void objc_msgSend(void);
であり、使用用途に応じて関数ポインタを型キャストして使用してねというスタンスの模様。
例えば、NSWindowのインスタンス化なら、
id objc_msgSend(id, SEL, NSRect, NSUInteger, NSUInteger, BOOL) {
}
となる。
これならzigのcomptime活かせそうだと思い、comptimeな展開結果を意識して
fn new_window_msgSend(self: id, selector: SEL, arg1: NSRect, arg2: NSUInteger, arg3: NSUInteger, arg4: BOOL) id {
const FuncType = fn (@TypeOf(self), @TypeOf(selector), @TypeOf(arg1), @TypeOf(arg2), @TypeOf(arg3), @TypeOf(arg4)) callconv(.C) id;
var func = @ptrCast(FuncType, objc_msgSend);
return @call(.{}, func .{ id, selector, rect, arg1, arg2, arg3, arg4 });
}
としたところ、なぜかウインドウが表示されない・・・。
また
2023-01-04 22:00:28.375 osx_app_in_plain_ziglang[52798:1855657] NSWindow does not support nonactivating panel styleMask 0x80
2023-01-04 22:00:28.375 osx_app_in_plain_ziglang[52798:1855657] NSWindow does not support utility styleMask 0x10
なログが実行時に表示される始末。
原因
zig言語
では最適化の兼ね合いでstructを引数に渡す場合、参照渡しにするというのがあり。
ここにモロに引っかかった模様
対処
comptimeの道を諦め、C言語
で
id new_window_msgSend(id alloc, SEL selector, NSRect rect, NSUInteger styleMask, NSUInteger backing, BOOL defer) {
return ((id (*)(id, SEL, NSRect, NSUInteger, NSUInteger, BOOL))objc_msgSend)(alloc, selector, rect, styleMask, backing, defer);
}
を用意しておいた上で、
zig言語
からがextern関数
で参照するという泥臭い方法をとった。
extern "C" fn new_window_msgSend(self: id, selector: SEL, arg1: NSRect, arg2: NSUInteger, arg3: NSUInteger, arg4: BOOL) id;
extern関数
であれば、C ABI
が採用されるためうまくいった模様。
レポジトリ
写経結果は、
に残しておいた。
コメントほとんど書いていないので読めた代物ではないが、zig言語
でのBinding
の足がかり程度に見て貰えば幸いである。
あと、Application Bundle
のために作ったbuild taskは有用かも。
2023/01/18 追記
以下のissueですでに報告され、修正がmaster
ブランチにはすでに取り込まれている模様。
現時点のstableである0.10.0
にはまだ反映されていないが、2023/01/18付で公開されたmaster
のプリビルド1で反映されていることを確認した。
-
もしかしてNightly Buildで毎日公開されてる? ↩