proposal to include "operator dist_object<T>::bool() const" that returns true normally but false for invalid dist_objects.

Issue #171 wontfix
Rob Egan created an issue

When a dist_object is moved, the specs state that it is invalidated, so it would be helpful to provide an interface to detect this condition.

template <typename T> dist_object<T>::dist_object(dist_object<T> &&other);
Precondition: Calling thread must have the master persona.
Makes this instance the calling process’s representative of the distributed object associated with other, transferring all state from other. Invalidates other, and any subsequent operations on other, except for destruction, produce undefined behavior.

Since a dist_object behaves mostly like a pointer it would be nice to test for its validity like it is possible with nullptr and shared_ptr, especially in the destructor of classes that include a dist_object as a member and want, themselves, to support the move constructor properly.

usage:

dist_object<int> d_o(world());
assert(d_o);

dist_object<int> d_o2(std::move(d_o));
assert(d_o2);
assert(!d_o);


Class My {
  dist_object<int> my_do;
public:
  My 
  : my_do(world()) 
  {}

  My(My &&mv) 
  : my_do(std::move(mv.my_do)) 
  {}

  My(const My &cp) = delete;

  ~My() {
    if(my_do) {
        // do some teardown with my_do
    }
  }
};

Comments (5)

  1. Dan Bonachea

    As a general principle, I don't like the idea of an implicit conversion to bool for a container - that introduces too much danger of misuse if someone has a dist_object<bool> mydo and accidentally types mydo when then mean *mydo. Setting syntax aside for now...

    Since a dist_object behaves mostly like a pointer it would be nice to test for its validity like it is possible with nullptr and shared_ptr

    I disagree with the premise here. Syntax aside, dist_object<T> does not have pointer-like semantics. It is a non-copyable, non-default-constructible object containing an unboxed T value. The fact there is a dist_id that can be used to locate a reference to the current instance in an implicit registry does not change the fact that a dist_object<T> instance is a non-copyable unboxed container. If anything dist_object<T> behaves more like a special std::tuple<T> than a pointer to T.

    It's true that moving a dist_object "invalidates" the source instance (making it no longer the registered local representative instance), but that's not analogous to a nulled pointer - in particular, the contained T in the source instance still exists, as a post-moved object that has not yet been destroyed. In retrospect, the move semantics should probably say it "deregisters" the source instance rather than "invalidates" it. The "invalid" term conflates with other degenerate states like post-destruction or uninitialized memory, which are not what we mean here.

    With regards to the specific example - a dist_object move also moves the wrapped value. So I think you can get the effect you are looking for by keeping a "is_moved" or "is_move_invalidated" state bit in your wrapped object (in this case int) or in the containing class (in this case My), either of which can track the move equally well. I understand the need to track "liveness" in the My class (in order to correctly implement move/destroy semantics), but I don't love the idea of co-opting dist_object to serve this "liveness flag" purpose. If nothing else it breaks the design principle that the source object of a move should only be passed to the destructor and no other calls.

    Re-reading the spec for dist_object I see some of the details here are a bit unclear, so I've just opened PR 60 to fix that.

  2. Amir Kamil

    Unlike std::shared_ptr, there isn’t a mechanism for constructing an “empty” dist_object, nor do we have semantics for what operations would be valid on such a dist_object. So I don’t think the two are analogous.

  3. Log in to comment