Skip to content

Commit

Permalink
inclass_store_t - for using as pimpl
Browse files Browse the repository at this point in the history
  • Loading branch information
Artur Bać (win10) committed Mar 31, 2024
1 parent a540fe2 commit fc5c6b5
Show file tree
Hide file tree
Showing 3 changed files with 280 additions and 0 deletions.
131 changes: 131 additions & 0 deletions include/small_vectors/inclass_buffer.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// SPDX-FileCopyrightText: 2024 Artur Bać
// SPDX-License-Identifier: MIT
// SPDX-PackageHomePage: https://github.com/arturbac/small_vectors
#pragma once

#include <cstddef>
#include <new>
#include <utility>
#include <concepts>
#include <memory>

namespace small_vectors::inline v3_0
{
namespace concepts
{
template<typename T>
concept complete_type = requires { sizeof(T); };
}

template<
typename ValueType,
std::size_t StorageSize = sizeof(ValueType),
std::size_t Alignment = std::alignment_of_v<ValueType>>
struct inclass_store_t
{
using value_type = ValueType;
static constexpr std::size_t storage_size{StorageSize};

struct store_t
{
[[alignas(Alignment)]] std::byte data[storage_size];
};

store_t store_;

private:
constexpr auto ptr() noexcept -> value_type *
{
return std::launder(reinterpret_cast<value_type *>(&store_.data[0]));
}

constexpr auto ptr() const noexcept -> value_type const *
{
return std::launder(reinterpret_cast<value_type const *>(&store_.data[0]));
}

public:
constexpr inclass_store_t() noexcept(std::is_nothrow_default_constructible_v<value_type>)
requires concepts::complete_type<value_type> && std::default_initializable<value_type>
{
if constexpr(std::is_trivially_default_constructible_v<value_type>)
store_ = {};
else
std::construct_at(ptr());
}

constexpr inclass_store_t(inclass_store_t const & other) noexcept(std::is_nothrow_copy_constructible_v<value_type>)
requires std::copy_constructible<value_type>
{
if constexpr(std::is_trivially_copy_constructible_v<value_type>)
store_ = other.store_;
else
std::construct_at(ptr(), *other.ptr());
}

constexpr inclass_store_t(inclass_store_t && other) noexcept(std::is_nothrow_move_constructible_v<value_type>)
requires concepts::complete_type<value_type> && std::move_constructible<value_type>
{
if constexpr(std::is_trivially_move_constructible_v<value_type>)
store_ = other.store_;
else
std::construct_at(ptr(), std::move(*other.ptr()));
}

constexpr auto operator=(inclass_store_t const & other) noexcept(std::is_nothrow_copy_assignable_v<value_type>)
-> inclass_store_t &
requires concepts::complete_type<value_type> && std::copyable<value_type> // Requires value_type to be copyable
{
if(this != &other)
{
if constexpr(std::is_trivially_copy_assignable_v<value_type>)
store_ = other.store_;
else
*ptr() = *other.ptr();
}
return *this;
}

constexpr auto operator=(inclass_store_t && other) noexcept(std::is_nothrow_move_assignable_v<value_type>)
-> inclass_store_t &
requires concepts::complete_type<value_type> && std::movable<value_type> // Requires value_type to be movable
{
if(this != &other)
{
if constexpr(std::is_trivially_destructible_v<value_type>)
std::destroy_at(ptr());
if constexpr(std::is_trivially_move_assignable_v<value_type>)
store_ = other.store_;
else
std::construct_at(ptr(), std::move(*other.ptr()));
}
return *this;
}

template<typename... Args>
constexpr inclass_store_t(Args &&... args)
requires concepts::complete_type<value_type> && std::constructible_from<value_type, Args &&...>
{
std::construct_at(ptr(), std::forward<Args>(args)...);
}

constexpr ~inclass_store_t() noexcept(std::is_nothrow_destructible_v<value_type>)
requires std::destructible<value_type> && (!std::is_trivially_constructible_v<value_type>)
{
std::destroy_at(ptr());
}

constexpr ~inclass_store_t() noexcept
requires std::is_trivially_constructible_v<value_type>
= default;

constexpr auto operator*() noexcept -> value_type & { return *ptr(); }

constexpr auto operator*() const noexcept -> value_type const & { return *ptr(); }

constexpr auto operator->() noexcept -> value_type * { return ptr(); }

constexpr auto operator->() const noexcept -> value_type const * { return ptr(); }
};

} // namespace small_vectors::inline v3_0
1 change: 1 addition & 0 deletions unit_tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ add_unittest(strong_type_ut)
add_unittest(string_ut)
add_unittest(ranges_ut)
add_unittest(expected_ut)
add_unittest(inclass_buffer_ut)

# github ubuntu latest is very old
find_package( Boost 1.74 COMPONENTS system )
Expand Down
148 changes: 148 additions & 0 deletions unit_tests/inclass_buffer_ut.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
#include <small_vectors/inclass_buffer.hpp>

#include <boost/ut.hpp>
#include <string>
#include <type_traits>

using namespace boost::ut;
using small_vectors::inclass_store_t;

struct test_trivial_struct
{
int content;
};

static_assert(std::alignment_of_v<test_trivial_struct> == alignof(test_trivial_struct), "Alignment mismatch.");
using namespace std::string_view_literals;

suite<"inclass_store_trivial_tests"> inclass_store_trivial_tests = []
{
using inclass_string_store
= inclass_store_t<test_trivial_struct, sizeof(test_trivial_struct), alignof(test_trivial_struct)>;
"default_constructor"_test = []
{
inclass_string_store store;
expect(eq(store->content, 0));
};
"parameterized_constructor"_test = []
{
inclass_string_store store{10};
expect(eq(10, store->content));
};
"copy_constructor"_test = []
{
inclass_string_store const original{-10};
inclass_string_store const copy{original};
expect(eq(-10, copy->content));
};
"move_constructor"_test = []
{
inclass_string_store original{0xfffffe};
inclass_string_store const moved{std::move(original)};
expect(eq(0xfffffe, moved->content));
};

"copy_assignment"_test = []
{
inclass_string_store original{0xfffffe};
inclass_string_store copy;
copy = original;
expect(eq(0xfffffe, (*copy).content));
};

"move_assignment"_test = []
{
inclass_string_store original{0xfffffe};
inclass_string_store moved;
moved = std::move(original);
expect(eq(0xfffffe, (*moved).content));
};
};
constexpr auto test_text
= "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis sit amet dictum neque."
" Aliquam erat volutpat. Vivamus bibendum pretium eros, eu porta libero dictum ut."
" Vivamus feugiat nisi elit, quis finibus risus pellentesque non. Cras accumsan felis quis dolor malesuada,"
" eu consequat velit malesuada."sv;

struct test_non_trivial_struct
{
std::string content;
};

suite<"inclass_store_non_trivial_tests"> inclass_store_non_trivial_tests = []
{
using inclass_string_store
= inclass_store_t<test_non_trivial_struct, sizeof(test_non_trivial_struct), alignof(test_non_trivial_struct)>;

"default_constructor"_test = []
{
inclass_string_store store;
expect(store->content.empty());
};

"parameterized_constructor"_test = []
{
inclass_string_store store{std::string{test_text}};
expect(eq(test_text, (*store).content));
};

"copy_constructor"_test = []
{
inclass_string_store const original{std::string{test_text}};
inclass_string_store copy{original};
expect(eq(test_text, (*copy).content));
};

"move_constructor"_test = []
{
inclass_string_store original{std::string{test_text}};
inclass_string_store const moved{std::move(original)};
expect(eq(test_text, (*moved).content));
};

"copy_assignment"_test = []
{
inclass_string_store original{std::string{test_text}};
inclass_string_store copy;
copy = original;
expect(eq(test_text, (*copy).content));
};

"move_assignment"_test = []
{
inclass_string_store original{std::string{test_text}};
inclass_string_store moved;
moved = std::move(original);
expect(eq(test_text, (*moved).content));
};
};

struct test_multi_arg_struct
{
std::string content;
int integral;
};

suite<"inclass_store_multi_arg_struct"> inclass_store_multi_arg_struct = []
{
using inclass_string_store = inclass_store_t<test_multi_arg_struct>;

"default_constructor"_test = []
{
inclass_string_store store;
expect(store->content.empty());
};

"parameterized_constructor"_test = []
{
inclass_string_store store{std::string{test_text}, 0x1f55aafe};
expect(eq(test_text, (*store).content));
expect(eq(0x1f55aafe, store->integral));
};
};

int main()
{
// Running the tests
return boost::ut::cfg<>.run();
}

0 comments on commit fc5c6b5

Please sign in to comment.