An Open Source Implementation of the Actor Model in C++
observable
that runs on an actor can now be converted to a stream
or
typed_stream
directly by calling to_stream
or to_typed_stream
on it.caf::async
API to read text and binary files asynchronously in a
separate thread. The contents of the files are consumed as flows (#1573).caf::unordered_flat_map
now has the contains
and
insert_or_assign
member functions.FOO_BAR
sets the option foo.bar
. Users may also override the default
name by putting the environment name after the short names, separated by a
comma. For example, opt_group{custom_options_, "foo"}.add("bar,b,MY_BAR")
overrides the default environment variable name FOO_BAR
with MY_BAR
.caf/chrono.hpp
now support precision for the
fractional part of the seconds and an option to print with a fixed number of
digits.caf::chunk
represents an immutable sequence of bytes with a
fixed size. Unlike std::span
, a chunk
owns its data and can be (cheaply)
copied and moved.make_behavior
member function
into a "function-based actor" via the new actor_from_state
utility. For
example, sys.spawn(caf::actor_from_state<my_state>, args...)
creates a new
actor that initializes its state with my_state{args...}
and then calls
make_behavior()
on the state object to obtain the initial behavior.aout
utility received a println
member function that adds a formatted
line to the output stream. The function uses the same formatting backend as
the logging API.caf::test::outline
is now properly exported from the test module.
This fixes builds with dynamic linking against libcaf_test
.request(...).await(...)
, the actor no longer suspends handling of
system messages while waiting for the response (#1584).spawn_inactive
and not launching the actor explicitly
(#1597).const
to publisher<T>::observe_on
.observable
implementations now properly call on_subscribe
on their
subscriber before calling on_error
.config_value::parse
now properly handles leading and trailing
whitespaces.caf::unordered_flat_map
previously relied on the order of
elements in the map and thus could result in false negatives. The new
implementation is correct and no longer relies on the order of elements.--dump-config
, CAF now properly renders nested dictionaries.
Previously, dictionaries in lists missed surrounding braces.foo.bar = 42
in a config file as foo { bar = 42 }
, just as
it does for CLI arguments.caf_test
with shared libraries
enabled (#1669).delay_for_fn
on a flow coordinator now returns a disposable
in
order to be consistent with delay_for
and delay_until
.dispose
on a server (e.g. an HTTP server) now properly closes all
open connections.expected
when calling transform
on an rvalue with a
function object that only accepts an rvalue.caf::net::make_pipe
no longer closes read/write channels of the
connected socket pair on Windows. This fixes a bug where the pipe would close
after two minutes of inactivity.to_string
on any of CAF's enum types now represents the enum value
using the short name instead of the fully qualified name. For example,
to_string(sec::none)
now returns "none"
instead of "caf::sec::none"
.
Accordingly, from_string
now accepts the short name (in additional to the
fully qualified name).%C
. CAF still recognizes this option
but it will always print null
.caf::telemetry::counter::inc
now allows passing 0 as an
argument. Previously, passing 0 triggered an assertion when building CAF with
runtime checks enabled.dispose
on a flow subscription now calls on_error(sec::disposed)
on the observer. Previously, CAF would simply call on_complete()
on the
observer, making it impossible to distinguish between a normal completion and
disposal.caf::logger
received a complete overhaul and became an interface class.
By turning the class into an interface, users can now install custom logger
implementations. CAF uses the previous implementation as the default logger if
no custom logger is configured. To install a logger, users can call
cfg.logger_factory(my_logger_factory)
on the actor_system_config
before
constructing the actor_system
. The logger factory is a function object with
signature caf::intrusive_ptr<caf::logger>(caf::actor_system&)
. Furthermore,
log messages are now formatted using std::format
when compiling CAF with
C++20 or later. Otherwise, CAF will fall back to a minimal formatting
implementation with compatible syntax. The logging API will also automatically
convert any type with a suitable inspect
overload to a string if the type is
not recognized by format
.to_stream
or to_typed_stream
on an actor is now deprecated. Simply
call to_stream
or to_typed_stream
directly on the observable
instead.operator<
for caf::unordered_flat_map
was broken and
relied on the order of elements in the map. We have removed it, since it has
never worked correctly and a correct implementation would be too expensive.caf::telemetry::label
now has a new compare
overload that
accepts a caf::telemetry::label_view
to make the interface of the both
classes symmetrical.caf::dictionary
now has new member functions for erasing
elements.--foo bar
. This is in addition to the existing
--foo=bar
syntax.make_message
and make_error
now support std::string_view
as input and automatically convert it to std::string
.caf::async::publisher
. Any observable can be transformed into a publisher by
calling to_publisher
. The publisher can then be used to subscribe to the
observable from other actors or threads. The publisher has only a single
member function: observe_on
. It converts the publisher back into an
observable. This new abstraction allows users to set up asynchronous flows
without having to manually deal with SPSC buffers.first
, last
, take_last
,
skip_last
, element_at
, and ignore_elements
.--help
now includes all
user-defined options. Previously, only options in the global category or
options with a short name were included. Only CAF options are now excluded
from the output. They will still be included in the output of --long-help
.--dump-config
now only contains CAF options from loaded
modules. Previously, it also included options from modules that were not
loaded.caf::flow::item_publisher
to caf::flow::multicaster
to better
reflect its purpose and to avoid confusion with the new
caf::async::publisher
.sec::malformed_message
.delta
byte span passed to consume
now resets whenever returning a
positive value from consume
.behavior
or message_handler
, callbacks that take a
message
argument are now treated as catch-all handlers.abort
instead of crashing the
application.--dump-config
that caused CAF applications to emit
malformed output.take(0)
on an observable now properly creates an observable that calls
on_complete
on its subscriber on the first activity of the source
observable. Previously, the created observable would never reach its threshold
and attempt to buffer all values indefinitely.intrusive_ptr
no longer accidentally creates new
intrusive_ptr
instances when comparing to raw pointers.caf::net::http::request_header
are now case-insensitive, as
required by the HTTP specification. Further, field_at
is now a const
member function (#1554).${CMAKE_INSTALL_BINDIR}
to make packaging easier.SSL_CTX_set_cipher_list
in
order to use the system settings by default. Users can provide a custom cipher
list by providing a value for the configuration option
caf.openssl.cipher-list
. To restore the previous behavior, set this
parameter to HIGH:!aNULL:!MD5
when running with a certificate and
AECDH-AES256-SHA@SECLEVEL=0
otherwise (or without @SECLEVEL=0
for older
versions of OpenSSL). Please note that these lists are not recommended as
safe defaults, which is why we are no longer setting these values.CAF_MAIN
macro. This fixes the WSANOTINITIALISED
error on Windows (#1409).caf::chrono
for making it easier to handle ISO 8601
timestamps. The new function std::chrono::to_string
converts system time to
an ISO timestamp. For reading an ISO timestamp, CAF now provides the class
caf::chrono::datetime
. It can parse ISO-formatted strings via parse
(or
datetime::from_string
) and convert them to a local representation via
to_local_time
. Please refer to the class documentation for more details.json_value
can now hold unsigned 64-bit integer values. This
allows it to store values that would otherwise overflow a signed integer.
Values that can be represented in both integer types will return true
for
is_integer()
as well as for the new is_unsigned()
function. Users can
obtain the stored value as uint64_t
via to_unsigned()
.json_value
, there is now a new
edge case where is_number()
returns true
but neither is_integer()
nor
is_double()
return true
: integer values larger than INT64_MAX
will only
return true for is_unsigned()
.web_socket::with
. This bug caused
servers to immediately abort incoming connection (#1402).json_value
, json_array
and json_object
allow working
with JSON inputs directly. Actors can also pass around JSON values safely.*_weak
variants of scheduled_actor::run_{delayed, scheduled}
.
These functions add no reference count to their actor, allowing it to become
unreachable if other actors no longer reference it.typed_actor_pointer
can now access the
run_{delayed,scheduled}
member functions.make_observable
).nullptr
-access inside some CAF function.expected
now implements the monadic member functions from C++23
std::expected
as well as value_or
.label-1
becomes label_1
(#1386).fan_out_request
request now properly deals with actor handles that
respond with void
(#1369).mcast
and ucast
operators now stop calling on_next
immediately when
disposed.close
or
abort
were called prior to consumers or producers attaching.caf::net::make_tcp_accept_socket
now handles passing 0.0.0.0
correctly by opening the socket in IPv4 mode. Passing an empty bind address
now defaults to INADDR6_ANY
(but allowing IPv4 clients) with INADDR_ANY
as
fallback in case opening the socket in IPv6 mode failed.caf::expected
that have no equivalent in
std::expected
are now deprecated. Further, caf::expected<unit_t>
as well
as constructing from unit_t
are deprecated as well. The reasoning behind
this decision is that caf::expected
should eventually become an alias for
std::expected<T, caf::error>
.null
as last value before
the closing parenthesis.fan_out_request
request now properly deals with actor handles that
respond with void
(#1369).caf.net
. This module enables CAF applications
to interface with network protocols more directly than caf.io
. The new
module contains many low-level building blocks for implementing bindings to
network protocols. However, CAF also ships ready-to-use, high-level APIs for
WebSocket and HTTP. Please have a look at our new examples that showcase the
new APIs!async
building blocks. Most notably, this includes
asynchronous buffers for the flow API and futures / promises that enable the
new HTTP request API. We plan on making these building blocks more general in
the future for supporting a wider range of use cases.type_id_mapper
to generate
and parse JSON text that uses different names for the types than the C++ API.null
as last value before
the closing parenthesis.middleman
) now show up in --long-help
output...._instance
convenience functions on the registry metric now properly
support double
metrics and histograms.--dump-config
now prints valid config file syntax.broken_promise
errors in some (legitimate) edge cases (#1361).stream
API in CAF has been replaced by a new API that
is based on the new flow API.replies_to
and reacts_to
no longer
serve any purpose and are thus deprecated.caf::byte
, caf::optional
and caf::string_view
became obsolete
after switching to C++17. Consequently, these types are now deprecated in
favor of their standard library counterpart.caf::variant
also became obsolete when switching to C++17.
Unfortunately, the implementation was not as standalone as its deprecated
companions and some of the free functions like holds_alternative
were too
greedy and did not play nicely with ADL when using std::variant
in the same
code base. Since fixing caf::variant
does not seem to be worth the time
investment, we remove this type without a deprecation cycle.CAF_CHECK_NOTHROW(expr)
CAF_CHECK_THROWS_AS(expr, type)
CAF_CHECK_THROWS_WITH(expr, str)
CAF_CHECK_THROWS_WITH_AS(expr, str, type)
run_until
miscounted the number of executed events, also
causing run_once
to report a wrong value. Both functions now return the
correct result.allow(...).with(...)
in unit tests without a matching message crashed
the program. By adding a missing NULL-check, allow
is now always safe to
use.put
, put_missing
, get_if
, etc. now
gracefully handle the global
category when explicitly using it.message_builder
did not call the destructors for
their values, potentially causing memory leaks (#1321).REQUIRE*
macros,
expect
and disallow
no longer call abort()
. Instead, they throw an
exception that only stops the current test instead of stopping the entire test
program.