Инициализация объекта из неизвестного производного класса известного базового класса - PullRequest
0 голосов
/ 22 апреля 2020

Итак, я делаю библиотеку для небольшого движка D2D; но дело не в этом, дело в том, что у меня есть этот класс, который будет базовым классом, который наследует класс пользователя. Моя основная идея была бы примерно такой:

struct BaseEngine {
    // I have two pure virtual functions so the user has to define them.
    virtual bool onLoad() = 0;
    virtual bool onFrame() = 0;
};

Теперь, если бы все это было в одном проекте, я мог бы сделать что-то вроде этого сразу после этого:

struct Derived : public BaseEngine {
    bool onLoad() override;
    bool onFrame() override;
};

const std::unique_ptr<BaseEngine> app = std::make_unique<Derived>();

Но на самом деле моя идея состоит в том, чтобы не хранить производный класс в моих заголовочных файлах и создавать библиотеку без какого-либо возможного определения производного класса, поэтому пользователь может просто назвать его как угодно в своем проекте.

Конечно, это не позволит мне скомпилировать его, потому что я не могу создать BaseEngine, потому что он имеет чисто виртуальные функции.

Тогда я хотя бы каким-то образом использовал шаблоны для решения этой проблемы? Сейчас я не очень знаком с шаблонами, но моя идея заключалась в том, чтобы сделать что-то вроде:

std::unique_ptr<BaseEngine> app;
template<class T : public BaseEngine>
void instantiator() {
    app = std::make_unique<T>();
}

Зная, что T содержит реализацию для onLoad() и onFrame(). Но, конечно, когда мне нужна такая функция, как шаблоны явно производных классов, никакой функции не существует (по крайней мере, я не знаю об этом).

Мой главный вопрос: есть ли способ мне инициализировать объект из «неизвестного» производного класса моего известного базового класса?

Редактировать: забыл упомянуть, что основная функция (в данном случае WinMain) будет работать на движке сторона, так как она заботится о регистрации класса окна и всех этих неприятных сообщениях.

Ответы [ 3 ]

0 голосов
/ 22 апреля 2020

Звучит так, будто вы хотите скомпилировать свой код в качестве разделяемой библиотеки, на которую вы можете ссылаться в другом проекте, который станет основным исполняемым файлом. К сожалению, могут быть проблемы с разделением типов классов между библиотеками и исполняемыми файлами из-за различий в способах их компиляции - я бы не рекомендовал идти по этому пути.

Если вы можете скомпилировать все сразу, хотя (т. е. у пользователя есть доступ к некомпилированным исходным и заголовочным файлам) вам не придется с этим сталкиваться. Просто предоставьте функцию, которая принимает экземпляр BaseEngine.

// BaseEngine.h
class BaseEngine { /* ... */ }
void startGame(std::unique_ptr<BaseEngine> engine) { /* ... */ }

// main.cpp (user edits this file)
#include "BaseEngine.h"

class DerivedEngine : public BaseEngine {
   // override methods here
}

int main() {
   return startGame(std::make_unique<DerivedEngine>());
}
0 голосов
/ 22 апреля 2020

Обычный ответ на это будет иметь функцию в вашем движке, которая принимает созданный вызывающим объект BaseEngine в качестве аргумента.

// Engine side function
bool InitializeEngine(BaseEngine * pEngine);

Альтернативный ответ (в зависимости от того, как пользователь предоставил код)) будет иметь экспортированную функцию с заданным именем и заставить эту функцию возвращать созданный производный от BaseEngine объект.

// User side code
BaseEngine * CreateEngine();

и затем использовать dlopen / dlsym (или LoadLibrary / GetProcAddress для windows ) чтобы получить указатель на функцию для CreateEngine. В любом случае за код объекта известен код пользователя, который отвечает за создание объекта.

0 голосов
/ 22 апреля 2020

Stati c полиморфизм: шаблон с любопытными повторяющимися шаблонами (CRTP)

Возможно, вы захотите использовать шаблон с любопытными повторяющимися шаблонами (CRTP):

#include <ios>
#include <iostream>
#include <memory>
#include <type_traits>

template <class T> struct BaseEngine {
  bool onLoad() const { return static_cast<T const *>(this)->onLoad(); }
  bool onFrame() { return static_cast<T *>(this)->onLoad(); }
};

// API exposed to library users.
template <typename T,
          typename = std::enable_if_t<std::is_base_of<BaseEngine<T>, T>::value>>
auto createEngine() {
  return std::make_unique<T>();
}

template <typename T> bool foo(const BaseEngine<T> &engine) {
  return engine.onLoad();
}

// Stub for your own testing etc.
struct StubEngine : BaseEngine<StubEngine> {
  bool onLoad() const {
    std::cout << "stub onLoad()\n";
    return true;
  }
  bool onFrame() {
    std::cout << "stub onFrame()\n";
    return false;
  }
};

struct BadEngine {};

int main() {
  auto engine = createEngine<StubEngine>();
  // auto bad_engine = createEngine<BadEngine>();
  // error: no matching function for call to 'createEngine'

  const bool f = foo(*engine);      // stub onLoad()
  std::cout << std::boolalpha << f; // true

  return 0;
}

Реализуя полиморфизм stati c с использованием шаблонов, вы можете работать в своей библиотеке lib с параметром шаблона типа T в контексте BaseEngine, не зная, что такое T (как это будет происходить до пользователь библиотеки для реализации конкретных конкретных «статически производных» классов, т. е. спецификаций c T); однако возможно наложение ограничений на T с использованием черт типа и SFINAE.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...