Fixed three minor issues
[libdai.git] / include / dai / properties.h
1 /* This file is part of libDAI - http://www.libdai.org/
2 *
3 * libDAI is licensed under the terms of the GNU General Public License version
4 * 2, or (at your option) any later version. libDAI is distributed without any
5 * warranty. See the file COPYING for more details.
6 *
7 * Copyright (C) 2006-2010 Joris Mooij [joris dot mooij at libdai dot org]
8 * Copyright (C) 2006-2007 Radboud University Nijmegen, The Netherlands
9 */
10
11
12 /// \file
13 /// \brief Defines the Property and PropertySet classes, which are mainly used for managing parameters of inference algorithms
14
15
16 #ifndef __defined_libdai_properties_h
17 #define __defined_libdai_properties_h
18
19
20 #include <iostream>
21 #include <sstream>
22 #include <boost/any.hpp>
23 #include <map>
24 #include <vector>
25 #include <typeinfo>
26 #include <dai/exceptions.h>
27 #include <dai/util.h>
28 #include <boost/lexical_cast.hpp>
29
30
31 namespace dai {
32
33
34 /// Type of the key of a Property
35 typedef std::string PropertyKey;
36
37 /// Type of the value of a Property
38 typedef boost::any PropertyValue;
39
40 /// A Property is a pair of a key and a corresponding value
41 typedef std::pair<PropertyKey, PropertyValue> Property;
42
43
44 /// Writes a Property object (key-value pair) to an output stream
45 /** \note Not all value types are automatically supported; if a type is unknown, an
46 * UNKNOWN_PROPERTY_TYPE exception is thrown. Adding support for a new type can
47 * be done by changing this function body.
48 */
49 std::ostream& operator<< ( std::ostream & os, const Property &p );
50
51
52 /// Represents a set of properties, mapping keys (of type PropertyKey) to values (of type PropertyValue)
53 /** Properties are used for specifying parameters of algorithms in a convenient way, where the values of
54 * the parameters can be of different types (e.g., strings, doubles, integers, enums). A PropertySet is
55 * an attempt to mimic the functionality of a Python dictionary object in C++, using the boost::any class.
56 *
57 * A PropertySet can be converted to and from a string, using the following format:
58 *
59 * <tt>[key1=val1,key2=val2,...,keyn=valn]</tt>
60 *
61 * That is,
62 * - the whole PropertySet is wrapped in square brackets ("[", "]")
63 * - all properties in the PropertySet are seperated by a comma (",")
64 * - each Property consists of:
65 * - the name of the key
66 * - an equality sign ("=")
67 * - its value (represented as a string)
68 *
69 * Also, a PropertySet provides functionality for converting the representation of
70 * individual values from some arbitrary type to and from std::string.
71 *
72 * \note Not all types are automatically supported; if a type is unknown, an UNKNOWN_PROPERTY_TYPE
73 * exception is thrown. Adding support for a new type can be done in the body of the
74 * operator<<(std::ostream &, const Property &).
75 */
76 class PropertySet : private std::map<PropertyKey, PropertyValue> {
77 public:
78 /// \name Constructors and destructors
79 //@{
80 /// Default constructor
81 PropertySet() {}
82
83 /// Construct from a string
84 /** \param s string in the format <tt>"[key1=val1,key2=val2,...,keyn=valn]"</tt>
85 */
86 PropertySet( const std::string& s ) {
87 std::stringstream ss;
88 ss << s;
89 ss >> *this;
90 }
91 //@}
92
93 /// \name Setting property keys/values
94 //@{
95 /// Sets a property (a key \a key with a corresponding value \a val)
96 PropertySet& set( const PropertyKey& key, const PropertyValue& val ) {
97 this->operator[](key) = val;
98 return *this;
99 }
100
101 /// Sets a property (a key \a key with a corresponding value \a val)
102 /** \deprecated Please use dai::PropertySet::set(const PropertyKey&, const PropertyValue&) instead
103 */
104 PropertySet& Set( const PropertyKey& key, const PropertyValue& val ) {
105 return set( key, val );
106 }
107
108 /// Set properties according to \a newProps, overriding properties that already exist with new values
109 PropertySet& set( const PropertySet& newProps ) {
110 const std::map<PropertyKey, PropertyValue> *m = &newProps;
111 foreach(value_type i, *m)
112 set( i.first, i.second );
113 return *this;
114 }
115
116 /// Set properties according to \a newProps, overriding properties that already exist with new values
117 /** \deprecated Please use dai::PropertySet::set(const PropertySet&) instead
118 */
119 PropertySet& Set( const PropertySet& newProps ) {
120 return set( newProps );
121 }
122
123 /// Shorthand for (temporarily) adding properties
124 /** \par Example:
125 \code
126 PropertySet p()("method","BP")("verbose",1)("tol",1e-9)
127 \endcode
128 */
129 PropertySet operator()( const PropertyKey& key, const PropertyValue& val ) const {
130 PropertySet copy = *this;
131 return copy.set(key,val);
132 }
133
134 /// Sets a property (a key \a key with a corresponding value \a val, which is first converted from \a ValueType to string)
135 /** The implementation makes use of boost::lexical_cast.
136 * \tparam ValueType Type from which the value should be cast
137 * \throw IMPOSSIBLE_TYPECAST if the type cast cannot be done
138 */
139 template<typename ValueType>
140 PropertySet& setAsString( const PropertyKey& key, const ValueType& val ) {
141 try {
142 return set( key, boost::lexical_cast<std::string>(val) );
143 } catch( boost::bad_lexical_cast & ) {
144 DAI_THROWE(IMPOSSIBLE_TYPECAST,"Cannot cast value of property '" + key + "' to string.");
145 }
146 }
147
148 /// Converts the type of the property value corresponding with \a key from string to \a ValueType (if necessary)
149 /** The implementation makes use of boost::lexical_cast
150 * \tparam ValueType Type to which the value should be cast
151 * \throw IMPOSSIBLE_TYPECAST if the type cast cannot be done
152 */
153 template<typename ValueType>
154 void convertTo( const PropertyKey& key ) {
155 PropertyValue val = get(key);
156 if( val.type() != typeid(ValueType) ) {
157 DAI_ASSERT( val.type() == typeid(std::string) );
158 try {
159 set(key, boost::lexical_cast<ValueType>(getAs<std::string>(key)));
160 } catch(boost::bad_lexical_cast &) {
161 DAI_THROWE(IMPOSSIBLE_TYPECAST,"Cannot cast value of property '" + key + "' from string to desired type.");
162 }
163 }
164 }
165
166 /// Converts the type of the property value corresponding with \a key from string to \a ValueType (if necessary)
167 /** The implementation makes use of boost::lexical_cast
168 * \tparam ValueType Type to which the value should be cast
169 * \throw IMPOSSIBLE_TYPECAST if the type cast cannot be done
170 * \deprecated Please use dai::PropertySet::convertTo() instead
171 */
172 template<typename ValueType>
173 void ConvertTo( const PropertyKey& key ) {
174 convertTo<ValueType>( key );
175 }
176 //@}
177
178 //@}
179
180 /// \name Queries
181 //@{
182 /// Return number of key-value pairs
183 size_t size() const {
184 return std::map<PropertyKey, PropertyValue>::size();
185 }
186
187 /// Removes all key-value pairs
188 void clear() {
189 std::map<PropertyKey, PropertyValue>::clear();
190 }
191
192 /// Removes key-value pair with given \a key
193 size_t erase( const PropertyKey &key ) {
194 return std::map<PropertyKey, PropertyValue>::erase( key );
195 }
196
197 /// Check if a property with the given \a key is defined
198 bool hasKey( const PropertyKey& key ) const {
199 PropertySet::const_iterator x = find(key);
200 return (x != this->end());
201 }
202
203 /// Returns a set containing all keys
204 std::set<PropertyKey> keys() const {
205 std::set<PropertyKey> res;
206 const_iterator i;
207 for( i = begin(); i != end(); i++ )
208 res.insert( i->first );
209 return res;
210 }
211
212 /// Gets the value corresponding to \a key
213 /** \throw OBJECT_NOT_FOUND if the key cannot be found in \c *this
214 */
215 const PropertyValue& get( const PropertyKey& key ) const {
216 PropertySet::const_iterator x = find(key);
217 if( x == this->end() )
218 DAI_THROWE(OBJECT_NOT_FOUND,"PropertySet::get cannot find property '" + key + "'");
219 return x->second;
220 }
221
222 /// Gets the value corresponding to \a key
223 /** \throw OBJECT_NOT_FOUND if the key cannot be found in \c *this
224 * \deprecated Please use dai::PropertySet::get() instead
225 */
226 const PropertyValue& Get( const PropertyKey& key ) const {
227 return get( key );
228 }
229
230 /// Gets the value corresponding to \a key, cast to \a ValueType
231 /** \tparam ValueType Type to which the value should be cast
232 * \throw OBJECT_NOT_FOUND if the key cannot be found in \c *this
233 * \throw IMPOSSIBLE_TYPECAST if the type cast cannot be done
234 */
235 template<typename ValueType>
236 ValueType getAs( const PropertyKey& key ) const {
237 try {
238 return boost::any_cast<ValueType>(get(key));
239 } catch( const boost::bad_any_cast & ) {
240 DAI_THROWE(IMPOSSIBLE_TYPECAST,"Cannot cast value of property '" + key + "' to desired type.");
241 return ValueType();
242 }
243 }
244
245 /// Gets the value corresponding to \a key, cast to \a ValueType
246 /** \tparam ValueType Type to which the value should be cast
247 * \throw OBJECT_NOT_FOUND if the key cannot be found in \c *this
248 * \throw IMPOSSIBLE_TYPECAST if the type cast cannot be done
249 * \deprecated Please use dai::PropertySet::getAs() instead
250 */
251 template<typename ValueType>
252 ValueType GetAs( const PropertyKey& key ) const {
253 return getAs<ValueType>( key );
254 }
255
256 /// Gets the value corresponding to \a key, cast to \a ValueType, converting from a string if necessary
257 /** If the type of the value is already equal to \a ValueType, no conversion is done.
258 * Otherwise, the type of the value should be a std::string, in which case boost::lexical_cast is
259 * used to convert this to \a ValueType.
260 * \tparam ValueType Type to which the value should be cast/converted
261 * \throw OBJECT_NOT_FOUND if the key cannot be found in \c *this
262 * \throw IMPOSSIBLE_TYPECAST if the type cast cannot be done
263 */
264 template<typename ValueType>
265 ValueType getStringAs( const PropertyKey& key ) const {
266 PropertyValue val = get(key);
267 if( val.type() == typeid(ValueType) ) {
268 return boost::any_cast<ValueType>(val);
269 } else if( val.type() == typeid(std::string) ) {
270 try {
271 return boost::lexical_cast<ValueType>(getAs<std::string>(key));
272 } catch(boost::bad_lexical_cast &) {
273 DAI_THROWE(IMPOSSIBLE_TYPECAST,"Cannot cast value of property '" + key + "' from string to desired type.");
274 }
275 } else
276 DAI_THROWE(IMPOSSIBLE_TYPECAST,"Cannot cast value of property '" + key + "' from string to desired type.");
277 return ValueType();
278 }
279 //@}
280
281 /// \name Input/output
282 //@{
283 /// Writes a PropertySet object to an output stream.
284 /** It uses the format <tt>"[key1=val1,key2=val2,...,keyn=valn]"</tt>.
285 * \note Only a subset of all possible types is supported (see the implementation of this function).
286 * Adding support for more types has to be done by hand.
287 * \throw UNKNOWN_PROPERTY_TYPE if the type of a property value is not supported.
288 */
289 friend std::ostream& operator<< ( std::ostream& os, const PropertySet& ps );
290
291 /// Reads a PropertySet object from an input stream.
292 /** It expects a string in the format <tt>"[key1=val1,key2=val2,...,keyn=valn]"</tt>.
293 * Values are stored as strings.
294 * \throw MALFORMED_PROPERTY if the string is not in the expected format
295 */
296 friend std::istream& operator>> ( std::istream& is, PropertySet& ps );
297 //@}
298 };
299
300
301 } // end of namespace dai
302
303
304 #endif