├── Containers ├── Readme.md ├── StackContainer.h ├── QueueContainer.h └── ArrayContainer.h └── README.md /Containers/Readme.md: -------------------------------------------------------------------------------- 1 | # Containers 2 | This subfolder includes the container libraries written in C++. 3 | Rules from the main folder is also valid at this subfolder. 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Embedded-System-Libraries 2 | A repo for storing any kind of libraries related to embedded systems. 3 | Classification is done with the help of sub-folders. 4 | Each library is documented with doxygen. 5 | Device dependency will be given at the very beginning of each file. 6 | Designs are open to use and contributions. 7 | -------------------------------------------------------------------------------- /Containers/StackContainer.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file StackContainer.h 3 | * @details A template stack container class for embedded systems. 4 | * The container is implemented without any dynamic allocation feature. 5 | * @author Caglayan DOKME, caglayandokme@gmail.com 6 | * @date July 10, 2021 -> First release 7 | * July 11, 2021 -> Data buffer replaced with uint8_t array to support 8 | * data types without default constructor. 9 | * July 15, 2021 -> Data storage replaced with std::aligned_storage 10 | * -> capacity() method added. 11 | * July 16, 2021 -> Member variable sz removed as it is easily derivable from idxTop 12 | * -> Missing destructor added. 13 | * -> lvalue ref-qualifier added to assignment operator. 14 | * -> Helper function at(..) added to avoid repetition of casting. 15 | * July 17, 2021 -> Swap issue fixed with manual swapping of the storage. 16 | * 17 | * @note Feel free to contact for questions, bugs or any other thing. 18 | * @copyright No copyright. 19 | */ 20 | 21 | /*** Recursive inclusion preventer ***/ 22 | #pragma once 23 | 24 | /*** Libraries ***/ 25 | #include // std::size_t 26 | #include // std::move, std::swap 27 | 28 | /*** Special definitions ***/ 29 | // If the C++ version is greater or equal to 2017xx 30 | #if __cplusplus >= 201703l 31 | #define NODISCARD [[nodiscard]] 32 | #else 33 | #define NODISCARD 34 | #endif 35 | 36 | /*** Container Class ***/ 37 | template 38 | class Stack{ 39 | static_assert(SIZE != 0, "Stack capacity cannot be zero!"); 40 | 41 | public: 42 | /*** C++ Standard Named Requirements for Containers ***/ 43 | using value_type = T; 44 | using reference = T&; 45 | using const_reference = const T&; 46 | using difference_type = std::ptrdiff_t; 47 | using size_type = std::size_t; 48 | using aligned_data = typename std::aligned_storage::type; 49 | 50 | /*** Constructors and Destructor ***/ 51 | // Default constructor 52 | Stack() = default; 53 | 54 | // Copy constructor 55 | Stack(const Stack& copyStack); 56 | 57 | // Destructor 58 | ~Stack(); 59 | 60 | /*** Element Access ***/ 61 | NODISCARD const_reference top() const; 62 | NODISCARD reference top(); 63 | 64 | /*** Modifiers ***/ 65 | template 66 | bool emplace(Args&&... args); 67 | bool push(const value_type& value); 68 | bool push(value_type&& value); 69 | void pop(); 70 | void swap(Stack& swapStack); 71 | 72 | /*** Operators ***/ 73 | bool operator==(const Stack& compStack) const; 74 | bool operator!=(const Stack& compStack) const; 75 | Stack& operator=(const Stack& sourceStack) &; 76 | 77 | /*** Status Checkers ***/ 78 | NODISCARD bool empty() const { return (0 == idxTop); } // true if the Stack is empty 79 | NODISCARD bool full() const { return (SIZE == idxTop); } // true if the Stack is full 80 | NODISCARD size_type size() const { return idxTop; } // Current size of the Stack 81 | NODISCARD size_type capacity() const { return SIZE; } // Maximum capacity of the Stack 82 | NODISCARD size_type available() const { return (SIZE - size()); } // Available slots in Stack 83 | 84 | private: 85 | /*** Members ***/ 86 | size_type idxTop{0}; // Index after the top element 87 | aligned_data data[SIZE]; // Contained data 88 | 89 | /*** Helper Functions ***/ 90 | NODISCARD const_reference at(const size_type elemIdx) const 91 | { 92 | return reinterpret_cast(data[elemIdx]); 93 | } 94 | 95 | NODISCARD reference at(const size_type elemIdx) 96 | { 97 | return reinterpret_cast(data[elemIdx]); 98 | } 99 | }; 100 | 101 | /** 102 | * @brief Copy constructor 103 | * @param copyStack Source stack for copying 104 | */ 105 | template 106 | Stack::Stack(const Stack& copyStack) 107 | { 108 | *this = copyStack; 109 | } 110 | 111 | /** 112 | * @brief Destructor 113 | */ 114 | template 115 | Stack::~Stack() 116 | { 117 | while(!empty()) 118 | pop(); 119 | } 120 | 121 | /** 122 | * @brief Returns a constant reference to the top element of the Stack 123 | * @return Constant lValue reference to the front element 124 | * @note The referenced data is not valid if the Stack is empty 125 | */ 126 | template 127 | const T& Stack::top() const 128 | { 129 | if(empty()) 130 | return at(0); 131 | 132 | return at(idxTop-1); 133 | } 134 | 135 | /** 136 | * @brief Returns a reference to the top element of the Stack 137 | * @return lValue reference to the front element 138 | * @note The referenced data is not valid if the Stack is empty 139 | */ 140 | template 141 | T& Stack::top() 142 | { 143 | if(empty()) 144 | return at(0); 145 | 146 | return at(idxTop-1); 147 | } 148 | 149 | /** 150 | * @brief Pushes the given element to the top of the Stack 151 | * @param value Reference to the value to be copied 152 | * @return true If the element is pushed successfully 153 | */ 154 | template 155 | bool Stack::push(const value_type& value) 156 | { 157 | if(full()) 158 | return false; 159 | 160 | // Copy construct element at the top 161 | new(data + idxTop) value_type(value); 162 | 163 | ++idxTop; 164 | 165 | return true; 166 | } 167 | 168 | /** 169 | * @brief Pushes the given element to the top of the Stack 170 | * @param value rValue Reference to the value to be moved 171 | * @return true If the element is pushed successfully 172 | */ 173 | template 174 | bool Stack::push(value_type&& value) 175 | { 176 | return emplace(std::move(value)); 177 | } 178 | 179 | /** 180 | * @brief Pushes the given element to the top of the Stack by constructing it in-place 181 | * @param args Arguments for constructing the new element 182 | * @return true If the element is pushed successfully 183 | */ 184 | template 185 | template 186 | bool Stack::emplace(Args&&... args) 187 | { 188 | if(full()) 189 | return false; 190 | 191 | new(data + idxTop) value_type(std::forward(args)...); 192 | 193 | ++idxTop; 194 | 195 | return true; 196 | } 197 | 198 | /** 199 | * @brief Pops the top element of the stack 200 | */ 201 | template 202 | void Stack::pop() 203 | { 204 | if(empty()) 205 | return; 206 | 207 | // Explicitly call the destructor as we used the placement new 208 | at(idxTop-1).~value_type(); 209 | 210 | --idxTop; 211 | } 212 | 213 | /** 214 | * @brief Swaps the content of two Stacks 215 | * @param swapStack Stack to be swapped with 216 | */ 217 | template 218 | void Stack::swap(Stack& swapStack) 219 | { 220 | size_type swapIdx = 0; // Number of swapped elements 221 | 222 | // Swap elements unless exceeding any of the top indexes 223 | for( ; (swapIdx < swapStack.idxTop) && (swapIdx < idxTop); ++swapIdx) 224 | { 225 | std::swap(swapStack.at(swapIdx), at(swapIdx)); 226 | } 227 | 228 | /* Remaining elements should be constructed manually as they have 229 | * no swappable element at the other stack. 230 | * The moved object should be destructed at its old place. */ 231 | 232 | // Determine which Stack was bigger 233 | if(idxTop > swapStack.idxTop) 234 | { 235 | for( ; swapIdx < idxTop; ++swapIdx) 236 | { 237 | // Construct element at new Stack 238 | new(swapStack.data + swapIdx) value_type(at(swapIdx)); 239 | 240 | // Remove element from previous Stack 241 | at(swapIdx).~value_type(); 242 | } 243 | } 244 | else if(idxTop < swapStack.idxTop) 245 | { 246 | for( ; swapIdx < swapStack.idxTop; ++swapIdx) 247 | { 248 | // Construct element at new Stack 249 | new(data + swapIdx) value_type(swapStack.at(swapIdx)); 250 | 251 | // Remove element from previous Stack 252 | swapStack.at(swapIdx).~value_type(); 253 | } 254 | } 255 | else 256 | { 257 | // Do nothing 258 | } 259 | 260 | // Swap indexes 261 | std::swap(idxTop, swapStack.idxTop); 262 | } 263 | 264 | /** 265 | * @brief Comparison operator 266 | * @param compStack Stack to be compared with. 267 | * @return true If both Stacks are equal. 268 | */ 269 | template 270 | bool Stack::operator==(const Stack& compStack) const 271 | { 272 | if(compStack.size() != size()) 273 | return false; 274 | 275 | for(size_type elemIdx = 0; elemIdx < idxTop; ++elemIdx) 276 | if(compStack.at(elemIdx) != at(elemIdx)) 277 | return false; 278 | 279 | return true; 280 | } 281 | 282 | /** 283 | * @brief Incomparison operator 284 | * @param compStack Stack to be compared with. 285 | * @return true If Stacks are not equal. 286 | */ 287 | template 288 | bool Stack::operator!=(const Stack& compStack) const 289 | { 290 | return !(*this == compStack); 291 | } 292 | 293 | /** 294 | * @brief Copy assignment operator 295 | * @param sourceStack Stack to be copied from 296 | * @return lValue reference to the left Stack to support cascaded operations 297 | */ 298 | template 299 | Stack& Stack::operator=(const Stack& sourceStack) & 300 | { 301 | while(!empty()) 302 | pop(); 303 | 304 | if(!sourceStack.empty()) 305 | { 306 | for(size_type elemIdx = 0; elemIdx < sourceStack.idxTop; ++elemIdx) 307 | new(data + elemIdx) value_type(sourceStack.at(elemIdx)); 308 | 309 | idxTop = sourceStack.idxTop; 310 | } 311 | 312 | return *this; 313 | } 314 | -------------------------------------------------------------------------------- /Containers/QueueContainer.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file QueueContainer.h 3 | * @details A template queue container class for embedded systems. 4 | * The container is implemented without any dynamic allocation feature. 5 | * @author Caglayan DOKME, caglayandokme@gmail.com 6 | * @date July 9, 2021 -> First release 7 | * July 10, 2021 -> Doxygen added. Copy constructor added. 8 | * -> Data buffer replaced with uint8_t array to support 9 | * data types without default constructor. 10 | * -> Default size parameter removed from template arguments 11 | * July 11, 2021 -> Index incrementor method optimized, mod operation removed 12 | * July 15, 2021 -> Data storage replaced with std::aligned_storage 13 | * capacity() method added. 14 | * July 16, 2021 -> Missing destructor implemented. 15 | * July 17, 2021 -> lvalue ref-qualifier added to assignment operator. 16 | * -> Helper function at(..) added to avoid repetition of casting. 17 | * -> Swap issue fixed with manual swapping of the storage. 18 | * 19 | * @note Feel free to contact for questions, bugs or any other thing. 20 | * @copyright No copyright. 21 | */ 22 | 23 | /*** Recursive inclusion preventer ***/ 24 | #pragma once 25 | 26 | /*** Libraries ***/ 27 | #include // std::size_t 28 | #include // std::move, std::swap 29 | #include // std::aligned_storage 30 | #include // operator new 31 | 32 | /*** Special definitions ***/ 33 | // If the C++ version is greater or equal to 2017xx 34 | #if __cplusplus >= 201703l 35 | #define NODISCARD [[nodiscard]] 36 | #else 37 | #define NODISCARD 38 | #endif 39 | 40 | /*** Container Class ***/ 41 | template 42 | class Queue{ 43 | static_assert(SIZE != 0, "Queue capacity cannot be zero!"); 44 | 45 | public: 46 | /*** C++ Standard Named Requirements for Containers ***/ 47 | using value_type = T; 48 | using reference = T&; 49 | using const_reference = const T&; 50 | using iterator = T*; 51 | using const_iterator = const T*; 52 | using difference_type = std::ptrdiff_t; 53 | using size_type = std::size_t; 54 | using aligned_data = typename std::aligned_storage::type; 55 | 56 | /*** Constructors and Destructor ***/ 57 | // Default constructor 58 | Queue(); 59 | 60 | // Copy Constructor 61 | Queue(const Queue& copyQ); 62 | 63 | // Destructor 64 | ~Queue(); 65 | 66 | /*** Element Access ***/ 67 | NODISCARD const_reference front() const; 68 | NODISCARD reference front(); 69 | NODISCARD const_reference back() const; 70 | NODISCARD reference back(); 71 | 72 | /*** Modifiers ***/ 73 | template 74 | bool emplace(Args&&... args); 75 | bool push(const value_type& value); 76 | bool push(value_type&& value); 77 | 78 | void pop(); 79 | void swap(Queue& swapQ); 80 | 81 | /*** Status Checkers ***/ 82 | NODISCARD bool empty() const { return (0 == sz); } // true if the Queue is empty 83 | NODISCARD bool full() const { return (SIZE == sz); } // true if the Queue is full 84 | NODISCARD size_type size() const { return sz; } // Current size of the Queue 85 | NODISCARD size_type capacity() const { return SIZE; } // Maximum capacity of the Queue 86 | NODISCARD size_type available() const { return (SIZE - sz); } // Available slots in Queue 87 | 88 | /*** Operators ***/ 89 | bool operator==(const Queue& compQ) const; 90 | bool operator!=(const Queue& compQ) const; 91 | Queue& operator=(const Queue& sourceQ) &; 92 | 93 | private: 94 | /*** Members ***/ 95 | size_type sz; // General size 96 | size_type idxFront; // Index of the front element 97 | size_type idxBack; // Index of the back element 98 | aligned_data data[SIZE]; // Stored data 99 | 100 | /*** Helper functions ***/ 101 | static void IncrementIndex(size_type& index) // Increments any index by not violating the range 102 | { 103 | index = (SIZE-1 == index) ? 0 : index+1; 104 | } 105 | 106 | NODISCARD const_reference at(const size_type elemIdx) const 107 | { 108 | return reinterpret_cast(data[elemIdx]); 109 | } 110 | 111 | NODISCARD reference at(const size_type elemIdx) 112 | { 113 | return reinterpret_cast(data[elemIdx]); 114 | } 115 | }; 116 | 117 | /** 118 | * @brief Default constructor 119 | */ 120 | template 121 | Queue::Queue() 122 | : sz(0), idxFront(0), idxBack(SIZE-1) 123 | { /* No operation */ } 124 | 125 | /** 126 | * @brief Copy constructor 127 | */ 128 | template 129 | Queue::Queue(const Queue& copyQ) 130 | : sz(0), idxFront(0), idxBack(SIZE-1) 131 | { 132 | *this = copyQ; 133 | } 134 | 135 | /** 136 | * @brief Destructor 137 | * @note Calls the destructor of each element explicitly 138 | */ 139 | template 140 | Queue::~Queue() 141 | { 142 | while(!empty()) 143 | pop(); 144 | } 145 | 146 | /** 147 | * @brief Returns a constant lValue reference to the front element of Queue 148 | * @return Constant lValue reference to the front element 149 | */ 150 | template 151 | const T& Queue::front() const 152 | { 153 | return at(idxFront); 154 | } 155 | 156 | /** 157 | * @brief Returns an lValue reference to the front element of Queue 158 | * @return lValue reference to the front element 159 | */ 160 | template 161 | T& Queue::front() 162 | { 163 | return at(idxFront); 164 | } 165 | 166 | /** 167 | * @brief Returns a constant lValue reference to the back element of Queue 168 | * @return Constant lValue reference to the back element 169 | */ 170 | template 171 | const T& Queue::back() const 172 | { 173 | return at(idxBack); 174 | } 175 | 176 | /** 177 | * @brief Returns an lValue reference to the back element of Queue 178 | * @return lValue reference to the back element 179 | */ 180 | template 181 | T& Queue::back() 182 | { 183 | return at(idxBack); 184 | } 185 | 186 | /** 187 | * @brief Pushes the element by constructing it in-place with the given arguments 188 | * @param args Arguments to be forwarded to the constructor of the new element 189 | * @return true If the operation is successful. 190 | * false If the queue was full 191 | */ 192 | template 193 | template 194 | bool Queue::emplace(Args&&... args) 195 | { 196 | if(full()) 197 | return false; 198 | 199 | // Adjust back index 200 | IncrementIndex(idxBack); 201 | 202 | // In-place construct element with the arguments at the back 203 | new(data + idxBack) value_type(std::forward(args)...); 204 | 205 | ++sz; // Increment size 206 | 207 | return true; 208 | } 209 | 210 | /** 211 | * @brief Pushes the element to the Queue 212 | * @param value Constant lValue reference to the object to be pushed 213 | * @return true If the operation is successful. 214 | * false If the queue was full 215 | */ 216 | template 217 | bool Queue::push(const value_type& value) 218 | { 219 | if(full()) 220 | return false; 221 | 222 | // Adjust back index 223 | IncrementIndex(idxBack); 224 | 225 | // Copy construct element at the back 226 | new(data + idxBack) value_type(value); 227 | 228 | ++sz; // Increment size 229 | 230 | return true; 231 | } 232 | 233 | /** 234 | * @brief Pushes the element to the Queue 235 | * @param value rValue reference to the object to be pushed 236 | * @return true If the operation is successful. 237 | * false If the queue was full 238 | */ 239 | template 240 | bool Queue::push(value_type&& value) 241 | { 242 | return emplace(std::move(value)); 243 | } 244 | 245 | /** 246 | * @brief Pops the front element of the Queue 247 | * @note Explicitly calls the destructor of the popped element 248 | */ 249 | template 250 | void Queue::pop() 251 | { 252 | if(!empty()) 253 | { 254 | // Explicitly call the destructor as we used the placement new 255 | at(idxFront).~value_type(); 256 | 257 | IncrementIndex(idxFront); 258 | 259 | // Decrement size 260 | --sz; 261 | } 262 | } 263 | 264 | /** 265 | * @brief Swaps the content of two Queues 266 | * @param swapQ Queue to be swapped with 267 | */ 268 | template 269 | void Queue::swap(Queue& swapQ) 270 | { 271 | size_type swapIdx0 = idxFront, swapIdx1 = swapQ.idxFront, swapCount = 0; 272 | 273 | // Swap the matching elements 274 | for( ; (swapCount < sz) && (swapCount < swapQ.sz); ++swapCount) 275 | { 276 | std::swap(at(swapIdx0), swapQ.at(swapIdx1)); 277 | 278 | IncrementIndex(swapIdx0); 279 | IncrementIndex(swapIdx1); 280 | } 281 | 282 | // Move the elements without any swappable match 283 | if(sz > swapQ.sz) 284 | { 285 | for( ; swapCount < sz; ++swapCount) 286 | { 287 | // Construct element at new Queue 288 | new(swapQ.data + swapIdx1) value_type(at(swapIdx0)); 289 | 290 | // Remove element from previous Queue 291 | at(swapIdx0).~value_type(); 292 | 293 | IncrementIndex(swapIdx0); 294 | IncrementIndex(swapIdx1); 295 | } 296 | } 297 | else if(sz < swapQ.sz) 298 | { 299 | for( ; swapCount < swapQ.sz; ++swapCount) 300 | { 301 | // Construct element at new Queue 302 | new(data + swapIdx0) value_type(swapQ.at(swapIdx1)); 303 | 304 | // Remove element from previous Queue 305 | at(swapIdx1).~value_type(); 306 | 307 | IncrementIndex(swapIdx0); 308 | IncrementIndex(swapIdx1); 309 | } 310 | } 311 | 312 | // Swap indexes 313 | std::swap(idxBack, swapQ.idxBack); 314 | std::swap(idxFront, swapQ.idxFront); 315 | std::swap(sz, swapQ.sz); 316 | } 317 | 318 | /** 319 | * @brief Comparison operator 320 | * @param compQ Queue to be compared with. 321 | * @return true If both Queues are equal. 322 | */ 323 | template 324 | bool Queue::operator==(const Queue& compQ) const 325 | { 326 | if(compQ.size() != sz) // Size must be equal 327 | return false; 328 | 329 | // Element-wise comparison 330 | size_type index0 = idxFront, index1 = compQ.idxFront; 331 | for(size_type elemIdx = 0; elemIdx < compQ.size(); ++elemIdx) 332 | { 333 | if(compQ.at(index1) != at(index0)) 334 | return false; 335 | 336 | IncrementIndex(index0); 337 | IncrementIndex(index1); 338 | } 339 | 340 | // Queues are equal if the function reaches here 341 | return true; 342 | } 343 | 344 | /** 345 | * @brief Incomparison operator 346 | * @param compQ Queue to be compared with. 347 | * @return true If Queues are not equal. 348 | */ 349 | template 350 | bool Queue::operator!=(const Queue& compQ) const 351 | { 352 | return !(compQ == *this); 353 | } 354 | 355 | /** 356 | * @brief Copy assignment operator 357 | * @param sourceQ Queue to be copied from 358 | * @return lValue reference to the left Queue to support cascaded operations 359 | */ 360 | template 361 | Queue& Queue::operator=(const Queue& sourceQ) & 362 | { 363 | // Pop all elements first 364 | while(!empty()) 365 | pop(); 366 | 367 | if(!sourceQ.empty()) 368 | { 369 | // Copy construct each element 370 | size_type sourceIdx = sourceQ.idxFront; 371 | for(size_type elemIdx = 0; elemIdx < sourceQ.sz; ++elemIdx, IncrementIndex(sourceIdx)) 372 | new(data + elemIdx) value_type(sourceQ.at(sourceIdx)); 373 | 374 | idxFront = 0; 375 | idxBack = sourceQ.sz - 1; 376 | sz = sourceQ.sz; 377 | } 378 | 379 | return *this; 380 | } 381 | -------------------------------------------------------------------------------- /Containers/ArrayContainer.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file ArrayContainer.h 3 | * @details A template container class with an underlying array data structure. 4 | * This container is designed for embedded devices with limited resources. 5 | * Dynamic allocation is not preferred to prevent fragmentation and some other performance related problems. 6 | * Code size optimizations is handled with helper functions. 7 | * @author Caglayan DOKME, caglayandokme@gmail.com 8 | * @date March 19, 2021 -> First release 9 | * March 20, 2021 -> Comparison operators overloaded. 10 | * -> Initializer list constructor added. 11 | * -> Helper functions added to decrease the code size. 12 | * March 22, 2021 -> Helper functions removed as the template functions are implicitly declared as inline. 13 | * -> Position based fill operation added. 14 | * -> Rule based fill operation added. 15 | * March 23, 2021 -> Type traits added to related functions. 16 | * -> Standard named requirements added to class. 17 | * March 26, 2021 -> Exception safety condition changed for assignments and swapping. 18 | * 19 | * @note Feel free to contact for questions, bugs, improvements or any other thing. 20 | * @copyright No copyright. 21 | */ 22 | 23 | /** Recursive inclusion preventer **/ 24 | #ifndef ARRAY_CONTAINER_H 25 | #define ARRAY_CONTAINER_H 26 | 27 | /** Libraries **/ 28 | #include // For size_t 29 | #include // For initializer_list constructor 30 | #include // For compile time controls 31 | #include // For assertions 32 | 33 | /** Special definitions **/ 34 | #if __cplusplus >= 201703l // If the C++ version is greater or equal to 2017xx 35 | #define NODISCARD [[nodiscard]] 36 | #else 37 | #define NODISCARD 38 | #endif 39 | 40 | /*** Container Class ***/ 41 | template 42 | class Array{ 43 | static_assert(SIZE != 0, "Array size cannot be zero!"); 44 | 45 | public: 46 | /*** C++ Standard Named Requirements for Containers ***/ 47 | typedef T value_type; 48 | typedef std::size_t size_type; 49 | typedef T& reference; 50 | typedef const T& const_reference; 51 | typedef T* iterator; 52 | typedef const T* const_iterator; 53 | typedef T* pointer; 54 | typedef const T* const_pointer; 55 | typedef ptrdiff_t difference_type; 56 | 57 | /*** Constructors and Destructors ***/ 58 | Array() noexcept(std::is_nothrow_constructible::value) = default; // Default constructor 59 | 60 | template // Fill constructor 61 | Array(const U& fillValue) noexcept(std::is_nothrow_assignable_v); 62 | 63 | template // Converting constructor 64 | Array(const Array& copyArr) noexcept(std::is_nothrow_assignable_v); 65 | 66 | template // Construct with C-Style array of any type 67 | Array(const U* const source, const size_type size) noexcept(std::is_nothrow_assignable_v); 68 | 69 | template // Initializer_list constructor 70 | Array(std::initializer_list initializerList) noexcept(std::is_nothrow_assignable_v); 71 | 72 | ~Array() = default; 73 | 74 | /*** Element Access ***/ 75 | NODISCARD iterator begin() noexcept { return data; } 76 | NODISCARD const_iterator begin() const noexcept { return data; } 77 | NODISCARD iterator end() noexcept { return data + SIZE; } 78 | NODISCARD const_iterator end() const noexcept { return data + SIZE; } 79 | 80 | NODISCARD const_iterator cbegin() const noexcept { return data; } 81 | NODISCARD const_iterator cend() const noexcept { return data + SIZE; } 82 | 83 | NODISCARD reference at(const size_type position) { assert(position >= SIZE); return data[position]; } 84 | NODISCARD const_reference at(const size_type position) const { assert(position >= SIZE); return data[position]; } 85 | 86 | /*** Operators ***/ 87 | NODISCARD const_reference operator[](const size_type index) const { return data[index]; } // Subscript for non-assignable reference 88 | NODISCARD reference operator[](const size_type index) { return data[index]; } // Subscript for assignable reference 89 | 90 | template // Compare any kind of arrays 91 | NODISCARD bool operator==(const Array& rightArr) const noexcept; 92 | template // Compare any kind of arrays by unequality 93 | NODISCARD bool operator!=(const Array& rightArr) const noexcept; 94 | 95 | template // Copy assignment operator 96 | Array& operator=(const Array& copyArr) noexcept(std::is_nothrow_assignable_v); 97 | 98 | /*** Operations ***/ 99 | Array& Swap(Array& swapArr) noexcept(std::is_nothrow_swappable_v); 100 | 101 | template 102 | Array& Fill(const U& fillValue) noexcept(std::is_nothrow_assignable_v); 103 | 104 | template 105 | Array& Fill(const U& fillValue, const size_type startPos, const size_type endPos = SIZE) noexcept(std::is_nothrow_assignable_v); 106 | 107 | template 108 | Array& Fill(const U& fillValue, iterator startPos, iterator endPos) noexcept(std::is_nothrow_assignable_v); 109 | 110 | template 111 | Array& FillWithRule(const RuleT& predicate); 112 | 113 | /*** Status Checkers ***/ 114 | NODISCARD constexpr size_type max_size() const noexcept { return SIZE; } // Return the maximum possible size 115 | NODISCARD constexpr size_type size() const noexcept { return SIZE; } // Returns total number of elements 116 | NODISCARD constexpr size_type sizeRaw() const noexcept { return SIZE * sizeof(T); } // Return actual size in bytes 117 | NODISCARD constexpr bool empty() const noexcept { return (SIZE == 0); } 118 | 119 | private: 120 | value_type data[SIZE]; 121 | }; 122 | 123 | /** 124 | * @brief Fill constructor fills every element with a copy of the given one 125 | * @param fillValue Reference value for filling. 126 | */ 127 | template 128 | template 129 | Array::Array(const U& fillValue) noexcept(std::is_nothrow_assignable_v) 130 | { 131 | for(reference element : *this) 132 | element = fillValue; 133 | } 134 | 135 | /** 136 | * @brief Copy constructor copies elements from another array 137 | * @param copyArr Source array 138 | * @note The source array can be of different type and size 139 | * @note Copy size is determined as the lower one of size attributes 140 | * 141 | * @attention Unintentional data loss may occur as the types and sizes might not be the same. 142 | * It is the user's responsibility to consider etiher data or precision loss. 143 | */ 144 | template 145 | template 146 | Array::Array(const Array& copyArr) noexcept(std::is_nothrow_assignable_v) 147 | { 148 | typename Array::const_iterator it = copyArr.cbegin(); 149 | 150 | for(reference element : *this) 151 | { 152 | element = *it; 153 | 154 | if(++it == copyArr.cend()) 155 | break; 156 | } 157 | } 158 | 159 | /** 160 | * @brief Construct the container with the given C-Style array 161 | * @param source Source buffer 162 | * @param sourceSize Number of elements in the source 163 | * @note In case of an inequality between size values, the lower one is chosen 164 | * @note noexcept exception specifier is not used due to the possibility 165 | * of wrong sourceSize inputs. 166 | */ 167 | template 168 | template 169 | Array::Array(const U* const source, const size_type sourceSize) noexcept(std::is_nothrow_assignable_v) 170 | { 171 | if(source != nullptr) 172 | { 173 | for(size_type index = 0; (index < SIZE) && (index < sourceSize); ++ index) 174 | data[index] = source[index]; 175 | } 176 | } 177 | 178 | /** 179 | * @brief Constructs the array with brace-enclosed initializer list. 180 | * @param initializerList Source list 181 | */ 182 | template 183 | template 184 | Array::Array(std::initializer_list initializerList) noexcept(std::is_nothrow_assignable_v) 185 | { 186 | size_type index = 0; 187 | for(const U& element : initializerList) 188 | { 189 | data[index++] = element; 190 | 191 | if(index == SIZE) 192 | break; 193 | } 194 | } 195 | 196 | /** 197 | * @brief Comparison operator compares two arrays of different types 198 | * @param rightArr Array of another type to be compared with 199 | * @return true If the elements are equal 200 | * @note Comparing arrays of different size is restricted. 201 | * A compile time error might occur. 202 | */ 203 | template 204 | template 205 | NODISCARD bool Array::operator==(const Array& rightArr) const noexcept 206 | { 207 | if(static_cast(this) == static_cast(&rightArr)) // Self comparison 208 | return true; 209 | 210 | typename Array::const_iterator itRight = rightArr.cbegin(); 211 | 212 | /* Comparing with std::memcmp is not eligible because although the size of 213 | * individual elements might be unequal (e.g. double(8) and int(4)), 214 | * their values can be equal (e.g. int(65) = double(65.0))*/ 215 | for(const_reference element : data) 216 | { 217 | if(element != *itRight) 218 | return false; 219 | 220 | ++itRight; 221 | } 222 | 223 | return true; 224 | } 225 | 226 | /** 227 | * @brief Comparison operator compares two arrays of different types 228 | * @param rightArr Array of another type to be compared with 229 | * @return false If the elements are equal 230 | * @note Comparing arrays of different size is restricted. 231 | * A compile time error might occur. 232 | */ 233 | template 234 | template 235 | NODISCARD bool Array::operator!=(const Array& rightArr) const noexcept 236 | { 237 | return !(this->operator==(rightArr)); 238 | } 239 | 240 | /** 241 | * @brief Copy assignment operator 242 | * @param copyArr Source array 243 | * @return lValue reference to the left array to support cascaded calls. 244 | */ 245 | template 246 | template 247 | Array& Array::operator=(const Array& copyArr) noexcept(std::is_nothrow_assignable_v) 248 | { 249 | if(static_cast(this) == static_cast(©Arr)) // Check self copy 250 | return *this; 251 | 252 | typename Array::const_iterator itRight = copyArr.cbegin(); 253 | 254 | for(reference element : *this) 255 | { 256 | element = *itRight; 257 | 258 | if(++itRight == copyArr.cend()) 259 | break; 260 | } 261 | 262 | return *this; 263 | } 264 | 265 | /** 266 | * @brief Swaps two arrays of the same size and type 267 | * @param swapArr Array to be swapped with 268 | * @return lValue reference to the left array to support cascaded calls. 269 | */ 270 | template 271 | Array& Array::Swap(Array& swapArr) noexcept(std::is_nothrow_swappable_v) 272 | { 273 | if(&swapArr == this) // Self swap control 274 | return *this; 275 | 276 | value_type temp; 277 | iterator itLeft = begin(), itRight = swapArr.begin(); 278 | 279 | for(; itLeft != end(); ++itLeft, ++itRight) 280 | { 281 | temp = *itLeft; 282 | *itLeft = *itRight; 283 | *itRight = temp; 284 | } 285 | 286 | return *this; 287 | } 288 | 289 | /** 290 | * @brief Fills the array with exact copies of the given fill value. 291 | * @param fillValue Reference fill value 292 | * @return lValue reference to the left array to support cascaded calls. 293 | */ 294 | template 295 | template 296 | Array& Array::Fill(const U& fillValue) noexcept(std::is_nothrow_assignable_v) 297 | { 298 | for(reference element : *this) 299 | element = fillValue; 300 | 301 | return *this; 302 | } 303 | 304 | /** 305 | * @brief Fills a sub-range of array with exact copies of the given fill value. 306 | * @param fillValue Reference fill value 307 | * @param startPos Start position for filling 308 | * @param endPos End position for filling(excluded) 309 | * @return lValue reference to the left array to support cascaded calls. 310 | */ 311 | template 312 | template 313 | Array& Array::Fill(const U& fillValue, const size_type startPos, const size_type endPos) noexcept(std::is_nothrow_assignable_v) 314 | { 315 | for(size_type index = startPos; (index < SIZE) && (index < endPos); ++index) 316 | data[index] = fillValue; 317 | 318 | return *this; 319 | } 320 | 321 | /** 322 | * @brief Fills a sub-range of array with exact copies of the given fill value using iterators. 323 | * @param fillValue Reference fill value 324 | * @param startPos Start iterator position for filling 325 | * @param endPos End iterator position for filling(excluded) 326 | * @return lValue reference to the left array to support cascaded calls. 327 | */ 328 | template 329 | template 330 | Array& Array::Fill(const U& fillValue, iterator startPos, iterator endPos) noexcept(std::is_nothrow_assignable_v) 331 | { 332 | if((startPos < begin()) || (startPos >= end())) // Manual address input might violate the address range 333 | return *this; 334 | 335 | if(startPos >= endPos) 336 | return *this; 337 | 338 | for(iterator it = startPos; (it < end()) && (it < endPos); ++it) 339 | *it = fillValue; 340 | 341 | return *this; 342 | } 343 | 344 | /** 345 | * @brief Position based fill operation 346 | * @param predicate Rule for calculating the element value using its position. 347 | * @return lValue reference to the left array to support cascaded calls. 348 | * 349 | * @note Example usage for this function: 350 | * userArr.Fill([](const std::size_t pos) {return (pos * pos);}); 351 | * 352 | * @note For more examples, refer to: 353 | * github.com/CaglayanDokme/CPP-Exercises/blob/main/FuncWithLambdaArg.cpp 354 | */ 355 | template 356 | template 357 | Array& Array::FillWithRule(const RuleT& predicate) 358 | { 359 | for(size_type index = 0; index < SIZE; ++index) 360 | data[index] = predicate(index); 361 | 362 | return *this; 363 | } 364 | 365 | #endif // Recursive inclusion preventer 366 | --------------------------------------------------------------------------------