Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide strong guarantee for rad::Vector #27

Merged
merged 6 commits into from
Jul 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion MODULE.bazel.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 31 additions & 0 deletions radiant/Algorithm.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright 2023 The Radiant Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#pragma once

#include "radiant/TypeTraits.h"
#include "radiant/Utility.h"
#include "radiant/TotallyRad.h"

namespace rad
{
template <typename T>
void Swap(T& a, T& b) noexcept
{
RAD_S_ASSERT_NOTHROW_MOVE_T(T);
T tmp = Move(a);
a = Move(b);
b = Move(tmp);
}
} // namespace rad
7 changes: 6 additions & 1 deletion radiant/TotallyRad.h
Original file line number Diff line number Diff line change
Expand Up @@ -282,8 +282,13 @@ inline bool DoAssert(const char* Assertion, const char* File, int Line)
#if RAD_ENABLE_NOTHROW_MOVE_ASSERTIONS
#define RAD_S_ASSERT_NOTHROW_MOVE(x) \
RAD_S_ASSERTMSG(x, "move operations should not throw")

#define RAD_S_ASSERT_NOTHROW_MOVE_T(x) \
RAD_S_ASSERTMSG(IsNoThrowMoveCtor<T>&& IsNoThrowMoveAssign<T>, \
"move operations should not throw")
#else
#define RAD_S_ASSERT_NOTHROW_MOVE(x) RAD_S_ASSERT(true)
#define RAD_S_ASSERT_NOTHROW_MOVE(x) RAD_S_ASSERT(true)
#define RAD_S_ASSERT_NOTHROW_MOVE_T(x) RAD_S_ASSERT(true)
#endif

#define RAD_NOT_COPYABLE(x) \
Expand Down
61 changes: 26 additions & 35 deletions radiant/Vector.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,29 +43,11 @@ class Vector final
using SizeType = uint32_t;
static constexpr uint16_t InlineCount = TInlineCount;
using AllocatorType = TAllocator;
template <typename OtherTAllocator, uint16_t OtherTInlineCount = 0>
using OtherType = Vector<T, OtherTAllocator, OtherTInlineCount>;

RAD_S_ASSERT_NOTHROW_MOVE((IsNoThrowMoveCtor<T> && IsNoThrowMoveAssign<T> &&
IsNoThrowMoveCtor<TAllocator> &&
IsNoThrowMoveAssign<TAllocator>));

//
// TODO - rad::Vector does not yet support types that might throw exceptions
// when manipulating it. It is the intention of the radiant authors for the
// library to be exception free. Radiant will never throw an exception
// itself, rather it will communicate errors by returning a Result. However,
// the radiant containers may be used with types that do not adhere to this
// philosophy. For rad::Vector to comply to this, it must cleanly handle
// cases where an exception interjects manipulation of it. If an exception
// happens rad::Vector should be in the state prior to the call to
// manipulate it. In other words, an exception occurring when manipulating
// rad::Vector should not leave it in a partially-manipulated state. This
// does not mean it should swallow the exception internally and return an
// error. The exception should propagate from rad::Vector but rad::Vector
// itself should be in a valid state when this happens.
//
RAD_S_ASSERTMSG((IsNoThrowDtor<T> && IsNoThrowDefaultCtor<T> &&
IsNoThrowCopyCtor<T> && IsNoThrowCopyAssign<T>),
"rad::Vector does not yet support types that might throw.");
RAD_S_ASSERT_NOTHROW_MOVE_T(T);
RAD_S_ASSERT_NOTHROW_MOVE_T(TAllocator);

~Vector()
{
Expand All @@ -81,14 +63,14 @@ class Vector final

/// @brief Constructs empty container with copy-constructed allocator.
/// @param alloc Allocator to copy.
Vector(const AllocatorType& alloc)
explicit Vector(const AllocatorType& alloc)
: m_storage(alloc)
{
}

/// @brief Constructs empty container with move-constructed allocator.
/// @param alloc Allocator to move.
Vector(AllocatorType&& alloc)
explicit Vector(AllocatorType&& alloc)
: m_storage(Forward<AllocatorType>(alloc))
{
}
Expand Down Expand Up @@ -129,15 +111,15 @@ class Vector final

/// @brief Retrieves a pointer to the first element in the contiguous set of
/// elements. Pointer value is undefined if the container is empty.
/// @return Pointer to the first element in the contiguious set of elements.
/// @return Pointer to the first element in the contiguous set of elements.
ValueType* Data() noexcept
{
return Storage().Data();
}

/// @brief Retrieves a pointer to the first element in the contiguous set of
/// elements. Pointer value is undefined if the container is empty.
/// @return Pointer to the first element in the contiguious set of elements.
/// @return Pointer to the first element in the contiguous set of elements.
const ValueType* Data() const noexcept
{
return Storage().Data();
Expand Down Expand Up @@ -258,7 +240,7 @@ class Vector final
/// less than count a number of default-constructed elements are appended.
/// @param count Requested size of the container.
/// @return Result reference to this container on success or an error.
Res<ThisType&> Resize(SizeType count) noexcept
Res<ThisType&> Resize(SizeType count) noexcept(IsNoThrowDefaultCtor<T>)
{
return Storage().Resize(Allocator(), count).OnOk(*this);
}
Expand All @@ -270,7 +252,8 @@ class Vector final
/// @param count Requested size of the container.
/// @param value Value to initialize new elements with.
/// @return Result reference to this container on success or an error.
Res<ThisType&> Resize(SizeType count, const ValueType& value) noexcept
Res<ThisType&> Resize(SizeType count,
const ValueType& value) noexcept(IsNoThrowCopyCtor<T>)
{
return Storage().Resize(Allocator(), count, value).OnOk(*this);
}
Expand All @@ -279,7 +262,8 @@ class Vector final
/// @param count Number of elements to assign to the container.
/// @param value Value to initialize elements with.
/// @return Result reference to this container on success or an error.
Res<ThisType&> Assign(SizeType count, const ValueType& value) noexcept
Res<ThisType&> Assign(SizeType count,
const ValueType& value) noexcept(IsNoThrowCopyCtor<T>)
{
return Storage().Assign(Allocator(), count, value).OnOk(*this);
}
Expand All @@ -288,7 +272,8 @@ class Vector final
/// @brief Replaces the contents of the container.
/// @param Init List of values to initialize elements with.
/// @return Result reference to this container on success or an error.
Res<ThisType&> Assign(std::initializer_list<ValueType> init) noexcept
Res<ThisType&> Assign(std::initializer_list<ValueType> init) noexcept(
IsNoThrowCopyCtor<T>)
{
return Storage()
.Assign(Allocator(),
Expand All @@ -300,7 +285,7 @@ class Vector final
/// @brief Replaces the contents of the container.
/// @param span Span of values to initialize elements with.
/// @return Result reference to this container on success or an error.
Res<ThisType&> Assign(Span<ValueType> span) noexcept
Res<ThisType&> Assign(Span<ValueType> span) noexcept(IsNoThrowCopyCtor<T>)
{
return Storage().Assign(Allocator(), span).OnOk(*this);
}
Expand All @@ -311,7 +296,8 @@ class Vector final
/// @param ...args Arguments to forward to the constructor of the element.
/// @return Result reference to this container on success or an error.
template <typename... TArgs>
Res<ThisType&> EmplaceBack(TArgs&&... args) noexcept
Res<ThisType&> EmplaceBack(TArgs&&... args) noexcept(
IsNoThrowCtor<T, TArgs...>)
{
return Storage()
.EmplaceBack(Allocator(), Forward<TArgs>(args)...)
Expand All @@ -321,7 +307,8 @@ class Vector final
/// @brief Appends a new element to the end of the container.
/// @param value Value to be appended.
/// @return Result reference to this container on success or an error.
Res<ThisType&> PushBack(const ValueType& value) noexcept
Res<ThisType&> PushBack(const ValueType& value) noexcept(
IsNoThrowCopyCtor<T>)
{
return EmplaceBack(value);
}
Expand Down Expand Up @@ -375,9 +362,13 @@ class Vector final
/// @brief Copies the elements in this container to another.
/// @param to Container to copy elements to.
/// @return Result reference to this container on success or an error.
Res<ThisType&> Copy(ThisType& to) noexcept
Res<ThisType&> Copy(ThisType& to) noexcept(IsNoThrowCopyCtor<T>)
{
return Storage().Copy(Allocator(), to.Storage()).OnOk(*this);
// Note: for now we can only copy types with the same allocator because
// the storage and allocator are private when the from and to vectors
// are not exactly the same type. This problem can be solved, but
// requires a bit of work.
return Storage().Copy(to.Allocator(), to.Storage()).OnOk(*this);
}

/// @brief Moves the elements in this container to another.
Expand Down
Loading
Loading