While building I suggest cmake --build . --parallel instead of cmake --build . so it uses all your cores for building.
On fedora ostree-devel needs to be installed first (if you do not build it via CMake).
OSTree as Build in Dependency
The best CMake project is a project you clone and it just compiles and grabs all dependencies for you. Besides that you have a known good fixed version of OSTree you are linking against. That's why using system dependencies is not a good idea generally for everything that is fast enough to build.
The following things are required:
sudo dnf install gpgme-devel e2fsprogs-devel fuse-devel
Providing absolute paths for libs is never a good idea. Therefore use a more flexible way of detecting external dependencies. Note for glib it is not really feasible to recompile it during setup and you can expect it being installed since it would take a loooong time and most likely introduce incompatibilities with the system version of glib (as I have experienced not just once :D).
Note: As a rule of thumb, you link against something with PUBLIC in case it's included in a header that can be used by an other component outside your library. Everything else you link PRIVATE.
Clang-Format
Use clang format! You have the config, so please use it ;)
If you are using VSCode, install the clangd extension. Nowadays it's a must have for modern C++ development.
Btw. It also handles automatic header grouping ;)
Header Files
Headers in C++ are called .hpp, not .h. I know we in the past also did not do that. But for all new projects we are actively enforcing it.
This is a perfect case for accessing uninitialised memory in case g_variant_get_child would ever not override the pointer.
Always init it with const char *fingerprint{nullptr};
And since you are working with raw pointers why don't use assert(fingerprint) (#include <cassert>) after each g_variant_get_child?
If you want, you can also take a look at gsl which provides more flexible asserts that are also existent during release builds.
Modern C++ is all about memory safety!
NULL
Don't use NULL, use nullptr in C++. nullptr is a pointer type which is far better in that case.
Clang-Tidy
Again, use clangd (see above). It really gives and teaches you how to write good C++ code in a lot of ways. (c-style casts, else after return, ...)
Something like using namespace ftxui; inside a header file is evil since it pollutes the header space of everyone who includes it.
A local using namespace ftxui; in a closed scope OK. Don't like it (I'm always preferring explicit).
Define Namespaces
Define namespaces! Don't have everything inside the global namespace. Namespaces usually follow the directory structure.
Example: Files inside src/core should be inside the otui::core namespace (if you use otui as an abbreviation for ostree-tui).
I really like the way you structure your code and who you document it. This is really something you can build up on top of. It really looks like your first real "large" C++ project, but that's good. You learn and I'm trying to push you a bit into the right directions here and there :)
README.md
cmake --build . --parallel
instead ofcmake --build .
so it uses all your cores for building.ostree-devel
needs to be installed first (if you do not build it via CMake).OSTree as Build in Dependency
The best CMake project is a project you clone and it just compiles and grabs all dependencies for you. Besides that you have a known good fixed version of OSTree you are linking against. That's why using system dependencies is not a good idea generally for everything that is fast enough to build.
The following things are required:
sudo dnf install gpgme-devel e2fsprogs-devel fuse-devel
Then here is a git patch for it:
PKG Config For GLib
Providing absolute paths for libs is never a good idea. Therefore use a more flexible way of detecting external dependencies. Note for glib it is not really feasible to recompile it during setup and you can expect it being installed since it would take a loooong time and most likely introduce incompatibilities with the system version of glib (as I have experienced not just once :D).
Here is a patch for it:
Note: As a rule of thumb, you link against something with
PUBLIC
in case it's included in a header that can be used by an other component outside your library. Everything else you linkPRIVATE
.Clang-Format
Use clang format! You have the config, so please use it ;) If you are using VSCode, install the clangd extension. Nowadays it's a must have for modern C++ development.
Btw. It also handles automatic header grouping ;)
Header Files
Headers in C++ are called
.hpp
, not.h
. I know we in the past also did not do that. But for all new projects we are actively enforcing it.C++ Time Types
If you create a C++ wrapper, use C++ types. Use
std::chrono::time_point
instead of this: https://github.com/AP-Sensing/ostree-tui/blob/300a02866ee6619ebfbadead4b4779ab2ca339af/src/util/cpplibostree.h#L32C1-L32C26If you need help with parsing them, take a look at the
date.h
lib which is just awesome and even slowly but surely gets integrated into C++. https://github.com/HowardHinnant/date/blob/master/include/date/date.hIt has really great CMake fetch_content support. Just take a look at our internal projects ;)
Uninitialised Types
https://github.com/AP-Sensing/ostree-tui/blob/300a02866ee6619ebfbadead4b4779ab2ca339af/src/util/cpplibostree.cpp#L154-L157
This is a perfect case for accessing uninitialised memory in case
g_variant_get_child
would ever not override the pointer. Always init it withconst char *fingerprint{nullptr};
And since you are working with raw pointers why don't use
assert(fingerprint)
(#include <cassert>
) after eachg_variant_get_child
?If you want, you can also take a look at
gsl
which provides more flexible asserts that are also existent during release builds. Modern C++ is all about memory safety!NULL
Don't use
NULL
, usenullptr
in C++.nullptr
is a pointer type which is far better in that case.Clang-Tidy
Again, use clangd (see above). It really gives and teaches you how to write good C++ code in a lot of ways. (c-style casts, else after return, ...)
==
Checkhttps://github.com/AP-Sensing/ostree-tui/blob/300a02866ee6619ebfbadead4b4779ab2ca339af/src/main.cpp#L11
Again, potential for a out of bounds access. If I manage to invoke your prog with
-1
asargc
, I can do everything.Always check ranges when ever you can and avoid
==
. In this case useif (argc <= 0) {
.CMake File Globs
https://github.com/AP-Sensing/ostree-tui/blob/main/src/core/CMakeLists.txt
Evil! Since this can lead to a loooot of bugs! Always specify all files explicitly in CMake. Example: https://github.com/COM8/aps-ui/blob/main/src/ui/widgets/CMakeLists.txt
Using namespaces In Headers
Something like
using namespace ftxui;
inside a header file is evil since it pollutes the header space of everyone who includes it. A localusing namespace ftxui;
in a closed scope OK. Don't like it (I'm always preferring explicit).Define Namespaces
Define namespaces! Don't have everything inside the global namespace. Namespaces usually follow the directory structure. Example: Files inside
src/core
should be inside theotui::core
namespace (if you useotui
as an abbreviation forostree-tui
).Lambdas
Yes, lambdas are cool I know, but a private function is also cool sometimes if it just appears inside a
.cpp
file or an anonymous namespace ;). Ref: https://releases.llvm.org/16.0.0/tools/clang/tools/extra/docs/clang-tidy/checks/misc/use-anonymous-namespace.htmlClosing Remarks
I really like the way you structure your code and who you document it. This is really something you can build up on top of. It really looks like your first real "large" C++ project, but that's good. You learn and I'm trying to push you a bit into the right directions here and there :)
Keep up the good work.