Search code examples
c++c++-actor-framework

How can the newly created instance of type T be accessed after calling spawn_typed<T>(...)? (C++ Actor Framework)


In the following example, the spawned actor is of type a_type, which is based on the typed_actor template. How do I convert from a_type to an instance of class A?

using a_type = typed_actor<replies_to<int>::with<void>>;

class A : public a_type::base
{
protected:
    behavior_type make_behavior() override
    {
        return
        {
            [this](int value)
            {
                aout(this) << value << endl;
            }
        };
    }
};

int main()
{
    a_type spawned = spawn_typed<A>();
}

Solution

  • Actors are asynchronous entities and should be considered isolated. While it is technically possible to get a raw pointer via actor_cast and downcast it to the actual type, I strongly advise against doing so. Only an actor itself is allowed to access its state in CAF. Decoupling actors via message passing avoids race conditions by design. Instead of calling members directly, actors send messages to mailboxes of others. These mailboxes are real concurrency workhorses and implemented without locks. Ideally, you spawn more actors than you have cores available, since actor applications scale by splitting large problems into many small chunks which are then solved by lots of actors concurrently. Always keep Amdahl's law in mind: your application cannot be faster than your longest sequential processing chain.

    The actor handles in CAF also enable network-transparency. Once you start to distribute your application across the network, accessing state directly will simply not work. Further, if you are using state_based actors, accessing the state from outside an actor is highly unsafe, because the state will be destroyed once the actor called quit() or got killed.

    There are basically three ways to exchange information:

    • 1:1 message passing. This is the correct solution most of the time. You can always use a scoped_actor if you need an ad-hoc way to communicate to an actor.
    • N:M message passing using groups. This is an easy and convenient way to push updates from any number of producers to any number of subscribers. CAF offers both named groups as well as "ad-hoc" anonymous groups to allow loosely coupled communication.
    • Sharing transactional (or otherwise synchronized) data structures. If you need to integrate actors into an existing application or need to combine actors with other concurrency abstractions, sharing concurrent data structures between actors and non-actors can be a valid solution. In this model, you pass the data structure to an actor via its constructor. However, before doing that, you should consider a design with plain message passing and groups (or actor pools) to organize concurrent workers first. This limits your application to a single machine and takes away design space for future developments.

    Actor pools also enable 1:N communication (broadcasting), "scatter/gather"-style workflows, etc.

    In any case, breaking the abstraction provided by CAF is usually a bad idea and you can end up in "undefined behavior land" real quick. One final note, if you find yourself in a dead-end with your current design, please feel free to start a discussion on the CAF mailing list.