Search code examples
c++stringtemplatesexceptionany

bad_any_cast exception when returning string from templated function


I'm creating a config file parser with values stored in an unordered_map. The config values are a mixture of strings, ints, floats, and bools, so I'm using std::any to store these in an unordered map like so:

static unordered_map<string, any> CONFIG_VALUES =
{
    {"title",           "The window title"},
    {"xRes",            1024},
    //...
};

I have a generic getter function to allow retrieval of config values like so:

template<typename T>
T GetValue(const string& valueName) const
{
    auto result = CONFIG_VALUES.find(valueName);
    if (result != CONFIG_VALUES.end())
    {
        return any_cast<T>(result->second);
    }
    else
    {
        throw std::runtime_error("Invalid config key");
    }
}

My code compiles, and I'm able to successfully retrieve an int like so:

int myXres = MyConfig->GetValue<int>("xRes");

But, if I try and get a string:

string myTitle = MyConfig->GetValue<string>("title");

I get a crash:

Unhandled exception at 0x00007FF99463A799 in program.exe: Microsoft C++ exception: std::bad_any_cast at memory location 0x000000DCD76FDCE8. occurred

In Visual Studio debugger locals I see that the std::any type of the string is

type    0x00007ff6950f2328 {program.exe!char const * `RTTI Type Descriptor'} {_Data={_UndecoratedName=0x0000000000000000 <NULL> ...} }  

I suspect that the "char const *" might be the issue here (because we're passing "string" as the template parameter), but I'm not sure how to fix it... (Or, maybe this is a red herring).

Any ideas?


Solution

  • You are right about the value being a char const*. You'll need to store it as a std::string in the map, like this:

    static unordered_map<string, any> CONFIG_VALUES =
    {
        {"title",           std::string("The window title")},
        {"xRes",            1024},
        //...
    };
    

    Alternatively, you could do the any_cast to the correct type, like this:

    string myTitle = GetValue<char const*>("title");
    

    Here's a working demo.