1.1 --- a/lemon/lgf_writer.h Sat May 31 12:34:44 2008 +0200
1.2 +++ b/lemon/lgf_writer.h Sat May 31 12:49:18 2008 +0200
1.3 @@ -71,6 +71,27 @@
1.4 }
1.5 };
1.6
1.7 + template <typename _Graph, bool _dir, typename _Map>
1.8 + class GraphArcMapLess {
1.9 + public:
1.10 + typedef _Map Map;
1.11 + typedef _Graph Graph;
1.12 + typedef typename Graph::Edge Item;
1.13 +
1.14 + private:
1.15 + const Graph& _graph;
1.16 + const Map& _map;
1.17 +
1.18 + public:
1.19 + GraphArcMapLess(const Graph& graph, const Map& map)
1.20 + : _graph(graph), _map(map) {}
1.21 +
1.22 + bool operator()(const Item& left, const Item& right) {
1.23 + return _map[_graph.direct(left, _dir)] <
1.24 + _map[_graph.direct(right, _dir)];
1.25 + }
1.26 + };
1.27 +
1.28 template <typename _Item>
1.29 class MapStorageBase {
1.30 public:
1.31 @@ -110,6 +131,36 @@
1.32 }
1.33 };
1.34
1.35 + template <typename _Graph, bool _dir, typename _Map,
1.36 + typename _Converter = DefaultConverter<typename _Map::Value> >
1.37 + class GraphArcMapStorage : public MapStorageBase<typename _Graph::Edge> {
1.38 + public:
1.39 + typedef _Map Map;
1.40 + typedef _Converter Converter;
1.41 + typedef _Graph Graph;
1.42 + typedef typename Graph::Edge Item;
1.43 + static const bool dir = _dir;
1.44 +
1.45 + private:
1.46 + const Graph& _graph;
1.47 + const Map& _map;
1.48 + Converter _converter;
1.49 +
1.50 + public:
1.51 + GraphArcMapStorage(const Graph& graph, const Map& map,
1.52 + const Converter& converter = Converter())
1.53 + : _graph(graph), _map(map), _converter(converter) {}
1.54 + virtual ~GraphArcMapStorage() {}
1.55 +
1.56 + virtual std::string get(const Item& item) {
1.57 + return _converter(_map[_graph.direct(item, dir)]);
1.58 + }
1.59 + virtual void sort(std::vector<Item>& items) {
1.60 + GraphArcMapLess<Graph, dir, Map> less(_graph, _map);
1.61 + std::sort(items.begin(), items.end(), less);
1.62 + }
1.63 + };
1.64 +
1.65 class ValueStorageBase {
1.66 public:
1.67 ValueStorageBase() {}
1.68 @@ -154,6 +205,26 @@
1.69 }
1.70 };
1.71
1.72 + template <typename Graph>
1.73 + struct GraphArcLookUpConverter {
1.74 + const Graph& _graph;
1.75 + const std::map<typename Graph::Edge, std::string>& _map;
1.76 +
1.77 + GraphArcLookUpConverter(const Graph& graph,
1.78 + const std::map<typename Graph::Edge,
1.79 + std::string>& map)
1.80 + : _graph(graph), _map(map) {}
1.81 +
1.82 + std::string operator()(const typename Graph::Arc& val) {
1.83 + typename std::map<typename Graph::Edge, std::string>
1.84 + ::const_iterator it = _map.find(val);
1.85 + if (it == _map.end()) {
1.86 + throw DataFormatError("Item not found");
1.87 + }
1.88 + return (_graph.direction(val) ? '+' : '-') + it->second;
1.89 + }
1.90 + };
1.91 +
1.92 bool isWhiteSpace(char c) {
1.93 return c == ' ' || c == '\t' || c == '\v' ||
1.94 c == '\n' || c == '\r' || c == '\f';
1.95 @@ -738,6 +809,517 @@
1.96 DigraphWriter<Digraph> tmp(fn, digraph);
1.97 return tmp;
1.98 }
1.99 +
1.100 + /// \ingroup lemon_io
1.101 + ///
1.102 + /// \brief LGF writer for directed graphs
1.103 + ///
1.104 + /// This utility writes an \ref lgf-format "LGF" file.
1.105 + template <typename _Graph>
1.106 + class GraphWriter {
1.107 + public:
1.108 +
1.109 + typedef _Graph Graph;
1.110 + TEMPLATE_GRAPH_TYPEDEFS(Graph);
1.111 +
1.112 + private:
1.113 +
1.114 +
1.115 + std::ostream* _os;
1.116 + bool local_os;
1.117 +
1.118 + Graph& _graph;
1.119 +
1.120 + std::string _nodes_caption;
1.121 + std::string _edges_caption;
1.122 + std::string _attributes_caption;
1.123 +
1.124 + typedef std::map<Node, std::string> NodeIndex;
1.125 + NodeIndex _node_index;
1.126 + typedef std::map<Edge, std::string> EdgeIndex;
1.127 + EdgeIndex _edge_index;
1.128 +
1.129 + typedef std::vector<std::pair<std::string,
1.130 + _writer_bits::MapStorageBase<Node>* > > NodeMaps;
1.131 + NodeMaps _node_maps;
1.132 +
1.133 + typedef std::vector<std::pair<std::string,
1.134 + _writer_bits::MapStorageBase<Edge>* > >EdgeMaps;
1.135 + EdgeMaps _edge_maps;
1.136 +
1.137 + typedef std::vector<std::pair<std::string,
1.138 + _writer_bits::ValueStorageBase*> > Attributes;
1.139 + Attributes _attributes;
1.140 +
1.141 + bool _skip_nodes;
1.142 + bool _skip_edges;
1.143 +
1.144 + public:
1.145 +
1.146 + /// \brief Constructor
1.147 + ///
1.148 + /// Construct a directed graph writer, which writes to the given
1.149 + /// output stream.
1.150 + GraphWriter(std::ostream& is, Graph& graph)
1.151 + : _os(&is), local_os(false), _graph(graph),
1.152 + _skip_nodes(false), _skip_edges(false) {}
1.153 +
1.154 + /// \brief Constructor
1.155 + ///
1.156 + /// Construct a directed graph writer, which writes to the given
1.157 + /// output file.
1.158 + GraphWriter(const std::string& fn, Graph& graph)
1.159 + : _os(new std::ofstream(fn.c_str())), local_os(true), _graph(graph),
1.160 + _skip_nodes(false), _skip_edges(false) {}
1.161 +
1.162 + /// \brief Constructor
1.163 + ///
1.164 + /// Construct a directed graph writer, which writes to the given
1.165 + /// output file.
1.166 + GraphWriter(const char* fn, Graph& graph)
1.167 + : _os(new std::ofstream(fn)), local_os(true), _graph(graph),
1.168 + _skip_nodes(false), _skip_edges(false) {}
1.169 +
1.170 + /// \brief Copy constructor
1.171 + ///
1.172 + /// The copy constructor transfers all data from the other writer,
1.173 + /// therefore the copied writer will not be usable more.
1.174 + GraphWriter(GraphWriter& other)
1.175 + : _os(other._os), local_os(other.local_os), _graph(other._graph),
1.176 + _skip_nodes(other._skip_nodes), _skip_edges(other._skip_edges) {
1.177 +
1.178 + other._os = 0;
1.179 + other.local_os = false;
1.180 +
1.181 + _node_index.swap(other._node_index);
1.182 + _edge_index.swap(other._edge_index);
1.183 +
1.184 + _node_maps.swap(other._node_maps);
1.185 + _edge_maps.swap(other._edge_maps);
1.186 + _attributes.swap(other._attributes);
1.187 +
1.188 + _nodes_caption = other._nodes_caption;
1.189 + _edges_caption = other._edges_caption;
1.190 + _attributes_caption = other._attributes_caption;
1.191 + }
1.192 +
1.193 + /// \brief Destructor
1.194 + ~GraphWriter() {
1.195 + for (typename NodeMaps::iterator it = _node_maps.begin();
1.196 + it != _node_maps.end(); ++it) {
1.197 + delete it->second;
1.198 + }
1.199 +
1.200 + for (typename EdgeMaps::iterator it = _edge_maps.begin();
1.201 + it != _edge_maps.end(); ++it) {
1.202 + delete it->second;
1.203 + }
1.204 +
1.205 + for (typename Attributes::iterator it = _attributes.begin();
1.206 + it != _attributes.end(); ++it) {
1.207 + delete it->second;
1.208 + }
1.209 +
1.210 + if (local_os) {
1.211 + delete _os;
1.212 + }
1.213 + }
1.214 +
1.215 + private:
1.216 +
1.217 + GraphWriter& operator=(const GraphWriter&);
1.218 +
1.219 + public:
1.220 +
1.221 + /// \name Writing rules
1.222 + /// @{
1.223 +
1.224 + /// \brief Node map reading rule
1.225 + ///
1.226 + /// Add a node map reading rule to the writer.
1.227 + template <typename Map>
1.228 + GraphWriter& nodeMap(const std::string& caption, const Map& map) {
1.229 + checkConcept<concepts::ReadMap<Node, typename Map::Value>, Map>();
1.230 + _writer_bits::MapStorageBase<Node>* storage =
1.231 + new _writer_bits::MapStorage<Node, Map>(map);
1.232 + _node_maps.push_back(std::make_pair(caption, storage));
1.233 + return *this;
1.234 + }
1.235 +
1.236 + /// \brief Node map writing rule
1.237 + ///
1.238 + /// Add a node map writing rule with specialized converter to the
1.239 + /// writer.
1.240 + template <typename Map, typename Converter>
1.241 + GraphWriter& nodeMap(const std::string& caption, const Map& map,
1.242 + const Converter& converter = Converter()) {
1.243 + checkConcept<concepts::ReadMap<Node, typename Map::Value>, Map>();
1.244 + _writer_bits::MapStorageBase<Node>* storage =
1.245 + new _writer_bits::MapStorage<Node, Map, Converter>(map, converter);
1.246 + _node_maps.push_back(std::make_pair(caption, storage));
1.247 + return *this;
1.248 + }
1.249 +
1.250 + /// \brief Edge map writing rule
1.251 + ///
1.252 + /// Add an edge map writing rule to the writer.
1.253 + template <typename Map>
1.254 + GraphWriter& edgeMap(const std::string& caption, const Map& map) {
1.255 + checkConcept<concepts::ReadMap<Edge, typename Map::Value>, Map>();
1.256 + _writer_bits::MapStorageBase<Edge>* storage =
1.257 + new _writer_bits::MapStorage<Edge, Map>(map);
1.258 + _edge_maps.push_back(std::make_pair(caption, storage));
1.259 + return *this;
1.260 + }
1.261 +
1.262 + /// \brief Edge map writing rule
1.263 + ///
1.264 + /// Add an edge map writing rule with specialized converter to the
1.265 + /// writer.
1.266 + template <typename Map, typename Converter>
1.267 + GraphWriter& edgeMap(const std::string& caption, const Map& map,
1.268 + const Converter& converter = Converter()) {
1.269 + checkConcept<concepts::ReadMap<Edge, typename Map::Value>, Map>();
1.270 + _writer_bits::MapStorageBase<Edge>* storage =
1.271 + new _writer_bits::MapStorage<Edge, Map, Converter>(map, converter);
1.272 + _edge_maps.push_back(std::make_pair(caption, storage));
1.273 + return *this;
1.274 + }
1.275 +
1.276 + /// \brief Arc map writing rule
1.277 + ///
1.278 + /// Add an arc map writing rule to the writer.
1.279 + template <typename Map>
1.280 + GraphWriter& arcMap(const std::string& caption, const Map& map) {
1.281 + checkConcept<concepts::ReadMap<Arc, typename Map::Value>, Map>();
1.282 + _writer_bits::MapStorageBase<Edge>* forward_storage =
1.283 + new _writer_bits::GraphArcMapStorage<Graph, true, Map>(_graph, map);
1.284 + _edge_maps.push_back(std::make_pair('+' + caption, forward_storage));
1.285 + _writer_bits::MapStorageBase<Edge>* backward_storage =
1.286 + new _writer_bits::GraphArcMapStorage<Graph, false, Map>(_graph, map);
1.287 + _edge_maps.push_back(std::make_pair('-' + caption, backward_storage));
1.288 + return *this;
1.289 + }
1.290 +
1.291 + /// \brief Arc map writing rule
1.292 + ///
1.293 + /// Add an arc map writing rule with specialized converter to the
1.294 + /// writer.
1.295 + template <typename Map, typename Converter>
1.296 + GraphWriter& arcMap(const std::string& caption, const Map& map,
1.297 + const Converter& converter = Converter()) {
1.298 + checkConcept<concepts::ReadMap<Arc, typename Map::Value>, Map>();
1.299 + _writer_bits::MapStorageBase<Edge>* forward_storage =
1.300 + new _writer_bits::GraphArcMapStorage<Graph, true, Map, Converter>
1.301 + (_graph, map, converter);
1.302 + _edge_maps.push_back(std::make_pair('+' + caption, forward_storage));
1.303 + _writer_bits::MapStorageBase<Edge>* backward_storage =
1.304 + new _writer_bits::GraphArcMapStorage<Graph, false, Map, Converter>
1.305 + (_graph, map, converter);
1.306 + _edge_maps.push_back(std::make_pair('-' + caption, backward_storage));
1.307 + return *this;
1.308 + }
1.309 +
1.310 + /// \brief Attribute writing rule
1.311 + ///
1.312 + /// Add an attribute writing rule to the writer.
1.313 + template <typename Value>
1.314 + GraphWriter& attribute(const std::string& caption, const Value& value) {
1.315 + _writer_bits::ValueStorageBase* storage =
1.316 + new _writer_bits::ValueStorage<Value>(value);
1.317 + _attributes.push_back(std::make_pair(caption, storage));
1.318 + return *this;
1.319 + }
1.320 +
1.321 + /// \brief Attribute writing rule
1.322 + ///
1.323 + /// Add an attribute writing rule with specialized converter to the
1.324 + /// writer.
1.325 + template <typename Value, typename Converter>
1.326 + GraphWriter& attribute(const std::string& caption, const Value& value,
1.327 + const Converter& converter = Converter()) {
1.328 + _writer_bits::ValueStorageBase* storage =
1.329 + new _writer_bits::ValueStorage<Value, Converter>(value, converter);
1.330 + _attributes.push_back(std::make_pair(caption, storage));
1.331 + return *this;
1.332 + }
1.333 +
1.334 + /// \brief Node writing rule
1.335 + ///
1.336 + /// Add a node writing rule to the writer.
1.337 + GraphWriter& node(const std::string& caption, const Node& node) {
1.338 + typedef _writer_bits::MapLookUpConverter<Node> Converter;
1.339 + Converter converter(_node_index);
1.340 + _writer_bits::ValueStorageBase* storage =
1.341 + new _writer_bits::ValueStorage<Node, Converter>(node, converter);
1.342 + _attributes.push_back(std::make_pair(caption, storage));
1.343 + return *this;
1.344 + }
1.345 +
1.346 + /// \brief Edge writing rule
1.347 + ///
1.348 + /// Add an edge writing rule to writer.
1.349 + GraphWriter& edge(const std::string& caption, const Edge& edge) {
1.350 + typedef _writer_bits::MapLookUpConverter<Edge> Converter;
1.351 + Converter converter(_edge_index);
1.352 + _writer_bits::ValueStorageBase* storage =
1.353 + new _writer_bits::ValueStorage<Edge, Converter>(edge, converter);
1.354 + _attributes.push_back(std::make_pair(caption, storage));
1.355 + return *this;
1.356 + }
1.357 +
1.358 + /// \brief Arc writing rule
1.359 + ///
1.360 + /// Add an arc writing rule to writer.
1.361 + GraphWriter& arc(const std::string& caption, const Arc& arc) {
1.362 + typedef _writer_bits::GraphArcLookUpConverter<Graph> Converter;
1.363 + Converter converter(_graph, _edge_index);
1.364 + _writer_bits::ValueStorageBase* storage =
1.365 + new _writer_bits::ValueStorage<Arc, Converter>(arc, converter);
1.366 + _attributes.push_back(std::make_pair(caption, storage));
1.367 + return *this;
1.368 + }
1.369 +
1.370 + /// \name Select section by name
1.371 + /// @{
1.372 +
1.373 + /// \brief Set \c \@nodes section to be read
1.374 + ///
1.375 + /// Set \c \@nodes section to be read
1.376 + GraphWriter& nodes(const std::string& caption) {
1.377 + _nodes_caption = caption;
1.378 + return *this;
1.379 + }
1.380 +
1.381 + /// \brief Set \c \@edges section to be read
1.382 + ///
1.383 + /// Set \c \@edges section to be read
1.384 + GraphWriter& edges(const std::string& caption) {
1.385 + _edges_caption = caption;
1.386 + return *this;
1.387 + }
1.388 +
1.389 + /// \brief Set \c \@attributes section to be read
1.390 + ///
1.391 + /// Set \c \@attributes section to be read
1.392 + GraphWriter& attributes(const std::string& caption) {
1.393 + _attributes_caption = caption;
1.394 + return *this;
1.395 + }
1.396 +
1.397 + /// \name Skipping section
1.398 + /// @{
1.399 +
1.400 + /// \brief Skip writing the node set
1.401 + ///
1.402 + /// The \c \@nodes section will be not written to the stream.
1.403 + GraphWriter& skipNodes() {
1.404 + LEMON_ASSERT(!_skip_nodes, "Multiple usage of skipNodes() member");
1.405 + return *this;
1.406 + }
1.407 +
1.408 + /// \brief Skip writing edge set
1.409 + ///
1.410 + /// The \c \@edges section will be not written to the stream.
1.411 + GraphWriter& skipEdges() {
1.412 + LEMON_ASSERT(!_skip_edges, "Multiple usage of skipEdges() member");
1.413 + return *this;
1.414 + }
1.415 +
1.416 + /// @}
1.417 +
1.418 + private:
1.419 +
1.420 + void writeNodes() {
1.421 + _writer_bits::MapStorageBase<Node>* label = 0;
1.422 + for (typename NodeMaps::iterator it = _node_maps.begin();
1.423 + it != _node_maps.end(); ++it) {
1.424 + if (it->first == "label") {
1.425 + label = it->second;
1.426 + break;
1.427 + }
1.428 + }
1.429 +
1.430 + *_os << "@nodes";
1.431 + if (!_nodes_caption.empty()) {
1.432 + _writer_bits::writeToken(*_os << ' ', _nodes_caption);
1.433 + }
1.434 + *_os << std::endl;
1.435 +
1.436 + if (label == 0) {
1.437 + *_os << "label" << '\t';
1.438 + }
1.439 + for (typename NodeMaps::iterator it = _node_maps.begin();
1.440 + it != _node_maps.end(); ++it) {
1.441 + _writer_bits::writeToken(*_os, it->first) << '\t';
1.442 + }
1.443 + *_os << std::endl;
1.444 +
1.445 + std::vector<Node> nodes;
1.446 + for (NodeIt n(_graph); n != INVALID; ++n) {
1.447 + nodes.push_back(n);
1.448 + }
1.449 +
1.450 + if (label == 0) {
1.451 + IdMap<Graph, Node> id_map(_graph);
1.452 + _writer_bits::MapLess<IdMap<Graph, Node> > id_less(id_map);
1.453 + std::sort(nodes.begin(), nodes.end(), id_less);
1.454 + } else {
1.455 + label->sort(nodes);
1.456 + }
1.457 +
1.458 + for (int i = 0; i < static_cast<int>(nodes.size()); ++i) {
1.459 + Node n = nodes[i];
1.460 + if (label == 0) {
1.461 + std::ostringstream os;
1.462 + os << _graph.id(n);
1.463 + _writer_bits::writeToken(*_os, os.str());
1.464 + *_os << '\t';
1.465 + _node_index.insert(std::make_pair(n, os.str()));
1.466 + }
1.467 + for (typename NodeMaps::iterator it = _node_maps.begin();
1.468 + it != _node_maps.end(); ++it) {
1.469 + std::string value = it->second->get(n);
1.470 + _writer_bits::writeToken(*_os, value);
1.471 + if (it->first == "label") {
1.472 + _node_index.insert(std::make_pair(n, value));
1.473 + }
1.474 + *_os << '\t';
1.475 + }
1.476 + *_os << std::endl;
1.477 + }
1.478 + }
1.479 +
1.480 + void writeEdges() {
1.481 + _writer_bits::MapStorageBase<Edge>* label = 0;
1.482 + for (typename EdgeMaps::iterator it = _edge_maps.begin();
1.483 + it != _edge_maps.end(); ++it) {
1.484 + if (it->first == "label") {
1.485 + label = it->second;
1.486 + break;
1.487 + }
1.488 + }
1.489 +
1.490 + *_os << "@edges";
1.491 + if (!_edges_caption.empty()) {
1.492 + _writer_bits::writeToken(*_os << ' ', _edges_caption);
1.493 + }
1.494 + *_os << std::endl;
1.495 +
1.496 + *_os << '\t' << '\t';
1.497 + if (label == 0) {
1.498 + *_os << "label" << '\t';
1.499 + }
1.500 + for (typename EdgeMaps::iterator it = _edge_maps.begin();
1.501 + it != _edge_maps.end(); ++it) {
1.502 + _writer_bits::writeToken(*_os, it->first) << '\t';
1.503 + }
1.504 + *_os << std::endl;
1.505 +
1.506 + std::vector<Edge> edges;
1.507 + for (EdgeIt n(_graph); n != INVALID; ++n) {
1.508 + edges.push_back(n);
1.509 + }
1.510 +
1.511 + if (label == 0) {
1.512 + IdMap<Graph, Edge> id_map(_graph);
1.513 + _writer_bits::MapLess<IdMap<Graph, Edge> > id_less(id_map);
1.514 + std::sort(edges.begin(), edges.end(), id_less);
1.515 + } else {
1.516 + label->sort(edges);
1.517 + }
1.518 +
1.519 + for (int i = 0; i < static_cast<int>(edges.size()); ++i) {
1.520 + Edge e = edges[i];
1.521 + _writer_bits::writeToken(*_os, _node_index.
1.522 + find(_graph.u(e))->second);
1.523 + *_os << '\t';
1.524 + _writer_bits::writeToken(*_os, _node_index.
1.525 + find(_graph.v(e))->second);
1.526 + *_os << '\t';
1.527 + if (label == 0) {
1.528 + std::ostringstream os;
1.529 + os << _graph.id(e);
1.530 + _writer_bits::writeToken(*_os, os.str());
1.531 + *_os << '\t';
1.532 + _edge_index.insert(std::make_pair(e, os.str()));
1.533 + }
1.534 + for (typename EdgeMaps::iterator it = _edge_maps.begin();
1.535 + it != _edge_maps.end(); ++it) {
1.536 + std::string value = it->second->get(e);
1.537 + _writer_bits::writeToken(*_os, value);
1.538 + if (it->first == "label") {
1.539 + _edge_index.insert(std::make_pair(e, value));
1.540 + }
1.541 + *_os << '\t';
1.542 + }
1.543 + *_os << std::endl;
1.544 + }
1.545 + }
1.546 +
1.547 + void writeAttributes() {
1.548 + if (_attributes.empty()) return;
1.549 + *_os << "@attributes";
1.550 + if (!_attributes_caption.empty()) {
1.551 + _writer_bits::writeToken(*_os << ' ', _attributes_caption);
1.552 + }
1.553 + *_os << std::endl;
1.554 + for (typename Attributes::iterator it = _attributes.begin();
1.555 + it != _attributes.end(); ++it) {
1.556 + _writer_bits::writeToken(*_os, it->first) << ' ';
1.557 + _writer_bits::writeToken(*_os, it->second->get());
1.558 + *_os << std::endl;
1.559 + }
1.560 + }
1.561 +
1.562 + public:
1.563 +
1.564 + /// \name Execution of the writer
1.565 + /// @{
1.566 +
1.567 + /// \brief Start the batch processing
1.568 + ///
1.569 + /// This function starts the batch processing
1.570 + void run() {
1.571 + if (!_skip_nodes) {
1.572 + writeNodes();
1.573 + }
1.574 + if (!_skip_edges) {
1.575 + writeEdges();
1.576 + }
1.577 + writeAttributes();
1.578 + }
1.579 +
1.580 + /// \brief Gives back the stream of the writer
1.581 + ///
1.582 + /// Gives back the stream of the writer
1.583 + std::ostream& ostream() {
1.584 + return *_os;
1.585 + }
1.586 +
1.587 + /// @}
1.588 + };
1.589 +
1.590 + /// \relates GraphWriter
1.591 + template <typename Graph>
1.592 + GraphWriter<Graph> graphWriter(std::ostream& os, Graph& graph) {
1.593 + GraphWriter<Graph> tmp(os, graph);
1.594 + return tmp;
1.595 + }
1.596 +
1.597 + /// \relates GraphWriter
1.598 + template <typename Graph>
1.599 + GraphWriter<Graph> graphWriter(const std::string& fn, Graph& graph) {
1.600 + GraphWriter<Graph> tmp(fn, graph);
1.601 + return tmp;
1.602 + }
1.603 +
1.604 + /// \relates GraphWriter
1.605 + template <typename Graph>
1.606 + GraphWriter<Graph> graphWriter(const char* fn, Graph& graph) {
1.607 + GraphWriter<Graph> tmp(fn, graph);
1.608 + return tmp;
1.609 + }
1.610 }
1.611
1.612 #endif