Date: Wed, 5 Jun 2024 23:37:30 +0100
On Wed, Jun 5, 2024 at 4:46 PM Giuseppe D'Angelo wrote:
>
> How is this different from
>
> https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p2825r2.html
At the beginning of the paper is:
template <typename R, typename Args...>
struct my_erased_wrapper {
using fptr = R(*)(Args_...);
fptr erased = +[](my_erased_wrapper* self, auto&&... args_) -> R {
return self->fptr(std::forward<decltype>(args_)...);
};
};
Observation 1: typename Args... is a typo for typename... Args
Observation 2: It says "self->ptr" but 'ptr' isn't a member (it's a type)
declcall fails for constructors and destructors, and maybe this is
fine, but an alternative could be for it to return a member function
pointer. So for example if you had:
struct S {
int n;
S(int const arg) : n(arg) {}
S(double const arg) : n( static_cast<int>(arg + 0.5) ) {}
};
and then if you were to do:
declcall( S(7. 6) )
then it could return the address of the double-accepting-constructor
inside a "void (S::*)(double)". Then we could do the following:
int main(void)
{
S var(5);
var.~S();
constexpr auto mfp = declcall( S(6.2) );
(var.*mfp)(6.3);
}
Similarly, if you were to write:
declcall( std::declval<S&>().~S() )
then it could return the address of the destructor inside a "void
(S::*)(void)", and then we could do:
int main(void)
{
S var(5);
constexpr auto mfp = declcall( std::declval<S&>().~S() )
(var.*mfp)();
::new(&var) S(6);
}
C++ has never allowed us to take the address of a constructor or
destructor, but this has always been a needless restriction. Getting
the address of a destructor isn't so much of a big deal since we can
alternatively take the address of the following function:
template<typename T>
constexpr void call_destructor(T *const p) noexcept(noexcept(p->~T()))
{
p->~T();
}
However, being able to take the address of a constructor with declcall
would be helpful since we can't take the address of the following
function without knowing what conversions of arguments will take
place:
template<typename T, typename... Params>
constexpr void call_constructor(T *const p, Params&&... args)
{
std::construct_at<T>( p, std::forward<Params>(args)... );
}
So I'm in favour of being able to use declcall to get the addresses of
constructors and destructors.
And just one last thing . . . and it's not particularly relevant to
declcall . . . but it always bugged me that constructors and
destructors were the same name as the class . . . I don't know why
Bjarne did that . . . it would have made so much more sense for them
to be normal member functions which return 'void' and are named
"_Constructor" or "_Destructor". I'd like if C++26 allowed us to
write:
struct S {
void _Constructor(void) {}
};
instead of:
struct S {
S(void) {}
};
meaning we could simply do:
void (S::*)(void) = &S::_Constructor;
>
> How is this different from
>
> https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p2825r2.html
At the beginning of the paper is:
template <typename R, typename Args...>
struct my_erased_wrapper {
using fptr = R(*)(Args_...);
fptr erased = +[](my_erased_wrapper* self, auto&&... args_) -> R {
return self->fptr(std::forward<decltype>(args_)...);
};
};
Observation 1: typename Args... is a typo for typename... Args
Observation 2: It says "self->ptr" but 'ptr' isn't a member (it's a type)
declcall fails for constructors and destructors, and maybe this is
fine, but an alternative could be for it to return a member function
pointer. So for example if you had:
struct S {
int n;
S(int const arg) : n(arg) {}
S(double const arg) : n( static_cast<int>(arg + 0.5) ) {}
};
and then if you were to do:
declcall( S(7. 6) )
then it could return the address of the double-accepting-constructor
inside a "void (S::*)(double)". Then we could do the following:
int main(void)
{
S var(5);
var.~S();
constexpr auto mfp = declcall( S(6.2) );
(var.*mfp)(6.3);
}
Similarly, if you were to write:
declcall( std::declval<S&>().~S() )
then it could return the address of the destructor inside a "void
(S::*)(void)", and then we could do:
int main(void)
{
S var(5);
constexpr auto mfp = declcall( std::declval<S&>().~S() )
(var.*mfp)();
::new(&var) S(6);
}
C++ has never allowed us to take the address of a constructor or
destructor, but this has always been a needless restriction. Getting
the address of a destructor isn't so much of a big deal since we can
alternatively take the address of the following function:
template<typename T>
constexpr void call_destructor(T *const p) noexcept(noexcept(p->~T()))
{
p->~T();
}
However, being able to take the address of a constructor with declcall
would be helpful since we can't take the address of the following
function without knowing what conversions of arguments will take
place:
template<typename T, typename... Params>
constexpr void call_constructor(T *const p, Params&&... args)
{
std::construct_at<T>( p, std::forward<Params>(args)... );
}
So I'm in favour of being able to use declcall to get the addresses of
constructors and destructors.
And just one last thing . . . and it's not particularly relevant to
declcall . . . but it always bugged me that constructors and
destructors were the same name as the class . . . I don't know why
Bjarne did that . . . it would have made so much more sense for them
to be normal member functions which return 'void' and are named
"_Constructor" or "_Destructor". I'd like if C++26 allowed us to
write:
struct S {
void _Constructor(void) {}
};
instead of:
struct S {
S(void) {}
};
meaning we could simply do:
void (S::*)(void) = &S::_Constructor;
Received on 2024-06-05 22:37:43