dynamic_property_map.hpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. #ifndef BOOST_PROPERTY_MAP_DYNAMIC_PROPERTY_MAP_HPP
  2. #define BOOST_PROPERTY_MAP_DYNAMIC_PROPERTY_MAP_HPP
  3. // Copyright 2004-5 The Trustees of Indiana University.
  4. // Use, modification and distribution is subject to the Boost Software
  5. // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
  6. // http://www.boost.org/LICENSE_1_0.txt)
  7. // dynamic_property_map.hpp -
  8. // Support for runtime-polymorphic property maps. This header is factored
  9. // out of Doug Gregor's routines for reading GraphML files for use in reading
  10. // GraphViz graph files.
  11. // Authors: Doug Gregor
  12. // Ronald Garcia
  13. //
  14. #include <boost/config.hpp>
  15. #include <boost/throw_exception.hpp>
  16. #include <boost/property_map/property_map.hpp>
  17. #include <boost/lexical_cast.hpp>
  18. #include <boost/any.hpp>
  19. #include <boost/function/function3.hpp>
  20. #include <boost/type_traits/is_convertible.hpp>
  21. #include <typeinfo>
  22. #include <boost/mpl/bool.hpp>
  23. #include <stdexcept>
  24. #include <sstream>
  25. #include <map>
  26. #include <boost/type.hpp>
  27. #include <boost/smart_ptr.hpp>
  28. namespace boost {
  29. namespace detail {
  30. // read_value -
  31. // A wrapper around lexical_cast, which does not behave as
  32. // desired for std::string types.
  33. template<typename Value>
  34. inline Value read_value(const std::string& value)
  35. { return boost::lexical_cast<Value>(value); }
  36. template<>
  37. inline std::string read_value<std::string>(const std::string& value)
  38. { return value; }
  39. }
  40. // dynamic_property_map -
  41. // This interface supports polymorphic manipulation of property maps.
  42. class dynamic_property_map
  43. {
  44. public:
  45. virtual ~dynamic_property_map() { }
  46. virtual boost::any get(const any& key) = 0;
  47. virtual std::string get_string(const any& key) = 0;
  48. virtual void put(const any& key, const any& value) = 0;
  49. virtual const std::type_info& key() const = 0;
  50. virtual const std::type_info& value() const = 0;
  51. };
  52. //////////////////////////////////////////////////////////////////////
  53. // Property map exceptions
  54. //////////////////////////////////////////////////////////////////////
  55. struct dynamic_property_exception : public std::exception {
  56. virtual ~dynamic_property_exception() throw() {}
  57. virtual const char* what() const throw() = 0;
  58. };
  59. struct property_not_found : public dynamic_property_exception {
  60. std::string property;
  61. mutable std::string statement;
  62. property_not_found(const std::string& property) : property(property) {}
  63. virtual ~property_not_found() throw() {}
  64. const char* what() const throw() {
  65. if(statement.empty())
  66. statement =
  67. std::string("Property not found: ") + property + ".";
  68. return statement.c_str();
  69. }
  70. };
  71. struct dynamic_get_failure : public dynamic_property_exception {
  72. std::string property;
  73. mutable std::string statement;
  74. dynamic_get_failure(const std::string& property) : property(property) {}
  75. virtual ~dynamic_get_failure() throw() {}
  76. const char* what() const throw() {
  77. if(statement.empty())
  78. statement =
  79. std::string(
  80. "dynamic property get cannot retrieve value for property: ")
  81. + property + ".";
  82. return statement.c_str();
  83. }
  84. };
  85. struct dynamic_const_put_error : public dynamic_property_exception {
  86. virtual ~dynamic_const_put_error() throw() {}
  87. const char* what() const throw() {
  88. return "Attempt to put a value into a const property map: ";
  89. }
  90. };
  91. namespace detail {
  92. // Trying to work around VC++ problem that seems to relate to having too many
  93. // functions named "get"
  94. template <typename PMap, typename Key>
  95. typename boost::property_traits<PMap>::reference
  96. get_wrapper_xxx(const PMap& pmap, const Key& key) {
  97. using boost::get;
  98. return get(pmap, key);
  99. }
  100. //
  101. // dynamic_property_map_adaptor -
  102. // property-map adaptor to support runtime polymorphism.
  103. template<typename PropertyMap>
  104. class dynamic_property_map_adaptor : public dynamic_property_map
  105. {
  106. typedef typename property_traits<PropertyMap>::key_type key_type;
  107. typedef typename property_traits<PropertyMap>::value_type value_type;
  108. typedef typename property_traits<PropertyMap>::category category;
  109. // do_put - overloaded dispatches from the put() member function.
  110. // Attempts to "put" to a property map that does not model
  111. // WritablePropertyMap result in a runtime exception.
  112. // in_value must either hold an object of value_type or a string that
  113. // can be converted to value_type via iostreams.
  114. void do_put(const any& in_key, const any& in_value, mpl::bool_<true>)
  115. {
  116. using boost::put;
  117. key_type key_ = any_cast<key_type>(in_key);
  118. if (in_value.type() == typeid(value_type)) {
  119. put(property_map_, key_, any_cast<value_type>(in_value));
  120. } else {
  121. // if in_value is an empty string, put a default constructed value_type.
  122. std::string v = any_cast<std::string>(in_value);
  123. if (v.empty()) {
  124. put(property_map_, key_, value_type());
  125. } else {
  126. put(property_map_, key_, detail::read_value<value_type>(v));
  127. }
  128. }
  129. }
  130. void do_put(const any&, const any&, mpl::bool_<false>)
  131. {
  132. BOOST_THROW_EXCEPTION(dynamic_const_put_error());
  133. }
  134. public:
  135. explicit dynamic_property_map_adaptor(const PropertyMap& property_map_)
  136. : property_map_(property_map_) { }
  137. virtual boost::any get(const any& key_)
  138. {
  139. return get_wrapper_xxx(property_map_, any_cast<typename boost::property_traits<PropertyMap>::key_type>(key_));
  140. }
  141. virtual std::string get_string(const any& key_)
  142. {
  143. std::ostringstream out;
  144. out << get_wrapper_xxx(property_map_, any_cast<typename boost::property_traits<PropertyMap>::key_type>(key_));
  145. return out.str();
  146. }
  147. virtual void put(const any& in_key, const any& in_value)
  148. {
  149. do_put(in_key, in_value,
  150. mpl::bool_<(is_convertible<category*,
  151. writable_property_map_tag*>::value)>());
  152. }
  153. virtual const std::type_info& key() const { return typeid(key_type); }
  154. virtual const std::type_info& value() const { return typeid(value_type); }
  155. PropertyMap& base() { return property_map_; }
  156. const PropertyMap& base() const { return property_map_; }
  157. private:
  158. PropertyMap property_map_;
  159. };
  160. } // namespace detail
  161. //
  162. // dynamic_properties -
  163. // container for dynamic property maps
  164. //
  165. struct dynamic_properties
  166. {
  167. typedef std::multimap<std::string, boost::shared_ptr<dynamic_property_map> >
  168. property_maps_type;
  169. typedef boost::function3<boost::shared_ptr<dynamic_property_map>,
  170. const std::string&,
  171. const boost::any&,
  172. const boost::any&> generate_fn_type;
  173. public:
  174. typedef property_maps_type::iterator iterator;
  175. typedef property_maps_type::const_iterator const_iterator;
  176. dynamic_properties() : generate_fn() { }
  177. dynamic_properties(const generate_fn_type& g) : generate_fn(g) {}
  178. ~dynamic_properties() {}
  179. template<typename PropertyMap>
  180. dynamic_properties&
  181. property(const std::string& name, PropertyMap property_map_)
  182. {
  183. boost::shared_ptr<dynamic_property_map> pm(
  184. boost::static_pointer_cast<dynamic_property_map>(
  185. boost::make_shared<detail::dynamic_property_map_adaptor<PropertyMap> >(property_map_)));
  186. property_maps.insert(property_maps_type::value_type(name, pm));
  187. return *this;
  188. }
  189. template<typename PropertyMap>
  190. dynamic_properties
  191. property(const std::string& name, PropertyMap property_map_) const
  192. {
  193. dynamic_properties result = *this;
  194. result.property(name, property_map_);
  195. return result;
  196. }
  197. iterator begin() { return property_maps.begin(); }
  198. const_iterator begin() const { return property_maps.begin(); }
  199. iterator end() { return property_maps.end(); }
  200. const_iterator end() const { return property_maps.end(); }
  201. iterator lower_bound(const std::string& name)
  202. { return property_maps.lower_bound(name); }
  203. const_iterator lower_bound(const std::string& name) const
  204. { return property_maps.lower_bound(name); }
  205. void
  206. insert(const std::string& name, boost::shared_ptr<dynamic_property_map> pm)
  207. {
  208. property_maps.insert(property_maps_type::value_type(name, pm));
  209. }
  210. template<typename Key, typename Value>
  211. boost::shared_ptr<dynamic_property_map>
  212. generate(const std::string& name, const Key& key, const Value& value)
  213. {
  214. if(!generate_fn) {
  215. BOOST_THROW_EXCEPTION(property_not_found(name));
  216. } else {
  217. return generate_fn(name,key,value);
  218. }
  219. }
  220. private:
  221. property_maps_type property_maps;
  222. generate_fn_type generate_fn;
  223. };
  224. template<typename Key, typename Value>
  225. bool
  226. put(const std::string& name, dynamic_properties& dp, const Key& key,
  227. const Value& value)
  228. {
  229. for (dynamic_properties::iterator i = dp.lower_bound(name);
  230. i != dp.end() && i->first == name; ++i) {
  231. if (i->second->key() == typeid(key)) {
  232. i->second->put(key, value);
  233. return true;
  234. }
  235. }
  236. boost::shared_ptr<dynamic_property_map> new_map = dp.generate(name, key, value);
  237. if (new_map.get()) {
  238. new_map->put(key, value);
  239. dp.insert(name, new_map);
  240. return true;
  241. } else {
  242. return false;
  243. }
  244. }
  245. template<typename Value, typename Key>
  246. Value
  247. get(const std::string& name, const dynamic_properties& dp, const Key& key)
  248. {
  249. for (dynamic_properties::const_iterator i = dp.lower_bound(name);
  250. i != dp.end() && i->first == name; ++i) {
  251. if (i->second->key() == typeid(key))
  252. return any_cast<Value>(i->second->get(key));
  253. }
  254. BOOST_THROW_EXCEPTION(dynamic_get_failure(name));
  255. }
  256. template<typename Value, typename Key>
  257. Value
  258. get(const std::string& name, const dynamic_properties& dp, const Key& key, type<Value>)
  259. {
  260. for (dynamic_properties::const_iterator i = dp.lower_bound(name);
  261. i != dp.end() && i->first == name; ++i) {
  262. if (i->second->key() == typeid(key))
  263. return any_cast<Value>(i->second->get(key));
  264. }
  265. BOOST_THROW_EXCEPTION(dynamic_get_failure(name));
  266. }
  267. template<typename Key>
  268. std::string
  269. get(const std::string& name, const dynamic_properties& dp, const Key& key)
  270. {
  271. for (dynamic_properties::const_iterator i = dp.lower_bound(name);
  272. i != dp.end() && i->first == name; ++i) {
  273. if (i->second->key() == typeid(key))
  274. return i->second->get_string(key);
  275. }
  276. BOOST_THROW_EXCEPTION(dynamic_get_failure(name));
  277. }
  278. // The easy way to ignore properties.
  279. inline
  280. boost::shared_ptr<boost::dynamic_property_map>
  281. ignore_other_properties(const std::string&,
  282. const boost::any&,
  283. const boost::any&) {
  284. return boost::shared_ptr<boost::dynamic_property_map>();
  285. }
  286. } // namespace boost
  287. #endif // BOOST_PROPERTY_MAP_DYNAMIC_PROPERTY_MAP_HPP