// graph-tool -- a general graph modification and manipulation thingy // // Copyright (C) 2007-2012 Tiago de Paula Peixoto // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 3 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #ifndef GRAPH_PROPERTIES_HH #define GRAPH_PROPERTIES_HH #include #include #include #if (GCC_VERSION >= 40400) # include # include # include # include #else # include # include # include # include #endif #include #include #include #include #if (BOOST_VERSION >= 104000) # include # include #else # include # include #endif #include "fast_vector_property_map.hh" #include #include #include #include #include #include "graph.hh" #include "graph_exceptions.hh" // this file provides general functions for manipulating graph properties namespace graph_tool { using namespace std; using namespace boost; // Metaprogramming // =============== // // Metafunctions and data structures to deal with property maps // Global Property Types // --------------------- // global property types. only these types are allowed in property maps // Note: we must avoid a vector (and bools in general) since it is quite // broken, and use a vector instead! // see: http://www.gotw.ca/publications/N1211.pdf typedef mpl::vector, vector, vector, vector, vector, vector, python::object> value_types; extern const char* type_names[]; // respective type names (defined in // graph_properties.cc) // scalar types: types contained in value_types which are scalar typedef mpl::vector scalar_types; // integer_types: scalar types which are integer typedef mpl::vector integer_types; // floating_types: scalar types which are floating point typedef mpl::vector floating_types; struct make_vector { template struct apply { typedef vector type; }; }; // scalar_vector_types: vector types with floating point values typedef mpl::transform::type scalar_vector_types; // integer_vector_types: vector types with floating point values typedef mpl::transform::type integer_vector_types; // floating_vector_types: vector types with floating point values typedef mpl::transform::type floating_vector_types; // // Property Map Types // ------------------ // metafunction to generate the correct property map type given a value type and // an index map struct property_map_type { template struct apply { typedef checked_vector_property_map type; }; }; // metafunction to get the sequence of property map types of ValueTypes and // IndexMap struct property_map_types { // this wraps an unary metafunction class Bind into a unary metafunction, // i.e., it is an identity operation. I'm not sure why it's necessary, but // using pure unary bind expressions below didn't work for me, and this fixed // it. template struct bind_wrap1 { template struct apply { typedef typename Bind::template apply::type type; }; }; template > struct apply { typedef typename mpl::transform< ValueTypes, bind_wrap1 > >::type scalar_properties; // put index map itself typedef typename mpl::if_< IncludeIndexMap, typename mpl::push_back::type, scalar_properties >::type type; }; }; // Property map manipulation // ========================= // // Functions which deal with several aspects of property map manipulation // this functor tests whether or not a given boost::any object holds a type // contained in a given type Sequence template struct belongs { struct get_type { get_type(const boost::any& val, bool& found) : _val(val), _found(found) {} template void operator()(Type) const { const Type* ptr = any_cast(&_val); if (ptr != 0) _found = true; } const boost::any& _val; bool& _found; }; bool operator()(const boost::any& prop) { bool found = false; mpl::for_each(get_type(prop, found)); return found; } }; // this will return the name of a given type template class get_type_name { public: get_type_name(const char* names[] = type_names) : _type_names(type_names) { if (_all_names.empty()) { mpl::for_each (bind(get_all_names(), _1, ref(_type_names), ref(_all_names))); } } const string& operator()(const std::type_info& type) const { string* name; mpl::for_each (bind(find_name(), _1, ref(type), ref(_all_names), ref(name))); return *name; } const vector& all_names() const { return _all_names; } private: struct find_name { template void operator()(Type, const std::type_info& type, vector& all_names, string*& name) const { size_t index = mpl::find::type::pos::value; if (type == typeid(Type)) name = &all_names[index]; } }; struct get_all_names { template void operator()(Type, const char** type_names, vector& names) const { size_t index = mpl::find::type::pos::value; names.push_back(type_names[index]); } }; const char** _type_names; static vector _all_names; }; template vector get_type_name::_all_names; // // Extra Property Map Types // ======================== // handle type convertions // generic types template struct convert { Type1 operator()(const Type2& v) const { return do_convert(v, is_convertible()); } Type1 do_convert(const Type2& v, mpl::bool_) const { return Type1(v); } Type1 do_convert(const Type2& v, mpl::bool_) const { return specific_convert()(v); } template struct specific_convert { T1 operator()(const T2& v) const { throw bad_lexical_cast(); // default action } }; // specific specializations // python::object template struct specific_convert { T1 operator()(const python::object& v) const { python::extract x(v); if (x.check()) return x(); else throw bad_lexical_cast(); } }; // string template struct specific_convert { T1 operator()(const string& v) const { //uint8_t is not char, it is bool! if (is_same::value) return convert()(lexical_cast(v)); else return lexical_cast(v); } }; template struct specific_convert { string operator()(const T2& v) const { //uint8_t is not char, it is bool! if (is_same::value) return lexical_cast(convert()(v)); else return lexical_cast(v); } }; // vectors template struct specific_convert, vector > { vector operator()(const vector& v) const { vector v2(v.size()); convert c; for (size_t i = 0; i < v.size(); ++i) v2[i] = c(v[i]); return v2; } }; }; // python::object to string, to solve ambiguity template<> template<> struct convert::specific_convert { string operator()(const python::object& v) const { python::extract x(v); if (x.check()) return x(); else throw bad_lexical_cast(); } }; // the following class wraps a generic property map, so it can be used as a // property with a given Key and Value type. The keys and values are converted // to the desired Key and Value type, which may cause a performance impact, // since virtual functions are used. Should be used only when property map // access time is not crucial template class Converter = convert> class DynamicPropertyMapWrap { public: typedef Value value_type; typedef Value reference; typedef Key key_type; typedef read_write_property_map_tag category; template DynamicPropertyMapWrap(boost::any pmap, PropertyTypes) { ValueConverter* converter = 0; mpl::for_each (bind(choose_converter(), _1, ref(pmap), ref(converter))); if (converter == 0) throw bad_lexical_cast(); else _converter = tr1::shared_ptr(converter); } DynamicPropertyMapWrap() {} Value get(const Key& k) const { return (*_converter).get(k); } void put(const Key& k, const Value& val) { (*_converter).put(k, val); } private: class ValueConverter { public: virtual Value get(const Key& k) = 0; virtual void put(const Key& k, const Value& val) = 0; }; template class ValueConverterImp: public ValueConverter { public: ValueConverterImp(PropertyMap pmap): _pmap(pmap) {} typedef typename property_traits::value_type val_t; typedef typename property_traits::key_type key_t; virtual Value get(const Key& k) { return get_dispatch(_pmap, k, is_convertible::category, readable_property_map_tag>()); } virtual void put(const Key& k, const Value& val) { return put_dispatch(_pmap, k, _c_put(val), is_convertible::category, writable_property_map_tag>()); } template Value get_dispatch(PMap pmap, const typename property_traits::key_type& k, mpl::bool_) { return _c_get(boost::get(pmap, k)); } template Value get_dispatch(PMap pmap, const typename property_traits::key_type& k, mpl::bool_) { throw graph_tool::ValueException("Property map is not readable."); } template void put_dispatch(PMap pmap, const typename property_traits::key_type& k, typename property_traits::value_type val, mpl::bool_) { boost::put(pmap, k, val); } template void put_dispatch(PMap pmap, const typename property_traits::key_type& k, typename property_traits::value_type val, mpl::bool_) { throw ValueException("Property map is not writable."); } private: PropertyMap _pmap; Converter _c_get; Converter _c_put; }; struct choose_converter { template void operator()(PropertyMap, boost::any& dmap, ValueConverter*& converter) const { if (typeid(PropertyMap) == dmap.type()) converter = new ValueConverterImp (any_cast(dmap)); } }; tr1::shared_ptr _converter; }; template Value get(const graph_tool::DynamicPropertyMapWrap& pmap, ConvKey k) { Key key = k; return pmap.get(key); } template void put(graph_tool::DynamicPropertyMapWrap& pmap, Key k, const Value& val) { pmap.put(k, val); } // the following is hash functor which, will hash a vertex or edge descriptor // based on its index template class DescriptorHash : public unary_function { public: DescriptorHash() {} DescriptorHash(IndexMap index_map): _index_map(index_map) {} size_t operator()(typename IndexMap::key_type const& d) const { return hash_value(_index_map[d]); } private: IndexMap _index_map; }; // the following is a property map based on a hashed container, which uses the // above hash function for vertex or edge descriptors template class HashedDescriptorMap : public put_get_helper > { public: typedef DescriptorHash hashfc_t; typedef tr1::unordered_map map_t; typedef associative_property_map prop_map_t; typedef typename property_traits::value_type value_type; typedef typename property_traits::reference reference; typedef typename property_traits::key_type key_type; typedef typename property_traits::category category; HashedDescriptorMap(IndexMap index_map) : _base_map(new map_t(0, hashfc_t(index_map))), _prop_map(*_base_map) {} HashedDescriptorMap(){} reference operator[](const key_type& k) { return _prop_map[k]; } const reference operator[](const key_type& k) const { return _prop_map[k]; } private: shared_ptr _base_map; prop_map_t _prop_map; }; // this wraps a container as a property map which is automatically initialized // with a given default value template class InitializedPropertyMap : public put_get_helper > { public: typedef typename Container::value_type::second_type value_type; typedef value_type& reference; typedef typename Container::key_type key_type; typedef read_write_property_map_tag category; InitializedPropertyMap(Container& base_map, value_type def) : _base_map(&base_map), _default(def) {} InitializedPropertyMap(){} reference operator[](const key_type& k) { return get(k); } const reference operator[](const key_type& k) const { return get(k); } const reference get(const key_type& k) const { typename Container::iterator val; val = _base_map->find(k); if (val == _base_map->end()) val = _base_map->insert(make_pair(k, _default)).first; return val->second; } private: Container* _base_map; value_type _default; }; // the following is a property map which always returns a constant value template class ConstantPropertyMap : public put_get_helper > { public: typedef Value value_type; typedef value_type& reference; typedef Key key_type; typedef readable_property_map_tag category; ConstantPropertyMap(const value_type& c): _c(c) {} ConstantPropertyMap(){} const value_type& operator[](const key_type& k) const { return _c; } private: value_type _c; }; // this wraps an existing property map, but always converts its values to a // given type template class Converter = convert> class ConvertedPropertyMap { public: typedef Type value_type; typedef typename property_traits::value_type orig_type; typedef value_type reference; typedef typename property_traits::key_type key_type; typedef read_write_property_map_tag category; ConvertedPropertyMap(PropertyMap base_map) : _base_map(base_map) {} ConvertedPropertyMap(){} value_type do_get(const key_type& k) const { return _convert_to(get(_base_map, k)); } void do_put(const key_type& k, const value_type& v) { put(_base_map, k, _convert_from(v)); } private: PropertyMap _base_map; Converter _convert_to; Converter _convert_from; }; template Type get(ConvertedPropertyMap pmap, typename ConvertedPropertyMap::key_type k) { return pmap.do_get(k); } template void put(ConvertedPropertyMap pmap, typename property_traits::key_type k, const typename ConvertedPropertyMap::value_type& val) { pmap.do_put(k, val); } } // graph_tool namespace #endif