2

I can compile the following code:

void threadFunc(Vec3f *&buffer) {}
...
std::unique_ptr<Vec3f []> buffer(new Vec3f[100]);
Vec3f *b = buffer.get();
std::thread(threadFunc, std::ref(b)).join();

But I can't compile:

std::thread(threadFunc, std::ref(buffer.get())).join();

I get the following error at compile time:

error: use of deleted function ‘void std::ref(const _Tp&&) [with _Tp = Vec3<float>*]’

EDIT: the thread is joined before unique_ptr goes out of scope

What's different between the 2 versions? Can I make the second solution work?

Additionally, it seems better to pass by reference a pointer to the managed object than the unique_ptr by reference itself (the threadFunc only has to modify the content of buffer). Something like:

void threadFunc(std::unique_ptr<Vec3f []> &ptr) {}
std::thread(threadFunc, std::ref(buffer)).join();

Would this be bad practice? Or is this acceptable as well? It seems to me that if I want to change the content of buffer, this is what I should pass to the thread function, not the unique_ptr itself? Any recommendation would be appreciated.

EDIT 2:

So According to one of the answers below, the second option is not possible because std::ref(buffer.get()) uses some temp object. Though the first version despite what's said should work (I can't see why this would be invalid):

Vec3f *tmp = new Vec3f[100];
std::unique_ptr<Vec3f []> buffer = std::unique_ptr<Vec3f []>(tmp);
Vec3f *b = buffet.get(); // address of b == address of tmp
std::thread(threadFunc, std::ref(b));

same as:

std::thread(threadFunc, std::ref(tmp));

As for the solution provided:

void threadFunc(Vec3f *buffer) { buffer[0] = 0; // invalid }
Vec3f *buffer = new Vec3f[100];
std::thread(threadFunc, buffer);

It seems invalid to me as buffer is passed by value and not by reference, however I need to write to buffer. So it needs to be passed by ref.

If someone could clarify it would be great.

user18490
  • 3,546
  • 4
  • 33
  • 52
  • 3
    Not sure where you're going with this, but having a pointer in one thread whose lifetime is managed in another is *asking* for a race condition – Cory Kramer Sep 22 '15 at 16:54
  • Threads only write to unique memory addresses in the buffer (threads will never write to the same memory addr). Plus I can lock/unlock access to the buffer when needed to avoid race condition? Does it make sense? Though this is not my question ;-) – user18490 Sep 22 '15 at 16:56
  • why do you care about that extra line? it's only one line.. – David Haim Sep 22 '15 at 17:29
  • In what way is the thread function supposed to change the value of the pointer that it receives a reference to? If that address is changed, what advantage does a `unique_ptr` give you? I think that there is more to your story which you're not telling. – Ulrich Eckhardt Sep 22 '15 at 19:05
  • @Ulrich: not really. I am just using `unique_ptr` instead of using an old fashion pointer. This is a habit I have taken because it 1) it forces me to think whether this should be shared or not, 2) because it does memory collection for me. The buffer is actually an image buffer. The treads write to this image buffer, but each thread writes to a different part of the buffer (so not race condition). I still haven't got a proper answer to the question though. Even if the ref can't be ceated because of a temp object, one version works and not the other and I find that inconsistent? – user18490 Sep 22 '15 at 23:15
  • Talking about proper answers, you haven't answered mine either: Why does the thread need a *reference* to the address it's supposed to write to? Why can't you pass a plain pointer? That said, if the `unique_ptr` is irrelevant to the issue, remove it from the code to move closer to a minimal example. Note that the code marked "invalid" doesn't explain any issue you have with it. Why do you consider this invalid, for example? – Ulrich Eckhardt Sep 23 '15 at 06:30
  • @Ulrich: I did answer in the comment above. The tread needs a reference in order to "write" to the buffer. The unique_ptr is relevant to the issue in the sense that this is what I am interested to figure out. I know I can pass a pointer directly. I know how to do that. What I am interested into is what I don't know about, which is how to pass as a reference a pointer to the object managed by a `unique_ptr` as shown in my example;-). – user18490 Sep 23 '15 at 18:30

1 Answers1

2

The reason why it doesn't work is because your are trying to get a ref to the temporary object. And you can't do this, because reference_wrapper inside it just has the pointer, and it refuses to hold the pointer to the temporary object. It is likely that the version which compiles is actually wrong, as you are passing a pointer to the managed object, which is probably deleted while in use in other thread (unless the thread is joined before unique_ptr goes out of scope)

By the way, unique_ptr was designed exactly to relieve you from those woes. Just pass unique_ptr by value, and let the other thread manage it's lifetime.

Edit: From our discussion, it seems threads are joined before unique_ptr goes out of scope. If my understanding is right, you do not need to provide unique_ptr to thread function at all. Following is a perfectly fine (pseudo)code:

typedef <your type> type_t;
void handle_func(type_t* ptr);
typedef std::unique_ptr<type_t> ptr_t;

ptr_t ptr(new type_t);
std::thread handler(handle_func, ptr.get());
handler.join();

On a side note, from the snippet above it is not even clear why unique_ptr is needed - as opposed to simply use local variable and pass an address or ref() to it. By I suppose, there are reasons for this I am not aware of :)

SergeyA
  • 61,605
  • 5
  • 78
  • 137
  • I actually corrected this in my code above. Indeed the thread is joined before unique_ptr goes out of scope ;-) – user18490 Sep 22 '15 at 16:58
  • Really? So you suggest to just use `std::thread(threadFunc, buffer)` with `void threadFunc(std::unique_ptr ptr)`? shouldn't this be a shared_ptr then if different threads access the same object? – user18490 Sep 22 '15 at 17:00
  • Accessing has nothing to do with the lifetime of the object. The question is not how you access the buffer, but how you manage it's lifetime. If you join the thread before your unique_ptr goes out of scope, do not worry about unique_ptr at all. Pass raw pointer to your thread. There is nothing wrong with it. – SergeyA Sep 22 '15 at 17:03
  • Accessing the buffer within multiple threads would be a different topic, and synchronization (if required) would be independent of life time management strategy. – SergeyA Sep 22 '15 at 17:05
  • Sure totally right but then I need to manage the deletion of the memory myself and I do tend to use unique_ptr a lot to manage this for me. – user18490 Sep 22 '15 at 17:05
  • Did I say you do have to manage your memory yourself? No, i did not. All I said was that you do not need to pass unique_ptr to your threads in your case. You need to pass them the raw pointer. I will update my answer with some code. – SergeyA Sep 22 '15 at 17:11
  • Okay would be great to see yes. Thanks a lot. It's interesting for me to see, because I understand that the pointer returned by get() is the pointer to the address of the object whose content I want to change. So I don't understand very well what you say about the temporary object etc. Why would the pointer to the actual location of my data (returned by get()) be a temp object? Thx for your patience;) – user18490 Sep 22 '15 at 17:14
  • I have done so. But looks like you are confused about what unique_ptr is and what it is not. unique_ptr is simply a memory-manager. And temporary object is an extremely important concept in C++ which I suggest your familiarize yourself with. StackOverflow is overflowing with Q&A on this matter. – SergeyA Sep 22 '15 at 17:19
  • I will have a look thanks. Though from looking at the code above you are not passing the pointer returned by get() by reference to the thread function? So I am not too sure how this would work? – user18490 Sep 22 '15 at 22:31
  • Sorry SergeyA but this still doesn't make any sense to me. If `get()` actually returns the point of the allocated memory (technically the buffer in my code) then passing this by reference to the thread function totally works (version 1 in my example above). While I understand from what you say that `std::ref(buffer.get())` doesn't work because it creates a temp object somewhere in the process okay. But one version works and not the other and i find that inconsistent. See my edits above. – user18490 Sep 22 '15 at 23:21
  • 1
    It does not matter that you are passing buffer by value. It's a pointer to allocated memory. You are copying the pointer, but it still points to the same memory. It seems to me you really need to get understanding of base C++ concepts. – SergeyA Sep 23 '15 at 13:01
  • This is a judgemental comment but okay. I know the difference between a variable passed by reference and one by value, and yet I wouldn't never pass a pointer by value if what I try to do is write to the array that this pointer is pointing to. All professional quality code you will see out there will use something like `Vec3f *&buffer`. Maybe we just don't have the same coding standards (assuming the men has been dynamically allocated). – user18490 Sep 23 '15 at 18:38
  • 1
    @user18490, you really are confused. There is nothing-nothing-nothing wrong in passing pointer by value. If you do not trust me, trust C++ standard library. Look into, for instance, http://en.cppreference.com/w/cpp/io/basic_istream/read function. It reads characters into buffer, and buffer is passed by value. The only time you need to pass a pointer by reference is when the pointer is going to be changed inside the function. For instance, when it is allocated. But it is not the case here. The code you are referring is very misguided. – SergeyA Sep 23 '15 at 18:52
  • "All professional quality code you will see out there will use something like `Vec3f *&buffer`." Sorry, that is simply wrong, show an example if you want to put a base to your claim. That said, you pass references to non-const objects only if the function is supposed to modify the object. In this case, that would be modifying the pointer, *not* what it points to, which is a totally different thing. – Ulrich Eckhardt Sep 23 '15 at 19:26
  • This is not wrong at all: reference-pointer is as in "passed by reference" the correct way. Everything else is passed by value even though the solution suggested by SergeyA works. Though as suggested in this link yes it's semantic, though all large systems I saw do use reference pointer. http://stackoverflow.com/questions/13006250/proper-way-to-pass-dynamic-arrays-to-other-functions – user18490 Sep 23 '15 at 19:35
  • @user18490, the link you have provided clearly says '... go for int*. If you need to trust some authorities here, here is the authority you can trust. – SergeyA Sep 23 '15 at 19:53
  • And yet you say the second version who compiles is likely to be wrong when it's perfectly fine. Never mind. And when you say pass by reference you mean pass by reference, not by value. My point was why A) is possible and 2) not. I pointed an inconsistency in C++ here, and yet you clearly don't have the skills/knowledge to answer this question. – user18490 Sep 24 '15 at 12:27
  • 1
    @user18490, the only inconsistency there is that you do not listen to what I am saying to you. On any rate, this is your developer skills, so I am leaving this discussion now. For the record, there are no inconsistencies in C++ here. – SergeyA Sep 24 '15 at 13:00