├── .clang-format ├── .gitignore ├── README.md ├── clang-format.bash ├── include └── small_vector │ └── small_vector.h └── test ├── .gitignore ├── CMakeLists.txt ├── doctest.hpp ├── main.cpp ├── test_access.cpp ├── test_constructors.cpp ├── test_fill.cpp ├── test_iterators.cpp ├── test_push_pop.cpp ├── test_sizing.cpp └── test_swap.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: LLVM 4 | AccessModifierOffset: -2 5 | AlignAfterOpenBracket: Align 6 | AlignConsecutiveAssignments: false 7 | AlignConsecutiveDeclarations: false 8 | AlignEscapedNewlines: Right 9 | AlignOperands: true 10 | AlignTrailingComments: true 11 | AllowAllParametersOfDeclarationOnNextLine: true 12 | AllowShortBlocksOnASingleLine: false 13 | AllowShortCaseLabelsOnASingleLine: false 14 | AllowShortFunctionsOnASingleLine: All 15 | AllowShortIfStatementsOnASingleLine: false 16 | AllowShortLoopsOnASingleLine: false 17 | AlwaysBreakAfterDefinitionReturnType: None 18 | AlwaysBreakAfterReturnType: None 19 | AlwaysBreakBeforeMultilineStrings: false 20 | AlwaysBreakTemplateDeclarations: false 21 | BinPackArguments: true 22 | BinPackParameters: true 23 | BraceWrapping: 24 | AfterClass: false 25 | AfterControlStatement: false 26 | AfterEnum: false 27 | AfterFunction: false 28 | AfterNamespace: false 29 | AfterObjCDeclaration: false 30 | AfterStruct: false 31 | AfterUnion: false 32 | AfterExternBlock: false 33 | BeforeCatch: false 34 | BeforeElse: false 35 | IndentBraces: false 36 | SplitEmptyFunction: true 37 | SplitEmptyRecord: true 38 | SplitEmptyNamespace: true 39 | BreakBeforeBinaryOperators: None 40 | BreakBeforeBraces: Attach 41 | BreakBeforeInheritanceComma: false 42 | BreakBeforeTernaryOperators: true 43 | BreakConstructorInitializersBeforeComma: false 44 | BreakConstructorInitializers: BeforeColon 45 | BreakAfterJavaFieldAnnotations: false 46 | BreakStringLiterals: true 47 | ColumnLimit: 80 48 | CommentPragmas: '^ IWYU pragma:' 49 | CompactNamespaces: false 50 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 51 | ConstructorInitializerIndentWidth: 4 52 | ContinuationIndentWidth: 4 53 | Cpp11BracedListStyle: true 54 | DerivePointerAlignment: false 55 | DisableFormat: false 56 | ExperimentalAutoDetectBinPacking: false 57 | FixNamespaceComments: true 58 | ForEachMacros: 59 | - foreach 60 | - Q_FOREACH 61 | - BOOST_FOREACH 62 | IncludeBlocks: Preserve 63 | IncludeCategories: 64 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/' 65 | Priority: 2 66 | - Regex: '^(<|"(gtest|gmock|isl|json)/)' 67 | Priority: 3 68 | - Regex: '.*' 69 | Priority: 1 70 | IncludeIsMainRegex: '(Test)?$' 71 | IndentCaseLabels: false 72 | IndentPPDirectives: None 73 | IndentWidth: 2 74 | IndentWrappedFunctionNames: false 75 | JavaScriptQuotes: Leave 76 | JavaScriptWrapImports: true 77 | KeepEmptyLinesAtTheStartOfBlocks: true 78 | MacroBlockBegin: '' 79 | MacroBlockEnd: '' 80 | MaxEmptyLinesToKeep: 1 81 | NamespaceIndentation: None 82 | ObjCBlockIndentWidth: 2 83 | ObjCSpaceAfterProperty: false 84 | ObjCSpaceBeforeProtocolList: true 85 | PenaltyBreakAssignment: 2 86 | PenaltyBreakBeforeFirstCallParameter: 19 87 | PenaltyBreakComment: 300 88 | PenaltyBreakFirstLessLess: 120 89 | PenaltyBreakString: 1000 90 | PenaltyExcessCharacter: 1000000 91 | PenaltyReturnTypeOnItsOwnLine: 60 92 | PointerAlignment: Right 93 | RawStringFormats: 94 | - Language: TextProto 95 | Delimiters: 96 | - 'pb' 97 | - 'proto' 98 | BasedOnStyle: google 99 | ReflowComments: true 100 | SortIncludes: true 101 | SortUsingDeclarations: true 102 | SpaceAfterCStyleCast: false 103 | SpaceAfterTemplateKeyword: true 104 | SpaceBeforeAssignmentOperators: true 105 | SpaceBeforeParens: ControlStatements 106 | SpaceInEmptyParentheses: false 107 | SpacesBeforeTrailingComments: 1 108 | SpacesInAngles: false 109 | SpacesInContainerLiterals: true 110 | SpacesInCStyleCastParentheses: false 111 | SpacesInParentheses: false 112 | SpacesInSquareBrackets: false 113 | Standard: Cpp11 114 | TabWidth: 8 115 | UseTab: Never 116 | ... 117 | 118 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | .vscode/ 28 | # Uncomment if you have tasks that create the project's static files in wwwroot 29 | #wwwroot/ 30 | 31 | # MSTest test Results 32 | [Tt]est[Rr]esult*/ 33 | [Bb]uild[Ll]og.* 34 | 35 | # NUNIT 36 | *.VisualState.xml 37 | TestResult.xml 38 | 39 | # Build Results of an ATL Project 40 | [Dd]ebugPS/ 41 | [Rr]eleasePS/ 42 | dlldata.c 43 | 44 | # DNX 45 | project.lock.json 46 | project.fragment.lock.json 47 | artifacts/ 48 | 49 | *_i.c 50 | *_p.c 51 | *_i.h 52 | *.ilk 53 | *.meta 54 | *.obj 55 | *.pch 56 | *.pdb 57 | *.pgc 58 | *.pgd 59 | *.rsp 60 | *.sbr 61 | *.tlb 62 | *.tli 63 | *.tlh 64 | *.tmp 65 | *.tmp_proj 66 | *.log 67 | *.vspscc 68 | *.vssscc 69 | .builds 70 | *.pidb 71 | *.svclog 72 | *.scc 73 | 74 | # Chutzpah Test files 75 | _Chutzpah* 76 | 77 | # Visual C++ cache files 78 | ipch/ 79 | *.aps 80 | *.ncb 81 | *.opendb 82 | *.opensdf 83 | *.sdf 84 | *.cachefile 85 | *.VC.db 86 | *.VC.VC.opendb 87 | 88 | # Visual Studio profiler 89 | *.psess 90 | *.vsp 91 | *.vspx 92 | *.sap 93 | 94 | # TFS 2012 Local Workspace 95 | $tf/ 96 | 97 | # Guidance Automation Toolkit 98 | *.gpState 99 | 100 | # ReSharper is a .NET coding add-in 101 | _ReSharper*/ 102 | *.[Rr]e[Ss]harper 103 | *.DotSettings.user 104 | 105 | # JustCode is a .NET coding add-in 106 | .JustCode 107 | 108 | # TeamCity is a build add-in 109 | _TeamCity* 110 | 111 | # DotCover is a Code Coverage Tool 112 | *.dotCover 113 | 114 | # NCrunch 115 | _NCrunch_* 116 | .*crunch*.local.xml 117 | nCrunchTemp_* 118 | 119 | # MightyMoose 120 | *.mm.* 121 | AutoTest.Net/ 122 | 123 | # Web workbench (sass) 124 | .sass-cache/ 125 | 126 | # Installshield output folder 127 | [Ee]xpress/ 128 | 129 | # DocProject is a documentation generator add-in 130 | DocProject/buildhelp/ 131 | DocProject/Help/*.HxT 132 | DocProject/Help/*.HxC 133 | DocProject/Help/*.hhc 134 | DocProject/Help/*.hhk 135 | DocProject/Help/*.hhp 136 | DocProject/Help/Html2 137 | DocProject/Help/html 138 | 139 | # Click-Once directory 140 | publish/ 141 | 142 | # Publish Web Output 143 | *.[Pp]ublish.xml 144 | *.azurePubxml 145 | # TODO: Comment the next line if you want to checkin your web deploy settings 146 | # but database connection strings (with potential passwords) will be unencrypted 147 | #*.pubxml 148 | *.publishproj 149 | 150 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 151 | # checkin your Azure Web App publish settings, but sensitive information contained 152 | # in these scripts will be unencrypted 153 | PublishScripts/ 154 | 155 | # NuGet Packages 156 | *.nupkg 157 | # The packages folder can be ignored because of Package Restore 158 | **/packages/* 159 | # except build/, which is used as an MSBuild target. 160 | !**/packages/build/ 161 | # Uncomment if necessary however generally it will be regenerated when needed 162 | #!**/packages/repositories.config 163 | # NuGet v3's project.json files produces more ignoreable files 164 | *.nuget.props 165 | *.nuget.targets 166 | 167 | # Microsoft Azure Build Output 168 | csx/ 169 | *.build.csdef 170 | 171 | # Microsoft Azure Emulator 172 | ecf/ 173 | rcf/ 174 | 175 | # Windows Store app package directories and files 176 | AppPackages/ 177 | BundleArtifacts/ 178 | Package.StoreAssociation.xml 179 | _pkginfo.txt 180 | 181 | # Visual Studio cache files 182 | # files ending in .cache can be ignored 183 | *.[Cc]ache 184 | # but keep track of directories ending in .cache 185 | !*.[Cc]ache/ 186 | 187 | # Others 188 | ClientBin/ 189 | ~$* 190 | *~ 191 | *.dbmdl 192 | *.dbproj.schemaview 193 | *.jfm 194 | *.pfx 195 | *.publishsettings 196 | node_modules/ 197 | orleans.codegen.cs 198 | 199 | # Since there are multiple workflows, uncomment next line to ignore bower_components 200 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 201 | #bower_components/ 202 | 203 | # RIA/Silverlight projects 204 | Generated_Code/ 205 | 206 | # Backup & report files from converting an old project file 207 | # to a newer Visual Studio version. Backup files are not needed, 208 | # because we have git ;-) 209 | _UpgradeReport_Files/ 210 | Backup*/ 211 | UpgradeLog*.XML 212 | UpgradeLog*.htm 213 | 214 | # SQL Server files 215 | *.mdf 216 | *.ldf 217 | 218 | # Business Intelligence projects 219 | *.rdl.data 220 | *.bim.layout 221 | *.bim_*.settings 222 | 223 | # Microsoft Fakes 224 | FakesAssemblies/ 225 | 226 | # GhostDoc plugin setting file 227 | *.GhostDoc.xml 228 | 229 | # Node.js Tools for Visual Studio 230 | .ntvs_analysis.dat 231 | 232 | # Visual Studio 6 build log 233 | *.plg 234 | 235 | # Visual Studio 6 workspace options file 236 | *.opt 237 | 238 | # Visual Studio LightSwitch build output 239 | **/*.HTMLClient/GeneratedArtifacts 240 | **/*.DesktopClient/GeneratedArtifacts 241 | **/*.DesktopClient/ModelManifest.xml 242 | **/*.Server/GeneratedArtifacts 243 | **/*.Server/ModelManifest.xml 244 | _Pvt_Extensions 245 | 246 | # Paket dependency manager 247 | .paket/paket.exe 248 | paket-files/ 249 | 250 | # FAKE - F# Make 251 | .fake/ 252 | 253 | # JetBrains Rider 254 | .idea/ 255 | *.sln.iml 256 | 257 | # CodeRush 258 | .cr/ 259 | 260 | # Python Tools for Visual Studio (PTVS) 261 | __pycache__/ 262 | *.pyc 263 | 264 | # CMake build directory 265 | build 266 | 267 | # Cppcheck build directory 268 | analysis-cppcheck-build-dir 269 | 270 | # Ideas directory 271 | ideas 272 | 273 | desktop.iniimages/ 274 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | small_vector 2 | ============ 3 | 4 | "Small Vector" optimization for Modern C++: store up to a small number of items on the stack 5 | 6 | ## Example 7 | 8 | ```cpp 9 | #include 10 | #include 11 | 12 | #include 13 | using namespace sv; 14 | 15 | int main() { 16 | 17 | // This small vector can hold up to 4 items on the stack 18 | small_vector vec{1, 2, 3, 4}; 19 | 20 | // It will move its contents to the heap if it 21 | // contains more than four items: 22 | vec.push_back(5); 23 | 24 | // Access and modify contents 25 | vec[0] = vec[1] + vec[2]; 26 | 27 | // Iterate and print contents 28 | std::copy(vec.begin(), vec.end(), std::ostream_iterator(std::cout, " ")); 29 | 30 | } 31 | ``` 32 | -------------------------------------------------------------------------------- /clang-format.bash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | find ./include -type f \( -iname \*.cpp -o -iname \*.h \) | xargs clang-format -style="{ColumnLimit : 100}" -i 3 | -------------------------------------------------------------------------------- /include/small_vector/small_vector.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | namespace sv { 7 | 8 | template > class small_vector { 9 | std::array stack_; 10 | std::vector heap_; 11 | std::size_t size_{0}; 12 | 13 | public: 14 | typedef T value_type; 15 | typedef std::size_t size_type; 16 | typedef value_type &reference; 17 | typedef const value_type &const_reference; 18 | typedef Allocator allocator_type; 19 | typedef T *pointer; 20 | typedef const T *const_pointer; 21 | 22 | small_vector() = default; 23 | 24 | explicit small_vector(size_type count, const T &value = T(), 25 | const Allocator &alloc = Allocator()) { 26 | if (count == N) { 27 | stack_.fill(value); 28 | } else if (count < N) { 29 | for (size_t i = 0; i < count; i++) { 30 | stack_[i] = value; 31 | } 32 | } else { 33 | // use heap 34 | heap_ = std::move(std::vector(count, value, alloc)); 35 | } 36 | size_ = count; 37 | } 38 | 39 | small_vector(const small_vector &other, const Allocator &alloc = Allocator()) 40 | : stack_(other.stack_), heap_(other.heap_, alloc), size_(other.size_) {} 41 | 42 | small_vector(small_vector &&other, const Allocator &alloc = Allocator()) 43 | : stack_(std::move(other.stack_)), heap_(std::move(other.heap_), alloc), 44 | size_(std::move(other.size_)) {} 45 | 46 | small_vector(std::initializer_list initlist, const Allocator &alloc = Allocator()) { 47 | const auto input_size = initlist.size(); 48 | if (input_size <= N) { 49 | std::copy(initlist.begin(), initlist.end(), stack_.begin()); 50 | } else { 51 | heap_ = std::move(std::vector(initlist, alloc)); 52 | } 53 | size_ = input_size; 54 | } 55 | 56 | small_vector &operator=(const small_vector &rhs) { 57 | stack_ = rhs.stack_; 58 | heap_ = rhs.heap_; 59 | size_ = rhs.size_; 60 | return *this; 61 | } 62 | 63 | small_vector &operator=(small_vector &&rhs) { 64 | stack_ = std::move(rhs.stack_); 65 | heap_ = std::move(rhs.heap_); 66 | size_ = rhs.size_; 67 | rhs.size_ = 0; 68 | return *this; 69 | } 70 | 71 | small_vector &operator=(std::initializer_list rhs) { 72 | if (rhs.size() <= N) { 73 | stack_ = rhs; 74 | } else { 75 | heap_ = rhs; 76 | } 77 | size_ = rhs.size(); 78 | } 79 | 80 | allocator_type get_allocator() const noexcept { return heap_.get_allocator(); } 81 | 82 | reference at(size_type pos) { 83 | if (size_ < N) { 84 | return stack_.at(pos); 85 | } else { 86 | return heap_.at(pos); 87 | } 88 | } 89 | 90 | const_reference at(size_type pos) const { 91 | if (size_ < N) { 92 | return stack_.at(pos); 93 | } else { 94 | return heap_.at(pos); 95 | } 96 | } 97 | 98 | reference operator[](size_type pos) { 99 | if (size_ < N) { 100 | return stack_[pos]; 101 | } else { 102 | return heap_[pos]; 103 | } 104 | } 105 | 106 | const_reference operator[](size_type pos) const { 107 | if (size_ < N) { 108 | return stack_[pos]; 109 | } else { 110 | return heap_[pos]; 111 | } 112 | } 113 | 114 | reference front() { 115 | if (size_ < N) { 116 | return stack_.front(); 117 | } else { 118 | return heap_.front(); 119 | } 120 | } 121 | 122 | const_reference front() const { 123 | if (size_ < N) { 124 | return stack_.front(); 125 | } else { 126 | return heap_.front(); 127 | } 128 | } 129 | 130 | reference back() { 131 | if (size_ < N) { 132 | return stack_[size_ - 1]; 133 | } else { 134 | return heap_[size_ - 1]; 135 | } 136 | } 137 | 138 | const_reference back() const { 139 | if (size_ < N) { 140 | return stack_[size_ - 1]; 141 | } else { 142 | return heap_[size_ - 1]; 143 | } 144 | } 145 | 146 | pointer data() noexcept { 147 | if (size_ < N) { 148 | return stack_.data(); 149 | } else { 150 | return heap_.data(); 151 | } 152 | } 153 | 154 | const_pointer data() const noexcept { 155 | if (size_ < N) { 156 | return stack_.data(); 157 | } else { 158 | return heap_.data(); 159 | } 160 | } 161 | 162 | bool empty() const { return size_ == 0; } 163 | 164 | size_type size() const { return size_; } 165 | 166 | void shrink_to_fit() { 167 | if (size_ >= N) { 168 | heap_.shrink_to_fit(); 169 | } 170 | } 171 | 172 | void push_back(const T &value) { 173 | if (size_ < N) { 174 | stack_[size_] = value; 175 | } else { 176 | if (size_ == N) { 177 | // move everything to heap 178 | std::move(stack_.begin(), stack_.end(), std::back_inserter(heap_)); 179 | } 180 | heap_.push_back(value); 181 | } 182 | size_ += 1; 183 | } 184 | 185 | void push_back(T &&value) { 186 | if (size_ < N) { 187 | stack_[size_] = std::move(value); 188 | } else { 189 | if (size_ == N) { 190 | // move everything to heap 191 | std::move(stack_.begin(), stack_.end(), std::back_inserter(heap_)); 192 | } 193 | heap_.push_back(std::move(value)); 194 | } 195 | size_ += 1; 196 | } 197 | 198 | void pop_back() { 199 | if (size_ == 0) { 200 | // do nothing 201 | return; 202 | } 203 | 204 | if (size_ < N) { 205 | size_ -= 1; 206 | } else { 207 | // currently using heap 208 | heap_.pop_back(); 209 | size_ -= 1; 210 | 211 | // now check if all data can fit on stack 212 | // if so, move back to stack 213 | if (size_ < N) { 214 | std::move(heap_.begin(), heap_.end(), stack_.begin()); 215 | heap_.clear(); 216 | } 217 | } 218 | } 219 | 220 | // Resizes the container to contain count elements. 221 | void resize(size_type count, T value = T()) { 222 | if (count <= N) { 223 | // new `count` of elements completely fit on stack 224 | if (size_ >= N) { 225 | // currently, all data on heap 226 | // move back to stack 227 | std::move(heap_.begin(), heap_.end(), stack_.begin()); 228 | } else { 229 | // all data already on stack 230 | // just update size 231 | } 232 | } else { 233 | // new `count` of data is going to be on the heap 234 | // check if data is currently on the stack 235 | if (size_ < N) { 236 | // move to heap 237 | std::move(stack_.begin(), stack_.end(), std::back_inserter(heap_)); 238 | } 239 | heap_.resize(count, value); 240 | } 241 | size_ = count; 242 | } 243 | 244 | void swap(small_vector &other) noexcept { 245 | std::swap(stack_, other.stack_); 246 | std::swap(heap_, other.heap_); 247 | std::swap(size_, other.size_); 248 | }; 249 | 250 | // Assigns the given value value to all elements in the container 251 | void fill(const_reference value) { 252 | if (size_ < N) { 253 | stack_.fill(value); 254 | } else { 255 | std::fill(heap_.begin(), heap_.end(), value); 256 | } 257 | } 258 | 259 | class iterator { 260 | pointer ptr_; 261 | 262 | public: 263 | typedef iterator self_type; 264 | typedef T value_type; 265 | typedef T &reference; 266 | typedef T *pointer; 267 | typedef std::forward_iterator_tag iterator_category; 268 | typedef int difference_type; 269 | iterator(pointer ptr) : ptr_(ptr) {} 270 | self_type operator++() { 271 | ptr_++; 272 | return *this; 273 | } 274 | self_type operator++(int) { 275 | self_type i = *this; 276 | ptr_++; 277 | return i; 278 | } 279 | reference operator*() { return *ptr_; } 280 | pointer operator->() { return ptr_; } 281 | bool operator==(const self_type &rhs) { return ptr_ == rhs.ptr_; } 282 | bool operator!=(const self_type &rhs) { return ptr_ != rhs.ptr_; } 283 | }; 284 | 285 | class const_iterator { 286 | pointer ptr_; 287 | 288 | public: 289 | typedef const_iterator self_type; 290 | typedef T value_type; 291 | typedef T &reference; 292 | typedef T *pointer; 293 | typedef int difference_type; 294 | typedef std::forward_iterator_tag iterator_category; 295 | const_iterator(pointer ptr) : ptr_(ptr) {} 296 | self_type operator++() { 297 | ptr_++; 298 | return *this; 299 | } 300 | self_type operator++(int) { 301 | self_type i = *this; 302 | ptr_++; 303 | return i; 304 | } 305 | const value_type &operator*() { return *ptr_; } 306 | const pointer operator->() { return ptr_; } 307 | bool operator==(const self_type &rhs) { return ptr_ == rhs.ptr_; } 308 | bool operator!=(const self_type &rhs) { return ptr_ != rhs.ptr_; } 309 | }; 310 | 311 | iterator begin() { 312 | if (size_ < N) { 313 | return iterator(stack_.data()); 314 | } else { 315 | return iterator(heap_.data()); 316 | } 317 | } 318 | 319 | iterator end() { 320 | if (size_ < N) { 321 | return iterator(stack_.data() + size_); 322 | } else { 323 | return iterator(heap_.data() + size_); 324 | } 325 | } 326 | 327 | const_iterator begin() const { 328 | if (size_ < N) { 329 | return const_iterator(stack_.data()); 330 | } else { 331 | return const_iterator(heap_.data()); 332 | } 333 | } 334 | 335 | const_iterator end() const { 336 | if (size_ < N) { 337 | return const_iterator(stack_.data() + size_); 338 | } else { 339 | return const_iterator(heap_.data() + size_); 340 | } 341 | } 342 | }; 343 | 344 | } // namespace sv -------------------------------------------------------------------------------- /test/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | build_linux/ -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.6) 2 | project(SMALL_VECTOR_TESTS) 3 | 4 | if(MSVC) 5 | # Force to always compile with W4 6 | if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]") 7 | string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") 8 | else() 9 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4") 10 | endif() 11 | elseif(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) 12 | # Update if necessary 13 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-long-long -pedantic") 14 | endif() 15 | 16 | if(NOT CMAKE_BUILD_TYPE) 17 | set(CMAKE_BUILD_TYPE Release) 18 | endif() 19 | 20 | # Disable deprecation for windows 21 | if (WIN32) 22 | add_compile_definitions(_CRT_SECURE_NO_WARNINGS) 23 | endif() 24 | 25 | # SMALL_VECTOR executable 26 | file(GLOB SMALL_VECTOR_TEST_SOURCES 27 | main.cpp 28 | test_constructors.cpp 29 | test_iterators.cpp 30 | test_access.cpp 31 | test_sizing.cpp 32 | test_swap.cpp 33 | test_fill.cpp 34 | test_push_pop.cpp 35 | ) 36 | set_source_files_properties(main.cpp 37 | PROPERTIES 38 | COMPILE_DEFINITIONS DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN) 39 | ADD_EXECUTABLE(SMALL_VECTOR ${SMALL_VECTOR_TEST_SOURCES}) 40 | INCLUDE_DIRECTORIES("../include" ".") 41 | set_target_properties(SMALL_VECTOR PROPERTIES OUTPUT_NAME tests) 42 | set_property(TARGET SMALL_VECTOR PROPERTY CXX_STANDARD 17) 43 | 44 | # Set ${PROJECT_NAME} as the startup project 45 | set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT SMALL_VECTOR) 46 | -------------------------------------------------------------------------------- /test/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | -------------------------------------------------------------------------------- /test/test_access.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using doctest::test_suite; 4 | using namespace sv; 5 | #include 6 | 7 | TEST_CASE("Access elements in a small vector of ints" * test_suite("access")) { 8 | small_vector a{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 9 | 10 | // .operator[] access 11 | REQUIRE(a[0] == 1); 12 | REQUIRE(a[4] == 5); 13 | 14 | const auto a_7 = a[7]; 15 | REQUIRE(a_7 == 8); 16 | 17 | auto& a_9 = a[9]; 18 | REQUIRE(a_9 == 10); 19 | 20 | // .at() access 21 | REQUIRE(a.at(0) == 1); 22 | REQUIRE(a.at(9) == 10); 23 | REQUIRE_THROWS(a.at(14)); 24 | 25 | // front() access 26 | REQUIRE(a.front() == 1); 27 | 28 | // back() access 29 | REQUIRE(a.back() == 10); 30 | } -------------------------------------------------------------------------------- /test/test_constructors.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using doctest::test_suite; 4 | using namespace sv; 5 | #include 6 | 7 | TEST_CASE("Default construct a small vector of ints" * test_suite("constructors")) { 8 | small_vector vec; 9 | REQUIRE(vec.size() == 0); 10 | } 11 | 12 | TEST_CASE("Fill construct a small vector of floats" * test_suite("constructors")) { 13 | small_vector vec(20, 1.0); 14 | REQUIRE(vec.size() == 20); 15 | std::size_t count = 0; 16 | for (size_t i = 0; i < vec.size(); i++, count++) { 17 | REQUIRE(vec[i] == 1.0); 18 | } 19 | REQUIRE(count == vec.size()); 20 | } 21 | 22 | TEST_CASE("Copy construct a small vector of strings" * test_suite("constructors")) { 23 | small_vector foo {"a", "b", "c", "d", "e", "f"}; 24 | small_vector bar(foo); 25 | REQUIRE(bar.size() == 6); 26 | REQUIRE(bar[0] == "a"); 27 | REQUIRE(bar[1] == "b"); 28 | REQUIRE(bar[2] == "c"); 29 | REQUIRE(bar[3] == "d"); 30 | REQUIRE(bar[4] == "e"); 31 | REQUIRE(bar[5] == "f"); 32 | } 33 | 34 | TEST_CASE("Move construct a small vector of chars" * test_suite("constructors")) { 35 | small_vector bar(std::move(small_vector{'a', 'b', 'c'})); 36 | REQUIRE(bar.size() == 3); 37 | REQUIRE(bar[0] == 'a'); 38 | REQUIRE(bar[1] == 'b'); 39 | REQUIRE(bar[2] == 'c'); 40 | } 41 | 42 | TEST_CASE("Construct a small vector of bools from initializer list" * test_suite("constructors")) { 43 | small_vector foo{1, 0, 1, 0, 1, 1, 1, 1}; 44 | REQUIRE(foo.size() == 8); 45 | REQUIRE(foo[0] == true); 46 | REQUIRE(foo[1] == false); 47 | REQUIRE(foo[2] == true); 48 | REQUIRE(foo[3] == false); 49 | REQUIRE(foo[4] == true); 50 | REQUIRE(foo[5] == true); 51 | REQUIRE(foo[6] == true); 52 | REQUIRE(foo[7] == true); 53 | } -------------------------------------------------------------------------------- /test/test_fill.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using doctest::test_suite; 4 | using namespace sv; 5 | #include 6 | 7 | TEST_CASE("Fill a small vector of ints with 0s" * test_suite("swap")) { 8 | small_vector a{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 9 | a.fill(0); 10 | REQUIRE(a.size() == 10); 11 | std::size_t j = 0; 12 | for (int i = 0; i < 10; i++, j++) { 13 | REQUIRE(a[i] == 0); 14 | } 15 | REQUIRE(j == 10); 16 | } -------------------------------------------------------------------------------- /test/test_iterators.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using doctest::test_suite; 4 | using namespace sv; 5 | #include 6 | 7 | TEST_CASE("Forward iterate small vector of ints" * test_suite("iterators")) { 8 | std::vector expected; 9 | small_vector foo{1, 2, 3}; 10 | for (auto& v : foo) { 11 | expected.push_back(v); 12 | } 13 | 14 | REQUIRE(expected[0] == 1); 15 | REQUIRE(expected[1] == 2); 16 | REQUIRE(expected[2] == 3); 17 | 18 | expected.clear(); 19 | for (const auto& v : foo) { 20 | expected.push_back(v); 21 | } 22 | 23 | REQUIRE(expected[0] == 1); 24 | REQUIRE(expected[1] == 2); 25 | REQUIRE(expected[2] == 3); 26 | } -------------------------------------------------------------------------------- /test/test_push_pop.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using doctest::test_suite; 4 | using namespace sv; 5 | #include 6 | 7 | TEST_CASE("Pushing into a small vector" * test_suite("push_back")) { 8 | small_vector a; 9 | for (auto v: {1, 2, 3, 4, 5, 6, 7, 8, 9}) { 10 | a.push_back(v); 11 | } 12 | REQUIRE(a.size() == 9); 13 | std::size_t j = 0; 14 | for (std::size_t i = 0; i < a.size(); i++, j++) { 15 | REQUIRE(a[i] == i + 1); 16 | } 17 | REQUIRE(j == 9); 18 | } 19 | 20 | TEST_CASE("Popping out of a small vector" * test_suite("pop_back")) { 21 | small_vector a; 22 | for (auto v: {1, 2, 3, 4, 5, 6, 7, 8, 9}) { 23 | a.push_back(v); 24 | } 25 | REQUIRE(a.size() == 9); 26 | 27 | // Remove 5 elements 28 | std::size_t i = 0; 29 | while(i < 5) { 30 | a.pop_back(); 31 | i += 1; 32 | } 33 | 34 | // Now all data should be on the stack 35 | REQUIRE(a.size() == 4); 36 | REQUIRE(a[0] == 1); 37 | REQUIRE(a[1] == 2); 38 | REQUIRE(a[2] == 3); 39 | REQUIRE(a[3] == 4); 40 | 41 | // pop back more 42 | while(i < 10) { 43 | a.pop_back(); 44 | i += 1; 45 | } 46 | 47 | REQUIRE(a.size() == 0); 48 | REQUIRE(a.empty()); 49 | } -------------------------------------------------------------------------------- /test/test_sizing.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using doctest::test_suite; 4 | using namespace sv; 5 | #include 6 | 7 | TEST_CASE("Sizing/Resizing in small vector of ints" * test_suite("sizing")) { 8 | small_vector a{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 9 | REQUIRE_FALSE(a.empty()); 10 | REQUIRE(a.size() == 10); 11 | 12 | a.resize(6); 13 | REQUIRE(a.size() == 6); 14 | 15 | a.resize(20); 16 | REQUIRE(a.size() == 20); 17 | std::size_t i = 0; 18 | for (std::size_t j = 6; j < a.size(); j++) { 19 | REQUIRE(a[j] == 0); 20 | i += 1; 21 | } 22 | REQUIRE(i == 14); 23 | } -------------------------------------------------------------------------------- /test/test_swap.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | using doctest::test_suite; 4 | using namespace sv; 5 | #include 6 | 7 | TEST_CASE("Swap a pair of small vectors" * test_suite("swap")) { 8 | small_vector a{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 9 | small_vector b; 10 | a.swap(b); 11 | REQUIRE(a.size() == 0); 12 | REQUIRE(b.size() == 10); 13 | std::size_t j = 0; 14 | for (int i = 0; i < 10; i++, j++) { 15 | REQUIRE(b[i] == i + 1); 16 | } 17 | REQUIRE(j == 10); 18 | } --------------------------------------------------------------------------------