LINUX.ORG.RU

Вопрос по структуру приложения

 ,


0

6

Добрый день, подскажите, пожалуйста, как лучше организовать общую структуру приложения. Что имеем: 1. Класс для работы с некой периферией (Class Periph) - занимается тем, что настраивает периферию и принимает с нее данные. 2. Класс для работы с данными полученными от периферии (Class DataParser). 3. Класс для логирования (Class Loging).

Принцип работы такой: пришли данные от периферии - мы их должны положить в очередь класса (DataParser). Потом в этом классе эти данные разбираются и т.д. Вопрос как лучше организовать передачу этих данных? Как-то находил пример подобного приложения, там было сделано так: каждый экземпляр класса был объявлен глобально, но по плюсам обычно крайне не рекомендуют использовать вообще глобальные переменные. Ну и опять же доступ к классу логирования, когда потребность записать лог может возникнуть в любом методе любого класса.

Класс для работы с некой периферией (Class Periph) - занимается тем, что настраивает периферию и принимает с нее данные.

Не один класс на всё, а по классу на тип периферийных устройств.

Класс для работы с данными полученными от периферии (Class DataParser)

Не класс для работы с данными, а отдельно свободные чистые функции для обработки данных, отдельно объекты агрегирующие данные.

Класс для логирования (Class Loging).

Возможно, но посмотри на существующие библиотеки логгирования (log4cplus, Boost.Log и др.).

Вопрос как лучше организовать передачу этих данных?

Через очереди :-) Каждый компонент получает ссылку на свой конец очереди и о том кто на другой стороне ничего не знает (и ему этого не надо). Тут можно посмотреть в сторону библиотек сигналов (QtCore, Boost.Signals2). У Qt сигналы работают между потоками, что может быть полезно.

Begemoth ★★★★★
()

1. Класс для работы с некой периферией (Class Periph) - занимается тем, что настраивает периферию и принимает с нее данные. 2. Класс для работы с данными полученными от периферии (Class DataParser).

Сразу два god object.

tailgunner ★★★★★
()
Ответ на: комментарий от Begemoth

если есть глобальные переменные?

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

no-such-file ★★★★★
()
Ответ на: комментарий от matrixd

ну типо глобальные переменные в ооп не укладываются емнип

Это сродни утверждению, что по геттеру и сеттеру на каждое поле - ООП, а открытые поля - не ООП.

Begemoth ★★★★★
()
Ответ на: комментарий от matrixd

Ну можно обмазаться const

Здорово, а как ты будешь инициализировать свой константный указатель на объект в рантайме, если он константный?

no-such-file ★★★★★
()

В самом простом случае один тупой цикл в главном потоке приложения. В этом цикле сначала снятие данных с устройства, затем обработка данных. Схематически это выглядит так:

int main() {
  logger_t log{ ... };
  peripheral_t device{ log, ... };
  parser_t parser{ log, ... };
  while( true ) {
    auto data = device.receive_data();
    parser.parse( data );
    ...
  }
}

Если этого недостаточно и нужно выносить работу с переферией и парсинг данных в разные потоки, то лучше использовать готовые фреймворки для организации многопоточного взаимодействия. Примитивы для этого можно найти в библиотеках ACE, POCO, C++ Actor Framework, SObjectizer, Intel TBB, Qt.

Для маленьких приложений C++ Actor Framework и SObjectizer применять будет проще всего.

Для логгирования хвалят библиотеку spdlog.

eao197 ★★★★★
()
Ответ на: комментарий от matrixd

Конструкторы и оператор присваивания - в private, либо как deleted (c++11)

А вообще singleton - это больше похоже на попытку притащить концепты из жабы в плюсы. В жапке глобальных переменных нет, вот и выкручиваются там костылями-синглтонами как могут, по сути симулируя именно глобальные переменные.

Это конечно мое мнение, но серьезных аргументов в пользу синглтона на плюсах я пока не слышал.

invy ★★★★★
()
Ответ на: комментарий от matrixd
Log::writeToDb << "entry";
Log::writeToStream << "entry;

где Log - namespace (для красоты), а writeToDb и writeToStream - глобальные объекты, по сути как cout/cerr;

namespace std _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION

  /**
   *  @name Standard Stream Objects
      ....
   */
  extern istream cin;           /// Linked to standard input
  extern ostream cout;          /// Linked to standard output
  extern ostream cerr;          /// Linked to standard error (unbuffered)
  extern ostream clog;          /// Linked to standard error (buffered)

И весь «синглтон» :)

invy ★★★★★
()
Последнее исправление: invy (всего исправлений: 3)
Ответ на: комментарий от invy

А вообще singleton - это больше похоже на попытку притащить концепты из жабы в плюсы.

Да легко что так.

Это конечно мое мнение, но серьезных аргументов в пользу синглтона на плюсах я пока не слышал.

Ну тут же кому что нравится.

matrixd
()
Ответ на: комментарий от invy

А вообще singleton - это больше похоже на попытку притащить концепты из жабы в плюсы.

Отнюдь. Singleton - это средство от «static initialization order fiasco»

annulen ★★★★★
()
Ответ на: комментарий от annulen

Хмм, а такой вариант:

template<typename T>
class delay_init
{
public:

  auto operator*() -> T& 
  {  
    if (!val) val = std::make_unique<T>();
    return *T;
  }

private:
   std::unique_ptr<T> val;
};

не решение? потокобезопасность, константность и произвольные аргументы конструктора добавляются без проблем (и std::unique_ptr можно заменить на boost::optional или будущий std::optional). При этом получаем переиспользуемый класс для отложенной инициализации, а не только идиому синглтонов.

Begemoth ★★★★★
()
Последнее исправление: Begemoth (всего исправлений: 1)
Ответ на: комментарий от annulen

Singleton - это средство от «static initialization order fiasco»

Или замена его на «dynamic initialization order fiasco»

tailgunner ★★★★★
()
Ответ на: комментарий от invy

В синглтоне тоже перегружать надо

Лол что? С синглтоном ты работаешь через методы-аксессоры. Нет ну если тебе припекает непременно сделать лишнюю работу, то можно конечно и прикрутить перегруженные операторы, но нахрена?

no-such-file ★★★★★
()
Ответ на: комментарий от Begemoth

Костылять?

Но ведь объект уже имеет аксессоры для доступа к элементам, а операторы нужно ещё накостылять, причём всё равно тебе придётся внутри них придумывать синглтон, только через жопу, т.е. вместо того, чтобы поместить конструктор в private, ты будешь руками писать код, который запретит использовать объект данного типа в lvalue. omfg Ты не забыл зачем всё это вообще делается, что это не ради олимпиады на лоре? Твои же коллеги набьют тебе морду, как только им придётся разбираться в этом говнище.

no-such-file ★★★★★
()
Ответ на: комментарий от invy

больше похоже на попытку притащить концепты из жабы в плюсы

И что в этом плохого? Слишком просто, а не через задницу и нет шаблонов на шаблонах в шаблонах?

но серьезных аргументов в пользу синглтона

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

на плюсах

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

no-such-file ★★★★★
()
Ответ на: комментарий от no-such-file

Ты дурак? Так и должно быть, ради этого всё и делается.


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

Слив засчитан.

invy ★★★★★
()
Последнее исправление: invy (всего исправлений: 2)
Ответ на: комментарий от invy
  A x = A::getInstance(); // fail...

смотря что там твой этот метод вернет.

A *x = A::getInstance();
Вполне прокатит

  A::getInstance() = A(); // epic fail...

Это вообще трэш. Ты либо думай как делать проверку не создается ли объект повторно, либо прячь конструктор. В любом случаи эти ошибки в процессе компиляции обнаружатся.

matrixd
()
Ответ на: комментарий от invy

Слив засчитан

Так ты даже русским не владеешь и не можешь донести свою мысль до собеседника. Нормальные люди как бы подразумевают, что в синглтоне

 T(T const&);
 void operator=(T const&);
спрятано в private - тут никакой дополнительной работы (писать реализацию) делать не надо.

no-such-file ★★★★★
()
Ответ на: комментарий от no-such-file

спрятано в private

В новом коде уже пишут «= delete».

anonymous
()

Ого) Столько ответов) Спасибо за пайтон, лисп и т.п но это встраиваемая система, по этой же причине либы для логирования пролетают. Вариант с асмом подходит, но тоже нет, спасибо) Но что-то как-то по существу ответов и не было:(

hell_wood
() автор топика
Ответ на: комментарий от hell_wood

1. QSerialPort 2. TheMyDataParseClass (слушает события QSerialPort) 3. qDebug()

но это встраиваемая система

встраиваемые системы разные бывают, если не потянет qt, перепеши на свои велосипеды

anonymous
()
Ответ на: комментарий от invy

что у тебя аргументы кончились

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

no-such-file ★★★★★
()
Ответ на: комментарий от anonymous

Сигналы-события да интересно, но как быть с многопоточностью (тут вопрос да не в стороне embedded ). Например Class Periph принимает данные в одном потоке, а Class DataParse должен разбирать данные в другом потоке. Если брать qt то там все сигналы на сколько я знаю в главном потоке обрабатываются.

hell_wood
() автор топика

IoC в С++

Вот я запустил тред, там меня все обсирали. А в этом треде такие же плюсовики советуют кучу гадостей и спорят.

vertexua ★★★★★
()

Почитай Dependency Injection и умоляю, не слушай грамотеев из этого треда, которые вылезли из пещер и советуют всякие синглтоны вроде MyClass::GetInstance(), это просто фантастически бездарно и тупо.

vertexua ★★★★★
()
Последнее исправление: vertexua (всего исправлений: 1)
Ответ на: комментарий от invy

В жабке такие проблемы как в плюсах все обсуждали в 1995 году. За вот такой синлтон как ты тут советуеш могут отрубить руки

vertexua ★★★★★
()

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

Не хочешь — не используй. Алсо «не рекомендуется» — еще не значит «ты не сможешь выстрелить себе в ногуесли захочешь»

slackwarrior ★★★★★
()
Ответ на: комментарий от eao197

ACE, POCO, C++ Actor Framework, SObjectizer, Intel TBB, Qt

Какая ненавязчивая самореклама :)

Для маленьких приложений

std::thread не, если уж сказали auto? Может и доп. зависимости не понадобятся. От слова вообще.

slackwarrior ★★★★★
()
Ответ на: комментарий от vertexua

Могут хотеть отрубить руки :) Ну и... могут продолжать хотеть :)

slackwarrior ★★★★★
()
Последнее исправление: slackwarrior (всего исправлений: 1)
Ответ на: комментарий от no-such-file

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

 T(T const&);
 void operator=(T const&);

invy ★★★★★
()
Ответ на: комментарий от no-such-file

наверное, мсье имеет в виду лейзи лоад при первом вызове геттера инстанца, который подобных телодвижений не требует вообще :)

slackwarrior ★★★★★
()
Ответ на: комментарий от no-such-file

В шаблоне не надо писать реализацию... Спасибо, кэп. Но и инициализацию снаружи делать не обязательно :)

slackwarrior ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.