lundi 23 février 2015

Type erasure works or fails depending on optimization level

I am trying to wrap a value type in a wrapper through type erasure (as part of a simple formatted text output library). The function print below is supposed to take an argument wrapped in a type erasing wrapper struct that knows (via a function pointer) how to convert it to a string and print it.


It prints 0 (or sometimes garbage) when I compile it with:



g++ -std=c++11 -Wall -Wextra -O0 -o test test.cpp


but it works as expected when compiled with -O1 through -O3 (-Og also fails). With clang++, it behaves the other way around (it fails when optimizations are enabled). I also tried g++ -m32 (I have multilib gcc on my x86_64 Linux Mint box) and 32-bit and 64-bit mingw-w64 cross compilers. The behavior is similar. Also, clang++-libc++ seems to always fail.


I must be triggering some undefined behavior (since I find it very unlikely to g++ and clang++ have the same bug). What is going on, what am I missing?



#include <iostream>
#include <string>

using namespace std;

// Struct holding a pointer to a type-erased value and a function pointer to
// convert it to a string
struct TypeErasingWrapper {
void* item; // Pointer to type erased value
string (*to_string)(void*); // Function pointer to convert it to a string
};

// Convert any value pointer to a string (using the proper overload of
// std::to_string
template <typename T>
string toString (void* item)
{
return to_string(*reinterpret_cast<T*>(item));
}

// Wrap any value in a type-erasing wrapper
template <typename T>
TypeErasingWrapper wrap(T value) {
return {&value, toString<T>};
}

// Print a type erased value
void print(TypeErasingWrapper wrapper)
{
cout << wrapper.to_string(wrapper.item) << endl;
}

int main()
{
print(wrap(1234));
}


Here's the version without templates that behaves the same way.



#include <iostream>
#include <string>

using namespace std;

// Struct holding a pointer to a type-erased int and a function pointer to
// convert it to a string
struct TypeErasingWrapper {
void* item; // Pointer to type erased int
string (*to_string)(void*); // Function pointer to convert it to a string
};

// Convert type-erased int to a string
string toString (void* item)
{
return to_string(*reinterpret_cast<int*>(item));
}

// Wrap an int in a type-erasing wrapper
TypeErasingWrapper wrap(int value) {
return {&value, toString};
}

// Print a type erased value
void print(TypeErasingWrapper wrapper)
{
cout << wrapper.to_string(wrapper.item) << endl;
}

int main()
{
print(wrap(1234));
}

Aucun commentaire:

Enregistrer un commentaire