I've got following code:
string join(initializer_list<string_view> strings);
initializer_list is std::initializer_list and string_view isn't std::string view but very similar class with constructors from const string& and const char*.
Then I've got following invocation of join:
EXPECT_EQ("this", join({ string("this") }));
After small investigation I've found that the first element of resulting initializer list isn't "this" but "\0his". This is becouse the destructor of the temporary created by string("this") is called right after creation of temporary string_view (so it contains invalid pointers). Why is it so that the lifetime of string("this") isn't extended to the end of the full-expression EXPECT_EQ("this", join({ string("this") }));?
Edit
Ok, as you suggested there is self-contained example:
#include <iostream>
#include <string>
using namespace std;
class string_view {
public:
string_view(const string& str)
: _begin(str.data())
, _end(str.data() + str.size()) {
std::cout << "string_view(...)" << std::endl;
}
const char* _begin;
const char* _end;
};
void join(initializer_list<string_view> strings) {
std::cout << "join(...)" << std::endl;
for (auto i = 0u; i < 5; ++i) {
std::cout << int(strings.begin()->_begin[i]) << " " << strings.begin()->_begin[i] << std::endl;
}
}
int main() {
join({ string("this") });
return 0;
}
The output of this program compiled with last Visual Studio C++ (Express):
string_view(...)
join(...)
0
104 h
105 i
115 s
0
It may vary from compiler to compiler as above program is probably ill-formed.
I've investigated what is the order of calls in debugger, and there is the sequence:
main()
basic_string(const char*)
string_view(const string&)
~basic_string()
initializer_list(...)
join(...)
I would like the content of the string("this") to be available inside join function. And it is not the case, becouse `string("this") is destroyed before.
Why is the destructor of temporary string string("this") invoked before the join function is called or in other words why isn't the lifetime of string("this") extended to the end of the full-expression join({ string("this") })?
What I think might be happening here is that you are constructing your initializer_list from a reference to your string, so that the string goes out of scope when your initializer_list has finished constructing.
You see your string is not a parameter to the join() function but to the initializer_list you construct.
My guess is that the initializer_list is constructed, your string goes out of scope and then your join() function tries to access the dead string through the reference contained in the string_view.
Related
I'm newbee about c++ and I'm having trouble with destructors. When i create an object and push to a vector that holds class, i can see variable "_path" is initialized. But after i try to reach the variable, i see object calls decontsuctor and i cant see the variable.
Here is the code:
#include <iostream>
#include <vector>
#include <string>
#include "ClassA.h"
A returnA(const char* char_a)
{
return A(char_a);
}
int main() {
std::vector<A> vectorA;
for (int i = 0; i < 10; i++)
{
std::string s = std::to_string(i);
vectorA.emplace_back(returnA(s.c_str()));
}
std::cout << "-------" << std::endl;
for (int i = 0; i < vectorA.size(); i++)
{
vectorA[i].getPath();
}
return 0;
}
class A
{
public:
const char* _path;
A(const char* path);
~A();
void getPath();
};
A::A(const char* path)
{
_path = path;
std::cout << "Obj is constructed! " << _path << std::endl;
}
A::~A()
{
std::cout << "Obj is deconstructed! ";
std::cout << _path << std::endl;
}
inline void A::getPath()
{
std::cout << _path << std::endl;
}
How can i prevent objects not deconstruct themselves and reach their variables without dynamic allocation?
std::string s = std::to_string(i);
vectorA.emplace_back(returnA(s.c_str()));
std::string's c_str() method returns a pointer to std::string's internal buffer that's no longer valid when the std::string gets changed in any way.
This std::string gets destroyed at the end of this for loop, and that certainly meets all "any way" requirements. This is how all objects work in C++. This std::string's gets declared inside a for loop, and it gets destroyed as soon as the end of the loop ends and it iterates again (or stops).
At this point accessing this pointer becomes undefined behavior, this is what you're seeing.
As far as destructor calls: this is completely unrelated, this is simply how vectors work. When objects get added to a vector they get moved or copied into the vector itself. Additionally, a vector resizes itself, as it grows, because that's what a vector is all about. If you add an object to a vector, and then destroy the original object you'll, obviously, see the destructor getting called:
vectorA.emplace_back(returnA(s.c_str()));
The temporary object returned from returnA gets copied/moved into the vector, here. Then this temporary object gets destroyed, and this is the destructor call you're seeing. But the vector continues to hold its copy of the emplaced object, safe and sound.
The simplest way to fix your undefined behavior is to simply replace the const char * member of your class with its own std::string (and just the parameter to the constructor accordingly. This is what std::strings for: as you move or copy them around they'll take care of all the housekeeping for you.
This, of course, won't change the destructor behavior or invocation, it does not affect that.
Consider this snippet:
#include <iostream>
#include <string>
#include <string_view>
using namespace std::literals;
class A
{
public:
std::string to_string() const noexcept
{
return "hey"; // "hey"s
}
std::string_view to_stringview() const noexcept
{
return "hello"; // "hello"sv
}
};
int main()
{
A a;
std::cout << "The class says: " << a.to_string() << '\n';
std::cout << "The class says: " << a.to_stringview() << '\n';
}
I was naively expecting some warning in to_stringview() like returning a reference of a local temporary, but both g++ and clang say nothing, so this code seems legit and works.
Since this generates the expected warning:
const std::string& to_string() const noexcept
{
return "hey"s;
}
I was wondering by which mechanism the lifetime of "hello" is different from the lifetime of "hey".
but both g++ and clang say nothing, so this code seems legit and works.
You cannot deduce latter from the former. A lot of non-legit code produces no warnings.
That said, string literals have static storage duration, so there is no problem with their lifetime. to_stringview is legit indeed.
P.S. String literals are arrays of char.
I was wondering by which mechanism the lifetime of "hello" is different from the lifetime of "hey".
There is no difference between the lifetime of those two string literals. But "hey"s is not a string literal. It is a "user defined" literal that creates a temporary instance of the class std::string. That temporary object has no static storage duration.
I'm passing a std::sub_match as an argument to a std::thread (see my example code below). The thread function expects a const string reference. A sub_match can be converted to a string. So everything compiles fine.
But sometimes the function receives the wrong string.
When I convert the sub_match to a string before passing it to the thread it works as expected. What is the difference?
I think this is a race condition as the original sub_match may not exist anymore when the thread executes. But I thought arguments to threads would be copied anyway.
How can I find out which arguments are safe to pass to a thread and which not?
#include <iostream>
#include <string>
#include <vector>
#include <thread>
#include <regex>
#include <unistd.h>
class test_t {
public:
test_t(void) {}
~test_t(void) {}
void start(void){
//-------------------------------------------------
// Do some memory allocation.
// The error seems to appear faster with that.
std::vector<std::string> vec;
for(unsigned int i = 0; i < 1000; ++i) {
vec.push_back("test_test_test");
}
//-------------------------------------------------
std::string event = "operating";
std::smatch match;
std::regex expr("\\(operating\\)",
std::regex_constants::icase |
std::regex_constants::basic);
if(std::regex_match(event, match, expr)) {
std::cout << "start thread" << std::endl;
m_thread = std::thread(&test_t::thread_func, this, match[1]); //NOK
// m_thread = std::thread(&test_t::thread_func, this, match[1].str()); // OK
// m_thread = std::thread(&test_t::thread_func, this, (std::string)match[1]); // OK
m_thread.detach();
std::cout << "thread started" << std::endl;
}
}
private:
std::thread m_thread;
void thread_func(const std::string& string) {
if(string != "operating") {
std::cout << "ERROR: string: \"" << string << "\"" << std::endl;
exit(EXIT_FAILURE);
} else {
std::cout << "string: \"" << string << "\"" << std::endl;
}
}
};
int main(int argc, char** argv) {
test_t test;
while(1) {
test.start();
usleep(100);
}
return 0;
}
Compilation Message:
Compiled with: g++ --std=c++11 -pthread -o test main.cpp
g++ --version: g++ (SUSE Linux) 4.8.5
Expected output:
start thread
thread started
string: "operating"
(repeat)
Actual output:
start thread
thread started
string: "operating"
ERROR: string: "test_test"
operator[] for std::smatch returns sub_match which can be treated as pair of iterators to matched characters.
After regex_match was called, you can use operator[] to access sub-matches as long as event exists. When event is deleted (you are not joining your thread, so start returns immediately and event is destroyed), sub-matches have dangling pointers and should not be accessed.
m_thread = std::thread(&test_t::thread_func, this, match[1]);
this doesn't work because when function goes out of scope, event is deleted and sub-match has dangling pointers.
m_thread = std::thread(&test_t::thread_func, this, match[1].str());
this works because str() returns copy of matched string.
m_thread = std::thread(&test_t::thread_func, this, (std::string)match[1]);
this also works because temporary string is created based on sub-match match[1], and temp is passed into thread.
From some docs:
Because std::match_results holds std::sub_matches, each of which is a pair of iterators into the original character sequence that was matched, it's undefined behavior to examine std::match_results if the original character sequence was destroyed or iterators to it were invalidated for other reasons.
… and the same page teaches us that std::smatch is an alias for std::match_results<std::string::const_iterator>.
You'll need to take a copy of the character range referred to by these iterators, and pass that to std::thread.
It is true that thread_func will already be doing this copy during argument conversion (since the function takes a const std::string&, not a std::sub_match), but this occurs on the thread by which point it's too late, as your pointers are already [potentially] dangling.
So, I am trying to use a variation of the approach from this question's answers to implement a large-ish, somewhat wide, constant tree structure in C++ (using VC++2012 with the v110 toolchain), as shown below:
#include <tuple>
#include <iostream>
struct test
{
test(const char* n, const test& n1, const test& n2):
name(n), nodes(n1, n2)
{
if (name)
std::cerr << "test() " << name << std::endl;
}
~test()
{
if (name)
std::cerr << "~test() " << name << std::endl;
}
const char* name;
std::tuple<const test&, const test&> nodes;
};
const test n = test(0, n, n);
test t = test("blah", test("bleh", n, n), n);
int main()
{
std::cerr << "get " << std::get<0>(t.nodes).name << std::endl;
return 0;
}
However, instead of outputting the expected
test() bleh
test() blah
get bleh
~test() bleh
~test() blah
or something of that ilk, I get
test() bleh
test() blah
~test() bleh
get
and then the program dies with a segfault (i.e. the Windows "this program has stopped working" box) when it tries to access the prematurely destroyed inner object. Is this my fault for assuming that making a temporary a const reference held subobject of another object would extend the lifetime in the same way a local or global named const reference would? Or is this a problem with VC++2012 not realizing that the temporary is being captured by reference in the parent object constructor when it should be doing so (i.e. not destroying the inner object until the outer object gets disposed of?)
This bit: std::get<0>(t.nodes).name is never going to work properly in your current code, whatever else is going on. That is because nodes contains references: std::tuple<const test&, const test&> instead of instances.
As a result when you pass the inner object: test("bleh", n, n), it is not copied into the nodes of the outer one. Therefore, when it goes out of scope and it is destroyed, your outer object's nodes (t.nodes) contains references pointing to the great beyond. As a result your program is going to take exception to that statement and crash with a segfault.
EDIT to add:
To clarify: the object that goes out of scope is the value of: test("bleh", n, n)
In this statement: test t = test("blah", test("bleh", n, n), n);
Because by the time t = test("blah",...) returns the inner object test("bleh",...) is gone. Note that t is actually a copy of the value of test("blah",...), the original temporary is also gone. (Well it gets more complex when you consider that the compiler may choose to optimise and do a move instead of a copy, but the semantics are as though it were a copy.)
My intution says it isn't, but the fact that everything is going on in the same line is a bit confusing. I wonder if the pointer is still valid when cout uses it.
#include <iostream>
#include <string>
struct A {
A() : m_s("test"){ }
const char* c_str() { return m_s.c_str(); }
std::string m_s;
};
int main() {
std::cout << "abc " << A().c_str() << " def" << std::endl;
}
Yes, it is safe. The temporary A() is destroyed at the end of the statement. That is after cout used the pointer returned.
The value returned by c_str() is valid as long as the std::string it was obtained from is unchanged and has not been destroyed. So your code is perfectly OK, as the temporary you create will have a lifetime equivalent to the full expression (the cout chain in this case) that it is part of.
It is safe provided content doesn't change in between the call.
The address that is returned, is indeed pointing to valid location of the char*.