Пшо ... это может быть длинный ответ ... но здесь идет ...
Сначала давайте начнем с этого утверждения:
Maybe this doesn't even make sense in C because of the lack of formal OOP language constructs?
Не могу не согласиться с этим утверждением. Как я покажу позже; просто то, что в C нет изящных ключевых слов, таких как «класс», не означает, что вы не можете выполнить то же самое.
Я постараюсь пройти этот пошаговый шаг как можно лучше - следуя потоку вашего вопроса.
ООП в С
Я подозреваю, что, основываясь на формулировке вашего вопроса, вы достаточно хорошо понимаете концепции ООП (вы даже думаете о шаблонах и даже хорошо представляете, как эти шаблоны будут действовать для вашего конкретного случая). сценарий) - так что позвольте мне сделать урок "ООП в C" за "30 секунд или меньше".
Как только вы освоитесь, вы поймете, что вы можете сделать гораздо больше, чем то, что я собираюсь показать здесь, - но я просто хочу дать вам вкус.
101
Сначала мы начнем с базового «класса» (пойдем со мной в этом):
foo.h:
typedef struct Foo Foo;
Foo * FooCreate(int age, int something);
void FooSetAge(Foo * this, int age);
void FooFree(Foo * this);
Foo_Internal.h: (вы поймете, почему я сломал это через секунду)
#include "Foo.h"
struct Foo {
int age;
int something;
};
void FooInitialize(Foo * this, int age, int something);
Foo.c:
#include "Foo_Internal.h"
// Constructor:
Foo * FooCreate(int age, int something) {
Foo * newFoo = malloc(sizeof(Foo));
FooInitialize(newFoo);
return newFoo;
}
void FooInitialize(Foo * this, int age, int something)
{
this->age = age;
this->something = something;
}
// "Property" setter:
void FooSetAge(Foo * this, int age) {
this->age = age;
}
void FooFree(Foo * this) {
// Do any other freeing required here.
free(this);
}
Пара вещей, на которые стоит обратить внимание:
- Мы скрыли детали реализации
Foo
за непрозрачным указателем. Другие люди не знают, что находится в Foo
, потому что эта деталь реализации находится во внутреннем заголовочном файле, а не в общедоступном заголовке.
- Мы реализуем «методы экземпляра» точно так же, как это делает язык ООП - за исключением того, что мы должны вручную передавать указатель «this» - другие языки просто делают это для вас - но это не имеет большого значения.
- У нас есть "свойства". Опять же, другие языки будут заключать методы получения / установки свойств в более приятный синтаксис - но все, что они действительно делают за кулисами, - это создание некоторого метода получения / установки для вас и преобразование вызовов «свойств» в вызовы методов.
Наследование
Так что, если нам нужен «подкласс» Foo
- который только добавляет дополнительную функциональность - но может заменить Foo
? Простой:
FooSubclass.h:
typedef struct FooSubclass FooSubclass;
FooSubclass * FooSubclassCreate(int age, int something, int somethingElse);
void FooSubclassSetSomethingElse(FooSubclass * this, int somethingElse);
void FooSubclassFree(FooSubclass * this);
FooSubclass_Internal.h:
#include "FooSubclass.h"
#include "Foo_Internal.h"
struct FooSubclass {
Foo base;
int something;
};
void FooSubclassInitialize(FooSubclass * this, int age, int something, int somethingElse);
FooSubclass.c
#include "FooSubclass_Internal.h"
// Constructor:
Foo * FooSubclassCreate(int age, int something, int somethingElse) {
FooSubclass * newFooSubclass = malloc(sizeof(FooSubclass));
FooSubclassInitialize(newFooSubclass, age, something, somethingElse);
return newFooSubclass;
}
void FooSubclassInitialize(FooSubclass * this, int age, int something, int somethingElse) {
FooInitialize(this, age, something);
this->somethingElse = somethingElse;
}
void FooSubclassSetSomethingElse(Foo * this, int somethingElse)
{
this->somethingElse = somethingElse;
}
void FooSubclassFree(FooSubclass * this) {
// Do any other freeing required here.
free(this);
}
Теперь, я должен упомянуть, точно так же, как мы сделали «инициализаторы», которые на самом деле не вызывают malloc
, но отвечают за инициализацию переменных-членов - нам также действительно нужны деаллокаторы - которые на самом деле не освобождают структуру - но вместо этого освободите / освободите любые «собственные» ссылки и т. д. Однако ... я на самом деле собираюсь упомянуть кое-что в разделе ниже, которое может объяснить, почему я еще не беспокоился об этом.
Теперь вы должны заметить - поскольку первый член нашего FooSubclass
, по сути, является структурой Foo
, - любая ссылка на FooSubclass
также является действительной ссылкой на Foo
, то есть может использоваться как таковой в любом месте.
Однако с этим есть несколько небольших проблем - как я уже упоминал в предыдущем абзаце - этот метод на самом деле не позволяет изменить поведение базового класса. (Что-то, что мы хотели бы сделать для освобождения нашего экземпляра, например).
Полиморфизм
Допустим, у нас есть некоторый метод - мы придумаем случайный пример BS - с именем calculate
.
Мы хотим вызвать calculate
для Foo
, чтобы вернуть одно значение - но другое значение, если оно было вызвано для FooSubclass
.
Это просто в C - на самом деле это просто вопрос создания метода-обертки, который фактически вызывает функцию, на которую ссылается указатель на функцию. Языки ООП делают это для вас за кулисами, и это обычно реализуется через то, что называется VTable .
Вот пример (я собираюсь прекратить давать полные примеры и вместо этого сосредоточиться на соответствующих частях):
Сначала мы определим сигнатуру метода. Здесь мы говорим, что «CalculateMethod» это: указатель на метод, который принимает один параметр (указатель) и возвращает int.
typedef int (*calculateMethod)(void *);
Затем мы добавляем переменную-член в наш базовый класс, которая будет указывать на некоторую функцию:
struct Foo {
// ...
calculateMethod calc;
// ...
}
Мы инициализируем это некоторым начальным значением в методе FooInitialize
(для нашей базовой реализации):
int FooCalculate(Foo * this)
{
this->calc(this);
}
int FooCalculateImplementation(void * this)
{
Foo * thisFoo = (Foo *)this;
return thisFoo->age + thisFoo->something;
}
void FooInitialize(Foo * this, ...)
{
// ...
this->calc = &FooCalculateImplementation;
// ...
}
Теперь мы делаем способ для подклассов переопределить этот метод - скажем, например, метод, объявленный в файле Foo_Internal.h
с именем void FooSetCalculateMethod(Foo * this, calculateMethod value);
- и вуаля! Методы, которые могут быть переопределены в подклассах.
Модель
Our model would typically consist of data acquisition from Analog to Digital converters in the product.
ОК - так, модель, пожалуй, самая простая вещь для реализации - простые «классы», которые используются в качестве механизмов хранения данных.
Вам придется что-то придумать для вашего конкретного сценария (будучи встроенной системой, я не уверен, какими будут ваши точные ограничения - и если вы беспокоитесь о RAM / постоянстве / и т.д.) - но я думаю, что вы не хочу, чтобы я погрузился в это в любом случае.
View
The views might be a web page powered by an embedded web server, or else an LCD screen with capacitive touch control.
Для физических вещей ваш «вид» может быть фиксированными кнопками на панели управления - или, как вы сказали, это может быть LCD или HTML.
Суть здесь в том, что вам просто нужны классы, которые способны представить остальную часть вашей системы с «простым» интерфейсом для отображения / изменения объектов в представлении - и инкапсулировать детали ввода-вывода для пользователя.
Как правило, часть «I» в «IO» нуждается в некотором небольшом фрагменте кода в представлении.
Я не думаю, что это идеальный вариант, но в большинстве случаев нет хорошего способа вернуть пользовательский ввод "view" прокси обратно на контроллеры. Может быть, с вашей системой есть хороший способ обойти это - учитывая, что вы имеете полный контроль.
Надеюсь, теперь вы понимаете, как можно легко создать классы представлений, соответствующие вашим потребностям.
Контроллер
Our controllers would more or less be the glue logic that manages the relationship between these two areas of code.
Обычно это внутренности приложения. Скорее всего, вам потребуется более одного контроллера в данный момент времени - один для ввода / обработки данных датчика, один или несколько для любого пользовательского интерфейса, который у вас активен, и, возможно, другие.
В любом случае, я надеюсь, что это поможет ... Я чувствую, что сейчас пишу книгу, поэтому я остановлюсь.
Дай мне знать, если ты хочешь больше или это поможет.