Date: Mon, 26 Feb 2024 10:22:14 +0000
With regard to dynamic_cast'ing to a void*, the Standard currently reads:
7.6.1.7 Dynamic cast
1. The result of the expression dynamic_cast<T>(v) is
the result of converting the expression v to type T.
5. Otherwise, v shall be a pointer to or a glvalue of a
polymorphic type.
7. If T is “pointer to cv void”, then the result is a pointer
to the most derived object pointed to by v.
So let's consider the following function:
void *Func(std::ostream *const v)
{
return dynamic_cast<void*>(v);
}
On an x86_64 computer running Linux or macOS, using the GNU g++
compiler, this becomes:
test rdi,rdi
je the_pointer_is_null
mov rax,QWORD PTR [rdi]
add rdi,QWORD PTR [rax-0x10]
mov rax,rdi
ret
the_pointer_is_null:
xor eax,eax
ret
If we remove the code that allows for 'v' to be a nullptr, it's
simplified further to:
mov rax,QWORD PTR [rdi]
add rdi,QWORD PTR [rax-0x10]
mov rax,rdi
ret
This assembler can be converted back into C++ as follows:
void *Func(void *const v)
{
auto const p_vtable = *static_cast<uintptr_t**>(v);
auto const top_offset = p_vtable[-2];
return static_cast<char*>(v) + top_offset;
}
The implementation of this function is the same no matter what the
type of 'v' is -- so long as it's a polymorphic type. In fact, it can
be rewritten as follows:
void *Func(void *const v)
{
struct Dummy { virtual ~Dummy(void){} };
return dynamic_cast<void*>( static_cast<Dummy*>(v) );
}
The point I want to make here is as follows: So long as 'v' points to
a polymorphic type, it is not necessary to know that that type is.
Therefore I think that the functionality of 'dynamic_cast' should be
expanded to allow 'v' to be a pointer to void -- with the note that
the behaviour is undefined if 'v' points to a non-polymorphic object.
7.6.1.7 Dynamic cast
1. The result of the expression dynamic_cast<T>(v) is
the result of converting the expression v to type T.
5. Otherwise, v shall be a pointer to or a glvalue of a
polymorphic type.
7. If T is “pointer to cv void”, then the result is a pointer
to the most derived object pointed to by v.
So let's consider the following function:
void *Func(std::ostream *const v)
{
return dynamic_cast<void*>(v);
}
On an x86_64 computer running Linux or macOS, using the GNU g++
compiler, this becomes:
test rdi,rdi
je the_pointer_is_null
mov rax,QWORD PTR [rdi]
add rdi,QWORD PTR [rax-0x10]
mov rax,rdi
ret
the_pointer_is_null:
xor eax,eax
ret
If we remove the code that allows for 'v' to be a nullptr, it's
simplified further to:
mov rax,QWORD PTR [rdi]
add rdi,QWORD PTR [rax-0x10]
mov rax,rdi
ret
This assembler can be converted back into C++ as follows:
void *Func(void *const v)
{
auto const p_vtable = *static_cast<uintptr_t**>(v);
auto const top_offset = p_vtable[-2];
return static_cast<char*>(v) + top_offset;
}
The implementation of this function is the same no matter what the
type of 'v' is -- so long as it's a polymorphic type. In fact, it can
be rewritten as follows:
void *Func(void *const v)
{
struct Dummy { virtual ~Dummy(void){} };
return dynamic_cast<void*>( static_cast<Dummy*>(v) );
}
The point I want to make here is as follows: So long as 'v' points to
a polymorphic type, it is not necessary to know that that type is.
Therefore I think that the functionality of 'dynamic_cast' should be
expanded to allow 'v' to be a pointer to void -- with the note that
the behaviour is undefined if 'v' points to a non-polymorphic object.
Received on 2024-02-26 10:22:27