libmoost
|
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__