Below you can find various examples of code snippets that demonstrate the capabilities of Kaizen.
Simply include the kaizen.h
header. No additional setup is required.
During development, the kaizen.h
header is generated during build (see below for building).
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
Our benchmarks consistently show that, for optimized builds, not only is there zero overhead from using
zen::in()
instead of a raw loop, but sometimes zen::in
even ends up slightly faster (yes, faster)
than the raw loop for both MSVC (the Visual Studio compiler) as well as GCC/Clang.
Compiler optimizations can be full of surprises.
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( 0, -5) == "Test subst"); // just like in Python
z.substring(100, 300) == ""); // out-of-bounds indices are okay too
z.substring( 5, 50) == "substrings"); // just like in Python
Rich extraction methods from strings, both arbitrary patterns and common cases:
z = "Hey, [Hello World] 1.2.3 amazing 1/2/2023";
z.starts_with("Hey")) // true
z.extract_between('[', ']'); // "Hello World"
z.extract_version(); // "1.2.3"
z.extract_pattern(R"((\d+\.\d+\.\d+))"); // "1.2.3"
z.extract_date(); // "1/2/2023"
z.extract_pattern(R"((\d+\/\d+\/\d+))"); // "1/2/2023"
// 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"
Remove a substring:
z = "Some uninteresting text";
z.remove("uninteresting "); // "Some text"
Trim whitespaces:
z = " Trim me ";
z.trim(); // "Trim me" - from leading & trailing empty spaces
z.deflate() // "Trim me" - any adjacent spaces are removed
Repeat a pattern of a string:
zen::repeat("/", 10) == "//////////";
zen::repeat(10, "*") == "**********";
Just give me a simple random number for everyday use:
int n = zen::random_int(); // by default between 0 and 10
int m = zen::random_int(1, 5) // or specify another range
Python-like printing with additional logging utilities:
std::vector<int> v = {1, 2, 3}
zen::print(v); // [1, 2, 3]
zen::print(v, "4", 5); // [1, 2, 3] 4 5
Or even:
std::vector<std::vector<int>> v = { {1, 2}, {3, 4} };
zen::print(v); // [[1, 2], [3, 4]]
In addition, zen::log()
is equivalent to zen::print()
except that it will automatically append a new line:
zen::log(v, "4", 5); // equivalent to zen::print(v, "4", 5, '\n');
In general, zen::log()
calls zen::print()
, which, in turn, calls zen::to_string()
, which is able to convert
to a string a wide range of objects that are intuitively expected to be convertible to a string. For example:
std::vector<int> v = { 1, 2, 3 };
zen::to_string(v); // [ 1, 2, 2 ]
With arbitrary nestedness of any containers:
std::vector<std::list<std::vector<int>>> vxv = { {{1, 2}, {3, 4}}, {{5, 6}, {7, 8}} };
zen::to_string(vxv); // [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
Richer containers with many useful functions:
zen::vector<int> v;
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
The standard container::empty()
can ambiguously be read as a verb, so Kaizen provides is_empty()
:
if (v.is_empty()) // same as v.empty()
if (zen::is_empty(c)) // same as c.empty(), works with any iterable container c
Sprinkle around some test cases with ZEN_EXPECT
accepting any expression and reporting it if it fails:
const zen::string z = "Test Case";
const zen::vector v = {1, 2, 3, 4};
ZEN_EXPECT(z.ends_with("Case")); // pass
ZEN_EXPECT(v.contains(7)); // fails, prints: CASE FAIL: ... EXPECTED: v.contains(7)
A static assert that shows the expression that failed:
// Will show something like:
// 'ZEN STATIC ASSERTION FAILED. "FAILED EXPRESSION:": zen::is_iterable_v<int>'
ZEN_STATIC_ASSERT(zen::is_iterable_v<int>, "FAILED EXPRESSION:");
A simple timer:
// Let's benchmark zen::in
zen::timer timer;
for (int i : zen::in(N)) {
// Some computation
}
zen::log(timer.stop().duration_string());
Semantic versioning:
zen::version v1(1, 2, 3, 4567); // construct from numbers
// Expect
v1.major() == 1;
v1.minor() == 2;
v1.patch() == 3;
v1.build() == 4567;
zen::version v8("8.2.3.4567"); // construct from a string
// Expect
v8.major() == 8;
v8.minor() == 2;
v8.patch() == 3;
v8.build() == 4567;
// Expect
v1 != v8;
v1 < v8;
v8 > v1;
using namespace zen::literals::version;
auto v7 = "7.6.5.4321"_version; // construct using string literals
v7.build() == 4321;
Dereference any level of nested pointers:
int _1 = 11;
int* _2 = &_1;
int** _3 = &_2;
int*** _4 = &_3;
int**** _5 = &_4;
int***** _6 = &_5;
// and so on
zen::deref(_1); // 11
zen::deref(_3); // 11
zen::deref(_6); // 11