-
Notifications
You must be signed in to change notification settings - Fork 0
embedded c
- (Embedded) C/C++ cheat sheet
- RTOS
For peripheral access use volatile for register definitions in order to assure the compiler generates the access
correctly.
Scheme:
(dest)src
Scheme:
x_cast<dest>(src)
-
static_cast: Most cases- No overhead (memory/run-time) -> compile time
-
dynamic_cast: virtual functions -> safe down-cast- Inheritance and using virtual functions must be enabled/available
- ~+ 2kB
-
dynamic_pointer_cast: for smart pointers (dynamic cast does not work here) -
const_cast: castsconst+volatileaway- No overhead (memory/run-time) -> compile time
-
reinterpret_cast: complete free address-based cast, casts of function pointers, data, memory mangaement, ...
Prefer forward declaration over includes
-
*a- In declarations: "pointer to
a" - In expressions: "contents of
a"
- In declarations: "pointer to
-
&a- In declarations: "reference to
a" - In expressions: "address of
a"
- In declarations: "reference to
Pass by reference (to avoid copying big objects) but assure that it will not be changed.
void sort(const vector<int>& a);// std::function can store, copy, and invoke any callable (func, lambda, expression, bind expression, func obj, ...)
#include <functional>
void someFunc(int num, int b) { /* ... */ }
std::function<void(int, int)> func = someFunc; // Assign function to std::function object
func = { /* some lambda */ }; // redefine std::function with constexpr
func(10, 5);
// std::bind creates a new function object that can adapt its function parameters (usually you bind one or more arguments to placeholders)
auto func = std::bind(someFunc, std::placeholders::_1, 5); // First argument (-> placeholder) still as argument but second we bind to `5`
func(10); // num = 10- Set pointer to
nullptr/NULLafter
MyObj* myObjPtr = (MyObj*)malloc(sizeof(MyObj));
// -
malloc()returns non-typed pointer (void*) -> type cast to typed pointer - Memory allocated via
malloc()is not initialised (in contrast tonew)
// Typically you would not do one try-catch block per `new` but place it wisely where you can handle the error
try {
MyObj* myObjPtr = new MyObj;
// ...
myObjPtr->foo();
}
catch(std::bad_alloc& e) { // Check if new was successfull
// Handle error
}
myObjPtr = nullptr;
// or (without exception handling)
MyObj* myObjPtr = new (std::nothrow) MyObj; // Returns a `nullptr`/`NULL` if not successfull
if (!myObjPtr) { // or better: `if(myObjPtr == nullptr)`
// Handle error
}
myObjPtr = nullptr;
delete myObjPtr; // Safe on `nullptr`-
newreturns typed pointer - Constructor gets automatically called (-> memory is initialised)
-
newwithoutdeleteleads to memory fragmentation - Embedded: pre-allocate all (via
new) during boot -> Better run-time performance - "placement new" (
#inlude <new>): Store object at arbitrary position (register, heap, ...) ->Ptr* objPtr = new(ptr) Obj(); //... objPtr->~Obj();-> nodeletepossible! -> deallocate + call destructor manually
#include <cstring> // strlen()
#include <string> // std::char_traits<char>::length()
const char* cStr = "blub";
char* charArray = new char[strlen(cStr) + 1]; // C str + `\0` OR `std::char_traits<char>::length(cStr)` -> C++
int* intArray = new int [4]; // int array with
delete[] charArray; // `[]` otherwise only the first Element gets deleted
delete[] intArray;struct myStruct {
unsigned long DATA;
unsigned long FLAG;
};Declare a struct variable named var:
struct myStruct var;Use struct myStruct as a type:
-
typedef struct myStruct UART_TypeDef("defineUART_TypeDefas the type name forstruct myStruct") -
typedef struct myStruct { ... } UART_TypeDef;(same as above)
Same as anonymous struct:
typedef struct {
<...>
} UART_TypeDef;Through a pointer (dot operator):
UART_TypeDef *ptr_uart
// Assign an address of a struct to the pointer
// &addr = 0x40000000 (struct named `addr` at add address 0x40000000)
ptr_uart = &addr;
// or
ptr_uart = 0x40000000;
// Assign a value
ptr_uart.DATA = 40;Directly (arrow/membership operator):
// Assign address to pointer explicitely
#define UART0 ((UART_TypeDef *) 0x40000000)
// Assign a value
UART0->DATA = 40;Tells the compiler that the function or variable is defined somewhere else (=> external linkage). Thus, extern extends the visibility to a global scope of those variables/functions.
// aVar is global now
extern int aVar;extern "C" & extern "C++":
Means that C/C++ linkage will be used.
-
extern "C": do not mangle names in C++ code (when omitted the ISR possibly cannot find the compiled function name due to name mangling (for exampleextern "C" void SystemInit(void);) - `extern "C++": do name mangling in C code (use C++ features in C code)
extern "C" {
// Use C linkage for these functions
void func1();
void func2();
}
// or
extern "C" void func3();
// or
// To use C++ features (namespaces) in C code
extern "C++" void func4(const std::string &);Due to the possibility of overloading functions in C++, C++ modifies the function names (adds argument type information in the name for linkage) during the compilation process to keep function names unique. This is not done with C code.
Say you want to create some kind of framework containing some stubs. You want the user to later define this function without modifying the code of your framework (obviously).
weak_func.c:
__WEAK int func(){
return 1;
}This function is called in main.c:
int func();
int main(){
func();
while(1);
}The user adds in a new file his implementation of the function ("strong" function) - so he "overwrites" the stub:
func.c:
int func(){
// user implementation
return aValue;
}Normally you would get a linker error since you have defined func() multiple times. __WEAK prevents that by throwing away the as __WEAK defined function.
-> For GCC it is __attribute__((weak)) instead of __WEAK
#include "Counter.h"
void count() { /* ... */ } // global count()
int main(void) {
using SomeCounter::count;
// ...
aCounter.count(); // count() of SomeCounter
::count(); // global count()
}namespace sc = SCout; // Make SCout available under a different name
sc::print()Annonymous namespaces act like a static keyword within its scope.
// blah.h
namespace {
int x = 42; // Only visible in `blah.h`
}
int askMe() { return x; } // Access to `x` since it is within the same compilation unit
// main.cpp
#include "blah.h"
int main(void) {
int answer = askMe(); // answer = 42
}No additional run-time + memory costs -> handling during compile time.
- Inheritance is mostly not the way to go for code reuse
- Composition is for code reuse, inheritance for flexibility
- Class containing one or more pure virtual operatorations are not instantiable
- Diamond (A<-(B C)<-D): If B and C implement the same method you have to use the scope operator to access it (otherwise compiler error):
B::myFunc(),C::myFunc(), but notmyFunc(). - Best practice: 1 actual class, 1-n interfaces
Make constructor protected if class is abstract -> only callable from derived class (using delete in this case is not useful)
class Blah {
public:
Blah() {}
~Blah() = delete; // Memory reductions without destructor:
// ~ 44 Bytes Code, ~ 12 Bytes RO
virtual void func() = 0; // Pure virtual (=0)
}
*Destructor in derived classes*: Compiler only sees the base class pointer and, hence, calls only the base class destructor. **Solution: make destructor `virtual` (-> `virtual ~Blah() { /* ... */ }`)** => **If using virtual *methods* in your base class** (this is optional for derived classes) **always make the destructor virtual too**.
### `private` inheritance
Alternative form of aggregation.
```c++
class Derived: private Base {
public:
// ...
using Base::aFunc(); // Make privately inherited function publicly available
}- Only memory for a base class object is allocated
- Each instance of two derived classes share the object they inherited from (= base class, only one object). Non-
virtualwould result in independent base class attributes and methods (-> base class = two objects).- Note: You have to call all constructors of all base classes manually in the derived classes constructor (otherwise the default constructor will be called):
- Solves the problem if you inherit from multiple classes which have the same base class (ambiguity since you two times the "base base class" attributes and methods)
class DerivedClass : virtual public BaseClass {
public:
DerivedClass() : BaseClass() {}
}template <class T>
T func(T par) {
// ...
}-
class==typename
// A generic templated function
template <class T>
void foo(T bar) {
// ...
}
// Template specialisation
template <>
void foo<char>(char bar) { // foo's implementation is different for `char`s
// ...
} <!–– TODO: Add efficiency (run-time + memory) examples TODO: Add exception handling TODO: Add + complete memory management TODO: Add dyn. polymorphism + RTTI TODO: Add + extend templates ––>
This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License *.
Code (snippets) are licensed under a MIT License *.
* Unless stated otherwise
