Utilize std::any do C++ Moderno nos seus projetos

Dê adeus de uma vez por todas ao void*.


std::any é um recurso da biblioteca padrão C++ que foi introduzido no C++17.

Este componente pertence ao conjunto de classes de contêineres com segurança de tipo, fornecendo um meio seguro para armazenar e manipular valores de qualquer tipo.

Ele é especialmente útil quando você precisa lidar com situações em que o tipo da variável pode(pleonasmo): variar! 😃

Aí você diz:

— Ah, cara! De boa. Para esses casos eu uso o void *.

Sim, realmente você tem razão, mas você já viu como a nova geração está em relação a segurança de memória ???

Lembrando que o termo segurança é usado em Português, pois não existe uma palavra que se adeque a tradução para Safe, ou seja: Safe Seguro)! 😛

Sem dizer que void * é realmente perigoso!

Se você fizer isso, funciona:

void * some_data; // Péssima ideia std::string str = "Oi";
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 << "Tipo de y: " << typeid(y).name() << '\n'; // include typeinfo
Enter fullscreen mode
Exit fullscreen mode

Mas, a chance de isso dar mer%$a é grande! Ao final do uso dessas variáveis, some_data vai continuar existindo, ou seja, tempo de vida indefinido!

E é para subsitituir o void* que o std::any foi criado no C++ Moderno que, com certeza, é totalmente Safe!

Em outras palavras, ele é um wrapper que encapsula sua variável para um shared_ptr(ponteiros inteligentes) da vida! Sim, e existe até um std::make_any!!!


Como utilizar o std::any

Primeiramente você precisa incluir o cabeçalho dele:

Logicamente, só funciona a partir do C++17 como foi dito no início!

#include <any>
Enter fullscreen mode
Exit fullscreen mode

E agora o mesmo código que foi apresentado acima, mas usando std::any:

#include <iostream>
#include <any>

int main(){ std::any some_data; std::string str = "Oi"; 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 << "Tipo de y: " << some_data.type().name() << '\n';
}
Enter fullscreen mode
Exit fullscreen mode

No código acima vimos que:

  • std::any some_data; – Declara a variável;
  • std::any_cast<T>(some_data) – Converte para o tipo desejado;
  • std::make_any<T> – Outra forma de criar objetos;
  • some_data.type().name() – Obtém o tipo de dado sem precisar de typeinfo.

E você pode usar pra absolutamente tudo: std::vector, Lambda e tudo que existir de tipo de dado!

E o cara pergunta outra coisa:

— Tá! E se eu quiser acabar o tempo de vida do std::any manualmente?

Basta usar a estrutura de união reset ou até mesmo com o operador de incialização:

some_data.reset();
// Ou
some_data = {};
Enter fullscreen mode
Exit fullscreen mode

— E pra verificar se std::any está vazio?
Use has_value():

std::cout << (some_data.has_value() ? "Cheio!" : "Vazio.") << '\n';
Enter fullscreen mode
Exit fullscreen mode

O type() sem união com name() pode ser usado para comparar tipos:

std::cout << (some_data.type() == typeid(void)) << '\n'; // 0 pra false
std::cout << (some_data.type() == typeid(int)) << '\n'; // 1 pra true
Enter fullscreen mode
Exit fullscreen mode

Para usar os nomes booleanos use: std::cout << std::boolalpha << (some_data.type() == typeid(int)) << '\n';.

Para lançar exceções você deve usar o std::bad_any_cast:

try { std::any any_str("Oiii"); 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;
}
Enter fullscreen mode
Exit fullscreen mode

Para verificar se realmente tudo está entre os conformes, nunca se esqueça de usar as flags para seu compilador: -Wall -Wextra -pedantic -g -fsanitize=address.


Além de totalmente SAFE, o std::any é muito prático e uma mão na roda!

Tinha um projeto da empresa que eu estava desenvolvendo, que passava um argumento de função e podia ser qualquer tipo, mas o retorno da função era std::string concatenada ao nome do objeto recebido.

E alguém havia criado um baita de um switch case para converter para std::string(bizarro!), eu subsititui para recebimento de parâmetro para std::any e converti com std::any_cast<std::string> e resolvi de forma: Moderna, Safe e Like a Boss! Exatamente isso que std::any é!!! 😃

Para mais informações acesse: https://en.cppreference.com/w/cpp/utility/any


Discover more from Coursity

Subscribe to get the latest posts sent to your email.

Leave a Comment

Your email address will not be published. Required fields are marked *

Discover more from Coursity

Subscribe now to keep reading and get access to the full archive.

Continue reading

Scroll to Top
Let’s delve into how apptunix erp can transform your business processes and drive growth. Allows using the site with your screen reader.