|
Boost : |
From: Marcus Lindblom (macke_at_[hidden])
Date: 2007-09-04 03:35:58
Hi,
I have some code that helps with defining iostream-operators for enums.
We use this in our C++<->xml bindings.
Basically, it's just two maps (string->enum & enum<-string) and some
syntactic sugar. However, I happen to think it is quite neat and so
figured it might be useful to other people too. (And it's always nice to
give something back :)
So, is this something that would fit in boost?
Advantages:
* Enables lexical_cast on enums.
* Pure implementation detail from user-of-enum's perspective.
* Very little syntax overhead, inspired by boost::assign.
* Works across namespaces & DLLS.
* Allows user to define io-ops for "external" enums too.
(But the IO-op-macro hs to go into the external namespace.)
* Uses pimpl-idiom for faster compilation.
* Suitable for header-only version.
Issues to work out:
* Uses the Loki-singleton to store implementation details.
* Initialization is a bit rough. (We get away with it because we have
a static "initClass" method for every class. It is a natural place for
enumio-setup in our app, but this does not translate to general code well.)
* Stores std::strings, although it's almost always constructed from
string literals. Wastes a bit of memory for simpler implementation.
* EnumIO<E>::The() is globally accessible, so there could be
conflicts. (Very low probability though)
See below for example & implementation. Comments?
Cheers,
/Marcus
--- user .h ------------------------------------------------
enum Foo { Bar, Baz; }
// to export from a win32 dll, add dllimport/export here
std::istream& operator>>(std::istream&, Foo& foo);
std::ostream& operator<<(std::ostream&, Foo foo);
--- user.cpp ------------------------------------------------
#include "user.h"
#include <enumio.h>
ENUMIO_IMPLEMENT_IOSTREAM_OPS(Foo)
// just one way to do it
bool init = (::EnumIO<Foo>::the() (Bar, "bar") (Baz, "baz")), true;
--- enumio.h ------------------------------------------------
class EnumUtilBase
{
public:
~ EnumIOBase();
protected:
explicit EnumIOBase(const std::type_info& enumType);
std::istream & in(std::istream & os, int &value) const;
std::ostream & out(std::ostream & os, int value) const;
protected:
void add(int value, const char* data);
private:
class Impl;
boost::scoped_ptr<Impl> m_i;
};
// Helps with serialization (iostream) of enums
template<class E>
class EnumIO :
public EnumIOBase
{
public:
typedef E Enum;
EnumIO<E> the() {
return ::Loki::SingletonHolder<::EnumIO<E> >::the(); }
EnumIO();
EnumIO<E>& operator()(E value, const char* data);
std::istream & in(std::istream & is, E & value) const;
std::ostream & out(std::ostream & os, E value) const;
};
#define ENUMIO_IMPLEMENT_IOSTREAM_OPS(E) \
std::istream& operator>>(std::istream& is, E& value) { \
return ::EnumIO<E>::the().in(is, value); } \
std::ostream& operator>>(std::ostream& os, E value) { \
return ::EnumIO<E>::the().out(os, value); }
template<class E>
inline EnumIO<E>::EnumIO() :
EnumIOBase(typeid(E))
{
BOOST_STATIC_ASSERT(sizeof(E) == sizeof(int));
}
template<class E>
inline EnumIO<E> &EnumIO<E>::operator()(E value, const char* data)
{
EnumIOBase::add(value, data);
return *this;
}
template<class E>
inline std::istream & EnumIO<E>::in(std::istream & is, E & value) const
{
return EnumIOBase::in(is, (int&) value);
}
template<class E>
inline std::ostream & EnumIO<E>::out(std::ostream& os, E value) const
{
return EnumIOBase::out(os, value);
}
--- enumio.cpp ------------------------------------------------
typedef std::map<int, std::string> MapOut;
typedef std::map<std::string, int> MapIn;
class EnumIOBase::Impl
{
public:
const char* m_typename; ///< is retrieved from typeid(X).name()
MapIn m_mapIn;
MapOut m_mapOut;
};
EnumIOBase::EnumIOBase(const std::type_info& enumType) :
m_i(new Impl)
{
m_i->m_typename = enumType.name();
}
EnumIOBase::~EnumIOBase()
{ }
std::istream & EnumIOBase::in(std::istream & is, int &value) const
{
std::string str;
is >> str;
if (m_i->m_mapIn.empty()) {
MapIn& m = const_cast<MapIn&>(m_i->m_mapIn);
for(MapOut::const_iterator i = m_i->m_mapOut.begin();
i != m_i->m_mapOut.end(); ++i) {
m.insert(std::make_pair(i->second, i->first));
}
}
MapIn::const_iterator i = m_i->m_mapIn.find(str);
if (i != m_i->m_mapIn.end()) {
value = i->second;
} else {
is.setstate(std::ios::failbit);
}
return is;
}
std::ostream & EnumIOBase::out(std::ostream & os, int value) const
{
const std::string& str = toStr(value);
if (!str.empty()) {
os << str;
} else {
os.setstate(std::ios::failbit);
}
return os;
}
void EnumIOBase::add(int value, const char* data)
{
m_i->m_mapOut.insert(std::make_pair(value, data));
}
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk