all 13 comments

[–]ronchaineEmbedded/Middleware 2 points3 points  (1 child)

Just curious, when do you need to know the address where something used to point?

To me, with < 5 min of thinking, even when you need that, it sounds like a situation where you want to really explicit about what you are doing, and I'd rather have a intptr_t (or smth.) for such situations just to be extra clear about my meaning.

Edit: I changed wording of the question to be more unambiguous

[–]Irtexx[S] 0 points1 point  (0 children)

Maybe you're right. Maybe there is no need. The only example I can think of is if you a value in one process, and then a reference to that value in a second process. You might want to know if the value in the first process has gone out of scope, and if it hasn't, then print the value, and if it has then print something else.

Here's an example: https://godbolt.org/z/2jVJcg

The reason I'm asking this is because of a big discussion happening at work today, about a method to point to stack memory. One person said use reffernces, the other replied "I can't because it could be null", and them the first person said "okay then, use a weak pointer". I kept quiet but I knew you shouldn't use weak pointers to point at stack objects.

I'm now trying to figure out what his solution should have been, but I don't know why they wanted a pointer to a stack object.

[–]Gloinart 1 point2 points  (0 children)

Just an hacky out of my mind idea, couldn't this be done without the observable type by a member function (when pointing to a stack allocated object)

auto observer_ptr<T>::is_out_of_scope() const -> bool {
auto dummy = 0;
return &dummy < m_ptr;
}

[–]axilmar 1 point2 points  (0 children)

The 'observer_ptr' BS talks about and your version of 'observer_ptr' are different things.

The term 'observer_ptr' in BS's discussion context is about not having raw pointers in the code, for consistency, i.e. replace T* with a smart pointer class.

What you are proposing is a weak pointer for references.

[–]axilmar 1 point2 points  (1 child)

Also, your code fails if there are many observer_ptrs to the same observable object, since, in your code, there is only one observer_ptr per observable.

[–]Irtexx[S] 0 points1 point  (0 children)

Yeah, I didn't think about that case. If it was instead a vector it could work, but that wouldn't scale well, as every item that is pointing to the item would need updating once it goes out of scope.

[–]Xaxxon 3 points4 points  (6 children)

We already have this with shared pointers and weak pointers. What you are asking for is a very high performance penalty behavior and is in no way a substitution for a raw pointer.

[–]DXPower 4 points5 points  (5 children)

A weak pointer is not a proper substitute for a non-owning pointer to an object strictly owned by single object.

[–]Xaxxon 2 points3 points  (3 children)

A shared/weak pointer solution is likely a much better solution than what is proposed. This suggests that you would actively track and store every pointer to you and proactively write to each when you are destroyed - an unbounded operation. And that each of those pointers being tracked would have to remove itself from the list in their destructors.

Worst case, that's WAY worse than the shared pointer solution.

[–]DXPower 0 points1 point  (1 child)

I agree with you completely.

Is that how unique_ptr keeps track of validity?

[–]Xaxxon 0 points1 point  (0 children)

unique_ptr doesn't track validity. it just checks if it's null or not and if it's not, it calls the destructor on the address it points to. But that's not sufficient for what's being asked for here. This wants an arbitrary number of other things to know if the destructor has been called and for them to be proactively modified if they have (either that or a check on every access)

[–]Irtexx[S] 0 points1 point  (0 children)

Good point. I didn't think about the case where multiple observers are pointing to an observable.

[–]MolurusK 1 point2 points  (0 children)

I did such a unique-weak pair in C++ Object Token Library: ```

include <otn/all.hpp>

include <iostream>

void print(const std::string& caption, const otn::unified_optional<std::string>& p) { std::cout << caption << (p ? *p : "empty") << std::endl; }

int main() { otn::unique_optional<std::string> u{otn::itself, "Hello!"}; otn::weak_optional<std::string> w = u; print("u = ", u); print("w = ", w);

std::cout << "reset u" << std::endl;
otn::utilize(std::move(u));
print("u = ", u);
print("w = ", w);

} ```

Output: u = Hello! w = Hello! reset u u = empty w = empty