I've been playing around with a generic mechanism for converting a value from one set of values to another, based on Boost's map_list_of
template. The two sets could ultimately be disjoint, so it's not just conversion from one enumerated type to another.
Anyway, the following code compiles and runs as intended, but I'm stuck on something. The definition of enumToString
, right before main()
, requires the static_cast<const std::map<COLOR, std::string> &>
cast. (FWIW, this constructor causes the convert()
function to return the key value as a string if it cannot find the key in the map.) How can I get the code to compile without this cast, all while sticking to C++03?
It might be that without the cast there is simply not be enough type information available for the compiler to figure out which KeyToValue
constructor to call.
#include <sstream>
#include <map>
#include "boost/assign.hpp"
template<typename K, typename V> // Forward reference.
class KeyToValue;
template<typename K, typename V> // K to V (value or callback default).
V convert(const KeyToValue<K, V> &t, const K &k)
{
typename std::map<K, V>::const_iterator it = t.m.find(k);
return it == t.m.end() ? (t.mc == NULL ? t.d : t.mc(k)) : it->second;
}
template<typename K> // K to string (auto default). (Use SFINAE for ostream&operator<<(K).)
std::string convert(const KeyToValue<K, std::string> &t, const K &k)
{
std::string v;
typename std::map<K, std::string>::const_iterator it = t.m.find(k);
if (it == t.m.end())
if (t.auto_default)
{
std::ostringstream oss;
oss << k;
v = oss.str();
}
else v = t.mc == NULL ? t.d : t.mc(k);
else v = it->second;
return v;
}
template<typename K, typename V> // Construct conversion object for convert().
class KeyToValue
{
public:
KeyToValue(const std::map<K, std::string> &m) : // To string w/auto default.
m(m), d(V()), mc(NULL), auto_default(true) { }
KeyToValue(const std::map<K, V> &m, const V &d) : // To V w/value default.
m(m), d(d), mc(NULL), auto_default(false) { }
KeyToValue(const std::map<K, V> &m, V (*mc)(const K &)) : // with callback.
m(m), d(V()), mc(mc), auto_default(false) { }
private:
const std::map<K, V> m;
const V d; // Default value.
V (*mc)(const K &); // Callback that returns default.
const bool auto_default; // Automatically create default from key?
template<typename K1, typename V1>
friend V1 convert(const KeyToValue<K1, V1> &t, const K1 &k);
template<typename K1>
friend std::string convert(const KeyToValue<K1, std::string> &t, const K1 &k);
};
#include <iostream>
enum COLOR { RED, BLUE, ORANGE, YELLOW, GOLD };
unsigned DefaultUnsigned(const COLOR &myEnum)
{
return -1;
}
const KeyToValue<COLOR, unsigned> enumToUnsigned(boost::assign::map_list_of
(ORANGE, 13) (YELLOW, 58), DefaultUnsigned );
const KeyToValue<COLOR, std::string> enumToString(
static_cast<const std::map<COLOR, std::string> &>(boost::assign::map_list_of
(ORANGE, "Orange") (YELLOW, "Yellow") ) );
int main()
{
std::cout << convert(enumToUnsigned, YELLOW) << std::endl;
std::cout << convert(enumToUnsigned, GOLD) << std::endl;
std::cout << convert(enumToString, YELLOW) << std::endl;
std::cout << convert(enumToString, GOLD) << std::endl;
}
This is the correct console output with the cast:
58
4294967295
Yellow
4
Without the cast, g++ (-std=c++98) generates these diagnostics:
prog.cc:64:43: error: call of overloaded 'KeyToValue(boost::assign_detail::generic_list<std::pair<COLOR, const char*> >&)' is ambiguous
(ORANGE, "Orange") (YELLOW, "Yellow") );
^
prog.cc:34:5: note: candidate: KeyToValue<K, V>::KeyToValue(const std::map<K, std::__cxx11::basic_string<char> >&) [with K = COLOR; V = std::__cxx11::basic_string<char>]
KeyToValue(const std::map<K, std::string> &m) : // To string w/auto default.
^
prog.cc:31:7: note: candidate: KeyToValue<COLOR, std::__cxx11::basic_string<char> >::KeyToValue(const KeyToValue<COLOR, std::__cxx11::basic_string<char> >&)
class KeyToValue
^
UPDATE: Here is the simplified version. How can I get rid of the cast?
#include <map>
#include "boost/assign.hpp"
struct KeyToValue {
KeyToValue(const std::map<int, bool> &m) { }
} intToBool(
static_cast<const std::map<int, bool> &>(
boost::assign::map_list_of (3, true)));
int main() { }