-
-
Notifications
You must be signed in to change notification settings - Fork 197
Description
Zen C now has RAII via the Drop trait as well as generics, and these two features have engendered a problem that I think is ideally solved via comptime reflection.
The problem arises when you have a generic container type which can hold types that may or may not implement the Drop trait. As of the current version of Zen C, I don't think there's a way for Zen C code to be able to check if a particular type implements a particular trait (neither at comptime nor runtime), and as such I don't think it's possible to properly define the Drop trait for a generic container type if you want the container to be able to drop its contained elements.
Here is a Zen C program that illustrates this:
include <stdlib.h>
struct Droppable {
n: int;
}
impl Drop for Droppable {
fn drop(self) {
printf("dropped Droppable: %d\n", self.n);
}
}
struct Array<T> {
arr: T*
}
def ARR_SIZE = 3;
impl Array<T> {
fn new() -> Array<T> {
return Array<T> { arr: malloc(sizeof(T) * ARR_SIZE) };
}
}
impl Drop for Array<T> {
fn drop(self) {
// Ideally this loop should be conditionally included at compile time
// only if T implements Drop
for i in 0..ARR_SIZE {
self.arr[i].drop();
}
free(self.arr);
printf("dropped Array\n");
}
}
fn main() {
// Everything is properly freed because Droppable implements Drop
let a1 = Array<Droppable>::new();
a1.arr[0] = Droppable { n: 1 };
a1.arr[1] = Droppable { n: 2 };
a1.arr[2] = Droppable { n: 3 };
for i in 0..ARR_SIZE {
printf("check out this array element: %d\n", a1.arr[i].n);
}
// Won't compile because int doesn't implement Drop
let a2 = Array<int>::new();
a2.arr[0] = 1;
a2.arr[1] = 2;
a2.arr[2] = 3;
for i in 0..ARR_SIZE {
printf("check out this array element: %d\n", a2.arr[i]);
}
}When you have an array of a droppable type, the array's drop method should also call the drop method for each of its elements before freeing the memory allocated for the array. If, however, the contained type does not implement Drop, then the array's drop method only needs to free its own memory. Ideally, the determination to drop or not drop the array elements happens at compile time because a) that info is technically available at compile time, and b) that would eliminate unnecessary runtime overhead.
I'm not sure if you've given any thought to comptime reflection yet, but from an api perspective it could look something like this:
impl Drop for Array<T> {
fn drop(self) {
comptime if T.implements(Drop) {
// The code in this block is only included in the resulting C file if T implements Drop
for i in 0..ARR_SIZE {
self.arr[i].drop();
}
}
free(self.arr);
printf("dropped Array\n");
}
}Thoughts on this?
Metadata
Metadata
Assignees
Labels
Type
Projects
Status