Sunday, September 7, 2014

RPN List – Return Non-Local Variable

Both the decode and translate routines generate an RPN list and instead of a pointer to the list will return the list by value, preferably by a move operation.  The decode routine (of the Program Model class) contains a local RPN list variable, and so the value of the variable is returned by a move operation since the variable is going out of scope.  However, the translate routine builds the RPN list in a member value of the Translator class (so all translator functions have access to the list).  If this variable is returned by value, the value is copied because the variable is not going out of scope.

With the pointer implementation when a pointer was returned, since the translate routine no longer needed the list, it made a temporary copy of the member list pointer, set the member pointer to null and returned the temporary copy of the pointer (this transferred the list to the caller who was then responsible for deleting the list):
RpnList *output = m_output;
m_output = NULL;
return output;
The Translator class RPN list member will be the actual list, not a pointer to a list.  When returning from the translate routine, the RPN list is no longer needed, so it can be transferred to the caller with a move operation.  This move does not occur by just returning the variable because it is not going out of scope.  The move operation can be forced by using the standard move (which resets the member variable to an empty list and transfers its value to the receiving variable of the caller):
return std::move(m_output);
By default, C++ provides a default constructor, copy constructor, copy assignment, move constructor, move assignment and destructor for a class if a program uses them.  If a constructor is declared, a default constructor is not generated.  If a copy operation, move operation or destructor is declared, then no default copy operation, move operation or destructor is generated.

Defining a destructor indicates that something other than the default operation needs to be done for a copy or move (for example, dealing with an allocated resource).  However, these rules are not completely enforced.  For backward compatibility, GCC still generates default copy operations if a destructor is defined without a warning.

The upstream problem mentioned a few posts ago http://interactivebasiccompilerproject.blogspot.com/2014/09/rpn-list-base-class-to-member.html was thought to have occurred due the destructor of the list base class was not being called because the above return move statement was not clearing the list in the member variable.  The problem was finally identified to be caused by the destructor (which wasn't doing anything) declaration was removed.  With the destructor, there were no move operations, the standard move performed a copy.  Removing the destructor allowed the default move operations to be generated.  A default move operation can be generated using the new C++11 syntax:
ClassName(ClassName &&other) = default;
This solved the issue of the member list not being cleared.  However, the default move operations were not sufficient as there was another problem...

No comments:

Post a Comment

All comments and feedback welcomed, whether positive or negative.
(Anonymous comments are allowed, but comments with URL links or unrelated comments will be removed.)