Last active
December 15, 2021 21:09
-
-
Save andrewrk/190170bc1441839644c3f15725a22096 to your computer and use it in GitHub Desktop.
Haiku dev talking about RAII in C++ in relation to Zig
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[14:41:12] <waddlesplash> Zig seems cool but you don't seem to be about the RAII that we love so much :-p | |
[14:47:25] <andrewrk> anyway I won't come in here trying to convince anyone to switch languages or coding styles :) | |
[14:47:44] <waddlesplash> in Haiku ... C++ virtual inheritance and RAII objects mean that the code can much more clearly communicate what it is trying to do at the same time it is actually doing it | |
[14:48:02] <andrewrk> I do think that there is a misunderstanding that we don't care about RAII principles in zig though. I mean yeah you have to manually `defer cleanup();` but the coding model is the same | |
[14:48:15] <waddlesplash> but what happens if you decide you don't want to clean something up? | |
[14:48:21] <waddlesplash> for instance we have a very common paradigm in Haiku: | |
[14:49:35] <waddlesplash> thing* whatever = allocate_memory_or_something(); | |
[14:49:35] <waddlesplash> MemoryDeleter<thing> whateverDeleter(whatever); | |
[14:49:36] <waddlesplash> if (error) return error; /* whatever is deleted */ | |
[14:49:36] <waddlesplash> whateverDeleter.Detach(); return ENOERR; /* success, we want to keep whatever */ | |
[14:50:10] <waddlesplash> you can't undo a "defer cleanup", but you can tell a RAII object that you actually want to keep the thing | |
[14:50:48] <waddlesplash> and yes, moving unique_ptrs would handle this case ... but only in code we manage. we can use this paradigm even when e.g. implementing POSIX functions that must return raw pointers | |
[14:51:31] <waddlesplash> similarly, in core kernel code, we may need to lock and unlock some lock a lot of times, so we don't want to just "defer unlock", we want a MutexLocker object that will keep track of whether the thing is locked or unlocked, and act appropriately on "return error" | |
[14:54:34] <andrewrk> waddlesplash, the pattern looks pretty identical in zig: https://clbin.com/Bv3zx | |
[14:55:17] <waddlesplash> ah, so you can defer object calls, I guess that is indeed equivalent then | |
[14:55:36] <andrewrk> yeah it's an extremely useful pattern, which I'm sure I don't need to convince you of | |
[14:55:59] <waddlesplash> zig doesn't have "templates" though, in the C++ sense, does it? | |
[14:56:20] <andrewrk> it's effectively the same thing | |
[14:56:33] <andrewrk> in zig it's parameters that are required to be compile-time known | |
[14:56:52] <andrewrk> and then any if/switch expressions whose conditions are compile-time known are flattened out, dead branches are not even analyzed | |
[14:56:54] <waddlesplash> well, last week for example I wrote some code for our ntfs driver that uses libntfs-3g, which is C | |
[14:57:05] <waddlesplash> and I had to open and close a lot of I nodes, so I wrote... | |
[14:57:06] <waddlesplash> typedef CObjectDeleter<ntfs_inode, int, ntfs_inode_close> NtfsInodeCloser; | |
[14:57:06] <nephele> comptime seems like a major advantage zig has, though :) | |
[14:57:15] <andrewrk> if you show me a simple example code I can show equivalent zig if you're curious | |
[14:58:05] <waddlesplash> andrewrk: well, the above template for instance allows me to specify an arbitrary function as a template argument, as well as what type it accepts and what return type it has | |
[14:58:22] <waddlesplash> and now I've got a new "deleter" type that I can liberally sprinkle through the code | |
[14:58:53] <andrewrk> yeah that is directly translateable to zig, although you might make some people squint their eyes and accuse you of unnecessary abstraction | |
[14:59:32] <waddlesplash> well, we have quite a lot of these typedefs, because we can and do create them when interacting with random C libraries :) | |
[15:03:44] <andrewrk> I'm not sure what the types or usage of CObjectDeleter is, but if I guessed correctly it would look something like this: https://clbin.com/EeKTz | |
[15:04:07] <waddlesplash> yep, that's about how it works | |
[15:04:11] <waddlesplash> some more nuance, but not a lot | |
[15:04:45] <waddlesplash> well, if zig had virtual inheritance or an equivalent thereof, maybe it'd be something we'd consider :-p | |
[15:05:03] <waddlesplash> or maybe if you add support for language extensions or something, someone will invent "Object Zig" :D | |
[15:05:03] <andrewrk> hahaha well I'm only here to ask for help reviewing a haiku PR, not to try to convince anyone to use zig :) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const Thing = struct { | |
detached: bool = false, | |
fn deinit(self: *Thing) void { | |
if (!self.detached) { | |
doTheCleanup(); | |
} | |
} | |
fn consume(self: *Thing) void { | |
self.detached = true; | |
} | |
}; | |
test "usage code" { | |
var thing = functionThatReturnsAThing(); | |
defer thing.deinit(); | |
// ... | |
thing.consume(); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
fn CObjectDeleter(comptime initFn: anytype, comptime T: type, comptime cleanupFn: anytype) type { | |
return struct { | |
state: T, | |
const Self = @This(); | |
fn init(state: T) !Self { | |
return Self{ | |
.state = try initFn(), | |
}; | |
} | |
fn deinit(self: *Self) void { | |
cleanupFn(self.state); | |
} | |
}; | |
} | |
test "example usage" { | |
var c_object_deleter = try CObjectDeleter(ntfs_inode, int, ntfs_inode_close).init(1234); | |
defer c_object_deleter.deinit(); | |
// ... | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment