diff --git a/develop/singles/headers/meta.hpp/meta_all.hpp b/develop/singles/headers/meta.hpp/meta_all.hpp index 3aa1cd7..7ca48b7 100644 --- a/develop/singles/headers/meta.hpp/meta_all.hpp +++ b/develop/singles/headers/meta.hpp/meta_all.hpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -2756,6 +2757,9 @@ namespace meta_hpp [[nodiscard]] uvalue unmap() const; [[nodiscard]] bool has_unmap_op() const noexcept; + [[nodiscard]] std::size_t size() const; + [[nodiscard]] bool has_size_op() const noexcept; + [[nodiscard]] bool less(const uvalue& other) const; [[nodiscard]] bool has_less_op() const noexcept; @@ -11280,6 +11284,71 @@ namespace meta_hpp::detail }; \ } +namespace meta_hpp::detail +{ + template < typename T > + struct size_traits; + + template < typename T > + concept has_size_traits // + = requires(const T& v) { size_traits>{}(v); }; +} + +namespace meta_hpp::detail +{ + template < typename T, std::size_t Size > + struct size_traits> { + std::size_t operator()(const std::array& v) const { + using std::size; + return size(v); + } + }; + + template < typename T, typename Traits, typename Allocator > + struct size_traits> { + std::size_t operator()(const std::basic_string& v) const { + using std::size; + return size(v); + } + }; + + template < typename T, typename Traits > + struct size_traits> { + std::size_t operator()(const std::basic_string_view& v) const { + using std::size; + return size(v); + } + }; + + template < typename T, std::size_t Extent > + struct size_traits> { + std::size_t operator()(const std::span& v) const { + using std::size; + return size(v); + } + }; + + template < typename T, typename Allocator > + struct size_traits> { + std::size_t operator()(const std::vector& v) const { + using std::size; + return size(v); + } + }; +} + +#define META_HPP_DECLARE_SIZE_TRAITS_FOR(T) \ + namespace meta_hpp::detail \ + { \ + template <> \ + struct size_traits { \ + std::size_t operator()(const T& v) const { \ + using std::size; \ + return size(v); \ + } \ + }; \ + } + namespace meta_hpp::detail { template < typename T > @@ -11323,9 +11392,11 @@ namespace meta_hpp void (*const move)(uvalue&& self, uvalue& to) noexcept; void (*const reset)(uvalue& self) noexcept; + uvalue (*const index)(const storage_u& self, std::size_t i); + std::size_t (*const size)(const storage_u& self); + uvalue (*const copy)(const storage_u& self); uvalue (*const deref)(const storage_u& self); - uvalue (*const index)(const storage_u& self, std::size_t i); uvalue (*const unmap)(const storage_u& self); bool (*const less)(const storage_u& l, const storage_u& r); @@ -11493,6 +11564,26 @@ namespace meta_hpp self.storage_.vtag = 0; }}, + .index{[]() { + if constexpr ( detail::has_index_traits ) { + return +[](const storage_u& self, std::size_t i) -> uvalue { + return detail::index_traits{}(*storage_cast(self), i); + }; + } else { + return nullptr; + } + }()}, + + .size{[]() { + if constexpr ( detail::has_size_traits ) { + return +[](const storage_u& self) -> std::size_t { + return detail::size_traits{}(*storage_cast(self)); + }; + } else { + return nullptr; + } + }()}, + .copy{[]() { if constexpr ( detail::has_copy_traits ) { return +[](const storage_u& self) -> uvalue { @@ -11513,16 +11604,6 @@ namespace meta_hpp } }()}, - .index{[]() { - if constexpr ( detail::has_index_traits ) { - return +[](const storage_u& self, std::size_t i) -> uvalue { - return detail::index_traits{}(*storage_cast(self), i); - }; - } else { - return nullptr; - } - }()}, - .unmap{[]() { if constexpr ( detail::has_unmap_traits ) { return +[](const storage_u& self) -> uvalue { @@ -11749,6 +11830,21 @@ namespace meta_hpp return tag != storage_e::nothing && vtable->unmap != nullptr; } + inline std::size_t uvalue::size() const { + auto&& [tag, vtable] = vtable_t::unpack_vtag(*this); + + if ( tag != storage_e::nothing && vtable->size != nullptr ) { + return vtable->size(storage_); + } + + throw_exception(error_code::bad_uvalue_operation); + } + + inline bool uvalue::has_size_op() const noexcept { + auto&& [tag, vtable] = vtable_t::unpack_vtag(*this); + return tag != storage_e::nothing && vtable->size != nullptr; + } + inline bool uvalue::less(const uvalue& other) const { if ( this == &other ) { return false; diff --git a/develop/untests/meta_utilities/value_tests.cpp b/develop/untests/meta_utilities/value_tests.cpp index 8e5916d..34471b3 100644 --- a/develop/untests/meta_utilities/value_tests.cpp +++ b/develop/untests/meta_utilities/value_tests.cpp @@ -102,6 +102,11 @@ namespace return {l.x + r.x, l.y + r.y}; } + [[maybe_unused]] + std::size_t size(const ivec2&) { + return 2; + } + [[maybe_unused]] bool operator==(const ivec2& l, const ivec2& r) noexcept { return l.x == r.x && l.y == r.y; @@ -136,6 +141,7 @@ META_HPP_DECLARE_COPY_TRAITS_FOR(ivec2_big) META_HPP_DECLARE_DEREF_TRAITS_FOR(deref_custom_class) META_HPP_DECLARE_INDEX_TRAITS_FOR(ivec2) +META_HPP_DECLARE_SIZE_TRAITS_FOR(ivec2) META_HPP_DECLARE_EQUALS_TRAITS_FOR(ivec2) @@ -911,7 +917,9 @@ TEST_CASE("meta/meta_utilities/value/arrays") { meta::uvalue v{42}; CHECK(v.get_type() == meta::resolve_type()); CHECK_FALSE(v.has_index_op()); + CHECK_FALSE(v.has_size_op()); CHECK_THROWS(std::ignore = v[0]); + CHECK_THROWS(std::ignore = v.size()); } SUBCASE("void*") { @@ -920,7 +928,9 @@ TEST_CASE("meta/meta_utilities/value/arrays") { meta::uvalue v{p}; CHECK(v.get_type() == meta::resolve_type()); CHECK_FALSE(v.has_index_op()); + CHECK_FALSE(v.has_size_op()); CHECK_THROWS(std::ignore = v[0]); + CHECK_THROWS(std::ignore = v.size()); } SUBCASE("const void*") { @@ -929,7 +939,9 @@ TEST_CASE("meta/meta_utilities/value/arrays") { meta::uvalue v{p}; CHECK(v.get_type() == meta::resolve_type()); CHECK_FALSE(v.has_index_op()); + CHECK_FALSE(v.has_size_op()); CHECK_THROWS(std::ignore = v[0]); + CHECK_THROWS(std::ignore = v.size()); } SUBCASE("int[3]") { @@ -938,7 +950,9 @@ TEST_CASE("meta/meta_utilities/value/arrays") { meta::uvalue v{arr}; CHECK(v.get_type() == meta::resolve_type()); CHECK(v.has_index_op()); + CHECK_FALSE(v.has_size_op()); + CHECK_THROWS(std::ignore = v.size()); CHECK(v[0].as() == 1); CHECK(v[1].as() == 2); CHECK(v[2].as() == 3); @@ -948,6 +962,8 @@ TEST_CASE("meta/meta_utilities/value/arrays") { meta::uvalue v{arr}; CHECK(v.get_type() == meta::resolve_type()); CHECK(v.has_index_op()); + CHECK_FALSE(v.has_size_op()); + CHECK_THROWS(std::ignore = v.size()); CHECK_FALSE(v[0]); } } @@ -958,7 +974,9 @@ TEST_CASE("meta/meta_utilities/value/arrays") { meta::uvalue v{arr}; CHECK(v.get_type() == meta::resolve_type()); CHECK(v.has_index_op()); + CHECK_FALSE(v.has_size_op()); + CHECK_THROWS(std::ignore = v.size()); CHECK(v[0].as() == 1); CHECK(v[1].as() == 2); CHECK(v[2].as() == 3); @@ -968,6 +986,8 @@ TEST_CASE("meta/meta_utilities/value/arrays") { meta::uvalue v{arr}; CHECK(v.get_type() == meta::resolve_type()); CHECK(v.has_index_op()); + CHECK_FALSE(v.has_size_op()); + CHECK_THROWS(std::ignore = v.size()); CHECK_FALSE(v[0]); } } @@ -976,7 +996,9 @@ TEST_CASE("meta/meta_utilities/value/arrays") { meta::uvalue v{std::array{1,2,3}}; CHECK(v.get_type() == meta::resolve_type>()); CHECK(v.has_index_op()); + CHECK(v.has_size_op()); + CHECK(v.size() == 3); CHECK(v[0].as() == 1); CHECK(v[1].as() == 2); CHECK(v[2].as() == 3); @@ -988,6 +1010,7 @@ TEST_CASE("meta/meta_utilities/value/arrays") { CHECK(v.get_type() == meta::resolve_type()); CHECK(v.has_index_op()); + CHECK(v.size() == 3); CHECK(v[0].as() == 'h'); CHECK(v[1].as() == 'i'); CHECK(v[2].as() == '!'); @@ -998,7 +1021,9 @@ TEST_CASE("meta/meta_utilities/value/arrays") { meta::uvalue v{std::string_view{"hi!"}}; CHECK(v.get_type() == meta::resolve_type()); CHECK(v.has_index_op()); + CHECK(v.has_size_op()); + CHECK(v.size() == 3); CHECK(v[0].as() == 'h'); CHECK(v[1].as() == 'i'); CHECK(v[2].as() == '!'); @@ -1010,7 +1035,9 @@ TEST_CASE("meta/meta_utilities/value/arrays") { meta::uvalue v{std::span{arr}}; CHECK(v.get_type() == meta::resolve_type>()); CHECK(v.has_index_op()); + CHECK(v.has_size_op()); + CHECK(v.size() == 3); CHECK(v[0].as() == 1); CHECK(v[1].as() == 2); CHECK(v[2].as() == 3); @@ -1021,7 +1048,9 @@ TEST_CASE("meta/meta_utilities/value/arrays") { const meta::uvalue v{std::vector{1,2,3}}; CHECK(v.get_type() == meta::resolve_type>()); CHECK(v.has_index_op()); + CHECK(v.has_size_op()); + CHECK(v.size() == 3); CHECK(v[0].as() == 1); CHECK(v[1].as() == 2); CHECK(v[2].as() == 3); @@ -1033,7 +1062,9 @@ TEST_CASE("meta/meta_utilities/value/arrays") { meta::uvalue v{ivec2{1,2}}; CHECK(v.get_type() == meta::resolve_type()); CHECK(v.has_index_op()); + CHECK(v.has_size_op()); + CHECK(v.size() == 2); CHECK(v[0].as() == 1); CHECK(v[1].as() == 2); } @@ -1041,6 +1072,8 @@ TEST_CASE("meta/meta_utilities/value/arrays") { meta::uvalue v{ivec3{1,2,3}}; CHECK(v.get_type() == meta::resolve_type()); CHECK_FALSE(v.has_index_op()); + CHECK_FALSE(v.has_size_op()); + CHECK_THROWS(std::ignore = v.size()); CHECK_THROWS(std::ignore = v[0]); } } diff --git a/headers/meta.hpp/meta_base/base.hpp b/headers/meta.hpp/meta_base/base.hpp index 12f79a5..7c5d000 100644 --- a/headers/meta.hpp/meta_base/base.hpp +++ b/headers/meta.hpp/meta_base/base.hpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include diff --git a/headers/meta.hpp/meta_detail/value_traits/size_traits.hpp b/headers/meta.hpp/meta_detail/value_traits/size_traits.hpp new file mode 100644 index 0000000..494b75e --- /dev/null +++ b/headers/meta.hpp/meta_detail/value_traits/size_traits.hpp @@ -0,0 +1,75 @@ +/******************************************************************************* + * This file is part of the "https://github.com/blackmatov/meta.hpp" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2021-2025, by Matvey Cherevko (blackmatov@gmail.com) + ******************************************************************************/ + +#pragma once + +#include "../../meta_base.hpp" +#include "../../meta_uvalue.hpp" + +namespace meta_hpp::detail +{ + template < typename T > + struct size_traits; + + template < typename T > + concept has_size_traits // + = requires(const T& v) { size_traits>{}(v); }; +} + +namespace meta_hpp::detail +{ + template < typename T, std::size_t Size > + struct size_traits> { + std::size_t operator()(const std::array& v) const { + using std::size; + return size(v); + } + }; + + template < typename T, typename Traits, typename Allocator > + struct size_traits> { + std::size_t operator()(const std::basic_string& v) const { + using std::size; + return size(v); + } + }; + + template < typename T, typename Traits > + struct size_traits> { + std::size_t operator()(const std::basic_string_view& v) const { + using std::size; + return size(v); + } + }; + + template < typename T, std::size_t Extent > + struct size_traits> { + std::size_t operator()(const std::span& v) const { + using std::size; + return size(v); + } + }; + + template < typename T, typename Allocator > + struct size_traits> { + std::size_t operator()(const std::vector& v) const { + using std::size; + return size(v); + } + }; +} + +#define META_HPP_DECLARE_SIZE_TRAITS_FOR(T) \ + namespace meta_hpp::detail \ + { \ + template <> \ + struct size_traits { \ + std::size_t operator()(const T& v) const { \ + using std::size; \ + return size(v); \ + } \ + }; \ + } diff --git a/headers/meta.hpp/meta_uvalue.hpp b/headers/meta.hpp/meta_uvalue.hpp index 6cbbb31..18e6085 100644 --- a/headers/meta.hpp/meta_uvalue.hpp +++ b/headers/meta.hpp/meta_uvalue.hpp @@ -77,6 +77,9 @@ namespace meta_hpp [[nodiscard]] uvalue unmap() const; [[nodiscard]] bool has_unmap_op() const noexcept; + [[nodiscard]] std::size_t size() const; + [[nodiscard]] bool has_size_op() const noexcept; + [[nodiscard]] bool less(const uvalue& other) const; [[nodiscard]] bool has_less_op() const noexcept; diff --git a/headers/meta.hpp/meta_uvalue/uvalue.hpp b/headers/meta.hpp/meta_uvalue/uvalue.hpp index 2a6e359..b433379 100644 --- a/headers/meta.hpp/meta_uvalue/uvalue.hpp +++ b/headers/meta.hpp/meta_uvalue/uvalue.hpp @@ -15,6 +15,7 @@ #include "../meta_detail/value_traits/equals_traits.hpp" #include "../meta_detail/value_traits/index_traits.hpp" #include "../meta_detail/value_traits/less_traits.hpp" +#include "../meta_detail/value_traits/size_traits.hpp" #include "../meta_detail/value_traits/unmap_traits.hpp" #include "../meta_detail/value_utilities/uarg.hpp" @@ -29,9 +30,11 @@ namespace meta_hpp void (*const move)(uvalue&& self, uvalue& to) noexcept; void (*const reset)(uvalue& self) noexcept; + uvalue (*const index)(const storage_u& self, std::size_t i); + std::size_t (*const size)(const storage_u& self); + uvalue (*const copy)(const storage_u& self); uvalue (*const deref)(const storage_u& self); - uvalue (*const index)(const storage_u& self, std::size_t i); uvalue (*const unmap)(const storage_u& self); bool (*const less)(const storage_u& l, const storage_u& r); @@ -199,6 +202,26 @@ namespace meta_hpp self.storage_.vtag = 0; }}, + .index{[]() { + if constexpr ( detail::has_index_traits ) { + return +[](const storage_u& self, std::size_t i) -> uvalue { + return detail::index_traits{}(*storage_cast(self), i); + }; + } else { + return nullptr; + } + }()}, + + .size{[]() { + if constexpr ( detail::has_size_traits ) { + return +[](const storage_u& self) -> std::size_t { + return detail::size_traits{}(*storage_cast(self)); + }; + } else { + return nullptr; + } + }()}, + .copy{[]() { if constexpr ( detail::has_copy_traits ) { return +[](const storage_u& self) -> uvalue { @@ -219,16 +242,6 @@ namespace meta_hpp } }()}, - .index{[]() { - if constexpr ( detail::has_index_traits ) { - return +[](const storage_u& self, std::size_t i) -> uvalue { - return detail::index_traits{}(*storage_cast(self), i); - }; - } else { - return nullptr; - } - }()}, - .unmap{[]() { if constexpr ( detail::has_unmap_traits ) { return +[](const storage_u& self) -> uvalue { @@ -455,6 +468,21 @@ namespace meta_hpp return tag != storage_e::nothing && vtable->unmap != nullptr; } + inline std::size_t uvalue::size() const { + auto&& [tag, vtable] = vtable_t::unpack_vtag(*this); + + if ( tag != storage_e::nothing && vtable->size != nullptr ) { + return vtable->size(storage_); + } + + throw_exception(error_code::bad_uvalue_operation); + } + + inline bool uvalue::has_size_op() const noexcept { + auto&& [tag, vtable] = vtable_t::unpack_vtag(*this); + return tag != storage_e::nothing && vtable->size != nullptr; + } + inline bool uvalue::less(const uvalue& other) const { if ( this == &other ) { return false; diff --git a/manuals/api/basics.md b/manuals/api/basics.md index 84bd401..094ce0b 100644 --- a/manuals/api/basics.md +++ b/manuals/api/basics.md @@ -191,6 +191,9 @@ public: uvalue unmap() const; bool has_unmap_op() const noexcept; + std::size_t size() const; + bool has_size_op() const noexcept; + bool less(const uvalue& other) const; bool has_less_op() const noexcept;