Search code examples
c++-actor-framework

Using containers with typed actors give "incomplete type" errors


When I add a container to a typed message I get "incomplete type" error messages. Here is a simple example. Notice how the final parameter in the "display_behavior" message is a string? This actor compiles and runs great.

using DisplayActor = caf::typed_actor<
    caf::result<void>(display_behavior, time_point<system_clock>, string)>;

class DisplayState {
private:
    shared_ptr<Displayable> displayable_;

public:
    explicit DisplayState(std::shared_ptr<Displayable> displayable) :
        displayable_(displayable) {}

    DisplayActor::behavior_type make_behavior() {
        return {
            [this](display_behavior, time_point<system_clock> quackTime, string behavior) {
                displayable_->displayBehavior(quackTime, behavior);
            }
        };
    }
};

using DisplayImpl = DisplayActor::stateful_impl<DisplayState>;

Now I replace the string parameter with a vector:

using DisplayActor = caf::typed_actor<
    caf::result<void>(display_behavior, time_point<system_clock>, vector<string>)>;

class DisplayState {
private:
    shared_ptr<Displayable> displayable_;

public:
    explicit DisplayState(std::shared_ptr<Displayable> displayable) :
        displayable_(displayable) {}

    DisplayActor::behavior_type make_behavior() {
        return {
            [this](display_behavior, time_point<system_clock> quackTime, vector<string> behavior) {
                //displayable_->displayBehavior(quackTime, behavior);
            }
        };
    }
};

using DisplayImpl = DisplayActor::stateful_impl<DisplayState>;

I get the following error: enter image description here

Am I doing something wrong?


Solution

  • If you see this kind of error, it usually means you are using a type that has no type ID assigned to it.

    Type IDs are used by CAF to generate meta data for messages. Based on this meta data, CAF can then implement "type switches" to automatically dispatch on message handlers based on the signature. As of 0.18.5, CAF assigns type IDs to some types of the standard library like the std::string (see https://github.com/actor-framework/actor-framework/blob/0.18.5/libcaf_core/caf/type_id.hpp#L375).

    However, std::vector<std::string> has no type ID by default. This means your application needs to assign one. The boilerplate code for that would look somewhat like this:

    CAF_BEGIN_TYPE_ID_BLOCK(my_types, first_custom_type_id)
    
      CAF_ADD_TYPE_ID(my_types, (std::vector<std::string>))
    
    CAF_END_TYPE_ID_BLOCK(my_types)
    

    And later on you need to also make sure to initialize the run-time type information. If you are using CAF_MAIN then it's just:

    CAF_MAIN(caf::id_block::my_types)
    

    If you are not using the macro, you need to add these lines before starting an actor_system (ideally first thing in main):

    caf::init_global_meta_objects<caf::id_block::my_types>(); // custom types
    // ... module types, e.g., caf::io::::init_global_meta_objects() ...
    caf::core::init_global_meta_objects(); // default types
    

    Currently (0.18.5), the manual discusses the type IDs under Configuring Actor Applications. It's in this section mostly for historic reasons because adding the run-time type information used to go through the actor-system config. Maybe the type IDs should get their own section or at least move to the "Type Inspection" part of the manual.

    I am still a bit confused though because this page shows how to write an inspect overload and the point_3d field has a vector member variable.

    You need a type ID for a type if you wish to send this type in a message on its own. This is required for the patter matching / type switch. Serializers can deal with std::vector and don't require type IDs. They operate on the inspect API. One fine day, C++ hopefully comes with (static) type reflection and most of the inspect overloads can be auto-generated by CAF.