diff --git a/include/fastgltf/tools.hpp b/include/fastgltf/tools.hpp index d43528d9b..e5fca2f7b 100644 --- a/include/fastgltf/tools.hpp +++ b/include/fastgltf/tools.hpp @@ -895,7 +895,7 @@ void copyComponentsFromAccessor(const Asset& asset, const Accessor& accessor, vo * Computes the transform matrix for a given node, and multiplies the given base with that matrix. */ FASTGLTF_EXPORT inline auto getTransformMatrix(const Node& node, const math::fmat4x4& base = math::fmat4x4()) { - return std::visit(visitor { + return visit_exhaustive(visitor { [&](const math::fmat4x4& matrix) { return base * matrix; }, diff --git a/include/fastgltf/types.hpp b/include/fastgltf/types.hpp index cd08df70f..45dd6516f 100644 --- a/include/fastgltf/types.hpp +++ b/include/fastgltf/types.hpp @@ -1188,7 +1188,7 @@ namespace fastgltf { template [[nodiscard]] auto and_then(F&& func)& { - using U = std::remove_cv_t>>; + using U = remove_cvref_t>; if (!has_value()) return U(std::nullopt); return std::invoke(std::forward(func), **this); @@ -1196,7 +1196,7 @@ namespace fastgltf { template [[nodiscard]] auto and_then(F&& func) const& { - using U = std::remove_cv_t>>; + using U = remove_cvref_t>; if (!has_value()) return U(std::nullopt); return std::invoke(std::forward(func), **this); @@ -1204,7 +1204,7 @@ namespace fastgltf { template [[nodiscard]] auto and_then(F&& func)&& { - using U = std::remove_cv_t>>; + using U = remove_cvref_t>; if (!has_value()) return U(std::nullopt); return std::invoke(std::forward(func), std::move(**this)); @@ -1212,7 +1212,7 @@ namespace fastgltf { template [[nodiscard]] auto and_then(F&& func) const&& { - using U = std::remove_cv_t>>; + using U = remove_cvref_t>; if (!has_value()) return U(std::nullopt); return std::invoke(std::forward(func), std::move(**this)); diff --git a/include/fastgltf/util.hpp b/include/fastgltf/util.hpp index afecb7c0a..d78966ed2 100644 --- a/include/fastgltf/util.hpp +++ b/include/fastgltf/util.hpp @@ -330,6 +330,14 @@ namespace fastgltf { return str.rfind(search, 0) == 0; } + // Simple reimplementation of std::remove_cvref, which was only added in C++20. + template + struct remove_cvref { + using type = std::remove_cv_t>; + }; + template + using remove_cvref_t = typename remove_cvref::type; + /** * Helper type in order to allow building a visitor out of multiple lambdas within a call to * std::visit @@ -341,6 +349,24 @@ namespace fastgltf { FASTGLTF_EXPORT template visitor(Ts...) -> visitor; + template + constexpr bool is_exhaustive_visitor(std::integer_sequence) noexcept { + return std::conjunction_v>>...>; + } + + /** + * Simple wrapper around std::visit for a single variant that checks at compile-time if the given visitor contains + * overloads for *all* required alternatives. This is meant to guarantee correctness, since using something like + * fastgltf::visitor could fail unexpectedly due to const-issues without any compile-time errors or warnings. + * @note This currently does not support auto parameters. + */ + FASTGLTF_EXPORT template + constexpr decltype(auto) visit_exhaustive(Visitor&& visitor, Variant&& variant) { + static_assert(is_exhaustive_visitor(std::make_index_sequence>>()), + "The visitor does not include all necessary overloads for the given variant"); + return std::visit(std::forward(visitor), std::forward(variant)); + } + // For simple ops like &, |, +, - taking a left and right operand. #define FASTGLTF_ARITHMETIC_OP_TEMPLATE_MACRO(T1, T2, op) \ FASTGLTF_EXPORT constexpr T1 operator op(const T1& a, const T2& b) noexcept { \ diff --git a/src/fastgltf.cpp b/src/fastgltf.cpp index 99ae3f666..d28f9dbfc 100644 --- a/src/fastgltf.cpp +++ b/src/fastgltf.cpp @@ -5050,8 +5050,8 @@ void fg::Exporter::writeNodes(const Asset& asset, std::string& json) { } if (!it->children.empty()) { - if (json.back() != '{') - json += ','; + if (json.back() != '{') + json += ','; json += R"("children":[)"; auto itc = it->children.begin(); while (itc != it->children.end()) { @@ -5064,8 +5064,8 @@ void fg::Exporter::writeNodes(const Asset& asset, std::string& json) { } if (!it->weights.empty()) { - if (json.back() != '{') - json += ','; + if (json.back() != '{') + json += ','; json += R"("weights":[)"; auto itw = it->weights.begin(); while (itw != it->weights.end()) { @@ -5077,27 +5077,27 @@ void fg::Exporter::writeNodes(const Asset& asset, std::string& json) { json += ']'; } - std::visit(visitor { + visit_exhaustive(visitor { [&](const TRS& trs) { if (trs.rotation != math::fquat(0.f, 0.f, 0.f, 1.f)) { - if (json.back() != '{') - json += ','; + if (json.back() != '{') + json += ','; json += R"("rotation":[)"; json += std::to_string(trs.rotation[0]) + ',' + std::to_string(trs.rotation[1]) + ',' + std::to_string(trs.rotation[2]) + ',' + std::to_string(trs.rotation[3]); json += "]"; } if (trs.scale != math::fvec3(1.f)) { - if (json.back() != '{') - json += ','; + if (json.back() != '{') + json += ','; json += R"("scale":[)"; json += std::to_string(trs.scale[0]) + ',' + std::to_string(trs.scale[1]) + ',' + std::to_string(trs.scale[2]); json += "]"; } if (trs.translation != math::fvec3(0.f)) { - if (json.back() != '{') - json += ','; + if (json.back() != '{') + json += ','; json += R"("translation":[)"; json += std::to_string(trs.translation[0]) + ',' + std::to_string(trs.translation[1]) + ',' + std::to_string(trs.translation[2]); json += "]"; @@ -5119,7 +5119,7 @@ void fg::Exporter::writeNodes(const Asset& asset, std::string& json) { }, }, it->transform); - if (!it->instancingAttributes.empty() || it->lightIndex.has_value()) { + if (!it->instancingAttributes.empty() || it->lightIndex.has_value()) { if (json.back() != '{') json += ','; json += R"("extensions":{)"; if (!it->instancingAttributes.empty()) { @@ -5136,8 +5136,8 @@ void fg::Exporter::writeNodes(const Asset& asset, std::string& json) { if (json.back() != '{') json += ','; json += R"("KHR_lights_punctual":{"light":)" + std::to_string(it->lightIndex.value()) + "}"; } - json += "}"; - } + json += "}"; + } if (extrasWriteCallback != nullptr) { auto extras = extrasWriteCallback(uabs(std::distance(asset.nodes.begin(), it)), fastgltf::Category::Nodes, userPointer); @@ -5149,8 +5149,8 @@ void fg::Exporter::writeNodes(const Asset& asset, std::string& json) { } if (!it->name.empty()) { - if (json.back() != '{') - json += ','; + if (json.back() != '{') + json += ','; json += R"("name":")" + fg::escapeString(it->name) + '"'; } json += '}';