Does anybody know of any resources that provide a straight forward example of trying to do Model View Controller design pattern in a C context? And in particular an embedded system?
To clarify, I am not interested in C#, C++, Objective-C, Java, PHP or any higher level language examples. I want to know what people think about how to approach this design pattern with pure ansi C99 or even C89. Maybe this doesn't even make sense in C because of the lack of formal OOP language constructs?
Some context: my co-workers and I are working on embedded systems powered by Arm based PSoC chips. We have control over the hardware design and PCBs, and have to do software development to enhance our product's feature set. Our model would typically consist of data acquisition from Analog to Digital converters in the product. The views might be a web page powered by an embedded web server, or else an LCD screen with capacitive touch control. Our controllers would more or less be the glue logic that manages the relationship between these two areas of code. We have lots of different products and variations to support so code reuse is desirable.
Not looking for highly detailed or enterprise level frameworks. But rather simple examples that illuminate good strategies for separating the programming concerns, but with a bias toward the idioms found in lower level C, e.g. structs, functions, event driven logic and a kind of abstract message passing that makes sense in C.
Because of the nature of the hardware we need to use C and have to bootstrap a lot of things ourselves. And in some cases we have access to an OS and in other cases just compile straight to processor and start with a main function. All very primitive, but looking for approaches that allow for code reuse and hopefully speed up the software engineering process.
-MVC is an architectural pattern consisting of three parts: Model, View, Controller. Model: Handles data logic. View: It displays the information from the model to the user. Controller: It controls the data flow into a model object and updates the view whenever data changes. -It is invented by Trygve Reenskau.
The Model-View-Controller (MVC) is an architectural pattern that separates an application into three main logical components: the model, the view, and the controller.
Is the framework outdated? ASP.NET MVC is no longer in active development. The last version update was in November 2018. Despite this, a lot of projects are using ASP.NET MVC for web solution development.
In the MVC architecture, developing different view components for your model component is easily achievable. It empowers you to develop different view components, thus limiting code duplication as it separates data and business logic.
Pshew... this might be a long answer... but here goes...
First, let's start with this statement:
Maybe this doesn't even make sense in C because of the lack of formal OOP language constructs?
Couldn't disagree more with that statement. As I'll show later on; just because C doesn't have nifty keywords like "class" doesn't mean you can't accomplish the same things.
I'll try to go through this step-by-step as best I can - following your question's flow.
I suspect, based on the phrasing of your question, that you have a pretty decent grasp of OOP concepts (you're even thinking in terms of patterns and even have a good idea of how those patterns will play out for your particular scenario) - so let me do an "OOP in C" tutorial in "30 seconds or less".
Once you get the hang of things you'll realize there is a lot more you can do than what I'm going to show here - but I just want to give you a taste.
First, we'll start with a basic "class" (go with me on this):
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: (you'll see why I broke this out in a second)
#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); }
Couple things to notice:
Foo
behind an opaque pointer. Other people don't know what is in a Foo
because that implementation detail is in the "internal" header file, not the "public" header. So what if we want a "subclass" of Foo
- which only adds additional functionality - but can be substituted for a Foo
? Simple:
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); }
Now, I should mention, just like we made "initializers" which don't actually call malloc
, but are responsible for initializing the member variables - we also really need deallocators - which don't actually free the struct - but instead free/release any "owning" references, etc. However... I'm actually going to mention something in the section below which might explain why I didn't bother with that yet.
You should notice now - that since our FooSubclass
's first member is, in fact, a Foo
struct - that any reference to a FooSubclass
is also a valid reference to a Foo
- meaning it can be used as such pretty much anywhere.
However, there are a few small issues with this - like I mentioned in the paragraph before last - this technique doesn't actually let you change behavior of the base class. (Something we'd like to do for deallocating our instance, for example).
Let's say we have some method - we'll come up with a random BS example - called calculate
.
We want calling calculate
on a Foo
to return one value - but a different value if it was called on a FooSubclass
.
This is simple in C - it's really just a matter of creating a wrapper method which actually calls a function referenced by a function pointer. OOP languages do this for you behind the scenes and it's usually implemented via what's referred to as a VTable.
Here's an example (I'm going to stop giving complete examples and instead focus on the relevant parts):
First we define the signature of the method. Here we're saying "calculateMethod" is: a pointer to a method which takes one parameter (a pointer) and returns an int.
typedef int (*calculateMethod)(void *);
Next, we add a member variable in our base class which will point to some function:
struct Foo { // ... calculateMethod calc; // ... }
We initialize this with some initial value in the FooInitialize
method (for our base implementation):
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; // ... }
Now we make some way for subclasses to override this method - say, for example, a method declared in the Foo_Internal.h
file called void FooSetCalculateMethod(Foo * this, calculateMethod value);
- and voila! Methods which can be overridden in subclasses.
Our model would typically consist of data acquisition from Analog to Digital converters in the product.
OK - so, Model is probably the easiest thing to implement - simple "classes" which are used as data storage mechanisms.
You'll have to figure something out for your particular scenario (being an embedded system I'm not sure what your exact restrictions will be - and if you're worried about RAM / persistence / etc) - but I think you don't want me to dive into that anyways.
The views might be a web page powered by an embedded web server, or else an LCD screen with capacitive touch control.
For physical things your "view" may be fixed buttons on a control panel - or, like you said, it could be an LCD or HTML.
The bottom line here is you simply need classes which are capable of presenting the rest of your system with a "simple" interface for displaying/changing things in the view - and encapsulate the details of IO to the user.
Typically the "I" part of "IO" needs at least some small wedge of code in the view.
I don't think this is ideal - but, most of the time, there isn't a good way around having your "view" proxy user input back to your controllers. Maybe with your system there is a good way around this - given you have total control.
I hope you can see now how you could easily go about creating some view classes which are relevant to your needs.
Our controllers would more or less be the glue logic that manages the relationship between these two areas of code.
This is usually the guts of the application. You'll likely need more than one controller around at a given time - one for ingress/processing of sensor data, one or more for whatever UI you've got active, and possibly others.
Anyways, I hope that helps... I feel like I'm writing a book now, so I'll stop.
Let me know if you want more, or if that helps at all.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With