1. FOR_EACH 宏实现
代码目录:cyber/base/for_each.h
DEFINE_TYPE_TRAIT(HasLess, operator<)
template <class Value, class End> typename std::enable_if<HasLess<Value>::value && HasLess<End>::value, bool>::type LessThan(const Value& val, const End& end) { return val < end; }
template <class Value, class End> typename std::enable_if<!HasLess<Value>::value || !HasLess<End>::value, bool>::type LessThan(const Value& val, const End& end) { return val != end; }
#define FOR_EACH(i, begin, end) \ for (auto i = (true ? (begin) : (end)); \ apollo::cyber::base::LessThan(i, (end)); ++i)
|
我们先看看这个宏的用法:
std::vector<int> vec; FOR_EACH(i, 0, 100) { vec.push_back(i); } FOR_EACH(it, vec.begin(), vec.end()) { std::cout<<*it<<std::endl; }
|
简单来说就是用于对不同类型的数据进行遍历操作:
DEFINE_TYPE_TRAIT(HasLess, operator<)
这个trait
操作是保证传进来的begin
和end
类型重载了<
,
for
循环里会去调用LessThan
函数,LessThan
函数的两个模板参数就是begin
所代表的类型和End
所代表的类型。
当HasLess<Value>::value && HasLess<End>::value
满足时,才会走第一个模板函数,将会对val
和end
的值进行比较,函数返回值为bool
类型,
当Value
或者End
其中任意一个类型不具有<
的重载则会走第二个模板函数,函数内部会对这两个值进行判断是否是相等
如果用户在使用时提供的类型既不支持 <
运算符,也不支持 !=
运算符,那么在编译时将会导致错误。
2. 对象池的实现
源码目录:cyber/base/object_pool.h
template <typename T> class ObjectPool : public std::enable_shared_from_this<ObjectPool<T>> { public: using InitFunc = std::function<void(T *)>; using ObjectPoolPtr = std::shared_ptr<ObjectPool<T>>;
template <typename... Args> explicit ObjectPool(uint32_t num_objects, Args &&... args);
template <typename... Args> ObjectPool(uint32_t num_objects, InitFunc f, Args &&... args);
virtual ~ObjectPool();
std::shared_ptr<T> GetObject();
private: struct Node { T object; Node *next; }; ObjectPool(ObjectPool &) = delete; ObjectPool &operator=(ObjectPool &) = delete; void ReleaseObject(T *);
uint32_t num_objects_ = 0; char *object_arena_ = nullptr; Node *free_head_ = nullptr; };
template <typename T> template <typename... Args> ObjectPool<T>::ObjectPool(uint32_t num_objects, Args &&... args) : num_objects_(num_objects) { const size_t size = sizeof(Node); object_arena_ = static_cast<char *>(std::calloc(num_objects_, size)); if (object_arena_ == nullptr) { throw std::bad_alloc(); }
FOR_EACH(i, 0, num_objects_) { T *obj = new (object_arena_ + i * size) T(std::forward<Args>(args)...); reinterpret_cast<Node *>(obj)->next = free_head_; free_head_ = reinterpret_cast<Node *>(obj); } }
template <typename T> template <typename... Args> ObjectPool<T>::ObjectPool(uint32_t num_objects, InitFunc f, Args &&... args) : num_objects_(num_objects) { const size_t size = sizeof(Node); object_arena_ = static_cast<char *>(std::calloc(num_objects_, size)); if (object_arena_ == nullptr) { throw std::bad_alloc(); }
FOR_EACH(i, 0, num_objects_) { T *obj = new (object_arena_ + i * size) T(std::forward<Args>(args)...); f(obj); reinterpret_cast<Node *>(obj)->next = free_head_; free_head_ = reinterpret_cast<Node *>(obj); } }
template <typename T> ObjectPool<T>::~ObjectPool() { if (object_arena_ != nullptr) { const size_t size = sizeof(Node); FOR_EACH(i, 0, num_objects_) { reinterpret_cast<Node *>(object_arena_ + i * size)->object.~T(); } std::free(object_arena_); } }
template <typename T> void ObjectPool<T>::ReleaseObject(T *object) { if (cyber_unlikely(object == nullptr)) { return; }
reinterpret_cast<Node *>(object)->next = free_head_; free_head_ = reinterpret_cast<Node *>(object); }
template <typename T> std::shared_ptr<T> ObjectPool<T>::GetObject() { if (cyber_unlikely(free_head_ == nullptr)) { return nullptr; }
auto self = this->shared_from_this(); auto obj = std::shared_ptr<T>(reinterpret_cast<T *>(free_head_), [self](T *object) { self->ReleaseObject(object); }); free_head_ = free_head_->next; return obj; } template <typename T> template <typename... Args> ObjectPool<T>::ObjectPool(uint32_t num_objects, InitFunc f, Args &&... args) : num_objects_(num_objects) { const size_t size = sizeof(Node); object_arena_ = static_cast<char *>(std::calloc(num_objects_, size)); if (object_arena_ == nullptr) { throw std::bad_alloc(); }
FOR_EACH(i, 0, num_objects_) { T *obj = new (object_arena_ + i * size) T(std::forward<Args>(args)...); f(obj); reinterpret_cast<Node *>(obj)->next = free_head_; free_head_ = reinterpret_cast<Node *>(obj); } }
template <typename T> ObjectPool<T>::~ObjectPool() { if (object_arena_ != nullptr) { const size_t size = sizeof(Node); FOR_EACH(i, 0, num_objects_) { reinterpret_cast<Node *>(object_arena_ + i * size)->object.~T(); } std::free(object_arena_); } }
template <typename T> void ObjectPool<T>::ReleaseObject(T *object) { if (cyber_unlikely(object == nullptr)) { return; }
reinterpret_cast<Node *>(object)->next = free_head_; free_head_ = reinterpret_cast<Node *>(object); }
template <typename T> std::shared_ptr<T> ObjectPool<T>::GetObject() { if (cyber_unlikely(free_head_ == nullptr)) { return nullptr; }
auto self = this->shared_from_this(); auto obj = std::shared_ptr<T>(reinterpret_cast<T *>(free_head_), [self](T *object) { self->ReleaseObject(object); }); free_head_ = free_head_->next; return obj; }
|
首先来看构造函数:
template <typename T> template <typename... Args> ObjectPool<T>::ObjectPool(uint32_t num_objects, Args &&... args) : num_objects_(num_objects) { const size_t size = sizeof(Node); object_arena_ = static_cast<char *>(std::calloc(num_objects_, size)); if (object_arena_ == nullptr) { throw std::bad_alloc(); }
FOR_EACH(i, 0, num_objects_) { T *obj = new (object_arena_ + i * size) T(std::forward<Args>(args)...); reinterpret_cast<Node *>(obj)->next = free_head_; free_head_ = reinterpret_cast<Node *>(obj); } }
|
向对象池中传递目标的类型和构建目标的参数,用T
和 ... Args
来进行传递,在构造函数内部首先计算了一个Node
的大小,然后分配num_objects
个node
大小的内存作为目标池来管理,根据这个node
的定义我们可以知道对象池管理对象时是以链表的方式将各个链表链接起来的,再分配完毕内存后,通过FOR_EACH
来在指定的地址处创建对象,然后让创建的这个对象的指针指向free_head_
还有另外一个构造函数:
template <typename T> template <typename... Args> ObjectPool<T>::ObjectPool(uint32_t num_objects, InitFunc f, Args &&... args) : num_objects_(num_objects) { const size_t size = sizeof(Node); object_arena_ = static_cast<char *>(std::calloc(num_objects_, size)); if (object_arena_ == nullptr) { throw std::bad_alloc(); }
FOR_EACH(i, 0, num_objects_) { T *obj = new (object_arena_ + i * size) T(std::forward<Args>(args)...); f(obj); reinterpret_cast<Node *>(obj)->next = free_head_; free_head_ = reinterpret_cast<Node *>(obj); } }
|
这个构造和上一个的区别在于多了一个参数InitFunc f
,允许在构造对象池的时候提供一个操作对象的函数,
using InitFunc = std::function<void(T *)>;
|
InitFunc
是一个可调用对象,此对象的返回值是void
,需要的参数是T *
类型,所以在构造对象池的时候使用f(obj);
来操作了对象
接着来看看释放一个对象的操作,释放对象就是将一个对象重新放入对象池中:然后更新free_head
的值
template <typename T> void ObjectPool<T>::ReleaseObject(T *object) { if (cyber_unlikely(object == nullptr)) { return; }
reinterpret_cast<Node *>(object)->next = free_head_; free_head_ = reinterpret_cast<Node *>(object); }
|
最后是拿到一个对象的操作:
template <typename T> std::shared_ptr<T> ObjectPool<T>::GetObject() { if (cyber_unlikely(free_head_ == nullptr)) { return nullptr; }
auto self = this->shared_from_this(); auto obj = std::shared_ptr<T>(reinterpret_cast<T *>(free_head_), [self](T *object) { self->ReleaseObject(object); }); free_head_ = free_head_->next; return obj; }
|
通过std::shared_ptr<T> ptr(pointer, deleter);
来得到这个obj
,第二个参数即是智能指针管理的这个对象的引用计数归零时将要执行的操作,这里就是说当obj
的引用计数归零时,让此对象重新返回对象池,所以传入的可调用对象的形式是一个lamada函数: [self](T *object) { self->ReleaseObject(object); }
,然后更新free_head_
所以从对象池中拿对象从arena
的高地址处free_head_
开始拿,释放也是如此,将一个对象插入到free_head_
之后,然后更新free_head_
最后是析构函数:
template <typename T> ObjectPool<T>::~ObjectPool() { if (object_arena_ != nullptr) { const size_t size = sizeof(Node); FOR_EACH(i, 0, num_objects_) { reinterpret_cast<Node *>(object_arena_ + i * size)->object.~T(); } std::free(object_arena_); } }
|
遍历调用对象的自己析构函数,然后释放arena
这块内存
参考链接