diff --git a/include/boost/pfr.hpp b/include/boost/pfr.hpp index 8194ef4a..71e430f9 100644 --- a/include/boost/pfr.hpp +++ b/include/boost/pfr.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include diff --git a/include/boost/pfr/core_memptr.hpp b/include/boost/pfr/core_memptr.hpp new file mode 100644 index 00000000..478cfe4f --- /dev/null +++ b/include/boost/pfr/core_memptr.hpp @@ -0,0 +1,56 @@ +// Copyright (c) 2021-2023 Denis Mikhailov +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_PFR_CORE_MEMPTR_HPP +#define BOOST_PFR_CORE_MEMPTR_HPP +#pragma once + +#include + +#include + +#include +#include + +#include +#include // metaprogramming stuff + +#include +#include + +/// \file boost/pfr/core_memptr.hpp +/// Contains all the basic interfaces for working with member pointers of some structure \forcedlink{get_memptr}, and others. +/// +/// \b Synopsis: + +namespace boost { namespace pfr { + +/// \brief Returns member pointer to a field with index `I` in some \aggregate with type 'T'. +/// +/// \b Example: +/// \code +/// struct my_struct { int i, short s; }; +/// my_struct s {10, 11}; +/// assert(boost::pfr::get_memptr<0>(s) == &my_struct::i); +/// auto memptr = boost::pfr::get_memptr<1>(s); +/// s.*memptr = 0; +/// \endcode +template +inline auto get_memptr(boost::pfr::type_identity) noexcept +{ + return detail::sequence_tuple::get( detail::tie_as_memptrs_tuple() ); +} + +/// \overload get_memptr +template +inline auto get_memptr(const T&) noexcept +{ + // TODO: test it without default constructor + return get_memptr(boost::pfr::type_identity{}); +} + +}} // namespace boost::pfr + +#endif // BOOST_PFR_CORE_MEMPTR_HPP diff --git a/include/boost/pfr/detail/align.hpp b/include/boost/pfr/detail/align.hpp new file mode 100644 index 00000000..44d0c894 --- /dev/null +++ b/include/boost/pfr/detail/align.hpp @@ -0,0 +1,37 @@ +// Copyright (c) 2021-2023 Glen Joseph Fernandes, Denis Mikhailov +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + +// Initial implementation was taken from Boost.Align library by Glen Joseph Fernandes, https://github.com/glenfe +// See https://github.com/boostorg/align/blob/boost-1.83.0/include/boost/align/detail/align.hpp for more details +// + +#ifndef BOOST_PFR_DETAIL_ALIGN_HPP +#define BOOST_PFR_DETAIL_ALIGN_HPP +#pragma once + +#include +#include // for std::size_t + +namespace boost { namespace pfr { namespace detail { + + constexpr std::size_t align_offset(std::size_t alignment, std::size_t size, std::size_t& offset, std::size_t& space) noexcept + { + //BOOST_ASSERT(boost::alignment::detail::is_alignment(alignment)); TODO enable + if (size <= space) { + std::size_t p = ~(alignment - 1) & (offset + alignment - 1); + std::size_t n = p - offset; + if (n <= space - size) { + offset = p; + space -= n; + return p; + } + } + return (size_t)-1; + } + +}}} // namespace boost::pfr::detail + +#endif // BOOST_PFR_DETAIL_ALIGN_HPP diff --git a/include/boost/pfr/detail/core_memptr.hpp b/include/boost/pfr/detail/core_memptr.hpp new file mode 100644 index 00000000..eac626d1 --- /dev/null +++ b/include/boost/pfr/detail/core_memptr.hpp @@ -0,0 +1,95 @@ +// Copyright (c) 2021-2023 Denis Mikhailov +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_PFR_DETAIL_CORE_MEMPTR_HPP +#define BOOST_PFR_DETAIL_CORE_MEMPTR_HPP +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +// Each core_memptr provides `boost::pfr::detail::tie_as_memptrs_tuple` and +// `boost::pfr::detail::for_each_memptr_dispatcher` functions. +// +// The whole memptr's functionality in PFR library is build on top of those two functions. + +namespace boost { namespace pfr { namespace detail { + +template +constexpr auto make_sequence_tuple(Args... args) noexcept { + return sequence_tuple::tuple{ args... }; +} + +template +using tuple_element_ = sequence_tuple::tuple_element()) ) >; + +template +using tuple_memptr_t = typename detail::tuple_element_::type T::*; + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +template +constexpr std::size_t tie_as_offsets_tuple_impl_apply(I i, std::size_t& offset, std::size_t& space) noexcept +{ + using element_type = typename detail::tuple_element_::type; + std::size_t aligned = detail::align_offset(alignof(element_type), sizeof(element_type), offset, space); + offset += sizeof(element_type); + space -= sizeof(element_type); + return aligned; +} + +template +constexpr auto tie_as_offsets_tuple_impl(std::index_sequence, std::size_t& offset, std::size_t& space) noexcept +{ + return detail::make_sequence_tuple( tie_as_offsets_tuple_impl_apply(size_t_{}, offset, space)... ); +} + +template +constexpr auto tie_as_offsets_tuple_impl(std::index_sequence<>, std::size_t& offset, std::size_t& space) noexcept +{ + // TODO: test for empty structure + (void)offset; + (void)space; + return detail::make_sequence_tuple(); +} + +template +constexpr auto tie_as_offsets_tuple() noexcept +{ + // TODO: discard structures with non-standard alignment and bit fields + std::size_t offset = 0; + std::size_t space = sizeof(T); + + return tie_as_offsets_tuple_impl(detail::make_index_sequence()>{}, offset, space); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +template +inline auto tie_as_memptrs_tuple_impl(std::index_sequence) noexcept +{ + constexpr auto offsets = tie_as_offsets_tuple(); + return detail::make_sequence_tuple( detail::memptr_cast>(size_t_(offsets)>{})... ); +} + +template +inline auto tie_as_memptrs_tuple() noexcept { + static_assert( + !std::is_union::value, + "====================> Boost.PFR: For safety reasons it is forbidden to reflect unions. See `Reflection of unions` section in the docs for more info." + ); + + return tie_as_memptrs_tuple_impl(detail::make_index_sequence()>{}); +} + +}}} // namespace boost::pfr::detail + +#endif // BOOST_PFR_DETAIL_CORE_MEMPTR_HPP diff --git a/include/boost/pfr/detail/memptr_cast.hpp b/include/boost/pfr/detail/memptr_cast.hpp new file mode 100644 index 00000000..9cd1a463 --- /dev/null +++ b/include/boost/pfr/detail/memptr_cast.hpp @@ -0,0 +1,45 @@ +// Copyright (c) 2021-2023 Denis Mikhailov +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_PFR_DETAIL_MEMPTR_CAST_HPP +#define BOOST_PFR_DETAIL_MEMPTR_CAST_HPP +#pragma once + +#include +#include +#include + +namespace boost { namespace pfr { namespace detail { + + constexpr std::uint8_t unsigned_by_size(size_t_<1>) noexcept { return 0; } + constexpr std::uint16_t unsigned_by_size(size_t_<2>) noexcept { return 0; } + constexpr std::uint32_t unsigned_by_size(size_t_<4>) noexcept { return 0; } + constexpr std::uint64_t unsigned_by_size(size_t_<8>) noexcept { return 0; } + + template + inline T memptr_cast(size_t_ offset) + { + using raw_type = decltype(unsigned_by_size(size_t_{ })); + + static_assert( + sizeof(raw_type) <= sizeof(std::size_t), + "====================> Boost.PFR: Internal error while casting offset to member pointer." + ); + static_assert( + decltype(offset)::value <= (std::size_t)(std::numeric_limits::max)(), + "====================> Boost.PFR: Internal error while casting offset to member pointer: overflow was detected" + ); + + union { + T memptr; + raw_type offset_; + }; + offset_ = static_cast(offset); + return memptr; + } + +}}} // namespace boost::pfr::detail + +#endif // BOOST_PFR_DETAIL_MEMPTR_CAST_HPP diff --git a/include/boost/pfr/type_identity.hpp b/include/boost/pfr/type_identity.hpp new file mode 100644 index 00000000..1ea38355 --- /dev/null +++ b/include/boost/pfr/type_identity.hpp @@ -0,0 +1,22 @@ +// Copyright (c) 2021-2023 Denis Mikhailov +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_PFR_TYPE_IDENTITY_HPP +#define BOOST_PFR_TYPE_IDENTITY_HPP +#pragma once + +#include + +namespace boost { namespace pfr { + +template< class T > +struct type_identity { + using type = T; +}; + +}} // namespace boost::pfr + + +#endif // BOOST_PFR_TYPE_IDENTITY_HPP diff --git a/test/core/compile-fail/packed.cpp b/test/core/compile-fail/packed.cpp new file mode 100644 index 00000000..03c24b94 --- /dev/null +++ b/test/core/compile-fail/packed.cpp @@ -0,0 +1,20 @@ +// Copyright (c) 2023 Denis Mikhailov +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include + +#pragma pack(1) +struct Foo +{ + char ch; + short id; + short opt; + int value; +}; + +int main() { + (void)boost::pfr::get_memptr<3>(boost::pfr::type_identity{}); // Must be a compile time error +} + diff --git a/test/core/run/memptr.cpp b/test/core/run/memptr.cpp new file mode 100644 index 00000000..5adc1688 --- /dev/null +++ b/test/core/run/memptr.cpp @@ -0,0 +1,42 @@ +// Copyright (c) 2021-2023 Denis Mikhailov +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include + +struct Foo +{ + char ch; + short id; + short opt; + int value; +}; + +int main() { + + auto ch_memptr = boost::pfr::get_memptr<0>(boost::pfr::type_identity{}); + auto id_memptr = boost::pfr::get_memptr<1>(boost::pfr::type_identity{}); + auto opt_memptr = boost::pfr::get_memptr<2>(boost::pfr::type_identity{}); + auto value_memptr = boost::pfr::get_memptr<3>(boost::pfr::type_identity{}); + + BOOST_TEST_EQ(ch_memptr, &Foo::ch); + BOOST_TEST_EQ(id_memptr, &Foo::id); + BOOST_TEST_EQ(opt_memptr, &Foo::opt); + BOOST_TEST_EQ(value_memptr, &Foo::value); + + auto obj = Foo{}; + + obj.*ch_memptr = 'c'; + obj.*id_memptr = 100; + obj.*opt_memptr = 200; + obj.*value_memptr = 3000; + + BOOST_TEST_EQ(obj.ch, 'c'); + BOOST_TEST_EQ(obj.id, 100); + BOOST_TEST_EQ(obj.opt, 200); + BOOST_TEST_EQ(obj.value, 3000); + + return boost::report_errors(); +}