## 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!
```