Пару дней назад на работе зашел разговор о том, как можно использовать указатели на методы в 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 комментариев:
класическим примером является случай, когда нужно передать указатель на мембер в сишный коллбэк (win api или др. библиотека)
Насколько я помню, так можно сделать только для статического метода. Указатель на нестатический метод класса, обычно, даже больше, чем указатель на функцию. Поэтому его даже привести к какому-нибудь типу коллбэк-функции нельзя безопасно.
"этот стон у нас песней зовется" (с)сами знаете :)
технически код полностью эквивалентен
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 не понимает :)
сорри за форматироване, исходники редко вставляю
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;
}
2Rubanets Myroslav:
>технически код полностью эквивалентен
Почти. Но с вызовом measure_duration намерения разработчика, имхо, более четко выражены.
Тонкое различие в двух подходах: деструктор scoped_timer_t будет отрабатывать всегда, даже если в методе выскочило исключение.
>Ну и в посте осознанно смешивается измерение "методов" фасада подсистемы с помощью сисколла и измерение любого метода под соусом "эффективно"
Под эффективно я понимал здесь то, что никаких дополнительных ресурсов тратиться не будет. Системные вызовы по-любому нужно будет делать. Но если в Java создавать анонимные классы, реализующие интерфейс, или делегаты в D (блоки кода в Ruby), то тут еще будет лишний объект на хипе создаваться. Но это все мелочи по сравнению с тем, как некоторые Java-программисты в подобных случаях рефлексию используют.
>код форматировать в блоггере классно - даже pre не понимает
Угу, знал бы я об этом в самом начале...
2night beast:
Вау! Классно! Я такими фокусами никогда не пользовался. Нужно будет запомнить.
2Евгений
при исключении время система разумеется не потребляет и мерять его не надо ;)
ну и должен сказать спасибо за пост - комментарии доставляют.
>при исключении время система разумеется не потребляет и мерять его не надо
it depends. Иногда и не нужно мерять время сбойных операций, поскольку оно будет на несколько порядков отличаться от нормальных времен.
Поэтому лучше детали упрятать в measure_duration, который может быть реализован как через scoped_timer_t, так и через прямые обращения к gettimeofday.
>ну и должен сказать спасибо за пост - комментарии доставляют.
Ну дык -- завсегда пожалуйста, заходите еще :)
Отправить комментарий