{"id":2123,"date":"2014-01-09T07:00:00","date_gmt":"2014-01-09T15:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2014\/01\/09\/can-you-dllexportdllimport-an-inline-function\/"},"modified":"2014-01-09T07:00:00","modified_gmt":"2014-01-09T15:00:00","slug":"can-you-dllexportdllimport-an-inline-function","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20140109-00\/?p=2123","title":{"rendered":"Can you dllexport\/dllimport an inline function?"},"content":{"rendered":"<p>\nThe MSDN documentation on the subject of\n<a HREF=\"http:\/\/msdn.microsoft.com\/en-us\/library\/xa0d9ste\">\nDefining Inline C++ Functions with dllexport and dllimport<\/a>\nwas written with\n<a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2011\/05\/12\/10163578.aspx\">\ncompiler-colored glasses<\/a>.\nThe statements are perfectly true, but they use terminology\nthat only compiler-writers understand.\n<\/p>\n<p>\nThe short version is that all modules which share an inline function\nare considered to be part of the same program,\nso all of the C++ rules regarding inline functions in programs\nneed to be followed.\n<\/p>\n<p>\nLet&#8217;s look at the paragraphs one at a time\nand translate them into English.\n<\/p>\n<blockquote CLASS=\"q\"><p>\nYou can define as inline a function with the\n<b>dllexport<\/b> attribute.\nIn this case, the function is always instantiated and exported,\nwhether or not any module in the program references the function.\nThe function is presumed to be imported by another program.\n<\/p><\/blockquote>\n<p>\nOkay, first of all, what is <i>instantiation<\/i>?\n<\/p>\n<p>\nIn this context, the term <i>instantiation<\/i> when applied\nto an inline function means\n&#8220;The code is generated (<i>instantiated<\/i>)\nfor the function as if it had not been\nmarked inline.&#8221;\n<\/p>\n<p>\nFor the purpose of discussion, let&#8217;s say that you have\na function written as\n<\/p>\n<pre>\n__declspec(dllexport)\ninline int times3(int i) { return i * 3; }\n<\/pre>\n<p>\nSuppose that you compile this into a DLL,\nand that DLL also calls the inline function.\n<\/p>\n<pre>\nint times9(int i) { return times3(times3(i)); }\n<\/pre>\n<p>\nWhat code gets generated?\n<\/p>\n<p>\nThe <code>times9<\/code> function sees that the\n<code>times3<\/code> function is inline,\nso it inlines the function body and there is no\ntrace of a <code>times3<\/code> function at all.\nThe compiler generates the code as if it had\nbeen written\n<\/p>\n<pre>\nint times9(int i) { return (i * 3) * 3; }\n<\/pre>\n<p>\nThat would normally be the end of it,\nexcept that\nthe <code>times3<\/code> function was marked\n<code>dllexport<\/code>.\nThis means that the compiler also generates\nand exports\na plain old function called <code>times3<\/code>\neven though <i>nobody in the DLL actually calls it as such<\/i>.\nThe code is generated and exported because you told the\ncompiler to export the function, so it needs to generate\na function in order to export it.\n<\/p>\n<p>\nThis is not anything special about the <code>dllexport<\/code>\nkeyword.\nThis is just a side-effect of the rule that\n&#8220;If you generate a pointer to an inline function,\nthe compiler must generate a non-inline version of the\nfunction and use a pointer to that non-inline version.&#8221;\nIn this case, the <code>dllexport<\/code> causes a pointer\nto the function to be placed in the export table.\n<\/p>\n<p>\nOkay, next paragraph:\n<\/p>\n<blockquote CLASS=\"q\"><p>\nYou can also define as inline a function declared\nwith the <b>dllimport<\/b> attribute.\nIn this case,\nthe function can be expanded (subject to \/Ob specifications),\nbut never instantiated.\nIn particular,\nif the address of an inline imported function is taken,\nthe address of the function residing in the DLL is returned.\nThis behavior is the same as taking the address\nof a non-inline imported function.\n<\/p><\/blockquote>\n<p>\nWhat this is trying to say is that if you declare\nan inline function as <b>dllimport<\/b>,\nthe compiler treats it just like a plain old inline\nfunction:\nit inlines the function based on the usual rules for\ninlining.\nBut if the compiler chooses to generate code for the function\nas if it were not inline\n(because the compiler decided to ignore the inline qualifier,\nor because somebody took the address of the inline function),\nit defers to the generated code from the original DLL,\nbecause you said,\n&#8220;Hey, the non-inline version of this\nfunction is also available from that DLL over there,&#8221;\nand the compiler says,\n&#8220;Awesome, you saved me the trouble of having to generate the non-inline\nversion the function.\nI can just use that one!&#8221;\n<\/p>\n<p>\nThe &#8220;I can just use that one!&#8221; is not just an optimization.\nIt is necessary in order to comply with the language standard,\nwhich says [dcl.fct.spec]\nthat\n&#8220;An inline function with external linkage\nshall have the same address in all translation units.&#8221;\nThis is the compiler-speak way of saying that the address of an\ninline function must be the same regardless of who asks.\nYou can&#8217;t have a different copy of the inline function in each DLL,\nbecause that would result in them having different addresses.\n(The &#8220;with external linkage&#8221; means that the rule doesn&#8217;t apply to\nstatic inline functions, which is behavior consistent\nwith static non-inline functions.)\n<\/p>\n<p>\nOkay, let&#8217;s try paragraph three:\n<\/p>\n<blockquote CLASS=\"q\"><p>\nThese rules apply to inline functions whose definitions appear\nwithin a class definition.\nIn addition, static local data and strings in inline functions\nmaintain the same identities between the DLL and client\nas they would in a single program\n(that is, an executable file without a DLL interface).\n<\/p><\/blockquote>\n<p>\nThe first part of the paragraph is just saying that\nan inline function defined as part of a class definition counts\nas an inline function for the purpose of this section.\nNo big deal; we were expecting that.\n<\/p>\n<p>\n<b>Update<\/b>: On the other hand, it is a big deal, because it\nresults in inline functions being exported when you may not realize it.\nConsider:\n<\/p>\n<pre>\nclass __declspec(dllexport) SimpleValue\n{\npublic:\n SimpleValue() : m_value(0) { }\n void setValue(int value);\n int getValue() { return m_value; }\nprivate:\n int m_value;\n};\n<\/pre>\n<p>\nThe <code>Simple&shy;Value<\/code> constructor and the\n<code>Simple&shy;Value::get&shy;Value<\/code> method\nare exported inline functions!\nConsequently, any change to the constructor or to\n<code>getValue<\/code> requires a recompilation of all code\nthat constructs a <code>Simple&shy;Value<\/code> or calls\nthe <code>get&shy;Value<\/code> method.\n<b>End update<\/b>.<\/p>\n<p>\nThe second part says that if the inline function uses a static\nlocal variable or a string literal,\nit is the same static local variable or string literal everywhere.\nThis is required by the standard [dcl.fct.spec] and is what you\nwould naturally expect:\n<\/p>\n<pre>\nint inline count()\n{\n static int c = 0;\n return ++c;\n}\n<\/pre>\n<p>\nYou expect there to be only one counter.\n<\/p>\n<p>\nAnd the final paragraph:\n<\/p>\n<blockquote CLASS=\"q\"><p>\nExercise care when providing imported inline functions.\nFor example, if you update the DLL,\ndon&#8217;t assume that the client will use the changed version of the DLL.\nTo ensure that you are loading the proper version of the DLL,\nrebuild the DLL&#8217;s client as well.\n<\/p><\/blockquote>\n<p>\nThis is just working through the consequences of the language requirement\n[dcl.fct.spec] that an inline function &#8220;shall have exactly the\nsame definition&#8221; everywhere.\nIf you change the definition in the exporting DLL\nand don&#8217;t recompile the importing DLL with the new definition,\nyou have violated a language constraint and the behavior is undefined.\n<\/p>\n<p>\nSo there you have it.\nThe rules of inline exported functions translated into English.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Yes, but it won&#8217;t actually do much.<\/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-2123","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Yes, but it won&#8217;t actually do much.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/2123","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=2123"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/2123\/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=2123"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=2123"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=2123"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}