Skip to content

Commit 4a38c22

Browse files
committed
vist, any, union, xor updated
1 parent 62dd98d commit 4a38c22

9 files changed

+526
-1
lines changed

CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,11 @@ add_executable(core_dump src/core_dump.cpp)
293293

294294
#add_executable(packaged_task src/packaged_task.cpp)
295295

296+
add_executable(std_visit src/std_visit.cpp)
297+
298+
add_executable(error_code src/error_code.cpp)
299+
300+
296301
if(${CMAKE_GNU_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION} GREATER_EQUAL 13)
297302
add_executable(printing_with_format src/printing_with_format.cpp)
298303
endif()

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,8 @@ This change ensures that VSCode uses the "Ninja Multi-Config" generator by defau
223223
* [Typedef, Type alias (using keyword)](docs/typedef.md)
224224
* [type_dispatch, integral_constant, true/false type](src/type_dispatch_integral_constant_true_false_type.cpp)
225225
* [Unions](docs/union.md)
226-
* [Variant](docs/std_variant.md)
226+
* [Variant](docs/std_variant.md)
227+
* [Visit](docs/std_visit.md)
227228
* [Variadic Templates Function](docs/variadic_templates.md)
228229
* [Volatile Keyword](docs/volatile.md)
229230
- [C++ Classes](#)

docs/any.md

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,83 @@ This method is useful when you want to write code that depends on whether a type
9797

9898
### 3. Attempt Compilation
9999
As a quick-and-dirty method, you can write a small piece of code that attempts to copy an instance of the class. If the code fails to compile, it's likely that the class is not `CopyConstructible`. However, this method is less precise and should be used cautiously.
100+
101+
## std::any vs templates
102+
103+
In C++, both `std::any` and templates serve different purposes and have different use cases. Understanding the differences and when to use each is important for effective C++ programming.
104+
105+
### **1. `std::any`:**
106+
107+
- **Type Erasure:** `std::any` is a type-erased container that can hold an instance of any type. This means that the type of the object stored in `std::any` is not known at compile time, only at runtime. It can store any type, but type information is erased, and you need to know the type to retrieve the stored object.
108+
109+
- **Runtime Polymorphism:** Since `std::any` works with any type, it provides a form of runtime polymorphism. You can store different types in the same container or pass around different types using `std::any`.
110+
111+
- **Performance:** Using `std::any` incurs runtime overhead. This includes type checks, storage management, and potential dynamic memory allocations. This makes it less efficient compared to templates, which are resolved at compile time.
112+
113+
- **Usage Example:**
114+
115+
```cpp
116+
#include <any>
117+
#include <iostream>
118+
119+
int main() {
120+
std::any value = 10; // Storing an int
121+
value = std::string("Hello"); // Storing a string
122+
123+
try {
124+
std::cout << std::any_cast<std::string>(value) << std::endl;
125+
} catch (const std::bad_any_cast& e) {
126+
std::cout << "Bad cast: " << e.what() << std::endl;
127+
}
128+
129+
return 0;
130+
}
131+
```
132+
133+
- **Use Case:** `std::any` is useful when you need to store or pass around objects of different types without knowing the type in advance or when working with APIs that require handling various types generically.
134+
135+
### **2. Templates:**
136+
137+
- **Compile-Time Polymorphism:** Templates provide compile-time polymorphism, meaning the code is generated at compile time based on the types provided. This allows for type-safe and efficient code generation.
138+
139+
- **Type Safety:** Templates are type-safe, as the types are known at compile time. This allows the compiler to catch type errors before the program runs.
140+
141+
- **Performance:** Templates are generally more performant than `std::any` because the compiler generates specialized code for each type, eliminating the need for runtime type checks and dynamic allocations.
142+
143+
- **Usage Example:**
144+
145+
```cpp
146+
#include <iostream>
147+
148+
template<typename T>
149+
void print(T value) {
150+
std::cout << value << std::endl;
151+
}
152+
153+
int main() {
154+
print(10); // Prints an integer
155+
print("Hello"); // Prints a string literal
156+
print(3.14); // Prints a double
157+
158+
return 0;
159+
}
160+
```
161+
162+
- **Use Case:** Templates are ideal when you want to write generic and efficient code that works with different types but where the types are known at compile time.
163+
164+
### **Comparison Summary:**
165+
166+
| Feature | `std::any` | Templates |
167+
|-----------------|-------------------------------------|----------------------------------|
168+
| Type Handling | Runtime type erasure | Compile-time type resolution |
169+
| Type Safety | Type safety only at runtime | Type safety at compile-time |
170+
| Performance | Slower, with potential overhead | Generally faster, optimized code |
171+
| Flexibility | Can hold any type, very flexible | Generic, but types must be known at compile time |
172+
| Usage Scenario | Heterogeneous type containers, APIs | Generic programming, containers, algorithms |
173+
174+
### **When to Use Which:**
175+
- **Use `std::any`** when you need to store or pass around objects of unknown types at runtime and are willing to accept the associated performance costs.
176+
177+
- **Use templates** when you need to write type-safe, efficient, and generic code where types are known at compile time.
178+
179+
Choosing between `std::any` and templates depends on whether you need runtime flexibility (`std::any`) or compile-time efficiency and type safety (templates).

docs/bitset_bit_field_bitwise_operations.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,5 +168,33 @@ std::cout <<result<<std::endl;
168168
std::cout <<(a >>= 2) <<std::endl;
169169
```
170170

171+
172+
### std::bit_xor
173+
The `std::bit_xor` function in C++20 offers several advantages over the traditional `^` operator:
174+
175+
**1. Generic Type Support:**
176+
177+
- `std::bit_xor` can operate on a wider range of integer types, including custom integral types.
178+
- This provides more flexibility and type safety in your code.
179+
180+
**2. Potential Performance Improvements:**
181+
182+
- While the performance difference between `std::bit_xor` and `^` might not be significant in most cases, the `std::bit_xor` implementation could potentially benefit from compiler optimizations or hardware-specific instructions.
183+
184+
**3. Consistency with Other Bitwise Operations:**
185+
186+
- `std::bit_xor` is part of the `std::bit` namespace, which also includes other bitwise operations like `std::bit_and`, `std::bit_or`, `std::bit_not`, and `std::bit_count`.
187+
- Using these functions consistently can improve code readability and maintainability.
188+
189+
**4. Future Enhancements:**
190+
191+
- As the C++ standard evolves, `std::bit_xor` might gain additional features or optimizations in the future.
192+
193+
**In summary:**
194+
195+
While the `^` operator is generally sufficient for most XOR operations, `std::bit_xor` provides a more generic, type-safe, and potentially performant alternative. If you need to work with custom integer types or prefer a more consistent approach for bitwise operations, `std::bit_xor` is a good choice. However, in most cases, the performance difference between the two methods will be negligible.
196+
197+
198+
171199
Refs: [1](https://www.geeksforgeeks.org/bitwise-algorithms/)
172200

docs/error_code.md

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
## std::error_code
2+
`std::error_code` in C++ is a part of the `<system_error>` header and represents an error condition in a program, typically used to report and handle error situations in a more flexible and extendable way compared to traditional methods like using integer error codes or exceptions.
3+
- It combines error codes with an error category, making it possible to understand and manage errors across different domains and libraries.
4+
- It is particularly useful in performance-critical code or when working in environments where exceptions are not suitable.
5+
6+
### Key Concepts:
7+
8+
1. **Error Code and Error Category**:
9+
- `std::error_code` consists of two parts: an error value (an integer) and an error category (`std::error_category`).
10+
- The error value represents the specific error.
11+
- The error category represents the domain or context in which the error value is meaningful, such as system errors, application-specific errors, etc.
12+
13+
2. **Error Handling**:
14+
- `std::error_code` provides a way to handle errors without throwing exceptions. This is particularly useful in contexts where exceptions are either not allowed or not preferred, such as in real-time systems or performance-critical code.
15+
16+
3. **Comparison**:
17+
- You can compare `std::error_code` objects directly to check if two error codes represent the same error.
18+
19+
4. **Conversion to `std::error_condition`**:
20+
- `std::error_code` can be converted to a more generic `std::error_condition`, which represents a portable error condition, independent of the platform-specific error codes.
21+
22+
### Example:
23+
24+
Let's walk through an example to understand how `std::error_code` can be used in practice.
25+
26+
#### Example 1: Basic Usage
27+
28+
```cpp
29+
#include <iostream>
30+
#include <system_error>
31+
#include <fstream>
32+
33+
void open_file(const std::string& filename, std::error_code& ec) {
34+
std::ifstream file(filename);
35+
if (!file) {
36+
ec = std::make_error_code(std::errc::no_such_file_or_directory);
37+
return;
38+
}
39+
// Process file...
40+
ec.clear(); // No error
41+
}
42+
43+
int main() {
44+
std::error_code ec;
45+
open_file("non_existent_file.txt", ec);
46+
47+
if (ec) {
48+
std::cout << "Error opening file: " << ec.message() << " (Error code: " << ec.value() << ")\n";
49+
} else {
50+
std::cout << "File opened successfully.\n";
51+
}
52+
53+
return 0;
54+
}
55+
```
56+
57+
#### Explanation:
58+
59+
1. **`std::errc::no_such_file_or_directory`**:
60+
- This is an enumerator from the `std::errc` enumeration, which contains platform-independent error codes.
61+
62+
2. **`std::make_error_code`**:
63+
- This function converts the `std::errc` value to a `std::error_code`.
64+
65+
3. **Error Message**:
66+
- `ec.message()` returns a human-readable error message corresponding to the error code.
67+
- `ec.value()` returns the actual error value.
68+
69+
In this example, if the file `"non_existent_file.txt"` does not exist, the function `open_file` sets the `std::error_code` to indicate that the file was not found, and the error is reported in `main`.
70+
71+
#### Example 2: Using `std::system_category`
72+
73+
`std::system_category()` represents the operating system's error codes, allowing you to work directly with native error codes.
74+
75+
```cpp
76+
#include <iostream>
77+
#include <system_error>
78+
#include <cerrno>
79+
#include <cstdio>
80+
81+
void delete_file(const std::string& filename, std::error_code& ec) {
82+
if (std::remove(filename.c_str()) != 0) {
83+
ec = std::error_code(errno, std::system_category());
84+
} else {
85+
ec.clear(); // No error
86+
}
87+
}
88+
89+
int main() {
90+
std::error_code ec;
91+
delete_file("non_existent_file.txt", ec);
92+
93+
if (ec) {
94+
std::cout << "Error deleting file: " << ec.message() << " (Error code: " << ec.value() << ")\n";
95+
} else {
96+
std::cout << "File deleted successfully.\n";
97+
}
98+
99+
return 0;
100+
}
101+
```
102+
103+
#### Explanation:
104+
105+
1. **`errno`**:
106+
- This global variable is set by certain system calls and library functions in the C standard library when an error occurs.
107+
108+
2. **`std::system_category()`**:
109+
- It is used here to associate the error code with the system's error category.
110+
111+
In this example, if the file `"non_existent_file.txt"` does not exist, `std::remove` sets `errno` to `ENOENT`, and we set the `std::error_code` accordingly. This code is then used to report the error.
112+
113+
114+
[code](../src/error_code.cpp)

docs/std_visit.md

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
## std::visit
2+
3+
`std::visit` is a powerful utility in C++ that is primarily used to work with `std::variant`, a type-safe union introduced in C++17. A `std::variant` can hold a value from a set of specified types, but only one at a time. The challenge with `std::variant` is accessing the value it holds safely, without resorting to unsafe type casts or multiple `std::get` calls. This is where `std::visit` comes into play.
4+
5+
### Why Do We Need `std::visit`?
6+
7+
`std::variant` encapsulates multiple types, and to handle the value it holds, we need a way to determine its current type and execute the corresponding logic. Using `std::visit`, we can apply a visitor (a callable object like a lambda, function, or functor) to the currently held value in the `std::variant`. This provides a clean, type-safe way to access the variant's value without manually checking the active type.
8+
9+
Without `std::visit`, we'd have to manually inspect the `std::variant`'s active type using `std::holds_alternative<T>()` or by trying multiple `std::get<T>()` calls, which can lead to code that's difficult to maintain and prone to errors.
10+
11+
### What Does `std::visit` Do?
12+
13+
`std::visit` applies a callable (often referred to as a visitor) to the value stored in a `std::variant`. The key benefit is that the visitor is automatically invoked with the correct type, eliminating the need for explicit type checking and handling.
14+
15+
**Key points about `std::visit`:**
16+
- It ensures that the visitor is called with the correct type from the `std::variant`.
17+
- The visitor can be a lambda function, a regular function, or a functor.
18+
- If the `std::variant` can hold multiple types, `std::visit` allows you to handle each type case in a unified, readable manner.
19+
- It can be used with multiple `std::variant` arguments simultaneously, where the visitor will handle combinations of the types.
20+
21+
### Syntax of `std::visit`
22+
23+
The basic syntax is as follows:
24+
25+
```cpp
26+
#include <variant>
27+
#include <iostream>
28+
29+
std::variant<int, float, std::string> myVariant = 42;
30+
31+
auto visitor = [](auto&& arg) {
32+
std::cout << arg << std::endl;
33+
};
34+
35+
std::visit(visitor, myVariant);
36+
```
37+
38+
Here's a breakdown of the syntax:
39+
- `std::visit(visitor, variant)` is how you call `std::visit`.
40+
- `visitor` is the callable object that you want to apply to the variant's currently held value.
41+
- `variant` is the `std::variant` you want to inspect.
42+
43+
### Example: Using `std::visit`
44+
45+
Let's look at a more detailed example:
46+
47+
```cpp
48+
#include <iostream>
49+
#include <variant>
50+
#include <string>
51+
52+
int main() {
53+
std::variant<int, float, std::string> myVariant = 10;
54+
55+
auto visitor = [](auto&& arg) {
56+
using T = std::decay_t<decltype(arg)>; // Get the type of the argument
57+
if constexpr (std::is_same_v<T, int>)
58+
std::cout << "It's an int: " << arg << std::endl;
59+
else if constexpr (std::is_same_v<T, float>)
60+
std::cout << "It's a float: " << arg << std::endl;
61+
else if constexpr (std::is_same_v<T, std::string>)
62+
std::cout << "It's a string: " << arg << std::endl;
63+
};
64+
65+
std::visit(visitor, myVariant); // Output: It's an int: 10
66+
67+
myVariant = "Hello";
68+
std::visit(visitor, myVariant); // Output: It's a string: Hello
69+
70+
myVariant = 3.14f;
71+
std::visit(visitor, myVariant); // Output: It's a float: 3.14
72+
73+
return 0;
74+
}
75+
```
76+
77+
### Explanation:
78+
- **Variant Initialization**: `myVariant` is a `std::variant` that can hold an `int`, `float`, or `std::string`.
79+
- **Visitor**: The visitor is a lambda function that checks the type of the argument using `if constexpr` and `std::is_same_v`, and prints out a message based on the type.
80+
- **`std::visit` Call**: `std::visit` applies the visitor to the current value of `myVariant`. Depending on what type is stored in `myVariant`, the corresponding branch of the lambda is executed.
81+
82+
### What `std::visit` Does That Can't Be Done Easily Without It
83+
84+
Without `std::visit`, you would have to manually manage type handling in `std::variant`, which can lead to verbose and error-prone code. For example:
85+
86+
```cpp
87+
#include <variant>
88+
#include <iostream>
89+
#include <string>
90+
91+
int main() {
92+
std::variant<int, float, std::string> myVariant = 42;
93+
94+
if (std::holds_alternative<int>(myVariant)) {
95+
std::cout << "It's an int: " << std::get<int>(myVariant) << std::endl;
96+
} else if (std::holds_alternative<float>(myVariant)) {
97+
std::cout << "It's a float: " << std::get<float>(myVariant) << std::endl;
98+
} else if (std::holds_alternative<std::string>(myVariant)) {
99+
std::cout << "It's a string: " << std::get<std::string>(myVariant) << std::endl;
100+
}
101+
102+
return 0;
103+
}
104+
```
105+
106+
This approach requires separate type checks (`std::holds_alternative<T>`) and then fetching the value (`std::get<T>`), making the code less elegant and harder to extend or maintain.
107+
108+
[code](../src/std_visit.cpp)
109+

0 commit comments

Comments
 (0)