graph_python_interface.hh 20.6 KB
Newer Older
1
2
// graph-tool -- a general graph modification and manipulation thingy
//
Tiago Peixoto's avatar
Tiago Peixoto committed
3
// Copyright (C) 2006-2016 Tiago de Paula Peixoto <tiago@skewed.de>
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
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/>.
17

18
19
#ifndef PYTHON_INTERFACE_HH
#define PYTHON_INTERFACE_HH
20

21
22
23
#include <boost/python.hpp>
#include <boost/python/type_id.hpp>

Tiago Peixoto's avatar
Tiago Peixoto committed
24
25
26
27
28
29
30
31
32
#include <functional>

namespace std
{
    template<>
    struct hash<boost::python::object>
    {
        size_t operator()(const boost::python::object& o) const
        {
33
            return std::hash<int64_t>()(boost::python::extract<int64_t>(o.attr("__hash__")()));
Tiago Peixoto's avatar
Tiago Peixoto committed
34
35
36
37
        }
    };
}

38
39
#include <boost/graph/graph_traits.hpp>
#include <boost/mpl/logical.hpp>
40
#include <boost/iterator/iterator_facade.hpp>
41

Tiago Peixoto's avatar
Tiago Peixoto committed
42
43
#include <type_traits>

44
45
#include "graph.hh"
#include "graph_filtering.hh"
46
#include "graph_selectors.hh"
47
#include "numpy_bind.hh"
48

49
50
51
#ifdef HAVE_BOOST_COROUTINE
#include <boost/coroutine/all.hpp>
#endif // HAVE_BOOST_COROUTINE
52

53
// This file includes a simple python interface for the internally kept
54
55
56
57
// graph. It defines a PythonVertex, PythonEdge and PythonIterator template
// classes, which contain the proper member functions for graph traversal. These
// types are then specialized for each version of the adapted graph (directed,
// undirected, filtered, reversed).
58

59
60
61
namespace graph_tool
{

62
63
// generic iterator adaptor which can be used to iterate vertices, edges,
// out_edges and in_edges through python
64
template <class Graph, class Descriptor, class Iterator>
65
class PythonIterator
66
{
67
public:
68
    PythonIterator(std::shared_ptr<Graph>& gp,
69
                   std::pair<Iterator,Iterator> e)
70
        : _g(gp), _e(e) {}
71
    Descriptor next()
72
    {
73
        if (_e.first == _e.second)
Tiago Peixoto's avatar
Tiago Peixoto committed
74
            boost::python::objects::stop_iteration_error();
75
        Descriptor e(_g, *_e.first);
76
77
        ++_e.first;
        return e;
78
79
    }
private:
80
    std::shared_ptr<Graph> _g;
81
    std::pair<Iterator,Iterator> _e;
Tiago Peixoto's avatar
Tiago Peixoto committed
82
83
};

84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#ifdef HAVE_BOOST_COROUTINE

// generic coroutine generator adaptor

typedef boost::coroutines::asymmetric_coroutine<boost::python::object> coro_t;

class CoroGenerator
{
public:
    template <class Dispatch>
    CoroGenerator(Dispatch& dispatch)
        : _coro(std::make_shared<coro_t::pull_type>(dispatch)),
          _iter(begin(*_coro)), _end(end(*_coro)) {}
    boost::python::object next()
    {
        if (_iter == _end)
            boost::python::objects::stop_iteration_error();
        boost::python::object oe = *_iter;
        ++_iter;
        return oe;
    }
private:
    std::shared_ptr<coro_t::pull_type> _coro;
    coro_t::pull_type::iterator _iter;
    coro_t::pull_type::iterator _end;
};

#endif // HAVE_BOOST_COROUTINE
Tiago Peixoto's avatar
Tiago Peixoto committed
112

113
// forward declaration of PythonEdge
114
115
template <class Graph>
class PythonEdge;
116

117
118
class VertexBase {}; // useful to unite all vertex

119
// below are classes related to the PythonVertex type
120
121
template <class Graph>
class PythonVertex : public VertexBase
122
123
{
public:
124
125
    PythonVertex(std::shared_ptr<Graph> g, GraphInterface::vertex_t v):
        _g(g), _v(v) {}
126

127
    bool is_valid() const
128
    {
129
130
131
        std::shared_ptr<Graph> gp(_g);
        Graph* g = gp.get();
        if (g == nullptr)
132
            return false;
133
134
        return ((_v != boost::graph_traits<Graph>::null_vertex()) &&
                (_v < num_vertices(*g)));
135
    }
136

137
    void check_valid() const
138
    {
139
        if (!is_valid())
140
            throw ValueException("invalid vertex descriptor: " +
Tiago Peixoto's avatar
Tiago Peixoto committed
141
                                 boost::lexical_cast<string>(_v));
142
    }
143

144
    GraphInterface::vertex_t get_descriptor() const
145
146
147
148
    {
        return _v;
    }

149
150
151
    template <class DegSelector>
    struct get_degree
    {
152
        void operator()(const Graph& g,
Tiago Peixoto's avatar
Tiago Peixoto committed
153
                        typename boost::graph_traits<Graph>::vertex_descriptor v,
154
155
                        size_t& deg) const
        {
156
            deg = DegSelector()(v, g);
157
        }
158

159
        template<class PMap>
160
161
        void operator()(const Graph& g,
                        typename boost::graph_traits<Graph>::vertex_descriptor v,
162
163
                        const boost::any& aweight, boost::python::object& deg,
                        bool& found, PMap) const
164
        {
165
166
167
168
169
170
171
            try
            {
                const PMap& weight = boost::any_cast<const PMap&>(aweight);
                deg = boost::python::object(DegSelector()(v, g, weight));
                found = true;
            }
            catch (boost::bad_any_cast&) {}
172
        }
173
174
    };

175
    size_t get_in_degree() const
176
    {
177
        check_valid();
178
179
        std::shared_ptr<Graph> gp(_g);
        Graph& g = *gp.get();
180
        size_t in_deg;
181
        get_degree<in_degreeS>()(g, _v, in_deg);
182
        return in_deg;
183
    }
184

185
    boost::python::object get_weighted_in_degree(boost::any pmap) const
186
    {
187
188
        std::shared_ptr<Graph> gp(_g);
        Graph& g = *gp.get();
189
        boost::python::object in_deg;
190
191
192
193
194
195
196
197
198
        bool found = false;
        boost::mpl::for_each<edge_scalar_properties>(std::bind(get_degree<in_degreeS>(),
                                                               std::ref(g), _v,
                                                               std::ref(pmap),
                                                               std::ref(in_deg),
                                                               std::ref(found),
                                                               std::placeholders::_1));
        if (!found)
            throw ValueException("edge weight property must be of scalar type");
199
200
201
        return in_deg;
    }

202
    size_t get_out_degree() const
203
    {
204
        check_valid();
205
206
        std::shared_ptr<Graph> gp(_g);
        Graph& g = *gp.get();
207
        size_t out_deg;
208
        get_degree<out_degreeS>()(g, _v, out_deg);
209
        return out_deg;
Tiago Peixoto's avatar
Tiago Peixoto committed
210
211
    }

212

213
    boost::python::object get_weighted_out_degree(boost::any pmap) const
214
    {
215
216
        std::shared_ptr<Graph> gp(_g);
        Graph& g = *gp.get();
217
        boost::python::object out_deg;
218
219
220
221
222
223
224
225
226
        bool found = false;
        boost::mpl::for_each<edge_scalar_properties>(std::bind(get_degree<out_degreeS>(),
                                                               std::ref(g), _v,
                                                               std::ref(pmap),
                                                               std::ref(out_deg),
                                                               std::ref(found),
                                                               std::placeholders::_1));
        if (!found)
            throw ValueException("edge weight property must be of scalar type");
227
228
229
        return out_deg;
    }

230
    // provide iterator support for out_edges
231
    boost::python::object out_edges() const
Tiago Peixoto's avatar
Tiago Peixoto committed
232
    {
233
        check_valid();
234
235
236
237
238
239
        std::shared_ptr<Graph> pg(_g);
        Graph& g = *pg;
        typedef typename boost::graph_traits<Graph>::out_edge_iterator
            out_edge_iterator;
        return boost::python::object(PythonIterator<Graph,PythonEdge<Graph>,
                                                    out_edge_iterator>
240
                                     (pg, boost::out_edges(_v, g)));
241
    }
242

243
    boost::python::object in_edges() const
244
    {
245
        check_valid();
246
247
248
249
250
251
252
        std::shared_ptr<Graph> pg(_g);
        Graph& g = *pg;
        typedef typename in_edge_iteratorS<Graph>::type
            in_edge_iterator;
        return boost::python::object(PythonIterator<Graph, PythonEdge<Graph>,
                                                    in_edge_iterator>
                                     (pg, in_edge_iteratorS<Graph>::get_edges(_v, g)));
Tiago Peixoto's avatar
Tiago Peixoto committed
253
    }
254

255
    std::string get_string() const
256
    {
257
        check_valid();
Tiago Peixoto's avatar
Tiago Peixoto committed
258
        return boost::lexical_cast<std::string>(_v);
259
260
    }

261
    size_t get_hash() const
262
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
263
        return std::hash<size_t>()(_v);
264
265
    }

266
    size_t get_index() const
267
    {
268
        return _v;
269
270
    }

271
    size_t get_graph_ptr() const
272
273
274
275
276
    {
        std::shared_ptr<Graph> pg(_g);
        return size_t(pg.get());
    }

277
    std::string get_graph_type() const
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
    {
        using boost::python::detail::gcc_demangle;
        return gcc_demangle(typeid(Graph).name());
    }

    template <class OGraph>
    bool operator==(const PythonVertex<OGraph>& other) const { return _v == other._v; }
    template <class OGraph>
    bool operator!=(const PythonVertex<OGraph>& other) const { return _v != other._v; }
    template <class OGraph>
    bool operator<(const PythonVertex<OGraph>& other) const { return _v < other._v; }
    template <class OGraph>
    bool operator<=(const PythonVertex<OGraph>& other) const { return _v <= other._v; }
    template <class OGraph>
    bool operator>(const PythonVertex<OGraph>& other) const { return _v > other._v; }
    template <class OGraph>
    bool operator>=(const PythonVertex<OGraph>& other) const { return _v >= other._v; }

296
private:
297
    std::weak_ptr<Graph> _g;
298
    GraphInterface::vertex_t _v;
299
};
300

301
// below are classes related to the PythonEdge type
302

303
304
class EdgeBase {}; // useful to unite all edge types

305
template <class Graph>
306
class PythonEdge : public EdgeBase
307
308
{
public:
Tiago Peixoto's avatar
Tiago Peixoto committed
309
    typedef typename boost::graph_traits<Graph>::edge_descriptor edge_descriptor;
310
311
    PythonEdge(std::shared_ptr<Graph> g, edge_descriptor e)
        : _g(g), _e(e) {}
312

313
    bool is_valid() const
Tiago Peixoto's avatar
Tiago Peixoto committed
314
    {
315
316
317
        std::shared_ptr<Graph> gp(_g);
        Graph* g = gp.get();
        if (g == nullptr)
318
            return false;
319

320
321
        auto s = source(_e, *g);
        auto t = target(_e, *g);
322

323
324
325
326
        return ((s != boost::graph_traits<Graph>::null_vertex()) &&
                (s < num_vertices(*g)) &&
                (t != boost::graph_traits<Graph>::null_vertex()) &&
                (t < num_vertices(*g)));
327
328
    }

329
    void check_valid() const
330
    {
331
        if (!is_valid())
Tiago Peixoto's avatar
Tiago Peixoto committed
332
            throw ValueException("invalid edge descriptor");
Tiago Peixoto's avatar
Tiago Peixoto committed
333
334
    }

335
    GraphInterface::edge_t get_descriptor() const
336
337
338
339
    {
        return _e;
    }

340
    PythonVertex<Graph> get_source() const
341
    {
342
        check_valid();
343
344
345
346
        std::shared_ptr<Graph> pg(_g);
        Graph& g = *pg;
        return PythonVertex<Graph>(pg, source(_e, g));
    }
347

348
    PythonVertex<Graph> get_target() const
Tiago Peixoto's avatar
Tiago Peixoto committed
349
    {
350
        check_valid();
351
352
353
        std::shared_ptr<Graph> pg(_g);
        Graph& g = *pg;
        return PythonVertex<Graph>(pg, target(_e, g));
Tiago Peixoto's avatar
Tiago Peixoto committed
354
355
    }

356
    std::string get_string() const
357
    {
358
        check_valid();
359
360
361
362
363
364
        Graph& g = *std::shared_ptr<Graph>(_g);
        auto s = source(_e, g);
        auto t = target(_e, g);
        return "(" + boost::lexical_cast<std::string>(s) + ", "
            + boost::lexical_cast<std::string>(t) + ")";
    }
365

366
    size_t get_hash() const
Tiago Peixoto's avatar
Tiago Peixoto committed
367
    {
368
        check_valid();
369
370
371
        Graph& g = *std::shared_ptr<Graph>(_g);
        auto eindex = get(boost::edge_index_t(), g);
        return std::hash<size_t>()(eindex[_e]);
Tiago Peixoto's avatar
Tiago Peixoto committed
372
373
    }

374
    size_t get_graph_ptr() const
375
    {
376
377
        std::shared_ptr<Graph> pg(_g);
        return site_t(pg.get());
378
379
    }

380
    std::string get_graph_type() const
381
382
383
384
385
386
387
388
389
390
391
    {
        using boost::python::detail::gcc_demangle;
        return gcc_demangle(typeid(Graph).name());
    }

    template <class OGraph>
    bool operator==(const PythonEdge<OGraph>& other) const { return _e == other._e; }
    template <class OGraph>
    bool operator!=(const PythonEdge<OGraph>& other) const { return !(*this == other); }
    template <class OGraph>
    bool operator<(const PythonEdge<OGraph>& other)  const
Tiago Peixoto's avatar
Tiago Peixoto committed
392
    {
393
394
        check_valid();
        other.check_valid();
395
396
        Graph& g = *std::shared_ptr<Graph>(_g);
        OGraph& og = *std::shared_ptr<OGraph>(other._g);
397
398
399
        auto eindex = get(boost::edge_index_t(), g);
        auto eindex2 = get(boost::edge_index_t(), og);
        return eindex[_e] < eindex2[other._e];
Tiago Peixoto's avatar
Tiago Peixoto committed
400
    }
401
402
403
404
405
406
    template <class OGraph>
    bool operator<=(const PythonEdge<OGraph>& other) const {return *this < other || *this == other;}
    template <class OGraph>
    bool operator>(const PythonEdge<OGraph>& other) const {return !(*this < other || *this == other);}
    template <class OGraph>
    bool operator>=(const PythonEdge<OGraph>& other) const {return *this > other || *this == other;}
Tiago Peixoto's avatar
Tiago Peixoto committed
407

408
private:
409
    std::weak_ptr<Graph> _g;
410
    edge_descriptor _e;
411
412
413

    template <class OGraph>
    friend class PythonEdge;
414
415
};

416
417
418
419
420
421
422
423
424
425
// metafunction to determine wether or not to return copies or internal
// references to property types
struct return_reference
{
    template <class ValueType>
    struct apply
    {
        // return actual references only for non-string and non-python::object
        // classes

Tiago Peixoto's avatar
Tiago Peixoto committed
426
427
428
429
430
431
432
433
        typedef typename boost::mpl::if_<
            typename boost::mpl::and_<
                std::is_class<ValueType>,
                typename boost::mpl::and_<
                    typename boost::mpl::not_<std::is_same<ValueType,
                                                           string> >::type,
                    typename boost::mpl::not_<std::is_same<ValueType,
                                                           boost::python::object> >::type>::type
434
                >::type,
Tiago Peixoto's avatar
Tiago Peixoto committed
435
436
            boost::mpl::bool_<true>,
            boost::mpl::bool_<false> >::type type;
437
438
439
440
    };
};

template <class PropertyMap>
441
442
443
class PythonPropertyMap
{
public:
444
445
    PythonPropertyMap(const PropertyMap& pmap)
        : _pmap(pmap) {}
446

Tiago Peixoto's avatar
Tiago Peixoto committed
447
    typedef typename boost::property_traits<PropertyMap>::value_type value_type;
448

Tiago Peixoto's avatar
Tiago Peixoto committed
449
    typedef typename boost::mpl::if_<
450
451
452
453
        typename return_reference::apply<value_type>::type,
        value_type&,
        value_type>::type reference;

454
    template <class PythonDescriptor>
455
    reference get_value(const PythonDescriptor& key)
456
    {
457
458
        key.check_valid();
        return get(_pmap, key.get_descriptor());
459
460
    }

461
462
    // in this case, val should be a copy, not a reference. This is to avoid a
    // problem with vector-valued property maps
463
    template <class PythonDescriptor>
464
    void set_value(const PythonDescriptor& key, value_type val)
465
    {
466
467
468
        set_value_dispatch(key, val,
                           std::is_convertible<typename boost::property_traits<PropertyMap>::category,
                                               boost::writable_property_map_tag>());
469
470
    }

471
    template <class PythonDescriptor>
472
473
    void set_value_dispatch(const PythonDescriptor& key, const value_type& val,
                            std::true_type)
474
    {
475
476
        key.check_valid();
        put(_pmap, key.get_descriptor(), val);
477
478
    }

479
    template <class PythonDescriptor>
480
481
    void set_value_dispatch(const PythonDescriptor&, const value_type&,
                            std::false_type)
482
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
483
        throw ValueException("property is read-only");
484
485
    }

486
    size_t get_hash() const
487
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
488
        return std::hash<size_t>()(size_t(this));
489
490
    }

491
    std::string get_type() const
492
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
493
494
495
        using boost::python::detail::gcc_demangle;
        if (std::is_same<typename boost::mpl::find<value_types,value_type>::type,
                         typename boost::mpl::end<value_types>::type>::value)
496
497
            return gcc_demangle(typeid(value_type).name());
        else
Tiago Peixoto's avatar
Tiago Peixoto committed
498
            return type_names[boost::mpl::find<value_types,
499
                                               value_type>::type::pos::value];
500
501
    }

502
    boost::any get_map() const
503
504
505
506
    {
        return _pmap;
    }

507
    boost::any get_dynamic_map() const
508
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
509
        return (boost::dynamic_property_map*)
510
511
512
513
            (new boost::detail::dynamic_property_map_adaptor<PropertyMap>
             (_pmap));
    }

514
    boost::python::object get_array(size_t size)
515
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
516
517
518
519
520
521
522
523
        typedef typename boost::mpl::or_<
            typename boost::mpl::or_<
                std::is_same<PropertyMap,
                             GraphInterface::vertex_index_map_t>,
                std::is_same<PropertyMap,
                             GraphInterface::edge_index_map_t> >::type,
            typename boost::mpl::not_<
                typename boost::mpl::has_key<numpy_types, value_type>::type >
524
            ::type>::type isnt_vector_map;
525
        return get_array_dispatch(size, isnt_vector_map());
526
527
    }

528
    boost::python::object get_array_dispatch(size_t size, boost::mpl::bool_<false>)
529
    {
530
        _pmap.resize(size);
531
532
533
        return wrap_vector_not_owned(_pmap.get_storage());
    }

534
    boost::python::object get_array_dispatch(size_t, boost::mpl::bool_<true>)
535
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
536
        return boost::python::object();
537
538
    }

539
    bool is_writable() const
540
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
541
542
        return std::is_convertible<typename boost::property_traits<PropertyMap>::category,
                                   boost::writable_property_map_tag>::value;
543
544
    }

545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
    void reserve(size_t size)
    {
        typename boost::mpl::or_<
            std::is_same<PropertyMap,
                         GraphInterface::vertex_index_map_t>,
            std::is_same<PropertyMap,
                         GraphInterface::edge_index_map_t> >::type is_index;
        reserve_dispatch(size, is_index);
    }

    void reserve_dispatch(size_t size, boost::mpl::bool_<false>)
    {
        _pmap.reserve(size);
    }

    void reserve_dispatch(size_t, boost::mpl::bool_<true>)
    {
    }

    void resize(size_t size)
    {
        typename boost::mpl::or_<
            std::is_same<PropertyMap,
                         GraphInterface::vertex_index_map_t>,
            std::is_same<PropertyMap,
                         GraphInterface::edge_index_map_t> >::type is_index;
        resize_dispatch(size, is_index);
    }

    void resize_dispatch(size_t size, boost::mpl::bool_<false>)
    {
        _pmap.resize(size);
    }

    void resize_dispatch(size_t, boost::mpl::bool_<true>)
    {
    }

    void shrink_to_fit()
    {
        typename boost::mpl::or_<
            std::is_same<PropertyMap,
                         GraphInterface::vertex_index_map_t>,
            std::is_same<PropertyMap,
                         GraphInterface::edge_index_map_t> >::type is_index;
        shrink_to_fit_dispatch(is_index);
    }

    void shrink_to_fit_dispatch(boost::mpl::bool_<false>)
    {
        _pmap.shrink_to_fit();
    }

    void shrink_to_fit_dispatch(boost::mpl::bool_<true>)
    {
    }

602
private:
603
    PropertyMap _pmap; // hold an internal copy, since it's cheap
604
605
606
};


607
608
609
610
611
612
613
614
//
// Create new properties
//

struct new_property_map
{
    template <class ValueType, class IndexMap>
    void operator()(ValueType, IndexMap index, const string& type_name,
Tiago Peixoto's avatar
Tiago Peixoto committed
615
                     boost::any pmap, boost::python::object& new_prop, bool& found) const
616
    {
Tiago Peixoto's avatar
Tiago Peixoto committed
617
        size_t i = boost::mpl::find<value_types,ValueType>::type::pos::value;
618
619
        if (type_name == type_names[i])
        {
620
621
            typedef typename property_map_type::apply<ValueType, IndexMap>::type
                map_t;
622
623
624
625
            map_t prop;
            if (pmap.empty())
                prop = map_t(index);
            else
Tiago Peixoto's avatar
Tiago Peixoto committed
626
                prop = boost::any_cast<map_t>(pmap);
627

Tiago Peixoto's avatar
Tiago Peixoto committed
628
            new_prop = boost::python::object(PythonPropertyMap<map_t>(prop));
629
630
631
632
633
634
            found = true;
        }
    }
};

template <class IndexMap>
Tiago Peixoto's avatar
Tiago Peixoto committed
635
636
boost::python::object new_property(const string& type, IndexMap index_map,
                                   boost::any pmap)
637
{
Tiago Peixoto's avatar
Tiago Peixoto committed
638
    boost::python::object prop;
639
    bool found = false;
Tiago Peixoto's avatar
Tiago Peixoto committed
640
641
642
643
    boost::mpl::for_each<value_types>(std::bind(new_property_map(),
                                                std::placeholders::_1, index_map,
                                                std::ref(type), pmap, std::ref(prop),
                                                std::ref(found)));
644
    if (!found)
Tiago Peixoto's avatar
Tiago Peixoto committed
645
        throw ValueException("Invalid property type: " + type);
646
647
648
    return prop;
}

649
650
651
652
653
654
655
656
657
//
// Python IO streams (minimal access to c++ streams)
//

class OStream
{
public:
    OStream(std::ostream& s): _s(s) {}

658
    void write(const std::string& s, size_t n)
659
660
661
662
    {
        _s.write(s.c_str(), long(n));
    }

663
    void flush()
664
665
666
667
668
669
670
671
672
673
674
675
676
    {
        _s.flush();
    }

private:
    std::ostream& _s;
};

class IStream
{
public:
    IStream(std::istream& s): _s(s) {}

677
    boost::python::object read(size_t n)
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
    {
        std::string buf;
        buf.resize(n);
        _s.read(&buf[0], n);
        buf.resize(_s.gcount());

#if (PY_MAJOR_VERSION >= 3)
        // in python 3 we need to construct a 'bytes' instance
        PyObject* bytes = PyBytes_FromStringAndSize(&buf[0], buf.size());
        boost::python::handle<> x(bytes);
        boost::python::object pbuf(x);
#else
        boost::python::str pbuf(buf);
#endif
        return pbuf;
    }

private:
    std::istream& _s;
};


700
701
702
} //graph_tool namespace

#endif