0
votes

I have a class that wraps a std::map, I have simplified it below. I wanted to implement a to_string() function which streams the first and second map entries into a stringstream with operator << - then returns the string result.

This works ok for int, float, string etc... anything that can be streamed basically.

But enum class xzy : int {...}; can't be stream -or it must be static_cast first. But in my template if I put static_cast around x.second then it will break other template types.

I want to know how I can deal with this. My first idea is to try to use type traits to detect if the type is integral (and then static cast it) otherwise just rely on the normal operator << function.

here is the class:

template<typename T1, typename T2>
class map_wrapper
{
public:
    std::map<T1, T2> m_map;
    std::map<T1, T2> &map() {return m_map;}
    std::string to_string()
    {
        std::stringstream ss;
        for (const auto &item : m_map)
        {
            // <----------------------- ISSUE HERE
            // So I want this to handle as many types as possible
            // Maybe I can do some sort of if type traits == integral then static cast?
            ss << item.first << ", " << static_cast<int>(item.second) << "\n";
            //ss << item.first << ", " << item.second << "\n";
        }
        return ss.str();
    }
};

here is my test code:


enum class types : int
{
    type1,
    type2,
    type3
};

int main()
{
    // This is all ok
    map_wrapper<int, int> int_map;
    int_map.map() = {{1, 2}, {3, 4}};
    std::cout << int_map.to_string() << std::endl;

    // This only works if I static_cast the enum types to an int within to_string()
    map_wrapper<int, types> type_map;
    type_map.map() = {{1, types::type1}, {2, types::type2}};
    std::cout << type_map.to_string() << std::endl;
    return 0;
}

I have annotated the parts that don't work as I want them to.

Live editable example: https://godbolt.org/z/rh8Y5v

update

Note the types enum is just an example - I would like it to work with any enum class ideally

1

1 Answers

1
votes

You cannot specialize a single member function, you would need to specialize the whole class. If you only need it for this function, I suggest providing a template operator overload:

template<typename Enum,
         typename = std::enable_if_t<std::is_enum_v<Enum>>>
std::ostream& operator<< (std::ostream& out, Enum e)
{
    return out << static_cast<int>(e);
}

See it online

This operator will accept any enum or enum class. If you want to only accept enum class, you can use std::is_scoped_enum from C++23 or roll out your own one (there's a possible implementation in cppreference to help).

Note that you can still provide overloads if you want to pretty print a particular enum (which would require another full class specialization if we went with that).