4

I started playing with the C++11 standard and the in-built threading. From what I gather when the value on a future is gotten, it is done using the move operator giving ownership away from the original object (like the old auto_ptr used to do on assignment). I tested this out by printing out the pointer of the char array inside an std::string object during the thread and printing the pointer after receiving it back in the main. However, the pointers are different. I would appreciate it if someone could tell me why they are different in this simple code and what the code would have to look like for them to be equal:

#include <cstdlib>
#include <iostream>
#include <vector>
#include <algorithm>
#include <chrono>
#include <string>
#include <thread>
#include <future>
using namespace std;
void thrfut(promise<string>&& promReceived)
{
    string strObj("Hello from future");
    cout << "Address of char array inside string inside of thread " << (void*)strObj.data() << endl;
    promReceived.set_value(strObj);
}
int main(int argc, char** argv) 
{
    promise<string> promiseOfText;
    future<string> futureText = promiseOfText.get_future(); // has to be before creating thread if the promise is passed rvalue reference, should be moved on calling get or not ? 
    thread threadHandlingPromise(&thrfut, std::move(promiseOfText));
    string stringReceived = futureText.get();
    cout << "Received from promise through thread: " << stringReceived  << endl;
    cout << "Address of of char array inside string received from promise in main " << (void*)stringReceived.data() << endl;
    threadHandlingPromise.join(); 
    return 0;
}

Here is a sample output

Address of char array inside string inside of thread 0x10ebc9be1
Received from promise through thread: Hello from future
Address of of char array inside string received from promise in main 0x7fff510f68c9

fyi: OS X 10.9.1 w/ Xcode 5 clang++ in Netbeans 8.0 other people have run the code on Ubuntu and Windows and returned the same address.

EDIT (cfr. comments in answers) * * * * *

I tried this:

struct MYC 
{   MYC() = default;
    ~MYC() { delete _pInt; };
    MYC(const MYC & myc) { puts("MYC copy"); _pInt = nullptr; if(myc._pInt != nullptr) {   _pInt = new int{*myc._pInt}; } }  
    MYC(MYC && myc) { puts("MYC move"); delete _pInt; _pInt = myc._pInt; myc._pInt = nullptr; }
    void setMe(int value) { delete _pInt; _pInt = new int{value} ; }
    int * _pInt = nullptr;
};
void thrfut(promise<MYC>&& promReceived)
{
    MYC obj;
    obj.setMe(5);
    cout << "Address of int inside MYC inside thread " << (void*)obj._pInt << endl;
    promReceived.set_value(std::move(obj));    
}
int main(int argc, char** argv) 
{
    promise<MYC> promiseOfMYC;
    future<MYC> futureMYC = promiseOfMYC.get_future();  
    thread threadHandlingPromise(&thrfut, std::move(promiseOfMYC));
    auto mycReceived = futureMYC.get();
    cout << "Address of int inside MYC received from promise in main " << (void*)mycReceived._pInt << endl;
    cout << "Value of int inside MYC received from promise in main " << *(mycReceived._pInt) << endl;
    threadHandlingPromise.join(); 
    return 0;
}

and got:

Address of int inside MYC inside thread 0x7fd1b9c00110
MYC move
MYC move
Address of int inside MYC received from promise in main 0x7fd1b9c00110
Value of int inside MYC received from promise in main 5

Which confirms the move dynamics for the non-string classes.

crogg01
  • 2,446
  • 15
  • 35
  • 1
    Small string optimization maybe? – user541686 Jan 20 '14 at 02:30
  • 1
    Hmm works fine on Windows g++ 4.8.1 :S – Brandon Jan 20 '14 at 02:35
  • You're surprised that two local objects on the stacks of different threads don't have the same address? – Casey Jan 20 '14 at 09:49
  • I checked small string optimization by making the string massive, but I still got two different values for the pointers to the data but they seemed only one offset away from each other (null termination perhaps?). In case someone is interested in small string optimization `http://john-ahlgren.blogspot.co.uk/2012/03/small-string-optimization-and-move.html`. – crogg01 Jan 20 '14 at 16:59

2 Answers2

3

Edit

I finally remembered GNU libstdc++ std::string employs reference counting. Perhaps this explains something?? (not everything, though)

Original answer

(1) It is indeed system dependent

$ g++48 promiseStr.cpp -o promiseStr -Wall -Wextra -std=c++0x -O0 -g3 -pthread && echo OK
OK
$ ./promiseStr
Address of char array inside string inside of thread 0x7f4b400008d8
Received from promise through thread: Hello from future
Address of of char array inside string received from promise in main 0x7f4b400008d8

$ lsb_release -a
LSB Version: (snip)
Distributor ID: Ubuntu
Description:    Ubuntu 12.04.2 LTS
Release:        12.04
Codename:       precise
$ g++48 --version
g++48 (GCC) 4.8.1
Copyright (C) 2013 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

(2) future<T>::get() returns T not &&T, so stringReceived might be copy-constructed.

(3) You might as well try ltrace on Linux to see what happens under the hood (Sorry, I have no OS X machines.)

$ ltrace -n2 -f -C ./promiseStr 2>&1 | grep basic_string
[pid 6899]       std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&)(0x7f486c17fde0, 0x406ea0, 0x7f486c17fdef, 0x7f486c180700, 0x7f486c180700) = 0x7f48640008d8
[pid 6899]             std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(std::string const&)(0x244e0b0, 0x7f486c17fde0, 0x7f4864000900, 0x7f486c17fd90, 0x7f486c17fd20) = 0x7f48640008d8
[pid 6899]         std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string()(0x7f486c17fde0, 0xffffffff, 0, 0x7f4864000028, 0x244e038 <unfinished ...>
[pid 6899]         <... std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() resumed> ) = 1
[pid 6898]   std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(std::string&&)(0x7fff5c17ece0, 0x244e0b0, 0x244e0b0, -1, 0x244e038) = 0x7f486cf723d8
[pid 6898]   std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string()(0x244e0b0, 1, 0x244e0a0, -1, 0x244e060) = 0x244e0b0
[pid 6898]   std::basic_ostream<char, std::char_traits<char> >& std::operator<< <char, std::char_traits<char>, std::allocator<char> >(std::basic_ostream<char, std::char_traits<char> >&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)(0x60bd40, 0x7fff5c17ece0, 0x7fff5c17ece0, 0x203a6461, 0x7f486c53cab0) = 0x60bd40
[pid 6898]       std::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string()(0x7fff5c17ece0, 0x7f486c759250, 0x7f486c180700, 0x7f486c759250, 0) = 0
Community
  • 1
  • 1
nodakai
  • 7,773
  • 3
  • 30
  • 60
  • I tried a suggestion from @ikh (with a new MYC class) but that seems to imply to copy CTOR gets called, yet the pointer addresses are different. – crogg01 Jan 20 '14 at 03:33
  • (2) no, it is move-constructed. the object that function returns is rvalue. For example, futureMYC.get() = MYC(); is not allowed. – ikh Jan 20 '14 at 03:36
  • Hmm, I admit my understanding of move and *value's is weak and my wording was inaccurate... I thought there were two steps involved; `get()` retuns an rvalue and `stringReceived` is constructed with the rvalue. I thought the first step, at least , had to be copying. – nodakai Jan 20 '14 at 03:44
  • @nodakai std::future::get() make future invaild and should be called once. In first step, it might move, not copy. (shared_future can copy..) – ikh Jan 20 '14 at 03:47
  • the ref counted std::string is unlikely to be the explanation because I got the same thing the MYC struct. – crogg01 Jan 20 '14 at 04:09
  • @HansRoggeman Address is naturally different; Leaving whether move or copy, different variables has different address. – ikh Jan 20 '14 at 04:35
  • to @ikh, you are right. Lack of sleep at that point. string implementation looks like the most likely solution. – crogg01 Jan 20 '14 at 11:23
2

On promReceived.set_value(strObj);, string's copy constructor is called; not move constructor.

And although you use promReceived.set_value(std::move(strObj));, maybe there's no guarantee that data()'s return values are same.. As Mehrdad says, it can be different for each implementation, such as small string optimization.

(edit: And your implementation can be use copy constructor inside library..)

ikh
  • 10,119
  • 1
  • 31
  • 70
  • I changed the code to explicitly use the move operator as mentioned `promReceived.set_value(std::move(strObj));` and it is still the same problem. I will try checking with a char array and see what that gives. – crogg01 Jan 20 '14 at 02:34
  • @HansRoggeman In fact, string::data doesn't always return its array. In C++11, it is equal to string::c_str – ikh Jan 20 '14 at 02:36
  • @HansRoggeman first, char* is just value; Although char* is copyed, the value is not changed. second, I suggest some test: give this class to promise: struct C { C(const C &) { puts("C copy"); } C(C &&) { puts("C move"); } };. An implementation can copy value inside library. and if not, std::string's implementation can be the reason of this. (like small string optimization) – ikh Jan 20 '14 at 02:55
  • @HansRoggeman first: Um, My test suggestion is just for see "C move".. rvalue reference just helps program's performance by saying to move constructor "Please just move your data; don't deep copy". The result is reasonable. second: string literal's data is in program's const data area. Although pointer is gone, data is not destroyed. – ikh Jan 20 '14 at 03:20
  • Looking at the output from using the struct MYC in the edit of the original question, I thought that @nodakai's point number (2) might show promise but then the printout would have shown at least one "MYC copy". So I am still puzzled. – crogg01 Jan 20 '14 at 03:35
  • @HansRoggeman the object that function returns is rvalue. see my reply on nodakai's answer – ikh Jan 20 '14 at 03:40