Stiffstream / sobjectizer

An implementation of Actor, Publish-Subscribe, and CSP models in one rather small C++ framework. With performance, quality, and stability proved by years in the production.

Home Page:https://stiffstream.com/en/products/sobjectizer.html

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

A possibility to subscribe a event-handler to a base type of a message

eao197 opened this issue · comments

NOTE. This issue is just a reminder that SObjectizer needs a solution for use-cases like described in #24
I don't know how and when this will be implemented (and can it be implemented at all), but the reminder will help to form scope for a new SObjectizer's release when appropriate resources will be available.

The task is to allow writing something like that:

class image_base : public so_5::message_t {...};
class image_vendor_A : public image_base {...};
class image_vendor_B : public image_base {...};
class image_vendor_C : public image_base {...};

// The first handler.
void first_handler::so_define_agent() {
  so_subscribe_self()
    // Handles only one type of images.
    .event([this](mhood_t<image_vendor_A> cmd) {...})
    // All other messages are just logged.
    .event([this](mhood_t<image_base> cmd) { log_image(*cmd); });
}
...
// Another handler.
void second_handler::so_define_agent() {
  so_subscribe_self()
    // Handles two types.
    .event([this](mhood_t<image_vendor_B> cmd) {...})
    .event([this](mhood_t<image_vendor_C> cmd) {...})
    // All other messages are just redirected as is.
    .event([this](mhood_t<image_base> cmd) {
      so_5::send(new_dest, cmd);
    });
}

// Yet another handler.
void third_handler::so_define_agent() {
  // Redirects all messages to another destination.
  so_subscribe_self().event([this](mhood_t<image_base> cmd) {
    so_5::send(new_dest, cmd);
  }
}

Additional requirement. It'll be great to have that functionality for handling messages from mchains too:

so_5::receive(from(mchain).handle_all(),
  [](so_5::mhood_t<image_vendor_A> cmd) {...},
  [](so_5::mhood_t<image_vendor_C> cmd) {...},
  [new_dest](so_5::mhood_t<image_base> cmd) { so_5::send(new_dest, cmd); });

There is an underwater rock here. The information about subscriptions is stored in two places:

  1. Inside the subscription storage in the agent itself. This information is used during the searching of an event-handler for an incoming message.
  2. Inside a mbox. This information is used for selecting receivers when a message is sent into the mbox.

For example, an agent can make three subscriptions to a message of type T from mbox M in different states. There will be three entries about type T in agent's subscription storage. But only one entry inside the mbox M: this entry tells M that there is a receiver for messages of type T.

If we have the following message type hierarchy:

image_base
`- image_vendor_A
`- image_vendor_B
`- image_vendor_C

and some agent makes a subscription for image_vendor_A and image_base from mbox M then there will be two entries in the mbox M: one for image_vendor_A and another for image_base. But there won't be any entries for image_vendor_B or image_vendor_C.

So if someone sends an instance of image_vendor_B to the mbox M then this instance will be ignored because there is no entries for such type in the mbox M.

Hi! I resume this old issue (motivated by #24). Do you have any ideas if this can be implemented somehow in the future? I think that matching rules similar to exception handers would be aligned with C++ semantics and can simply users' understanding.

Hi! I made several attempts to solve this problem in the past, but didn't found a solution.

So, maybe it's time to make a new attempt... :)

Awesome! If I can be helpful, don't hesitate to let me know (maybe for some feedback on the possible interface and trade-offs)!

A question that have never been asked earlier:

There are not only event handlers, but also delivery filters, message limits and message sinks (with transformers or something else) that are also bound to message types. Should things support inheritance too?

For example, let's assume there is an agent:

class demo_handler final : public so_5::agent_t
{
  void so_define_agent() override {
    so_set_delivery_filter(source_mbox, [](const image_base & msg) { ... });
    so_subscribe(source_mbox)
      .event([](const image_vendor_A & msg) {...})
      .event([](const image_vendor_B & msg) {...})
      ;
  }
};

Should the delivery filter for image_base receive messages of image_vendor_A and image_vendor_B?

Another example:

so_5::single_sink_binding binding;
binding.bind<image_base>(source_mbox, so_5::wrap_to_msink(dest_mbox));

so_5::send<image_vendor_A>(source_mbox, ...);
so_5::send<image_vendor_B>(source_mbox, ...);
...

Should such a binding redirects image_vendor_A and image_vendor_B to the dest_mbox?

And a more complex case with delivery_filters.

Let's imagine a hierarchy:

image_base
`- image_vendor_A
`- image_bgr_base
   `- image_vendor_B
   `- image_vendor_C

And potential so_define_agent:

void some_handler::so_define_agent() {
  so_set_delivery_filter(source_mbox, [](const image_base &) {...});
  so_set_delivery_filter(source_mbox, [](const image_bgr_base &) {...});

  so_subscribe(source_mbox)
    .event([](const image_vendor_B &) {...})
    .event([](const image_bgr_base &) {...})
    .event([](const image_vendor_A &) {...})
    .event([](const image_base &) {...})
    ;
}

I can imagine that such double (or triple, or quadruple, or ...) filtering might be necessary and should be enabled. But I afraid it would make implementation of custom mboxes much harder.