Как создать в стеке выделенный массив указателей polymorphi c на интерфейсы, включая соответствующий pointee производного типа? - PullRequest
0 голосов
/ 14 января 2020

Для контекста, я работаю над встроенной средой высокой надежности. Некоторые из ограничений, налагаемых в этой среде, включают;

  1. Нет динамического c выделение памяти
  2. Требуется соответствие c ++ 14 или ниже
  3. Нет информации о типе времени выполнения

Таким образом, конечный результат заключается в том, что я хочу класс, содержащий const std::array указателей типа BaseInterface, который может содержать выделенный стек набор произвольных производных классов.

Рассмотрим неполный пример ниже.

Примечание: Конструктор 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. Создание функционального не универсального c вспомогательного класса / функции, содержащего 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 Ответ

0 голосов
/ 14 января 2020

Похоже, что вы хотите сохранить производные объекты в виде кортежа, а затем применить этот кортеж с функцией, которая преобразует это в массив:

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 в утверждении stati c.

...