Skip to content

Commit 6d380a1

Browse files
authored
merge develop
2 parents b398f28 + d7383ef commit 6d380a1

File tree

8 files changed

+477
-2
lines changed

8 files changed

+477
-2
lines changed

.github/workflows/code_testing.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ jobs:
8282
- name: Run examples
8383
working-directory: build
8484
run: |
85-
for example in examples/examples_*; do
85+
for example in examples/example*; do
8686
echo "executing ${example}"
8787
./${example}
8888
done

CMakeLists.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.24)
22

33
project(
44
dice-template-library
5-
VERSION 1.11.0
5+
VERSION 1.12.0
66
DESCRIPTION
77
"This template library is a collection of template-oriented code that we, the Data Science Group at UPB, found pretty handy. It contains: `switch_cases` (Use runtime values in compile-time context), `integral_template_tuple` (Create a tuple-like structure that instantiates a template for a range of values), `integral_template_variant` (A wrapper type for `std::variant` guarantees to only contain variants of the form `T<IX>` and `for_{types,values,range}` (Compile time for loops for types, values or ranges))."
88
HOMEPAGE_URL "https://dice-research.org/")

README.md

+6
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ It contains:
1919
- `channel`: A single producer, single consumer queue
2020
- `variant2`: Like `std::variant` but optimized for exactly two types
2121
- `mutex`/`shared_mutex`: Rust inspired mutex interfaces that hold their data instead of living next to it
22+
- `static_string`: A string type that is smaller than `std::string` for use cases where you do not need to resize the string
2223

2324
## Usage
2425

@@ -113,6 +114,11 @@ Rust inspired mutex interfaces that hold their data instead of living next to it
113114
The benefit of this approach is that it makes it harder (impossible in rust) to access the
114115
data without holding the mutex.
115116

117+
### `static_string`
118+
A string type that is smaller than `std::string` but does not have the ability to grow or shrink.
119+
This is useful if you never need to resize the string and want to keep the memory footprint low.
120+
It also supports allocators with "fancy" pointers.
121+
116122
### Further Examples
117123

118124
Compilable code examples can be found in [examples](./examples). The example build requires the cmake

examples/CMakeLists.txt

+7
Original file line numberDiff line numberDiff line change
@@ -109,3 +109,10 @@ target_link_libraries(example_shared_mutex
109109
dice-template-library::dice-template-library
110110
)
111111

112+
add_executable(example_static_string
113+
example_static_string.cpp)
114+
target_link_libraries(example_static_string
115+
PRIVATE
116+
dice-template-library::dice-template-library
117+
)
118+

examples/example_static_string.cpp

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#include <dice/template-library/static_string.hpp>
2+
3+
#include <cassert>
4+
#include <iostream>
5+
#include <string>
6+
7+
int main() {
8+
assert(sizeof(dice::template_library::static_string) < sizeof(std::string));
9+
10+
dice::template_library::static_string str{"Hello World"};
11+
std::cout << str << std::endl;
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
#ifndef DICE_TEMPLATELIBRARY_CONSTSTRING_HPP
2+
#define DICE_TEMPLATELIBRARY_CONSTSTRING_HPP
3+
4+
#include <cassert>
5+
#include <cstring>
6+
#include <memory>
7+
#include <ostream>
8+
#include <string_view>
9+
#include <utility>
10+
11+
namespace dice::template_library {
12+
13+
/**
14+
* A constant-size (i.e. non-growing), heap-allocated string type
15+
* Behaves like std::string except that it cannot grow and therefore occupies 1 less word in memory (it has no capacity field).
16+
*/
17+
template<typename Char, typename Traits = std::char_traits<Char>, typename Allocator = std::allocator<Char>>
18+
struct basic_static_string {
19+
using value_type = Char;
20+
using traits_type = Traits;
21+
using allocator_type = Allocator;
22+
using size_type = size_t;
23+
using different_type = std::ptrdiff_t;
24+
using pointer = typename std::allocator_traits<allocator_type>::pointer;
25+
using const_pointer = typename std::allocator_traits<allocator_type>::const_pointer;
26+
using view_type = std::basic_string_view<Char, Traits>;
27+
using iterator = value_type *;
28+
using const_iterator = value_type const *;
29+
using reverse_iterator = std::reverse_iterator<iterator>;
30+
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
31+
32+
private:
33+
pointer data_;
34+
size_type size_;
35+
[[no_unique_address]] allocator_type alloc_;
36+
37+
static constexpr void swap_data(basic_static_string &a, basic_static_string &b) noexcept {
38+
using std::swap;
39+
swap(a.data_, b.data_);
40+
swap(a.size_, b.size_);
41+
}
42+
43+
void copy_assign_from_other(basic_static_string const &other) {
44+
if (size_ != 0) {
45+
std::allocator_traits<allocator_type>::deallocate(alloc_, data_, size_);
46+
}
47+
48+
size_ = other.size_;
49+
50+
if (size_ != 0) {
51+
data_ = std::allocator_traits<allocator_type>::allocate(alloc_, size_);
52+
std::memcpy(std::to_address(data_), std::to_address(other.data_), size_);
53+
} else {
54+
data_ = nullptr;
55+
}
56+
}
57+
58+
public:
59+
constexpr basic_static_string(allocator_type const &alloc = allocator_type{}) noexcept
60+
: data_{nullptr}, size_{0}, alloc_{alloc} {
61+
}
62+
63+
explicit basic_static_string(view_type const sv, allocator_type const &alloc = allocator_type{})
64+
: size_{sv.size()}, alloc_{alloc} {
65+
66+
if (sv.empty()) {
67+
data_ = nullptr;
68+
return;
69+
}
70+
71+
data_ = std::allocator_traits<allocator_type>::allocate(alloc_, size_);
72+
std::memcpy(std::to_address(data_), sv.data(), size_);
73+
}
74+
75+
basic_static_string(basic_static_string const &other) : basic_static_string{static_cast<view_type>(other), other.alloc_} {
76+
}
77+
78+
basic_static_string &operator=(basic_static_string const &other) {
79+
if (this == &other) {
80+
return *this;
81+
}
82+
83+
if constexpr (std::allocator_traits<allocator_type>::propagate_on_container_copy_assignment::value) {
84+
alloc_ = other.alloc_;
85+
}
86+
87+
copy_assign_from_other(other);
88+
return *this;
89+
}
90+
91+
constexpr basic_static_string(basic_static_string &&other) noexcept : data_{std::exchange(other.data_, nullptr)},
92+
size_{std::exchange(other.size_, 0)},
93+
alloc_{std::move(other.alloc_)} {
94+
}
95+
96+
basic_static_string &operator=(basic_static_string &&other) noexcept(std::allocator_traits<allocator_type>::propagate_on_container_move_assignment::value
97+
|| std::allocator_traits<allocator_type>::is_always_equal::value) {
98+
assert(this != &other);
99+
100+
if constexpr (std::allocator_traits<allocator_type>::propagate_on_container_move_assignment::value) {
101+
swap(*this, other);
102+
return *this;
103+
} else if constexpr (std::allocator_traits<allocator_type>::is_always_equal::value) {
104+
swap_data(*this, other);
105+
return *this;
106+
} else {
107+
if (alloc_ == other.alloc_) [[likely]] {
108+
swap_data(*this, other);
109+
return *this;
110+
}
111+
112+
// alloc_ != other.alloc_ and not allowed to propagate, need to copy
113+
copy_assign_from_other(other);
114+
return *this;
115+
}
116+
}
117+
118+
~basic_static_string() {
119+
if (data_ != nullptr) {
120+
std::allocator_traits<allocator_type>::deallocate(alloc_, data_, size_);
121+
}
122+
}
123+
124+
operator view_type() const noexcept {
125+
return {std::to_address(data_), size_};
126+
}
127+
128+
[[nodiscard]] const_pointer data() const noexcept {
129+
return data_;
130+
}
131+
[[nodiscard]] pointer data() noexcept {
132+
return data_;
133+
}
134+
135+
[[nodiscard]] bool empty() const noexcept {
136+
return size_ == 0;
137+
}
138+
139+
[[nodiscard]] size_type size() const noexcept {
140+
return size_;
141+
}
142+
143+
[[nodiscard]] value_type operator[](size_type const ix) const noexcept {
144+
assert(ix < size());
145+
return std::to_address(data_)[ix];
146+
}
147+
148+
[[nodiscard]] value_type &operator[](size_type const ix) noexcept {
149+
assert(ix < size());
150+
return std::to_address(data_)[ix];
151+
}
152+
153+
[[nodiscard]] value_type front() const noexcept {
154+
assert(size() > 0);
155+
return (*this)[0];
156+
}
157+
158+
[[nodiscard]] value_type &front() noexcept {
159+
assert(size() > 0);
160+
return (*this)[0];
161+
}
162+
163+
[[nodiscard]] value_type back() const noexcept {
164+
assert(size() > 0);
165+
return (*this)[size() - 1];
166+
}
167+
168+
[[nodiscard]] value_type &back() noexcept {
169+
assert(size() > 0);
170+
return (*this)[size() - 1];
171+
}
172+
173+
[[nodiscard]] const_iterator begin() const noexcept {
174+
return std::to_address(data_);
175+
}
176+
[[nodiscard]] const_iterator end() const noexcept {
177+
return std::to_address(data_) + size_;
178+
}
179+
180+
[[nodiscard]] const_iterator cbegin() const noexcept {
181+
return begin();
182+
}
183+
[[nodiscard]] const_iterator cend() const noexcept {
184+
return end();
185+
}
186+
187+
[[nodiscard]] iterator begin() noexcept {
188+
return std::to_address(data_);
189+
}
190+
[[nodiscard]] iterator end() noexcept {
191+
return std::to_address(data_) + size_;
192+
}
193+
194+
[[nodiscard]] const_reverse_iterator rbegin() const noexcept {
195+
return const_reverse_iterator{end()};
196+
}
197+
[[nodiscard]] const_reverse_iterator rend() const noexcept {
198+
return const_reverse_iterator{begin()};
199+
}
200+
201+
[[nodiscard]] const_reverse_iterator crbegin() const noexcept {
202+
return rbegin();
203+
}
204+
[[nodiscard]] const_reverse_iterator crend() const noexcept {
205+
return rend();
206+
}
207+
208+
[[nodiscard]] reverse_iterator rbegin() noexcept {
209+
return reverse_iterator{end()};
210+
}
211+
[[nodiscard]] reverse_iterator rend() noexcept {
212+
return reverse_iterator{begin()};
213+
}
214+
215+
friend void swap(basic_static_string &a, basic_static_string &b) noexcept {
216+
using std::swap;
217+
swap(a.data_, b.data_);
218+
swap(a.size_, b.size_);
219+
swap(a.alloc_, b.alloc_);
220+
}
221+
222+
bool operator==(basic_static_string const &other) const noexcept {
223+
return static_cast<view_type>(*this) == static_cast<view_type>(other);
224+
}
225+
226+
auto operator<=>(basic_static_string const &other) const noexcept {
227+
return static_cast<view_type>(*this) <=> static_cast<view_type>(other);
228+
}
229+
230+
friend bool operator==(basic_static_string const &self, view_type const other) noexcept {
231+
return static_cast<view_type>(self) == other;
232+
}
233+
234+
friend auto operator<=>(basic_static_string const &self, view_type const other) noexcept {
235+
return static_cast<view_type>(self) <=> other;
236+
}
237+
};
238+
239+
template<typename Char, typename CharTraits, typename Allocator>
240+
std::basic_ostream<Char, CharTraits> &operator<<(std::basic_ostream<Char, CharTraits> &os, basic_static_string<Char, CharTraits, Allocator> const &str) {
241+
os << static_cast<std::basic_string_view<Char, CharTraits>>(str);
242+
return os;
243+
}
244+
245+
using static_string = basic_static_string<char>;
246+
using static_wstring = basic_static_string<wchar_t>;
247+
using static_u8string = basic_static_string<char8_t>;
248+
using static_u16string = basic_static_string<char16_t>;
249+
using static_u32string = basic_static_string<char32_t>;
250+
251+
} // namespace dice::template_library
252+
253+
#endif // DICE_TEMPLATELIBRARY_CONSTSTRING_HPP

tests/CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,5 @@ custom_add_test(tests_mutex)
7676
add_executable(tests_shared_mutex tests_shared_mutex.cpp)
7777
custom_add_test(tests_shared_mutex)
7878

79+
add_executable(tests_static_string tests_static_string.cpp)
80+
custom_add_test(tests_static_string)

0 commit comments

Comments
 (0)