Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added copy constructor, move constructor and copy assignment operator. #826

Merged
merged 16 commits into from
Jan 17, 2017
Merged

Conversation

s1998
Copy link
Contributor

@s1998 s1998 commented Dec 11, 2016

I tried to add copy constructor, move constructor and copy assignment operator to DTree.
Function declarations are present in dtree.hpp and function definitions are added in dtree_impl.hpp
Token number #817 .
Waiting for review and suggestions to fix the issue.

@rcurtin
Copy link
Member

rcurtin commented Dec 12, 2016

Hi there,

Thanks for the contribution. Could you please add some tests for these? Take a look at some tests I recently wrote in a different branch: https://github.com/rcurtin/mlpack/blob/bindings/src/mlpack/tests/lsh_test.cpp#L840

Once there are working tests I'll give it a full review.

@s1998
Copy link
Contributor Author

s1998 commented Dec 15, 2016

@rcurtin
I added the tests.

Copy link
Member

@rcurtin rcurtin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for adding the tests. There are still a couple issues to take care of before we can merge this, though.


//Copying the children
left = obj.left;
right = obj.right;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't you simplify this to left = new DTree(*obj.left); and the same for the right?

BOOST_REQUIRE(tree2.Left()==leftChild);
BOOST_REQUIRE(tree2.Right()==rightChild);
BOOST_REQUIRE(tree3.Left()==leftChild);
BOOST_REQUIRE(tree3.Right()==rightChild);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At this point leftChild and rightChild should be invalid pointers; I bet valgrind will call this an invalid memory access and in some cases this test will fail.


//Checking children were safely moved
BOOST_REQUIRE(tree2.Left()==leftChild);
BOOST_REQUIRE(tree2.Right()==rightChild);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue with leftChild and rightChild here.

template <typename MatType, typename TagType>
DTree<MatType, TagType>& DTree<MatType, TagType>::operator=(const DTree<MatType, TagType>& obj)
{
if (this != &obj){
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think these checks are necessary. If that is true, the user is doing something pretty stupid and we shouldn't need to handle it.

@s1998
Copy link
Contributor Author

s1998 commented Dec 22, 2016

@rcurtin
I tried to fix the issues. The checks are still failing. Travis-ci says that RAModel test failed (I was unable to find how dtree and knn is related. If you could tell me about that, I would try to fix the issue).

alphaUpper(obj.alphaUpper),
left(obj.left),
right(obj.right)
{ /* Nothing to do. */ }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't actually copy the left and right child nodes, but your copy operator does. Can you fix that please?

Copy link
Contributor Author

@s1998 s1998 Jan 9, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rcurtin The child node (data) needs to be copied or the pointer to the child node?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the copy constructor, so you need to copy the data. Take a look at the copy constructor test you wrote---if you just copy the pointer value, then when you delete the tree you copied from, suddenly the left and right pointers point to invalid memory. I bet that if you run valgrind to check for memory leaks on that test, like

$ bin/mlpack_test -t DETTest/CopyConstructorAndOperatorTest

then valgrind will throw invalid memory access errors everywhere, even if the test itself does not fail.

obj.bucketTag = -1;
obj.alphaUpper = 0.0;
obj.left = NULL;
obj.right = NULL;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should be able to do this as an initialization list.

Copy link
Contributor Author

@s1998 s1998 Jan 5, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually had seen those links before creating the PR and had used initialization list in copy constructor. I saw them again now. What confused me was that at line 249 you commented that one can use initialization list for the same . But there we are trying to set the default values of the field of object passed (obj - the one on RHS ) and not of the object created.
What I understood from the link was that initialization list can be used to initialize the fields of the object that is being created (the one for which the constructor has been called - on LHS).
I think I haven't still understood what exactly was said here. I have tried to fix other bugs.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah---you can use an initialization list in this method to set the values of the object being constructed. You are right that you can't do that with the RHS, but that isn't what I meant. Sorry for the confusion.

template <typename MatType, typename TagType>
DTree<MatType, TagType>& DTree<MatType, TagType>::operator=(DTree<MatType, TagType>&& obj)
{
if (this != &obj){
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the test to see if this is the same as the object being copied is unnecessary.

ratio = std::move(obj.ratio);
logVolume = std::move(obj.logVolume);
bucketTag = std::move(obj.bucketTag);
alphaUpper = std::move(obj.alphaUpper);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't bother with std::move() for primitive types like int, double, etc., since move will degrade to copies for primitive types. As a result, using move() there can actually make the code more confusing, because we can't assume that the rvalues will be set to default values or something. (That is my current understanding of the spec.)

@s1998
Copy link
Contributor Author

s1998 commented Jan 10, 2017

@rcurtin
I tried to fix the issues and changed the tests to ensure children along with their data are properly copied.
I don't know why the appveyor still fails.
bin/mlpack_test -t DETTest/CopyConstructorAndOperatorTest
bin/mlpack_test -t DETTest/MoveOperatorTest
bin/mlpack_test -t DETTest/MoveConstructorTestgive positive results on my system.

if(obj.right != NULL)
right = new DTree(*obj.right);
else
right = NULL;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can write these in the initialization list cleverly: left((obj.left == NULL) ? NULL : new DTree(*obj.left)), for instance.

if(obj.right != NULL)
right = new DTree(*obj.right);
else
right = NULL;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens when left and right are not NULL when this method is called?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think they are supposed to be freed.


//Moving children
left = std::move(obj.left);
right = std::move(obj.right);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you're just moving pointers here, you don't actually need to use std::move(), you can just do left = obj.left and right = obj.right.

@rcurtin
Copy link
Member

rcurtin commented Jan 11, 2017

The AppVeyor failure is a compilation error; if you dig through the log and search for "error " you can see what the issue is:

C:\projects\mlpack\src\mlpack\tests\det_test.cpp(516): error C2248: 'mlpack::det::DTree<arma::mat,int>::maxVals': cannot access private member declared in class 'mlpack::det::DTree<arma::mat,int>' [C:\projects\mlpack\build\src\mlpack\tests\mlpack_test.vcxp

So although gcc lets you get away with accessing a private member of DTree, MSVC doesn't. It seems like you'll have to find a different way to test that.

@s1998
Copy link
Contributor Author

s1998 commented Jan 12, 2017

@rcurtin I tried to fix the issues. (I should have gone through AppVeyor log more carefully.)

@rcurtin
Copy link
Member

rcurtin commented Jan 13, 2017

Thanks for taking care of all the issues. Did you have anything else to modify or should I merge this?

@s1998
Copy link
Contributor Author

s1998 commented Jan 13, 2017

I can't think of anything else to modify. If there are no issues left, may be this should be merged. :)

@rcurtin rcurtin merged commit 6c952f7 into mlpack:master Jan 17, 2017
@rcurtin
Copy link
Member

rcurtin commented Jan 17, 2017

Ok, merged in. I made some style fixes in 547e2cb. Thanks for your hard work on this PR. Would you like to add your name and email to the list of contributors in src/mlpack/core.hpp and COPYRIGHT.txt?

@s1998
Copy link
Contributor Author

s1998 commented Jan 18, 2017

@rcurtin I would like to add my name and email to the list, would I need a different PR for that ?

@rcurtin
Copy link
Member

rcurtin commented Jan 18, 2017

Yeah---this one is already merged so you'll need to open another one for that.

@s1998
Copy link
Contributor Author

s1998 commented Jan 18, 2017

Okay .

@rcurtin rcurtin modified the milestones: mlpack 2.2.0, mlpack 3.0.0 Mar 17, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants