Перейти к содержанию
    

Практическое применение С++ в МК (не Ардуина!)

Начал изучать С++, почитал и просмотрел видеоуроки, кое-что осознал, немного попробовался. Теперь хочу для тренировки написать на С++ один из своих проектов, написанных на С и который уже давно исправно работает. 
Исходно, есть устройство управляющее светом на 10 каналов через 10 реле, управление как кнопками со стационарного блока так и по радиоканалу через NRF24L01. Там еще есть дисплейчик и пищалка, но пока это не важно.

Сейчас я прикидываю, как описать логику классов и их вложенности-наследования. Полагаю, нужен класс Channel, описывающий свойства объекта "канал освещения" и класс Relay, описывающий объект "реле". Радиомодуль, кнопки и прочее пока не рассматриваем. Вопрос в том, должен ли класс Relay наследоваться из класса Channel (class Relay: private Channel { }) или наоборот class Channel: private Relay {}, или даже класс Relay должен быть вложен в класс Channel, или же вообще они не должны быть связаны друг с другом? 
Проблемка еще в том, что у 10 штук реле, понятное дело, есть 10 разных выходов микроконтроллера. Писать в классе Relay 10 разных методов? Вроде как "не наш метод". Каким способом (не слишком замудренным) разделить в классе Relay один метод void Control(bool state) на разные выходы? По-старинке с помощью передачи в параметре номера реле? Но тогда зачем разные экземпляры класса Relay? Пока что придумал у класса Relay хранить идентификатор реле int id, отражающий его номер, а в методе Control реализовывать ветвление switch по номеру идентификатора, чтобы обращаться к разным выходам. Номер id присваивается в параметризованном конструкторе класса Relay.

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Спойлер
/* Класс КАНАЛ */
class Channel {
private:
	bool state;				// состояние канала

	/* Класс РЕЛЕ */
	class Relay {
	private:
		int id;				// ID реле

	public:
		void setID(int id)
		{
			this->id = id;
		}

		void setState(bool state)
		{
			switch(id) {
			case 1:
				if(state) cout << "Реле 1 включено" << endl;
				else cout << "Реле 1 отключено" << endl;
				break;

			case 2:
				if(state) cout << "Реле 2 включено" << endl;
				else cout << "Реле 2 отключено" << endl;
				break;
			}

		}
	} relay;

public:
	Channel(int id)
	{
		relay.setID(id);
	}

	void setState(bool state)
	{
		if(state) cout << "Канал активирован" << endl;
		else cout << "Канал отключен" << endl;
		relay.setState(state);
	}

	bool getState()
	{
		return state;
	}
};

 

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

Ладно, буду дальше пробовать другие способы.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вы не указали, что из литературы Вы изучали. Изучать ООП по видеоурокам имеет смысл только после чтения литературых хороших авторов, те того, что написано профи и "в здравом уме и трезвом рассудке".

Мне понравилась книга Объектно-ориентированное программирование с использованием C++ Айра Пол
https://www.livelib.ru/book/1000912830-obektnoorientirovannoe-programmirovanie-s-ispolzovaniem-c-ajra-pol

там есть прикладные примеры проработки тз, то что у Вас в первом посте.

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Непосредственно сейчас я хочу понять, как применять полученные знания на практике для решения тех задач на микроконтроллерах, которые я раньше писал на Си. В книгах почти всегда описываются примеры для "бальших компутеров". И зачастую именно нужно перевести примеры "оттуда" в "сюда". Я вот тоже видеоматериалы смотрел, там примеры типа class Human { string name; int Age } или class Point... 

Книжку скачал, буду посмотреть, спасибо...

Изменено пользователем Variant99

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Нет, один канал управления - одно реле. Но изначально в Сишном проекте было, что каждый канал управлял одной релюшкой (в реале - контактором), светодиодом состояния в кнопке управления и еще формировал ответную посылку для радиомодуля, чтобы передать состояния в дистанционный пульт. Ну и конечно же делал "пик-пик" пищалкой.

Сейчас вот накидал вот так пока что:

Спойлер
class Relay {
private:
	int id;

protected:
	Relay(int id)
	{
		this->id = id;
	}

	void Control(bool act)
	{
		if(act) cout << "Реле " << id << " включено" << endl;
		else cout << "Реле " << id << " отключено" << endl;
	}

};

class Channel: private Relay {
private:
	bool state;

public:
	Channel(int id) :
			Relay(id)
	{

	}

	void setState(bool state)
	{
		this->state = state;

		Relay::Control(state);

		if(state) cout << "Канал активирован" << endl;
		else cout << "Канал отключен" << endl;
	}

	bool getState()
	{
		return state;
	}
};

 

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

Кстати, я тут подумал... А на кой вообще отдельный класс Relay, если в принципе можно релюшками щелкать в приватных методах класса Channel... Хм... Ну в принципе то да...

Вот, точно! Кчерту класс Relay, будь проще! 🙂 Вот так:

class Channel {
private:
	int id;
	bool state;

	void RelayControl(bool action)
	{
		if(action) cout << "Реле " << id << " включено" << endl;
		else cout << "Реле " << id << " отключено" << endl;
	}

public:
	Channel(int id)
	{
		this->id = id;
		this->state = false;
	}

	void setState(bool state)
	{
		this->state = state;
		RelayControl(state);

		if(state) cout << "Канал " << id << " активирован" << endl;
		else cout << "Канал " << id << " отключен" << endl;
	}

	bool getState()
	{
		return state;
	}
};

 

Изменено пользователем Variant99
подумал и кой-чо придумал

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 hour ago, Variant99 said:

В книгах почти всегда описываются примеры для "бальших компутеров".

Рекомендую данную книгу. Там то, что Вам нужно.

image.thumb.png.b6eee26d512d259d36aed09e01ce3115.png

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А я порекомендую посмотреть в сторону scmRTOS (тем более что ее авторы обитают на форуме). Очень полезно для понимания полезности С++ в МК.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В 12.11.2022 в 15:47, Variant99 сказал:

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

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

/* Класс КАНАЛ */
class Channel {
private:
	bool state;				// состояние канала

	/* Класс РЕЛЕ */
	class Relay {
	private:
		int id;				// ID реле

	public:
        Relay(параметры);
      ...
	};
    Relay relay;

public:
	Channel(int id):
            relay(параметры)
	{
         ...
    }
   ...
};

 

Изменено пользователем gridinp

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Да, я уже нашел ошибку, просто упустил детали.

Покрутившись и попробовав разные варианты и разные комбинации, пришел к выводу, что отдельный класс Relay вроде как и необязателен в этом конкретном случае, достаточно в методе класса Channel вызывать приватную функцию управления портами сразу. Хотя еще не решил, как лучше ногами МК управлять. Вроде и не хочется лишних функций вызывать и лишних классов создавать, но желательно реализацию Low-level уровня иметь отдельно в другом файле. В Си это легко делалось. 

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

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

 

Спойлер
/** 
 * @brief Класс канала освещения
 */
class Channel {
private:
	int id;
	bool state;

	void RelayControl();
public:
	Channel();
	Channel(int id);

	int getID() const;
	bool isState() const;
	void setState(bool state);
};

 

Спойлер
/** 
 * @brief	Получение идентификатора канала.
 * @return ID
 */
int Channel::getID() const
{
	return id;
}

/** 
 * @brief	Получение текущего состояния канала.
 * @return состояние вкл/выкл
 */
bool Channel::isState() const
{
	cout << "Запрос состояния канала #" << id << "... ";
	if(state) cout << "Активен." << endl;
	else cout << "Отключен." << endl;
	return state;
}

/** 
 * @brief	Установка состояния канала.
 * @param state - состояние вкл/выкл
 */
void Channel::setState(bool state)
{
	cout << "Управление каналом #" << id << ": ";

	this->state = state;
	RelayControl();		// Вызов ф-ции управления релюшками

	cout << "канал ";
	if(state) cout << "активен." << endl;
	else cout << "выключен." << endl;
}


/** 
 * @brief	Конструктор без параметров.
 * @details	Каналу назначается ID = 0
 */
Channel::Channel()
{
	state = false;
	id = 0;
	cout << "Создан канал с ID" << id << endl;
}

/** 
 * @brief	Конструктор с назначением указанного ID.
 * @param id - ID канала
 */
Channel::Channel(int id)
{
	state = false;
	this->id = id;
	cout << "Создан канал с ID" << id << endl;
}


/** 
 * @brief	Управление состоянием реле.
 */
void Channel::RelayControl()
{
	cout << "Реле #" << id << " ";
	if(state) cout << "включено, ";
	else cout << "отключено, ";
}

 

Спойлер
/** -------------------------------------------------------
 * @brief	Структура совокупности каналов освещения.
 */
struct Place {
	struct {
		Channel front {Channel(1)};
		struct {
			Channel top {Channel(2)}, bottom {Channel(3)};
		} middle;
		struct {
			Channel top {Channel(4)}, bottom {Channel(5)};
		} back;
	} zone1;

	struct {
		Channel front {Channel(6)};
		struct {
			Channel center {Channel(7)}, sides {Channel(8)};
		} middle;

		struct {
			Channel center {Channel(9)}, sides {Channel(10)};
		} floor;
	} zone2;
};

 

	Place hall;
	
	hall.zone1.middle.bottom.setState(true);
	
	if(hall.zone2.front.isState()) 
		hall.zone2.front.setState(false);
	else 
		hall.zone2.front.setState(true);
Создан канал с ID1
Создан канал с ID2
Создан канал с ID3
Создан канал с ID4
Создан канал с ID5
Создан канал с ID6
Создан канал с ID7
Создан канал с ID8
Создан канал с ID9
Создан канал с ID10

Управление каналом #3: Реле #3 включено, канал активен.

Запрос состояния канала #6... Отключен.
Управление каналом #6: Реле #6 включено, канал активен.

 

Изменено пользователем Variant99
добавил код

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Как итог - если ходите получить быстро работающий код, используйте С. Для относительно несложных проектов более чем достаточно возможностей Си и он гораздо оптимальнее. А если хотите красивости и раскладывания по полочкам, тогда уже идите на С++. Весь этот синтаксический сахар хоть и выглядит местами красиво, но блин ну тупой он и тормознутый. В С++ немало костылей и заплаток наложенных на С и оставшихся там. Например доставшийся в наследие от Си struct. Это тоже самое что и class, только отличается дефолтная область видимости без явного указания. Еще один синтаксический сахар - namespace и синтаксис принадлежности к классу. Ничего особого, просто _ заменили на ::  . С constexp накуевертили конечно тоже сахара. Прикольная штука - конструкторы и деструкторы, но блин надо следить чтобы они не вызывались лишний раз когда не нужно.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

51 minutes ago, Variant99 said:

Если соблюдать принципы "этой вашей инкапсуляции", то язык С++ получается небыстрым.

51 minutes ago, Variant99 said:

Как итог - если ходите получить быстро работающий код, используйте С.

При грамотном проектировании Вы этих замедлений кода не увидите. Ну только если в сильно специфичных местах. ИМХО, тут даже обсуждать нечего. Любой специалист, знакомый с языком, выполнит при необходимости требуемую оптимизацию.

51 minutes ago, Variant99 said:

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

Я думаю, Вам надо разобраться с языком и не за месяц, а потратить на него пару лет, чтобы выдвигать такие поспешные и не совсем верные тезисы.

 

P.S. Попахивает зарождением "холивара". Но я в этом разделе не модератор, поэтому просто предлагаю остановиться и разобраться с инструментом как следует.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 hour ago, Variant99 said:

В общем, после двух недель

 

1 hour ago, Variant99 said:

но блин ну тупой он и тормознутый

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

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

С иерархией объектов всё в поряде, разобрался. Просто к тому, что точно такая же иерархия выстраивается и на обычном Си. Как итог - разницы нет. А если нет разницы, то на кой эта суета то? Синтаксический сахар? Ну да, есть такое. Заплатки навесили и рады. А если кто че там говорит за ускорение, так это доработали компилятор разве что и дописали/переписали стандартные либы. Взять например тип string - жуткая заплатка. Полноценного string не вышло, но налепили заплатку. Синтаксический сахар для тех, кто хочет красивенько оформить кодик.
Возможно, оптимизация происходит при работе со сложными вещами, за счет динамического создания/уничтожения объектов. Хотя скорости это не добавляет, наоборот, отнимает. Но можно более-менее рулить временными объектами. Синтаксический сахар, конечно же, но поданный в хрустальной вазочке вместо простецкой россыпи в Си.
Поскольку С++ приемник Си, то на него налепили кучу заплаток и преподнесли как очень модную плюшечку. Плюшечки местами прикольненькие, можно кушати. Но местами проглядывает и старенькая тухлятинка, которую позабыли выкинути.
Так что вобщем кароче так: С++ - модная плюшка, вместившая в себя няшненькие заплатки поверх архаичного и немодного ныне простого Си. Чтобы низкоуровневый код на С++ работал быстро, надо писать в стиле Си. Чтобы получить красивенькие плюшечки, посыпанные синтаксическим сахарком, надо писать в стиле С++.

haker_fox,  никакого холивара (блин, как убрать выделение полужирным? не выключается чето)

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

Изменено пользователем Variant99

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 hour ago, Variant99 said:

Заплатки навесили и рады

То есть Страуструпа Variant99 ниасилил, поэтому нифига про С++ ни то что не понял, а банально не знает. Обилие оборотов подобных "синтаксический сахар", "няшненькие заплатки" и тому подобных лишь подтверждает это. 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Гость
Эта тема закрыта для публикации ответов.
×
×
  • Создать...