Шаблон библиотеки [требует правки]

251
15 мая 2017, 01:16

Здравствуйте. Пишу софт для встраиваемых систем.Начал переходить с Си на Си++. Разработывая библиотеки на с++ я заметил, что часть кода в каждой из моих библиотек повторяется, а именно часть привязки каждой библиотеки к общей энергнезависимой памяти и энергозависомй памяти доступной из вне. В итоге формируется общая таблица данных доступных по протоколу ModBus и тд.

Таким образом добавляя ту или иную библиотеку в проект ее данные становятся доступными для изменения и считывания по ModBus.

Вопрос в следующем, как лучше организовать код, чтобы не нужно было писать одинаковый код для каждой новой библиотеки?

Я начал с создания базового класса для всех библиотек в котором описаны общие для всех библиотек функции, а каждая из библиотек наследуя базовый описывает свои особенные виртуальные методы. У меня получилось, что все созданные объекты любого унаследованного от базового класса становятся в общую очередь, в которой происходит вызов их виртуальных функций в Main.

Однако я чувствую, что поступаю неверно и есть способ лучше. Подскажите, как лучше реализовать данную задачу?

Answer 1

Вопрос в следующем, как лучше организовать код, чтобы не нужно было писать одинаковый код для каждой новой библиотеки?

Вариантов проектирования, как правило, бывает множество. Учитывая, что используется С++, желательно использовать некоторые "правила хорошего тона" ООП. В вашем случае - "разделение ответственности". Как я понял из постановки вопроса - "привязка" не есть основная задача библиотек. Так вынесите этот функционал в отдельный класс (например паттерн "фабрика"). Если "привязка" - функционал глобальный, имеет смысл использовать паттерн "синглтон".

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

#include <iostream>
#include <memory>
////////////////////////////////////////////////////////////////////////////////////////
template<typename T>
struct Holder {
  Holder() {
    std::cout << "Holder()::Holder()" << std::endl;
    if (!Self)  Self = new T();
    Self->Init();
  }
  ~Holder() {
    if (Self) {
      Self->Cleanup();
      delete Self;
      std::cout << "~Holder()::Holder()" << std::endl;
    }
  }
  T* Self = nullptr;
};
////////////////////////////////////////////////////////////////////////////////////////
template <typename T>
class Singleton {
  public:
    static T* Instance() {
      std::cout << "Singlton::Instance()" << std::endl;
      static Holder<T> Dummy;
      return Dummy.Self;
    }
  private:
    Singleton() = delete;
    Singleton(Singleton const&) = delete;
    Singleton& operator= (Singleton const&) = delete;
    Singleton(Singleton const&&) = delete;
    Singleton& operator= (Singleton const&&) = delete;
};
////////////////////////////////////////////////////////////////////////////////////////
class Base {
  public:
    virtual void Init() { std::cout << "Base::Init()" << std::endl; }
    virtual void Cleanup() { std::cout << "Base::Cleanup()" << std::endl; }
};
////////////////////////////////////////////////////////////////////////////////////////
class Config: public Base {
  public:
    int i = 0;
    void Init() override { std::cout << "Config::Init()" << std::endl; }
};
////////////////////////////////////////////////////////////////////////////////////////
class ConfigDerived: public Config {
  public:
    int j = 0;
    void Cleanup() override { std::cout << "ConfigDerived::Cleanup()" << std::endl; }
};
////////////////////////////////////////////////////////////////////////////////////////
int main() {
  std::cout << "===================================" << std::endl;
  auto R1 = Singleton<Config>::Instance();
  auto R2 = Singleton<ConfigDerived>::Instance();
  std::cout << "R1->i: " << R1->i << std::endl;
  R1->i = 7;
  std::cout << "R2->i: " << R2->i << std::endl;
  std::cout << "===================================" << std::endl;
  R2->i = 3;
  auto XX = Singleton<ConfigDerived>::Instance();
  std::cout << "XX->i: " << XX->i << std::endl;
  std::cout << "===================================" << std::endl;
  return 0;
}
Answer 2

Фактически ты пишешь сво собственный API, это возможный вариант.

В принципе есть три выхода:

  1. Написать низкоукровневый API использующий "C" связывание, и две обертки для каждого языка. Недостаток - придется отладить обе обертки. преимущество - в некоторых случаях легче поддерживать, если вносимые изменения касаются только части API, но не интерфейсов.
  2. Написать полноценную библиотеку для Си, отладить, написать обертку для С++. Вопрос - нужна ли обертка вообще и для чего?
  3. Создать обертку из "псевдо-классов", так сказать эмулировать Симула-подобное ОПП в Си. Нужно быть аккуратным чтобы не допустить UB.

Как ни странно звучит последний вариант, на самом деле он достаточно часто встречается. в различном банковском и промышленном ПО.

READ ALSO
Класс std::ifstream, метод получения имени файла

Класс std::ifstream, метод получения имени файла

Доброго времени сутокПокопал много страницы учебников/интернета, но так и ничего не нашёл

279
Создание собственного списка c++

Создание собственного списка c++

Здравствуйте, я пытаюсь воспроизвести похожее на list из библиотеки stl

320
Перегрузка operator[]

Перегрузка operator[]

Вот часть кода, помогите пожалуйста реализовать перегрузку оператора []

219
Умножение в столбик

Умножение в столбик

Как написать оператор *= для длинных беззнаковых чисел методом умножения в столбик (длинная арифметика) без использования оператора *, если...

300