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

PCL_COMMON: removing default constructors/destructors #3440

Merged
merged 1 commit into from
Nov 4, 2019
Merged

PCL_COMMON: removing default constructors/destructors #3440

merged 1 commit into from
Nov 4, 2019

Conversation

lightyear15
Copy link
Contributor

This commit:

  • adds initiliazition for the non-static members
  • removes default constructors, destructors
  • removes from constructors all the calls to default member constructors

@kunaltyagi
Copy link
Member

There are some merge conflicts. Please resolve them (in another commit)

Copy link
Member

@kunaltyagi kunaltyagi left a comment

Choose a reason for hiding this comment

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

Nice work! What do you think about extending the changes to all members in the classes you have touched?

common/include/pcl/TextureMesh.h Show resolved Hide resolved
common/include/pcl/common/impl/accumulators.hpp Outdated Show resolved Hide resolved
/** \brief Empty destructor */
virtual ~PointRepresentation () {}
virtual ~PointRepresentation () = default;
//TODO: check if copy and move constructors / assignment operators are needed
Copy link
Member

Choose a reason for hiding this comment

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

Shall we follow the rule of 5 here and just go ahead and default them? Will there be anything bad about it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

At a first glance I would say no. But I don't have a complete view of the entire code base

Copy link
Member

Choose a reason for hiding this comment

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

There are a few pitfalls in just declaring them defaults, specially in exception unsafe code. It might be better not to have them declared than to declare buggy defaults. Maybe a work for another PR

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Please expand, why defaults are buggy? And also, how implicit declared destructor (i.e. destructor created by the compiler when user does not declare one) is any different by the default one?
What pitfalls are you referring to when code is not exception safe?

Copy link
Member

Choose a reason for hiding this comment

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

how implicit declared destructor ... is any different by the default one?

That's not what I was referring to. Your edit is perfect. I was commenting on my own statement of "what's the issue with blind rule of 5".

What pitfalls are you referring to when code is not exception safe?

Essentially, during copy, if there is an error, the rollback should result in the same state as before attempted copy. But with a) potentially self-copy b) copy of structs with resource owning semantics c) other similar corner cases, the rule of 5 might not be a good idea to apply blindly because the defaults do a element-wise operations. There might be scenarios where based on usage, we might have to do a copy into local variable before moving the resources and local variables to ensure that if copy fails, rollback is trivial.

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 see your point - b), a) not really - nonetheless I would say that this might happen when your class has non-trivially copyable elements, but in the case of PODs or classes that hold std::strings or std::vector<int>s, probably rule of 5 ( or even 7 ) is just fine.
I know even std::string copy constructor might fail with std::bad_alloc, but again, running out of memory it's like an atomic bomb dropped in front of your house: who cares if the windows were closed or not (i.e. destructors are called or not ) .

Copy link
Member

Choose a reason for hiding this comment

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

We are meandering out of context of the PR. But this is as good place as any.

running out of memory it's like an atomic bomb dropped in front of your house

bad_alloc isn't called just due to OOM. It's also called when

  1. a single allocation is larger than the one allowed by the allocator (eg: differing implementations of malloc). <-- most common reason
  • allocation takes too much time and it'll impact the "real-time"ness of the application. In soft real-time systems, page swaps required for a large allocation can take time which results in a throw (all custom malloc support maximum time to wait) <-- common in robotics
  • application has run out of it's quota of memory (not the OS). This approach is frequently used in sandboxes and extra memory is given to application to clean itself or to recover (via custom api calls) <-- common in cloud based virtualization
  • there's a custom memory management (FreeRTOS, distributed memory, etc.) which can throw bad_alloc for other reasons too <-- common in virtualization, hypervisors

Running out of memory is a very rare scenario for bad_alloc. Case 1 (common case) is essentially atom bomb for common users because it's not recoverable (by design or bad-calculations leading to the point) but not for all users.

common/include/pcl/point_cloud.h Outdated Show resolved Hide resolved
common/include/pcl/common/vector_average.h Outdated Show resolved Hide resolved
common/include/pcl/PCLPointCloud2.h Outdated Show resolved Hide resolved
@lightyear15
Copy link
Contributor Author

Nice work! What do you think about extending the changes to all members in the classes you have touched?

That was the idea, but unfortunately there are some stuff that need to be changed in order to accomplish it. Some matrices and vectors are initialized with identity matrices or other special matrices by calling static functions. AFAIK those functions need to be marked as constexpr first, then they can be used in member initialization.

@kunaltyagi
Copy link
Member

Force pushing every-time causes lack of knowledge of what's updated.

@lightyear15
Copy link
Contributor Author

Force pushing every-time causes lack of knowledge of what's updated.

sorry about that, next time I will make temporary commits, so that author or maintainer can squash them into one before merging

@kunaltyagi
Copy link
Member

kunaltyagi commented Oct 30, 2019

Apparently old compilers don't like the modifications wrt Eigen static functions and templates

common/include/pcl/common/vector_average.h:111:71: error: declaration of 'Eigen::Matrix<real, dimension, 1> pcl::VectorAverage<real, dimension>::dimension'
         Eigen::Matrix<real, dimension, 1> mean_ = Eigen::Matrix<real, dimension, 1>::Identity ();

I wonder what's going wrong in parsing. It's weird that the compiler thinks we are creating a variable dimension and not mean_

@lightyear15
Copy link
Contributor Author

Apparently old compilers don't like the modifications wrt Eigen static functions and templates

common/include/pcl/common/vector_average.h:111:71: error: declaration of 'Eigen::Matrix<real, dimension, 1> pcl::VectorAverage<real, dimension>::dimension'
         Eigen::Matrix<real, dimension, 1> mean_ = Eigen::Matrix<real, dimension, 1>::Identity ();

I wonder what's going wrong in parsing. It's weird that the compiler thinks we are creating a variable dimension and not mean_

I wonder if some simple aliasing, something like

using VectorType = Eigen::Matrix<real, dimension, 1>;
using MatrixType = Eigen::Matrix<real, dimension, dimension>;

would fix the problem. Because it really looks like a bug in the compiler, doesn't it?

@kunaltyagi
Copy link
Member

Sounds like a good idea. Could you try that in the docker image (or the CI)? Image is pointcloudlibrary/env:16.04

Copy link
Member

@kunaltyagi kunaltyagi left a comment

Choose a reason for hiding this comment

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

LGTM. Please rebase and squash as appropriate

This commit:
- adds initiliazition for the non-static members
- removes default constructors, destructors
- removes from constructors all the calls to default member constructors
- adds aliases in vector_average to avoid odd errors in old compilers
@kunaltyagi kunaltyagi merged commit 34d543d into PointCloudLibrary:master Nov 4, 2019
Copy link
Member

@SergioRAgostinho SergioRAgostinho left a comment

Choose a reason for hiding this comment

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

I meant to review this PR but apparently was late to the party. I just highlighted a number of places where destructors should not be required.

//-----CONSTRUCTOR&DESTRUCTOR-----
/** Constructor - dimension gives the size of the vectors to work with. */
VectorAverage ();
/** Destructor */
~VectorAverage () {}
~VectorAverage () = default;
Copy link
Member

Choose a reason for hiding this comment

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

Is there a reason for this destructor to not have been removed? I see no copy/move ctors/assignments.

@@ -65,6 +65,7 @@ namespace pcl

/** \brief Destructor. */
virtual ~StopWatch () = default;
Copy link
Member

Choose a reason for hiding this comment

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

This destructor can also be erased. It's a base class with no virtual methods.

{ reset (); }

/** Destructor */
~TransformationFromCorrespondences () { };
~TransformationFromCorrespondences () = default;
Copy link
Member

Choose a reason for hiding this comment

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

Same comment here. Base class, with no custom copy/move ctors/assignments nor virtual methods.


/** \brief Constructor. */
inline Correspondence (int _index_query, int _index_match, float _distance) :
index_query (_index_query), index_match (_index_match), distance (_distance)
{}

/** \brief Empty destructor. */
virtual ~Correspondence () {}
virtual ~Correspondence () = default;
Copy link
Member

Choose a reason for hiding this comment

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

This one also does not require declaring the dtor at all.

/** \brief Empty destructor. */
virtual ~PointCorrespondence3D () {}
virtual ~PointCorrespondence3D () = default;
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 also remove this one.

@@ -143,7 +140,8 @@ namespace pcl
Eigen::Affine3f transformation; //!< The transformation to go from the coordinate system
//!< of point2 to the coordinate system of point1
/** \brief Empty destructor. */
virtual ~PointCorrespondence6D () {}
virtual ~PointCorrespondence6D () = default;
Copy link
Member

Choose a reason for hiding this comment

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

And this one.


/** \brief True if no points are invalid (e.g., have NaN or Inf values in any of their floating point fields). */
bool is_dense;
bool is_dense = true;
Copy link
Member

Choose a reason for hiding this comment

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

Note: PCLPointCloud2 is initialized with is_dense set to false but this one is set to true :/

Copy link
Member

Choose a reason for hiding this comment

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

Not nice, but probably also not critical. I think we should change the default in PCLPointCloud2 because hardly anyone constructs that type of point cloud "by hand".

Copy link
Member

Choose a reason for hiding this comment

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

Can you confirm what you wrote? You state one thing but your argument seems to point in the other direction. I interpreted what you wrote as

I think we should not change the default in PCLPointCloud2 because hardly anyone constructs that type of point cloud "by hand".

PCLPointCloud2 comes from ROS messages and these usually means actual sensors which naturally have nans, while the PointClouds are very often handcrafted. Which would actually mean, this was a well thought decision and their difference is intentional.

Copy link
Member

Choose a reason for hiding this comment

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

No, I indeed meant what I wrote. My thought is that since PCLPointCloud2 comes from ROS message, the default-constructed value of this field does not make any difference, it will be set to whatever value the message had. Similarly, when you call toPCLPointCloud2() to create a blob point cloud,is_dense will be set to that of the source cloud.

{}

/** \brief Destructor. */
virtual ~PointCloud () {}
virtual ~PointCloud () = default;
Copy link
Member

Choose a reason for hiding this comment

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

This one can also be removed, same reasons as all the other ones.

@kunaltyagi
Copy link
Member

I meant to review this PR

Sorry. I didn't realize 😅 . We can revert and continue working on this. Or a new one?

@SergioRAgostinho
Copy link
Member

Just a new one, assuming of course you agree with the items I pointed out.

@kunaltyagi
Copy link
Member

Unneeded virtual dtors should be removed. It's good to not pay for what we don't use.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants