-
Notifications
You must be signed in to change notification settings - Fork 161
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
Member pointers functionality #77
base: develop
Are you sure you want to change the base?
Changes from all commits
b353849
3329156
a330973
b48f596
e69f265
ef0015f
0ff1475
89b9aa4
724775f
76ccbc1
2636b2e
7dcd890
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <boost/pfr/detail/config.hpp> | ||
|
||
#include <boost/pfr/detail/core_memptr.hpp> | ||
|
||
#include <boost/pfr/detail/sequence_tuple.hpp> | ||
#include <boost/pfr/detail/stdtuple.hpp> | ||
|
||
#include <type_traits> | ||
#include <utility> // metaprogramming stuff | ||
|
||
#include <boost/pfr/core.hpp> | ||
#include <boost/pfr/type_identity.hpp> | ||
|
||
/// \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<std::size_t I, class T> | ||
inline auto get_memptr(boost::pfr::type_identity<T>) noexcept | ||
{ | ||
return detail::sequence_tuple::get<I>( detail::tie_as_memptrs_tuple<T>() ); | ||
} | ||
|
||
/// \overload get_memptr | ||
template<std::size_t I, class T> | ||
inline auto get_memptr(const T&) noexcept | ||
{ | ||
// TODO: test it without default constructor | ||
return get_memptr<I>(boost::pfr::type_identity<T>{}); | ||
} | ||
|
||
}} // namespace boost::pfr | ||
|
||
#endif // BOOST_PFR_CORE_MEMPTR_HPP |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <boost/pfr/detail/config.hpp> | ||
#include <cstddef> // 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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. provide a more clear names for local variables instead of |
||
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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <boost/pfr/detail/config.hpp> | ||
#include <boost/pfr/detail/core.hpp> | ||
#include <boost/pfr/detail/fields_count.hpp> | ||
#include <boost/pfr/detail/sequence_tuple.hpp> | ||
#include <boost/pfr/detail/make_integer_sequence.hpp> | ||
#include <boost/pfr/detail/align.hpp> | ||
#include <boost/pfr/detail/memptr_cast.hpp> | ||
|
||
// 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 <class... Args> | ||
constexpr auto make_sequence_tuple(Args... args) noexcept { | ||
return sequence_tuple::tuple<Args...>{ args... }; | ||
} | ||
|
||
template <std::size_t I, class T> | ||
using tuple_element_ = sequence_tuple::tuple_element<I, decltype( detail::tie_as_tuple(std::declval<T&>()) ) >; | ||
|
||
template <std::size_t I, class T> | ||
using tuple_memptr_t = typename detail::tuple_element_<I, T>::type T::*; | ||
|
||
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||
|
||
template<class T, class I> | ||
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_<decltype(i)::value, T>::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<class T, std::size_t... I> | ||
constexpr auto tie_as_offsets_tuple_impl(std::index_sequence<I...>, std::size_t& offset, std::size_t& space) noexcept | ||
{ | ||
return detail::make_sequence_tuple( tie_as_offsets_tuple_impl_apply<T>(size_t_<I>{}, offset, space)... ); | ||
} | ||
|
||
template<class T> | ||
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<class T> | ||
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<T>(detail::make_index_sequence<detail::fields_count<T>()>{}, offset, space); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. add a namespace to |
||
} | ||
|
||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | ||
|
||
template<class T, std::size_t... I> | ||
inline auto tie_as_memptrs_tuple_impl(std::index_sequence<I...>) noexcept | ||
{ | ||
constexpr auto offsets = tie_as_offsets_tuple<T>(); | ||
return detail::make_sequence_tuple( detail::memptr_cast<detail::tuple_memptr_t<I, T>>(size_t_<sequence_tuple::get<I>(offsets)>{})... ); | ||
} | ||
|
||
template <class T> | ||
inline auto tie_as_memptrs_tuple() noexcept { | ||
static_assert( | ||
!std::is_union<T>::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<T>(detail::make_index_sequence<detail::fields_count<T>()>{}); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. add a namespace to |
||
} | ||
|
||
}}} // namespace boost::pfr::detail | ||
|
||
#endif // BOOST_PFR_DETAIL_CORE_MEMPTR_HPP |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <boost/pfr/detail/config.hpp> | ||
#include <cstdint> | ||
#include <limits> | ||
|
||
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<typename T, std::size_t I> | ||
inline T memptr_cast(size_t_<I> offset) | ||
{ | ||
using raw_type = decltype(unsigned_by_size(size_t_<sizeof(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<raw_type>::max)(), | ||
"====================> Boost.PFR: Internal error while casting offset to member pointer: overflow was detected" | ||
); | ||
|
||
union { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's an UB. Just use std::memcpy. The efficiency would remain the same - compilers know how to optimize std::memcpy |
||
T memptr; | ||
raw_type offset_; | ||
}; | ||
offset_ = static_cast<raw_type>(offset); | ||
return memptr; | ||
} | ||
|
||
}}} // namespace boost::pfr::detail | ||
|
||
#endif // BOOST_PFR_DETAIL_MEMPTR_CAST_HPP |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <boost/pfr/detail/config.hpp> | ||
|
||
namespace boost { namespace pfr { | ||
|
||
template< class T > | ||
struct type_identity { | ||
using type = T; | ||
}; | ||
|
||
}} // namespace boost::pfr | ||
|
||
|
||
#endif // BOOST_PFR_TYPE_IDENTITY_HPP |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <boost/pfr/core_memptr.hpp> | ||
|
||
#pragma pack(1) | ||
struct Foo | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Provide the same test but with a structure like this:
In order to test the case when settled alignment doesn't affect the size. BTW default align is not always |
||
{ | ||
char ch; | ||
short id; | ||
short opt; | ||
int value; | ||
}; | ||
|
||
int main() { | ||
(void)boost::pfr::get_memptr<3>(boost::pfr::type_identity<Foo>{}); // Must be a compile time error | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. tabs |
||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <boost/pfr/core_memptr.hpp> | ||
#include <boost/core/lightweight_test.hpp> | ||
|
||
struct Foo | ||
{ | ||
char ch; | ||
short id; | ||
short opt; | ||
int value; | ||
}; | ||
|
||
int main() { | ||
|
||
auto ch_memptr = boost::pfr::get_memptr<0>(boost::pfr::type_identity<Foo>{}); | ||
auto id_memptr = boost::pfr::get_memptr<1>(boost::pfr::type_identity<Foo>{}); | ||
auto opt_memptr = boost::pfr::get_memptr<2>(boost::pfr::type_identity<Foo>{}); | ||
auto value_memptr = boost::pfr::get_memptr<3>(boost::pfr::type_identity<Foo>{}); | ||
|
||
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(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's change it to
In that case the type_identity is not required and should be removed. The
overload is not required either