├── CMakeLists.txt ├── tests ├── CMakeLists.txt.in ├── CMakeLists.txt ├── simple_int_test.cpp ├── iterator_test.cpp └── member_func_test.cpp ├── samples ├── CMakeLists.txt ├── sample_int_buffer.cpp ├── sample_custom_buffer.cpp └── sample_string_buffer.cpp ├── conanfile.py ├── LICENSE ├── README.md ├── .github └── workflows │ └── circularbuffer_build_pipeline.yml └── circular_buffer.h /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | 3 | 4 | cmake_policy(SET CMP0079 NEW) 5 | project("circularbuffer") 6 | 7 | set(CMAKE_CXX_STANDARD 11) 8 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 9 | 10 | 11 | add_library(${PROJECT_NAME} INTERFACE) 12 | 13 | target_include_directories(${PROJECT_NAME} INTERFACE ${${PROJECT_NAME}_SOURCE_DIR}) 14 | enable_testing() 15 | add_subdirectory(samples) 16 | add_subdirectory(tests) 17 | 18 | 19 | install(TARGETS circularbuffer DESTINATION lib) 20 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt.in: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | 3 | project(googletest-download NONE) 4 | 5 | include(ExternalProject) 6 | ExternalProject_Add(googletest 7 | GIT_REPOSITORY https://github.com/google/googletest.git 8 | GIT_TAG release-1.8.0 9 | SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-src" 10 | BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-build" 11 | CONFIGURE_COMMAND "" 12 | BUILD_COMMAND "" 13 | INSTALL_COMMAND "" 14 | TEST_COMMAND "" 15 | ) 16 | -------------------------------------------------------------------------------- /samples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(CMAKE_CXX_STANDARD 11) 2 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 3 | 4 | add_executable(sample_int_buffer sample_int_buffer.cpp) 5 | target_link_libraries(sample_int_buffer 6 | PRIVATE 7 | circularbuffer 8 | ) 9 | 10 | add_executable(sample_string_buffer sample_string_buffer.cpp) 11 | target_link_libraries(sample_string_buffer 12 | PRIVATE 13 | circularbuffer 14 | ) 15 | 16 | add_executable(sample_custom_buffer sample_custom_buffer.cpp) 17 | target_link_libraries(sample_custom_buffer 18 | PRIVATE 19 | circularbuffer 20 | ) 21 | -------------------------------------------------------------------------------- /conanfile.py: -------------------------------------------------------------------------------- 1 | from conans import ConanFile, CMake 2 | 3 | 4 | class CircularBufferConan(ConanFile): 5 | name = "circular_buffer" 6 | version = "0.1.0" 7 | license = "MIT License" 8 | author = "vinit james vinit.james24@gmail.com" 9 | url = "https://github.com/vinitjames/circularbuffer" 10 | description = "A simple, general purpose STL style circularbuffer implementation in C++." 11 | topics = ("circularbuffer", "ringbuffer", "conan-recipe", "c++") 12 | exports_sources = "*" 13 | 14 | def build(self): # this is not building a library, just tests 15 | cmake = CMake(self) 16 | cmake.configure() 17 | cmake.build() 18 | cmake.test() 19 | 20 | def package(self): 21 | print(self.name) 22 | self.copy("{}.h".format(self.name), dst="include", src=".") 23 | self.copy("LICENSE", dst="license", src=".") 24 | 25 | def package_info(self): 26 | self.info.header_only() 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Vinit James 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | CircularBuffer 2 | =========== 3 | 4 | [![Actions Status](https://github.com/vinitjames/circularbuffer_CPP/workflows/Build%20and%20Test/badge.svg)](https://github.com/vinitjames/circularbuffer_CPP/actions)   [![MIT License](https://img.shields.io/github/license/vinitjames/circularbuffer?color=blue)](https://github.com/vinitjames/circularbuffer/blob/master/LICENSE) 5 | ## About 6 | circularbuffer_CPP is a simple, general purpose STL style [circularbuffer](https://en.wikipedia.org/wiki/Circular_buffer) implementation in C++. 7 | 8 | 9 | ## Features 10 | * STL style implementaion of Circular Buffer 11 | * Lightweight (only single header file) 12 | * Simple API conforming to STL container semantics. 13 | * Random Access Iterator for easy forwad and reverse iteration and looping through elements. 14 | * Test suites 15 | 16 | ## Installation 17 | Run: 18 | ``` 19 | git clone https://github.com/vinitjames/circularbuffer_CPP.git 20 | ``` 21 | ## Without CMAKE 22 | copy circular_buffer.h to your project 23 | 24 | ## With CMAKE 25 | To locally build and run test run the following commands 26 | ```sh 27 | $ mkdir build 28 | $ cd build 29 | $ cmake .. 30 | $ cmake --build . 31 | $ ctest 32 | ``` 33 | To include in project as a submodule just clone the repo in a subdirectory and use `add_subdirectory()` in the top level `CMakeLists.txt` 34 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | 3 | set(CMAKE_CXX_STANDARD 11) 4 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 5 | 6 | message(STATUS "cmake in tests, project name ${PROJECT_NAME}" ) 7 | 8 | configure_file(CMakeLists.txt.in googletest-download/CMakeLists.txt) 9 | execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . 10 | RESULT_VARIABLE result 11 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download 12 | ) 13 | if(result) 14 | message(FATAL_ERROR "CMake step for googletest failed: ${result}") 15 | endif() 16 | 17 | execute_process(COMMAND ${CMAKE_COMMAND} --build . 18 | RESULT_VARIABLE result 19 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/googletest-download 20 | ) 21 | 22 | if(result) 23 | message(FATAL_ERROR "Build step for googletest failed: ${result}") 24 | endif() 25 | 26 | # Prevent overriding the parent project's compiler/linker 27 | # settings on Windows 28 | set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) 29 | 30 | # Add googletest directly to our build. This defines 31 | # the gtest and gtest_main targets. 32 | add_subdirectory(${CMAKE_CURRENT_BINARY_DIR}/googletest-src 33 | ${CMAKE_CURRENT_BINARY_DIR}/googletest-build 34 | EXCLUDE_FROM_ALL) 35 | 36 | add_executable(simple_int_test simple_int_test.cpp) 37 | target_link_libraries(simple_int_test 38 | circularbuffer 39 | gtest_main 40 | ) 41 | add_test(NAME simple_int_test COMMAND simple_int_test) 42 | 43 | add_executable(member_func_test member_func_test.cpp) 44 | target_link_libraries(member_func_test 45 | circularbuffer 46 | gtest_main 47 | ) 48 | add_test(NAME member_func_test COMMAND member_func_test) 49 | 50 | add_executable(iterator_test iterator_test.cpp) 51 | target_link_libraries(iterator_test 52 | circularbuffer 53 | gtest_main 54 | ) 55 | add_test(NAME iterator_test COMMAND iterator_test) 56 | -------------------------------------------------------------------------------- /samples/sample_int_buffer.cpp: -------------------------------------------------------------------------------- 1 | #include "circular_buffer.h" 2 | #include 3 | #include 4 | 5 | int main(int argc, char *argv[]) 6 | { 7 | //Initializing a buffer 8 | CircularBuffer int_buff{5}; 9 | 10 | //checking buffer empty function 11 | std::cout<<"Calling buffer empty function "< 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | struct custom_struct{ 9 | static int count ; 10 | char* bytes =nullptr; 11 | int id = 0; 12 | custom_struct(){ 13 | bytes = (char*)malloc(100); 14 | id = count; 15 | std::cout<<"constructing custom_struct: "< custom_buffer(5); 64 | custom_struct element; 65 | for (int i = 0; i < 10; ++i) { 66 | custom_buffer.push_back(element); 67 | } 68 | 69 | CircularBuffer buffermoved = std::move(custom_buffer); 70 | CircularBuffer buffermoveassigned{10}; 71 | buffermoveassigned = std::move(buffermoved); 72 | buffermoveassigned.push_back(std::move(element)); 73 | return 0; 74 | } 75 | 76 | -------------------------------------------------------------------------------- /.github/workflows/circularbuffer_build_pipeline.yml: -------------------------------------------------------------------------------- 1 | name: Build and Test 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) 7 | BUILD_TYPE: Release 8 | 9 | jobs: 10 | build: 11 | # The CMake configure and build commands are platform agnostic and should work equally 12 | # well on Windows or Mac. You can convert this to a matrix build if you need 13 | # cross-platform coverage. 14 | # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | 20 | - name: Create Build Environment 21 | # Some projects don't allow in-source building, so create a separate build directory 22 | # We'll use this as our working directory for all subsequent commands 23 | run: cmake -E make_directory ${{github.workspace}}/build 24 | 25 | - name: Configure CMake 26 | # Use a bash shell so we can use the same syntax for environment variable 27 | # access regardless of the host operating system 28 | shell: bash 29 | working-directory: ${{github.workspace}}/build 30 | # Note the current convention is to use the -S and -B options here to specify source 31 | # and build directories, but this is only available with CMake 3.13 and higher. 32 | # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 33 | run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE 34 | 35 | - name: Build 36 | working-directory: ${{github.workspace}}/build 37 | shell: bash 38 | # Execute the build. You can specify a specific target with "--target " 39 | run: cmake --build . --config $BUILD_TYPE 40 | 41 | - name: Test 42 | working-directory: ${{github.workspace}}/build 43 | shell: bash 44 | # Execute tests defined by the CMake configuration. 45 | # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail 46 | run: ctest -C $BUILD_TYPE 47 | -------------------------------------------------------------------------------- /tests/simple_int_test.cpp: -------------------------------------------------------------------------------- 1 | #include "circular_buffer.h" 2 | #include 3 | #include 4 | #include "gtest/gtest.h" 5 | 6 | #define TEST_BUFFER_SIZE 100 7 | 8 | class CircularBufferTest : public ::testing::Test{ 9 | 10 | protected: 11 | 12 | CircularBuffer test{TEST_BUFFER_SIZE}; 13 | 14 | }; 15 | 16 | TEST_F(CircularBufferTest, CapacityTest){ 17 | EXPECT_EQ(TEST_BUFFER_SIZE, test.capacity()); 18 | EXPECT_FALSE(test.capacity() == 0); 19 | } 20 | 21 | TEST_F(CircularBufferTest, EmptyTest){ 22 | EXPECT_TRUE(test.empty()); 23 | test.push_back(10); 24 | test.push_back(5); 25 | EXPECT_FALSE(test.empty()); 26 | test.pop_front(); 27 | test.pop_front(); 28 | EXPECT_TRUE(test.empty()); 29 | for(int i=0; i 3 | #include 4 | #include "circular_buffer.h" 5 | #include 6 | #include 7 | #include 8 | 9 | 10 | int main(int argc, char *argv[]) 11 | { 12 | 13 | CircularBuffer string_buff{10}; 14 | //checking buffer empty function 15 | std::cout<<"Calling buffer empty function "< string_buff_copy1{string_buff}; 65 | std::cout<<"Buffer Copy Created, copied buffer size "< string_buff_copy2{10}; 69 | string_buff_copy2 = string_buff; 70 | std::cout<<"Buffer Copy assigned , copied buffer size "< string_buff_move1{std::move(string_buff)}; 74 | std::cout<<"Buffer created with move constructor, moved buffer size "< string_buff_move2{10}; 78 | string_buff_move2 = std::move(string_buff_move1); 79 | std::cout<<"Buffer moved with assignment operator, moved buffer size "< 3 | #include 4 | #include 5 | #include "gtest/gtest.h" 6 | 7 | #define TEST_BUFFER_SIZE 100 8 | #define REDUCE_SIZE 50 9 | 10 | class CircularBufferTest : public ::testing::Test{ 11 | 12 | protected: 13 | 14 | CircularBuffer test_str_buff{TEST_BUFFER_SIZE}; 15 | CircularBuffer test_int_buff{TEST_BUFFER_SIZE}; 16 | }; 17 | 18 | TEST_F(CircularBufferTest, IteratorBasedLoopTest){ 19 | //create full buffer 20 | for(int i=0; i()); 109 | int i = 99; 110 | for(const auto& elem: test_int_buff) 111 | EXPECT_EQ(elem, i--); 112 | } 113 | 114 | TEST_F(CircularBufferTest, CopyUsingIterator){ 115 | //create full buffer 116 | for(int i=0; i buffer_copy; 119 | buffer_copy.reserve(test_str_buff.size()); 120 | std::copy(test_str_buff.begin(), test_str_buff.end(), std::back_inserter(buffer_copy)); 121 | int i = 0; 122 | for(const auto& elem: test_str_buff) 123 | EXPECT_EQ(elem, buffer_copy[i++]); 124 | buffer_copy.clear(); 125 | //remove elements from buffer 126 | for(int i=0; i 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | 12 | template 13 | class CircularBuffer { 14 | private: 15 | 16 | typedef T* pointer; 17 | typedef const T* const_pointer; 18 | typedef T& reference; 19 | typedef const T& const_reference; 20 | typedef size_t size_type; 21 | typedef ptrdiff_t difference_type; 22 | template struct BufferIterator; 23 | 24 | 25 | public: 26 | typedef T value_type; 27 | 28 | explicit CircularBuffer(size_t size) 29 | :_buff{std::unique_ptr(new value_type[size])}, _max_size{size}{} 30 | 31 | CircularBuffer(const CircularBuffer& other) 32 | :_buff{std::unique_ptr(new value_type[other._max_size])}, 33 | _max_size{other._max_size}, 34 | _size{other._size}, 35 | _head{other._head}, 36 | _tail{other._tail}{ 37 | std::copy(other.data(), other.data() + _max_size, _buff.get()); 38 | } 39 | 40 | 41 | CircularBuffer& operator=(const CircularBuffer& other){ 42 | if ( this != &other){ 43 | _buff.reset(new value_type[other._max_size]); 44 | _max_size = other._max_size; 45 | _size = other._size; 46 | _head = other._head; 47 | _tail = other._tail; 48 | std::copy(other.data(), other.data() + _max_size, _buff.get()); 49 | } 50 | return *this; 51 | } 52 | 53 | CircularBuffer(CircularBuffer&& other) noexcept 54 | :_buff{std::move(other._buff)}, 55 | _max_size{other._max_size}, 56 | _size{other._size}, 57 | _head{other._head}, 58 | _tail{other._tail}{ 59 | 60 | other._buff = nullptr; 61 | other._max_size = 0; 62 | other._size = 0; 63 | other._head = 0; 64 | other._tail = 0; 65 | } 66 | 67 | 68 | CircularBuffer& operator=(CircularBuffer&& other) noexcept{ 69 | if ( this != &other){ 70 | _buff = std::move(other._buff); 71 | _max_size = other._max_size; 72 | _size = other._size; 73 | _head = other._head; 74 | _tail = other._tail; 75 | 76 | other._buff = nullptr; 77 | other._max_size = 0; 78 | other._size = 0; 79 | other._head = 0; 80 | other._tail = 0; 81 | } 82 | return *this; 83 | } 84 | 85 | void push_back(const value_type& data); 86 | void push_back(value_type&& data) noexcept; 87 | void pop_front(); 88 | reference front(); 89 | reference back(); 90 | const_reference front() const; 91 | const_reference back() const; 92 | void clear(); 93 | bool empty() const ; 94 | bool full() const ; 95 | size_type capacity() const ; 96 | size_type size() const; 97 | size_type buffer_size() const {return sizeof(value_type)*_max_size;}; 98 | const_pointer data() const { return _buff.get(); } 99 | 100 | const_reference operator[](size_type index) const; 101 | reference operator[](size_type index); 102 | const_reference at(size_type index) const; 103 | reference at(size_type index); 104 | 105 | typedef BufferIterator iterator; 106 | typedef BufferIterator const_iterator; 107 | 108 | iterator begin(); 109 | const_iterator begin() const; 110 | iterator end(); 111 | const_iterator end() const; 112 | const_iterator cbegin() const noexcept; 113 | const_iterator cend() const noexcept; 114 | iterator rbegin() noexcept; 115 | const_iterator rbegin() const noexcept; 116 | iterator rend() noexcept; 117 | const_iterator rend() const noexcept; 118 | 119 | 120 | private: 121 | void _increment_bufferstate(); 122 | void _decrement_bufferstate(); 123 | mutable std::mutex _mtx; 124 | std::unique_ptr _buff; 125 | size_type _head = 0; 126 | size_type _tail = 0; 127 | size_type _size = 0; 128 | size_type _max_size = 0; 129 | 130 | template 131 | struct BufferIterator{ 132 | public: 133 | friend class CircularBuffer; 134 | typedef std::random_access_iterator_tag iterator_category; 135 | typedef ptrdiff_t difference_type; 136 | typedef T value_type; 137 | typedef typename std::conditional::type reference; 138 | typedef typename std::conditional::type pointer; 139 | typedef typename std::conditional*, 140 | CircularBuffer*>::type cbuf_pointer; 141 | private: 142 | cbuf_pointer _ptrToBuffer; 143 | size_type _offset; 144 | size_type _index; 145 | bool _reverse; 146 | 147 | bool _comparable(const BufferIterator& other) const{ 148 | return (_ptrToBuffer == other._ptrToBuffer)&&(_reverse == other._reverse); 149 | } 150 | 151 | public: 152 | BufferIterator() 153 | :_ptrToBuffer{nullptr}, _offset{0}, _index{0}, _reverse{false}{} 154 | 155 | BufferIterator(const BufferIterator& it) 156 | :_ptrToBuffer{it._ptrToBuffer}, 157 | _offset{it._offset}, 158 | _index{it._index}, 159 | _reverse{it._reverse}{} 160 | 161 | reference operator*(){ 162 | if(_reverse) 163 | return (*_ptrToBuffer)[(_ptrToBuffer->size() - _index - 1)]; 164 | return (*_ptrToBuffer)[_index]; 165 | } 166 | 167 | pointer operator->() { return &(operator*()); } 168 | 169 | reference operator[](size_type index){ 170 | BufferIterator iter = *this; 171 | iter._index += index; 172 | return *iter; 173 | } 174 | 175 | BufferIterator& operator++(){ 176 | ++_index; 177 | return *this; 178 | } 179 | 180 | BufferIterator operator++(int){ 181 | BufferIterator iter = *this; 182 | ++_index; 183 | return iter; 184 | } 185 | 186 | BufferIterator& operator--(){ 187 | --_index; 188 | return *this; 189 | } 190 | 191 | BufferIterator operator--(int){ 192 | BufferIterator iter = *this; 193 | --_index; 194 | return iter; 195 | } 196 | 197 | friend BufferIterator operator+(BufferIterator lhsiter, difference_type n){ 198 | lhsiter._index += n; 199 | return lhsiter; 200 | } 201 | 202 | friend BufferIterator operator+(difference_type n, BufferIterator rhsiter){ 203 | rhsiter._index += n; 204 | return rhsiter; 205 | } 206 | 207 | 208 | BufferIterator& operator+=(difference_type n){ 209 | _index += n; 210 | return *this; 211 | } 212 | 213 | friend BufferIterator operator-(BufferIterator lhsiter, difference_type n){ 214 | lhsiter._index -= n; 215 | return lhsiter; 216 | } 217 | 218 | friend difference_type operator-(const BufferIterator& lhsiter, const BufferIterator& rhsiter){ 219 | 220 | return lhsiter._index - rhsiter._index; 221 | } 222 | 223 | BufferIterator& operator-=(difference_type n){ 224 | _index -= n; 225 | return *this; 226 | } 227 | 228 | bool operator==(const BufferIterator& other) const{ 229 | if (!_comparable(other)) 230 | return false; 231 | return ((_index == other._index)&&(_offset == other._offset)); 232 | } 233 | 234 | bool operator!=(const BufferIterator& other) const{ 235 | if (!_comparable(other)) 236 | return true; 237 | return ((_index != other._index)||(_offset != other._offset)); 238 | } 239 | 240 | bool operator<(const BufferIterator& other) const { 241 | if (!_comparable(other)) 242 | return false; 243 | return ((_index + _offset)<(other._index+other._offset)); 244 | } 245 | 246 | bool operator>(const BufferIterator& other) const{ 247 | if (!_comparable(other)) 248 | return false; 249 | return ((_index + _offset)>(other._index+other._offset)); 250 | } 251 | 252 | bool operator<=(const BufferIterator& other) const { 253 | if (!_comparable(other)) 254 | return false; 255 | return ((_index + _offset)<=(other._index+other._offset)); 256 | } 257 | 258 | bool operator>=(const BufferIterator& other) const { 259 | if (!_comparable(other)) 260 | return false; 261 | return ((_index + _offset)>=(other._index+other._offset)); 262 | } 263 | }; 264 | }; 265 | 266 | template 267 | inline 268 | bool CircularBuffer::full() const{ 269 | return _size == _max_size; 270 | } 271 | 272 | template 273 | inline 274 | bool CircularBuffer::empty() const{ 275 | return _size == 0; 276 | } 277 | 278 | template 279 | inline 280 | typename CircularBuffer::size_type CircularBuffer::capacity() const{ 281 | return _max_size; 282 | } 283 | 284 | template 285 | inline 286 | void CircularBuffer::clear(){ 287 | std::lock_guard _lck(_mtx); 288 | _head = _tail = _size = 0; 289 | } 290 | 291 | template 292 | inline 293 | typename CircularBuffer::size_type CircularBuffer::size() const{ 294 | std::lock_guard _lck(_mtx); 295 | return _size; 296 | } 297 | 298 | template 299 | inline 300 | typename CircularBuffer::reference CircularBuffer::front() { 301 | std::lock_guard _lck(_mtx); 302 | if(empty()) 303 | throw std::length_error("front function called on empty buffer"); 304 | return _buff[_tail]; 305 | } 306 | 307 | template 308 | inline 309 | typename CircularBuffer::reference CircularBuffer::back() { 310 | std::lock_guard _lck(_mtx); 311 | if(empty()) 312 | throw std::length_error("back function called on empty buffer"); 313 | return _head == 0 ? _buff[_max_size - 1] : _buff[_head - 1]; 314 | } 315 | 316 | template 317 | inline 318 | typename CircularBuffer::const_reference CircularBuffer::front() const{ 319 | std::lock_guard _lck(_mtx); 320 | if(empty()) 321 | throw std::length_error("front function called on empty buffer"); 322 | return _buff[_tail]; 323 | } 324 | 325 | template 326 | inline 327 | typename CircularBuffer::const_reference CircularBuffer::back() const{ 328 | std::lock_guard _lck(_mtx); 329 | if(empty()) 330 | throw std::length_error("back function called on empty buffer"); 331 | return _head == 0 ? _buff[_max_size - 1] : _buff[_head - 1]; 332 | } 333 | 334 | template 335 | inline 336 | void CircularBuffer::push_back(const T& data){ 337 | std::lock_guard _lck(_mtx); 338 | //if(full()) 339 | // _buff[_tail].~T(); 340 | _buff[_head] = data; 341 | _increment_bufferstate(); 342 | } 343 | 344 | template 345 | inline 346 | void CircularBuffer::push_back(T&& data) noexcept{ 347 | std::lock_guard _lck(_mtx); 348 | _buff[_head] = std::move(data); 349 | _increment_bufferstate(); 350 | } 351 | 352 | 353 | template 354 | inline 355 | void CircularBuffer::_increment_bufferstate(){ 356 | if(full()) 357 | _tail = (_tail + 1)%_max_size; 358 | else 359 | ++_size; 360 | _head = (_head + 1)%_max_size; 361 | } 362 | 363 | template 364 | inline 365 | void CircularBuffer::pop_front(){ 366 | std::lock_guard _lck(_mtx); 367 | if(empty()) 368 | throw std::length_error("pop_front called on empty buffer"); 369 | _decrement_bufferstate(); 370 | } 371 | 372 | template 373 | inline 374 | void CircularBuffer::_decrement_bufferstate(){ 375 | --_size; 376 | _tail = (_tail + 1)%_max_size; 377 | } 378 | 379 | template 380 | inline 381 | typename CircularBuffer::reference CircularBuffer::operator[](size_t index) { 382 | std::lock_guard _lck(_mtx); 383 | if((index<0)||(index>=_size)) 384 | throw std::out_of_range("Index is out of Range of buffer size"); 385 | index += _tail; 386 | index %= _max_size; 387 | return _buff[index]; 388 | } 389 | 390 | template 391 | inline 392 | typename CircularBuffer::const_reference CircularBuffer::operator[](size_t index) const { 393 | std::lock_guard _lck(_mtx); 394 | if((index<0)||(index>=_size)) 395 | throw std::out_of_range("Index is out of Range of buffer size"); 396 | index += _tail; 397 | index %= _max_size; 398 | return _buff[index]; 399 | } 400 | 401 | template 402 | inline 403 | typename CircularBuffer::reference CircularBuffer::at(size_t index) { 404 | std::lock_guard _lck(_mtx); 405 | if((index<0)||(index>=_size)) 406 | throw std::out_of_range("Index is out of Range of buffer size"); 407 | index += _tail; 408 | index %= _max_size; 409 | return _buff[index]; 410 | } 411 | 412 | template 413 | inline 414 | typename CircularBuffer::const_reference CircularBuffer::at(size_t index) const { 415 | std::lock_guard _lck(_mtx); 416 | if((index<0)||(index>=_size)) 417 | throw std::out_of_range("Index is out of Range of buffer size"); 418 | index += _tail; 419 | index %= _max_size; 420 | return _buff[index]; 421 | } 422 | 423 | template 424 | inline 425 | typename CircularBuffer::iterator CircularBuffer::begin() { 426 | std::lock_guard _lck(_mtx); 427 | iterator iter; 428 | iter._ptrToBuffer = this; 429 | iter._offset = _tail; 430 | iter._index = 0; 431 | iter._reverse = false; 432 | return iter; 433 | } 434 | 435 | template 436 | inline 437 | typename CircularBuffer::const_iterator CircularBuffer::begin() const{ 438 | std::lock_guard _lck(_mtx); 439 | const_iterator iter; 440 | iter._ptrToBuffer = this; 441 | iter._offset = _tail; 442 | iter._index = 0; 443 | iter._reverse = false; 444 | return iter; 445 | } 446 | 447 | template 448 | inline 449 | typename CircularBuffer::iterator CircularBuffer::end() { 450 | std::lock_guard _lck(_mtx); 451 | iterator iter; 452 | iter._ptrToBuffer = this; 453 | iter._offset = _tail; 454 | iter._index = _size; 455 | iter._reverse = false; 456 | return iter; 457 | } 458 | 459 | template 460 | inline 461 | typename CircularBuffer::const_iterator CircularBuffer::end() const{ 462 | std::lock_guard _lck(_mtx); 463 | const_iterator iter; 464 | iter._ptrToBuffer = this; 465 | iter._offset = _tail; 466 | iter._index = _size; 467 | iter._reverse = false; 468 | return iter; 469 | } 470 | 471 | template 472 | inline 473 | typename CircularBuffer::const_iterator CircularBuffer::cbegin() const noexcept{ 474 | std::lock_guard _lck(_mtx); 475 | const_iterator iter; 476 | iter._ptrToBuffer = this; 477 | iter._offset = _tail; 478 | iter._index = 0; 479 | iter._reverse = false; 480 | return iter; 481 | } 482 | 483 | template 484 | inline 485 | typename CircularBuffer::const_iterator CircularBuffer::cend() const noexcept{ 486 | std::lock_guard _lck(_mtx); 487 | const_iterator iter; 488 | iter._ptrToBuffer = this; 489 | iter._offset = _tail; 490 | iter._index = _size; 491 | iter._reverse = false; 492 | return iter; 493 | } 494 | 495 | template 496 | inline 497 | typename CircularBuffer::iterator CircularBuffer::rbegin() noexcept{ 498 | std::lock_guard _lck(_mtx); 499 | iterator iter; 500 | iter._ptrToBuffer = this; 501 | iter._offset = _tail; 502 | iter._index = 0; 503 | iter._reverse = true; 504 | return iter; 505 | } 506 | 507 | template 508 | inline 509 | typename CircularBuffer::const_iterator CircularBuffer::rbegin() const noexcept{ 510 | std::lock_guard _lck(_mtx); 511 | const_iterator iter; 512 | iter._ptrToBuffer = this; 513 | iter._offset = _tail; 514 | iter._index = 0; 515 | iter._reverse = true; 516 | return iter; 517 | } 518 | 519 | template 520 | inline 521 | typename CircularBuffer::iterator CircularBuffer::rend() noexcept{ 522 | std::lock_guard _lck(_mtx); 523 | iterator iter; 524 | iter._ptrToBuffer = this; 525 | iter._offset = _tail; 526 | iter._index = _size; 527 | iter._reverse = true; 528 | return iter; 529 | } 530 | 531 | template 532 | inline 533 | typename CircularBuffer::const_iterator CircularBuffer::rend() const noexcept{ 534 | std::lock_guard _lck(_mtx); 535 | const_iterator iter; 536 | iter._ptrToBuffer = this; 537 | iter._offset = _tail; 538 | iter._index = _size; 539 | iter._reverse = true; 540 | return iter; 541 | } 542 | 543 | #endif /* CIRCULAR_BUFFER_H */ 544 | -------------------------------------------------------------------------------- /tests/member_func_test.cpp: -------------------------------------------------------------------------------- 1 | #include "circular_buffer.h" 2 | #include 3 | #include 4 | #include "gtest/gtest.h" 5 | 6 | #define TEST_BUFFER_SIZE 100 7 | #define REDUCE_SIZE 100 8 | 9 | class CircularBufferTest : public ::testing::Test{ 10 | 11 | protected: 12 | 13 | CircularBuffer test_buff{TEST_BUFFER_SIZE}; 14 | 15 | }; 16 | 17 | TEST_F(CircularBufferTest, CapacityTest){ 18 | EXPECT_EQ(TEST_BUFFER_SIZE, test_buff.capacity()); 19 | EXPECT_FALSE(test_buff.capacity() == 0); 20 | } 21 | 22 | TEST_F(CircularBufferTest, EmptyTest){ 23 | EXPECT_TRUE(test_buff.empty()); 24 | test_buff.push_back("string 1"); 25 | test_buff.push_back("string 2"); 26 | EXPECT_FALSE(test_buff.empty()); 27 | test_buff.pop_front(); 28 | test_buff.pop_front(); 29 | EXPECT_TRUE(test_buff.empty()); 30 | for(int i=0; i::iterator it = test_buff.begin(); 291 | EXPECT_EQ(*it, "string0" ); 292 | //access with begin iterator 293 | for(int i=0; i::const_iterator const_it = test_buff.begin(); 298 | for(int i=0; i::iterator it = test_buff.rbegin(); 324 | //access with rbegin iterator 325 | for(int i=TEST_BUFFER_SIZE-1; i>=0; i--) 326 | EXPECT_EQ(*(it++), "string" + std::to_string(i)); 327 | 328 | //access with const begin iterator 329 | CircularBuffer::const_iterator const_it = test_buff.rbegin(); 330 | for(int i=TEST_BUFFER_SIZE - 1; i>=0; i--) 331 | EXPECT_EQ(*(const_it++), "string" + std::to_string(i)); 332 | //test out of bounds 333 | try { 334 | *it = "test_string"; 335 | FAIL() << "Expected std::out_of_range error"; 336 | } 337 | catch(const std::out_of_range& err){ 338 | EXPECT_EQ(err.what(), std::string("Index is out of Range of buffer size")); 339 | } 340 | 341 | try { 342 | std::string out_of_bound = *(const_it); 343 | FAIL() << "Expected std::out_of_range error"; 344 | } 345 | catch(const std::out_of_range& err){ 346 | EXPECT_EQ(err.what(), std::string("Index is out of Range of buffer size")); 347 | } 348 | } 349 | 350 | TEST_F(CircularBufferTest, CbeginIteratorTest){ 351 | //create full buffer 352 | for(int i=0; i::const_iterator const_it = test_buff.cbegin(); 356 | EXPECT_EQ(*const_it, "string0" ); 357 | //access with begin iterator 358 | for(int i=0; i::iterator it = test_buff.end(); 377 | //access with end iterator 378 | for(int i = TEST_BUFFER_SIZE-1; i>=0; i--) 379 | EXPECT_EQ(*(--it), "string" + std::to_string(i)); 380 | 381 | //access with const end iterator 382 | CircularBuffer::const_iterator const_it = test_buff.end(); 383 | for(int i = TEST_BUFFER_SIZE-1; i>=0; i--) 384 | EXPECT_EQ(*(--const_it), "string" + std::to_string(i)); 385 | 386 | //test out of bounds 387 | try { 388 | *(--it) = "test_string"; 389 | FAIL() << "Expected std::out_of_range error"; 390 | } 391 | catch(const std::out_of_range& err){ 392 | EXPECT_EQ(err.what(), std::string("Index is out of Range of buffer size")); 393 | } 394 | 395 | try { 396 | std::string out_of_bound = *(--const_it); 397 | FAIL() << "Expected std::out_of_range error"; 398 | } 399 | catch(const std::out_of_range& err){ 400 | EXPECT_EQ(err.what(), std::string("Index is out of Range of buffer size")); 401 | } 402 | } 403 | 404 | TEST_F(CircularBufferTest, RendIteratorTest){ 405 | //create full buffer 406 | for(int i=0; i::iterator it = test_buff.rend() - 1; 410 | //access with rbegin iterator 411 | for(int i=0; i::const_iterator const_it = test_buff.rend() - 1; 416 | for(int i=0; i::const_iterator const_it = test_buff.cend(); 443 | //access with end iterator 444 | for(int i = TEST_BUFFER_SIZE-1; i>=0; i--) 445 | EXPECT_EQ(*(--const_it), "string" + std::to_string(i)); 446 | 447 | try { 448 | std::string out_of_bound = *(--const_it); 449 | FAIL() << "Expected std::out_of_range error"; 450 | } 451 | catch(const std::out_of_range& err){ 452 | EXPECT_EQ(err.what(), std::string("Index is out of Range of buffer size")); 453 | } 454 | } 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | --------------------------------------------------------------------------------