- PVSM.RU - https://www.pvsm.ru -
С момента выхода стандарта С++11 прошло уже больше двух лет. И большинство С++ разработчиков уже знакомы с основными его нововведениями и костылями. Одним из нововведений было добавление в стандартную библиотеку класса std::function
.
Классы std::function и boost::function являются высокоуровневыми обертками над функциями и функциональными объектами. Объекты таких классов позволяют хранить и вызывать функции и функторы с заданной сигнатурой, что бывает удобно, например, при создании callback вызовов (например, мы можем регистрировать несколько обработчиков, и это могут быть как обычные функции, так и объекты с определенным оператором =)
© [1]
Этого замечательного шаблонного класса давно не хватало нам. Однако, в погоне за универсальностью разработчикам стандартной библиотеки пришлось пойти на некоторые жертвы. Так как С++ используется в первую очередь там, где необходимо выжать максимальную скорость из железа, со больше всего разочаровывает любой overhead по скорости. В частности, к таким вещам можно отнести:
std::function
вызывается оператор new
, как следствие того, что std::function
позволяет сохранять в себя функтор произвольного размера.std::function
имеет семантику копирования, которая, в принципе, редко когда бывает необходима в при действиях с функторами (в связи с появлением move-семантики), но стоит определенных ресурсов CPU.Как от этого избавиться?
Для этого, конечно же, нужно написать свой std::function
без этих фатальных недостатков =) К счастью, новые возможности С++11 позволяют нам это сделать без лишних телодвижений и обращений к специфичным возможностям компиляторов.
std::function
.Для начала рассмотрим простой способ реализовать делегат на С++, который я подчерпнул из статей Fastest Possible C++ Delegates [2] и Lightweight Generic C++ Callbacks [3].
class function_t {
public:
template <typename U>
fixed_function_t(U &&object)
{
typedef typename std::remove_reference<U>::type unref_type;
m_object_ptr = new unref_type(object);
m_method_ptr = &method_stub<unref_type>;
}
void operator()() const
{
if (m_method_ptr) {
(*m_method_ptr)(m_object_ptr);
}
}
private:
void *m_object_ptr;
typedef R (*method_type)(void *);
method_type m_method_ptr;
template <class T>
static void method_stub(void *object_ptr)
{
static_cast<T *>(object_ptr)->operator()();
}
};
Пока что закроем глаза на отсутствие вызова delete
, чтобы не загружать код излишней информацией.
А дальше, будем добавлять все необходимые на фичи.
Для этого будем конструировать объект делегата в уже существующем на момент создания нашего function
буфере. Добавим в наш класс следующее поле:
class function_t {
enum {STORAGE_SIZE = 32};
public:
// ...
private:
typename std::aligned_storage<STORAGE_SIZE, STORAGE_SIZE>::type m_storage;
// ...
};
И будем конструировать объект делегата в нём.
template <typename U>
fixed_function_t(U &&object)
{
typedef typename std::remove_reference<U>::type unref_type;
m_object_ptr = new (&m_storage) unref_type(std::forward<U>(object));
m_method_ptr = &method_stub<unref_type>;
}
struct noncopyable_t {
noncopyable_t & operator=(const noncopyable_t&) = delete;
noncopyable_t(const noncopyable_t&) = delete;
noncopyable_t() = default;
};
class function_t : private noncopyable_t {
//...
При этом надо не забыть реализовать правильно move семантику! В move конструкторе придется делать memcpy
для m_storage
, и изменить указатель на метод.
std::function
template <typename R, typename... ARGS>
class fixed_function_t : private noncopyable_t {
enum {STORAGE_SIZE = 32};
public:
//...
R operator()(ARGS... args) const
{
if (m_method_ptr) {
return (*m_method_ptr)(m_object_ptr, args...);
}
return R();
}
private:
//...
template <class T>
static R method_stub(void *object_ptr, ARGS... args)
{
return static_cast<T *>(object_ptr)->operator()(args...);
}
};
Готово!
Надеюсь мой велосипед будет для вас полезен в качестве источника идей, а, может быть, он даже попадет в каком-то виде в продакшн. Я буду только рад.
Полный код [4] с тестами [5] можно найти, как это принято, на Github.
Спасибо за внимание.
Автор: Inkooboo
Источник [6]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/programmirovanie/49410
Ссылки в тексте:
[1] ©: http://habrahabr.ru/post/159389/
[2] Fastest Possible C++ Delegates: http://www.codeproject.com/Articles/7150/Member-Function-Pointers-and-the-Fastest-Possible
[3] Lightweight Generic C++ Callbacks: http://www.codeproject.com/Articles/136799/Lightweight-Generic-C-Callbacks-or-Yet-Another-Del
[4] код: http://github.com/inkooboo/snippets-of-my-dreams/blob/master/fixed_function.hpp
[5] тестами: http://github.com/inkooboo/snippets-of-my-dreams/blob/master/test/fixed_function.cpp
[6] Источник: http://habrahabr.ru/post/203888/
Нажмите здесь для печати.