Date: Tue, 25 Nov 2025 16:08:00 +0000
I still think that 'std::chimeric_ptr' is the best thing since sliced
bread, but I want to take a look at another possible strategy.
First let's start off with the following class hierarchy:
struct Widget {
virtual ~Widget(void) noexcept = default;
};
struct Text : virtual Widget {
virtual void set_text(void) noexcept = 0;
};
struct Colorful : virtual Widget {
virtual void set_color(void) noexcept = 0;
};
struct Placard : Colorful, Text {
void set_color(void) noexcept override { puts("Placard SetColor"); }
void set_text (void) noexcept override { puts("Placard SetText" ); }
};
struct Billboard : Text, Colorful {
void set_color(void) noexcept override { puts("Billboard SetColor"); }
void set_text (void) noexcept override { puts("Billboard SetText" ); }
};
Next let's make a pretend class as follows:
struct ColorfulText : virtual Text, virtual Colorful {
ColorfulText(void); // declare but do not define
};
We will never actually create an object of type 'ColorfulText', but we
will pretend to. After we have created a make-believe object of this
type, we can then use the following function on it:
void set_text_and_color( ColorfulText &ct )
{
ct.set_text ();
ct.set_color();
}
So now we need to write a function that creates a make-believe
ColorfulText object. Inside this function, we have to copy the VTable
of ColorfulText and edit the offsets to the virtual bases. Something
like the following:
class MakeBeliever_ColorfulText {
alignas(std::max_align_t) char vtable[128u], object[128u];
public:
template<class T>
MakeBeliever_ColorfulText(T &&arg) noexcept
{
using std::intptr_t, std::memcpy;
auto const pt = std::addressof(static_cast<Text &>(arg));
auto const pc = std::addressof(static_cast<Colorful&>(arg));
// Copy the entire vtable
memcpy(vtable,
(char*)std::get_polymorphic_facilitator<ColorfulText>() - 64, 128u);
void const **const vptr = (void const**)(vtable + 64);
// In the copied vtable, edit the offset to Text (located at -48)
intptr_t &offset_of_Text = *((intptr_t*)vptr - 6);
offset_of_Text = (char*)pt - object;
// In the copied vtable, edit the offset to Colorful
(located at -56)
intptr_t &offset_of_Colorful = *((intptr_t*)vptr - 7);
offset_of_Colorful = (char*)pc - object;
// Set the vptr inside the make-believe object
((void const**)object)[0] = vptr;
}
operator ColorfulText&(void) noexcept
{
return *(ColorfulText*)object;
}
};
And then we can invoke it as follows:
int main(void)
{
Placard placard;
set_text_and_color( MakeBeliever_ColorfulText(placard) );
}
I have this tested and working up on GodBolt:
https://godbolt.org/z/5Y3fTeqq3
Now of course my GodBolt code contains undefined behaviour and also
behaviour specific to the Itanium ABI on x86_64, but the point of it
is to show that this can be done and that the compiler vendor can make
it happen without UB. So maybe it's worth looking into as a way of
solving this problem? As an alternative to std::chimeric_ptr?
And here's the entire GodBolt copy-pasted:
#include <cstddef> // max_align_t
#include <cstdint> // uintptr_t
#include <cstdio> // puts
#include <cstring> // memcpy
#include <iostream> // cout, endl
#include <memory> // get_polymorphic_facilitator
using std::puts;
struct Widget {
virtual ~Widget(void) noexcept = default;
};
struct Text : virtual Widget {
virtual void set_text(void) noexcept = 0;
};
struct Colorful : virtual Widget {
virtual void set_color(void) noexcept = 0;
};
struct Placard : Colorful, Text {
void set_color(void) noexcept override { puts("Placard SetColor"); }
void set_text (void) noexcept override { puts("Placard SetText" ); }
};
struct Billboard : Text, Colorful {
void set_color(void) noexcept override { puts("Billboard SetColor"); }
void set_text (void) noexcept override { puts("Billboard SetText" ); }
};
// ================= The trickery begins below this line
struct ColorfulText : virtual Text, virtual Colorful {
ColorfulText(void); // declare but do not define
};
class MakeBeliever_ColorfulText {
alignas(std::max_align_t) char vtable[128u], object[128u];
public:
template<class T>
MakeBeliever_ColorfulText(T &&arg) noexcept
{
using std::intptr_t, std::memcpy;
auto const pt = std::addressof(static_cast<Text &>(arg));
auto const pc = std::addressof(static_cast<Colorful&>(arg));
// Copy the entire vtable
memcpy(vtable,
(char*)std::get_polymorphic_facilitator<ColorfulText>() - 64, 128u);
void const **const vptr = (void const**)(vtable + 64);
// In the copied vtable, edit the offset to Text (located at -48)
intptr_t &offset_of_Text = *((intptr_t*)vptr - 6);
offset_of_Text = (char*)pt - object;
// In the copied vtable, edit the offset to Colorful (located at -56)
intptr_t &offset_of_Colorful = *((intptr_t*)vptr - 7);
offset_of_Colorful = (char*)pc - object;
// Set the vptr inside the make-believe object
((void const**)object)[0] = vptr;
}
operator ColorfulText&(void) noexcept
{
return *(ColorfulText*)object;
}
};
void set_text_and_color( ColorfulText &ct )
{
ct.set_text ();
ct.set_color();
}
int main(void)
{
Placard placard;
set_text_and_color( MakeBeliever_ColorfulText(placard) );
}
bread, but I want to take a look at another possible strategy.
First let's start off with the following class hierarchy:
struct Widget {
virtual ~Widget(void) noexcept = default;
};
struct Text : virtual Widget {
virtual void set_text(void) noexcept = 0;
};
struct Colorful : virtual Widget {
virtual void set_color(void) noexcept = 0;
};
struct Placard : Colorful, Text {
void set_color(void) noexcept override { puts("Placard SetColor"); }
void set_text (void) noexcept override { puts("Placard SetText" ); }
};
struct Billboard : Text, Colorful {
void set_color(void) noexcept override { puts("Billboard SetColor"); }
void set_text (void) noexcept override { puts("Billboard SetText" ); }
};
Next let's make a pretend class as follows:
struct ColorfulText : virtual Text, virtual Colorful {
ColorfulText(void); // declare but do not define
};
We will never actually create an object of type 'ColorfulText', but we
will pretend to. After we have created a make-believe object of this
type, we can then use the following function on it:
void set_text_and_color( ColorfulText &ct )
{
ct.set_text ();
ct.set_color();
}
So now we need to write a function that creates a make-believe
ColorfulText object. Inside this function, we have to copy the VTable
of ColorfulText and edit the offsets to the virtual bases. Something
like the following:
class MakeBeliever_ColorfulText {
alignas(std::max_align_t) char vtable[128u], object[128u];
public:
template<class T>
MakeBeliever_ColorfulText(T &&arg) noexcept
{
using std::intptr_t, std::memcpy;
auto const pt = std::addressof(static_cast<Text &>(arg));
auto const pc = std::addressof(static_cast<Colorful&>(arg));
// Copy the entire vtable
memcpy(vtable,
(char*)std::get_polymorphic_facilitator<ColorfulText>() - 64, 128u);
void const **const vptr = (void const**)(vtable + 64);
// In the copied vtable, edit the offset to Text (located at -48)
intptr_t &offset_of_Text = *((intptr_t*)vptr - 6);
offset_of_Text = (char*)pt - object;
// In the copied vtable, edit the offset to Colorful
(located at -56)
intptr_t &offset_of_Colorful = *((intptr_t*)vptr - 7);
offset_of_Colorful = (char*)pc - object;
// Set the vptr inside the make-believe object
((void const**)object)[0] = vptr;
}
operator ColorfulText&(void) noexcept
{
return *(ColorfulText*)object;
}
};
And then we can invoke it as follows:
int main(void)
{
Placard placard;
set_text_and_color( MakeBeliever_ColorfulText(placard) );
}
I have this tested and working up on GodBolt:
https://godbolt.org/z/5Y3fTeqq3
Now of course my GodBolt code contains undefined behaviour and also
behaviour specific to the Itanium ABI on x86_64, but the point of it
is to show that this can be done and that the compiler vendor can make
it happen without UB. So maybe it's worth looking into as a way of
solving this problem? As an alternative to std::chimeric_ptr?
And here's the entire GodBolt copy-pasted:
#include <cstddef> // max_align_t
#include <cstdint> // uintptr_t
#include <cstdio> // puts
#include <cstring> // memcpy
#include <iostream> // cout, endl
#include <memory> // get_polymorphic_facilitator
using std::puts;
struct Widget {
virtual ~Widget(void) noexcept = default;
};
struct Text : virtual Widget {
virtual void set_text(void) noexcept = 0;
};
struct Colorful : virtual Widget {
virtual void set_color(void) noexcept = 0;
};
struct Placard : Colorful, Text {
void set_color(void) noexcept override { puts("Placard SetColor"); }
void set_text (void) noexcept override { puts("Placard SetText" ); }
};
struct Billboard : Text, Colorful {
void set_color(void) noexcept override { puts("Billboard SetColor"); }
void set_text (void) noexcept override { puts("Billboard SetText" ); }
};
// ================= The trickery begins below this line
struct ColorfulText : virtual Text, virtual Colorful {
ColorfulText(void); // declare but do not define
};
class MakeBeliever_ColorfulText {
alignas(std::max_align_t) char vtable[128u], object[128u];
public:
template<class T>
MakeBeliever_ColorfulText(T &&arg) noexcept
{
using std::intptr_t, std::memcpy;
auto const pt = std::addressof(static_cast<Text &>(arg));
auto const pc = std::addressof(static_cast<Colorful&>(arg));
// Copy the entire vtable
memcpy(vtable,
(char*)std::get_polymorphic_facilitator<ColorfulText>() - 64, 128u);
void const **const vptr = (void const**)(vtable + 64);
// In the copied vtable, edit the offset to Text (located at -48)
intptr_t &offset_of_Text = *((intptr_t*)vptr - 6);
offset_of_Text = (char*)pt - object;
// In the copied vtable, edit the offset to Colorful (located at -56)
intptr_t &offset_of_Colorful = *((intptr_t*)vptr - 7);
offset_of_Colorful = (char*)pc - object;
// Set the vptr inside the make-believe object
((void const**)object)[0] = vptr;
}
operator ColorfulText&(void) noexcept
{
return *(ColorfulText*)object;
}
};
void set_text_and_color( ColorfulText &ct )
{
ct.set_text ();
ct.set_color();
}
int main(void)
{
Placard placard;
set_text_and_color( MakeBeliever_ColorfulText(placard) );
}
Received on 2025-11-25 16:08:17
