Fixed some menus

Added mode documentation files
This commit is contained in:
John Wellbelove 2026-03-28 12:01:25 +00:00
parent a05e870abf
commit 2ececd4fe0
15 changed files with 521 additions and 2 deletions

View File

@ -1,5 +1,6 @@
---
title: "Embedded Template Library"
weight: 998
---
---

47
docs/about.md Normal file
View File

@ -0,0 +1,47 @@
---
title: "About"
weight: 2003
---
*"Do It Once - Do it Right - So you don't have to do it again"*
J Wellbelove : 2014
## Why write this library?
I wrote this library, and all the others I have written over the
years, because I'm lazy.
Yes, lazy!
One of the things I really hate when writing software is having to do the same, or something almost the same, over and over again. The first thing I think when presented with a problem that requires a specific set of functionality is "Is this a specific case of a more generic problem?". Surprisingly, I can say "yes" more often than you would expect. Even if not all of the problem can be seen as generic, there are almost certainly parts that are. In every job I've had I've left an extensive code library behind me.
Reinventing the wheel every time is daft for many reasons.
### Code bloat
Multiple instances of slight variations of a theme results in an increase in code size due to no commonality of functionality.
Testing, more testing or no testing
Are all the variants tested to the same degree? Are some tested at all?
### Variable functionality
Not all the variants are going to have the same level of functionality, or the same API. Ad-hoc solutions are invariably only going to solve the little bit of the problem that was needed at the time.
This goes against the YAGNI1 principle so popular in some programming dogmas.
I think that YAGNI is often just another way of saying ISEP2.
I believe in GIRFT3.
I have over three decades of empirical proof.
### No collective knowledge base
Without commonality every new variant has to be learned. The underlying principles may be understood (i.e. Linked list), but each implementation has to be understood separately, along with its particular caveats and foibles.
Documentation is likely to be patchy, if it exists at all.
### Octopus code
The application is liable to have a close coupling with the solution. For example, I've often seen code using linked lists directly accessing the node pointers. Ad-hoc solutions are liable to have lazy (the bad kind) implementations.
## About me
I have been involved in technology and computer systems for all of my working life and have amassed considerable knowledge of designing and implementing systems that are both performant and correct.
My role normally encompasses the entire project life-cycle, from specification to maintenance phase.
Most systems I have worked on have required high speed and deterministic performance, often within a highly constrained platform. I am experienced in designing and adapting algorithms to solutions that are both space and time efficient, avoiding the normal overheads of standard solutions.
Acting as a mentor for colleagues has often been a significant, though unofficial, part of my role.

View File

@ -1,3 +1,4 @@
---
title: "Callbacks"
weight: 100
---

View File

@ -1,3 +1,4 @@
---
title: "Codecs"
weight: 100
---

View File

@ -1,5 +1,6 @@
---
title: "Containers"
weight: 100
---
## Containers

View File

@ -1,3 +1,4 @@
---
title: "Ranges"
weight: 100
---

View File

@ -1,5 +1,6 @@
---
title: "Releases"
weight: 2002
---
---

View File

@ -1,3 +1,6 @@
---
title: "Source"
weight: 2000
---
---

View File

@ -1,3 +1,4 @@
---
title: "Strings"
weight: 100
---

6
docs/tutorials/_index.md Normal file
View File

@ -0,0 +1,6 @@
---
title: "Tutorials"
weight: 2001
---
---

View File

@ -0,0 +1,86 @@
---
title: "Messages"
---
A tutorial on how the ETL's messaging framework can be used.
## Overview
The messaging framework is designed to allow a powerful, but easy to use, message routing and handling system to be created. It comprises of a collection of messages, message routers, message buses and finite state machines.
Routers, buses and state machines share a unified API that allows different parts of the framework to be mixed and matched.
For example, a message source may pass messages to a message bus. The message bus may then forward messages to a number of other message recipients such as other buses, routers and FSMs. The sender of the message does not need to know the exact type of the receiver, only that it can receive messages.
## Messages
```C++
etl::message
```
All messages are untimately derived from `etl::imessage`.
They are directly derived from the template `etl::message<const etl::message_id_t ID>`.
Each message must have an id that is at least unique to the receiver, though normally ids would be unique across the application.
```C++
enum class MessageId : etl::message_id_t
{
StartMotorId,
StopMotorId
};
struct StartMotor : public etl::message<StartMotorId>
{
};
struct StopMotor : public etl::message<StopMotorId>
{
};
```
Messages are normally passed around as `const references` to `etl::imessage`.
By default, messages are not virtual. This may be changed by defining `ETL_POLYMORPHIC_MESSAGES` either in the project properties or in `etl_profile.h`.
By default the message id is a `uint_least8_t`. This may be changed by defining `ETL_MESSAGE_ID_TYPE` as the required type.
## Message Routers
```C++
etl::message_router
```
Messages routers are defined to handle a specified set of messages. Defining a message router will mandate that a specific set of `on_receive` functions to handle each message type be defined, along with an 'unknown' handler.
Failure to define the full set of handler functions will result in a compile error.
Message routers may either handle the message directly or implement message queues to defer handling.
The message router uses a message packet class for implementing heterogeneous message queues.
See the `QueuedMessageRouter` project in the `examples` directory.
## Message Buses
```C++
etl::message_bus
```
Message buses are a type of message router.
Message buses distribute messages to other message recipients. They do not process any messages.
Other message recipients (those classes derived from etl::imessage_router) may subscribe to the bus.
Messages sent to the bus may either be broadcast or directed only to routers with a specified id.
## Message Timer
```C++
etl::message_timer
```
The message timer is a generator of messages. Up to 254 timers may be defined that can be configured to send a message to a router, bus or FSM at periodic intervals.
## Finite State Machines (FSM & HFSM)
```C++
etl::fsm
etl::hfsm
```
FSMs are also a type of message router.
Messages sent to FSMs are handled in on_event handlers in classes derived from `etl::fsm_state`.
The states are defined to handle a specified set of messages. Failure to define the full set of event functions will result in a compile error. Messages sent to states that have not been define to handle them will call an 'Unknown' handler.
The FSM class uses the state pattern to change message handlers on state changes.
By default the state id is a `uint_least8_t`. This may be changed by defining `ETL_STATE_ID_TYPE` as the required type.

View File

@ -0,0 +1,172 @@
---
title: "Observer"
---
A tutorial on how the ETL's observer pattern can be used.
## The Classes
```C++
etl::observer
etl::observable
```
## Example
This example shows an observable mouse driver that is observed by two event handlers.
```C++
#include <iostream>
#include "observer.h"
// Position data.
struct Position
{
int x;
int y;
};
// Button data.
struct Button
{
enum
{
Down,
Up
};
int state;
};
// Wheel data.
struct Wheel
{
int delta;
};
// Definition of a mouse observer.
typedef etl::observer<const Position&, Button, Wheel> Mouse_Observer;
// Definition of an event handler for mouse notifications.
class Event_Handler1 : public Mouse_Observer
{
public:
// Handle a position notification.
void notification(const Position& position)
{
std::cout << "Event_Handler1 : Position = " << position.x << "," << position.y << "\n";
}
// Handle a button notification.
void notification(Button button)
{
std::cout << "Event_Handler1 : Button = " << ((button.state == Button::Up) ? "Up\n" : "Down\n");
}
// Handle a wheel notification.
void notification(Wheel wheel)
{
std::cout << "Event_Handler1 : Wheel delta = " << wheel.delta << "\n";
}
};
// Definition of a second event handler for mouse notifications.
class Event_Handler2 : public Mouse_Observer
{
public:
// Handler a position notification.
void notification(const Position& position)
{
std::cout << "Event_Handler2 : Position = " << position.x << "," << position.y << "\n";
}
// Handle a button notification.
void notification(Button button)
{
std::cout << "Event_Handler2 : Button = " << ((button.state == Button::Up) ? "Up\n" : "Down\n");
}
// Handle a wheel notification.
void notification(Wheel wheel)
{
// Not interested in wheel deltas.
}
};
// The observable mouse driver.
const int MAX_MOUSE_OBSERVERS = 2;
class Mouse_Driver : public etl::observable<Mouse_Observer, MAX_MOUSE_OBSERVERS>
{
public:
// Notify observers about a position event.
void Position_Event()
{
Position position = { 100, 200 };
notify_observers(position);
}
// Notify observers about a button up event.
void Button_Event_Up()
{
Button button = { Button::Up };
notify_observers(button);
}
// Notify observers about a button down event.
void Button_Event_Down()
{
Button button = { Button::Down };
notify_observers(button);
}
// Notify observers about a wheel up event.
void Wheel_Event_Up()
{
Wheel wheel = { 50 };
notify_observers(wheel);
}
// Notify observers about a wheel down event.
void Wheel_Event_Down()
{
Wheel wheel = { -25 };
notify_observers(wheel);
}
};
int main()
{
Mouse_Driver mouse_driver;
Event_Handler1 event_handler1;
Event_Handler2 event_handler2;
// Tell the mouse driver about the observers.
mouse_driver.add_observer(event_handler1);
mouse_driver.add_observer(event_handler2);
// Generate some events.
mouse_driver.Button_Event_Down();
mouse_driver.Button_Event_Up();
mouse_driver.Position_Event();
mouse_driver.Wheel_Event_Down();
mouse_driver.Wheel_Event_Up();
return 0;
}
```
## Program output
```
Event_Handler1 : Button = Down
Event_Handler2 : Button = Down
Event_Handler1 : Button = Up
Event_Handler2 : Button = Up
Event_Handler1 : Position = 100,200
Event_Handler2 : Position = 100,200
Event_Handler1 : Wheel delta = -25
Event_Handler1 : Wheel delta = 50
```

View File

@ -0,0 +1,188 @@
---
title: "Visitor"
---
A tutorial on how the ETL's visitor pattern can be used.
## The Classes
```C++
etl::visitor
etl::visitable
```
## Example
I'll use the familiar 'Shape' example.
First, you create the base for your 'Shape' visitor.
```C++
//*****************************************************************
// Pre-declare the shapes.
//*****************************************************************
class Square;
class Circle;
class Triangle;
//*****************************************************************
// The shape visitor base class.
// Pure virtual 'visit' functions will be defined for the Square,
// Circle, and Triangle types.
//*****************************************************************
class Shape_Visitor : public etl:visitor<Square, Circle, Triangle>
{
};
```
Then, you define the Shape base class. It derives from the `etl::visitable` class that defines a pure virtual accept function that accepts a `Shape_Visitor`.
```C++
//*****************************************************************
// The shape base class.
//*****************************************************************
class Shape : public etl::visitable<Shape_Visitor>
{
};
```
Next, you define the shapes `Square`, `Circle`, and `Triangle`. Each overrides the accept function that calls the visitor with itself as a parameter.
```C++
//*****************************************************************
// The square class
//*****************************************************************
class Square : public Shape
{
void accept(Shape_Visitor &visitor)
{
visitor.visit(*this);
}
};
//*****************************************************************
// The circle class
//*****************************************************************
class Circle : public Shape
{
void accept(Shape_Visitor &visitor)
{
visitor.visit(*this);
}
};
//*****************************************************************
// The triangle class
//*****************************************************************
class Triangle : public Shape
{
void accept(Shape_Visitor &visitor)
{
visitor.visit(*this);
}
};
```
Now that you have the framework in place, you can do something with it. Here's an example that creates `Draw` and `Serialise` visitors and applies them to a vector of `Shape` objects.
```C++
//*****************************************************************
// The 'draw' visitor.
//*****************************************************************
class Draw_Visitor : public Shape_Visitor
{
public:
void visit(Square &square)
{
std::cout << "Draw the square\n";
}
void visit(Circle &circle)
{
std::cout << "Draw the circle\n";
}
void visit(Triangle &triangle)
{
std::cout << "Draw the triangle\n";
}
};
//*****************************************************************
// The 'serialise' visitor.
//*****************************************************************
class Serialise_Visitor : public Shape_Visitor
{
public:
void visit(Square &square)
{
std::cout << "Serialise the square\n";
}
void visit(Circle &circle)
{
std::cout << "Serialise the circle\n";
}
void visit(Triangle &triangle)
{
std::cout << "Serialise the triangle\n";
}
};
//*****************************************************************
// The actual visitors.
//*****************************************************************
Draw_Visitor draw_visitor;
Serialise_Visitor serialise_visitor;
//*****************************************************************
// The list of shapes.
//*****************************************************************
std::vector<Shape*> shape_list;
//*****************************************************************
// The Apply a visitor.
//*****************************************************************
void Apply(Shape_Visitor &visitor)
{
for (auto pShape : shape_list)
{
// Send the visitor to the shape.
pShape->accept(visitor);
}
}
//*****************************************************************
// Main
//*****************************************************************
int main()
{
// Create some shapes.
Square square;
Circle circle;
Triangle triangle;
// Add them to the vector
shape_list.push_back(&square);
shape_list.push_back(&circle);
shape_list.push_back(&triangle);
// Apply the visitors.
Apply(draw_visitor);
Apply(serialise_visitor);
return 0;
}
```
## Output
The output from the example is as follows.
```
Draw the square
Draw the circle
Draw the triangle
Serialise the square
Serialise the circle
Serialise the triangle
```

View File

@ -29,7 +29,7 @@ html:not(.dark) h2 {
html:not(.dark) h3,
html:not(.dark) h4
{
color: green !important;
color: rgb(29, 172, 29) !important;
border-bottom: 1px solid darkgrey !important;
}

View File

@ -48,4 +48,14 @@ theme = 'hextra'
[[menus.main]]
name = "Home"
pageRef = "/docs"
weight = 10
weight = 1
[[menus.main]]
name = "About"
url = "/about/"
weight = 999
[[menus.main]]
name = "Blog"
url = "/blog/"
weight = 3