Say goodbye to void* once and for all.
std::any is a feature of the C++ standard library that was introduced in C++17.
This component belongs to the set of type-safe container classes, providing a safe means to store and manipulate values of any type.
It is especially useful when you need to deal with situations where the type of the variable can vary! 😃
Then you say:
- Oh man! Good. For these cases I use
void *.
Yes, you're really right, but have you seen how the new generation is in relation to memory safety???
Not to mention that void* is really dangerous!
If you do this, it works:
void * some_data; // Bad idea
std::string str = "Hi";
int x = 3;
decltype(x) y = 6;
some_data = &str;
std::cout << *(std::string*)some_data << '\n';
some_data = &x;
std::cout << *(int*)some_data << '\n';
some_data = &y;
std::cout << "Type of y: " << typeid(y).name() << '\n'; // include typeinfo
But, the chance of this giving mer%$a is great! At the end of using these variables, some_data will continue to exist, that is, an indefinite lifetime!
And it is to replace void* that std::any was created in Modern C++ which, of course, is totally Safe!
In other words, it is a wrapper that encapsulates your variable to a shared_ptr(smart pointers) of life! Yes, and there is even a std::make_any!!!
How to use std::any
First you need to include its header:
Logically, it only works from C++17 as was said at the beginning!
#include <any>
And now the same code that was presented above, but using std::any:
#include <iostream>
#include <any>
int main(){
std::any some_data;
std::string str = "Hi";
int x = 3;
auto y = std::make_any<decltype(x)>(6);
some_data = str;
std::cout << std::any_cast<std::string>(some_data) << '\n';
some_data = x;
std::cout << std::any_cast<int>(some_data) << '\n';
some_data = y;
std::cout << "Type of y: " << some_data.type().name() << '\n';
}
In the code above we saw that:
-
std::any some_data;- Declares the variable; -
std::any_cast<T>(some_data)- Converts to the desired type; -
std::make_any<T>- Another way to create objects; -
some_data.type().name()- Gets the data type without needingtypeinfo.
And you can use it for absolutely everything: std::vector, Lambda and all existing data types!
And the guy asks something else:
- OK! What if I want to end the lifetime of
std::anymanually?
Just use the reset union structure or even the initialization operator:
some_data.reset();
// Or
some_data = {};
— And to check if
std::anyis empty?
Use has_value():
std::cout << (some_data.has_value() ? "Full!" : "Empty.") << '\n';
The unionless type() with name() can be used to compare types:
std::cout << (some_data.type() == typeid(void)) << '\n'; // 0 to false
std::cout << (some_data.type() == typeid(int)) << '\n'; // 1 to true
To use Boolean names use:
std::cout << std::boolalpha << (some_data.type() == typeid(int)) << '\n';.
To throw exceptions you must use std::bad_any_cast:
try {
std::any any_str("Hiii");
auto my_any{ std::make_any<std::string>(any_str.type().name()) };
std::cout << std::any_cast<std::string>(my_any) << '\n';
}catch (const std::bad_any_cast& e) {
std::cerr << "Error: " << e.what() << std::endl;
}
To check whether everything really complies, never forget to use the flags for your compiler: -Wall -Wextra -pedantic -g -fsanitize=address.
In addition to being completely SAFE, std::any is very practical and a great help!
There was a company project that I was developing, which passed a function argument and could be any type, but the function's return was std::string concatenated to the name of the object received.
And someone had created a great switch case to convert to std::string(bizarre!), I substituted it to receive the parameter for std::any and converted it with std::any_cast< std::string> and I solved it in a way: Modern, Safe and Like a Boss! Exactly what std::any is!!! 😃
For more information visit: https://en.cppreference.com/w/cpp/utility/any
Top comments (0)