-
Notifications
You must be signed in to change notification settings - Fork 89
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
Feature/coding conventions #544
Conversation
Codecov Report
@@ Coverage Diff @@
## master #544 +/- ##
==========================================
- Coverage 78.03% 78.02% -0.01%
==========================================
Files 229 229
Lines 35016 35016
==========================================
- Hits 27324 27322 -2
- Misses 7692 7694 +2
... and 1 file with indirect coverage changes 📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more |
documents/development/README.md
Outdated
- Private header files in a `private` and `src` directory. | ||
- Source files in a `src` directory. | ||
- For the Apache Celix framework and utils lib only header-only C++ files are allowed. | ||
- Prefer header-only C++ files for the Apache Celix libraries |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't this and the line above it contradict each other?
First sentence: For the Apache Celix framework and utils lib only
=> I read this as "header-only C++ files are only allowed for the Apache Celix framework and utils lib"
Prefer header-only C++ files for the Apache Celix libraries
=> I read this as "header-only C++ files are preferred for all Apache Celix libraries".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the former item refers to Celix::framework
and Celix::utils
, while the latter item refers to other libraries.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I moved the two lines to ### C++ Libaries
and tried to clarify them a bit more.
- Target name should be `CamelCase` (starting with a capital). | ||
- There should be `celix::` prefixed aliases for the library. | ||
- C++ Libraries should support C++14. | ||
- Exception are `celix::Promises` and `celix::PushStreams` which requires C++17. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do we do when switching to newer C++ versions? Update the coding guidelines? Do we need to mention the C++ version in the coding guidelines? Is that not more of a technical 'setting'?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO updating the guidelines, this should be a living document.
## Benchmarking | ||
|
||
- When needed, use benchmarking to measure performance. | ||
- Use the Google Benchmark framework for benchmarking. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why only aim at using Google Benchmark? If someone finds a better tool to do the job, that's up to them IMO (up to the user).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To keep the external libs a bit more manageable. If there is a good reason to use a different benchmark lib, I see no problem with updating the guidlines.
/cc @wopss maybe you will find this an interesting PR as well ;) |
|
||
- Use `CamelCase` (starting with a capital) for class names. | ||
- Use descriptive names for classes. | ||
- Classes should be part of a `celix::` namespace or sub `celix::` namespace. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How deep can a namespace be? Can I do something like celix::custom_implemntation_detail::some_other_namespace
or just a celix::detail
is more than enough for an implementation specific class?
I think it is worth mentioning if you prefer the namespaces to be max 2 level deep, e.g. celix::
and celix::detail
. I think everything that is "implementation" specific and not part of the public API should be in celix::detail
(max. 2 levels).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer impl
over detail
for implementation details, because detail is more logical to be used broader than for implementation details .
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is also good. But I strongly suggest detail
for private implementation details.
The reason I suggest detail
is because it is widely used in C++, most libraries (e.g. boost, fmtlib, spdlog) are using the following "convention":
libname::
- This is for public API, which includes everything the user of the library is intended to interact with.libname::detail
- This is for implementation details that are not meant to be seen or used directly by the user of the library.
- Use `enum class` instead of `enum`. | ||
- Use `SNAKE_CASE` for enum values without a celix/class prefix. Note that for enum values no prefix is required | ||
because enum class values are scoped. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a preference for the base type of an enum? E.g.:
enum class ServiceRegistrationState : std::int8_t
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No not yet. Is there a good reason to do this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly storage space. By default enum class
is 4 bytes, if not all values are used then the rest of the space is wasted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actual space depends of course on alignment, but if there are no real downsides to using a std::int8_t
, I think this is a good idea. I will update this.
.clang-format
Outdated
ConstructorInitializerIndentWidth: 4 | ||
Cpp11BracedListStyle: true | ||
DerivePointerAlignment: false | ||
IncludeBlocks: Regroup |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I looked into clang-format 15 and it has the following for LLVM:
IncludeCategories:
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 2
CaseSensitive: false
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
Priority: 3
CaseSensitive: false
- Regex: '.*'
Priority: 1
CaseSensitive: false
which will result in STL headers being at the end of the include list, is this something that is preferred? I would recommend something like (based on LLVM configuration) this in a header or source file:
#include <HEADER_OF_CPP_IF_NEEDED>
#include <STL_HEADER_1>
#include <STL_HEADER_2>
#include <OTHER_LIB_HEADER_1>
#include <OTHER_LIB_HEADER_2>
#include <THIRD_PARTY_HEADERS>
I would even set this to Preserved
and let the developer decide, or create a custom IncludeCategories
block.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I personally are not a fan of organizing headers manually, so I prefer adding a IncludeCategories section.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AFAIK, there is a case where manually organizing headers can be useful: for each .h/.c pairs, e.g., celix_bundle_cache.h(.c), always include the header file (celix_bundle_cache.h
) in the first line of the non-comment part of the source file (celix_bundle_cache.c
) to make sure the header file is self-contained, which means it can be used alone. Self-contained header file is really nice.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
clang-format does take into account. So when you run formatting over a foo.c
source file and there is a include of foo.h
this will be included first.
This should even be extendable with the IncludeIsMainRegex
option, but I could not get that to work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've consistently encountered problems with this setting while developing libraries, particularly when using angle brackets (<>
) to include header files within the library. However, these problems don't seem to occur when using double quotes (""
).
E.g.:
// lib/detail/header1.cpp
#include <lib/detail/header1.hpp>
#include <lib/header2.hpp>
#include <lib/header3.hpp>
#include <memory>
#include <spdlog/spdlog.hpp>
will get reorganized in:
// lib/detail/header1.cpp
#include <memory>
#include <lib/header2.hpp>
#include <lib/header3.hpp>
#include <lib/detail/header1.hpp> // This header should be at the top of the file in my opinion.
#include <spdlog/spdlog.hpp>
while, the following:
// lib/detail/header1.cpp
#include "lib/detail/header1.hpp"
#include "lib/header2.hpp"
#include "lib/header3.hpp"
#include <memory>
#include <spdlog/spdlog.hpp>
will get reorganized in:
// lib/detail/header1.cpp
#include "lib/detail/header1.hpp"
#include <memory>
#include "lib/header2.hpp"
#include "lib/header3.hpp"
#include <spdlog/spdlog.hpp>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I purpose to not regroup includes header for now.
I will leave this as comments, so that it can be used if desired on case per case basis.
If needed we can revisit this at a later stage.
Good job for writing all of these! Since some things can be overseen in reviews I suggest to setup a workflow to check the formatting of new C++ code. If this is a good idea I can submit a PR for it. |
Thanks. |
Also updates some source files to test and show clang-format header grouping result.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
…entions # Conflicts: # libs/framework/src/bundle_archive.c
…entions # Conflicts: # libs/utils/include/celix/Filter.h
If no further comments, I suggest give the clang-format file a try. @xuzhenbao |
While I would prefer additional reviewers, this pull request has remained open for an extended period, so I'm going to merge it now. If there are any suggestions for updating the coding conventions, these can be implemented in a separate pull request. |
This PR introduces an Apache Celix Coding Convention document along with a .clang-format file.
These additions aim to streamline Celix development by promoting more readable and maintainable code.
Please consider this PR as a proposal and feel free to contribute suggestions for changes. However, if this PR is merged, compliance with the coding conventions and format style is encouraged during pull request reviews.
For this particular PR, I would appreciate receiving some more reviews to ensure that the proposed changes are widely accepted by the community.