среда, 21 апреля 2010 г.

[prog.c++] Пример использования указателей на методы в C++

Пару дней назад на работе зашел разговор о том, как можно использовать указатели на методы в C++. Штука это специфическая и применяется мной не часто, реже чем указатели на функции. Да еще, как обычно, когда нужно вспомнить яркий пример чего-нибудь, память не подсказывает ничего путного. Поэтому не смог я тогда вспомнить хорошего и убедительного примера.

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

void
a_cp_service_t::do_all_periodic_actions()
   {
      measure_duration(
            m_monitors.m_avg_time_save_outgoing_pkg,
            this,
            &a_cp_service_t::handle_received_outgoing_packages );

      measure_duration(
            m_monitors.m_avg_time_handle_SR,
            this,
            &a_cp_service_t::handle_received_send_results );

      measure_duration(
            m_monitors.m_avg_time_handle_SR_pkg_ack,
            this,
            &a_cp_service_t::handle_received_SR_package_acks );

Где measure_duration – это очень простая функция-шаблон:

template< class T >
inline void
measure_duration(
   monitor_t & monitor,
   T * object,
   void (T::*method)() )
   {
      ACE_Time_Value s = ACE_OS::gettimeofday();
      (object->*method)();
      ACE_Time_Value f = ACE_OS::gettimeofday();

      monitor.add_time_slice( f - s );
   }

Т.е. указатели на методы в C++ позволили обойтись без определения каких-либо интерфейсов (к чему пришлось бы прибегнуть в Java), либо лямбда-функций-делегатов (C#/D/Scala), либо рефлексии (опять же Java, если интерфейсы не нравятся), либо макросов (если не знать, что такое указатели на методы в C++). А так все просто, прозрачно и эффективно.

8 комментариев:

night beast комментирует...

класическим примером является случай, когда нужно передать указатель на мембер в сишный коллбэк (win api или др. библиотека)

eao197 комментирует...

Насколько я помню, так можно сделать только для статического метода. Указатель на нестатический метод класса, обычно, даже больше, чем указатель на функцию. Поэтому его даже привести к какому-нибудь типу коллбэк-функции нельзя безопасно.

Miroslav комментирует...

"этот стон у нас песней зовется" (с)сами знаете :)
технически код полностью эквивалентен
void a_cp_service_t::do_all_periodic_actions()
{
{ scope_timer_t t(m_monitors.m_avg_time_save_outgoing_pkg);
this->handle_received_outgoing_packages();
}
... и так 3 раза

Ну и в посте осознанно смешивается измерение "методов" фасада подсистемы с помощью сисколла и измерение любого метода под соусом "эффективно". Джава пробивает ? ;)
Легкий pun intended xD
ps код форматировать в блоггере классно - даже pre не понимает :)

night beast комментирует...

сорри за форматироване, исходники редко вставляю

template< typename T, void (T::*name)() >
void test ( void * ptr ) {
(static_cast<T *>(ptr)->*name)();
}


struct Foo {
void x () { std::cout << __func__ << std::endl; }
void y () { std::cout << __func__ << std::endl; }
};

int main()
{
Foo x;
void (*v)( void * );
v = test<Foo,&Foo::x>;
v(&x);
v = test<Foo,&Foo::y>;
v(&x);
return 0;
}

eao197 комментирует...

2Rubanets Myroslav:

>технически код полностью эквивалентен

Почти. Но с вызовом measure_duration намерения разработчика, имхо, более четко выражены.

Тонкое различие в двух подходах: деструктор scoped_timer_t будет отрабатывать всегда, даже если в методе выскочило исключение.

>Ну и в посте осознанно смешивается измерение "методов" фасада подсистемы с помощью сисколла и измерение любого метода под соусом "эффективно"

Под эффективно я понимал здесь то, что никаких дополнительных ресурсов тратиться не будет. Системные вызовы по-любому нужно будет делать. Но если в Java создавать анонимные классы, реализующие интерфейс, или делегаты в D (блоки кода в Ruby), то тут еще будет лишний объект на хипе создаваться. Но это все мелочи по сравнению с тем, как некоторые Java-программисты в подобных случаях рефлексию используют.

>код форматировать в блоггере классно - даже pre не понимает

Угу, знал бы я об этом в самом начале...

eao197 комментирует...

2night beast:

Вау! Классно! Я такими фокусами никогда не пользовался. Нужно будет запомнить.

Miroslav комментирует...

2Евгений
при исключении время система разумеется не потребляет и мерять его не надо ;)

ну и должен сказать спасибо за пост - комментарии доставляют.

eao197 комментирует...

>при исключении время система разумеется не потребляет и мерять его не надо

it depends. Иногда и не нужно мерять время сбойных операций, поскольку оно будет на несколько порядков отличаться от нормальных времен.

Поэтому лучше детали упрятать в measure_duration, который может быть реализован как через scoped_timer_t, так и через прямые обращения к gettimeofday.

>ну и должен сказать спасибо за пост - комментарии доставляют.

Ну дык -- завсегда пожалуйста, заходите еще :)