Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are (member) function pointers behaving so weirdly in Visual C++?

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?

like image 694
user541686 Avatar asked Jan 05 '13 20:01

user541686


2 Answers

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.

like image 72
jstine Avatar answered Sep 21 '22 21:09

jstine


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.

like image 32
user541686 Avatar answered Sep 23 '22 21:09

user541686