Date: Sat, 06 Nov 2021 00:26:17 +0000
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.
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
-------- Original Message --------
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?
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.
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
-------- Original Message --------
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?
Received on 2021-11-05 19:26:29