High-level Ada bindings for CZMQ (ZeroMQ high-level C binding).
Note
I'm new to Ada and want to learn it in depth by applying it to practical DevOps tasks. Since I couldn't find too many libraries for this space that are still maintained, I built this library. Community input (e.g. issues, pull requests) that helps my learning is always appreciated.
Warning
Until this library has reached a mature version 1.0, expect there to be breaking changes even with only minor version bumps.
- Memory Safe: Automatic resource cleanup using Ada controlled types
- Type Safe: Strong typing with Ada's type system
- Modern Ada: Uses Ada 2012 features (extended return statements)
- Clean API: High-level Ada interface hiding C complexity
czmq_ada/
├── src/
│ ├── czmq.ads # Root package
│ ├── czmq-low_level.ads # Thin C bindings
│ ├── czmq-sockets.ads/.adb # High-level socket API
│ └── czmq-messages.ads/.adb # High-level message API
├── examples/
│ ├── push_pull.adb # PUSH-PULL pattern (recommended)
│ ├── publisher.adb # PUB-SUB publisher
│ └── subscriber.adb # PUB-SUB subscriber
├── czmq_ada.gpr # Main library project
└── examples/examples.gpr # Examples project
- Alire package manager (provides the GNAT toolchain)
- CZMQ library and development headers
Fedora/RHEL:
sudo dnf install czmq-devel zeromq-develUbuntu/Debian:
sudo apt install libczmq-devalr buildcd tests
alr build
bin/test_sockets
bin/test_certificates
bin/test_sockets_curve
bin/test_authenticationcd examples
alr buildtype Socket_Type is (
Pair, Pub, Sub, Req, Rep,
Dealer, Router, Pull, Push,
XPub, XSub, Stream
);-- Create socket without endpoint
Socket : Socket := New_Pub ("");
-- Or with endpoint (@ for bind, > for connect)
Socket : Socket := New_Pub ("@tcp://127.0.0.1:5555");Bind (Socket, "tcp://*:5555");
Connect (Socket, "tcp://127.0.0.1:5555");
Unbind (Socket, "tcp://*:5555");
Disconnect (Socket, "tcp://127.0.0.1:5555");-- Create and send
Msg : Message := New_Message;
Add_String (Msg, "Hello");
Add_String (Msg, "World");
Send (Msg, Socket); -- Consumes the message
-- Receive and read
Msg : Message := Receive (Socket);
Str : String := Pop_String (Msg);
Count : Natural := Size (Msg);examples/bin/push_pullTerminal 1 - Publisher:
examples/bin/publisherTerminal 2 - Subscriber:
examples/bin/subscriberNote
The publisher waits 2 seconds before sending messages. This is necessary to avoid the "slow joiner syndrome" - a timing issue where initial messages may be lost during subscription establishment. See Chapter 5 of the ZeroMQ Guide for details.
- High-level wrappers for actors, pollers, and frames are not yet implemented
- PUB-SUB requires careful timing or synchronization
When creating a SUB socket with an empty subscription filter (subscribe to all messages), it's critical to pass an actual empty C string "" rather than NULL:
-- Correct: Always create C string for subscription
C_Subscribe := CS.New_String (Subscribe); -- "" becomes empty C string
-- Wrong: Would pass NULL to C
if Subscribe /= "" then
C_Subscribe := CS.New_String (Subscribe);
end if;In C, an empty string "" and NULL are different. CZMQ interprets:
- Empty string
""= subscribe to all messages NULL= no subscription set (no messages received)
All socket and message types use Ada's controlled types (Limited_Controlled) for automatic cleanup:
declare
Socket : Socket := New_Pub ("@tcp://*:5555");
Msg : Message := New_Message;
begin
-- Use socket and message
-- Automatic cleanup when leaving scope
end;Messages are consumed by Send - the handle is set to null after sending to prevent double-free.
This project, like the underlying CZMQ library, is licensed under the Mozilla Public License 2.0 (MPL-2.0). See the LICENSE file for details.