## Small features ___ ## operator<=> Since C++20 we don't need to define all comparison operators for custom structures. Instead of `==`, `!=`, `>`, `<`, `<=`, `>=` we can simply write one universal comparator `<=>`. ```C++ struct Student { auto operator<=>(const Student& other) const = default; std::string name_; double average_; int index_; }; int main() { Student student1{.name_ = "Mateusz", .average_ = 4.67, .index_ = 456123}; Student student2{.name_ = "Michał", .average_ = 4.57, .index_ = 123456}; Student student3{.name_ = "Marcelina", .average_ = 4.67, .index_ = 321456}; Student student4{.name_ = "Mirosław", .average_ = 4.47, .index_ = 135246}; std::cout << "student1 < student2 ? " << (student1 < student2) << '\n'; // True std::cout << "student1 < student3 ? " << (student1 < student3) << '\n'; // False std::cout << "student1 < student4 ? " << (student1 < student4) << '\n'; // True std::cout << "student2 < student3 ? " << (student2 < student3) << '\n'; // False std::cout << "student2 < student4 ? " << (student2 < student4) << '\n'; // True std::cout << "student3 < student4 ? " << (student3 < student4) << '\n'; // True std::cout << "student1 == student1 ? " << (student1 == student1) << '\n'; // True } ``` ___ ## How it works? First, we need to understand how the compiler treats the structure `Student`. This structure has 3 fields: `name`, `average`, and `index`. During the comparison, the first field is to compare if both values are the same, if true we reach a second one, and so on. In other words, first different fields decide which structure is lower. ```C++ int main() { Student student1{.name_ = "Mateusz", .average_ = 4.67, .index_ = 456123}; Student student2{.name_ = "Mateusz", .average_ = 4.67, .index_ = 123456}; Student student3{.name_ = "Mateusz", .average_ = 4.57, .index_ = 321456}; Student student4{.name_ = "Mateusz", .average_ = 4.57, .index_ = 135246}; std::cout << "student1 > student2 ? " << (student1 > student2) << '\n'; // T (higher index) std::cout << "student1 > student3 ? " << (student1 > student3) << '\n'; // T (higher average) std::cout << "student1 > student4 ? " << (student1 > student4) << '\n'; // T (higher average) std::cout << "student2 > student3 ? " << (student2 > student3) << '\n'; // T (higher average) std::cout << "student2 > student4 ? " << (student2 > student4) << '\n'; // T (higher average) std::cout << "student3 > student4 ? " << (student3 > student4) << '\n'; // T (higher index) std::cout << "student1 == student1 ? " << (student1 == student1) << '\n'; // T (the same person) } ``` ___ ## But I don't want to compare some fields... When you don't want to compare one or more fields, you need to write your own comparator. ```C++ struct Student { std::partial_ordering operator<=>(const Student& other) const { if (const auto res = average_ <=> other.average_; res != 0) { return res; } return name_ <=> other.name_; } }; int main() { Student student1{.name_ = "Mateusz", .average_ = 4.67, .index_ = 456123}; Student student2{.name_ = "Mateusz", .average_ = 4.67, .index_ = 123456}; Student student3{.name_ = "Mateusz", .average_ = 4.57, .index_ = 321456}; std::cout << "student1 > student2 ? " << (student1 > student2) << '\n'; // T (higher index) std::cout << "student1 < student3 ? " << (student1 < student3) << '\n'; // F (lower average) // This line do not compile :( std::cout << "student1 == student1 ? " << (student1 == student1) << '\n'; // T (the same person) } ``` But why do we wrote`std::partial_ordering` instead of `auto`? It's because we compare two floating point values. I know that earlier we wrote `auto operator<=>` and it's worked, but this was fully generated by the compiler. If we are writing it manually and we need to specify what result we return. ___ ## Types of ordering * strong_ordering: a total ordering, where equality implies substitutability (that is (a <=> b) == strong_ordering::equal implies that for reasonable functions f, f(a) == f(b). “Reasonable” is deliberately underspecified – but shouldn’t include functions that return the address of their arguments or do things like return the capacity() of a vector, etc. We want to only look at “salient” properties – itself very underspecified, but think of it as referring to the value of a type. The value of a vector is the elements it contains, not its address, etc.). The values are strong_ordering::greater, strong_ordering::equal, and strong_ordering::less. * weak_ordering: a total ordering, where equality actually only defines an equivalence class. The canonical example here is case-insensitive string comparison – where two objects might be weak_ordering::equivalent but not actually equal (hence the naming change to equivalent). * partial_ordering: a partial ordering. Here, in addition to the values greater, equivalent, and less (as with weak_ordering), we also get a new value: unordered. This gives us a way to represent partial orders in the type system: 1.f <=> NaN is partial_ordering::unordered. ___ ## Ok but what with operator== beacuse we have custom `operator<=>`, which return `std::partial_ordering` we can't use `operator==`. We need to provide own `operator==`. And we can do this by default (compare all fileds) or manually (compare specific fileds). ```C++ struct Student { std::partial_ordering operator<=>(const Student& other) const { if (const auto res = average_ <=> other.average_; res != 0) { return res; } return name_ <=> other.name_; } bool operator==(const Student& other) const { return name_ == other.name_ && average_ == other.average_; } std::string name_; double average_; int index_; }; int main() { Student student1{.name_ = "Mateusz", .average_ = 4.67, .index_ = 456123}; Student student2{.name_ = "Mateusz", .average_ = 4.67, .index_ = 123456}; std::cout << "student1 > student2 ? " << (student1 > student2) << '\n'; // T (higher index) std::cout << "student1 == student1 ? " << (student1 == student1) << '\n'; // T (the same person) } ``` ___ We can also decide to use default `operator==` and this also work! ```C++ struct Student { std::partial_ordering operator<=>(const Student& other) const { if (const auto res = average_ <=> other.average_; res != 0) { return res; } return name_ <=> other.name_; } bool operator==(const Student&) const = default; std::string name_; double average_; int index_; }; int main() { Student student1{.name_ = "Mateusz", .average_ = 4.67, .index_ = 456123}; Student student2{.name_ = "Mateusz", .average_ = 4.67, .index_ = 123456}; std::cout << "student1 > student2 ? " << (student1 > student2) << '\n'; // T (higher index) std::cout << "student1 == student1 ? " << (student1 == student1) << '\n'; // T (the same person) } ``` ___ ## More about partial_ordering To sum up, if we compare 2 floating point values, the result is `std::partial_ordering`. We can't use `operator==` in such a case. This makes sense, but when do we also need this type of order? Let's check the below example: ```C++ struct Student { std::partial_ordering operator<=>(const Student& other) const { if (!name_ || !other.name_) { return std::partial_ordering::unordered; } if (const auto res = name_ <=> other.name_; res != 0) { return res; } return index_ <=> other.index_; } bool operator==(const Student&) const = default; std::optional name_; double average_; int index_; }; int main() { Student student1{.name_ = "Mateusz", .average_ = 4.67, .index_ = 456123}; Student student2{.name_ = std::nullopt, .average_ = 4.67, .index_ = 123456}; std::cout << "student1 > student2 ? " << (student1 > student2) << '\n'; // F (second student doesn't has name!) std::cout << "student1 < student2 ? " << (student1 < student2) << '\n'; // F (second student doesn't has name!) std::cout << "student1 != student2 ? " << (student1 != student2) << '\n'; // T (here we use operator== not <=>) std::cout << "student1 == student2 ? " << (student1 == student2) << '\n'; // F (here we use operator== not <=>) std::cout << "student2 == student2 ? " << (student2 == student2) << '\n'; // T (here we use operator== not <=>) } ``` ___ ## Ok but how <=> exactly works? Each type or ordering could represent one of those 3 values (partial could also has `unordered`): * std::strong_ordering::less / weak_ordering::less / std::partial_ordering::less * std::strong_ordering::equal / weak_ordering::equivalent / std::partial_ordering::equivalent * std::strong_ordering::greater / weak_ordering::greater / std::partial_ordering::greater We can simply compare them: ```C++ int val1(1234); int val2(12345); auto res = val1 <=> val2; if (res < 0) std::cout << "val1 < val2" << std::endl; else if (res == 0) std::cout << "val1 == val2" << std::endl; else if (res > 0) std::cout << "val1 > val2" << std::endl; ``` This is implicity generated by compiler. For instance `a > b` is converted to `(a <=> b) > 0` ___ ## Finally, something about weak ordering Weak ordering is returned when does not imply substitutability: if a is equivalent to b, f(a) may not be equivalent to f(b), where f denotes a function that reads only comparison-salient state that is accessible via the argument's public const members. In other words, equivalent values may be distinguishable. ```C++ struct Student { int score() const { return std::accumulate(cbegin(grades_), cend(grades_), 0, [](const auto& sum, const auto& pair) { return sum + pair.second; }); } std::weak_ordering operator<=>(const Student& other) const { if (const auto res = this->score() <=> other.score(); res != 0) { return res; } return name_ <=> other.name_; } bool operator==(const Student&) const = default; std::string name_; std::multimap grades_; int index_; }; int main() { Student student1{.name_ = "Mateusz", .grades_ = {{"Math", 5},{"Bio", 4}}, .index_ = 456123}; Student student2{.name_ = "Mateusz", .grades_ = {{"Math", 5},{"Eng", 4}}, .index_ = 123456}; Student student3{.name_ = "Mateusz", .grades_ = {{"Math", 4},{"Phys", 3}}, .index_ = 123456}; std::cout << "student1 > student2 ? " << (student1 > student2) << '\n'; // F std::cout << "student1 < student2 ? " << (student1 < student2) << '\n'; // F std::cout << "student1 != student2 ? " << (student1 != student2) << '\n'; // T (use operator == here) std::cout << "student1 == student2 ? " << (student1 == student2) << '\n'; // F (use operator == here) std::cout << "student1 > student3 ? " << (student1 > student3) << '\n'; // T (higher grades) std::cout << "student1 < student3 ? " << (student1 < student3) << '\n'; // F } ``` ___ ## Implicit conversion `std::strong_ordering` can be implicitly converted to `std::weak_ordering` and `std::weak_ordering` can be implicitly converted to `std::partial_ordering`. ___ ## designated initializers This is a small feature that you saw a few times in action during these lectures: ```C++ struct Student { std::string name_; std::multimap grades_; int index_; }; int main() { Student student1{.name_ = "Mateusz", .grades_ = {{"Math", 5},{"Bio", 4}}, .index_ = 456123}; Student student2{.name_ = "Mateusz", .grades_ = {{"Math", 5},{"Eng", 4}}, .index_ = 123456}; Student student3{.name_ = "Mateusz", .grades_ = {{"Math", 4},{"Phys", 3}}, .index_ = 123456}; ``` ___ Every structure or class with public fields could now be initialized by using the name of a field. This makes code more readable and you don't need to switch a few times between headers and source code to check which field means what. ```C++ struct StreamInfo { std::string sourceAddress_; std::string destinationAddress_; uint16_t sourcePort_; uint16_t destinationPort_; uint16_t vlan_; }; struct Streamer { Streamer(uint16_t sourcePort): sourcePort_(sourcePort) {} uint16_t getSourcePort() const { return sourcePort_; } private: uint16_t sourcePort_; }; int main() { std::map ipTables; Streamer streamer(6523); ipTables.emplace("192.168.0.15", "192.168.0.35"); ipTables.emplace("192.168.0.16", "192.168.0.42"); StreamInfo info { .sourceAddress_ = "192.168.0.15", .destinationAddress_ = ipTables["192.168.0.15"], .sourcePort_ = streamer.getSourcePort(), .destinationPort_ = 9998, .vlan_ = [](){ return 123; }() }; } ``` ___ ## Atributes Since C++20 we got 4 more atributes * [[nodiscard("reason")]] - encourages the compiler to issue a warning if the return value is discarded * [[likely]] and [[unlikely]] - indicates that the compiler should optimize for the case where a path of execution through a statement is more or less likely than any other path of execution * [[no_unique_address]] - indicates that a non-static data member need not have an address distinct from all other non-static data members of its class ___ ## likely and unlikely Everyone knows well this `if-else` statements: ```C++ if (!vec.empty()) { return vec.front(); } if (divider != 0) { return num / divider; } else { return std::nan("nan"); } if (ptr) { return ptr->doSth(); } if (name_ == other.name_) { return index < other.index_; } else { return name_ < other.name_ } ``` * What is common for all of them? ___ Let's help compiler! ```C++ constexpr double pow(double x, int n) noexcept { if (n > 0) [[likely]] { return x * pow(x, n - 1); } else [[unlikely]] { return 1; } } constexpr double pow2(double x, int n) noexcept { if (n > 0) { return x * pow(x, n - 1); } else { return 1; } } int main() { auto benchmark = [](auto fun) { const auto start = std::chrono::high_resolution_clock::now(); fun(); const auto diff = std::chrono::high_resolution_clock::now() - start; std::cout << "Time: " << std::fixed << std::setprecision(6) << diff.count() << " ns\n"; }; std::vector vec(1'000'000); std::iota(begin(vec), end(vec), 1); benchmark([&]() { for (auto el : vec) { pow(el, 13); } }); benchmark([&]() { for (auto el : vec) { pow2(el, 13); } }); // Time: 59839100 ns // Time: 79786800 ns } ``` ___ There is more! ```C++ int Nwd(int a, int b) { while (b != 0) [[likely]] { a = std::exchange(b, a % b); } return a; } int Nwd2(int a, int b) { while (b != 0) { a = std::exchange(b, a % b); } return a; } int main() { auto benchmark = [](auto fun) { const auto start = std::chrono::high_resolution_clock::now(); fun(); const auto diff = std::chrono::high_resolution_clock::now() - start; std::cout << "Time: " << std::fixed << std::setprecision(6) << diff.count() << " ns\n"; }; std::vector vec(1'000'000); std::iota(begin(vec), end(vec), 1); benchmark([&](){ for (auto el : vec) { Nwd(1'000'000, el); } }); benchmark([&](){ for (auto el : vec) { Nwd2(1'000'000, el); } }); // Time: 140909100 ns // Time: 182572100 ns } ``` ___ ## Ok ok, but what happens when I already use optimization flag? ```C++ int main() { auto benchmark = [](auto fun) { const auto start = std::chrono::high_resolution_clock::now(); fun(); const auto diff = std::chrono::high_resolution_clock::now() - start; std::cout << "Time: " << std::fixed << std::setprecision(6) << diff.count() << " ns\n"; }; std::vector vec(1'000'000); std::iota(begin(vec), end(vec), 1); int res = 0; benchmark([&](){ for (auto el : vec) { res += Nwd(1'000'000, el); } }); // Time: 88765100 ns benchmark([&](){ for (auto el : vec) { res += Nwd2(1'000'000, el); } }); // Time: 82797600 ns benchmark([&]() { for (auto el : vec) { res += pow(el, 13); } }); // Time: 6002300 ns benchmark([&]() { for (auto el : vec) { res += pow2(el, 13); } }); // Time: 8012500 ns return res; } ``` Wait what?!. When we use -O3 optimization flag we got worse output for [[likely]]. It depends on various things, sometimes code run faster sometimes not, but generally, you can't charm the compiler with optimization, but sometimes you can help :) ___ Use it when it is worth it, and the compiler could have a problem with manual optimization. For instance in 99.99% during response validation, an error occurs when there is no `result` filed, other cases happen mainly when someone implements wrong behavior and this is eliminated during tests. Ofc you still can always write more predictable if statement at the beginning :) And this is even better option in my opinion. ```C++ bool validate(const std::string& str) { constexpr char kResult[] = "result:"; if (str.empty()) [[unlikely]] { return false; } else if (str.size() > 30) [[unlikely]] { return false; } else if (str.front() != '{') [[unlikely]] { return false; } else if (str.back() != '}') [[unlikely]] { return false; } else if (std::all_of(std::cbegin(str), std::cend(str), [](const char c) { return !std::isalnum(c); })) [[unlikely]] { return false; } else if (std::search(std::cbegin(str), std::cend(str), std::cbegin(kResult), std::cend(kResult)) == std::cend(str)) [[likely]] { return false; } return true; } bool validate2(const std::string& str) { constexpr char kResult[] = "result:"; if (str.empty()) { return false; } else if (str.size() > 30) { return false; } else if (str.front() != '{') { return false; } else if (str.back() != '}') { return false; } else if (std::all_of(std::cbegin(str), std::cend(str), [](const char c) { return !std::isalnum(c); })) { return false; } else if (std::search(std::cbegin(str), std::cend(str), std::cbegin(kResult), std::cend(kResult)) == std::cend(str)) { return false; } return true; } int main() { auto benchmark = [](auto fun) { const auto start = std::chrono::high_resolution_clock::now(); fun(); const auto diff = std::chrono::high_resolution_clock::now() - start; std::cout << "Time: " << std::fixed << std::setprecision(6) << diff.count() << " ns\n"; }; int wrong = 0; std::vector vec(1'000'000, "{result:12345}"); benchmark([&]() { wrong += std::count_if(std::cbegin(vec), std::cend(vec), [](const auto& str) { return validate(str); }); }); benchmark([&]() { wrong += std::count_if(std::cbegin(vec), std::cend(vec), [](const auto& str) { return validate2(str); }); }); // Time: 293222100 ns // Time: 376011000 ns // // With 03 // Time: 14437300 ns // Time: 21943400 ns return wrong; } ``` ___ ## no_unique_address Allows this data member to be overlapped with other non-static data members or base class subobjects of its class. Generally, we can use it when we have an empty filed in the structure, and we don't want to lost 1 byte to store it. ```C++ struct Empty {}; struct Filed { int val_; double val2_; Empty empty_; }; struct Filed2 { int val_; double val2_; [[no_unique_address]] Empty empty_; }; int main() { static_assert(sizeof(Empty) >= 1); std::cout << "sizeof(Filed): " << sizeof(Filed) << '\n'; // 24 std::cout << "sizeof(Filed): " << sizeof(Filed2) << '\n'; // 16 } ``` ___ ## pack-expansion in lmabdas Since C++20 we can easily move all arguments to lambda. Before C++20 we can do this only for one object, but now it works for packages also. ```C++ auto postponeTask(auto&& fun, auto&&... args) { return [f = std::move(fun), ... pack = std::move(args)]() { return f(pack...); }; } int main() { auto task = postponeTask([](const std::string& str, int num) { std::cout << str << " | " << num << '\n'; }, std::string("Ala ma kota"), 42); task(); } ``` ___ If you don't have C++20 (but you have C++17) you can still use the trick with the `std::tuple` with `std::apply` method. ```C++ template auto postponeTask(Fun fun, Args... args) { return [fun = std::move(fun), tup = std::make_tuple(std::move(args)...)]() -> decltype(auto) { return std::apply([fun = std::move(fun)](auto const&... args) -> decltype(auto) { return fun(args...); }, tup); }; } int main() { auto task = postponeTask([](const std::string& str, int num) { std::cout << str << " | " << num << '\n'; }, std::string("Ala ma kota"), 42); task(); } ``` ___ ## template syntax for lambdas Ok, but what if I want to perfect forward arguments, instead of moving them? Since C++20 you can use template syntax! ```C++ int main() { auto benchmark = [](auto&& fun, Args... args) { const auto now = std::chrono::system_clock::now(); const auto res = std::move(fun)(std::forward(args)...); const auto then = std::chrono::system_clock::now(); std::cout << "res: " << res << " | time: " << (then - now).count() << " ns\n"; }; benchmark([](int count, int init) -> long long { std::vector vec(count); std::iota(begin(vec), end(vec), init); return std::accumulate(begin(vec), end(vec), 0); }, 1'000'000, 50); } ``` ___ ## uniform erasure Ok let's do sth easier! How many times have you written sth like this :)? ```C++ std::vector vec {1,2,3,4,5,6}; vec.erase(std::remove_if(begin(vec), end(vec), [](auto num){ return num & 1; }), vec.end()); ``` `std::remove` only prepares a vector to actually remove, but in the end, we need to erasure these values. Yes, this is why a lot of ppl hate C++, always complications. For instance we can write sth like this: ```C++ std::map map {{"One", 1}, {"Two", 2}, {"Three", 3}}; map.erase("One"); std::list list {"Ala", "ma", "kota"}; list.remove("Ala"); ``` In the case of a list, there is a member function `remove`, which actually removes an element! But for the map, we have only `erase`, which also takes a key as a value and removes an element. 3 containers and 3 different behavior. Definitely, newcomers won't like this. The funniest part is that list has `remove_if` method, but the map doesn't have `erase_if` ;) ___ Since C++20 erasing elements is finally unified! We have two options, `erase` or `erase_if`. Unfortunately, in C++ there is always an exception to the rule. `std::map` and `std::set` (and their hash versions) don't have `std::erase` overload, because they already have specialized members functions. ```C++ std::vector vec {1,2,3,4,5,6}; std::erase(vec, 4); std::erase_if(vec, [](auto num){ return num & 1; }); std::map map {{"One", 1}, {"Two", 2}, {"Three", 3}}; map.erase("One"); // There is no std::erase() for map :/ std::erase_if(map, [](const auto& pair){ return pair.second == 2; }); // But there is std::erase_if std::list list {"Ala", "ma", "kota"}; std::erase(list, "ma"); std::erase_if(list, [](const auto& str){ return str.length() == 3; }); std::unordered_set set{1,2,3,4,5,6}; set.erase(4); std::erase_if(set, [](auto num){ return num & 1; }); ``` ___ ## source_loaction Since C++20 we got a nice functionality to perform easy and efficient logging. ```C++ void log(const std::string_view message, const std::source_location location = std::source_location::current()) { std::cout << "file: " << location.file_name() << "(" << location.line() << ":" << location.column() << ") `" << location.function_name() << "`: " << message << '\n'; } template void fun(T x) { log(x); } int main(int, char*[]) { log("Hello world!"); fun("Hello C++20!"); } ``` ```bash file: prog.cc(24:8) `int main(int, char**)`: Hello world! file: prog.cc(19:8) `void fun(T) [with T = const char*]`: Hello C++20! ``` ___ It's easy to rebuild a little this solution and create own logger: ```C++ enum LogType {INF, WRN, ERR}; struct Logger { Logger(LogType logType, std::source_location location = std::source_location::current()): logType_{logType}, location_{location} {} Logger& operator<<(std::string_view message) { const auto time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); std::ostringstream os; os << "[" << toString(logType_) << "]" << "(" << std::put_time(std::localtime(&time), "%Y-%m-%d %X") << "): " << location_.file_name() << "(" << location_.line() << ":" << location_.column() << ") `" << location_.function_name() << "`: " << message << '\n'; file_ << os.str() << std::flush; return *this; } private: std::string_view toString(LogType logType) { switch (logType) { case INF: return "INFO"; case WRN: return "WARNING"; case ERR: return "ERROR"; default: return "UNKNOWN!"; } } LogType logType_; std::source_location location_; static std::ofstream file_; }; std::ofstream Logger::file_("log.txt"); ``` ___ Ok now is time to log sth ```C++ int main() { Logger(INF) << "Hello!" << "And Hi" << "And Dzien dobry!"; Logger(WRN) << "Ojej!"; Logger(ERR) << "Critical!!!" << "UPS!" << "Bad bad!"; } ``` And in file `log.txt` we got: ```C++ [INFO](2022-05-22 14:24:47): prog.cc(55:15) `int main()`: Hello! [INFO](2022-05-22 14:24:47): prog.cc(55:15) `int main()`: And Hi [INFO](2022-05-22 14:24:47): prog.cc(55:15) `int main()`: And Dzien dobry! [WARNING](2022-05-22 14:24:47): prog.cc(56:15) `int main()`: Ojej! [ERROR](2022-05-22 14:24:47): prog.cc(57:15) `int main()`: Critical!!! [ERROR](2022-05-22 14:24:47): prog.cc(57:15) `int main()`: UPS! [ERROR](2022-05-22 14:24:47): prog.cc(57:15) `int main()`: Bad bad! ``` ___ # bit operations There is a new header `` which allows performing many valuable operations on bits. All functions are declared as constexpr, so there is a big chance that everything will be evaluated at compile time! Finally, you don't need to write an ugly macro. Let's start with the most important one. `std::endian` indicates the endianness of all scalar types: - If all scalar types are little-endian, std::endian::native equals std::endian::little - If all scalar types are big-endian, std::endian::native equals std::endian::big Corner case platforms are also supported: - If all scalar types have sizeof equal to 1, endianness does not matter and all three values, std::endian::little, std::endian::big, and std::endian::native are the same. - If the platform uses mixed endian, std::endian::native equals neither std::endian::big nor std::endian::little. ```C++ int main() { if constexpr (std::endian::native == std::endian::big) std::cout << "big-endian\n"; else if constexpr (std::endian::native == std::endian::little) std::cout << "little-endian\n"; else std::cout << "mixed-endian\n"; // Output: little-endian } ``` ___ For me very usefull is also `popcount`, and all `countX_Y` function, where`X` could be `l` - left, `r` - right, and `Y` could be `zero` or `one`: ```C++ int main() { // counts the number of 1 bits in an unsigned integer std::cout << std::popcount(0b1010101010101u) << '\n'; // 7 // counts the number of consecutive 1 bits, starting from the most significant bit std::cout << std::countl_one(std::numeric_limits::max()) << '\n'; // 8 // counts the number of consecutive 0 bits, starting from the most significant bit std::cout << std::countl_zero(0b111000011111u) << '\n'; // 20 because this is uint32_t // counts the number of consecutive 1 bits, starting from the least significant bit std::cout << std::countr_one(0b111000011111u) << '\n'; // 5 // counts the number of consecutive 0 bits, starting from the least significant bit std::cout << std::countr_zero(0b111000011100u) << '\n'; // 2 } ``` ___ Sometimes in the algorithm, we need to know the closest power of 2 for a given number. ```C++ int main() { for (uint8_t i = 0 ; i < 16 ; ++i) { std::cout << "num: " << std::bitset(i) << " | bit_width: " << +std::bit_width(i) << " | power of 2: " << std::has_single_bit(i) << " | floor: " << +std::bit_floor(i) << " | ceil: " << +std::bit_ceil(i) << '\n'; } } ``` ```bash num: 00000000 | bit_width: 0 | power of 2: 0 | floor: 0 | ceil: 1 num: 00000001 | bit_width: 1 | power of 2: 1 | floor: 1 | ceil: 1 num: 00000010 | bit_width: 2 | power of 2: 1 | floor: 2 | ceil: 2 num: 00000011 | bit_width: 2 | power of 2: 0 | floor: 2 | ceil: 4 num: 00000100 | bit_width: 3 | power of 2: 1 | floor: 4 | ceil: 4 num: 00000101 | bit_width: 3 | power of 2: 0 | floor: 4 | ceil: 8 num: 00000110 | bit_width: 3 | power of 2: 0 | floor: 4 | ceil: 8 num: 00000111 | bit_width: 3 | power of 2: 0 | floor: 4 | ceil: 8 num: 00001000 | bit_width: 4 | power of 2: 1 | floor: 8 | ceil: 8 num: 00001001 | bit_width: 4 | power of 2: 0 | floor: 8 | ceil: 16 num: 00001010 | bit_width: 4 | power of 2: 0 | floor: 8 | ceil: 16 num: 00001011 | bit_width: 4 | power of 2: 0 | floor: 8 | ceil: 16 num: 00001100 | bit_width: 4 | power of 2: 0 | floor: 8 | ceil: 16 num: 00001101 | bit_width: 4 | power of 2: 0 | floor: 8 | ceil: 16 num: 00001110 | bit_width: 4 | power of 2: 0 | floor: 8 | ceil: 16 num: 00001111 | bit_width: 4 | power of 2: 0 | floor: 8 | ceil: 16 ``` ___ There is also a rotation `left` /` right`. Standard `c ++ 23` introduced also` byteswap` to convert variables from little endian to big endian and vice versa. ```C++ uint16_t num = 0b1111001100111001; std::cout << "num: " << std::bitset<16>(num) << "\n\n"; for (uint8_t i = 0 ; i < 16 ; ++i) { std::cout << "rotr: " << std::bitset<16>(std::rotr(num, i)) << " | rotl: " << std::bitset<16>(std::rotl(num, i)) << '\n'; } ``` ```bash num: 1111001100111001 rotr: 1111001100111001 | rotl: 1111001100111001 rotr: 1111100110011100 | rotl: 1110011001110011 rotr: 0111110011001110 | rotl: 1100110011100111 rotr: 0011111001100111 | rotl: 1001100111001111 rotr: 1001111100110011 | rotl: 0011001110011111 rotr: 1100111110011001 | rotl: 0110011100111110 rotr: 1110011111001100 | rotl: 1100111001111100 rotr: 0111001111100110 | rotl: 1001110011111001 rotr: 0011100111110011 | rotl: 0011100111110011 rotr: 1001110011111001 | rotl: 0111001111100110 rotr: 1100111001111100 | rotl: 1110011111001100 rotr: 0110011100111110 | rotl: 1100111110011001 rotr: 0011001110011111 | rotl: 1001111100110011 rotr: 1001100111001111 | rotl: 0011111001100111 rotr: 1100110011100111 | rotl: 0111110011001110 rotr: 1110011001110011 | rotl: 1111100110011100 ``` ___ ## format your string like in printf The main problem with `std::cout` is a problem with easy formatting (I know streams are also inefficient, but this is not a point right now). For instance: ```C++ int main() { for (uint8_t i = 0 ; i < 16 ; ++i) { std::cout << "num: " << std::bitset(i) << " | bit_width: " << +std::bit_width(i) << " | power of 2: " << std::has_single_bit(i) << " | floor: " << +std::bit_floor(i) << " | ceil: " << +std::bit_ceil(i) << '\n'; } } ``` With `` library now we can write: ```C++ for (uint8_t i = 0; i < 16; ++i) { std::cout << std::format("num: {} | bit_width: {} | power of 2: {} | floor: {} | ceil {}\n", std::bitset(i).to_string(), std::bit_width(i), std::has_single_bit(i), std::bit_floor(i), std::bit_ceil(i)); } ``` ___ On day `25.05.2022` the newest `gcc 13` didn't support the library ``. Fortunately `clang 15` already supports it! `` library is very powerful, we can easily create any format we want and pass any values we need: ```C++ void raw_write_to_log(std::string_view users_fmt, std::format_args&& args) { constinit static int line{}; std::clog << std::format("{:04} : ", line++) << std::vformat(users_fmt, args) << '\n'; } template constexpr void log(Args&&... args) { // Generate formatting string "{} "... std::array braces{}; constexpr const char c[] = "{} "; for (auto i{0u}; i != braces.size() - 1; ++i) { braces[i] = c[i % 3]; } braces.back() = '\0'; raw_write_to_log(braces.data(), std::make_format_args(std::forward(args)...)); } int main() { std::string str{"Printable"}; log("You", "Can", "Pass", "Any", "Number", "Of", "Arguments"); log("Everything", "Which", "Can", "Be", str); log(1, 4.5, 1234); } ``` ```bash 0000 : You Can Pass Any Number Of Arguments 0001 : Everything Which Can Be Printable 0002 : 1 4.5 1234 ``` ___ ## Limitations We need to wait to specify how we should specify custom object formatting. Currently on cppreference we can see an example, but looking very ugly and don't compile on `clang 15`. ```C++ #include #include // A wrapper for type T template struct Box { T value; }; // The wrapper Box can be formatted using the format specification of the wrapped value template struct std::formatter, CharT> : std::formatter { // parse() is inherited from the base class // Define format() by calling the base class implementation with the wrapped value template auto format(Box t, FormatContext& fc) const { return std::formatter::format(t.value, fc); } }; int main() { Box v = { 42 }; std::cout << std::format("{:#x}", v); } ``` ___ But to make you more interested in this library check out more possibilities: ```C++ int main() { std::string buffer; std::format_to( std::back_inserter(buffer), "Hello, C++{}!\n", // formater "20", // args "More args, make no error :)"); std::cout << buffer << '\n'; } ``` ```bash Hello, C++20! ```