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

Implement #70 #71

Merged
merged 3 commits into from
Aug 20, 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
84 changes: 62 additions & 22 deletions include/fastgltf/types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1020,6 +1020,19 @@ namespace fastgltf {
static constexpr auto missing_value = static_cast<BufferTarget>(std::numeric_limits<std::underlying_type_t<BufferTarget>>::max());
};

FASTGLTF_EXPORT template<typename T>
class OptionalWithFlagValue;
n0F4x marked this conversation as resolved.
Show resolved Hide resolved

/**
* A type alias which checks if there is a specialization of OptionalFlagValue for T and "switches"
* between fastgltf::OptionalWithFlagValue and std::optional.
*/
FASTGLTF_EXPORT template <typename T>
using Optional = std::conditional_t<
!std::is_same_v<std::nullopt_t, std::remove_const_t<decltype(OptionalFlagValue<T>::missing_value)>>,
OptionalWithFlagValue<T>,
std::optional<T>>;

/**
* A custom optional class for fastgltf,
* which uses so-called "flag values" which are specific values of T that will never be present as an actual value.
Expand All @@ -1030,7 +1043,7 @@ namespace fastgltf {
* If no specialization for T of OptionalFlagValue is provided, a static assert will be triggered.
* In those cases, use std::optional or fastgltf::Optional instead.
*/
FASTGLTF_EXPORT template<typename T>
template<typename T>
class OptionalWithFlagValue final {
static_assert(!std::is_same_v<std::nullopt_t, std::remove_const_t<decltype(OptionalFlagValue<T>::missing_value)>>,
"OptionalWithFlagValue can only be used when there is an appropriate specialization of OptionalFlagValue<T>.");
Expand Down Expand Up @@ -1170,31 +1183,67 @@ namespace fastgltf {
}

template <typename F>
[[nodiscard]] OptionalWithFlagValue<T> and_then(F&& func)& {
[[nodiscard]] auto and_then(F&& func)& {
using U = std::remove_cv_t<std::remove_reference_t<std::invoke_result_t<F, T&>>>;
spnda marked this conversation as resolved.
Show resolved Hide resolved
if (!has_value())
return U();
return std::invoke(std::forward<F>(func), **this);
}

template <typename F>
[[nodiscard]] auto and_then(F&& func) const& {
using U = std::remove_cv_t<std::remove_reference_t<std::invoke_result_t<F, const T&>>>;
if (!has_value())
return U();
return std::invoke(std::forward<F>(func), **this);
}

template <typename F>
[[nodiscard]] auto and_then(F&& func)&& {
using U = std::remove_cv_t<std::remove_reference_t<std::invoke_result_t<F, T>>>;
if (!has_value())
return U();
return std::invoke(std::forward<F>(func), std::move(**this));
}

template <typename F>
[[nodiscard]] auto and_then(F&& func) const&& {
using U = std::remove_cv_t<std::remove_reference_t<std::invoke_result_t<F, const T>>>;
if (!has_value())
return U();
return std::invoke(std::forward<F>(func), std::move(**this));
}

template <typename F>
[[nodiscard]] auto transform(F&& func)& {
using U = std::remove_cv_t<std::invoke_result_t<F, T&>>;
if (!has_value())
return std::nullopt;
return func(value());
return Optional<U>();
return Optional<U>(std::invoke(std::forward<F>(func), **this));
}

template <typename F>
[[nodiscard]] OptionalWithFlagValue<T> and_then(F&& func)&& {
[[nodiscard]] auto transform(F&& func) const& {
using U = std::remove_cv_t<std::invoke_result_t<F, const T&>>;
if (!has_value())
return std::nullopt;
return func(std::move(value()));
return Optional<U>();
return Optional<U>(std::invoke(std::forward<F>(func), **this));
}

template <typename F>
[[nodiscard]] OptionalWithFlagValue<T> transform(F&& func)& {
[[nodiscard]] auto transform(F&& func)&& {
using U = std::remove_cv_t<std::invoke_result_t<F, T>>;
if (!has_value())
return std::nullopt;
return OptionalWithFlagValue<T>(func(value()));
return Optional<U>();
return Optional<U>(std::invoke(std::forward<F>(func), std::move(**this)));
}

template <typename F>
[[nodiscard]] OptionalWithFlagValue<T> transform(F&& func)&& {
[[nodiscard]] auto transform(F&& func) const&& {
using U = std::remove_cv_t<std::invoke_result_t<F, const T>>;
if (!has_value())
return std::nullopt;
return OptionalWithFlagValue<T>(func(std::move(value())));
return Optional<U>();
return Optional<U>(std::invoke(std::forward<F>(func), std::move(**this)));
n0F4x marked this conversation as resolved.
Show resolved Hide resolved
}

template <typename F>
Expand Down Expand Up @@ -1305,15 +1354,6 @@ namespace fastgltf {
return opt.has_value() && *opt >= value;
}

/**
* A type alias which checks if there is a specialization of OptionalFlagValue for T and "switches"
* between fastgltf::OptionalWithFlagValue and std::optional.
*/
FASTGLTF_EXPORT template <typename T>
using Optional = std::conditional_t<
!std::is_same_v<std::nullopt_t, std::remove_const_t<decltype(OptionalFlagValue<T>::missing_value)>>,
OptionalWithFlagValue<T>,
std::optional<T>>;
#pragma endregion

#pragma region Structs
Expand Down
6 changes: 3 additions & 3 deletions tests/math_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ TEST_CASE("Test TRS parsing and optional decomposition", "[maths]") {
const auto* pDefaultTRS = std::get_if<fastgltf::TRS>(&assetWithMatrix->nodes.front().transform);
REQUIRE(pDefaultTRS != nullptr);
auto translation = glm::make_vec3(pDefaultTRS->translation.data());
auto rotation = glm::make_quat(pDefaultTRS->rotation.value_ptr());
auto rotation = glm::make_quat(pDefaultTRS->rotation.data());
auto scale = glm::make_vec3(pDefaultTRS->scale.data());
auto rotationMatrix = glm::toMat4(rotation);
auto transform = glm::translate(glm::mat4(1.0f), translation) * rotationMatrix * glm::scale(glm::mat4(1.0f), scale);
Expand All @@ -262,7 +262,7 @@ TEST_CASE("Test TRS parsing and optional decomposition", "[maths]") {
// Check if the decomposed components equal the original components.
const auto* pDecomposedTRS = std::get_if<fastgltf::TRS>(&assetDecomposed->nodes.back().transform);
REQUIRE(glm::make_vec3(pDecomposedTRS->translation.data()) == translation);
REQUIRE(glm::make_quat(pDecomposedTRS->rotation.value_ptr()) == rotation);
REQUIRE(glm::make_quat(pDecomposedTRS->rotation.data()) == rotation);
REQUIRE(glm::make_vec3(pDecomposedTRS->scale.data()) == scale);
}

Expand Down Expand Up @@ -290,7 +290,7 @@ TEST_CASE("Test TRS parsing and optional decomposition", "[maths]") {
// future, but I suspect using double in the decompose functions should help mitigate most
// of it.
REQUIRE(glm::make_vec3(translation.data()) == glmTranslation);
REQUIRE(glm::all(glm::epsilonEqual(glm::make_quat(rotation.value_ptr()), glmRotation, glm::epsilon<float>() * 10)));
REQUIRE(glm::all(glm::epsilonEqual(glm::make_quat(rotation.data()), glmRotation, glm::epsilon<float>() * 10)));
REQUIRE(glm::all(glm::epsilonEqual(glm::make_vec3(scale.data()), glmScale, glm::epsilon<float>())));
}
}
Loading