简体   繁体   中英

It is possible to efficiently cast std::any with std::any_cast

I'm trying to use std::any in my library. I take the example of cppreference.com and I implement the example in this way :

#include <any>
#include <iostream>

int main()
{
    struct example_1{
        int i1;
        int i2;
    };


    // any type
    std::any a = 1;
    std::cout << a.type().name() << ": " << std::any_cast<int>(a) << '\n';
    a = 3.14;
    std::cout << a.type().name() << ": " << std::any_cast<double>(a) << '\n';
    a = true;
    std::cout << a.type().name() << ": " << std::any_cast<bool>(a) << '\n';

    struct example_1 ex1;
    ex1.i1 = 1;
    ex1.i2 = 2;

    a = ex1;
    std::cout << a.type().name() << ": " << std::any_cast<struct example_1>(a).i1 << " ; " << std::any_cast<struct example_1>(a).i2 << '\n';

    // bad cast
    try
    {
        a = 1;
        std::cout << std::any_cast<float>(a) << '\n';
    }
    catch (const std::bad_any_cast& e)
    {
        std::cout << e.what() << '\n';
    }

    // has value
    a = 1;
    if (a.has_value())
    {
        std::cout << a.type().name() << '\n';
    }

    // reset
    a.reset();
    if (!a.has_value())
    {
        std::cout << "no value\n";
    }

    // pointer to contained data
    a = 1;
    int* i = std::any_cast<int>(&a);
    std::cout << *i << "\n";
}

the output is the following :

i: 1
d: 3.14
b: 1
Z4mainE9example_1: 1 ; 2
bad any_cast
i
no value
1

in the case of basic type is easy but in the case of a struct we have as return of the call std::any.type().name() the symbol of the struct.

I thought that to cast to the right type the passed std::any argument I can search in the symbol string if contains the substring "structname" or "classname". But I think this is only a workaround. Is there a way to efficiently cast std::any to the right type?

Another question, if I use it in my library, I didn't know the name of the struct or class of the object passed ( so my workaround can't work ). Is there a way to retrieve it and to use std::any_cast with the right type?

The values returned by .type().name() are completely implementation-defined and you have no guarantees for them in any way. You don't even have a guarantee that .type().name() is not the same for multiple different types.

Even if that weren't the case .type().name() returns a runtime value and you cannot use a non-constant value to compute a type.

C++ is statically typed. Every expression (after template instantiation) has a type fixed at compile-time. You therefore can't work with std::any without knowledge of the type it (potentially) holds.

You can obtain the type and the value that std::any holds, using any_cast or the std::type_info returned by .type() (through the other members it provides outside of .name() ), only if you have a finite set of possible types, determined at compile-time, that it can hold at that code location (after template instantiation). If you don't know the potential types it holds at a given location in your code (after template instantiation), then you cannot obtain the type or value it holds there.

If a user of your library is going to use their types with your std::any and you actually want to extract the value inside your library code, then you will need to template your code and the user will need to provide the potential types as template arguments to it.

There are few cases where using std::any makes sense. Usually std::variant or a virtual base class interface (from which the user can derive their own types) are much more likely to be the appropriate tools. Or, if the runtime polymorphism isn't actually needed (we don't know your use case), simply writing template code to which the user can provide their custom types may be sufficient.


Also side note: Don't use struct before a type name in C++ outside of its class definition or an explicit forward-declaration. It is not required, but can have unintended consequences should name lookup for the type fail.

It's because of optimizations which will be done during compile. If you want to get full name of a type on runtime , you must demangle the name of the type (which is not standard).

suppose you have your type name inside string variable type_name :

int demangle_status;
char* demangled_name = abi::__cxa_demangle(type_name.c_str(), nullptr, nullptr, &demangle_status;
if (demangle_status == 0)
{
    type_name = demangled_name;
    std::free(demangled_name)
}
std::cout << type_name

Note that you have to include <cxxabi.h> in order to call abi::__cxa_demangle

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM