diff options
author | Egor Tensin <Egor.Tensin@gmail.com> | 2017-06-24 22:54:43 +0300 |
---|---|---|
committer | Egor Tensin <Egor.Tensin@gmail.com> | 2017-06-25 05:05:48 +0300 |
commit | 9b3ad53e855764e46a9febe395b88d3ca1aeaf45 (patch) | |
tree | 9930fcc81ef6fd38cb168732bdfd6bf87a4f0828 /_posts/2017-06-24-static-vs-inline-vs-unnamed-namespaces.md | |
parent | add _drafts/ (diff) | |
download | blog-9b3ad53e855764e46a9febe395b88d3ca1aeaf45.tar.gz blog-9b3ad53e855764e46a9febe395b88d3ca1aeaf45.zip |
add post "static vs. inline vs. namespace {"
Also includes a bunch of includes to easily add grouped code snippets to
a post.
They were present before, but I wiped them away during the recent major
history rewriting which I still regret about.
Diffstat (limited to '')
-rw-r--r-- | _posts/2017-06-24-static-vs-inline-vs-unnamed-namespaces.md | 277 |
1 files changed, 277 insertions, 0 deletions
diff --git a/_posts/2017-06-24-static-vs-inline-vs-unnamed-namespaces.md b/_posts/2017-06-24-static-vs-inline-vs-unnamed-namespaces.md new file mode 100644 index 0000000..e22ab21 --- /dev/null +++ b/_posts/2017-06-24-static-vs-inline-vs-unnamed-namespaces.md @@ -0,0 +1,277 @@ +--- +title: static vs. inline vs. namespace { +layout: post +excerpt: > + Should I use <code>static</code>, <code>inline</code> or unnamed namespaces + for function definitions? +custom_css: + - snippets.css +snippets_root_directory: snippets/static_vs_inline_vs_unnamed_namespaces/ +snippets: + static: + - static/main.cpp + - static/proxy.cpp + - static/proxy.hpp + - static/shared.hpp + inline: + - inline/shared.hpp + inline_weird: + - inline/weird/main.cpp + - inline/weird/another.cpp + - inline/weird/another.hpp + unnamed_namespaces_weird: + - unnamed_namespaces/weird/main.cpp + - unnamed_namespaces/weird/another.cpp + - unnamed_namespaces/weird/another.hpp + unnamed_namespaces_ok: + - unnamed_namespaces/ok/main.cpp + - unnamed_namespaces/ok/another.cpp + - unnamed_namespaces/ok/another.hpp + static_and_inline: + - static_and_inline/main.cpp + - static_and_inline/proxy.cpp + - static_and_inline/proxy.hpp + - static_and_inline/shared.hpp + unnamed_namespace_and_inline: + - unnamed_namespace_and_inline/main.cpp + - unnamed_namespace_and_inline/proxy.cpp + - unnamed_namespace_and_inline/proxy.hpp + - unnamed_namespace_and_inline/shared.hpp + separate_method_definitions: + - separate_method_definitions/main.cpp + - separate_method_definitions/another.cpp + - separate_method_definitions/another.hpp + - separate_method_definitions/shared.hpp +--- +In this post I'll try to figure out whether I should use `static`, `inline` or +unnamed namespaces for function definitions. + +`static` +-------- + +It's an old C-style method of defining functions in header files. +This way, every translation unit gets its own copy of a function. +What does that mean? +The most obvious implication that pops into my head is that every local static +variable defined inside that function gets an independent replica in every +translation unit. +For example, the program below would print + +``` +1 +1 +``` + +due to the fact that both main.cpp and proxy.cpp get their own versions of `n` +from `shared()`. + +{% include snippets/section.html section_id='static' %} + +In C, this is the only way to share function definitions between translation +units (apart from declaring a function in a header file and putting its +definition to a .c file). + +### Properties + +* Using `static`, you can share function definitions between multiple +translation units. +* Each unit gets its own replica of the function: they have different +addresses, their local static variables are independent, etc. +* If different translation units define different functions with the same +name using the `static` specifier, each unit can use its function without any +issues. +This might seem like an trivial claim, but there are issues with other +approaches that are discussed below. + +`inline` +-------- + +It's well-known that this keyword has pretty much nothing to do with whether a +function will actually be inlined or not. +It's used much more often to define functions in header files, since every +function defined this way will be the same (as in "will have the same address") +in every translation unit. +Let's try and adjust the definition of `shared()` accordingly: + +{% include snippets/section.html section_id='inline' %} + +The same program would then print + +``` +1 +2 +``` + +since both `main()` and `proxy()` would call the same `shared()`, incrementing +the same `n`. + +Weird things happen when different translation units define different `inline` +functions with the same name. + +{% include snippets/section.html section_id='inline_weird' %} + +According to my simple experiments, this program produces different output +based on which .cpp file was specified first on the command line during +compilation. +For example, this is the output of test.exe produced with either `cl /W4 /EHsc +main.cpp another.cpp /Fe:test.exe` or `g++ -Wall -Wextra -std=c++11 main.cpp +another.cpp -o test.exe`. + +``` +main.cpp: shared() +main.cpp: shared() +``` + +If we swap the order of .cpp files (`another.cpp main.cpp` instead of `main.cpp +another.cpp`), the output becomes + +``` +another.cpp: shared() +another.cpp: shared() +``` + +No warnings/errors are emitted, making the situation truly disturbing. +I tested this with GNU compiler version 5.4.0 and Microsoft compiler version +19.00.24210. + +This behaviour can be easily fixed by either making these functions `static` +or by using unnamed namespaces (see below). + +### Properties + +* Using `inline`, you can share function definitions between multiple +translation units. +* Each translation unit will use the same function: it will have the same +address in every translation unit, its local static variables will be shared, +etc. +* Defining different `inline` functions with the same name in different +translation units is undefined behaviour. + +Two inline functions might be different even if they are the same textually. +For example, they might reference two global variables which have the same +name, but are defined in different translation units. +{: .alert .alert-info } + +`namespace {` +------------- + +With respect to function definitions, unnamed namespaces are, according to my +understanding, quite similar to the `static` keyword. +The additional value they provide is that they provide a way to apply `static` +not only to functions, but to classes also. +Remember the weirdness that happens when multiple translation units define +different `inline` functions with the same name? +Arguably, it gets even worse if we add classes to the equation. + +{% include snippets/section.html section_id='unnamed_namespaces_weird' %} + +Compiling this program the same way we did in the `inline` example (`cl /W4 +/EHsc main.cpp another.cpp /Fe:test.exe`/`g++ -Wall -Wextra -std=c++11 main.cpp +another.cpp -o test.exe`) yields different outputs depending on which .cpp file +was specified first. + +``` +main.cpp: Test::Test() +1 +main.cpp: Test::Test() +``` + +``` +another.cpp: Test::Test() +1065353216 +another.cpp: Test::Test() +``` + +I'm not sure why anybody would want that. +This can be easily fixed by putting both `Test` classes into unnamed +namespaces. +The program than reads + +{% include snippets/section.html section_id='unnamed_namespaces_ok' %} + +After the adjustment, it produces the same output regardless of compilation +options. + +``` +main.cpp: Test::Test() +1 +another.cpp: Test::Test() +``` + +Notice how sharing classes defined in header files isn't discussed here. +The standard actually guarantees that if a class is defined in a header file, +all translation units that use it share the definition. + +### Properties + +* Essentially, unnamed namespaces allow the `static` keyword to be applied to +classes. +* Similar to the `static` approach, each translation unit gets its own replica +of a function/class, including their own local static variables, etc. +* Defining different classes with the same name in different translation units +(without utilizing unnamed namespaces) is undefined behaviour. + +Conclusion +---------- + +Here's my attempt to build an algorithm to decide whether a class/function +should be defined with either of the `static`/`inline` specifiers or put into +an unnamed namespace. +The first question I answer is: is the entity defined in a header file or in a +.cpp file? + +* **In a header** — Is it a class or a function? + * **Class** — There's no need to do anything. + * **Function** — Do you want it to behave differently for each +translation unit (may be useful, for example, for logging)? + * **Yes** — Use `static`. + * **No** — Use `inline`. +* **In a .cpp file** — Put it into an unnamed namespace. + +Tricky cases +------------ + +### `static` + `inline` + +In case a function is defined as `static inline`, `static` wins, and `inline` +is ignored. +The program below outputs + +``` +1 +1 +``` + +{% include snippets/section.html section_id='static_and_inline' %} + +In general, I can't think of a reason to define a `static inline` function. + +### `namespace {` + `inline` + +If an `inline` function is defined in an unnamed namespace, the unnamed +namespace wins. +The program below outputs + +``` +1 +1 +``` + +{% include snippets/section.html section_id='unnamed_namespace_and_inline' %} + +In general, I can't think of a reason to define an `inline` function in an +unnamed namespace. + +### Separate method definitions + +If you want to separate your class declaration from its method definitions +while keeping them in the same header file, each method must be explicitly +defined `inline`. +The program below outputs + +``` +1 +2 +``` + +{% include snippets/section.html section_id='separate_method_definitions' %} |