lemon/lgf_writer.h
author Balazs Dezso <deba@inf.elte.hu>
Sat, 31 May 2008 12:34:44 +0200
changeset 164 00d297da491e
parent 156 e561aa7675de
child 165 b4c336c27a03
permissions -rw-r--r--
Reworking demo file
     1 /* -*- C++ -*-
     2  *
     3  * This file is a part of LEMON, a generic C++ optimization library
     4  *
     5  * Copyright (C) 2003-2008
     6  * Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport
     7  * (Egervary Research Group on Combinatorial Optimization, EGRES).
     8  *
     9  * Permission to use, modify and distribute this software is granted
    10  * provided that this copyright notice appears in all copies. For
    11  * precise terms see the accompanying LICENSE file.
    12  *
    13  * This software is provided "AS IS" with no warranty of any kind,
    14  * express or implied, and with no claim as to its suitability for any
    15  * purpose.
    16  *
    17  */
    18 
    19 ///\ingroup lemon_io
    20 ///\file
    21 ///\brief Lemon Graph Format writer.
    22 
    23 
    24 #ifndef LEMON_LGF_WRITER_H
    25 #define LEMON_LGF_WRITER_H
    26 
    27 #include <iostream>
    28 #include <fstream>
    29 #include <sstream>
    30 
    31 #include <algorithm>
    32 
    33 #include <vector>
    34 #include <functional>
    35 
    36 #include <lemon/assert.h>
    37 #include <lemon/graph_utils.h>
    38 
    39 namespace lemon {
    40 
    41   namespace _writer_bits {
    42 
    43     template <typename Value>
    44     struct DefaultConverter {
    45       std::string operator()(const Value& value) {
    46 	std::ostringstream os;
    47 	os << value;
    48 	return os.str();
    49       }
    50     };
    51 
    52     template <typename T>
    53     bool operator<(const T&, const T&) {
    54       throw DataFormatError("Label map is not comparable");
    55     }
    56 
    57     template <typename _Map>
    58     class MapLess {
    59     public:
    60       typedef _Map Map;
    61       typedef typename Map::Key Item;
    62 
    63     private:
    64       const Map& _map;
    65       
    66     public:
    67       MapLess(const Map& map) : _map(map) {}
    68 
    69       bool operator()(const Item& left, const Item& right) {
    70 	return _map[left] < _map[right];
    71       }
    72     };
    73 
    74     template <typename _Item>    
    75     class MapStorageBase {
    76     public:
    77       typedef _Item Item;
    78 
    79     public:
    80       MapStorageBase() {}
    81       virtual ~MapStorageBase() {}
    82 
    83       virtual std::string get(const Item& item) = 0;
    84       virtual void sort(std::vector<Item>&) = 0;
    85     };
    86 
    87     template <typename _Item, typename _Map, 
    88 	      typename _Converter = DefaultConverter<typename _Map::Value> >
    89     class MapStorage : public MapStorageBase<_Item> {
    90     public:
    91       typedef _Map Map;
    92       typedef _Converter Converter;
    93       typedef _Item Item;
    94       
    95     private:
    96       const Map& _map;
    97       Converter _converter;
    98 
    99     public:
   100       MapStorage(const Map& map, const Converter& converter = Converter()) 
   101 	: _map(map), _converter(converter) {}
   102       virtual ~MapStorage() {}
   103 
   104       virtual std::string get(const Item& item) {
   105 	return _converter(_map[item]);
   106       }
   107       virtual void sort(std::vector<Item>& items) {
   108 	MapLess<Map> less(_map);
   109 	std::sort(items.begin(), items.end(), less);
   110       }
   111     };
   112 
   113     class ValueStorageBase {
   114     public:
   115       ValueStorageBase() {}
   116       virtual ~ValueStorageBase() {}
   117 
   118       virtual std::string get() = 0;      
   119     };
   120 
   121     template <typename _Value, typename _Converter = DefaultConverter<_Value> >
   122     class ValueStorage : public ValueStorageBase {
   123     public:
   124       typedef _Value Value;
   125       typedef _Converter Converter;
   126 
   127     private:
   128       const Value& _value;
   129       Converter _converter;
   130 
   131     public:
   132       ValueStorage(const Value& value, const Converter& converter = Converter())
   133  	: _value(value), _converter(converter) {}
   134 
   135       virtual std::string get() {
   136 	return _converter(_value);
   137       }
   138     };
   139 
   140     template <typename Value>
   141     struct MapLookUpConverter {
   142       const std::map<Value, std::string>& _map;
   143       
   144       MapLookUpConverter(const std::map<Value, std::string>& map) 
   145 	: _map(map) {}
   146       
   147       std::string operator()(const Value& str) {
   148 	typename std::map<Value, std::string>::const_iterator it = 
   149 	  _map.find(str);
   150 	if (it == _map.end()) {
   151 	  throw DataFormatError("Item not found");
   152 	}
   153 	return it->second;
   154       }
   155     };
   156 
   157     bool isWhiteSpace(char c) {
   158       return c == ' ' || c == '\t' || c == '\v' || 
   159         c == '\n' || c == '\r' || c == '\f'; 
   160     }
   161 
   162     bool isEscaped(char c) {
   163       return c == '\\' || c == '\"' || c == '\'' || 
   164 	c == '\a' || c == '\b';
   165     }
   166 
   167     static void writeEscape(std::ostream& os, char c) {
   168       switch (c) {
   169       case '\\':
   170 	os << "\\\\";
   171 	return;
   172       case '\"':
   173 	os << "\\\"";
   174 	return;
   175       case '\a':
   176 	os << "\\a";
   177 	return;
   178       case '\b':
   179 	os << "\\b";
   180 	return;
   181       case '\f':
   182 	os << "\\f";
   183 	return;
   184       case '\r':
   185 	os << "\\r";
   186 	return;
   187       case '\n':
   188 	os << "\\n";
   189 	return;
   190       case '\t':
   191 	os << "\\t";
   192 	return;
   193       case '\v':
   194 	os << "\\v";
   195 	return;
   196       default:
   197 	if (c < 0x20) {
   198 	  std::ios::fmtflags flags = os.flags();
   199 	  os << '\\' << std::oct << static_cast<int>(c);
   200 	  os.flags(flags);
   201 	} else {
   202 	  os << c;
   203 	}
   204 	return;
   205       }     
   206     }
   207 
   208     bool requireEscape(const std::string& str) {
   209       if (str.empty() || str[0] == '@') return true;
   210       std::istringstream is(str);
   211       char c;
   212       while (is.get(c)) {
   213 	if (isWhiteSpace(c) || isEscaped(c)) {
   214 	  return true;
   215 	}
   216       }
   217       return false;
   218     }
   219     
   220     std::ostream& writeToken(std::ostream& os, const std::string& str) {
   221 
   222       if (requireEscape(str)) {
   223 	os << '\"';
   224 	for (std::string::const_iterator it = str.begin(); 
   225 	     it != str.end(); ++it) {
   226 	  writeEscape(os, *it);
   227 	}	
   228 	os << '\"';
   229       } else {
   230 	os << str;
   231       }
   232       return os;
   233     }
   234 
   235   }
   236   
   237   /// \ingroup lemon_io
   238   ///  
   239   /// \brief LGF writer for directed graphs
   240   ///
   241   /// This utility writes an \ref lgf-format "LGF" file.
   242   ///
   243   /// The writing method does a batch processing. The user creates a
   244   /// writer object, then various writing rules can be added to the
   245   /// writer, and eventually the writing is executed with the \c run()
   246   /// member function. A map writing rule can be added to the writer
   247   /// with the \c nodeMap() or \c arcMap() members. An optional
   248   /// converter parameter can also be added as a standard functor
   249   /// converting from the value type of the map to std::string. If it
   250   /// is set, it will determine how the map's value type is written to
   251   /// the output stream. If the functor is not set, then a default
   252   /// conversion will be used. The \c attribute(), \c node() and \c
   253   /// arc() functions are used to add attribute writing rules.
   254   ///
   255   ///\code
   256   ///     DigraphWriter<Digraph>(std::cout, digraph).
   257   ///       nodeMap("coordinates", coord_map).
   258   ///       nodeMap("size", size).
   259   ///       nodeMap("title", title).
   260   ///       arcMap("capacity", cap_map).
   261   ///       node("source", src).
   262   ///       node("target", trg).
   263   ///       attribute("caption", caption).
   264   ///       run();
   265   ///\endcode
   266   ///
   267   ///
   268   /// By default, the writer does not write additional captions to the
   269   /// sections, but they can be give as an optional parameter of
   270   /// the \c nodes(), \c arcs() or \c
   271   /// attributes() functions.
   272   ///
   273   /// The \c skipNodes() and \c skipArcs() functions forbid the
   274   /// writing of the sections. If two arc sections should be written
   275   /// to the output, it can be done in two passes, the first pass
   276   /// writes the node section and the first arc section, then the
   277   /// second pass skips the node section and writes just the arc
   278   /// section to the stream. The output stream can be retrieved with
   279   /// the \c ostream() function, hence the second pass can append its
   280   /// output to the output of the first pass.
   281   template <typename _Digraph>
   282   class DigraphWriter {
   283   public:
   284 
   285     typedef _Digraph Digraph;
   286     TEMPLATE_DIGRAPH_TYPEDEFS(Digraph);
   287     
   288   private:
   289 
   290 
   291     std::ostream* _os;
   292     bool local_os;
   293 
   294     Digraph& _digraph;
   295 
   296     std::string _nodes_caption;
   297     std::string _arcs_caption;
   298     std::string _attributes_caption;
   299     
   300     typedef std::map<Node, std::string> NodeIndex;
   301     NodeIndex _node_index;
   302     typedef std::map<Arc, std::string> ArcIndex;
   303     ArcIndex _arc_index;
   304 
   305     typedef std::vector<std::pair<std::string, 
   306       _writer_bits::MapStorageBase<Node>* > > NodeMaps;    
   307     NodeMaps _node_maps; 
   308 
   309     typedef std::vector<std::pair<std::string, 
   310       _writer_bits::MapStorageBase<Arc>* > >ArcMaps;
   311     ArcMaps _arc_maps;
   312 
   313     typedef std::vector<std::pair<std::string, 
   314       _writer_bits::ValueStorageBase*> > Attributes;
   315     Attributes _attributes;
   316 
   317     bool _skip_nodes;
   318     bool _skip_arcs;
   319 
   320   public:
   321 
   322     /// \brief Constructor
   323     ///
   324     /// Construct a directed graph writer, which writes to the given
   325     /// output stream.
   326     DigraphWriter(std::ostream& is, Digraph& digraph) 
   327       : _os(&is), local_os(false), _digraph(digraph),
   328 	_skip_nodes(false), _skip_arcs(false) {}
   329 
   330     /// \brief Constructor
   331     ///
   332     /// Construct a directed graph writer, which writes to the given
   333     /// output file.
   334     DigraphWriter(const std::string& fn, Digraph& digraph) 
   335       : _os(new std::ofstream(fn.c_str())), local_os(true), _digraph(digraph),
   336 	_skip_nodes(false), _skip_arcs(false) {}
   337 
   338     /// \brief Constructor
   339     ///
   340     /// Construct a directed graph writer, which writes to the given
   341     /// output file.
   342     DigraphWriter(const char* fn, Digraph& digraph) 
   343       : _os(new std::ofstream(fn)), local_os(true), _digraph(digraph),
   344 	_skip_nodes(false), _skip_arcs(false) {}
   345 
   346     /// \brief Copy constructor
   347     ///
   348     /// The copy constructor transfers all data from the other writer,
   349     /// therefore the copied writer will not be usable more. 
   350     DigraphWriter(DigraphWriter& other) 
   351       : _os(other._os), local_os(other.local_os), _digraph(other._digraph),
   352 	_skip_nodes(other._skip_nodes), _skip_arcs(other._skip_arcs) {
   353 
   354       other._os = 0;
   355       other.local_os = false;
   356 
   357       _node_index.swap(other._node_index);
   358       _arc_index.swap(other._arc_index);
   359 
   360       _node_maps.swap(other._node_maps);
   361       _arc_maps.swap(other._arc_maps);
   362       _attributes.swap(other._attributes);
   363 
   364       _nodes_caption = other._nodes_caption;
   365       _arcs_caption = other._arcs_caption;
   366       _attributes_caption = other._attributes_caption;
   367     }
   368 
   369     /// \brief Destructor
   370     ~DigraphWriter() {
   371       for (typename NodeMaps::iterator it = _node_maps.begin(); 
   372 	   it != _node_maps.end(); ++it) {
   373 	delete it->second;
   374       }
   375 
   376       for (typename ArcMaps::iterator it = _arc_maps.begin(); 
   377 	   it != _arc_maps.end(); ++it) {
   378 	delete it->second;
   379       }
   380 
   381       for (typename Attributes::iterator it = _attributes.begin(); 
   382 	   it != _attributes.end(); ++it) {
   383 	delete it->second;
   384       }
   385 
   386       if (local_os) {
   387 	delete _os;
   388       }
   389     }
   390 
   391   private:
   392     
   393     DigraphWriter& operator=(const DigraphWriter&);
   394 
   395   public:
   396 
   397     /// \name Writing rules
   398     /// @{
   399     
   400     /// \brief Node map reading rule
   401     ///
   402     /// Add a node map reading rule to the writer.
   403     template <typename Map>
   404     DigraphWriter& nodeMap(const std::string& caption, const Map& map) {
   405       checkConcept<concepts::ReadMap<Node, typename Map::Value>, Map>();
   406       _writer_bits::MapStorageBase<Node>* storage = 
   407 	new _writer_bits::MapStorage<Node, Map>(map);
   408       _node_maps.push_back(std::make_pair(caption, storage));
   409       return *this;
   410     }
   411 
   412     /// \brief Node map writing rule
   413     ///
   414     /// Add a node map writing rule with specialized converter to the
   415     /// writer.
   416     template <typename Map, typename Converter>
   417     DigraphWriter& nodeMap(const std::string& caption, const Map& map, 
   418 			   const Converter& converter = Converter()) {
   419       checkConcept<concepts::ReadMap<Node, typename Map::Value>, Map>();
   420       _writer_bits::MapStorageBase<Node>* storage = 
   421 	new _writer_bits::MapStorage<Node, Map, Converter>(map, converter);
   422       _node_maps.push_back(std::make_pair(caption, storage));
   423       return *this;
   424     }
   425 
   426     /// \brief Arc map writing rule
   427     ///
   428     /// Add an arc map writing rule to the writer.
   429     template <typename Map>
   430     DigraphWriter& arcMap(const std::string& caption, const Map& map) {
   431       checkConcept<concepts::ReadMap<Arc, typename Map::Value>, Map>();
   432       _writer_bits::MapStorageBase<Arc>* storage = 
   433 	new _writer_bits::MapStorage<Arc, Map>(map);
   434       _arc_maps.push_back(std::make_pair(caption, storage));
   435       return *this;
   436     }
   437 
   438     /// \brief Arc map writing rule
   439     ///
   440     /// Add an arc map writing rule with specialized converter to the
   441     /// writer.
   442     template <typename Map, typename Converter>
   443     DigraphWriter& arcMap(const std::string& caption, const Map& map, 
   444 			  const Converter& converter = Converter()) {
   445       checkConcept<concepts::ReadMap<Arc, typename Map::Value>, Map>();
   446       _writer_bits::MapStorageBase<Arc>* storage = 
   447 	new _writer_bits::MapStorage<Arc, Map, Converter>(map, converter);
   448       _arc_maps.push_back(std::make_pair(caption, storage));
   449       return *this;
   450     }
   451 
   452     /// \brief Attribute writing rule
   453     ///
   454     /// Add an attribute writing rule to the writer.
   455     template <typename Value>
   456     DigraphWriter& attribute(const std::string& caption, const Value& value) {
   457       _writer_bits::ValueStorageBase* storage = 
   458 	new _writer_bits::ValueStorage<Value>(value);
   459       _attributes.push_back(std::make_pair(caption, storage));
   460       return *this;
   461     }
   462 
   463     /// \brief Attribute writing rule
   464     ///
   465     /// Add an attribute writing rule with specialized converter to the
   466     /// writer.
   467     template <typename Value, typename Converter>
   468     DigraphWriter& attribute(const std::string& caption, const Value& value, 
   469 			     const Converter& converter = Converter()) {
   470       _writer_bits::ValueStorageBase* storage = 
   471 	new _writer_bits::ValueStorage<Value, Converter>(value, converter);
   472       _attributes.push_back(std::make_pair(caption, storage));
   473       return *this;
   474     }
   475 
   476     /// \brief Node writing rule
   477     ///
   478     /// Add a node writing rule to the writer.
   479     DigraphWriter& node(const std::string& caption, const Node& node) {
   480       typedef _writer_bits::MapLookUpConverter<Node> Converter;
   481       Converter converter(_node_index);
   482       _writer_bits::ValueStorageBase* storage = 
   483 	new _writer_bits::ValueStorage<Node, Converter>(node, converter);
   484       _attributes.push_back(std::make_pair(caption, storage));
   485       return *this;
   486     }
   487 
   488     /// \brief Arc writing rule
   489     ///
   490     /// Add an arc writing rule to writer.
   491     DigraphWriter& arc(const std::string& caption, const Arc& arc) {
   492       typedef _writer_bits::MapLookUpConverter<Arc> Converter;
   493       Converter converter(_arc_index);
   494       _writer_bits::ValueStorageBase* storage = 
   495 	new _writer_bits::ValueStorage<Arc, Converter>(arc, converter);
   496       _attributes.push_back(std::make_pair(caption, storage));
   497       return *this;
   498     }
   499 
   500     /// \name Select section by name
   501     /// @{
   502 
   503     /// \brief Set \c \@nodes section to be read
   504     ///
   505     /// Set \c \@nodes section to be read
   506     DigraphWriter& nodes(const std::string& caption) {
   507       _nodes_caption = caption;
   508       return *this;
   509     }
   510 
   511     /// \brief Set \c \@arcs section to be read
   512     ///
   513     /// Set \c \@arcs section to be read
   514     DigraphWriter& arcs(const std::string& caption) {
   515       _arcs_caption = caption;
   516       return *this;
   517     }
   518 
   519     /// \brief Set \c \@attributes section to be read
   520     ///
   521     /// Set \c \@attributes section to be read
   522     DigraphWriter& attributes(const std::string& caption) {
   523       _attributes_caption = caption;
   524       return *this;
   525     }
   526 
   527     /// \name Skipping section
   528     /// @{
   529 
   530     /// \brief Skip writing the node set
   531     ///
   532     /// The \c \@nodes section will be not written to the stream.
   533     DigraphWriter& skipNodes() {
   534       LEMON_ASSERT(!_skip_nodes, "Multiple usage of skipNodes() member");
   535       return *this;
   536     }
   537 
   538     /// \brief Skip writing arc set
   539     ///
   540     /// The \c \@arcs section will be not written to the stream.
   541     DigraphWriter& skipArcs() {
   542       LEMON_ASSERT(!_skip_arcs, "Multiple usage of skipArcs() member");
   543       return *this;
   544     }
   545 
   546     /// @}
   547 
   548   private:
   549 
   550     void writeNodes() {
   551       _writer_bits::MapStorageBase<Node>* label = 0;
   552       for (typename NodeMaps::iterator it = _node_maps.begin();
   553 	   it != _node_maps.end(); ++it) {
   554         if (it->first == "label") {
   555 	  label = it->second;
   556 	  break;
   557 	}
   558       }
   559 
   560       *_os << "@nodes";
   561       if (!_nodes_caption.empty()) {
   562 	_writer_bits::writeToken(*_os << ' ', _nodes_caption);
   563       }
   564       *_os << std::endl;
   565 
   566       if (label == 0) {
   567 	*_os << "label" << '\t';
   568       }
   569       for (typename NodeMaps::iterator it = _node_maps.begin();
   570 	   it != _node_maps.end(); ++it) {
   571 	_writer_bits::writeToken(*_os, it->first) << '\t';
   572       }
   573       *_os << std::endl;
   574 
   575       std::vector<Node> nodes;
   576       for (NodeIt n(_digraph); n != INVALID; ++n) {
   577 	nodes.push_back(n);
   578       }
   579       
   580       if (label == 0) {
   581 	IdMap<Digraph, Node> id_map(_digraph);
   582 	_writer_bits::MapLess<IdMap<Digraph, Node> > id_less(id_map);
   583 	std::sort(nodes.begin(), nodes.end(), id_less);
   584       } else {
   585 	label->sort(nodes);
   586       }
   587 
   588       for (int i = 0; i < static_cast<int>(nodes.size()); ++i) {
   589 	Node n = nodes[i];
   590 	if (label == 0) {
   591 	  std::ostringstream os;
   592 	  os << _digraph.id(n);
   593 	  _writer_bits::writeToken(*_os, os.str());
   594 	  *_os << '\t';
   595 	  _node_index.insert(std::make_pair(n, os.str()));
   596 	}
   597 	for (typename NodeMaps::iterator it = _node_maps.begin();
   598 	     it != _node_maps.end(); ++it) {
   599 	  std::string value = it->second->get(n);
   600 	  _writer_bits::writeToken(*_os, value);
   601 	  if (it->first == "label") {
   602 	    _node_index.insert(std::make_pair(n, value));
   603 	  }
   604 	  *_os << '\t';
   605 	}
   606 	*_os << std::endl;
   607       }
   608     }
   609 
   610     void writeArcs() {
   611       _writer_bits::MapStorageBase<Arc>* label = 0;
   612       for (typename ArcMaps::iterator it = _arc_maps.begin();
   613 	   it != _arc_maps.end(); ++it) {
   614         if (it->first == "label") {
   615 	  label = it->second;
   616 	  break;
   617 	}
   618       }
   619 
   620       *_os << "@arcs";
   621       if (!_arcs_caption.empty()) {
   622 	_writer_bits::writeToken(*_os << ' ', _arcs_caption);
   623       }
   624       *_os << std::endl;
   625 
   626       *_os << '\t' << '\t';
   627       if (label == 0) {
   628 	*_os << "label" << '\t';
   629       }
   630       for (typename ArcMaps::iterator it = _arc_maps.begin();
   631 	   it != _arc_maps.end(); ++it) {
   632 	_writer_bits::writeToken(*_os, it->first) << '\t';
   633       }
   634       *_os << std::endl;
   635 
   636       std::vector<Arc> arcs;
   637       for (ArcIt n(_digraph); n != INVALID; ++n) {
   638 	arcs.push_back(n);
   639       }
   640       
   641       if (label == 0) {
   642 	IdMap<Digraph, Arc> id_map(_digraph);
   643 	_writer_bits::MapLess<IdMap<Digraph, Arc> > id_less(id_map);
   644 	std::sort(arcs.begin(), arcs.end(), id_less);
   645       } else {
   646 	label->sort(arcs);
   647       }
   648 
   649       for (int i = 0; i < static_cast<int>(arcs.size()); ++i) {
   650 	Arc a = arcs[i];
   651 	_writer_bits::writeToken(*_os, _node_index.
   652 				 find(_digraph.source(a))->second);
   653 	*_os << '\t';
   654 	_writer_bits::writeToken(*_os, _node_index.
   655 				 find(_digraph.target(a))->second);
   656 	*_os << '\t';
   657 	if (label == 0) {
   658 	  std::ostringstream os;
   659 	  os << _digraph.id(a);
   660 	  _writer_bits::writeToken(*_os, os.str());
   661 	  *_os << '\t';
   662 	  _arc_index.insert(std::make_pair(a, os.str()));
   663 	}
   664 	for (typename ArcMaps::iterator it = _arc_maps.begin();
   665 	     it != _arc_maps.end(); ++it) {
   666 	  std::string value = it->second->get(a);
   667 	  _writer_bits::writeToken(*_os, value);
   668 	  if (it->first == "label") {
   669 	    _arc_index.insert(std::make_pair(a, value));
   670 	  }
   671 	  *_os << '\t';
   672 	}
   673 	*_os << std::endl;
   674       }
   675     }
   676 
   677     void writeAttributes() {
   678       if (_attributes.empty()) return;
   679       *_os << "@attributes";
   680       if (!_attributes_caption.empty()) {
   681 	_writer_bits::writeToken(*_os << ' ', _attributes_caption);
   682       }
   683       *_os << std::endl;
   684       for (typename Attributes::iterator it = _attributes.begin();
   685 	   it != _attributes.end(); ++it) {
   686 	_writer_bits::writeToken(*_os, it->first) << ' ';
   687 	_writer_bits::writeToken(*_os, it->second->get());
   688 	*_os << std::endl;
   689       }
   690     }
   691     
   692   public:
   693     
   694     /// \name Execution of the writer    
   695     /// @{
   696 
   697     /// \brief Start the batch processing
   698     ///
   699     /// This function starts the batch processing
   700     void run() {
   701       if (!_skip_nodes) {
   702 	writeNodes();
   703       }
   704       if (!_skip_arcs) {      
   705 	writeArcs();
   706       }
   707       writeAttributes();
   708     }
   709 
   710     /// \brief Gives back the stream of the writer
   711     ///
   712     /// Gives back the stream of the writer
   713     std::ostream& ostream() {
   714       return *_os;
   715     }
   716 
   717     /// @}
   718   };
   719 
   720   /// \relates DigraphWriter
   721   template <typename Digraph>
   722   DigraphWriter<Digraph> digraphWriter(std::ostream& os, Digraph& digraph) {
   723     DigraphWriter<Digraph> tmp(os, digraph);
   724     return tmp;
   725   }
   726 
   727   /// \relates DigraphWriter
   728   template <typename Digraph>
   729   DigraphWriter<Digraph> digraphWriter(const std::string& fn, 
   730 				       Digraph& digraph) {
   731     DigraphWriter<Digraph> tmp(fn, digraph);
   732     return tmp;
   733   }
   734 
   735   /// \relates DigraphWriter
   736   template <typename Digraph>
   737   DigraphWriter<Digraph> digraphWriter(const char* fn, Digraph& digraph) {
   738     DigraphWriter<Digraph> tmp(fn, digraph);
   739     return tmp;
   740   }
   741 }
   742 
   743 #endif