{"id":94015,"date":"2016-08-03T07:00:00","date_gmt":"2016-08-03T21:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/?p=94015"},"modified":"2019-03-13T11:05:00","modified_gmt":"2019-03-13T18:05:00","slug":"20160803-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20160803-00\/?p=94015","title":{"rendered":"Using #pragma detect_mismatch to help catch ODR violations"},"content":{"rendered":"<p>One of the more insidious problems you may encounter are those traced back to violations of the C++ <a HREF=\"https:\/\/en.wikipedia.org\/wiki\/One_Definition_Rule\">One Definition Rule<\/a>. As a general rule, if the conflicting definitions occur in separate translation units, the behavior is undefined, but no diagnostic is required. The lack of a diagnostic means that if two translation units define a type differently (say, because they were compiled with different compile-time configurations), you may not notice a problem until you start dealing with mysterious memory corruption. <\/p>\n<p>These types of bugs are not fun to diagnose. <\/p>\n<p>If you use the Microsoft Visual C++ toolchain, then you can use the <code>#pragma detect_mismatch(\"name\", \"value\")<\/code> directive to give the linker some help in identifying mismatched definitions. Specifically, the linker verifies that all such declarations with the same name also have the same value. <\/p>\n<p>The idea here is that if you have something that is declared differently based on compilation settings, you can emit a different <code>#pragma detect_mismatch(\"name\", \"value\")<\/code> for each version, using the same name but a different value. The linker will then verify that everybody used the same version of the header file. <\/p>\n<p>Here&#8217;s an example: <\/p>\n<pre>\n\/\/ This is a fake mutex that does no locking.\nstruct fake_mutex\n{\n void lock() {}\n void unlock() {}\n};\n\nclass Contoso\n{\n#ifdef SINGLE_THREADED\n   \/\/ single-threaded doesn't need a mutex.\n   typedef fake_mutex mutex_t;\n#else\n   \/\/ multi-threaded needs a true mutex.\n   typedef std::mutex mutex_t;\n#endif\n\npublic:\n  Contoso();\n\n  void Activate()\n  {\n     std::lock_guard&lt;mutex_t&gt; lock(object_mutex);\n#ifndef NDEBUG\n    isActivated = true;\n#endif\n    ... business logic to activate the object ...\n  }\n\n  void Charge()\n  {\n     std::lock_guard&lt;mutex_t&gt; lock(object_mutex);\n    \/\/ You must activate before you can charge.\n    assert(!isActivated);\n    ... business logic to charge the object ...\n  }\n\nprivate:\n  ...\n  mutex_t object_mutex;\n#ifndef NDEBUG\n  bool isActivated = false;\n#endif\n};\n<\/pre>\n<p>If this class is used in a project, but one file in the project is compiled with <code>SINGLE_THREADED<\/code> and another file is compiled without <code>SINGLE_THREADED<\/code>, or if the two files disagree on <code>NDEBUG<\/code>, then you have an ODR violation. In practice, this means that bad things will happen if the two files try to access the same <code>Contoso<\/code> object. <\/p>\n<p>You can use <code>#pragma detect_mismatch<\/code> to encode which definition is being used. This allows the linker to detect whether a single project uses multiple conflicting definitions. <\/p>\n<pre>\n\/\/ This is a fake mutex that does no locking.\nstruct fake_mutex\n{\n void lock() {}\n void unlock() {}\n};\n\nclass Contoso\n{\n#ifdef SINGLE_THREADED\n   \/\/ single-threaded doesn't need a mutex.\n   typedef fake_mutex mutex_t;\n   <font COLOR=\"blue\">#pragma detect_mismatch(\"Contoso threading\", \"Single\");<\/font>\n#else\n   \/\/ multi-threaded needs a true mutex.\n   typedef std::mutex mutex_t;\n   <font COLOR=\"blue\">#pragma detect_mismatch(\"Contoso threading\", \"Multi\");<\/font>\n#endif\n\n<font COLOR=\"blue\">#ifdef NDEBUG\n   #pragma detect_mismatch(\"Contoso debug\", \"Nondebug\");\n#else\n   #pragma detect_mismatch(\"Contoso debug\", \"Debug\");\n#endif<\/font>\n\npublic:\n  Contoso();\n\n  void Activate()\n  {\n     std::lock_guard&lt;mutex_t&gt; lock(object_mutex);\n#ifndef NDEBUG\n    isActivated = true;\n#endif\n    ... business logic to activate the object ...\n  }\n\n  void Charge()\n  {\n     std::lock_guard&lt;mutex_t&gt; lock(object_mutex);\n    \/\/ You must activate before you can charge.\n    assert(!isActivated);\n    ... business logic to charge the object ...\n  }\n\nprivate:\n  ...\n  mutex_t object_mutex;\n#ifndef NDEBUG\n  bool isActivated = false;\n#endif\n};\n<\/pre>\n<p>You can see the directive in action in <a HREF=\"https:\/\/channel9.msdn.com\/Series\/C9-Lectures-Stephan-T-Lavavej-Advanced-STL\/C9-Lectures-Stephan-T-Lavavej-Advanced-STL-3-of-n\">this Channel 9 video<\/a> starring C++ library master Stephan T. Lavavej. The <code>detect_mismatch<\/code> trick appears around timecode 29:30. <\/p>\n<p>Note of course that you can use this technique for things other than catching ODR violations. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>There can be more than one, but they must be the same.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-94015","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>There can be more than one, but they must be the same.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/94015","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=94015"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/94015\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=94015"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=94015"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=94015"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}