The CPP-GL
library provides functionality for input and output stream operations, allowing users to easily read from and write to various data streams. This feature facilitates the serialization and deserialization of graph data, enabling efficient storage and retrieval of graph structures.
The library supports multiple output formats, controlled through predefined options, making it easy to customize the level of detail included in the output. Input operations ensure that graphs can be recreated from formatted input streams, maintaining data integrity.
Users can save graphs to a file in multiple formats, while the library supports reading from files only in the Graph Specification Format (GSF). This ensures that graphs can be reconstructed accurately from files, preserving the graph's structure and properties.
- Basic usage of stream I/O operations
- I/O options
- File I/O operations
- GSF (Grahp Specification Format)
- Related pages
The CPP-GL
library provides convenient stream insertion and extraction operators for the graph
, vertex_descriptor
, and edge_descriptor
classes, allowing for easy reading and writing of graph data.
Note
In all examples in this section:
graph_type
will refer to any specialization of thegl::graph
graph_element_type
will refer to any specialization of one of thevertex_descriptor
oredge_descriptor
classes
To output a graph or graph element object to a stream, use the insertion operator <<
:
graph_type g;
// Populate the graph 'g' with vertices and edges
std::cout << g << std::endl; // Outputs the graph in the format set for the std::cout stream
const graph_element_type& g_elem = g.<element-getter-method>(params);
std::cout << g_elem << std::endl; // Outputs the single graph element in the format set for the std::cout stream
The output format can be controlled using the predefined option setters defined in the gl/graph_io.hpp header file.
Option setter | Description | Equivalent |
---|---|---|
verbose |
Outputs detailed information about an element | set_option(graph_option::verbose) |
concise |
Outputs a concise representation of an element | unset_option(graph_option::verbose) |
with_vertex_properties |
Outputs an element with vertex properties included | set_option(graph_option::with_vertex_properties) |
without_vertex_properties |
Outputs an element without vertex properties | unset_option(graph_option::with_vertex_properties) |
with_edge_properties |
Outputs an element with edge properties included NOTE: For vertices the behaviour is unchanged |
set_option(graph_option::with_edge_properties) |
without_edge_properties |
Outputs an element without edge properties NOTE: For vertices the behaviour is unchanged |
unset_option(graph_option::with_edge_properties) |
with_properties |
Includes both vertex and edge properties in the output | set_options({ |
without_properties |
Excludes both vertex and edge properties from the output | unset_options({ |
enable_gsf |
Enables GSF output NOTE: Changes the stream behaviour only for the graph class |
set_option(graph_option::gsf) |
disable_gsf |
Disables GSF output NOTE: Changes the stream behaviour only for the graph class |
unset_option(graph_option::gsf) |
Important
- The
(un)set_option
functions return an instance ofio::stream_options_manipulator
with handles setting the options of the stream using bitmasks based on the options' bit positions. - The
(un)set_option
functions as well as thegraph_option
enum and all the predefined option setters are defined in thegl::io
namespace. - The
(un)set_option
functions (as well as the predefined option setters) modify the behaviour only of stream they are inserted to or extracted from and have effect for all graph or graph element instances in such stream.
Below you can find an example program showing how to use the I/O option setters:
#include <gl/graph.hpp>
#include <gl/topologies.hpp> // necessary for `topology::biclique`
int main() {
// declare the graph traits
using graph_traits = gl::graph_traits<gl::directed_t, gl::types::name_property, gl::types::name_property>;
using graph_type = gl::graph<graph_traits>;
// initialize a biclique where |A| = 2 and |B| = 3
const auto graph = gl::topology::biclique<graph_type>(2, 3);
// set the vertex and edge properties of the graph
std::size_t v_idx = 0, e_idx = 0;
for (const auto& vertex : graph.vertices()) {
vertex.properties = {std::format("vertex_{}", ++v_idx)};
for (const auto& edge : graph.adjacent_edges(vertex))
edge.properties = {std::format("edge_{}", ++e_idx)};
}
// print the graph in a concise representation and without properties
std::cout << gl::io::concise << gl::io::without_properties
<< "> concise, without_properties\n" << graph << std::endl;
// print the graph in a verbose representation and with all properties
std::cout << gl::io::verbose << gl::io::with_properties
<< "> verbose, with_properties\n" << graph << std::endl;
}
This program will output:
> concise, without_properties
directed 5 12
- 0 : [0, 2] [0, 3] [0, 4]
- 1 : [1, 2] [1, 3] [1, 4]
- 2 : [2, 0] [2, 1]
- 3 : [3, 0] [3, 1]
- 4 : [4, 0] [4, 1]
> verbose, with_properties
type: directed
number of vertices: 5
number of edges: 12
vertices:
- [id: 0 | properties: "vertex_1"]
adjacent edges:
- [first: 0, second: 2 | properties: "edge_1"]
- [first: 0, second: 3 | properties: "edge_2"]
- [first: 0, second: 4 | properties: "edge_3"]
- [id: 1 | properties: "vertex_2"]
adjacent edges:
- [first: 1, second: 2 | properties: "edge_4"]
- [first: 1, second: 3 | properties: "edge_5"]
- [first: 1, second: 4 | properties: "edge_6"]
- [id: 2 | properties: "vertex_3"]
adjacent edges:
- [first: 2, second: 0 | properties: "edge_7"]
- [first: 2, second: 1 | properties: "edge_8"]
- [id: 3 | properties: "vertex_4"]
adjacent edges:
- [first: 3, second: 0 | properties: "edge_9"]
- [first: 3, second: 1 | properties: "edge_10"]
- [id: 4 | properties: "vertex_5"]
adjacent edges:
- [first: 4, second: 0 | properties: "edge_11"]
- [first: 4, second: 1 | properties: "edge_12"]
The library provides functionality to read and write graphs to and from files. This allows you to store graph data and load it when needed. Graphs can be saved to files in various formats, while loading is supported exclusively in the Graph Specification Format (GSF).
You can save a graph to a file using the io::save(graph, path, options)
function:
- Template parameters:
GraphType: type_traits::c_graph
– The type of the graph to save.Mode: io::detail::c_io_save_mode = io::write
- File open mode.
Note
The Mode
parameter can be:
io::write
: Creates a new file. If the file already exists, an instance ofstd::filesystem::error
is thrown.io::append
: Opens an existing file and appends the graph data to it. If the file does not exist, an instance ofstd::filesystem::error
is thrown.
- Description: Saves the specified graph to the provided file path. The function also allows for optional stream options to control the output format.
- Parameters:
graph: const GraphType&
– The graph to be saved to the file.path: const std::filesystem::path&
(default:"graph.gsf"
) – The path to the file where the graph will be saved.options: const std::initializer_list<stream_options_manipulator>&
(default:{}
) – Optional stream options to control the output.
- Return type:
void
Important
- The
io::save
function uses a newstd::fstream
object, so all desired saving options must be specified in theoptions
. list - The
io::save
function enables GSF output bu defaul, so to disable it, you have to addio::disable_gsf
to theoptions
list.
You can load a graph from a file using the io::load(path)
function:
- Description: Loads a graph from the specified file path.
- Template parameters:
GraphType: type_traits::c_graph
– The type of the graph to be loaded.
Note
- If the specified file path does not exist, an instance of
std::filesystem::error
is thrown. - If the specified file does not contain a GSF representation, the behaviour is undefined.
- If the GSF representation does not match the
GraphType
, an instance ofstd::ios_base::failure
is thrown.
- Parameters:
path: const std::filesystem::path&
(default:"graph.gsf"
) – The path to the file from which the graph will be loaded.
- Return type:
GraphType
Important
The file I/O utility is defined in the gl/graph_file_io.hpp header file.
Below you can find an example showing how to use the save
and load
functions:
#include <gl/graph.hpp>
#include <gl/topologies.hpp> // necessary for `topology::clique`
#include <gl/graph_file_io.hpp> // necessary to `save` and `load` functions
int main() {
// declare the graph type
using graph_traits = gl::graph_traits<gl::directed_t, gl::types::name_property, gl::types::name_property>;
using graph_type = gl::graph<graph_traits>;
// initialize a clique of size 3
const auto clique = gl::topology::clique<graph_type>(3);
// set the vertex and edge properties of the graph
std::size_t v_idx = 0, e_idx = 0;
for (const auto& vertex : clique.vertices()) {
vertex.properties = {std::format("vertex_{}", ++v_idx)};
for (const auto& edge : clique.adjacent_edges(vertex))
edge.properties = {std::format("edge_{}", ++e_idx)};
}
// set the std::cout stream options
std::cout << gl::io::verbose << gl::io::with_properties;
// print the Original graph
std::cout << "> original graph:\n" << clique << std::endl;
// save the graph to a `clique.gsf` file with all properties
gl::io::save(clique, "clique.gsf", {gl::io::with_properties});
// load the graph from the same file and print it
const auto loaded_graph = gl::io::load<graph_type>("clique.gsf");
std::cout << "> loaded graph:\n" << loaded_graph << std::endl;
}
This program will output:
> original graph:
type: directed
number of vertices: 3
number of edges: 6
vertices:
- [id: 0 | properties: "vertex_1"]
adjacent edges:
- [first: 0, second: 1 | properties: "edge_1"]
- [first: 0, second: 2 | properties: "edge_2"]
- [id: 1 | properties: "vertex_2"]
adjacent edges:
- [first: 1, second: 0 | properties: "edge_3"]
- [first: 1, second: 2 | properties: "edge_4"]
- [id: 2 | properties: "vertex_3"]
adjacent edges:
- [first: 2, second: 0 | properties: "edge_5"]
- [first: 2, second: 1 | properties: "edge_6"]
> loaded graph:
type: directed
number of vertices: 3
number of edges: 6
vertices:
- [id: 0 | properties: "vertex_1"]
adjacent edges:
- [first: 0, second: 1 | properties: "edge_1"]
- [first: 0, second: 2 | properties: "edge_2"]
- [id: 1 | properties: "vertex_2"]
adjacent edges:
- [first: 1, second: 0 | properties: "edge_3"]
- [first: 1, second: 2 | properties: "edge_4"]
- [id: 2 | properties: "vertex_3"]
adjacent edges:
- [first: 2, second: 0 | properties: "edge_5"]
- [first: 2, second: 1 | properties: "edge_6"]
The Graph Specification Format (GSF) is a simple text-based format used to represent the structure of a graph. It stores information about the graph's vertices and edges in a human-readable format. This format is the only supported format for loading graphs from files.
Note
Files containing the GSF representation of graphs should have a .gsf
extension, but it is not mandatory.
<directional-specifier> <n-vertices> <n-unique-edges> <vertex-properties-marker> <vertex-properties-marker>
<optional-vertex-properties-list>
<edge-list>
Where:
Element | Description |
---|---|
directional-specifier |
1 if the graph is directed0 if the graph is undirected |
n-vertices |
The number of vertices in the graph |
n-unique-edges |
The number of unique edges in the graph |
vertex-properties-marker |
1 if the vertex properties are present in the graph's specification0 otherwise |
edge-properties-marker |
1 if the edge properties are present in the graph's specification0 otherwise |
optional-vertex-properties-list |
A list of length n-vertices where each element represents theproperties of a vertex with the ID being the element's index in the list NOTE: This list is present in the specification if vertex-properties-marker == 1 |
edge-list |
A list of length n-unique-edges where each element represents an edge in the graphNOTE: Each edge in the list is represented as: - A piar first_id second_id if edge-properties-marker == 0 - A triple first_id second_id properties otherwise |
Below you can find an example GSF representation of the graph from the Exampl: file I/O section with all properties:
1 3 6 1 1
"vertex_1"
"vertex_2"
"vertex_3"
0 1 "edge_1"
0 2 "edge_2"
1 0 "edge_3"
1 2 "edge_4"
2 0 "edge_5"
2 1 "edge_6"