Can an inline variable be changed after initialization in C++17? - c++

My scenario is the following (it worked in clang but not in gcc)
liba.hpp:
inline int MY_GLOBAL = 0;
libother.cpp: (dll)
#include "myliba.hpp"
void myFunc() {
//
MYGLOBAL = 28;
}
someexe.cpp:
RunAppThatUsesBothLibAandLibOther();
The problem is that the inline variable was showing 0 in places where I expected 28 because it was alrady modified at run-time. MSVC disagrees with this, but clang does the thing I would expect.
The question is: can inline variables be modified at run-time in my scenario? (I solved the problem by de-inlining the variable.)

Yes, inline variables can be modified after initialization.
However, DLLs are strange things on Windows with MSVC. To a close approximation, each DLL is modelled as its own C++ program, with an entirely independent runtime. Therefore, there is one copy of your inline variable for the main program, and another for the DLL.

Related

For a function that takes a const struct, does the compiler not optimize the function body?

I have the following piece of code:
#include <stdio.h>
typedef struct {
bool some_var;
} model_t;
const model_t model = {
true
};
void bla(const model_t *m) {
if (m->some_var) {
printf("Some var is true!\n");
}
else {
printf("Some var is false!\n");
}
}
int main() {
bla(&model);
}
I'd imagine that the compiler has all the information required to eliminate the else clause in the bla() function. The only code path that calls the function comes from main, and it takes in const model_t, so it should be able to figure out that that code path is not being used. However:
With GCC 12.2 we see that the second part is linked in.
If I inline the function this goes away though:
What am I missing here? And is there some way I can make the compiler do some smarter work? This happens in both C and C++ with -O3 and -Os.
The compiler does eliminate the else path in the inlined function in main. You're confusing the global function that is not called anyway and will be discarded by the linker eventually.
If you use the -fwhole-program flag to let the compiler know that no other file is going to be linked, that unused segment is discarded:
[See online]
Additionally, you use static or inline keywords to achieve something similar.
The compiler cannot optimize the else path away as the object file might be linked against any other code. This would be different if the function would be static or you use whole program optimization.
The only code path that calls the function comes from main
GCC can't know that unless you tell it so with -fwhole-program or maybe -flto (link-time optimization). Otherwise it has to assume that some static constructor in another compilation unit could call it. (Including possibly in a shared library, but another .cpp that you link with could do it.) e.g.
// another .cpp
typedef struct { bool some_var; } model_t;
void bla(const model_t *m); // declare the things from the other .cpp
int foo() {
model_t model = {false};
bla(&model);
return 1;
}
int some_global = foo(); // C++ only: non-constant static initializer.
Example on Godbolt with these lines in the same compilation unit as main, showing that it outputs both Some var is false! and then Some var is true!, without having changed the code for main.
ISO C doesn't have easy ways to get init code executed, but GNU C (and GCC specifically) have ways to get code run at startup, not called by main. This works even for shared libraries.
With -fwhole-program, the appropriate optimization would be simply not emitting a definition for it at all, as it's already inlined into the call-site in main. Like with inline (In C++, a promise that any other caller in another compilation unit can see its own definition of the function) or static (private to this compilation unit).
Inside main, it has optimized away the branch after constant propagation. If you ran the program, no branch would actually execute; nothing calls the stand-alone definition of the function.
The stand-alone definition of the function doesn't know that the only possible value for m is &model. If you put that inside the function, then it could optimize like you're expecting.
Only -fPIC would force the compiler to consider the possibility of symbol-interposition so the definition of const model_t model isn't the one that is in effect after (dynamic) linking. But you're compiling code for an executable not a library. (You can disable symbol-interposition for a global variable by giving it "hidden" visibility, __attribute__((visibility("hidden"))), or use -fvisibility=hidden to make that the default).

constexpr std::max on MSVC fails if passed initialization list is maped as a symbol (constexpr)

Background
Big C++17 project which is build for multiple platforms. Have to support MSVC, clang and gcc
Problem
Following code fails on MSCV and it is fine for other compilers.
#include <algorithm>
#include <initializer_list>
#define FOO_1 1
#define FOO_2 2
#define FOO_3 3
class Baz {
static constexpr auto data = {FOO_1, FOO_2, FOO_3};
static constexpr auto copy_max = std::max({FOO_1, FOO_2, FOO_3});
static constexpr auto data_max = std::max(data);
void bar(std::initializer_list<int>);
void test();
};
void Baz::test()
{ bar(data); }
https://godbolt.org/z/7nv3z3rMY
FOO_x macros comes for external library. Baz is my code.
Now I need data_max for some logic and I whish to calculate that at compile time.
As a workaround I've introduce copy_max which is initialized by duplicate code which is bad (there are lots of values and list can be changed), also data are used in runtime code.
Apparently problem is present only for msvc 19.29 (installed on my machine) older version do not have this issue.
Question
Is there a way to somehow overcome this problem without using extra macros or duplicating code? I've try to use std::max_element, which is constexpr since C++17, with same outcome.
Is this a know bug or should I file new one?

different behavior when linking with static library vs using object files in C++

I'm working with some legacy C++ code that is behaving in a way I don't understand. I'm using the Microsoft compiler but I've tried it with g++ (on Linux) as well—same behavior.
I have 4 files listed below. In essence, it's a registry that's keeping track of a list of members. If I compile all files and link the object files into one program, it shows the correct behavior: registry.memberRegistered is true:
>cl shell.cpp registry.cpp member.cpp
>shell.exe
1
So somehow the code in member.cpp gets executed (which I don't really understand, but OK).
However, what I want is to build a static library from registry.cpp and member.cpp, and link that against the executable built from shell.cpp. But when I do this, the code in member.cpp does not get executed and registry.memberRegistered is false:
>cl registry.cpp member.cpp /c
>lib registry.obj member.obj -OUT:registry.lib
>cl shell.cpp registry.lib
>shell.exe
0
My questions: how come it works the first way and not the second and is there a way (e.g. compiler/linker options) to make it work with the second way?
registry.h:
class Registry {
public:
static Registry& get_registry();
bool memberRegistered;
private:
Registry() {
memberRegistered = false;
}
};
registry.cpp:
#include "registry.h"
Registry& Registry::get_registry() {
static Registry registry;
return registry;
}
member.cpp:
#include "registry.h"
int dummy() {
Registry::get_registry().memberRegistered = true;
return 0;
}
int x = dummy();
shell.cpp:
#include <iostream>
#include "registry.h"
class shell {
public:
shell() {};
void init() {
std::cout << Registry::get_registry().memberRegistered;
};
};
void main() {
shell *cf = new shell;
cf->init();
}
You have been hit by what is popularly known as static initialization order fiasco.
The basics is that the order of initialization of static objects across translation units is unspecified. See this
The call here Registry::get_registry().memberRegistered; in "shell.cpp" may happen before the call here int x = dummy(); in "member.cpp"
EDIT:
Well, x isn't ODR-used. Therefore, the compiler is permitted not to evaluate int x = dummy(); before or after entering main(), or even at all.
Just a quote about it from CppReference (emphasis mine)
It is implementation-defined whether dynamic initialization
happens-before the first statement of the main function (for statics)
or the initial function of the thread (for thread-locals), or deferred
to happen after.
If the initialization is deferred to happen after the first statement
of main/thread function, it happens before the first odr-use of any
variable with static/thread storage duration defined in the same
translation unit as the variable to be initialized. If no variable or function is odr-used from a given translation unit, the non-local variables defined in that translation unit may never be initialized (this models the behavior of an on-demand dynamic library)...
The only way to get your program working as you want is to make sure x is ODR-used
shell.cpp
#include <iostream>
#include "registry.h"
class shell {
public:
shell() {};
void init() {
std::cout << Registry::get_registry().memberRegistered;
};
};
extern int x; //or extern int dummy();
int main() {
shell *cf = new shell;
cf->init();
int k = x; //or dummy();
}
^ Now, your program should work as expected. :-)
This is a result of the way linkers treat libraries: they pick and choose the objects that define symbols left undefined by other objects processed so far. This helps keep executable sizes smaller, but when a static initialization has side effects, it leads to the fishy behavior you've discovered: member.obj / member.o doesn't get linked in to the program at all, although its very existence would do something.
Using g++, you can use:
g++ shell.cpp -Wl,-whole-archive registry.a -Wl,-no-whole-archive -o shell
to force the linker to put all of your library in the program. There may be a similar option for MSVC.
Thanks a lot for all the replies. Very helpful.
So both the solution proposed WhiZTiM (making x ODR-used) and aschepler (forcing linker to include the whole library) work for me. The latter has my preference since it doesn't require any changes to the code. However, there seems to be no MSVC equivalent for --whole-archive.
In Visual Studio I managed to solve the problem as follows (I have a project for the registry static library, and one for the shell executable):
In the shell project add a reference to the registry project;
In the linker properties of the shell project under General set
"Link Library Dependencies" and "Use Library Dependent Inputs" to
"Yes".
If these options are set registry.memberRegistered is properly initialized. However, after studying the compiler/linker commands I concluded that setting these options results in VS simply passing the registry.obj and member.obj files to the linker, i.e.:
>cl /c member.cpp registry.cpp shell.cpp
>lib /OUT:registry.lib member.obj registry.obj
>link /OUT:shell.exe "registry.lib" shell.obj member.obj registry.obj
>shell.exe
1
To my mind, this is essentially the first approach to my original question. If you leave out registry.lib in the linker command it works fine as well.
Anyway, for now, it's good enough for me.
I'm working with CMake so now I need to figure out how to adjust CMake settings to make sure that the object files get passed to the linker? Any thoughts?

How to check if function is declared in global scope at compile time

Let I have a header, for example #include <GL/gl.h>. It contains subset of OpenGL API functions. I need something like this:
static_assert(has_glDrawArraysIndirect::value, "There is no glDrawArraysIndirect");
Or even better:
PFNGLDRAWARRAYSINSTANCEDPROC ptr_glDrawArraysIndirect = ptr_to_glDrawArraysIndirect::ptr;
Where ptr_to_glDrawArraysIndirect::ptr unrolls to pointer to glDrawArraysIndirect if it's defined or to a stub function stub_glDrawArraysIndirect otherwise.
My target operating system is very specific. Any linker based solution (like GetProcAddress or dlsym) doesn't work for me, since there is no dynamic linker. More than, my driver doesn't provide glXGetProcAdrress nor wglGetProcAddress, basically there there is no way to query pointer at run time by function name (Actually, I want to implement such a mechanism).
Any ideas?
Here is an answer that can detect it at compile time and produce a boolean value. It works by creating a template function of the same name in a namespace and then using that namespace inside of the is_defined() function. If the real glDrawArraysIndirect() exists it will take preference over the template version. If you comment out the first declaration of glDrawArraysIndirect() the static assert at the bottom will trigger.
Test on GodBolt
#include <type_traits>
enum GLenum {};
void glDrawArraysIndirect(GLenum, const void*);
namespace detail {
struct dummy;
template<typename T>
dummy& glDrawArraysIndirect(T, const void*);
}
constexpr bool is_defined()
{
using namespace detail;
using ftype = decltype(glDrawArraysIndirect(GLenum(), nullptr));
return std::is_same<ftype, void>();
}
static_assert(is_defined(), "not defined");
With a little tweak you can make your custom function the template and use a similar trick
ideone.com
#include <type_traits>
#include <iostream>
//#define USE_REAL
enum GLenum {TEST};
typedef void (*func_type)(GLenum, const void*);
#ifdef USE_REAL
void glDrawArraysIndirect(GLenum, const void*);
#endif
namespace detail {
struct dummy {};
template<typename T = dummy>
void glDrawArraysIndirect(GLenum, const void*, T = T())
{
std::cout << "In placeholder function" << std::endl;
}
}
void wrapDraw(GLenum x, const void* y)
{
using namespace detail;
glDrawArraysIndirect(x, y);
}
#ifdef USE_REAL
void glDrawArraysIndirect(GLenum, const void*)
{
std::cout << "In real function" << std::endl;
}
#endif
int main()
{
wrapDraw(TEST, nullptr);
}
Include the expression sizeof(::function) somewhere. (If the function exists then asking for the size of the pointer to the function is a perfectly valid thing to do).
It will be benign at runtime, and :: forces the use of the function declared at global scope.
Of course, if function does not exist at global scope, then compilation will fail.
Along with other errors, the compiler will issue a specific error if you were to write something on the lines of
static_assert(sizeof(::function), "There is no global function");
My target operating system is very specific. Any linker based solution (like GetProcAddress or dlsym) doesn't work for me, since there is no dynamic linker.
Is this an embedded system or just a weirdly stripped down OS running on standard PC hardware?
More than, my driver doesn't provide glXGetProcAdrress nor wglGetProcAddress, basically there there is no way to query pointer at run time by function name
The abiliy to query function pointers at runtime does not depend on the presence of a dynamic linker. Those two are completely orthogonal and even a purely statically linked embedded OpenGL implementation can offer a GetProcAddress interface just fine. Instead of trying to somehow solve the problem at compile or link time, I'd rather address the problem by implementing a GetProcAddress for your OpenGL driver; you can do that even if the driver is available as only a static library in binary form. Step one:
Create function pointer stubs for each and every OpenGL function, statically initialized to NULL and attributed weak linkage. Link this into a static library you may call gl_null_stubs or similar.
Create a GetProcAddress function that for every OpenGL function there is returns the pointer to the function symbol within the scope of the function's compilation unit.
Now link your weird OpenGL driver with the stubs library and the GetProcAddress implementation. For every function there is, the weak linkage of the stub will the static library symbol to take precedence. For all OpenGL symbols not in your driver the stubs will take over.
There: Now you have a OpenGL driver library that has a GetProcAddress implementation. That wasn't that hard, was it?
How to check if function is declared in global scope at compile time?
My target operating system is very specific...
A possible solution might be, if you are using a recent GCC -probably as a cross-compiler for your weird target OS and ABI- to customize the gcc (or g++ etc...) compiler with your own MELT extension.
MELT is a domain specific language, implemented as a free software GCC plugin (mostly on Linux), to customize the GCC compiler.

Is there a way to detect inline function ODR violations?

So I have this code in 2 separate translation units:
// a.cpp
#include <stdio.h>
inline int func() { return 5; }
int proxy();
int main() { printf("%d", func() + proxy()); }
// b.cpp
inline int func() { return 6; }
int proxy() { return func(); }
When compiled normally the result is 10. When compiled with -O3 (inlining on) I get 11.
I have clearly done an ODR violation for func().
It showed up when I started merging sources of different dll's into fewer dll's.
I have tried:
GCC 5.1 -Wodr (which requires -flto)
gold linker with -detect-odr-violations
setting ASAN_OPTIONS=detect_odr_violation=1 before running an instrumented binary with the address sanitizer.
Asan can supposedly catch other ODR violations (global vars with different types or something like that...)
This is a really nasty C++ issue and I am amazed there isn't reliable tooling for detecting it.
Pherhaps I have misused one of the tools I tried? Or is there a different tool for this?
EDIT:
The problem remains unnoticed even when I make the 2 implementations of func() drastically different so they don't get compiled to the same amount of instructions.
This also affects class methods defined inside the class body - they are implicitly inline.
// a.cpp
struct A { int data; A() : data(5){} };
// b.cpp
struct A { int data; A() : data(6){} };
Legacy code with lots of copy/paste + minor modifications after that is a joy.
The tools are imperfect.
I think Gold's check will only notice when the symbols have different types or different sizes, which isn't true here (both functions will compile to the same number of instructions, just using a different immediate value).
I'm not sure why -Wodr doesn't work here, but I think it only works for types, not functions, i.e. it will detect two conflicting definitions of a class type T but not your func().
I don't know anything about ASan's ODR checking.
The simplest way to detect such concerns is to copy all the functions into a single compilation unit (create one temporarily if needed). Any C++ compiler will then be able to detect and report duplicate definitions when compiling that file.