Skip to content

Quickstart

Integration:

1
git clone https://github.com/alandefreitas/small/
1
2
3
4
add_subdirectory(small)
# ...
add_executable(your_target main.cpp)
target_link_libraries(your_target PUBLIC small::small)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
include(FetchContent)

FetchContent_Declare(small
    GIT_REPOSITORY https://github.com/alandefreitas/small
    GIT_TAG origin/master # or whatever tag you want
)

FetchContent_GetProperties(small)
if(NOT small_POPULATED)
    FetchContent_Populate(small)
    add_subdirectory(${small_SOURCE_DIR} ${small_BINARY_DIR} EXCLUDE_FROM_ALL)
endif()

# ...
add_executable(your_target main.cpp)
target_link_libraries(your_target PUBLIC small::small)
1
2
3
4
5
6
7
8
9
# Follow installation instructions and then... 
find_package(small REQUIRED)
if(NOT small_FOUND)
    # Throw or put your FetchContent script here
endif()

# ...
add_executable(your_target main.cpp)
target_link_libraries(your_target PUBLIC small::small)

Note

Get the binary package from the release section.

These binaries refer to the latest release version of small.

Hint

If you need a more recent version of small, you can download the binary packages from the CI artifacts or build the library from the source files.

Note

Ensure your C++ compiler and CMake are up-to-date and then:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# Create a new directory
mkdir build
cd build
# Configure
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-O2"
# Build
sudo cmake --build . --parallel 2 --config Release
# Install 
sudo cmake --install .
# Create packages
sudo cpack .
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# Create a new directory
mkdir build
cd build
# Configure
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-O2"
# Build
cmake --build . --parallel 2 --config Release
# Install 
cmake --install .
# Create packages
cpack .
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# Create a new directory
mkdir build
cd build
# Configure
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="/O2"
# Build
cmake --build . --parallel 2 --config Release
# Install 
cmake --install .
# Create packages
cpack .

Parallel Build

Replace --parallel 2 with --parallel <number of cores in your machine>

Setting C++ Compiler

If your C++ compiler that supports C++17 is not your default compiler, make sure you provide CMake with the compiler location with the DCMAKE_C_COMPILER and DCMAKE_CXX_COMPILER options. For instance:

1
cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-O2" -DCMAKE_C_COMPILER=/usr/bin/gcc-8 -DCMAKE_CXX_COMPILER=/usr/bin/g++-8

Note

Because containers are header-only, you can directly copy the contents from the source directory into your project.

Hint

In that case, you are responsible for setting the appropriate target include directories and any compile definitions you might require.

Once the library is properly integrated, you can create containers from the namespace small like any other STL container:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
//
// Copyright (c) 2022 alandefreitas (alandefreitas@gmail.com)
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
//

#include <small/map.hpp>
#include <small/queue.hpp>
#include <small/set.hpp>
#include <small/stack.hpp>
#include <small/string.hpp>
#include <small/vector.hpp>
#include <iostream>

template <class R>
void
print(R&& v);

void
print_codepoints(const small::string& v);

int
main() {
    // Vector as usual
    small::vector<int> v1 = { 1, 2, 3, 4, 5 };
    print(v1); // 1 2 3 4 5

    // Vector with inline storage for at least 10 elements
    small::vector<int, 10> v2 = { 1, 2, 3, 4 };
    v2.push_back(5);
    print(v2); // 1 2 3 4 5

    // Vector with inline storage only
    small::max_size_vector<int, 5> v3 = { 1, 2, 3, 4 };
    v3.push_back(5);
    print(v3); // 1 2 3 4 5

    // String
    small::string s1 = "Hello world!";
    print(s1); // H e l l o   w o r l d !

    // String with custom inline storage
    small::basic_string<char, 40> s2 = "Hello world!";
    print(s2); // H e l l o   w o r l d !

    // UTF8 String from larger UTF-32 string
    small::string s3 = U"This works too! 😀  é!";
    std::cout << s3 << '\n'; // T h i s   w o r k s   t o o !   😀
    print_codepoints(s3);    // T|h|i|s| |w|o|r|k|s| |t|o|o|!| |😀| | |é|!|

    // Associative containers
    small::set<int> a1 = { 2, 1, 5, 4, 3 };
    print(a1); // 1 2 3 4 5

    small::map<int, int> a2 = {
        {1, 10},
        {2, 20},
        {3, 30},
        {4, 40},
        {5, 50}
    };
    print(a2); // <1,10> <2,20> <3,30> <4,40> <5,50>

    small::multimap<int, int> a3 = {
        {1, 10},
        {1, 20},
        {1, 30},
        {1, 40},
        {1, 50}
    };
    print(a3); // <1,10> <1,20> <1,30> <1,40> <1,50>

    small::unordered_set<int> a4 = { 2, 1, 5, 4, 3 };
    print(a4); // 2 1 5 4 3

    // Container adaptors
    small::stack<int> c1;
    c1.push(1);
    c1.push(2);
    c1.push(3);
    c1.push(4);
    c1.push(5);
    std::cout << c1.top() << '\n'; // 5

    small::queue<int> c2;
    c2.push(1);
    c2.push(2);
    c2.push(3);
    c2.push(4);
    c2.push(5);
    std::cout << c2.front() << '\n'; // 1
    std::cout << c2.back() << '\n';  // 5

    small::priority_queue<int> c3;
    c3.push(1);
    c3.push(2);
    c3.push(3);
    c3.push(4);
    c3.push(5);
    std::cout << c3.top() << '\n'; // 5

    return 0;
}

template <class R>
void
print(R&& v) {
    for (const auto& x: v) {
        constexpr bool x_is_pair = small::detail::is_pair_v<
            std::decay_t<decltype(x)>>;
        if constexpr (not x_is_pair) {
            std::cout << x << ' ';
        } else {
            std::cout << '<' << x.first << ',' << x.second << '>' << ' ';
        }
    }
    std::cout << "\n";
}

void
print_codepoints(const small::string& v) {
    for (auto it = v.begin_codepoint(); it != v.end_codepoint(); ++it) {
        std::cout << *it << '|';
    }
    std::cout << "\n";
}

All containers are optimized for the case when they're small but also efficient when they are large. The containers mix the common techniques found in other small container libraries:

  • Inline allocation for small containers
  • Custom expected sizes
  • Identification of relocatable types
  • Custom growth factors with better defaults
  • Communication with system memory allocators
  • Explicit consideration of CPU cache sizes and branch prediction

Most applications have many small lists and sets of elements. These containers avoid spending a lot of time with large containers that contain just a few elements. Small containers usually try to use the stack before dynamically allocating memory and try to represent associative containers with stack arrays, unless these sets are very large.

The following containers are available:

  • small::vector
  • small::max_size_vector
  • small::string
  • small::set
  • small::max_size_set
  • small::multiset
  • small::max_size_multiset
  • small::unordered_set
  • small::max_size_unordered_set
  • small::unordered_multiset
  • small::max_size_unordered_multiset
  • small::map
  • small::max_size_map
  • small::multimap
  • small::max_size_multimap
  • small::unordered_map
  • small::max_size_unordered_map
  • small::unordered_multimap
  • small::max_size_unordered_multimap
  • small::stack
  • small::queue
  • small::priority_queue

Although many compilers support small string optimization (SSO) already, this library will ensure all strings support SOO, custom inline sizes, relocation, and unicode.