1 /* This file is part of libDAI - http://www.libdai.org/
3 * Copyright (c) 2006-2011, The libDAI authors. All rights reserved.
5 * Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
10 /// \brief Defines the Property and PropertySet classes, which are mainly used for managing parameters of inference algorithms
13 #ifndef __defined_libdai_properties_h
14 #define __defined_libdai_properties_h
19 #include <boost/any.hpp>
23 #include <dai/exceptions.h>
25 #include <boost/lexical_cast.hpp>
31 /// Type of the key of a Property
32 typedef std::string PropertyKey
;
34 /// Type of the value of a Property
35 typedef boost::any PropertyValue
;
37 /// A Property is a pair of a key and a corresponding value
38 typedef std::pair
<PropertyKey
, PropertyValue
> Property
;
41 /// Writes a Property object (key-value pair) to an output stream
42 /** \note Not all value types are automatically supported; if a type is unknown, an
43 * UNKNOWN_PROPERTY_TYPE exception is thrown. Adding support for a new type can
44 * be done by changing this function body.
46 std::ostream
& operator<< ( std::ostream
& os
, const Property
&p
);
49 /// Represents a set of properties, mapping keys (of type PropertyKey) to values (of type PropertyValue)
50 /** Properties are used for specifying parameters of algorithms in a convenient way, where the values of
51 * the parameters can be of different types (e.g., strings, doubles, integers, enums). A PropertySet is
52 * an attempt to mimic the functionality of a Python dictionary object in C++, using the boost::any class.
54 * A PropertySet can be converted to and from a string, using the following format:
56 * <tt>[key1=val1,key2=val2,...,keyn=valn]</tt>
59 * - the whole PropertySet is wrapped in square brackets ("[", "]")
60 * - all properties in the PropertySet are seperated by a comma (",")
61 * - each Property consists of:
62 * - the name of the key
63 * - an equality sign ("=")
64 * - its value (represented as a string)
66 * Also, a PropertySet provides functionality for converting the representation of
67 * individual values from some arbitrary type to and from std::string.
69 * \note Not all types are automatically supported; if a type is unknown, an UNKNOWN_PROPERTY_TYPE
70 * exception is thrown. Adding support for a new type can be done in the body of the
71 * operator<<(std::ostream &, const Property &).
73 class PropertySet
: private std::map
<PropertyKey
, PropertyValue
> {
75 /// \name Constructors and destructors
77 /// Default constructor
80 /// Construct from a string
81 /** \param s string in the format <tt>"[key1=val1,key2=val2,...,keyn=valn]"</tt>
83 PropertySet( const std::string
& s
) {
90 /// \name Setting property keys/values
92 /// Sets a property (a key \a key with a corresponding value \a val)
93 PropertySet
& set( const PropertyKey
& key
, const PropertyValue
& val
) {
94 this->operator[](key
) = val
;
98 /// Set properties according to \a newProps, overriding properties that already exist with new values
99 PropertySet
& set( const PropertySet
& newProps
) {
100 const std::map
<PropertyKey
, PropertyValue
> *m
= &newProps
;
101 bforeach(value_type i
, *m
)
102 set( i
.first
, i
.second
);
106 /// Shorthand for (temporarily) adding properties
109 PropertySet p()("method","BP")("verbose",1)("tol",1e-9)
112 PropertySet
operator()( const PropertyKey
& key
, const PropertyValue
& val
) const {
113 PropertySet copy
= *this;
114 return copy
.set(key
,val
);
117 /// Sets a property (a key \a key with a corresponding value \a val, which is first converted from \a ValueType to string)
118 /** The implementation makes use of boost::lexical_cast.
119 * \tparam ValueType Type from which the value should be cast
120 * \throw IMPOSSIBLE_TYPECAST if the type cast cannot be done
122 template<typename ValueType
>
123 PropertySet
& setAsString( const PropertyKey
& key
, const ValueType
& val
) {
125 return set( key
, boost::lexical_cast
<std::string
>(val
) );
126 } catch( boost::bad_lexical_cast
& ) {
127 DAI_THROWE(IMPOSSIBLE_TYPECAST
,"Cannot cast value of property '" + key
+ "' to string.");
131 /// Converts the type of the property value corresponding with \a key from string to \a ValueType (if necessary)
132 /** The implementation makes use of boost::lexical_cast
133 * \tparam ValueType Type to which the value should be cast
134 * \throw IMPOSSIBLE_TYPECAST if the type cast cannot be done
136 template<typename ValueType
>
137 void convertTo( const PropertyKey
& key
) {
138 PropertyValue val
= get(key
);
139 if( val
.type() != typeid(ValueType
) ) {
140 DAI_ASSERT( val
.type() == typeid(std::string
) );
142 set(key
, boost::lexical_cast
<ValueType
>(getAs
<std::string
>(key
)));
143 } catch(boost::bad_lexical_cast
&) {
144 DAI_THROWE(IMPOSSIBLE_TYPECAST
,"Cannot cast value of property '" + key
+ "' from string to desired type.");
154 /// Return number of key-value pairs
155 size_t size() const {
156 return std::map
<PropertyKey
, PropertyValue
>::size();
159 /// Removes all key-value pairs
161 std::map
<PropertyKey
, PropertyValue
>::clear();
164 /// Removes key-value pair with given \a key
165 size_t erase( const PropertyKey
&key
) {
166 return std::map
<PropertyKey
, PropertyValue
>::erase( key
);
169 /// Check if a property with the given \a key is defined
170 bool hasKey( const PropertyKey
& key
) const {
171 PropertySet::const_iterator x
= find(key
);
172 return (x
!= this->end());
175 /// Returns a set containing all keys
176 std::set
<PropertyKey
> keys() const {
177 std::set
<PropertyKey
> res
;
179 for( i
= begin(); i
!= end(); i
++ )
180 res
.insert( i
->first
);
184 /// Gets the value corresponding to \a key
185 /** \throw OBJECT_NOT_FOUND if the key cannot be found in \c *this
187 const PropertyValue
& get( const PropertyKey
& key
) const {
188 PropertySet::const_iterator x
= find(key
);
189 if( x
== this->end() )
190 DAI_THROWE(OBJECT_NOT_FOUND
,"PropertySet::get cannot find property '" + key
+ "'");
194 /// Gets the value corresponding to \a key, cast to \a ValueType
195 /** \tparam ValueType Type to which the value should be cast
196 * \throw OBJECT_NOT_FOUND if the key cannot be found in \c *this
197 * \throw IMPOSSIBLE_TYPECAST if the type cast cannot be done
199 template<typename ValueType
>
200 ValueType
getAs( const PropertyKey
& key
) const {
202 return boost::any_cast
<ValueType
>(get(key
));
203 } catch( const boost::bad_any_cast
& ) {
204 DAI_THROWE(IMPOSSIBLE_TYPECAST
,"Cannot cast value of property '" + key
+ "' to desired type.");
209 /// Gets the value corresponding to \a key, cast to \a ValueType, converting from a string if necessary
210 /** If the type of the value is already equal to \a ValueType, no conversion is done.
211 * Otherwise, the type of the value should be a std::string, in which case boost::lexical_cast is
212 * used to convert this to \a ValueType.
213 * \tparam ValueType Type to which the value should be cast/converted
214 * \throw OBJECT_NOT_FOUND if the key cannot be found in \c *this
215 * \throw IMPOSSIBLE_TYPECAST if the type cast cannot be done
217 template<typename ValueType
>
218 ValueType
getStringAs( const PropertyKey
& key
) const {
219 PropertyValue val
= get(key
);
220 if( val
.type() == typeid(ValueType
) ) {
221 return boost::any_cast
<ValueType
>(val
);
222 } else if( val
.type() == typeid(std::string
) ) {
224 return boost::lexical_cast
<ValueType
>(getAs
<std::string
>(key
));
225 } catch(boost::bad_lexical_cast
&) {
226 DAI_THROWE(IMPOSSIBLE_TYPECAST
,"Cannot cast value of property '" + key
+ "' from string to desired type.");
229 DAI_THROWE(IMPOSSIBLE_TYPECAST
,"Cannot cast value of property '" + key
+ "' from string to desired type.");
234 /// \name Input/output
236 /// Writes a PropertySet object to an output stream.
237 /** It uses the format <tt>"[key1=val1,key2=val2,...,keyn=valn]"</tt>.
238 * \note Only a subset of all possible types is supported (see the implementation of this function).
239 * Adding support for more types has to be done by hand.
240 * \throw UNKNOWN_PROPERTY_TYPE if the type of a property value is not supported.
242 friend std::ostream
& operator<< ( std::ostream
& os
, const PropertySet
& ps
);
244 /// Formats a PropertySet as a string
245 std::string
toString() const {
246 std::stringstream ss
;
251 /// Reads a PropertySet object from an input stream.
252 /** It expects a string in the format <tt>"[key1=val1,key2=val2,...,keyn=valn]"</tt>.
253 * Values are stored as strings.
254 * \throw MALFORMED_PROPERTY if the string is not in the expected format
256 friend std::istream
& operator>> ( std::istream
& is
, PropertySet
& ps
);
258 /// Reads a PropertySet from a string
259 void fromString( const std::string
& s
) {
260 std::stringstream
ss( s
);
267 } // end of namespace dai