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

Member pointers functionality #77

Draft
wants to merge 12 commits into
base: develop
Choose a base branch
from

Conversation

denzor200
Copy link
Contributor

Hello Anton. I have some idea and i want to suggest it for you. This patch will add pretty useful functionality for extracting member pointer of some structure. For example:

struct Foo
{
	char ch;
	short id;
	short opt;
	int value;
};

auto ch_memptr = boost::pfr::get_memptr<0>(Foo{});
auto id_memptr = boost::pfr::get_memptr<1>(Foo{});
auto opt_memptr = boost::pfr::get_memptr<2>(Foo{});
auto value_memptr = boost::pfr::get_memptr<3>(Foo{});

auto obj = Foo{};

obj.*ch_memptr = 'c';
obj.*id_memptr = 100;
obj.*opt_memptr = 200;
obj.*value_memptr = 3000;

std::cout << obj.ch << " " << obj.id << " " << obj.opt << " " << obj.value << std::endl; ///< will print "c 100 200 3000"

Its just a draft - not a final code. If you are interested - i will continue for it.

@denzor200 denzor200 marked this pull request as draft April 25, 2021 20:12
@denzor200
Copy link
Contributor Author

This may help to resolve this issue:
#62
BUT i unfortunately do not know how to make get_memptr constexpr((

@apolukhin
Copy link
Member

Good idea!

I''ve simplified it a little bit https://godbolt.org/z/e8rKWrrEs

In my implementation problems are the same:

  • pointer arithmetic is not a constant expression (bit_cast refuses to convert pointers to integers)
  • Something must be done with the default constructor

@denzor200
Copy link
Contributor Author

Something must be done with the default constructor

  1. template<typename T> struct declval_helper { static T value; };
    But it not for clang and msvc
    https://godbolt.org/z/nYz6M61YT
  2. What if we pass a NULL reference into boost::pfr::get? This is works good, but i cant anticipate the consequences

@denzor200
Copy link
Contributor Author

denzor200 commented Jun 19, 2021

Something must be done with the default constructor

  1. Why don't we use the already checked offset_based_getter? In my opinion it solves this problem good.
    https://godbolt.org/z/hfxbnvqrE
    This works on MSVC too

@schaumb
Copy link

schaumb commented Feb 23, 2022

I tried to construct constexpr member pointer getter:

GCC and msvc are promising:
https://godbolt.org/z/hPa9718aY
https://godbolt.org/z/Th8KTKxns

Caveats:

  • msvc static assert: same member pointer at runtime, but not recognized as equal at compile time.
  • only for not final classes
  • clang not works with this technique

@denzor200
Copy link
Contributor Author

@schaumb interesting. Unfortunatelly, refl cannot be passed as template parameter, only orig can.

@schaumb
Copy link

schaumb commented Feb 24, 2022

@denzor200

I created a gcc ticket, because it is a compiler bug (probably?).

msvc constexpr member pointers can be used as template parameter. But:

template<auto> struct X {};
static_assert(std::is_same_v<X<refl>, X<orig>>);  // ERROR


template <auto V> decltype(V) wash() { return V; };
static_assert(&wash<refl> == &wash<orig>);  // OK
static_assert(wash<refl>() == wash<orig>()); // OK
// compiler save first call function instance template argument (currently the refl), so:
static_assert(wash<refl>() == refl); // OK
static_assert(wash<orig>() == orig); // ERROR

Not equality problem occures only if the original member pointer is used. But wash can be used as equality checker.

@denzor200
Copy link
Contributor Author

@schaumb
I created clang ticket here: llvm/llvm-project#56541

Let's see where the answer will be faster :)

Copy link
Member

@apolukhin apolukhin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! Here's a few nitpicks

/// s.*memptr = 0;
/// \endcode
template<std::size_t I, class T>
inline auto get_memptr(boost::pfr::type_identity<T>) noexcept
Copy link
Member

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

template<std::size_t I, class T>
inline auto get_memptr() noexcept

In that case the type_identity is not required and should be removed. The

template<std::size_t I, class T>
inline auto get_memptr(const T&) noexcept

overload is not required either

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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add a namespace to tie_as_offsets_tuple_impl to avoid ADL

"====================> 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>()>{});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add a namespace to tie_as_memptrs_tuple_impl to avoid ADL

"====================> Boost.PFR: Internal error while casting offset to member pointer: overflow was detected"
);

union {
Copy link
Member

Choose a reason for hiding this comment

The 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

{
//BOOST_ASSERT(boost::alignment::detail::is_alignment(alignment)); TODO enable
if (size <= space) {
std::size_t p = ~(alignment - 1) & (offset + alignment - 1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

provide a more clear names for local variables instead of p and n

short id;
short opt;
int value;
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add a non-trivial type to an aggregate, std:;string for example

@@ -0,0 +1,42 @@
// Copyright (c) 2021 Denis Mikhailov
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move this file to the test/core_memptr dir

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All new test filed should be moved to test/core_memptr

};

int main() {
(void)boost::pfr::get_memptr<3>(boost::pfr::type_identity<Foo>{}); // Must be a compile time error
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tabs

#include <boost/pfr/core_memptr.hpp>

#pragma pack(1)
struct Foo
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Provide the same test but with a structure like this:

#pragma pack(1)
struct sample
{
    int ch;
    int id;
    int opt;
    int value;
};

In order to test the case when settled alignment doesn't affect the size.

BTW default align is not always sizeof(int). You should provide some logic for select appropriate type between int, long, etc and for do nothing when nothing was selected for this platform.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants