How to mark a variable as initialized at the language level?
I am using a linked C-library to get values into a variable.
void f_wrapper(int& v){
auto s = f_legacy(&v);
if(s != success) throw std::runtime_error{};
}
...
int value; // clang-tidy complains here: using "variable 'value' is not initialized"
f_wrapper(value);
However, clang-tidy complains here: using "variable 'value' is not initialized".
As far as understand, this is simply because the compiler cannot see through the linked function.
I don't want to do int value = 0
(because I trust that the library will initialize value
and I believe in "partially formed" values.)
Also, I am looking not to add // NOLINT(warning)
just to suppress the warning.
(The pattern repeats often.)
Is there something I could do mark value
as initialized in the language?
I am looking for something like:
void f_wrapper(int& v) {
auto s = f_legacy(&v);
if(s != success) throw std::runtime_error{};
[[mark initialized(v)]];
}
Worst case I can do this inside the wrapper, because at least I don't see the unnecessary initialization the main code, but it is very cumbersome in cases where arrays are initialized. Also I don't want to pay the prize of this initialization or choose a default value.
void f_wrapper(int& v) {
v = int{}; // initialize to suppress warning at high level code
auto s = f_legacy(&v);
if(s != success) throw std::runtime_error{};
}
Solution 1:
It's not what you want to hear, but there's really only one way to initialize a variable "at the language level" in standard C++ -- and that's to initialize it. It really is that straight-forward. You're basically stuck with either this, or NOLINT
ing it, as you've already described in your question. It's not a glamorous solution, and it's not what you're after; but there really aren't any better ways.
I'm sure there may be ways to "trick" the tooling into thinking it's initialized, but any such suggestions would amounts to hacks at best.
That said; assuming the clang-tidy
check causing grief is the cppcoreguidelines-init-variables
rule, it's worth noting that this only triggers for integers, booleans, floats, doubles, and pointers -- and the cost to default-initialize such a value is generally negligible. Unless you've benchmarked this cost and found default-initializing this to be a true bottleneck, there's very little reason to avoid this practice.
In general, initializing the variable is the cleanest / safest way to silence this issue, and is ultimately the best practice anyway. Without initializing, all it takes is a small bug in f_legacy
to cause unexpected UB. If f_legacy
has any bugs such that it returns success but forgets to set a value to v
, then v
will not be initialized and will formally be UB -- and this is all just to save default-initializing a primitive.
Just a suggestion:
Rather than having to initialize someone-else's uninitialized value, as per your "worst-case" f_wrapper
example, could the API possibly be altered to return the initialized value rather than initialize what is passed in? f_wrapper
already throws if (presumably) the legacy function fails, meaning you will always have to have a value. Initializing here is much less egregious than initializing the caller's maybe-uninitialized value, in my opinion:
int f_wrapper() {
auto v = int{};
auto s = f_legacy(&v);
if(s != success) throw std::runtime_error{};
return v;
}