Commit 8a2c633e authored by Hrvoje's avatar Hrvoje

Merge branch 'develop' into 'stable'

Release candidate merge into stable

See merge request aconnoProducts/aconnoLibs/Patterns!31
parents 51ce1cf2 56e5f6fe
......@@ -197,6 +197,87 @@ Observable and Observer that will be connected need to have the same template ar
2. Write any ammount of classes B<> that implement Observer<T>
3. Pass an instance of your A<> class to every B<> instance on construction
## LifetimeList
LifetimeList is a generic collection that can be used without dynamic memory.
New elements are added through one of the factory functions. In the case of the
dynamically generated nodes, smart pointers are used to ensure proper lifetime.
Dynamic and static nodes can be mixed in the same list.
Elements get deleted when they go out of scope with the help of their destructor.
A list has to be emptied before it is destroyed.
### Notes
- LifetimeList is std::iterable<...>.
- the factory functions use perfect forwarding. That means, if T of LifetimeList<T> is constructable, you can pass any arguments that the constructor of T accepts.
- Do not use stack allocated Nodes, they will be swithed out of scope.
- When using statically allocated lists, use lazy loading. This way the constructor is guarenteed to be called before list is used the first time
### Example
The first example shows how to use LifetimeList to make a list of all objects of a class:
```cpp
#include "LifetimeList.h"
/**
* class that has a registry of all instances
*/
class MyClass
{
public:
MyClass() : node(getInstances().appendStatic(*this))
{
}
/**
* lazy loading getter function needs to be used.
* A static list inside this class would not be able to guarantee,
* that the list is constructed before it is used the first time.
*/
static Collections::LifetimeList<MyClass&>& getInstance()
{
static Collections::LifetimeList<MyClass&> instances{};
return instances;
}
/**
* DO NOT DO THIS!
*
* It does not guarantee order of construction!
*/
// static Collections::LifetimeList<MyClass&> instances;
private:
Collections::LifetimeList<MyClass&>::Node node; /**< the node belongs to the object */
};
Collections::LifetimeList<MyClass&> MyClass::instances{};
// instantiate as many as you want, they will all be registered in instances.
static MyClass myObjects[100];
```
The second example shows how to use it as a stand alone list
```cpp
#include "LifetimeList.h"
Collections::LifetimeList<int> iList{}; /**< generic list of integers */
void main()
{
for (int i = 0; i < 10; ++i) {
// static gets created in this scope
auto nodeStatic = list.appendStatic(1);
// dynamic gets created to a smart pointer
auto nodeDynamic = list.pushDynamic(2);
// destroy dynamic node
nodeDynamic.reset();
// make another dynamic node
nodeDynamic = list.pushDynamic(3);
// scope ends here, object and smart pointer get destroyed
}
}
```
## PatternsPort
In order to execute error handling and testing properly, a few forward declarations
......
......@@ -17,7 +17,6 @@
//--------------------------------- INCLUDES ----------------------------------
#include <PatternsPort.h>
#include <cstdint>
#include <cstdlib>
......@@ -39,19 +38,18 @@
#define CHECK_ERROR(err) \
Error::internalCheck(err, \
static_cast<uint32_t>(__LINE__), \
static_cast<const char *const>(__FILE__))
static_cast<const char* const>(__FILE__))
#endif
/**
* @brief basic error definitions and functions.
* Pure static class. Non instantiable.
*/
class Error
{
class Error {
// delete default constructors
Error() = delete;
Error(const Error &other) = delete;
Error &operator=(const Error &other) = delete;
Error() = delete;
Error(const Error& other) = delete;
Error& operator=(const Error& other) = delete;
public:
// structs and enums
......@@ -77,6 +75,12 @@ public:
InvalidUse,
Busy,
StackCorrupt,
InvalidParameter,
Timeout,
MinReached,
MaxReached,
Empty,
Lifetime,
Unknown,
......@@ -92,14 +96,7 @@ public:
*
* @param code
*/
static constexpr void internalCheck(Code code)
{
if (code != Error::None) {
Port::disableInterrupts();
// kill the MCU and force restart
Port::restart();
}
}
static void internalCheck(Code code);
#else
/**
* @brief tests an error code.
......@@ -109,18 +106,9 @@ public:
*
* @param code
*/
static constexpr void internalCheck(Code code,
uint32_t lineNumber,
const char *fileName)
{
if (code != Error::None) {
Port::disableInterrupts();
// report
Port::logError(fileName, lineNumber, getDescription(code));
// kill the MCU and force restart
Port::restart();
}
}
static void internalCheck(Code code,
uint32_t lineNumber,
const char* fileName);
#endif
#ifdef DEBUG
......@@ -133,7 +121,7 @@ public:
* @param code
* @return const char* const
*/
static const char *const getDescription(Code code);
static const char* const getDescription(Code code);
#endif
private:
......@@ -142,7 +130,7 @@ private:
* @brief string error descriptors
*
*/
static const char *const Descriptions[];
static const char* const Descriptions[];
#endif
};
#endif //__ERRORS_H__
\ No newline at end of file
/**
* @file LifetimeList.h
* @author Joshua Lauterbach (joshua@aconno.de)
* @brief generic list whichs nodes get managed by their lifetime
* @version 1.0
* @date 2020-07-10
*
* @copyright aconno GmbH (c) 2020
*
*/
#ifndef __LIFETIMELIST_H__
#define __LIFETIMELIST_H__
//-------------------------------- PROTOTYPES ---------------------------------
namespace Collections
{
template <class T>
class LifetimeList;
}
//--------------------------------- INCLUDES ----------------------------------
#include <iterator>
#include <memory>
namespace Collections
{
//-------------------------------- CONSTANTS ----------------------------------
//---------------------------- CLASS DEFINITION -------------------------------
/**
* @brief Linked list that add's and remove's with Node lifetime.
*
* @warning do not use with stack allocated Nodes. They will be swapped out of scope.
*
* New nodes are added with LifetimeList'a factory functions.
* They are deleted when a Nodes destructor is called.
*/
template <class T>
class LifetimeList {
// delete default constructors
LifetimeList(const LifetimeList& other) = delete;
LifetimeList& operator=(const LifetimeList& other) = delete;
public:
/**
* @brief Holds single values of the LifetimeList.
*
* Entries in the LifetimeList are bound to the Lifetime of the Node.
*/
class Node {
// delete default constructors
Node() = delete;
friend LifetimeList<
T>; /**< needs to be able to access private constructor */
public:
Node(const Node& other) = default;
Node& operator=(const Node& other) = default;
~Node();
private:
template <class... ConstParamTypes>
Node(LifetimeList<T>& list,
Node* insertAfter,
ConstParamTypes&&... parameters);
void insert(Node* insertAfter);
T value; /**< holds the value of the node */
LifetimeList<T>& list; /**< list this node belongs to */
Node*
previous; /**< previous element in the list. nullptr means first element */
Node* next; /**< next element in the list. nullptr is last element */
};
/**
* @brief iterates over a LifetimeList
*
*/
class Iterator :
public std::iterator<std::forward_iterator_tag,
T,
std::ptrdiff_t,
std::remove_reference_t<T>> {
friend LifetimeList; /**< needed for private constructor */
public:
Iterator(const Iterator& other) = default;
Iterator& operator=(const Iterator& other) = default;
private:
Iterator();
Iterator(LifetimeList& lst);
public:
bool operator==(const Iterator& other);
bool operator!=(const Iterator& other);
T& operator*() const;
Iterator& operator++();
private:
Node* current; /**< node at the current position */
};
LifetimeList();
~LifetimeList();
template <class... ConstParamTypes>
Node pushStatic(ConstParamTypes&&... value);
template <class... ConstParamTypes>
Node appendStatic(ConstParamTypes&&... value);
template <class... ConstParamTypes>
std::unique_ptr<Node> pushDynamic(ConstParamTypes&&... value);
template <class... ConstParamTypes>
std::unique_ptr<Node> appendDynamic(ConstParamTypes&&... value);
Iterator begin();
Iterator end();
size_t size();
private:
Node* root; /**< root(first) node of the collection */
Node* tail; /**< tail(last) node of the collection */
};
} // namespace Collections
// includes because this is a template class
#include "../src/LifetimeList.cpp"
#include "../src/LifetimeListIterator.cpp"
#include "../src/LifetimeListNode.cpp"
#endif //__LIFETIMELIST_H__
\ No newline at end of file
......@@ -23,6 +23,7 @@ class Observable;
//--------------------------------- INCLUDES ----------------------------------
#include <forward_list>
#include "Error.h"
#include "Observer.h"
namespace Patterns
......@@ -64,10 +65,8 @@ public:
* @brief unregister given observer.
*
* @param observer
* @return true observer was removed
* @return false observer was not registered in the first place
*/
bool unregisterObserver(
Error::Code unregisterObserver(
Observer<ObservableParamTypes...> &observer) noexcept;
// interface implementation
......
......@@ -18,23 +18,30 @@
//-------------------------------- CONSTANTS ----------------------------------
#ifdef DEBUG
const char *const Error::Descriptions[] = {
[None] = "operation successful",
[NotInitialized] = "module or object not initialized",
[NotFound] = "resource(s) not found",
[ChecksumFailed] = "a checksum did not match",
[AlreadyInit] = "resource already initialized",
[UsingDestroyed] = "trying to use a already freed/destroyed resource",
[Alignment] = "a value is not properly aligned to word or buffer",
[TooLarge] = "supplied or requested value is too large",
[OutOfResources] = "there are no more of the requested resources",
[Memory] = "memory was accessed in invalid way",
[Internal] = "internal error in precompiled module where the code can "
"not be accessed",
[InvalidUse] = "Invalid use of a function",
[Busy] = "resource is busy",
[StackCorrupt] = "stackframe got corrupted (Maybe array out of bounds)",
[Unknown] = "Unknown. This must never happen!"};
const char* const Error::Descriptions[] =
{[None] = "operation successful",
[NotInitialized] = "module or object not initialized",
[NotFound] = "resource(s) not found",
[ChecksumFailed] = "a checksum did not match",
[AlreadyInit] = "resource already initialized",
[UsingDestroyed] = "trying to use a already freed/destroyed resource",
[Alignment] = "a value is not properly aligned to word or buffer",
[TooLarge] = "supplied or requested value is too large",
[OutOfResources] = "there are no more of the requested resources",
[Memory] = "memory was accessed in invalid way",
[Internal] = "internal error in precompiled module where the code can "
"not be accessed",
[InvalidUse] = "Invalid use of a function",
[Busy] = "resource is busy",
[StackCorrupt] = "stackframe got corrupted (Maybe array out of bounds)",
[InvalidParameter] = "parameter supplied to function are invalid",
[Timeout] = "time given for async operation ran out",
[MinReached] = "a minimum value has been undershoot",
[MaxReached] = "a maximum value has been obershoot",
[Empty] = "trying to perform operation on emppty container",
[Lifetime] = "lifetime conditions of an object were violated",
[Unknown] = "Unknown. This must never happen!"};
#endif
//------------------------------ CONSTRUCTOR ----------------------------------
......@@ -42,7 +49,7 @@ const char *const Error::Descriptions[] = {
//------------------------ EXPOSED STATIC FUNCTIONS ---------------------------
#ifdef DEBUG
const char *const Error::getDescription(Code code)
const char* const Error::getDescription(Code code)
{
if (code >= Code::Count) {
// invalid error code
......@@ -52,4 +59,44 @@ const char *const Error::getDescription(Code code)
}
#endif
//------------------------ PRIVATE STATIC FUNCTIONS ---------------------------
\ No newline at end of file
//------------------------ PRIVATE STATIC FUNCTIONS ---------------------------
#ifndef DEBUG
/**
* @brief tests an error code.
* If code is anything else than success, restarts.
*
* @warning do not directly use. Use CHECK_ERROR() instead.
*
* @param code
*/
void Error::internalCheck(Code code)
{
if (code != Error::None) {
Port::disableInterrupts();
// kill the MCU and force restart
Port::restart();
}
}
#else
/**
* @brief tests an error code.
* If code is anything else than success, restarts.
*
* @warning do not directly use. Use CHECK_ERROR() instead.
*
* @param code
*/
void Error::internalCheck(Code code, uint32_t lineNumber, const char* fileName)
{
if (code != Error::None) {
Port::disableInterrupts();
// report
Port::logError(fileName, lineNumber, getDescription(code));
// kill the MCU and force restart
while (true) {
// stay here forever
};
}
}
#endif
/**
* @file LifetimeList.cpp
* @author Joshua Lauterbach (joshua@aconno.de)
* @brief generic list of nodes that get added and removed by their lifetime.
* @version 1.0
* @date 2020-07-10
*
* @copyright aconno GmbH (c) 2020
*
*/
//--------------------------------- INCLUDES ----------------------------------
#include "LifetimeList.h"
#include <Error.h>
//--------------------------- STRUCTS AND ENUMS -------------------------------
//-------------------------------- CONSTANTS ----------------------------------
//------------------------------ CONSTRUCTOR ----------------------------------
/**
* @brief Construct a new, empty Lifetime List
*
*/
template <class T>
Collections::LifetimeList<T>::LifetimeList() : root(nullptr), tail(nullptr)
{}
/**
* @brief Does sanity checks. Non empty lists must not be destroyed!
* All contained nodes have to be destroyed before the list is.
*
* @warning Non empty lists that get destroyed will cause an error.
*
* @tparam T
*/
template <class T>
Collections::LifetimeList<T>::~LifetimeList()
{
if (size() != 0) {
CHECK_ERROR(Error::Lifetime);
}
}
//--------------------------- EXPOSED FUNCTIONS -------------------------------
/**
* @brief factory function to add a node at the start of the list
*
* @warning the added Node only persists until it runs out of scope.
* In that case it removes itself from the list.
*
* @tparam T type of elements in container
* @tparam ConstParamTypes Type of parameters to initialize T object enclosed by Node
* @param parameters parameters to initialize T object enclosed by Node
* @return Collections::LifetimeList<T>::Node new node in the list that contains given object
*/
template <class T>
template <class... ConstParamTypes>
typename Collections::LifetimeList<T>::Node Collections::LifetimeList<
T>::pushStatic(ConstParamTypes&&... parameters)
{
return Node(*this,
static_cast<Node*>(nullptr),
std::forward<ConstParamTypes>(parameters)...);
}
/**
* @brief factory function to add a node at the end of the list
*
* @warning the added Node only persists until it runs out of scope.
* In that case it removes itself from the list.
*
* @tparam T type of elements in container
* @tparam ConstParamTypes Type of parameters to initialize T object enclosed by Node
* @param parameters parameters to initialize T object enclosed by Node
* @return Collections::LifetimeList<T>::Node new node in the list that contains given object
*/
template <class T>
template <class... ConstParamTypes>
typename Collections::LifetimeList<T>::Node Collections::LifetimeList<
T>::appendStatic(ConstParamTypes&&... parameters)
{
return Node(*this, tail, std::forward<ConstParamTypes>(parameters)...);
}
/**
* @brief factory function to add a node at the start of the list
*
* @warning the added Node is heap allocated. Only use when dynamic object lifetime is needed.
*
* @tparam T type of elements in container
* @tparam ConstParamTypes Type of parameters to initialize T object enclosed by Node
* @param parameters parameters to initialize T object enclosed by Node
* @return Collections::LifetimeList<T>::Node new node in the list that contains given object
*/
template <class T>
template <class... ConstParamTypes>
std::unique_ptr<typename Collections::LifetimeList<T>::Node> Collections::
LifetimeList<T>::pushDynamic(ConstParamTypes&&... parameters)
{
return std::unique_ptr<Node>(
new Node(*this,
static_cast<Node*>(nullptr),
std::forward<ConstParamTypes>(parameters)...));
}
/**
* @brief @brief factory function to add a node at the end of the list
*
* @warning the added Node is heap allocated. Only use when dynamic object lifetime is needed.
*
* @tparam T type of elements in container
* @tparam ConstParamTypes Type of parameters to initialize T object enclosed by Node
* @param parameters parameters to initialize T object enclosed by Node
* @return Collections::LifetimeList<T>::Node new node in the list that contains given object
*/
template <class T>
template <class... ConstParamTypes>
std::unique_ptr<typename Collections::LifetimeList<T>::Node> Collections::
LifetimeList<T>::appendDynamic(ConstParamTypes&&... parameters)
{
return std::unique_ptr<Node>(
new Node(*this,
static_cast<Node*>(tail),
std::forward<ConstParamTypes>(parameters)...));
}
/**
* @brief get begin iterator
*
* @return Iterator
*/
template <class T>
typename Collections::LifetimeList<T>::Iterator Collections::LifetimeList<
T>::begin()
{
return Iterator(static_cast<LifetimeList&>(*this));
}
/**
* @brief get end iterator
*
* @return Iterator
*/
template <class T>
typename Collections::LifetimeList<T>::Iterator Collections::LifetimeList<
T>::end()
{
return Iterator();
}
/**
* @brief returns the number of elements in this LifetimeList
*
* @tparam T
* @return size_t
*/
template <class T>
size_t Collections::LifetimeList<T>::size()
{
size_t n = 0;
auto node = begin();
for (; node != end(); ++node) {
++n;
}
return n;
}
//----------------------- INTERFACE IMPLEMENTATIONS ---------------------------
//--------------------------- PRIVATE FUNCTIONS -------------------------------
//---------------------------- STATIC FUNCTIONS -------------------------------
\ No newline at end of file
/**
* @file LifetimeListIterator.cpp
* @author Joshua Lauterbach (joshua@aconno.de)
* @brief iterator for LifetimeList
* @version 1.0
* @date 2020-07-10
*
* @copyright aconno GmbH (c) 2020
*
*/
//--------------------------------- INCLUDES ----------------------------------
#include "LifetimeList.h"
//--------------------------- STRUCTS AND ENUMS -------------------------------
//-------------------------------- CONSTANTS ----------------------------------
//------------------------------ CONSTRUCTOR ----------------------------------
/**
* @brief creates an empty iterator
*
* @tparam T datatype of objects in LifetimeList
*/
template <class T>
Collections::LifetimeList<T>::Iterator::Iterator() : current(nullptr)
{}
/**
* @brief creates an iterator over lst
*
* @tparam T
* @param lst
* @param atEnd false: start in front, true: start at end
*/
template <class T>
Collections::LifetimeList<T>::Iterator::Iterator(LifetimeList<T>& lst)
: current(lst.root)
{}
//--------------------------- EXPOSED FUNCTIONS -------------------------------
/**
* @brief comperator to fulfill the iterator interface
*
* @tparam T
* @param other
* @return true
* @return false
*/
template <class T>
bool Collections::LifetimeList<T>::Iterator::operator==(const Iterator& other)
{
return other.current == current;
}
/**
* @brief comperator to fulfill the iterator interface
*
* @tparam T
* @param other
* @return true
* @return false
*/
template <class T>
bool Collections::LifetimeList<T>::Iterator::operator!=(const Iterator& other)
{
return other.current != current;
}
/**
* @brief returns the encapsuled value of current node
*
* @tparam T
* @return T
*/
template <class T>
T& Collections::LifetimeList<T>::Iterator::operator*() const
{
return current->value;
}
/**
* @brief iterate over the linked list
*
* @tparam T
* @return Collections::LifetimeList<T>::Iterator&
*/
template <class T>
typename Collections::LifetimeList<T>::Iterator& Collections::LifetimeList<
T>::Iterator::operator++()
{
if (current != nullptr) {
current = current->next;
}
return *this;
}
//----------------------- INTERFACE IMPLEMENTATIONS ---------------------------
//--------------------------- PRIVATE FUNCTIONS -------------------------------
//---------------------------- STATIC FUNCTIONS -------------------------------
\ No newline at end of file
/**
* @file LifetimeListNode.cpp
* @author Joshua Lauterbach (joshua@aconno.de)
* @brief implementation of nodes for the LifetimeList class
* @version 1.0
* @date 2020-07-10
*
* @copyright aconno GmbH (c) 2020
*
*/
//--------------------------------- INCLUDES ----------------------------------
#include "LifetimeList.h"
//--------------------------- STRUCTS AND ENUMS -------------------------------
//-------------------------------- CONSTANTS ----------------------------------
//------------------------------ CONSTRUCTOR ----------------------------------