Several types in one list
This is where Paternitas helps most.
One std list carries a Message and a Job.
Each comes back as itself.
The types
Each struct has a TypedNode, and its helper right after it.
const Message: type = struct {
text: []const u8,
tnode: paternitas.DoublyTypedNode = .{},
};
const TypedMessage: type = paternitas.Typed(Message);
const Job: type = struct {
tnode: paternitas.DoublyTypedNode = .{},
id: u32,
attempts: u32 = 0,
};
const TypedJob: type = paternitas.Typed(Job);
The program
Call setTypeId on each struct. Put both in one list. Pop them.
var message: Message = .{ .text = "hello" };
TypedMessage.setTypeId(&message);
var job: Job = .{ .id = 42 };
TypedJob.setTypeId(&job);
var list: std.DoublyLinkedList = .{};
list.append(TypedMessage.node(&message));
list.append(TypedJob.node(&job));
const last: *std.DoublyLinkedList.Node = list.last orelse return error.ListEmpty;
if (!TypedJob.is(last)) return error.WrongParent;
var counts: Counts = .{};
while (list.popFirst()) |node| try recoverAndCount(node, &counts);
It logs one line per struct:
The full program: Two types, one list.
Ask each type in turn
One function handles one Node.
/// Asks each Parent type in turn. A Node that no type claims is an error.
fn recoverAndCount(node: *std.DoublyLinkedList.Node, counts: *Counts) !void {
if (TypedMessage.parentFromNode(node)) |m| {
std.log.info("message: {s}", .{m.*.text});
counts.*.messages += 1;
return;
}
if (TypedJob.parentFromNode(node)) |j| {
std.log.info("job: {d}", .{j.*.id});
counts.*.jobs += 1;
return;
}
return error.UnknownParent;
}
- The wrong type gets null, not a wrong pointer.
- So you can ask
Messagefirst, thenJob. - A Node that no type claims is an error you can see:
error.UnknownParent.
When you only need to know
is(node) answers yes or no.
It does not give you the struct.
const last: *std.DoublyLinkedList.Node = list.last orelse return error.ListEmpty;
if (!TypedJob.is(last)) return error.WrongParent;
is gives false when setTypeId was never called on that struct.
Who knows the types
The loop knows the types it handles.
The list does not.
- The list is still the plain std list.
- Its calls do not change.
- Add a third struct type: the list code stays the same.
In a large system, that matters.
Timeout list and a queue does the same for a server:
- A server keeps its connections in a timeout list.
- One connection leaves the list, goes through a queue, and comes back.