Coverage Report

Created: 2026-01-22 06:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/immer/immer/box.hpp
Line
Count
Source
1
//
2
// immer: immutable data structures for C++
3
// Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente
4
//
5
// This software is distributed under the Boost Software License, Version 1.0.
6
// See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt
7
//
8
9
#pragma once
10
11
#include <immer/detail/util.hpp>
12
#include <immer/memory_policy.hpp>
13
14
#include <cstddef>
15
16
namespace immer {
17
18
namespace detail {
19
20
template <typename U, typename MP>
21
struct gc_atom_impl;
22
23
template <typename U, typename MP>
24
struct refcount_atom_impl;
25
26
} // namespace detail
27
28
/*!
29
 * Immutable box for a single value of type `T`.
30
 *
31
 * The box is always copiable and movable. The `T` copy or move
32
 * operations are never called.  Since a box is immutable, copying or
33
 * moving just copy the underlying pointers.
34
 */
35
template <typename T, typename MemoryPolicy = default_memory_policy>
36
class box
37
{
38
    friend struct detail::gc_atom_impl<T, MemoryPolicy>;
39
    friend struct detail::refcount_atom_impl<T, MemoryPolicy>;
40
41
    struct holder : MemoryPolicy::refcount
42
    {
43
        T value;
44
45
        template <typename... Args>
46
        holder(Args&&... args)
47
321k
            : value(std::forward<Args>(args)...)
48
321k
        {
49
321k
        }
immer::box<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, immer::memory_policy<immer::free_list_heap_policy<immer::cpp_heap, 1024ul>, immer::refcount_policy, immer::spinlock_policy, immer::no_transience_policy, false, true> >::holder::holder<char const (&) [4]>(char const (&) [4])
Line
Count
Source
47
39.3k
            : value(std::forward<Args>(args)...)
48
39.3k
        {
49
39.3k
        }
immer::box<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, immer::memory_policy<immer::free_list_heap_policy<immer::cpp_heap, 1024ul>, immer::refcount_policy, immer::spinlock_policy, immer::no_transience_policy, false, true> >::holder::holder<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&&)
Line
Count
Source
47
282k
            : value(std::forward<Args>(args)...)
48
282k
        {
49
282k
        }
immer::box<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, immer::memory_policy<immer::free_list_heap_policy<immer::cpp_heap, 1024ul>, immer::refcount_policy, immer::spinlock_policy, immer::no_transience_policy, false, true> >::holder::holder<>()
Line
Count
Source
47
1
            : value(std::forward<Args>(args)...)
48
1
        {
49
1
        }
50
    };
51
52
    using heap = typename MemoryPolicy::heap::type;
53
54
    holder* impl_ = nullptr;
55
56
    box(holder* impl)
57
        : impl_{impl}
58
    {
59
    }
60
61
public:
62
871k
    const holder* impl() const { return impl_; };
63
64
    using value_type    = T;
65
    using memory_policy = MemoryPolicy;
66
67
    /*!
68
     * Constructs a box holding `T{}`.
69
     */
70
    box()
71
1
        : impl_{detail::make<heap, holder>()}
72
1
    {
73
1
    }
74
75
    /*!
76
     * Constructs a box holding `T{arg}`
77
     */
78
    template <typename Arg,
79
              typename Enable = std::enable_if_t<
80
                  !std::is_same<box, std::decay_t<Arg>>::value>,
81
              // this is similar to std::is_constructible but works around the
82
              // fact that is_constructible is ill-formed for incomplete types
83
              typename = decltype(T(std::declval<Arg>()))>
84
    box(Arg&& arg)
85
321k
        : impl_{detail::make<heap, holder>(std::forward<Arg>(arg))}
86
321k
    {
87
321k
    }
immer::box<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, immer::memory_policy<immer::free_list_heap_policy<immer::cpp_heap, 1024ul>, immer::refcount_policy, immer::spinlock_policy, immer::no_transience_policy, false, true> >::box<char const (&) [4], void, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(char const (&) [4])
Line
Count
Source
85
39.3k
        : impl_{detail::make<heap, holder>(std::forward<Arg>(arg))}
86
39.3k
    {
87
39.3k
    }
immer::box<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, immer::memory_policy<immer::free_list_heap_policy<immer::cpp_heap, 1024ul>, immer::refcount_policy, immer::spinlock_policy, immer::no_transience_policy, false, true> >::box<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, void, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&&)
Line
Count
Source
85
282k
        : impl_{detail::make<heap, holder>(std::forward<Arg>(arg))}
86
282k
    {
87
282k
    }
88
89
    /*!
90
     * Constructs a box holding `T{arg1, arg2, args...}`
91
     */
92
    template <typename Arg1,
93
              typename Arg2,
94
              typename... Args,
95
              // this is similar to std::is_constructible but works around the
96
              // fact that is_constructible is ill-formed for incomplete types
97
              typename = decltype(T(std::declval<Arg1>(),
98
                                    std::declval<Arg2>(),
99
                                    std::declval<Args>()...))>
100
    box(Arg1&& arg1, Arg2&& arg2, Args&&... args)
101
        : impl_{detail::make<heap, holder>(std::forward<Arg1>(arg1),
102
                                           std::forward<Arg2>(arg2),
103
                                           std::forward<Args>(args)...)}
104
    {
105
    }
106
107
    friend void swap(box& a, box& b)
108
3.92M
    {
109
3.92M
        using std::swap;
110
3.92M
        swap(a.impl_, b.impl_);
111
3.92M
    }
112
113
3.55M
    box(box&& other) { swap(*this, other); }
114
    box(const box& other)
115
820k
        : impl_(other.impl_)
116
820k
    {
117
820k
        impl_->inc();
118
820k
    }
119
    box& operator=(box&& other)
120
371k
    {
121
371k
        swap(*this, other);
122
371k
        return *this;
123
371k
    }
124
    box& operator=(const box& other)
125
    {
126
        auto aux = other;
127
        swap(*this, aux);
128
        return *this;
129
    }
130
    ~box()
131
4.69M
    {
132
4.69M
        if (impl_ && impl_->dec()) {
133
321k
            impl_->~holder();
134
321k
            heap::deallocate(sizeof(holder), impl_);
135
321k
        }
136
4.69M
    }
137
138
    /*! Query the current value. */
139
505k
    IMMER_NODISCARD const T& get() const { return impl_->value; }
140
141
    /*! Conversion to the boxed type. */
142
285k
    operator const T&() const { return get(); }
143
144
    /*! Access via dereference */
145
    const T& operator*() const { return get(); }
146
147
    /*! Access via pointer member access */
148
    const T* operator->() const { return &get(); }
149
150
    /*!
151
     * Returns a new box built by applying the `fn` to the underlying
152
     * value.
153
     *
154
     * @rst
155
     *
156
     * **Example**
157
     *   .. literalinclude:: ../example/box/box.cpp
158
     *      :language: c++
159
     *      :dedent: 8
160
     *      :start-after: update/start
161
     *      :end-before:  update/end
162
     *
163
     * @endrst
164
     */
165
    template <typename Fn>
166
    IMMER_NODISCARD box update(Fn&& fn) const&
167
    {
168
        return std::forward<Fn>(fn)(get());
169
    }
170
    template <typename Fn>
171
    IMMER_NODISCARD box&& update(Fn&& fn) &&
172
    {
173
        if (impl_->unique())
174
            impl_->value = std::forward<Fn>(fn)(std::move(impl_->value));
175
        else
176
            *this = std::forward<Fn>(fn)(impl_->value);
177
        return std::move(*this);
178
    }
179
};
180
181
template <typename T, typename MP>
182
IMMER_NODISCARD bool operator==(const box<T, MP>& a, const box<T, MP>& b)
183
392k
{
184
392k
    return a.impl() == b.impl() || a.get() == b.get();
185
392k
}
186
template <typename T, typename MP>
187
IMMER_NODISCARD bool operator!=(const box<T, MP>& a, const box<T, MP>& b)
188
43.3k
{
189
43.3k
    return a.impl() != b.impl() && a.get() != b.get();
190
43.3k
}
191
template <typename T, typename MP>
192
IMMER_NODISCARD bool operator<(const box<T, MP>& a, const box<T, MP>& b)
193
{
194
    return a.impl() != b.impl() && a.get() < b.get();
195
}
196
197
template <typename T, typename MP, typename T2>
198
IMMER_NODISCARD auto operator==(const box<T, MP>& a, T2&& b)
199
    -> std::enable_if_t<!std::is_same<box<T, MP>, std::decay_t<T2>>::value,
200
                        decltype(a.get() == b)>
201
{
202
    return a.get() == b;
203
}
204
template <typename T, typename MP, typename T2>
205
IMMER_NODISCARD auto operator!=(const box<T, MP>& a, T2&& b)
206
    -> std::enable_if_t<!std::is_same<box<T, MP>, std::decay_t<T2>>::value,
207
                        decltype(a.get() != b)>
208
{
209
    return a.get() != b;
210
}
211
template <typename T, typename MP, typename T2>
212
IMMER_NODISCARD auto operator<(const box<T, MP>& a, T2&& b)
213
    -> std::enable_if_t<!std::is_same<box<T, MP>, std::decay_t<T2>>::value,
214
                        decltype(a.get() < b)>
215
{
216
    return a.get() < b;
217
}
218
219
template <typename T2, typename T, typename MP>
220
IMMER_NODISCARD auto operator==(T2&& b, const box<T, MP>& a)
221
    -> std::enable_if_t<!std::is_same<box<T, MP>, std::decay_t<T2>>::value,
222
                        decltype(a.get() == b)>
223
{
224
    return a.get() == b;
225
}
226
template <typename T2, typename T, typename MP>
227
IMMER_NODISCARD auto operator!=(T2&& b, const box<T, MP>& a)
228
    -> std::enable_if_t<!std::is_same<box<T, MP>, std::decay_t<T2>>::value,
229
                        decltype(a.get() != b)>
230
{
231
    return a.get() != b;
232
}
233
template <typename T2, typename T, typename MP>
234
IMMER_NODISCARD auto operator<(T2&& b, const box<T, MP>& a)
235
    -> std::enable_if_t<!std::is_same<box<T, MP>, std::decay_t<T2>>::value,
236
                        decltype(b < a.get())>
237
{
238
    return b < a.get();
239
}
240
241
} // namespace immer
242
243
namespace std {
244
245
template <typename T, typename MP>
246
struct hash<immer::box<T, MP>>
247
{
248
    std::size_t operator()(const immer::box<T, MP>& x) const
249
    {
250
        return std::hash<T>{}(*x);
251
    }
252
};
253
254
} // namespace std