graph_properties.hh 20.6 KB
Newer Older
Tiago Peixoto's avatar
Tiago Peixoto committed
1
2
// graph-tool -- a general graph modification and manipulation thingy
//
Tiago Peixoto's avatar
Tiago Peixoto committed
3
// Copyright (C) 2006-2020 Tiago de Paula Peixoto <tiago@skewed.de>
Tiago Peixoto's avatar
Tiago Peixoto committed
4
5
6
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
Tiago Peixoto's avatar
Tiago Peixoto committed
7
// as published by the Free Software Foundation; either version 3
Tiago Peixoto's avatar
Tiago Peixoto committed
8
9
10
11
12
13
14
15
// 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
16
// along with this program. If not, see <http://www.gnu.org/licenses/>.
Tiago Peixoto's avatar
Tiago Peixoto committed
17
18
19
20

#ifndef GRAPH_PROPERTIES_HH
#define GRAPH_PROPERTIES_HH

21
#include <typeinfo>
Tiago Peixoto's avatar
Tiago Peixoto committed
22
#include <string>
23
#include <vector>
Tiago Peixoto's avatar
Tiago Peixoto committed
24
25
26
#include <memory>
#include <random>
#include <functional>
27
#include <boost/functional/hash.hpp>
28
#include <boost/python/object.hpp>
29
#include <boost/python/extract.hpp>
30

31
32
33
34
35
36
37
38
#include <boost/version.hpp>
#if (BOOST_VERSION >= 104000)
#   include <boost/property_map/property_map.hpp>
#   include <boost/property_map/dynamic_property_map.hpp>
#else
#   include <boost/property_map.hpp>
#   include <boost/dynamic_property_map.hpp>
#endif
39
#include "fast_vector_property_map.hh"
40
41
#include <boost/mpl/vector.hpp>
#include <boost/mpl/for_each.hpp>
42
43
#include <boost/mpl/transform.hpp>
#include <boost/mpl/find.hpp>
Tiago Peixoto's avatar
Tiago Peixoto committed
44

45
#include "graph.hh"
46
#include "graph_exceptions.hh"
47
#include "hash_map_wrap.hh"
48

49
// this file provides general functions for manipulating graph properties
50
51

namespace graph_tool
Tiago Peixoto's avatar
Tiago Peixoto committed
52
53
{

54
55
56
57
58
// Metaprogramming
// ===============
//
// Metafunctions and data structures to deal with property maps

59
60
// Global Property Types
// ---------------------
61
// global property types. only these types are allowed in property maps
62
63
64
65
// Note: we must avoid a vector<bool> (and bools in general) since it is quite
//       broken, and use a vector<uint8_t> instead!
//       see: http://www.gotw.ca/publications/N1211.pdf

66
67
68
69
70
71
typedef boost::mpl::vector15<uint8_t, int16_t, int32_t, int64_t, double,
                             long double, std::string, std::vector<uint8_t>,
                             std::vector<int16_t>, std::vector<int32_t>,
                             std::vector<int64_t>, std::vector<double>,
                             std::vector<long double>, std::vector<std::string>,
                             boost::python::object> value_types;
72

73
74
extern const char* type_names[]; // respective type names (defined in
                                 // graph_properties.cc)
75
76

// scalar types: types contained in value_types which are scalar
77
typedef boost::mpl::vector6<uint8_t, int16_t, int32_t, int64_t, double, long double>
78
    scalar_types;
79
80

// integer_types: scalar types which are integer
81
typedef boost::mpl::vector4<uint8_t, int16_t, int32_t, int64_t> integer_types;
82
83

// floating_types: scalar types which are floating point
84
typedef boost::mpl::vector2<double, long double> floating_types;
85
86
87

struct make_vector
{
88
    template <class ValueType> struct apply { typedef std::vector<ValueType> type; };
89
90
91
};

// scalar_vector_types: vector types with floating point values
Tiago Peixoto's avatar
Tiago Peixoto committed
92
typedef boost::mpl::transform<scalar_types,make_vector>::type scalar_vector_types;
93
94

// integer_vector_types: vector types with floating point values
Tiago Peixoto's avatar
Tiago Peixoto committed
95
typedef boost::mpl::transform<integer_types,make_vector>::type integer_vector_types;
96
97

// floating_vector_types: vector types with floating point values
Tiago Peixoto's avatar
Tiago Peixoto committed
98
typedef boost::mpl::transform<floating_types,make_vector>::type floating_vector_types;
99

100
101
102
//
// Property Map Types
// ------------------
103

104
105
// metafunction to generate the correct property map type given a value type and
// an index map
106
107
108
109
110
struct property_map_type
{
    template <class ValueType, class IndexMap>
    struct apply
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
111
        typedef boost::checked_vector_property_map<ValueType,IndexMap> type;
112
    };
113
114
};

115
116
// metafunction to get the sequence of property map types of ValueTypes and
// IndexMap
117
118
119
120
121
122
123
124
125
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 <class Bind>
    struct bind_wrap1
    {
126
        template <class T1> struct apply
127
128
129
        { typedef typename Bind::template apply<T1>::type type; };
    };

130
    template <class ValueTypes, class IndexMap,
Tiago Peixoto's avatar
Tiago Peixoto committed
131
              class IncludeIndexMap = boost::mpl::bool_<true>>
132
133
    struct apply
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
134
        typedef typename boost::mpl::transform<
135
            ValueTypes,
Tiago Peixoto's avatar
Tiago Peixoto committed
136
137
138
            bind_wrap1<boost::mpl::bind2<property_map_type,
                                         boost::mpl::_1,
                                         IndexMap>>
139
140
            >::type scalar_properties;

141
        // put index map itself
Tiago Peixoto's avatar
Tiago Peixoto committed
142
        typedef typename boost::mpl::if_<
143
            IncludeIndexMap,
Tiago Peixoto's avatar
Tiago Peixoto committed
144
            typename boost::mpl::push_back<scalar_properties,IndexMap>::type,
145
146
            scalar_properties
            >::type type;
147
    };
148
};
149

150
151
152
153
154
// Property map manipulation
// =========================
//
// Functions which deal with several aspects of property map manipulation

155

156
157
158
159
160
161
162
163
164
165
166
167
168
// this functor tests whether or not a given boost::any object holds a type
// contained in a given type Sequence
template <class Sequence>
struct belongs
{
    struct get_type
    {
        get_type(const boost::any& val, bool& found)
            : _val(val), _found(found) {}

        template <class Type>
        void operator()(Type) const
        {
Tiago Peixoto's avatar
Tiago Peixoto committed
169
            const Type* ptr = boost::any_cast<Type>(&_val);
170
171
172
173
174
175
176
177
178
179
180
            if (ptr != 0)
                _found = true;
        }

        const boost::any& _val;
        bool& _found;
    };

    bool operator()(const boost::any& prop)
    {
        bool found = false;
Tiago Peixoto's avatar
Tiago Peixoto committed
181
        boost::mpl::for_each<Sequence>(get_type(prop, found));
182
183
184
185
186
187
188
189
190
191
192
        return found;
    }
};

// this will return the name of a given type
template <class TypeSequence = value_types,
          class NamedSequence = value_types>
class get_type_name
{
public:
    get_type_name(const char* names[] = type_names)
193
        : _type_names(names)
194
195
196
    {
        if (_all_names.empty())
        {
Tiago Peixoto's avatar
Tiago Peixoto committed
197
            boost::mpl::for_each<TypeSequence>
198
199
                (std::bind(get_all_names(), std::placeholders::_1,
                      std::ref(_type_names), std::ref(_all_names)));
200
201
202
        }
    }

203
    const std::string& operator()(const std::type_info& type) const
204
    {
205
        std::string const* name;
Tiago Peixoto's avatar
Tiago Peixoto committed
206
        boost::mpl::for_each<TypeSequence>
207
208
            (std::bind(find_name(), std::placeholders::_1, std::ref(type),
                       std::ref(_all_names), std::ref(name)));
209
210
211
        return *name;
    }

212
    const std::vector<std::string>& all_names() const
213
214
215
216
217
218
219
220
    {
        return _all_names;
    }

private:
    struct find_name
    {
        template <class Type>
221
        void operator()(Type, const std::type_info& type,
222
223
                        const std::vector<std::string>& all_names,
                        std::string const*& name) const
224
        {
Tiago Peixoto's avatar
Tiago Peixoto committed
225
            size_t index = boost::mpl::find<TypeSequence,Type>::type::pos::value;
226
227
228
229
230
231
232
            if (type == typeid(Type))
                name = &all_names[index];
        }
    };

    struct get_all_names
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
233
        typedef void result_type;
234
        template <class Type>
235
        void operator()(Type, const char** t_names,
236
                        std::vector<std::string>& names) const
237
        {
Tiago Peixoto's avatar
Tiago Peixoto committed
238
            size_t index = boost::mpl::find<NamedSequence,Type>::type::pos::value;
239
            names.push_back(t_names[index]);
240
241
242
243
        }
    };

    const char** _type_names;
244
    std::vector<std::string> _all_names;
245
246
};

247
248
249
250
//
// Extra Property Map Types
// ========================

251
252
253
254
255
256
257
258
259

// handle type convertions

// generic types
template <class Type1, class Type2>
struct convert
{
    Type1 operator()(const Type2& v) const
    {
260
        if constexpr (std::is_same<Type1, boost::python::object>::value)
261
        {
262
            return boost::python::object(v);
263
        }
264
265
266
267
268
        else if constexpr (std::is_convertible<Type2,Type1>::value)
        {
            return Type1(v);
        }
        else if constexpr (std::is_same<Type2, boost::python::object>::value)
269
        {
Tiago Peixoto's avatar
Tiago Peixoto committed
270
            boost::python::extract<Type1> x(v);
271
272
273
            if (x.check())
                return x();
            else
Tiago Peixoto's avatar
Tiago Peixoto committed
274
                throw boost::bad_lexical_cast();
275
        }
276
        else if constexpr (std::is_same<Type2, std::string>::value)
277
278
        {
            //uint8_t is not char, it is bool!
279
280
            if (std::is_same<Type1, uint8_t>::value)
                return convert<Type1,int>()(boost::lexical_cast<int>(v));
281
            else
282
                return boost::lexical_cast<Type1>(v);
283
        }
284
        else if constexpr (std::is_same<Type1, std::string>::value)
285
286
        {
            //uint8_t is not char, it is bool!
287
288
            if (std::is_same<Type2, uint8_t>::value)
                return boost::lexical_cast<std::string>(convert<int,Type2>()(v));
289
            else
290
                return boost::lexical_cast<std::string>(v);
291
        }
292
293
294
295
296
297
298
299
300
301
302
303
304
305
        else
        {
            return specific_convert<Type1, Type2>()(v);
        }
    }

    // default action
    template <class T1, class T2>
    struct specific_convert
    {
        T1 operator()(const T2&) const
        {
            throw boost::bad_lexical_cast();
        }
306
307
    };

308
309
    // specific specializations

310
311
    // vectors
    template <class T1, class T2>
312
    struct specific_convert<std::vector<T1>, std::vector<T2>>
313
    {
314
        std::vector<T1> operator()(const std::vector<T2>& v) const
315
        {
316
            std::vector<T1> v2(v.size());
317
318
319
320
321
322
323
324
325
            convert<T1,T2> c;
            for (size_t i = 0; i < v.size(); ++i)
                v2[i] = c(v[i]);
            return v2;
        }
    };

};

326
327
328
329
330
331
332
333
334
335
// No op
template <class Type1>
struct convert<Type1, Type1>
{
    const Type1& operator()(const Type1& v) const
    {
        return v;
    }
};

336
337
338
339
340
// 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
341
342
template <class Value, class Key,
          template <class T1, class T2> class Converter = convert>
343
344
345
346
347
348
class DynamicPropertyMapWrap
{
public:
    typedef Value value_type;
    typedef Value reference;
    typedef Key key_type;
Tiago Peixoto's avatar
Tiago Peixoto committed
349
    typedef boost::read_write_property_map_tag category;
350

351
352
    template <class PropertyTypes>
    DynamicPropertyMapWrap(boost::any pmap, PropertyTypes)
353
354
    {
        ValueConverter* converter = 0;
Tiago Peixoto's avatar
Tiago Peixoto committed
355
        boost::mpl::for_each<PropertyTypes>
356
            (std::bind(choose_converter(), std::placeholders::_1, std::ref(pmap),
Tiago Peixoto's avatar
Tiago Peixoto committed
357
                       std::ref(converter)));
358
        if (converter == 0)
Tiago Peixoto's avatar
Tiago Peixoto committed
359
            throw boost::bad_lexical_cast();
360
        else
361
            _converter = std::shared_ptr<ValueConverter>(converter);
362
    }
363

364
365
    DynamicPropertyMapWrap() {}

366
367
    Value get(const Key& k) const
    {
368
        return (*_converter).get(k);
369
370
371
372
    }

    void put(const Key& k, const Value& val)
    {
373
        (*_converter).put(k, val);
374
375
376
    }

private:
377
378
379
    class ValueConverter
    {
    public:
380
381
        virtual Value get(const Key& k) = 0;
        virtual void put(const Key& k, const Value& val) = 0;
382
        virtual ~ValueConverter() {}
383
384
    };

385
    template <class PropertyMap>
386
387
388
    class ValueConverterImp: public ValueConverter
    {
    public:
389
        ValueConverterImp(PropertyMap pmap): _pmap(pmap) {}
390
        virtual ~ValueConverterImp() {}
Tiago Peixoto's avatar
Tiago Peixoto committed
391
392
        typedef typename boost::property_traits<PropertyMap>::value_type val_t;
        typedef typename boost::property_traits<PropertyMap>::key_type key_t;
393
394
395

        virtual Value get(const Key& k)
        {
396
            return get_dispatch(_pmap, k,
397
398
                                std::is_convertible<typename boost::property_traits<PropertyMap>::category,
                                                    boost::readable_property_map_tag>());
399
400
401
        }

        virtual void put(const Key& k, const Value& val)
402
        {
403
            put_dispatch(_pmap, k, _c_put(val),
404
405
                         std::is_convertible<typename boost::property_traits<PropertyMap>::category,
                                             boost::writable_property_map_tag>());
406
407
408
        }

        template <class PMap>
409
        Value get_dispatch(PMap&& pmap, const typename boost::property_traits<std::remove_reference_t<PMap>>::key_type& k,
Tiago Peixoto's avatar
Tiago Peixoto committed
410
                           std::true_type)
411
412
413
        {
            return _c_get(boost::get(pmap, k));
        }
414

415
        template <class PMap>
416
        Value get_dispatch(PMap&&, const typename boost::property_traits<std::remove_reference_t<PMap>>::key_type&,
Tiago Peixoto's avatar
Tiago Peixoto committed
417
                           std::false_type)
418
419
        {
            throw graph_tool::ValueException("Property map is not readable.");
420
        }
421

422
        template <class PMap>
423
424
        void put_dispatch(PMap&& pmap, const typename boost::property_traits<std::remove_reference_t<PMap>>::key_type& k,
                          typename boost::property_traits<std::remove_reference_t<PMap>>::value_type val,
Tiago Peixoto's avatar
Tiago Peixoto committed
425
                          std::true_type)
426
427
428
429
430
        {
            boost::put(pmap, k, val);
        }

        template <class PMap>
431
432
        void put_dispatch(PMap&&, const typename boost::property_traits<std::remove_reference_t<PMap>>::key_type&,
                          typename boost::property_traits<std::remove_reference_t<PMap>>::value_type,
Tiago Peixoto's avatar
Tiago Peixoto committed
433
                          std::false_type)
434
435
436
        {
            throw ValueException("Property map is not writable.");
        }
437

438
439
    private:
        PropertyMap _pmap;
440
441
        Converter<Value, val_t> _c_get;
        Converter<val_t, Value> _c_put;
442
443
444
445
    };

    struct choose_converter
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
446
        typedef void return_type;
447
448
        template <class PropertyMap>
        void operator()(PropertyMap, boost::any& dmap,
449
450
                        ValueConverter*& converter) const
        {
451
452
            if (typeid(PropertyMap) == dmap.type())
                converter = new ValueConverterImp<PropertyMap>
Tiago Peixoto's avatar
Tiago Peixoto committed
453
                    (boost::any_cast<PropertyMap>(dmap));
454
455
456
        }
    };

457
    std::shared_ptr<ValueConverter> _converter;
458
459
};

460
template <class Value, class Key, class ConvKey>
461
Value get(const graph_tool::DynamicPropertyMapWrap<Value,Key>& pmap,
462
          ConvKey k)
463
{
464
465
    Key key = k;
    return pmap.get(key);
466
467
468
}

template <class Value, class Key>
469
470
void put(graph_tool::DynamicPropertyMapWrap<Value,Key>& pmap,
         Key k, const Value& val)
471
{
472
    pmap.put(k, val);
473
474
}

475
476
// the following is hash functor which, will hash a vertex or edge descriptor
// based on its index
477
template <class IndexMap>
478
class DescriptorHash
479
    : public std::unary_function<typename IndexMap::key_type, size_t>
480
481
482
483
{
public:
    DescriptorHash() {}
    DescriptorHash(IndexMap index_map): _index_map(index_map) {}
484
    size_t operator()(typename IndexMap::key_type const& d) const
485
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
486
487
        std::hash<typename IndexMap::value_type> hash;
        return hash(_index_map[d]);
488
    }
489
490
491
492
private:
    IndexMap _index_map;
};

493
494
// the following is a property map based on a hashed container, which uses the
// above hash function for vertex or edge descriptors
495
496
template <class IndexMap, class Value>
class HashedDescriptorMap
Tiago Peixoto's avatar
Tiago Peixoto committed
497
    : public boost::put_get_helper<Value&, HashedDescriptorMap<IndexMap,Value>>
498
499
500
{
public:
    typedef DescriptorHash<IndexMap> hashfc_t;
501
    typedef std::unordered_map<typename IndexMap::key_type,Value,hashfc_t>
502
        map_t;
Tiago Peixoto's avatar
Tiago Peixoto committed
503
    typedef boost::associative_property_map<map_t> prop_map_t;
504

Tiago Peixoto's avatar
Tiago Peixoto committed
505
506
507
508
    typedef typename boost::property_traits<prop_map_t>::value_type value_type;
    typedef typename boost::property_traits<prop_map_t>::reference reference;
    typedef typename boost::property_traits<prop_map_t>::key_type key_type;
    typedef typename boost::property_traits<prop_map_t>::category category;
509

510
    HashedDescriptorMap(IndexMap index_map)
511
512
        : _base_map(std::make_shared<map_t>(0, hashfc_t(index_map))),
        _prop_map(*_base_map) {}
513
    HashedDescriptorMap(){}
514

515
516
517
518
    reference operator[](const key_type& k) { return _prop_map[k]; }
    const reference operator[](const key_type& k) const { return _prop_map[k]; }

private:
519
    std::shared_ptr<map_t> _base_map;
520
521
522
523
524
525
526
527
    prop_map_t _prop_map;
};


// this wraps a container as a property map which is automatically initialized
// with a given default value
template <class Container>
class InitializedPropertyMap
Tiago Peixoto's avatar
Tiago Peixoto committed
528
529
    : public boost::put_get_helper<typename Container::value_type::second_type&,
                                   InitializedPropertyMap<Container>>
530
531
532
533
534
{
public:
    typedef typename Container::value_type::second_type value_type;
    typedef value_type& reference;
    typedef typename Container::key_type key_type;
Tiago Peixoto's avatar
Tiago Peixoto committed
535
    typedef boost::read_write_property_map_tag category;
536
537

    InitializedPropertyMap(Container& base_map, value_type def)
538
        : _base_map(&base_map), _default(def) {}
539
540
541
542
    InitializedPropertyMap(){}

    reference operator[](const key_type& k)
    {
543
        return get(k);
544
545
    }

546
    reference operator[](const key_type& k) const
547
    {
548
        return get(k);
549
550
    }

551
    reference get(const key_type& k) const
552
    {
553
554
555
        typename Container::iterator val;
        val = _base_map->find(k);
        if (val == _base_map->end())
556
            val = _base_map->insert({k, _default}).first;
557
        return val->second;
558
559
560
561
562
563
564
    }

private:
    Container* _base_map;
    value_type _default;
};

565
// the following is a property map which always returns the same value
566
567
template <class Value, class Key>
class ConstantPropertyMap
Tiago Peixoto's avatar
Tiago Peixoto committed
568
    : public boost::put_get_helper<Value, ConstantPropertyMap<Value,Key>>
569
570
571
572
573
{
public:
    typedef Value value_type;
    typedef value_type& reference;
    typedef Key key_type;
Tiago Peixoto's avatar
Tiago Peixoto committed
574
    typedef boost::readable_property_map_tag category;
575

576
577
    ConstantPropertyMap(const value_type& c): c(c) {}
    ConstantPropertyMap(): c() {}
578
    ConstantPropertyMap(const ConstantPropertyMap& o): c(o.c) {}
579

580
    const value_type& operator[](const key_type&) const { return c; }
581

582
583
    ConstantPropertyMap& operator=(const ConstantPropertyMap& other)
    {
584
        c = other.c;
585
586
587
        return *this;
    }

588
589
private:
    value_type c;
590
591
};

592
593
594
template <class Value, class Key>
void put(const ConstantPropertyMap<Value, Key>&, const Key&, const Value&) {}

Tiago Peixoto's avatar
Tiago Peixoto committed
595
// the following is a property map which always returns one
596
template <class Value, class Key>
Tiago Peixoto's avatar
Tiago Peixoto committed
597
class UnityPropertyMap
598
    : public boost::put_get_helper<Value, UnityPropertyMap<Value, Key>>
Tiago Peixoto's avatar
Tiago Peixoto committed
599
600
{
public:
601
    typedef Value value_type;
Tiago Peixoto's avatar
Tiago Peixoto committed
602
603
604
605
606
    typedef value_type reference;
    typedef Key key_type;
    typedef boost::readable_property_map_tag category;

    template <class K>
607
    constexpr value_type operator[](const K&) const { return value_type(1); }
Tiago Peixoto's avatar
Tiago Peixoto committed
608
609
610
};


611
612
613
template <class Value, class Key>
void put(const UnityPropertyMap<Value, Key>&, const Key&, const Value&) {}

Tiago Peixoto's avatar
Tiago Peixoto committed
614
615
616
617
618
619
620
template <class Property>
struct is_constant_property
{
    typedef typename boost::property_traits<Property>::key_type key_type;
    typedef typename boost::property_traits<Property>::value_type value_type;
    typedef typename std::conditional<std::is_same<Property, ConstantPropertyMap<value_type, key_type>>::value,
                                      std::true_type,
621
                                      typename std::is_same<Property, UnityPropertyMap<value_type, key_type>>::type>::type type;
Tiago Peixoto's avatar
Tiago Peixoto committed
622
623
};

624
625
// this wraps an existing property map, but always converts its values to a
// given type
626
627
template <class PropertyMap, class Type,
          template <class T1, class T2> class Converter = convert>
628
629
630
631
class ConvertedPropertyMap
{
public:
    typedef Type value_type;
Tiago Peixoto's avatar
Tiago Peixoto committed
632
    typedef typename boost::property_traits<PropertyMap>::value_type orig_type;
633
    typedef value_type reference;
Tiago Peixoto's avatar
Tiago Peixoto committed
634
635
    typedef typename boost::property_traits<PropertyMap>::key_type key_type;
    typedef boost::read_write_property_map_tag category;
636

637
638
639
640
    ConvertedPropertyMap(PropertyMap base_map)
        : _base_map(base_map) {}
    ConvertedPropertyMap(){}

641
    value_type do_get(const key_type& k) const
642
    {
643
        return _convert_to(get(_base_map, k));
644
645
    }

646
    void do_put(const key_type& k, const value_type& v)
647
    {
648
        put(_base_map, k, _convert_from(v));
649
650
651
    }
private:
    PropertyMap _base_map;
652
653
    Converter<value_type, orig_type> _convert_to;
    Converter<orig_type, value_type> _convert_from;
654
};
655

656
template <class PropertyMap, class Type>
657
658
Type get(ConvertedPropertyMap<PropertyMap,Type> pmap,
         typename ConvertedPropertyMap<PropertyMap,Type>::key_type k)
659
{
660
    return pmap.do_get(k);
661
662
663
}

template <class PropertyMap, class Type>
664
void put(ConvertedPropertyMap<PropertyMap,Type> pmap,
Tiago Peixoto's avatar
Tiago Peixoto committed
665
         typename boost::property_traits<PropertyMap>::key_type k,
666
         const typename ConvertedPropertyMap<PropertyMap,Type>::value_type& val)
667
{
668
    pmap.do_put(k, val);
669
670
671
}

} // graph_tool namespace
672

Tiago Peixoto's avatar
Tiago Peixoto committed
673
#endif