libmoost
/home/mhx/git/github/libmoost/include/moost/configurable/indexed_binder.hpp
Go to the documentation of this file.
00001 /* vim:set ts=3 sw=3 sts=3 et: */
00028 #ifndef MOOST_CONFIGURABLE_INDEXED_BINDER_HPP__
00029 #define MOOST_CONFIGURABLE_INDEXED_BINDER_HPP__
00030 
00031 #include "configurable.h"
00032 
00033 #include <vector>
00034 
00035 #include <boost/shared_ptr.hpp>
00036 #include <boost/noncopyable.hpp>
00037 
00038 #include <set>
00039 #include <stdexcept>
00040 #include <istream>
00041 #include <ostream>
00042 
00043 #include <boost/lexical_cast.hpp>
00044 
00045 namespace moost { namespace configurable {
00046 
00051 template<typename T>
00052 class indexed_binder : public configurable, boost::noncopyable
00053 {
00054 private:
00055 
00056   std::vector< boost::shared_ptr< configurable > > m_pconfigurables;
00057 
00058 public:
00059 
00060   indexed_binder() {}
00061 
00062   virtual ~indexed_binder() {}
00063 
00064   const T& operator[](const size_t index) const
00065   {
00066     return *static_cast<const T *>(m_pconfigurables[index].get());
00067   }
00068 
00069   T& operator[](const size_t index)
00070   {
00071     return *static_cast<T *>(m_pconfigurables[index].get());
00072   }
00073 
00074   size_t size()
00075   {
00076     return m_pconfigurables.size();
00077   }
00078 
00079   void resize(size_t size)
00080   {
00081     size_t old_size = m_pconfigurables.size();
00082     m_pconfigurables.resize(size);
00083     for (; old_size < size; ++old_size)
00084       m_pconfigurables[old_size].reset(new T());
00085   }
00086 
00087   void read(std::istream & source);
00088 
00089   void write(std::ostream & dest, int indent = 0) const;
00090 
00091   void set(const std::string & key, const std::string & value);
00092 
00093   void get(const std::string & key, std::string & value) const;
00094 
00095   void list(std::vector< std::pair< std::string, std::string > > & items);
00096 
00097   // set_default for indexed_binder should just resize to zero
00098   void set_default();
00099 };
00100 
00101 // implementation:
00102 
00103 template<typename T>
00104 void indexed_binder<T>::read(std::istream & source)
00105 {
00106   // first, read {
00107   std::string token;
00108 
00109   source >> token;
00110 
00111   if (token != "{")
00112     throw std::runtime_error("bad token: '" + token + "', expecting '}'");
00113 
00114   // any index that doesn't read, we must set the default
00115   std::set< size_t > found_indices;
00116 
00117   // first, set ourselves to zero
00118   resize(0);
00119 
00120   for (;;)
00121   {
00122     source >> token;
00123 
00124     if (token == "}")
00125       break;
00126 
00127     if (source.eof())
00128       throw std::runtime_error("unexpected eof, expecting '}'");
00129 
00130     try
00131     {
00132       size_t index = boost::lexical_cast<size_t>(token);
00133 
00134       if (index >= size())
00135         resize(index + 1);
00136 
00137       source.get(); // skip past the ' '
00138       try
00139       {
00140         m_pconfigurables[index]->read(source);
00141         found_indices.insert(index);
00142       }
00143       catch (const std::runtime_error & e)
00144       {
00145         throw std::runtime_error(boost::lexical_cast<std::string>(index) + ":" + e.what());
00146       }
00147     }
00148     catch (boost::bad_lexical_cast)
00149     {
00150       throw std::runtime_error("bad token: '" + token + "', expecting index");
00151     }
00152   }
00153 
00154   // set defaults for any remaining indices
00155   for (size_t i = 0; i != size(); ++i)
00156   {
00157     if (found_indices.find(i) == found_indices.end())
00158     {
00159       try
00160       {
00161         m_pconfigurables[i]->set_default();
00162       }
00163       catch (const std::runtime_error & e)
00164       {
00165         throw std::runtime_error(boost::lexical_cast<std::string>(i) + ":" + e.what());
00166       }
00167     }
00168   }
00169 }
00170 
00171 template<typename T>
00172 void indexed_binder<T>::write(std::ostream & dest, int indent /* = 0 */) const
00173 {
00174   // new context always means new indent
00175   indent += configurable::DEFAULT_INDENT;
00176 
00177   // first, write {
00178   dest << '{' << std::endl;
00179   for (int i = 0; i != indent; ++i)
00180     dest << ' ';
00181 
00182   for (size_t i = 0; i != m_pconfigurables.size(); ++i)
00183   {
00184     dest << i << ' ';
00185     m_pconfigurables[i]->write(dest, indent);
00186     dest << std::endl;
00187     for (int i = 0; i != indent; ++i)
00188       dest << ' ';
00189   }
00190 
00191   // backup!
00192   dest.seekp(-configurable::DEFAULT_INDENT, std::ios::cur);
00193   dest << '}';
00194 }
00195 
00196 template<typename T>
00197 void indexed_binder<T>::set(const std::string & key, const std::string & value)
00198 {
00199   if (key == "size")
00200   {
00201     size_t size = boost::lexical_cast<size_t>(value);
00202     resize(size);
00203   }
00204   else
00205   {
00206     size_t sep_pos = key.find('.');
00207     if (sep_pos == std::string::npos)
00208       throw std::runtime_error("bad key: '" + key + "', expecting separator '.'");
00209     size_t index = boost::lexical_cast<size_t>(key.substr(0, sep_pos));
00210     if (index >= m_pconfigurables.size())
00211        throw std::runtime_error("index out of range");
00212     m_pconfigurables[index]->set(key.substr(sep_pos + 1), value);
00213   }
00214 }
00215 
00216 template<typename T>
00217 void indexed_binder<T>::get(const std::string & key, std::string & value) const
00218 {
00219   if (key == "size")
00220   {
00221     value = boost::lexical_cast<std::string>(m_pconfigurables.size());
00222   }
00223   else
00224   {
00225     size_t sep_pos = key.find('.');
00226     if (sep_pos == std::string::npos)
00227       throw std::runtime_error("bad key: '" + key + "', expecting separator '.'");
00228     size_t index = boost::lexical_cast<size_t>(key.substr(0, sep_pos));
00229     if (index >= m_pconfigurables.size())
00230       throw std::runtime_error("index out of range");
00231     m_pconfigurables[index]->get(key.substr(sep_pos + 1), value);
00232   }
00233 }
00234 
00235 template<typename T>
00236 void indexed_binder<T>::list(std::vector< std::pair< std::string, std::string > > & items)
00237 {
00238   for (size_t i = 0; i != m_pconfigurables.size(); ++i)
00239   {
00240     std::vector< std::pair< std::string, std::string > > sub_items;
00241     m_pconfigurables[i]->list(sub_items);
00242     for (std::vector< std::pair< std::string, std::string > >::const_iterator it_s = sub_items.begin(); it_s != sub_items.end(); ++it_s)
00243       items.push_back(std::make_pair(boost::lexical_cast<std::string>(i) + '.' + it_s->first, it_s->second));
00244   }
00245   items.push_back(std::make_pair("size", boost::lexical_cast<std::string>(size())));
00246 }
00247 
00248 // set_default for indexed_binder should just resize to zero
00249 template<typename T>
00250 void indexed_binder<T>::set_default()
00251 {
00252   resize(0);
00253 }
00254 
00255 }}
00256 
00257 #endif // MOOST_CONFIGURABLE_INDEXED_BINDER_HPP__