Collection of utilities and enhanced data structures for a wide range of C++ projects in a single header file kaizen.h
In pretty much any project written in C++, most of the time only about 1% of the code needs to execute super-fast. While the other 99% doesn't, it usually takes up most of the overall development time by far and, as any code, must therefore be written, read, understood and modified fast. One of the main goals of this library is to serve precisely that need of that 99%.
Over time, functions and tools similar to what can be found in this library make their way into the Standard. For
obvious and often justifiable reasons, that process is slow, but even more unfortunately - even after they become
available in the Standard, in practice they are not available in an average production code for at least another
decade. The second goal of this library, therefore, is to make them available to those who want them today:
simply #include "kaizen.h"
and off you go.
Kaizen does not aim to be neither radiation-hardened nor generic for all imaginable scenarios and use cases right from the get-go. Instead, inspired by the Japanese concept of Kaizen, emphasizing frequent and gradual improvement, this library builds on top of STL and aims to provide a malleable and growing set of practical, simple and well-tested tools through a single header file that, like a Swiss army knife, includes just enough of everything that can be useful for a broad range of C++ projects right off the bat.
So, for example, even though STL containers were not meant to be derived from (in particular, their destructors
are not virtual), zen::string
derives from std::string
in order to quickly, without having to implement all
the conversion operators and delegate functions that a composition-based approach would require, provide the ability
to convert to and from std::string
at any point in the codebase whenever there's need for the richer interface
of working with strings that zen::string
provides. Nevertheless, zen::string
and similarly derived types in zen
do have all the necessary restrictions in place to prevent such dynamic allocations just in case.
This approach is rooted in the philosophy that in the vast majority of cases and projects, the benefits from these
utilities far outweigh any theoretical dangers of for some reason allocating zen::string
itself dynamically and
then deleting it through a pointer to base std::string
, whether accidentally or on purpose. Any codebase is far more
likely to experience problems and corrosion from the regular breed of bugs that are much easier to make accidentally.
The latest kaizen.h
release header can be downloaded from releases.
The development version is generated during the build, see below.
Here's a taste of what you can do with Kaizen right out of the box:
Parse program arguments declaratively:
#include "kaizen.h"
int main(int argc, char* argv[])
{
zen::cmd_args args(argv, argc);
bool verbose = args.accept("-verbose").is_present();
bool ignore = args.accept("-ignore" ).is_present();
// For: -copy from/some/dir to/some/dir
args.accept("-copy");
args.get_options("-copy")[0] // "from/some/dir"
args.get_options("-copy")[1] // "to/some/dir"
// Or sometime later
if (args.is_present("-ignore"))
}
Open a file and read any line right away:
zen::file license_text("../LICENSE.txt"_path);
zen::string version = license_text.getline(1);
zen::string license = license_text.getline(3);
Python-like range notation:
for (int i : zen::in(5)) // i from 0 to 4
for (int i : zen::in(1, 10)) // i from 1 to 9
for (int i : zen::in(0, 10, 2)) // i from 0 to 8, step 2
Python-like substring extractions:
// indices ----> 012345678912345
zen::string z = "Test substrings";
z.substring( 0, 4) == "Test"); // both arguments are indices
z.substring(-20, 4) == "Test"); // negative indices are okay
z.substring(100, 300) == ""); // out-of-bounds indices are okay too
z.substring( 0, -5) == "Test subst"); // just like in Python
z.substring( 5, 50) == "substrings"); // just like in Python
// A drop-in replacement for std::string
std::string x = z; z = x; // and so on
Replace a substring:
z = "I love apples, apples, apples";
z.replace( "apples", "oranges"); // "I love oranges, apples, apples"
z.replace_all("apples", "oranges"); // "I love oranges, oranges, oranges"
Python-like printing:
std::vector<int> v = {1, 2, 3}
zen::print(v); // [1, 2, 3]
zen::print(v, "4", 5); // [1, 2, 3] 4 5
Richer containers with many useful functions:
zen::vector<int> v; // declare & use just like std::vector
zen::generate_random(v); // randomly populate anything resizable & iterable
if (v.contains(42)) { // easily check for containment
zen::sum(v); // easily sum up anything iterable with addable elements
}
// A drop-in replacement for std::vector
std::vector x = v; v = x; // and so on
Many more examples can be found here.
Circles signify a potential scale of involvement in terms of time and/or energy. Green circles signify the easy stuff, essentially requiring no coding. Yellow circles signify a potential need to code, although quite often a suggestion would do here as well. Red circles signify a clear need to code.
- π’ Review code & commits. Our commits follow the RAPID Practice and therefore are very easy to review.
- π’ Suggest a utility. If you have an idea for a common C++ utility code, feel free to suggest in our discussions.
- π’ Document. If you see any missing documentation or a discrepancy (wrong code, etc.).
- π‘ Generalize a utility. If you see how to generalize any code in a meaningful way, like this.
- π‘ Optimize. If you see how to optimize any code in a meaningful way.
- π‘ Harden. If you see how to improve the robustness of any code (unhandled edge cases, etc.).
- π‘ Reduce LOC. If you see ways to reduce lines of code (common sense - with no loss of readability).
- π΄ Implement a utility. If you have a commonly useful utility piece of code.
- π΄ Resolve an issue. Some are in the form of a "TODO" in code.
- π΄ Add tests. There can never be enough tests that cover anything previously not covered.
- π΄ Automate. If you see ways to automate any process and thus save iteration time.
- See our Communication page for our approach to communication.
- See RAPID Practice for conventions on issues & commit messages.
You'll need the GCC compiler for Linux or MSVC (comes with Visual Studio) for Windows development.
The project is probably more widely and more backward-compatible, but at the moment is being developed and tested with the following tools:
- Python 3.11
- MSVC 19.37
- GCC 9.4.0
Open the repo folder in your favorite IDE (on Windows, if you're not very used to Visual Studio,
I recommend using Visual Studio Code with WSL) and follow the steps described below to run it on your system.
The main()
function simply runs the tests and prints out the report (see a sample screenshot below).
Build & Run on Windows & Linux (including WSL)
-
Open a Terminal: You can open a terminal window by searching for "Terminal" in your Linux desktop's application menu or by using the keyboard shortcut (usually
Ctrl+Alt+T
). -
Navigate to Project Directory: Use the
cd
command to navigate to the directory where your project'sCMakeLists.txt
file is located. Replacepath/to/your/cloned/kaizen
with the actual path:cd path/to/your/cloned/kaizen
-
Create a Build Directory: It's a good practice to create a separate directory for the build files to keep them isolated from the source files. Inside your project directory, run:
mkdir build cd build
-
Configure the Project with CMake: Now, from inside the
build
directory, run CMake to configure the project. This will read theCMakeLists.txt
file and generate the necessary Makefiles for building the project. The..
at the end of the command tells CMake to look in the parent directory for theCMakeLists.txt
file:cmake ..
This will generate convenience utility scripts runbuild_win.bat
and runbuild_linx.sh
that combine the next steps 5 and 6, so you can simply call the corresponding script for your environment.
NOTE: This will only work smoothly either within a Linux environment (including WSL) or from within a Windows terminal that's integrated
into Visual Studio (not Visual Studio Code), where the integrated terminal automatically sets up all the necessary environment variables (pointing to the MSVC compiler cl.exe
, etc.) that are
automatically configured when you run Visual Studio's own developer command prompt.
If you run this from a Windows terminal inside another IDE like Visual Studio Code, you will probably get an error that looks like this:
-- Building for: NMake Makefiles
-- The C compiler identification is unknown
-- The CXX compiler identification is unknown
CMake Error at CMakeLists.txt:11 (project):
The CMAKE_C_COMPILER:
cl
is not a full path and was not found in the PATH.
If you want to develop using MSVC compiler from Visual Studio Code, there are various ways of setting that up, please see online.
If you need to specify a particular version of g++ or any other build options, you can do so with additional arguments. For example, to set the C++ compiler to g++, you could use:
cmake -DCMAKE_CXX_COMPILER=g++ ..
-
Compile the Project: After configuring, you can compile the project with:
cmake --build .
On Linux, you can also call
make
directly (does the same thing). -
Run the Executable: If the build is successful, you can run the resulting executable from the build directory.
Linux:
./kaizen
Windows:
.\kaizen.exe
A build and run will produce a console output that will look like this: