Вышли очередные версии библиотек SObjectizer и so5extra.
SObjectizer – это один из немногих все еще живых и развивающихся «акторных фреймворков» для C++ (еще есть QP/C++, CAF: C++ Actor Framework и совсем молодой еще проект rotor). Краткий обзор SObjectizer-а можно найти в этой презентации или в этой довольно старой уже статье. Хотелось бы подчеркнуть, что SObjectizer поддерживает не только модель акторов, но еще и такие модели как Publish-Subscribe и Communicating Sequential Processes. А so5extra – это набор дополнительных полезных прибамбасов для SObjectizer-а (например, реализованные на базе Asio диспетчер с пулом нитей и env_infrastructures, дополнительные типы message box-ов, средства для реализации синхронного взаимодействия и т.д.)
Если в двух словах, то:
- SObjectizer-5.7 теперь позволяет использовать
send_case
в функцииselect()
. Это делает SObjectizer-овскийselect()
гораздо более похожим наselect
из Golang-а. Но это нововведение нарушило совместимость с предыдущей версией 5.6, т.к. теперь старая функцияcase_
стала называтьсяreceive_case
; - в версии 5.7 устранен недочет в механизме доставки обернутых в конверты сообщений (т.е. enveloped messages) в случае использования
transfer_to_state()
иsuppress()
у агентов-получателей; - код so5extra теперь распространяется под BSD-3-CLAUSE лицензией, что позволяет бесплатно использовать so5extra при разработке закрытого программного обеспечения. Предыдущие версии распространялись под двойной лицензией (GNU Affero GPL v.3 и коммерческой);
- в so5extra-1.4 реализованы mchain-ы фиксированной емкости для случаев, когда эта емкость известна на этапе компиляции.
Если же рассказывать более подробно, то основная фишка SObjectizer-5.7 – это возможность использования select()
для отсылки исходящих сообщений (по аналогии с тем, как это происходит в Golang-е). Так что теперь можно делать вот такие вещи:
using namespace so_5;
struct Greetings {
std::string msg_;
};
// Попытка отослать сообщения в соответствующие каналы,
// но все операции должны уложиться в 250ms.
select(from_all().handle_n(3).total_time(250ms),
send_case(chAlice,
message_holder_t<Greetings>::make("Hello, Alice!"),
[]{ std::cout << "message sent to chAlice" << std::endl; }),
send_case(chBob,
message_holder_t<Greetings>::make("Hello, Bob!"),
[]{ std::cout << "message sent to chBob" << std::endl; }),
send_case(chEve,
message_holder_t<Greeting>::make("Hello, Eve!"),
[]{ std::cout << "message sent to chEve" << std::endl; }));
В одном select()
можно использовать и send_case()
и receive_case()
вместе. Например, вот SObjectizer-овская версия вычисления чисел Фибоначчи из в отдельном рабочем потоке (по мотивам из Golang’s tour):
using namespace std;
using namespace std::chrono_literals;
using namespace so_5;
struct quit {};
void fibonacci( mchain_t values_ch, mchain_t quit_ch )
{
int x = 0, y = 1;
mchain_select_result_t r;
do
{
r = select(
from_all().handle_n(1),
// Отсылка сообщения типа 'int' со значением 'x' внутри.
// Отсылка выполняется только когда values_ch готов для приема
// новых исходящих сообщений.
send_case( values_ch, message_holder_t<int>::make(x),
[&x, &y] { // This block of code will be called after the send().
auto old_x = x;
x = y; y = old_x + y;
} ),
// Ожидание сообщения типа `quit` из канала quit_ch.
receive_case( quit_ch, [](quit){} ) );
}
// Продолжаем операции пока что-то отсылается и ничего не прочитано.
while( r.was_sent() && !r.was_handled() );
}
int main()
{
wrapped_env_t sobj;
thread fibonacci_thr;
auto thr_joiner = auto_join( fibonacci_thr );
// Канал для чисел Фибоначчи будет иметь ограниченный объем.
auto values_ch = create_mchain( sobj, 1s, 1,
mchain_props::memory_usage_t::preallocated,
mchain_props::overflow_reaction_t::abort_app );
auto quit_ch = create_mchain( sobj );
auto ch_closer = auto_close_drop_content( values_ch, quit_ch );
fibonacci_thr = thread{ fibonacci, values_ch, quit_ch };
// Читаем первые 10 значений из values_ch.
receive( from( values_ch ).handle_n( 10 ),
// Отображаем каждое прочитанное значение.
[]( int v ) { cout << v << endl; } );
send< quit >( quit_ch );
}
Полное описание нововведений версии 5.7.0 можно найти здесь.
Основное изменение в so5extra-1.4 – это смена лицензии на BSD-3-CLAUSE. Поэтому теперь все множество дополнений к SObjectizer-у из so5extra могут бесплатно использоваться в разработке закрытого коммерческого ПО.
Единственное нововведение в so5extra-1.4 – это реализация mchain для случая, когда максимальный объем mchain-а известен на этапе компиляции. Подобные mchain-ы зачастую используются в сценариях request-response, где ожидается всего одно ответное сообщение на запрос:
#include <so_5_extra/mchains/fixed_size.hpp>
#include <so_5/all.hpp>
...
using namespace so_5;
// Канал для получения ответного сообщения.
auto reply_ch = extra::mchains::fixed_size::create_mchain<1>(env,
mchain_props::overflow_reaction_t::drop_newset);
// Отсылаем запрос.
send<SomeRequest>(target, ..., reply_ch, ...);
// Ждем и обрабатываем ответ.
receive(so_5::from(reply_ch).handle_n(1), [](const SomeReply & reply) { ... });
Надеюсь, что SObjectizer/so5extra кому-нибудь окажется полезен. Если есть вопросы, то спрашивайте, постараюсь ответить.