如何模板化堆栈分配的多态指针数组到接口,包括派生类型的相应点?

How to template a stack allocated array of polymorphic pointers to interfaces, including the corresponding pointee of derived type?

本文关键字:类型 派生 包括 接口 堆栈 何模板 分配 多态 数组 指针      更新时间:2023-10-16

对于上下文,我正在研究一个嵌入式高可靠性环境。在此环境中实施的一些限制包括:

  1. 无动态内存分配
  2. 需要符合 c++14 或更低
  3. 版本
  4. 无运行时类型信息

所以最终结果是我想要一个包含 BaseInterface 类型的指针const std::array的类,该类可以包含一组堆栈分配的任意派生类。

请考虑下面的非完整示例。

注意:BaseHandler 构造函数和成员变量注释以获取更多上下文问题。

#include <array>
#include <tuple>
class BaseInterface {
public:
virtual void DoSomething() = 0;
};
class Derived1 : public BaseInterface {
void DoSomething() override {}
};
class Derived2 : public BaseInterface {
void DoSomething() override {}
};
/**
* @brief Handles stack allocated polymorphic arrays
*
* @tparam base_ptr_t pointer type that each element must derive from
* @tparam derived_t parameter pack of derived variables
*/
template <class base_ptr_t, class... derived_t>
class BaseHandler {
public:
BaseHandler(Elements &&... element)
: memory_store_x_{std::forward<derived_t>(element)...},
array_(/* How to make each elementwise pointer point to each element in the tuple/array of aligned_union?*/) {}
typedef std::array<base_ptr_t, sizeof...(Elements)> array_t;
const array_t GetArray();
private:
const array_t array_;
// Use tuple to store actual memory?
const std::tuple<Elements...> memory_store_0_;
// Use array of aligned_union's to store (perhaps memory inneficient compared
// to tuple) ??
std::array<std::aligned_union_t<0, derived_t...>, sizeof...(derived_t)>
memory_store_1_;
};
template <size_t N>
void Foo(const std::array<const BaseInterface *, N> &arr) {
// Do something interesting
}
int main() {
// Intended usage
BaseHandler<const BaseInterface *, Derived1, Derived2> handler(Derived1(),
Derived2());
auto arr = handler.GetArray();
Foo(arr);
}

我探索但不喜欢的一些解决方法包括:

  1. 创建一个包含static const std::array<X,X>的函数非泛型帮助程序类/函数,例如
const std::array<BaseInterface*,2> MakeResourceList() {
// Static lifetime, i.e. will not be destroyed when out of scope
static const Derived1 a();
static const Derived2 b();
std::array<BaseInterface*,2> result = {&a,&b};
return result;
}
  1. 构造所有资源并手动将它们传递到数组中。导致代码膨胀。
  2. 对所有构造的资源使用全局变量。

似乎您想将派生对象存储为元组,然后将该元组与将其转换为数组的函数一起应用:

template <class base_t, class... derived_t>
class BaseHandler {
static_assert(std::conjunction_v<std::is_base_of_v<base_t, derived_t>...>);
std::tuple<derived_t...> storage_;
public:
std::array<base_t*, sizeof...(derived_t)> GetArray() {
return std::apply([](derived_t&... deriveds){
return std:array<base_t*, sizeof...(derived_t)>{{&deriveds...}};
}, storage_);
}
};

为了清楚起见,这将 C++17 用于一些库内容,但所有这些都可以在 C++14 中实现,没有任何问题。

我还将模板参数从base_ptr_t更改为base_t,因为它使您的所有模板参数都相同"种类",并且您不必在静态断言中执行remove_pointer_t