From 4273f9657356194f5f4340de97fd2c617127462a Mon Sep 17 00:00:00 2001 From: Philippe Pittoli Date: Tue, 21 Oct 2025 00:30:56 +0200 Subject: [PATCH] Add pong example with c bindings. --- build.zig | 14 ++-- libipc.h | 1 + src/examples/c_pong.zig | 23 ------- src/examples/pong-with-c-bindings.zig | 96 +++++++++++++++++++++++++++ 4 files changed, 104 insertions(+), 30 deletions(-) delete mode 100644 src/examples/c_pong.zig create mode 100644 src/examples/pong-with-c-bindings.zig diff --git a/build.zig b/build.zig index 861e19d..170536a 100644 --- a/build.zig +++ b/build.zig @@ -71,13 +71,13 @@ pub fn build(b: *std.Build) void { b.installArtifact(lib_dynamic_c); // pong service using the c client. - const c_pong = b.addExecutable(.{ - .name = "ping", // name of the executable + const pong_with_c_bindings = b.addExecutable(.{ + .name = "pong-with-c-bindings", // name of the executable .root_module = b.createModule(.{ // b.createModule defines a new module just like b.addModule but, // unlike b.addModule, it does not expose the module to consumers of // this package, which is why in this case we don't have to give it a name. - .root_source_file = b.path("src/examples/c_pong.zig"), + .root_source_file = b.path("src/examples/pong-with-c-bindings.zig"), .target = target, .optimize = optimize, // List of modules available for import in source files part of the @@ -97,10 +97,10 @@ pub fn build(b: *std.Build) void { // install prefix when running `zig build` (i.e. when executing the default // step). By default the install prefix is `zig-out/` but can be overridden // by passing `--prefix` or `-p`. - b.installArtifact(c_pong); + b.installArtifact(pong_with_c_bindings); // Link the executable to the library. - c_pong.linkLibrary(lib_static_c); + pong_with_c_bindings.linkLibrary(lib_static_c); // This creates a top level step. Top level steps have a name and can be // invoked by name when running `zig build` (e.g. `zig build run`). @@ -115,7 +115,7 @@ pub fn build(b: *std.Build) void { // or if another step depends on it, so it's up to you to define when and // how this Run step will be executed. In our case we want to run it when // the user runs `zig build run`, so we create a dependency link. - const run_cmd = b.addRunArtifact(c_pong); + const run_cmd = b.addRunArtifact(pong_with_c_bindings); run_step.dependOn(&run_cmd.step); // By making the run step depend on the default step, it will be run from the @@ -142,7 +142,7 @@ pub fn build(b: *std.Build) void { // root module. Note that test executables only test one module at a time, // hence why we have to create two separate ones. const exe_tests = b.addTest(.{ - .root_module = c_pong.root_module, + .root_module = pong_with_c_bindings.root_module, }); // A run step that will run the second test executable. diff --git a/libipc.h b/libipc.h index ed639d7..e375d13 100644 --- a/libipc.h +++ b/libipc.h @@ -1,6 +1,7 @@ #ifndef LIBIPC #define LIBIPC +#include // Required to have types such as `size_t`. #include /** diff --git a/src/examples/c_pong.zig b/src/examples/c_pong.zig deleted file mode 100644 index 54a4d33..0000000 --- a/src/examples/c_pong.zig +++ /dev/null @@ -1,23 +0,0 @@ -// Example of a `pong` client using the C bindings. -const std = @import("std"); - -// Only bindings are available. -const libipc = @cImport({ - @cInclude("stdio.h"); // Required to have types such as `size_t`. - @cInclude("../../../libipc.h"); -}); - -const service_name = "pong"; - -pub fn main() !void { - // Prints to stderr, ignoring potential errors. - std.debug.print("{s} service.\n", .{service_name}); - var ctx : *anyopaque = undefined; - var ret : c_int = 0; - ret = libipc.ipc_context_init (@ptrCast(&ctx)); - - if (ret != 0) { - std.debug.print("oops\n", .{}); - return; - } -} diff --git a/src/examples/pong-with-c-bindings.zig b/src/examples/pong-with-c-bindings.zig new file mode 100644 index 0000000..2eb5036 --- /dev/null +++ b/src/examples/pong-with-c-bindings.zig @@ -0,0 +1,96 @@ +// Example of a `pong` client using the C bindings. +const std = @import("std"); + +// Only bindings are available. +const libipc = @cImport({ + @cInclude("../../../libipc.h"); +}); + +const SERVICE = "pong"; +const SERVICE_LEN = 4; +const MAX_MSG_SIZE = 10000; + +pub fn main() !u8 { + var ctx : *anyopaque = undefined; + var ret : c_int = 0; + var servicefd : c_int = undefined; + var buffer : [MAX_MSG_SIZE]u8 = undefined; + var size : usize = MAX_MSG_SIZE; + var event_type : u8 = undefined; + var index : usize = 0; + var originfd : c_int = undefined; + + std.debug.print("Init context.\n", .{}); + ret = libipc.ipc_context_init (@ptrCast(&ctx)); + + if (ret != 0) { + std.debug.print("Cannot init context.\n", .{}); + return 1; + } + + defer { + std.debug.print("Deinit context.\n", .{}); + libipc.ipc_context_deinit (@ptrCast(&ctx)); + } + + std.debug.print ("Connect to a 'pong' service.\n", .{}); + ret = libipc.ipc_connect_service (ctx, &servicefd, SERVICE, SERVICE_LEN); + + if (ret != 0) { + std.debug.print("Cannot connect to a service.\n", .{}); + return 1; + } + + std.debug.print ("Let's schedule a message.\n", .{}); + ret = libipc.ipc_schedule (ctx, servicefd, "hello, plz bounce me", 21); + + if (ret != 0) { + std.debug.print("Cannot schedule a message.\n", .{}); + return 1; + } + + std.debug.print("Let's set the timer to one second.\n", .{}); + libipc.ipc_context_timer (ctx, 1000); + + var should_continue : bool = true; + var event_count : u32 = 0; + + std.debug.print("Let's loop over events.\n", .{}); + while(should_continue) { + size = MAX_MSG_SIZE; + var newfd : c_int = undefined; + const pbuffer : [*c]u8 = &buffer; + ret = libipc.ipc_wait_event (ctx, &event_type, &index, &originfd, &newfd, pbuffer, &size); + if (ret != 0) { + std.debug.print("Error while waiting for an event.", .{}); + return 1; + } + + event_count += 1; + std.debug.print("EVENT n°{d} type {d}\t", .{ event_count, event_type }); + + switch (event_type) { + libipc.ERROR => { std.debug.print("Error.", .{}); return 1; }, + libipc.EXTERNAL => { std.debug.print("External (shouldn't happen).", .{}); return 1; }, + libipc.SWITCH_RX => { std.debug.print("Switch RX (shouldn't happen).", .{}); return 1; }, + libipc.SWITCH_TX => { std.debug.print("Switch TX (shouldn't happen).", .{}); return 1; }, + libipc.CONNECTION => { std.debug.print("Connection (shouldn't happen).", .{}); return 1; }, + libipc.DISCONNECTION => { std.debug.print("Disconnection (shouldn't happen).", .{}); return 1; }, + libipc.TIMER => { std.debug.print("TIMER.\n", .{}); }, + libipc.MESSAGE_TX => { std.debug.print("A message has been sent.\n", .{}); }, + libipc.MESSAGE_RX => { + if (size == 0) { + std.debug.print("No message returned.\n", .{}); + return 1; + } + buffer[size] = 0; + std.debug.print("Response (size {d}): {s}.\n", .{ size, buffer }); + // We received the response, quitting. + should_continue = false; + }, + else => { std.debug.print("Unknown IPC message type.\n", .{}); return 1; }, + } + } + + return 0; +}