Removed deprecated interfaces
[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 /// Set properties according to \a newProps, overriding properties that already exist with new values
102 PropertySet& set( const PropertySet& newProps ) {
103 const std::map<PropertyKey, PropertyValue> *m = &newProps;
104 foreach(value_type i, *m)
105 set( i.first, i.second );
106 return *this;
107 }
108
109 /// Shorthand for (temporarily) adding properties
110 /** \par Example:
111 \code
112 PropertySet p()("method","BP")("verbose",1)("tol",1e-9)
113 \endcode
114 */
115 PropertySet operator()( const PropertyKey& key, const PropertyValue& val ) const {
116 PropertySet copy = *this;
117 return copy.set(key,val);
118 }
119
120 /// Sets a property (a key \a key with a corresponding value \a val, which is first converted from \a ValueType to string)
121 /** The implementation makes use of boost::lexical_cast.
122 * \tparam ValueType Type from which the value should be cast
123 * \throw IMPOSSIBLE_TYPECAST if the type cast cannot be done
124 */
125 template<typename ValueType>
126 PropertySet& setAsString( const PropertyKey& key, const ValueType& val ) {
127 try {
128 return set( key, boost::lexical_cast<std::string>(val) );
129 } catch( boost::bad_lexical_cast & ) {
130 DAI_THROWE(IMPOSSIBLE_TYPECAST,"Cannot cast value of property '" + key + "' to string.");
131 }
132 }
133
134 /// Converts the type of the property value corresponding with \a key from string to \a ValueType (if necessary)
135 /** The implementation makes use of boost::lexical_cast
136 * \tparam ValueType Type to which the value should be cast
137 * \throw IMPOSSIBLE_TYPECAST if the type cast cannot be done
138 */
139 template<typename ValueType>
140 void convertTo( const PropertyKey& key ) {
141 PropertyValue val = get(key);
142 if( val.type() != typeid(ValueType) ) {
143 DAI_ASSERT( val.type() == typeid(std::string) );
144 try {
145 set(key, boost::lexical_cast<ValueType>(getAs<std::string>(key)));
146 } catch(boost::bad_lexical_cast &) {
147 DAI_THROWE(IMPOSSIBLE_TYPECAST,"Cannot cast value of property '" + key + "' from string to desired type.");
148 }
149 }
150 }
151 //@}
152
153 //@}
154
155 /// \name Queries
156 //@{
157 /// Return number of key-value pairs
158 size_t size() const {
159 return std::map<PropertyKey, PropertyValue>::size();
160 }
161
162 /// Removes all key-value pairs
163 void clear() {
164 std::map<PropertyKey, PropertyValue>::clear();
165 }
166
167 /// Removes key-value pair with given \a key
168 size_t erase( const PropertyKey &key ) {
169 return std::map<PropertyKey, PropertyValue>::erase( key );
170 }
171
172 /// Check if a property with the given \a key is defined
173 bool hasKey( const PropertyKey& key ) const {
174 PropertySet::const_iterator x = find(key);
175 return (x != this->end());
176 }
177
178 /// Returns a set containing all keys
179 std::set<PropertyKey> keys() const {
180 std::set<PropertyKey> res;
181 const_iterator i;
182 for( i = begin(); i != end(); i++ )
183 res.insert( i->first );
184 return res;
185 }
186
187 /// Gets the value corresponding to \a key
188 /** \throw OBJECT_NOT_FOUND if the key cannot be found in \c *this
189 */
190 const PropertyValue& get( const PropertyKey& key ) const {
191 PropertySet::const_iterator x = find(key);
192 if( x == this->end() )
193 DAI_THROWE(OBJECT_NOT_FOUND,"PropertySet::get cannot find property '" + key + "'");
194 return x->second;
195 }
196
197 /// Gets the value corresponding to \a key, cast to \a ValueType
198 /** \tparam ValueType Type to which the value should be cast
199 * \throw OBJECT_NOT_FOUND if the key cannot be found in \c *this
200 * \throw IMPOSSIBLE_TYPECAST if the type cast cannot be done
201 */
202 template<typename ValueType>
203 ValueType getAs( const PropertyKey& key ) const {
204 try {
205 return boost::any_cast<ValueType>(get(key));
206 } catch( const boost::bad_any_cast & ) {
207 DAI_THROWE(IMPOSSIBLE_TYPECAST,"Cannot cast value of property '" + key + "' to desired type.");
208 return ValueType();
209 }
210 }
211
212 /// Gets the value corresponding to \a key, cast to \a ValueType, converting from a string if necessary
213 /** If the type of the value is already equal to \a ValueType, no conversion is done.
214 * Otherwise, the type of the value should be a std::string, in which case boost::lexical_cast is
215 * used to convert this to \a ValueType.
216 * \tparam ValueType Type to which the value should be cast/converted
217 * \throw OBJECT_NOT_FOUND if the key cannot be found in \c *this
218 * \throw IMPOSSIBLE_TYPECAST if the type cast cannot be done
219 */
220 template<typename ValueType>
221 ValueType getStringAs( const PropertyKey& key ) const {
222 PropertyValue val = get(key);
223 if( val.type() == typeid(ValueType) ) {
224 return boost::any_cast<ValueType>(val);
225 } else if( val.type() == typeid(std::string) ) {
226 try {
227 return boost::lexical_cast<ValueType>(getAs<std::string>(key));
228 } catch(boost::bad_lexical_cast &) {
229 DAI_THROWE(IMPOSSIBLE_TYPECAST,"Cannot cast value of property '" + key + "' from string to desired type.");
230 }
231 } else
232 DAI_THROWE(IMPOSSIBLE_TYPECAST,"Cannot cast value of property '" + key + "' from string to desired type.");
233 return ValueType();
234 }
235 //@}
236
237 /// \name Input/output
238 //@{
239 /// Writes a PropertySet object to an output stream.
240 /** It uses the format <tt>"[key1=val1,key2=val2,...,keyn=valn]"</tt>.
241 * \note Only a subset of all possible types is supported (see the implementation of this function).
242 * Adding support for more types has to be done by hand.
243 * \throw UNKNOWN_PROPERTY_TYPE if the type of a property value is not supported.
244 */
245 friend std::ostream& operator<< ( std::ostream& os, const PropertySet& ps );
246
247 /// Reads a PropertySet object from an input stream.
248 /** It expects a string in the format <tt>"[key1=val1,key2=val2,...,keyn=valn]"</tt>.
249 * Values are stored as strings.
250 * \throw MALFORMED_PROPERTY if the string is not in the expected format
251 */
252 friend std::istream& operator>> ( std::istream& is, PropertySet& ps );
253 //@}
254 };
255
256
257 } // end of namespace dai
258
259
260 #endif