вторник, 10 марта 2015 г.

[prog.c++] Пример устранения многословности кода

Код на C++ имеет репутацию многословного. Хотя здесь далеко не все так однозначно. И если в C++98/03 было больше объективных предпосылок к многословности, то в C++11/14 таковых остается все меньше и меньше. Поэтому в современном C++ объемный прикладной код, как мне представляется, проистекает из-за недостаточной проработанности задействованных в коде библиотек. И, если посмотреть на то, что ты делаешь, более критично, то выяснится, что возможности для уменьшения объема кода имеются. Нужно только победить свою лень и поэкспериментировать.

Вот, в качестве примера, то, что было сделано в первом приближении:

a_worker_t(
   environment_t & env,
   string reply )
   :  agent_t( env,
         tuning_options()
            .message_limits(
                  message_limit< msg_request >( 20 ).redirect( [this]{ return m_logger; } ),
                  message_limit< msg_finish >( 1 ).drop )
            .subscription_storage_factory(
                  vector_based_subscription_storage_factory( 2 ) ) )
   ,  m_reply( move( reply ) )
{}

Т.е. нужно было явно вызвать метод tuning_options(), затем у возвращаемого объекта отдельно вызывать методы message_limits() и storage_subscription_factory(). При передаче параметров в message_limits() сначала нужно было конструировать объект message_limit<MSG>, а затем брать из этого объекта соответствующий атрибут или вызывать соответствующий метод.

В общем, простейшее решение "в лоб". Ни плохое, ни хорошее. Обычное.

Однако, если чуточку подумать, вспомнить про перегрузку операторов, то в пару-тройку несложных шагов можно прийти вот к такому:

a_worker_t(
   environment_t & env,
   string reply )
   :  agent_t( env
         + limit_then_redirect< msg_request >( 20, [this] { return m_logger; } )
         + limit_then_drop< msg_finish >( 1 )
         + vector_based_subscription_storage_factory( 2 ) )
   ,  m_reply( move( reply ) )
{}

Базируется это все на тех же tuning_options(), message_limits() и subscription_storage_factory(), но задействуются они неявно, внутри соответствующих operator+. В итоге писать нужно меньше, да и воспринимается новый код проще.

Объективности ради нужно сказать, что второй, более компактный вариант, на данный момент менее эффективен, чем первый. Но, при необходимости, решить проблему можно будет посредством большей специализации operator+. Например, сейчас все operator+ выглядят приблизительно так:

templateclass M, class L >
agent_context_t
operator+(
   agent_context_t ctx,
   message_limit::redirect_indicator_t< M, L > limit )
   {
      ctx.options().message_limits( std::move( limit ) );
      return std::move( ctx );
   };

Нужно будет добавить еще один вариант:

templateclass M, class L >
agent_context_t &&
operator+(
   agent_context_t && ctx,
   message_limit::redirect_indicator_t< M, L > limit )
   {
      ctx.options().message_limits( std::move( limit ) );
      return std::move( ctx );
   };

Что, конечно же, увеличит объем библиотечного кода (хотя, если по умному задействовать макросы, то не факт). Но зато прикладной код окажется и лаконичным, и эффективным.

Комментариев нет: