PointCloudLibrary / pcl

Point Cloud Library (PCL)
https://pointclouds.org/
Other
9.98k stars 4.62k forks source link

Check if classes are in correct namespaces #2709

Closed SunBlack closed 4 years ago

SunBlack commented 5 years ago

During dumping symbols of a PCL library, I saw some classes are not in correct namespace.

Example: DifferenceOfNormalsEstimation is only in namespace pcl and not in pcl::features like it should, because this class is part of module pcl_features.

Fastes way to find this "bad" classes should be using (MSVC example)

dumpbin /exports  pcl_features_release.lib

And then search for all lines which does not contain e.g. pcl::features.

This would be a breaking change! Maybe this is a good time to do so, because of switch to C++14 it is maybe better to change next version number from 1.10 to 2.0 (even C++14 alone is a reason for me to do so ;) ).

taketwo commented 5 years ago

Enabling C++14 flags is not breaking API, thus does not warrant for major version bump.

Historically most of the classes are in the root pcl namespace. Some are in nested namespace. Yes, this is not consistent. Should we change this? In my opinion, no. We will gain close to nothing by shuffling classes around, but will piss off lots and lots of people by breaking their code.

SunBlack commented 5 years ago

Enabling C++14 flags is not breaking API, thus does not warrant for major version bump.

True, but some old compilers won't work anymore, so you can count it as major change and currently there are a lot of changes to PCL (e.g. regarding boost). So wanted only say maybe it is a good time to do breaking changes since there are/will be a lot of changes internally.

Historically most of the classes are in the root pcl namespace. Some are in nested namespace. Yes, this is not consistent. Should we change this? In my opinion, no. We will gain close to nothing by shuffling classes around, but will piss off lots and lots of people by breaking their code.

True, but now we piss off new users, if they see a class in a module, but they have always to look into documentation/code in which namespace it is.

SunBlack commented 5 years ago

I have an idea how we can change this (not tested):

Before

namespace pcl
{
  class DifferenceOfNormalsEstimation ...
}

After

namespace pcl
{
  namespace features
  {
    class DifferenceOfNormalsEstimation ...
  }
  using DifferenceOfNormalsEstimation [[deprecated]] = features::DifferenceOfNormalsEstimation;
}
taketwo commented 5 years ago

Thanks to C++11, this approach will work (before we would need to use typedef and it does not support template parameters).

That said, I'm still in favor of maintaining status quo. The change you proposed will result in 90% of the library classes and functions marked as deprecated. Just imagine how much effort it will be for existing users to update all their projects...

SunBlack commented 5 years ago

Just imagine how much effort it will be for existing users to update all their projects...

As long as you don't treat deprecation warning as error, it doesn't hurt. And for new users it's better if we fix it.

Currently I'm waiting for #2376, so I don't need to fix indention manually (this is one thing I don't like about PCL style guide. indention of namespace xD)

taketwo commented 5 years ago

As long as you don't treat deprecation warning as error, it doesn't hurt.

This being a good practice, I would assume a lot of people do treat warnings as errors. Then they need to update their flags to exclude deprecation warnings. And then their build output is still flooded with deprecation warnings... This is a mess.

And for new users it's better if we fix it.

Is it? As I mentioned earlier, most of PCL stuff is in the root namespace. By default, if you a thinking about a function or a class, it will be in pcl::. Off the top of my head, exceptions are only io:: and visualization::. Not so much trouble to learn this. And most people use IDEs nowadays, which helps.

SunBlack commented 5 years ago

And for new users it's better if we fix it.

Is it? As I mentioned earlier, most of PCL stuff is in the root namespace. By default, if you a thinking about a function or a class, it will be in pcl::. Off the top of my head, exceptions are only io:: and visualization::. Not so much trouble to learn this. And most people use IDEs nowadays, which helps.

It is as long as it is not consistently. It doesn't matter if we have always pcl::<module>::<class> or pcl::<class> - just mixing it is ugly.

SergioRAgostinho commented 5 years ago

Once more late to this party.

I tend to prefer pcl::<class>, as I like to explicitly write the namespace to quickly identify "what is coming from where". pcl::<namespace>:: becomes cumbersome quickly. That being said, two comments linger in the back of my mind that would require checking:

Regarding pissing off-users, I'm not sure on how to address this. Once we decide on a convention I believe I would first created all required "aliases" so that users could employ the new naming convention once it's settled. After that I would start deprecating the more obscure modules first. Maybe one module every six months?

Anyway, this is very low priority for me.

Morwenn commented 4 years ago

Late point of view: the pcl::<namespace>::, if applied consistently, could really help discoverability. Knowing that feature from module MMM lives in pcl::mmm:: means that we don't have to check over and over again all the time, which is convenient. It would also make it obvious which components would appear/disappear when passing a BUILD_mmm flag to CMake, and very easy to check what parts of the code would be affected with a simple grep pcl::mmm command.

I was bitten recently because the grabbers don't all live in the same namespace. For these components specifically I wouldn't mind them all being in pcl::io:: because the nested namespace names are short and I generally write a grabber's class name once or twice and then just use member functions on an instance, so adding it there isn't bothersome.

Now I'm also sympathetic with the fact that common function and class names are more easily accessible if they live directly in pcl:: - having to repeat some names over and over again is sometimes annoying, but that's also why C++ has using directives to import common names in the local scope.

I think that a first step would be to put all the affected components in their module's namespace and to provide aliases to avoid breaking the current API. It would allow people who wish to fully qualify names - for any of the reasons I mentioned in the first paragraph - to do so, and wouldn't break any code. If you don't want to break ABI either it can be done the other way around by making the subnamespace-level features aliases of the ones in pcl::. Deprecation can come later if needed or never.

kunaltyagi commented 4 years ago

I don't know how important this party is 🎉 , but we can take a look at other libraries:

For large implementations, we have

Based on this, the core of PCL should not be in a namespace. Specialized use/impl can be sent in pcl::module. A reorganization of modules would be most helpful here. The best example I can think of are code relying on a platform specific API. To name a few:

In other cases such as for KdTree, pcl::kdtree::KdTree doesn't make much sense since the name of class and module name are similar (stuttering is not ideal to type). Creating an 'acceleration' namespace for all acceleration data structures would be better.

Some consideration should be given to how "healthy" a namespace is. There should be sufficient items to merit a namespace. (Should there be an "bloatedness" concept too? Maybe no) Of course, if there is any scope of confusion, it's better to have a namespace to disambiguate the meaning.

We should also strive to keep the class names similar across namespaces. pcl::XYZ and pcl::gpu::XYZ_gpu don't really help the user, since pcl::XYZ and pcl::gpu::XYZ would provide a nicer overall API. In the same vein, I think pcl::openni::Grabber would make more sense than pcl::io::OpenNiGrabber (since it should be easy to tell what's available and what's not by looking at some compile definitions/macros provided by PCL)

As a library, it shouldn't matter if things are difficult for the authors. As a library, it is a sort of contract to make things as easy as possible for the end-user. If that means using namespaces, use it (aka Microsoft for developers familiar with .NET) and if it means not using namespaces, don't use (stdlib). If each module is independent then let each module have a namespace (aka Boost). Large number of module_a::XYZ in module B would imply they need better cohesion or reorganization.

The approach of std::chrono::literals should also be looked at. The same types are available via multiple locations. This implies that we can provide the same type via pcl::stereo::io::XYZ as well as pcl::io::XYZ if need be.

I have no stand on how to proceed with the issue, just some observations that might help.

Morwenn commented 4 years ago

Concerning Boost and the standard library there are a few more things to consider (once again it's just informative, not a proposal):

taketwo commented 4 years ago

Thanks for the discussion. Clearly, there are numerous valid arguments in favor of both approaches. If we were to start writing this library from scratch, it wouldn't be an easy decision. However, the reality is that we have a mature code base. In my opinion, this tips the balance towards flat namespacing as this is what we (mostly) already have.

stale[bot] commented 4 years ago

Marking this as stale due to 30 days of inactivity. It will be closed in 7 days if no further activity occurs.

kunaltyagi commented 4 years ago

Shall we consider the discussion as over and the conclusion as "maintain status quo"?

SergioRAgostinho commented 4 years ago

I'm ok with that decision.