I am looking at using the boost te lib in a project compiled with visual c++ (MSVC 19.29.30153.0 with c++ standard 20). I take the simpilest example erase it which is essentially:
struct Drawable {
void draw(std::ostream &out) const {
te::call([](auto const &self, auto &out) { self.draw(out); }, *this, out);
}
};
struct Square {
void draw(std::ostream &out) const { out << "Square"; }
};
int main() {
te::poly<Drawable> d = Square{};
d.draw();
}
This compiles, but at runtime fails at this point in te::poly:
return reinterpret_cast<R (*)(void *, Ts...)>(self.vptr[N - 1])(
self.ptr(), std::forward<Ts>(args)...);
With a read access violation:
Exception thrown: read access violation. self was 0xFFFFFFFFFFFFFFFF.
If i add in a line to watch the variable:
auto my_pointer = self.ptr();
It does indeed fail to call this, and on inspecting the variables:
self {vptr=0x00007ff6698614e2 {check_te_windows.exe! ...
+ __vfptr 0x00000142775a64a0 {0x000000fdfdfdfdcd} void * *
+ vptr 0x00007ff6698614e2
The vtable pointer does appear to be broken. This library has worked for me in clang, gcc and intels oneapi compiler. Here is a gcc live example: https://godbolt.org/z/boKaY19oj
Does anyone know what the problem might be on visual c++ compiler?
In the class template
polythe constructor initializer list shows the initializer forvtablebeforestorage, butstorageis still initialized first, because of declaration order:That's suspect, but what's even more suspect is the double
std::forwardwhich may invite a double-move from thetargument. In your sample, this suspect constructor is indeed invoked with the following template parameters:Instrumenting the Square class to prohibit the move make it not compile: https://godbolt.org/z/v4bjYf18b
IDEA 1
Based on these observations, you might think to avoid rvalue-arguments (your temporaries): https://godbolt.org/z/cMhd6Pa4h (of course, no problem on GCC, but check on MSVC?)
IDEA 2
You might flip the declaration order of the
polymembers from:to:
Of course, this changes layout and depending on nefarious practices might cause other things to change behaviour.
Summary, Pointers
Regardless, I'd suggest raising the issue with the TE devs.
Also consider using Boost Type Erasure, which is properly part of Boost: https://www.boost.org/doc/libs/1_84_0/doc/html/boost_typeerasure.html