C++ Logo

std-proposals

Advanced search

Re: std::parent_of_member

From: connor horman <chorman64_at_[hidden]>
Date: Sat, 6 Nov 2021 10:20:51 -0400
On Fri, 5 Nov 2021 at 20:27, Lénárd Szolnoki via Std-Proposals <
std-proposals_at_[hidden]> wrote:

> Hi,
>
> I don't think a non-UB implementation is possible here. I have the
> impression that the pointer to the parent type is intentionally unreachable
> and in theory compilers are allowed to optimize based on that.
>
If it's a standard library function, there's always an implementation:
__builtin_parent_of_member

That being said, isn't this undefined behaviour anyways for anything other
than the first member of a standard-layout type? Either that or the
definition of reachability would have to be extended to include pointers to
any subobject.


> Having said that I never saw a compiler optimize based on that, although I
> experimented in the area.
>
> Example:
>
> struct A {
> int i;
> int j;
> };
>
> void foo(int*);
>
> void bar() {
> A a{1,2};
> foo(&a.j);
> return a.i; //can be optimized to return 1;
> }
>
> Also notable that if you want something like this and you control the
> types then you can do multiple inheritance and downcast.
>
> Cheers,
> Lénárd
>
> ------------------------------
> *From:* Michael Scire via Std-Proposals <std-proposals_at_[hidden]>
> *Sent:* November 5, 2021 11:09:14 PM GMT+00:00
> *To:* sotrdg sotrdg via Std-Proposals <std-proposals_at_[hidden]>
> *Cc:* Michael Scire <sciresm_at_[hidden]>
> *Subject:* [std-proposals] std::parent_of_member
>
> Hi,
>
> I wanted to propose a utility for working with intrusive members at
> compile-time.
>
> Tentatively, I would propose
>
> template<typename ParentType, typename MemberType> // Could we have
> template<auto MemberPtr> with ParentType and MemberType deduced?
> constexpr ParentType &parent_of_member(MemberType ParentType::*member_ptr,
> MemberType &member) {
> if (std::is_constant_evaluated()) {
> // Compiler magic needed here
> } else {
> const uintptr_t member_address =
> reinterpret_cast<uintptr_t>(std::addressof(member));
> const uintptr_t parent_address = member_address -
> reinterpret_cast<uintptr_t>(std::addressof(reinterpret_cast<ParentType
> *>(0)->*member_ptr));
>
> return *reinterpret_cast<ParentType *>(parent_address);
> }
> }
>
> Where parent_of_member would take in a pointer-to-member and a reference
> to a member, and return a reference to the parent object if the reference
> really is to the relevant member, and be undefined behavior otherwise.
>
> The example run-time implementation above may not avoid undefined
> behavior, but I do believe it is possible to implement without undefined
> behavior at runtime. Please correct me if I'm wrong.
>
> However, the reinterpret_casts/arithmetic above cannot be done at
> compile-time, and so compiler-assistance would be necessary.
>
> I think the above should be implementable -- both clang and gcc use
> symbolic representations of objects at compile-time, and so the compiler
> should have the ability to verify that the member reference is a member
> reference in truth.
>
>
> The above function is particularly useful for e.g. the case of intrusive
> lists. My use case looks something like:
>
> struct IntrusiveListNode {
> IntrusiveListNode *prev;
> IntrusiveListNode *next;
>
> // ...
> };
>
> class IntrusiveListImpl {
> // Common list management object for IntrusiveListNode, non-templated
> and shared for all types using intrusive list nodes.
> // ...
> };
>
> template<class Traits>
> class IntrusiveList {
> // Thin wrapper around IntrusiveListImpl for a specific intrusive list
> member.
> }
>
> template<auto MemberPtr, class Derived = /* Get ParentType from MemberPtr
> */>
> class IntrusiveListMemberTraits;
>
> template<class ParentType, IntrusiveListNode ParentType::*MemberPtr, class
> Derived>
> class IntrusiveListMemberTraits<MemberPtr, Derived> {
> public:
> using ListType = IntrusiveList<IntrusiveListMemberTraits>;
> public:
> static constexpr IntrusiveListNode &GetNode(Derived &parent) {
> return parent.*MemberPtr;
> }
>
> static constexpr IntrusiveListNode const &GetNode(Derived const
> &parent) {
> return parent.*MemberPtr;
> }
>
> static constexpr Derived &GetParent(IntrusiveListNode &node) {
> return static_cast<Derived &>(std::parent_of_member(MemberPtr,
> node));
> }
>
> static constexpr Derived const &GetParent(IntrusiveListNode const
> &node) {
> return static_cast<const Derived
> &>(std::parent_of_member(MemberPtr, node));
> }
> };
>
>
> struct ExampleTypeWithNode {
> int data;
> IntrusiveListNode node;
> };
>
> using ExampleTypeList =
> IntrusiveListMemberTraits<&ExampleTypeWithNode::node>::ListType;
>
> At which point all works as expected, with ExampleTypeList being a list of
> ExampleTypeWithNode using the member `node`.
>
> I have all this implemented and working at run-time, with two primary
> "Traits" variants -- one when node is a class member, and one where the
> class derives from IntrusiveListBaseNode<T>.
>
> When the intrusive node is a base of the class, GetParent/GetNode can be
> implemented at compile-time (static_cast<Derived
> &>(static_cast<IntrusiveListBaseNode<ParentType> &>(node))), and the code
> functions.
>
> It would be very useful to me to be able to have both variants of
> intrusive list function at compile-time, instead of only one -- and there
> are other intrusive members which would benefit greatly from this.
>
> I also think that a standard-blessed way to use a pointer-to-member to go
> from member to parent would be useful, rather than everybody using
> reinterpret_casts/arithmetic.
>
> My only thought is that it might be nice if the syntax could be e.g.
> std::parent_of_member<MemberPtr>(member), rather than
> std::parent_of_member(MemberPtr, member);
>
> Thoughts?
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>

Received on 2021-11-06 09:21:05