Skip to content

Commit

Permalink
added structs.mixins
Browse files Browse the repository at this point in the history
also:
- added copy-based fallbacks for `unordered_erase()`, `insert()`, `emplace()` and `swap_columns()` (previously they required movability)
  • Loading branch information
marzer committed Aug 24, 2023
1 parent 8ed5476 commit 7730f33
Show file tree
Hide file tree
Showing 18 changed files with 1,519 additions and 684 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

- Fixed rvalue row corruption bug on MSVC ([info](https://developercommunity.visualstudio.com/t/C:-Corrupt-references-when-creating-a/10446877))
- Added `Cols...` selector template parameter to `at()`, `front()` and `back()`
- Added `structs.mixins`
- Added copy-based fallbacks for `unordered_erase()`, `insert()`, `emplace()` and `swap_columns()` (previously they required movability)

## v0.6.0

Expand Down
40 changes: 37 additions & 3 deletions docs/pages/schema.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
@page schema Config File Schema
@page schema Config Schema

@tableofcontents

This is the config file schema for the `*.toml` configuration files that are used with `soagen`, a command-line tool
This is the schema for the `*.toml` configuration files that are used with `soagen`, a command-line tool
for generating SoA container classes for use in C++.

@see <ul>
Expand Down Expand Up @@ -404,7 +404,7 @@ This code will be at class scope.

@note You _could_ use this to inject functions, but writing C++ code in a toml config file isn't exactly the best
user-experience. If you must do this, best practice would be to only use it for very simple functions like one-liner
conversion operators, et cetera.
conversion operators, et cetera. A better strategy is to use CRTP (see @ref schema_structs_mixins).

<!-- --------------------------------------------------------------------------------------------------------------- -->

Expand All @@ -421,6 +421,40 @@ This code will be at class scope.

<!-- --------------------------------------------------------------------------------------------------------------- -->

@subsection schema_structs_mixins mixins

List of type names to use as CRTP 'mixin' base classes. Each named type must be a template class that takes one type argument
(additional template arguments are allowed but must be defaulted).

**Type:** string, or array of strings

**Required:** No

**Default:** None

**Example:**

```toml
[structs.particles]
mixins = 'foo::add_some_functionality'
```

Where `foo::add_some_functionality` is something like this:

```cpp
namespace foo
{
template <typename Derived>
struct add_some_functionality
{
// ...
};
}

```

<!-- --------------------------------------------------------------------------------------------------------------- -->

@subsection schema_structs_movable movable

Used to disable the move constructor + assignment operator if necessary.
Expand Down
2 changes: 1 addition & 1 deletion docs/poxy.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ extra_files = [
'images/author.jpg',
]
stylesheets = ['pages.css']
navbar = ['pages', 'namespaces', 'classes']
navbar = ['schema', 'pages', 'namespaces', 'classes']

[warnings]
enabled = true
Expand Down
185 changes: 100 additions & 85 deletions examples/entities.hpp

Large diffs are not rendered by default.

435 changes: 230 additions & 205 deletions examples/shapes.hpp

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
project(
'soagen',
'cpp',
version : '0.1.1',
version : '0.7.0',
meson_version : '>=0.60.0',
license : 'MIT',
default_options : [ 'cpp_std=c++17', 'b_ndebug=if-release', 'buildtype=release' ]
Expand Down
5 changes: 5 additions & 0 deletions src/soagen/header_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,11 @@ def write(self, o: Writer):
#endif
'''
)
o()
for struct in self.structs:
struct.write_impl_namespace(o)
o()
with Namespace(o, 'soagen::detail'):
for struct in self.structs:
struct.write_soagen_detail_specializations(o)

Expand Down
137 changes: 127 additions & 10 deletions src/soagen/hpp/column_traits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,52 @@ namespace soagen::detail
source_buffer + source_index * sizeof(storage_type));
}

//--- move-or-copy-construction (whichever is possible) --------------------------------------------------------

static constexpr bool is_move_or_copy_constructible = is_move_constructible || is_copy_constructible;

static constexpr bool is_nothrow_move_or_copy_constructible =
is_move_constructible ? is_nothrow_move_constructible : is_nothrow_copy_constructible;

static constexpr bool is_trivially_move_or_copy_constructible =
is_move_constructible ? is_trivially_move_constructible : is_trivially_copy_constructible;

SOAGEN_HIDDEN_CONSTRAINT(sfinae, auto sfinae = is_move_or_copy_constructible)
SOAGEN_ATTR(nonnull)
SOAGEN_CPP20_CONSTEXPR
static storage_type& move_or_copy_construct(std::byte* destination, std::byte* source) //
noexcept(is_nothrow_move_or_copy_constructible)
{
SOAGEN_ASSUME(destination != nullptr);
SOAGEN_ASSUME(source != nullptr);
SOAGEN_ASSUME(destination != source);

if constexpr (is_move_constructible)
{
return move_construct(destination, source);
}
else
{
return copy_construct(destination, source);
}
}

SOAGEN_HIDDEN_CONSTRAINT(sfinae, auto sfinae = is_move_or_copy_constructible)
SOAGEN_ATTR(nonnull)
SOAGEN_CPP20_CONSTEXPR
static storage_type& move_or_copy_construct(std::byte* dest_buffer,
size_t dest_index,
std::byte* source_buffer,
size_t source_index) //
noexcept(is_nothrow_move_or_copy_constructible)
{
SOAGEN_ASSUME(dest_buffer != nullptr);
SOAGEN_ASSUME(source_buffer != nullptr);

return move_or_copy_construct(dest_buffer + dest_index * sizeof(storage_type),
source_buffer + source_index * sizeof(storage_type));
}

//--- destruction ----------------------------------------------------------------------------------------------

SOAGEN_ATTR(nonnull)
Expand Down Expand Up @@ -686,6 +732,52 @@ namespace soagen::detail
source_buffer + source_index * sizeof(storage_type));
}

//--- move-or-copy-assignment (whichever is possible) --------------------------------------------------------

static constexpr bool is_move_or_copy_assignable = is_move_assignable || is_copy_assignable;

static constexpr bool is_nothrow_move_or_copy_assignable =
is_move_assignable ? is_nothrow_move_assignable : is_nothrow_copy_assignable;

static constexpr bool is_trivially_move_or_copy_assignable =
is_move_assignable ? is_trivially_move_assignable : is_trivially_copy_assignable;

SOAGEN_HIDDEN_CONSTRAINT(sfinae, auto sfinae = is_move_or_copy_assignable)
SOAGEN_ATTR(nonnull)
SOAGEN_CPP20_CONSTEXPR
static storage_type& move_or_copy_assign(std::byte* destination, std::byte* source) //
noexcept(is_nothrow_move_or_copy_assignable)
{
SOAGEN_ASSUME(destination != nullptr);
SOAGEN_ASSUME(source != nullptr);
SOAGEN_ASSUME(destination != source);

if constexpr (is_move_assignable)
{
return move_assign(destination, source);
}
else
{
return copy_assign(destination, source);
}
}

SOAGEN_HIDDEN_CONSTRAINT(sfinae, auto sfinae = is_move_or_copy_assignable)
SOAGEN_ATTR(nonnull)
SOAGEN_CPP20_CONSTEXPR
static storage_type& move_or_copy_assign(std::byte* dest_buffer,
size_t dest_index,
std::byte* source_buffer,
size_t source_index) //
noexcept(is_nothrow_move_or_copy_assignable)
{
SOAGEN_ASSUME(dest_buffer != nullptr);
SOAGEN_ASSUME(source_buffer != nullptr);

return move_or_copy_assign(dest_buffer + dest_index * sizeof(storage_type),
source_buffer + source_index * sizeof(storage_type));
}

//--- swap -----------------------------------------------------------------------------------------------------

static constexpr bool is_trivially_swappable =
Expand All @@ -696,12 +788,13 @@ namespace soagen::detail

static constexpr bool is_swappable = is_trivially_swappable //
|| std::is_swappable_v<storage_type>
|| (is_move_constructible && is_move_assignable);
|| (is_move_or_copy_constructible && is_move_or_copy_assignable);

static constexpr bool is_nothrow_swappable =
is_trivially_swappable
|| (std::is_swappable_v<storage_type> ? std::is_nothrow_swappable_v<storage_type>
: (is_nothrow_move_constructible && is_nothrow_move_assignable));
|| (std::is_swappable_v<storage_type>
? std::is_nothrow_swappable_v<storage_type>
: (is_nothrow_move_or_copy_constructible && is_nothrow_move_or_copy_assignable));

SOAGEN_HIDDEN_CONSTRAINT(sfinae, auto sfinae = is_swappable)
SOAGEN_ATTR(nonnull)
Expand All @@ -715,10 +808,10 @@ namespace soagen::detail

if constexpr (is_trivially_swappable)
{
alignas(storage_type) std::byte buf[sizeof(storage_type)];
std::memcpy(soagen_aligned_storage(buf), soagen_aligned_storage(lhs), sizeof(storage_type));
alignas(storage_type) std::byte temp[sizeof(storage_type)];
std::memcpy(soagen_aligned_storage(temp), soagen_aligned_storage(lhs), sizeof(storage_type));
std::memcpy(soagen_aligned_storage(lhs), soagen_aligned_storage(rhs), sizeof(storage_type));
std::memcpy(soagen_aligned_storage(rhs), soagen_aligned_storage(buf), sizeof(storage_type));
std::memcpy(soagen_aligned_storage(rhs), soagen_aligned_storage(temp), sizeof(storage_type));
}
else if constexpr (std::is_swappable_v<storage_type>)
{
Expand All @@ -727,11 +820,35 @@ namespace soagen::detail
}
else
{
static_assert(is_move_constructible && is_move_assignable);
static_assert(is_move_or_copy_constructible && is_move_or_copy_assignable);

storage_type temp(static_cast<storage_type&&>(get(lhs)));
move_assign(lhs, rhs);
move_assign(rhs, &temp);
alignas(storage_type) std::byte temp[sizeof(storage_type)];

if constexpr ((is_nothrow_move_or_copy_constructible && is_nothrow_move_or_copy_assignable)
|| std::is_trivially_destructible_v<storage_type>)
{
move_or_copy_construct(soagen_aligned_storage(temp), lhs);
move_or_copy_assign(lhs, rhs);
move_or_copy_assign(rhs, soagen_aligned_storage(temp));
destruct(temp);
}
else
{
bool needs_destruct = false;
try
{
move_or_copy_construct(soagen_aligned_storage(temp), lhs);
needs_destruct = true;
move_or_copy_assign(lhs, rhs);
move_or_copy_assign(rhs, soagen_aligned_storage(temp));
}
catch (...)
{
if (needs_destruct)
destruct(temp);
throw;
}
}
}
}

Expand Down
67 changes: 58 additions & 9 deletions src/soagen/hpp/core.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,17 @@ namespace soagen
template <typename T>
using has_data_member_ = is_detected_<has_data_member_impl_, T>;

template <typename T, typename Pos, typename... Args>
using has_emplace_member_impl_ =
decltype(std::declval<T&>().emplace(std::declval<Pos>(), std::declval<Args>()...));
template <typename T, typename Pos, typename... Args>
using has_emplace_member_ = is_detected_<has_emplace_member_impl_, Pos, Args...>;

template <typename T, typename... Args>
using has_emplace_back_member_impl_ = decltype(std::declval<T&>().emplace_back(std::declval<Args>()...));
template <typename T, typename... Args>
using has_emplace_back_member_ = is_detected_<has_emplace_back_member_impl_, Args...>;

template <typename T, typename U>
using is_equality_comparable_impl_ = decltype(std::declval<const std::remove_reference_t<T>&>()
== std::declval<const std::remove_reference_t<U>&>());
Expand Down Expand Up @@ -324,6 +335,16 @@ namespace soagen
inline constexpr bool has_data_member =
POXY_IMPLEMENTATION_DETAIL(std::conjunction<detail::has_data_member_<T>...>::value);

/// @brief True if `T` has an `emplace(Pos, Args...)` member.
template <typename T, typename Pos, typename... Args>
inline constexpr bool has_emplace_member =
POXY_IMPLEMENTATION_DETAIL(detail::has_emplace_member_<T, Pos, Args...>::value);

/// @brief True if `T` has an `emplace_back(Args...)` member.
template <typename T, typename... Args>
inline constexpr bool has_emplace_back_member =
POXY_IMPLEMENTATION_DETAIL(detail::has_emplace_back_member_<T, Args...>::value);

/// @brief True if `T` and `U` meet the `EqualityComparable` named requirement.
template <typename T, typename U = T>
inline constexpr bool is_equality_comparable =
Expand Down Expand Up @@ -375,6 +396,22 @@ namespace soagen
struct has_nothrow_data_member_<T, false> : std::false_type
{};

template <bool /* has_emplace_member<T, Pos, Args> */, typename T, typename Pos, typename... Args>
struct has_nothrow_emplace_member_
: std::bool_constant<noexcept(std::declval<T&>().emplace(std::declval<Pos>(), std::declval<Args>()...))>
{};
template <typename T, typename Pos, typename... Args>
struct has_nothrow_emplace_member_<false, T, Pos, Args...> : std::false_type
{};

template <bool /* has_emplace_back_member<T, Args> */, typename T, typename... Args>
struct has_nothrow_emplace_back_member_
: std::bool_constant<noexcept(std::declval<T&>().emplace_back(std::declval<Args>()...))>
{};
template <typename T, typename... Args>
struct has_nothrow_emplace_back_member_<false, T, Args...> : std::false_type
{};

template <typename T, typename U = T, bool = is_equality_comparable<T, U>>
struct is_nothrow_equality_comparable_
: std::bool_constant<noexcept(std::declval<const std::remove_reference_t<T>&>()
Expand Down Expand Up @@ -420,6 +457,16 @@ namespace soagen
inline constexpr bool has_nothrow_data_member =
POXY_IMPLEMENTATION_DETAIL(std::conjunction<detail::has_nothrow_data_member_<T>...>::value);

/// @brief True if `T` has a non-throwing `emplace(Pos, Args...)` member.
template <typename T, typename Pos, typename... Args>
inline constexpr bool has_nothrow_emplace_member = POXY_IMPLEMENTATION_DETAIL(
detail::has_nothrow_emplace_member_<has_emplace_member<T, Pos, Args...>, T, Pos, Args...>::value);

/// @brief True if `T` has a non-throwing `emplace_back(Args...)` member.
template <typename T, typename... Args>
inline constexpr bool has_nothrow_emplace_back_member = POXY_IMPLEMENTATION_DETAIL(
detail::has_nothrow_emplace_back_member_<has_emplace_back_member<T, Args...>, T, Args...>::value);

/// @brief True if `T` and `U` meet the `EqualityComparable` named requirement without throwing.
template <typename T, typename U = T>
inline constexpr bool is_nothrow_equality_comparable =
Expand All @@ -432,31 +479,33 @@ namespace soagen

#if !SOAGEN_DOXYGEN && SOAGEN_HAS_BUILTIN(__type_pack_element)

template <size_t I, typename... T>
using type_at_index = __type_pack_element<I, T...>;
template <auto I, typename... T>
using type_at_index = __type_pack_element<static_cast<size_t>(I), T...>;

#else

/// @cond
namespace detail
{
template <size_t, typename...>
struct type_at_index_;

template <size_t I, typename T, typename... U>
struct type_at_index_impl
{
using type = typename type_at_index_impl<I - 1, U...>::type;
};
struct type_at_index_<I, T, U...> : type_at_index_<I - 1, U...>
{};

template <typename T, typename... U>
struct type_at_index_impl<0, T, U...>
struct type_at_index_<0, T, U...>
{
using type = T;
};
}
/// @endcond

/// @brief Gets the type `T` at index `I` in the parameter pack.
template <size_t I, typename... T>
using type_at_index = POXY_IMPLEMENTATION_DETAIL(typename detail::type_at_index_impl<I, T...>::type);
template <auto I, typename... T>
using type_at_index =
POXY_IMPLEMENTATION_DETAIL(typename detail::type_at_index_<static_cast<size_t>(I), T...>::type);

#endif

Expand Down
Loading

0 comments on commit 7730f33

Please sign in to comment.