Question on returning std::tie from a function. If I understand correctly, then std::tie only contains references. So, returning a std::tie which points to function-local variables is a very bad idea. Shouldn't the compiler be able to detect this and issue a warning ?
Actually, we had this error in our code and all compilers and sanitizers we have missed to detect it. I was quite puzzled that this didn't get reported by any tool. Or do I understand anything incorrectly?
#include <tuple>
struct s_t {
int a;
};
int& foo(s_t s) {
return s.a; // warning: reference to local variable 's' returned
}
int& bar(s_t &s) {
return s.a; // ok
}
auto bad(s_t s) {
return std::tie(s.a); // no warning
}
auto fine(s_t &s) {
return std::tie(s.a); // no warning
}
int main() {
s_t s1,s2;
auto bad_references = bad(s1);
auto good_references = fine(s2);
// ...
return 0;
}
Your understanding of the lifetime behavior is correct. std::tie stores only references and you must assure that they are not used after the referenced object is destroyed. By-value function parameters are destroyed either at the end of the function call or at the end of the full-expression containing the function call (implementation-defined). So using the references stored in bad_references will cause undefined behavior.
You might just be expecting too much from the compiler warning features and linter features. They generally don't do extensive analysis of the code. The analysis here would need to keep track of the reference through multiple function call layers, a store to a member and the return from the function. Such more complex analysis is what a static analyzer is for.
However, starting with version 12.1 GCC seems to use the result of inlining function calls to report with -O2 -Wall -Wextra:
In file included from <source>:1:
In constructor 'constexpr std::_Head_base<_Idx, _Head, false>::_Head_base(const _Head&) [with long unsigned int _Idx = 0; _Head = int&]',
inlined from 'constexpr std::_Tuple_impl<_Idx, _Head>::_Tuple_impl(const _Head&) [with long unsigned int _Idx = 0; _Head = int&]' at /opt/compiler-explorer/gcc-12.1.0/include/c++/12.1.0/tuple:435:21,
inlined from 'constexpr std::tuple< <template-parameter-1-1> >::tuple(const _Elements& ...) [with bool _NotEmpty = true; typename std::enable_if<_TCC<_Dummy>::__is_implicitly_constructible<const _Elements& ...>(), bool>::type <anonymous> = true; _Elements = {int&}]' at /opt/compiler-explorer/gcc-12.1.0/include/c++/12.1.0/tuple:729:28,
inlined from 'constexpr std::tuple<_Elements& ...> std::tie(_Elements& ...) [with _Elements = {int}]' at /opt/compiler-explorer/gcc-12.1.0/include/c++/12.1.0/tuple:1745:44,
inlined from 'auto bad(s_t)' at <source>:16:24:
/opt/compiler-explorer/gcc-12.1.0/include/c++/12.1.0/tuple:193:9: warning: storing the address of local variable 's' in '*(std::_Head_base<0, int&, false>*)<return-value>.std::_Head_base<0, int&, false>::_M_head_impl' [-Wdangling-pointer=]
193 | : _M_head_impl(__h) { }
| ^~~~~~~~~~~~~~~~~
<source>: In function 'auto bad(s_t)':
<source>:15:14: note: 's' declared here
15 | auto bad(s_t s) {
| ~~~~^
<source>:15:14: note: '<unknown>' declared here
I didn't manage to get current Clang and MSVC to produce a diagnostic, though. I guess this will work for GCC also only as long as all the relevant function calls are inlined. With e.g. -O0 the GCC warning is not produced and it probably won't be produced either if there are more complex function call layers inbetween.
A static analyzer like clang-analyzer reports
<source>:16:5: warning: Address of stack memory associated with local variable 's' is still referred to by the stack variable 'bad_references' upon returning to the caller. This will be a dangling reference [clang-analyzer-core.StackAddressEscape]
return std::tie(s.a); // no warning
^
<source>:27:27: note: Calling 'bad'
auto bad_references = bad(s1);
See https://godbolt.org/z/zE8Px8vT9 for both.
With Clang trunk and optimizations disabled ASAN also reports the problem:
=================================================================
==1==ERROR: AddressSanitizer: stack-use-after-return on address 0x7fddb5e00020 at pc 0x55678be240c4 bp 0x7ffe4ed87ef0 sp 0x7ffe4ed87ee8
READ of size 4 at 0x7fddb5e00020 thread T0
#0 0x55678be240c3 in main /app/example.cpp:31:12
#1 0x7fddb849e0b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x240b2) (BuildId: 9fdb74e7b217d06c93172a8243f8547f947ee6d1)
#2 0x55678bd6231d in _start (/app/output.s+0x2131d)
Address 0x7fddb5e00020 is located in stack of thread T0 at offset 32 in frame
#0 0x55678be23d2f in bad(s_t) /app/example.cpp:15
This frame has 1 object(s):
[32, 36) 's' <== Memory access at offset 32 is inside this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
(longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-use-after-return /app/example.cpp:31:12 in main
[...]
See https://godbolt.org/z/eYo7cWeaM.
Probably the inlining makes it harder for the sanitizers to detect this. They probably don't add the checks prior to inlining.
What you should know to self-answer this question is RAII (resource acquisition is initialization- Some one say it is the most importance thing needed to know in C++)
In your example:
foo(s_t s); will initialize s via copy constructor. When foo exit, s will be destroyed so that s.a referencing to a dangling object
bar(s_t &s) is ok because &s is reference to existed variable (that usually) outlive the function call
bad(s_t s) fine(s_t &s) is ok because it is return value not reference/pointer to local variable
Related
I was trying to implement a certain type trait, but faced this following error.
Here is a simple program that shows the behavior:
#include <iostream>
template<typename T>
struct my_floating_point // just to make a case
{
// static constexpr bool value = std::is_floating_point_v<T>;
// THIS WORKS
static constexpr bool value = []()
{
if constexpr(std::is_floating_point_v<T>) { return true; }
else { return false; }
}; // compilation error
};
int main()
{
std::cout << my_floating_point<int>::value << std::endl;
std::cout << my_floating_point<double>::value << std::endl;
std::cout << my_floating_point<char>::value << std::endl;
}
The commented line works just fine.
However, the uncommented piece of code gives this warning with g++:
g++ -std=c++17 -O3 main.cpp -Wall -Wextra -pedantic
main.cpp: In instantiation of ‘constexpr const bool my_floating_point<int>::value’:
main.cpp:18:39: required from here
main.cpp:9:24: warning: the address of ‘static constexpr bool my_floating_point<int>::<lambda()>::_FUN()’ will never be NULL [-Waddress]
static constexpr bool value = []()
^~~~~
main.cpp:9:24: warning: the address of ‘static constexpr bool my_floating_point<int>::<lambda()>::_FUN()’ will never be NULL [-Waddress]
And running it outputs
1
1
1
What is wrong?
Note: clang++ for some reason doesn't give warning, but outputs the same.
[]()
{
// ...
}
A lambda expression evaluates to an instance of an anonymous type. That's what a lambda really is: an object whose type is unspecified. Attempting to assign an instance of this object to a bool is not valid C++.
The good news is that this anonymous type has an operator() overload, that just happens, accidentally, to return a bool. So, for example, this will work:
static constexpr bool value = []()
{
if constexpr(std::is_floating_point_v<T>) { return true; }
else { return false; }
}(); // compiles just fine.
Summary: Simplify to get a more readable warning message, then compare the message to a possibly better-recognized case. This is more of a work-through answer (teach to fish) than a direct explanation (give a fish).
This is one of the cases where really working at a minimal reproducible example can have benefits. I acknowledge that some of the complication was kept "just to make a case", but let's drop that for now.
The three lines in main each trigger the same warning, so simplify to just one of those lines. Preferably, we keep a line whose output is unexpected, such as the first of the three lines (int is not a floating point type, so my_floating_point<int>::value is intended to be false). That cuts the compiler's messages to a third of what they were and helps identify which messages go together (at this point, they all go together).
With only one type to consider, we no longer need to my_floating_point to be a template. Templates sometimes add a significant amount of insignificant detail to warnings (such as – in this case – the required from here message), so drop the template and replace std::is_floating_point_v<T> with std::is_floating_point_v<int>. This reduces the warning down to one message plus the indication of where the warning occurred.
One more simplification: take value outside the struct, allowing my_floating_point to be eliminated. Admittedly, this does not have much impact on the error message, but it does remove a red herring from the example code.
#include <iostream>
static constexpr bool value = []()
{
if constexpr(std::is_floating_point_v<int>) { return true; }
else { return false; }
}; // compilation warning
int main()
{
std::cout << value << std::endl;
}
prog.cc:7:1: warning: the address of 'static constexpr bool<lambda()>::_FUN()' will never be NULL [-Waddress]
7 | }; // compilation warning
| ^
Note that this is almost one of the lines from the question's compiler output. The only difference is the removal of my_floating_point<int>:: which corresponds nicely with the simplifications we did.
OK, now that we have the situation suitably simplified, let's compare the warning to one produced in a slightly different setup. Instead of a lambda, let's use an official function. The goal is to gain some basic (yet imprecise) understanding by by comparing similar scenarios.
Don't get hung up on "how would I think to do this?" This is something I knew to do because I recalled a remarkably similar warning from a different setup. The first part of this answer presented steps that I think are reasonable to expect from others. In contrast, this part of this answer requires familiarity with messages from the compiler.
#include <iostream>
bool fun()
{
if constexpr(std::is_floating_point_v<int>) { return true; }
else { return false; }
}
static constexpr bool value = fun; // compilation warning
int main()
{
std::cout << value << std::endl;
}
prog.cc:9:31: warning: the address of 'bool fun()' will never be NULL [-Waddress]
9 | static constexpr bool value = fun; // compilation warning
| ^~~
Again, the same warning that the address of [something] will never be NULL. (Perhaps you already see where this is going?) In this code, since the function is not invoked, the symbol fun becomes a pointer to the function. Assigning a pointer to a bool results in true if and only if the pointer is non-null. This particular pointer cannot be null because it is the address of something. So the compiler throws out a warning; the code is syntactically valid, but you probably meant to invoke the function (by adding parentheses just before the semicolon, as in fun() instead of just fun).
Similar mechanics are in play for the code with a lambda. When the lambda is not invoked, it can convert to a bool, becoming true if and only if there is a lambda. As in the function case, the result has to be true. The compiler throws out a warning; the code is syntactically valid, but you probably meant to invoke the lambda (by adding parentheses just before the semicolon).
static constexpr bool value = []()
{
if constexpr(std::is_floating_point_v<int>) { return true; }
else { return false; }
}(); // no compilation warning with the parentheses
Even better, the output is now 0, as intended.
OK, the short version is "add parentheses", but the real lesson of this answer is the usefulness of recognizing compiler messages. When a warning is worded similarly to one you've encountered before, try to replicate the warning you are familiar with. See if there are similarities that allow you to transfer a solution from the familiar case to the less familiar one.
Familiarity takes time, but it takes less time if you make it a goal. ;)
My code is:
#include <memory>
#include <vector>
struct Core{
Core(int n){} };
int main() {
int base;
std::vector<std::unique_ptr<Core>> cores;
cores.push_back( std::move(std::unique_ptr<Core>(new Core(base))) );
cores[0].swap(std::unique_ptr<Core>(new Core(base)));
return 0;
}
And I get this error:
||=== Build: Release in Test (compiler: GNU GCC Compiler) ===|
In function 'int main()' error: no matching function for call to 'std::unique_ptr<Core>::swap(std::unique_ptr<Core>)'
note: candidate is:
note: void std::unique_ptr<_Tp,_Dp>::swap(std::unique_ptr<_Tp,_Dp>&) [with_Tp = Core; _Dp = std::default_delete<Core>]
note: no known conversion for argument 1 from 'std::unique_ptr<Core>' to 'std::unique_ptr<Core>&'
You can not swap with a temporary object (an rvalue) like this:
cores[0].swap(std::unique_ptr<Core>(new Core(base))); // error
But a temporary can swap with a named object (lvalue) like this
std::unique_ptr<Core>(new Core(base)).swap(cores[0]); // but this works
Alternatively you can just set the value directly:
cores[0] = std::unique_ptr<Core>(new Core(base));
Or (better) using std::make_unique
cores[0] = std::make_unique<Core>(base);
You pass the parameters for the object's constructor directly to the std::make_unique function.
And if you must use new there is always the std::unique_ptr::reset function:
cores[0].reset(new Core(base));
You are using std::move too much. In your cases, the move happens automatically. So, you can just write
cores.push_back( std::unique_ptr<Core>(new Core(base)) );
cores[0] = std::unique_ptr<Core>(new Core(base));
Or, using std::make_unique and std::unique_ptr::reset,
cores.push_back( std::make_unique<Core>(base) );
cores[0].reset(new Core(base));
If, however, you had your unique_ptr, say, as a local variable, you'd have to use std::move:
std::unique_ptr<Core> p(new Core(base));
cores.push_back(std::move(p));
This is as efficient as the above, but creates a potentially needless local variable.
You need an lvalue std::unique_ptr<Core> to swap with, an unnamed temporary isn't allowed.
What you can do is
cores.push_back(std::unique_ptr<Core>(new Core(base))); // don't need move here
{
std::unique_ptr<Core> other(new Core(base));
cores[0].swap(other);
}
I've written a convenient functor wrapper for tuple std::get. When using it with boost transformed and operator[], I get warning that I'm returning reference to local temporary object. My system: ubuntu 14.04, compilers: clang-3.5 and g++-4.8.2, boost version: 1.56.
#include <boost/range/adaptor/transformed.hpp>
#include <utility>
#include <vector>
template <std::size_t I>
struct tuple_get {
template <typename Tuple>
auto operator()(Tuple &&tuple) const ->
decltype(std::get<I>(std::forward<Tuple>(tuple))) {
return std::get<I>(std::forward<Tuple>(tuple));
}
};
int main() {
//removing const gets rid of warning
const std::vector<std::tuple<int,int>> v = {std::make_tuple(0, 0)};
//gives warning
(v | boost::adaptors::transformed(tuple_get<0>{})) [0];
}
Warning details:
include/boost/range/iterator_range_core.hpp:390:16: warning: returning reference to local temporary object [-Wreturn-stack-address]
return this->m_Begin[at];
note: in instantiation of member function 'boost::iterator_range_detail::iterator_range_base<boost::transform_iterator<tuple_get<0>,
std::__1::__wrap_iter<const std::__1::tuple<int, int> *>, boost::use_default, boost::use_default>, boost::random_access_traversal_tag>::operator[]' requested here
(v | boost::adaptors::transformed(tuple_get<0>{})) [0];
Adding flag -Wreturn-stack-address is not a solution since it's dangerous in bigger projects.
I noticed that deleting const keyword gets rid of warning but I don't know why and don't want to assume that functor gets only non-const ranges.
Questions: how to fix code to get rid of warning? Why deleting const gets rid of warning?
It's true.
//
// When storing transform iterators, operator[]()
// fails because it returns by reference. Therefore
// operator()() is provided for these cases.
//
So, you should be able to fix it with
(v | boost::adaptors::transformed(tuple_get<0>{})) (0);
which returns the abstract_value_type (which is the reference only if the elements are abstract, array or function, the value_type otherwise).
I am trying to use a boost::function in my ReceiveRequest to run on its own thread but I must be sending the wrong parameters. (At least thats what I think the compiler is trying to tell me)
Here are the lines that is causing the issue:
//some variables for the function call
std::string something("");
asio::system_error e();
asio::thread daThread();
CurrentRequest.payload;
//attempts to call function
CurrentRequest.Callback(something, &e, CurrentRequest.payload); //line 184
CurrentRequest.Callback(something, &e, &CurrentRequest.payload); //line 185
Here is what the compiler is telling me:
g++ -o ss -pthread -lrt StringSocket.cpp main.cpp -I ~/asio/include -I ~/boost/include ~/boost/lib/*.a
StringSocket.cpp: In member function ‘void StringSocket::ProcessReceive()’:
StringSocket.cpp:184: error: no match for call to ‘(ReceiveRequest::receiveCallback) (std::string&, asio::system_error (*)(), void*&)’
/home/jsander/boost/include/boost/function/function_template.hpp:761: note: candidates are: R boost::function3<R, T1, T2, T3>::operator()(T0, T1, T2) const [with R = void, T0 = std::string*, T1 = asio::system_error&, T2 = void*]
StringSocket.cpp:185: error: no match for call to ‘(ReceiveRequest::receiveCallback) (std::string&, asio::system_error (*)(), void**)’
/home/jsander/boost/include/boost/function/function_template.hpp:761: note: candidates are: R boost::function3<R, T1, T2, T3>::operator()(T0, T1, T2) const [with R = void, T0 = std::string*, T1 = asio::system_error&, T2 = void*]
Here is the ReceiveRequest class:
class ReceiveRequest
{
typedef boost::function<void (std::string *message, asio::system_error& e, void *payload) > receiveCallback;
public:
receiveCallback Callback;
void* payload;
ReceiveRequest(receiveCallback _Callback, void* _payload)
{
Callback = _Callback;
payload = _payload;
}
~ReceiveRequest() { }
};
These errors seem to be making a distinction between pointers and references to variables. I thought they could be used interchangeable as parameters. boost::function also appears to turn all of my local variables into references.
I am also confused that one of my parameters passes as "e" turns into "asio::system_error (*)()". Why is there a second pair of parenthesis added to my variable?
There are multiple issues here:
asio::system_error e();
This isn't doing what you want. Because of the way C++ syntax works, this is actually declaring a function e that takes no parameters and returns an asio::system_error. If you add a void in the parenthesis, this becomes easier to see. It should be declared as:
asio::system_error e;
Secondly, your typedef says your function should take a reference to a system_error: asio::system_error& e. However, when you pass the above in (assuming you fix the first problem), you're trying to pass a pointer:
CurrentRequest.Callback(..., &e, ....); // Should just be 'e'
I'm trying to make a type which can type-safely encapsulate arbitrary types. I got the idea in my head that this might be possible from this answer: 5 years later, is there something better than the "Fastest Possible C++ Delegates"? So far I have only succeeded in moving the problem, but I ran into an error that I can't find the root of.
The compiler seems to be telling me that it can't cast the value to the value's own type, which strikes me as bizarre.
I am running Mac OS X 10.6 with llvm-gcc 4.2 (gcc 4.2.1 front-end).
Suggestions of how to get rid of the void* or move it to a less consequential position are welcome, but this question isn't really about that.
The error:
$ g++ main.cpp
main.cpp: In static member function ‘static Stamp StampFactory<T>::make(T*) [with T = int]’:
main.cpp:33: instantiated from ‘Stamp makeStamp(T*) [with T = int]’
main.cpp:39: instantiated from here
main.cpp:26: error: could not convert template argument ‘t’ to ‘int*’
The code:
typedef void (*VoidFunc)(void*);
struct Stamp
{
Stamp(VoidFunc p)
{
this->press = p;
}
VoidFunc press;
};
template<typename T>
struct StampFactory
{
template<T* rvalue>
struct Pattern
{
void operator()(void* lvalue)
{
*dynamic_cast<T*>(lvalue) = *rvalue;
}
};
static Stamp make(T* t)
{
return Stamp(Pattern<t>()); // 28
}
};
template<typename T>
Stamp makeStamp(T* t)
{
return StampFactory<T>::make(t); // 33
}
int main(int argc, char** argv)
{
int i = 0;
Stamp s = makeStamp(&i); //39
}
The error is due to the fact that template arguments must be compile-time constants (or constexpr), and thus, cannot be a variable (or function parameter). It is allowed to have a pointer as a template argument, but there isn't much that you can feed to it because it needs to be a compile-time constant pointer value (and the only thing I can think of that qualifies is a char-pointer to a string literal). The general rule is simple: All template arguments must be known at compile-time, whether it is a type or a value. This excludes function parameters or other kinds of run-time variables.
I wish I could suggest an alternative to achieve what you desire, but I can't understand what you are actually trying to do at all.