This is an archive of the discontinued LLVM Phabricator instance.

eliminate one form of PythonObject::Reset()
ClosedPublic

Authored by lawrence_danna on Oct 16 2019, 9:27 PM.

Details

Summary

I'd like to eliminate all forms of Reset() and all public constructors
on these objects, so the only way to make them is with Take<> and Retain<>
and the only way to copy or move them is with actual c++ copy, move, or
assignment.

This is a simple place to start.

Diff Detail

Event Timeline

lawrence_danna created this revision.Oct 16 2019, 9:27 PM
Herald added a project: Restricted Project. · View Herald TranscriptOct 16 2019, 9:27 PM

include move version too

labath accepted this revision.Oct 17 2019, 1:38 AM

I like where this is going.

lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h
234–239

You can consider simplifying this further down to a "universal"/"sink" operator=(PythonObject other). Since the object is really just a pointer, the extra object being created won't hurt (in fact, the removal of &-indirection might make things faster).

This revision is now accepted and ready to land.Oct 17 2019, 1:38 AM
lawrence_danna marked 2 inline comments as done.Oct 17 2019, 11:12 AM
lawrence_danna added inline comments.
lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h
234–239

wouldn't that result in an extra retain and release every time a PythonObject was copied instead of referenced or moved?

labath added inline comments.Oct 17 2019, 11:20 AM
lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h
234–239

No, it shouldn't, because the temporary PythonObject will be move-constructed (== no refcount traffic), if the operator= is called with an xvalue (if the rhs was not an xvalue, then you wouldn't end up calling the && overload anyway). Then you can move the temporary object into *this, and avoid refcount traffic again.

So, there is an additional PythonObject created, but it's move-constructed if possible, which should be efficient, if I understand these classes correctly. This is the recommended practice (at least by some) when you don't want to squeeze every last nanosecond of performance..

lawrence_danna marked 3 inline comments as done.Oct 17 2019, 11:25 AM
lawrence_danna added inline comments.
lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h
234–239

How do you move the temporary object into *this, if you only have operator=(PythonObject other) to assign with?

lawrence_danna marked an inline comment as done.Oct 17 2019, 11:25 AM
labath added inline comments.Oct 17 2019, 11:26 AM
lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h
234–239

In case that wasn't clear, the idea is to replace two operator= overloads with a single universal one taking a temporary. The advantage of that is less opportunities to implement move/copy incorrectly. The cost is one temporary move-constructed object more.

lawrence_danna marked 2 inline comments as done.Oct 17 2019, 11:44 AM
lawrence_danna added inline comments.
lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h
234–239

so does that amount to just deleting the copy-assign, and keeping the move-assign how it is?

labath added inline comments.Oct 17 2019, 11:49 AM
lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h
234–239

How do you move the temporary object into *this, if you only have operator=(PythonObject other) to assign with?

You need to do it manually, like the current && overload does, but you don't also need to implement the copy semantics in the const& overload.

so does that amount to just deleting the copy-assign, and keeping the move-assign how it is?

Almost. The implementation of move-assign would remain the same, but you'd drop the && (otherwise you'd lose the ability to copy-assign) from the signature. I.e.,

PythonObject &operator=(PythonObject other) {
    Reset();
    m_py_obj = std::exchange(other.m_py_obj, nullptr); // I just learned of this today so I have to show off.
    return *this;
  }
lawrence_danna marked 2 inline comments as done.Oct 17 2019, 11:53 AM
lawrence_danna added inline comments.
lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h
234–239

oooooooh, i get it. It didn't occur to me that you could treat other as a rvalue without passing it as one in the parameter list, but of course you can because if it's pass-by-value then it's just a local variable by the time operator= gets its hands on it.

universal assignment

lawrence_danna marked an inline comment as done.Oct 17 2019, 12:11 PM
This revision was automatically updated to reflect the committed changes.
labath added inline comments.Oct 18 2019, 12:31 AM
lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h
234–239

Yeah, isn't c++ fun? :)

I actually think this is one of the best features of c++11, as it allows you to write a single function that takes ownership of something more-or-less efficiently without any superfluous overloads or fancy &&s. The only problem is overcoming the learned "wisdom" that const & is the most efficient way to pass objects around.