Boost logo

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