I've had a really bizarre problem that I've reduced to the following test case:
#include <iostream> #include <map> #include <string> struct Test { std::map<std::string, void (Test::*)()> m; Test() { this->m["test1"] = &Test::test1; this->m["test2"] = &Test::test2; } void test1() { } void test2() { } void dispatch(std::string s) { if (this->m.at(s) == &Test::test1) { std::cout << "test1 will be called..." << std::endl; } else if (this->m.at(s) == &Test::test2) { std::cout << "test2 will be called..." << std::endl; } (this->*this->m.at(s))(); } }; int main() { Test t; t.dispatch("test1"); t.dispatch("test2"); }
It outputs
test1 will be called...
test1 will be called...
when optimizations are enabled, which is really bizarre. What's going on?
This is a by-product of what Visual C++ refers to as Identical COMDAT Folding (ICF). It merges identical functions into a single instance. You can disable it by adding the following switch to the linker commandline: /OPT:NOICF
(from the Visual Studio UI it is found under Properties->Linker->Optimization->Enable COMDAT Folding)
You can find details at the MSDN article here: /OPT (Optimizations)
The switch is a linker-stage switch, which means you won't be able to enable it just for a specific module or a specific region of code (such as __pragma( optimize() )
which is available for compiler-stage optimization).
In general, however, it is considered poor practice to rely on either function pointers or literal string pointers (const char*
) for testing uniqueness. String folding is widely implemented by almost all C/C++ compilers. Function folding is only available on Visual C++ at this time, though increased widespread use of template<> meta-programming has increased requests for this feature to be added to gcc and clang toolchains.
Edit: Starting with binutils 2.19, the included gold linker supposedly also supports ICF, though I have been unable to verify it on my local Ubuntu 12.10 install.
It turns out Visual C++'s linker can merge functions with identical definitions into one.
Whether that's legal or not according to C++, I have no idea; it affects observable behavior, so it looks like a bug to me. Someone else with more information may want to chime in on that though.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With