Welcome to the CinderPeak community! This guide is designed to be a practical, beginner-friendly resource to help you familiarize yourself with the basic project functionality. This guide provides the usage instructions of how to include the header files, and which classes and structures are available for users to use and how can you use them to get started with the project with some basic usage examples.
CinderPeak's public API is modular, with components split into separate headers. The table below highlights the key ones.
| Header File | Key Classes/Structures | Purpose |
|---|---|---|
| CinderPeak.hpp | (Convenience Header) | Includes all necessary public headers for basic use. This should be your default choice. |
| CinderGraph.hpp | CinderGraph | The concrete implementation of a graph using an adjacency list data structure. |
| PeakStore.hpp | PeakStore | Core storage engine that manages graph data internally. |
| StorageEngine/Utils.hpp | GraphCreationOptions, CinderVertex, CinderEdge | Contains utility functions, graph creation options, and base classes for custom types. |
To use CinderPeak, you need only to include this header file along with the required namespace declarations.
#include "CinderPeak.hpp"
using namespace CinderPeak::PeakStore;
using namespace CinderPeak;Purpose and Importance:
This main public API header serves as a single entry point, bundling all library components. It simplifies setup by removing the need for multiple includes. While the core logic is in /src, you'll mostly interact with this header.
If you want to include specific components only:
#include "CinderGraph.hpp" // For adjacency list graphs
#include "StorageEngine/Utils.hpp" // For utility functions and optionsPurpose and Importance:
CinderGraph.hpp: Adjacency list implementation. Efficient for sparse graphs, supports directed/undirected edges, allows dynamic updates, and suits DFS/BFS/shortest pathsStorageEngine/Utils.hpp: Provides common utilities for the library, including type checks, graph creation options, metadata, and constants
CinderPeak's architecture is built on a few key components that you'll interact with regularly. It is designed around a small set of key classes that define how graphs are represented, created, and manipulated.
| Class / Structure | Header Location | Purpose |
|---|---|---|
| CinderGraph | src/CinderGraph.hpp | An adjacency list implementation of a graph. Efficient for sparse graphs. |
| PeakStore | src/PeakStore.hpp | Core storage engine that handles the internal graph data management. |
| GraphCreationOptions | src/StorageEngine/Utils.hpp | Configuration class that defines how a graph should behave (Directed, Weighted, etc.). |
| CinderVertex | src/StorageEngine/Utils.hpp | Base class for custom vertex types. |
| CinderEdge | src/StorageEngine/Utils.hpp | Base class for custom edge types. |
- Implements adjacency list representation
- Best for sparse graphs or when graphs are frequently updated
- Efficient for traversals like BFS/DFS
- Direct instantiation:
CinderGraph<VertexType, EdgeType> graph(options)
- Manages internal storage of graph data, ensuring efficient data handling across graph types.
- Typically used internally but essential for understanding the library’s architecture.
Defines how a graph should behave (Directed, Undirected, Weighted, Unweighted). Available options include:
GraphCreationOptions::DirectedGraphCreationOptions::UndirectedGraphCreationOptions::WeightedGraphCreationOptions::UnweightedGraphCreationOptions::SelfLoopsGraphCreationOptions::ParallelEdges
Base classes that custom vertex and edge types must inherit from. They provide:
- Unique ID generation
- Name generation
- Comparison operators
- Hash support for use in containers
CinderPeak is a fully templated library, which means you can define your own struct or class for vertex and edge data. However, custom types must inherit from the provided base classes.
Example: Defining Custom Classes for a Social Network
#include <string>
#include "CinderPeak.hpp"
using namespace CinderPeak::PeakStore;
using namespace CinderPeak;
// A custom class to hold data for each person (vertex)
class UserProfile : public CinderVertex {
public:
std::string username;
int age;
UserProfile() {}
UserProfile(const std::string& name, int userAge) : username(name), age(userAge) {}
};
// A custom class to hold data for each friendship (edge)
class Friendship : public CinderEdge {
public:
int year_met;
std::string relationship_type; // e.g., "Family", "Work"
Friendship() {}
Friendship(int year, const std::string& type) : year_met(year), relationship_type(type) {}
};Here are practical, step-by-step examples showing how to use the library's core functionality.
This example shows how to create a basic directed, weighted graph storing integers.
#include <iostream>
#include "CinderPeak.hpp"
using namespace CinderPeak::PeakStore;
using namespace CinderPeak;
int main() {
// Create graph creation options
GraphCreationOptions opts({
GraphCreationOptions::Directed,
GraphCreationOptions::Weighted
});
// Create a graph that stores integers for both vertices and edges
CinderGraph<int, int> graph(opts);
// Add vertices to the graph
graph.addVertex(10);
graph.addVertex(20);
graph.addVertex(30);
// Add weighted edges to connect the vertices
graph.addEdge(10, 20, 5); // Edge from 10 to 20 with weight 5
graph.addEdge(20, 30, 10); // Edge from 20 to 30 with weight 10
// Access edge weights using operator[]
std::cout << "Edge weight from 10 to 20: " << graph[10][20] << std::endl;
// Or using getEdge method
int edgeWeight = graph.getEdge(20, 30);
std::cout << "Edge weight from 20 to 30: " << edgeWeight << std::endl;
return 0;
}This example uses the UserProfile and Friendship classes defined earlier to create a directed social network graph.
#include <iostream>
#include <string>
#include "CinderPeak.hpp"
using namespace CinderPeak::PeakStore;
using namespace CinderPeak;
// Custom classes (defined as shown in previous section)
class UserProfile : public CinderVertex {
public:
std::string username;
int age;
UserProfile() {}
UserProfile(const std::string& name, int userAge) : username(name), age(userAge) {}
};
class Friendship : public CinderEdge {
public:
int year_met;
std::string relationship_type;
Friendship() {}
Friendship(int year, const std::string& type) : year_met(year), relationship_type(type) {}
};
int main() {
// Create a directed, weighted graph with custom types
GraphCreationOptions opts({
GraphCreationOptions::Directed,
GraphCreationOptions::Weighted
});
CinderGraph<UserProfile, Friendship> socialGraph(opts);
// Create user profiles
UserProfile alice("Alice", 30);
UserProfile bob("Bob", 25);
UserProfile charlie("Charlie", 35);
// Add vertices to the graph
socialGraph.addVertex(alice);
socialGraph.addVertex(bob);
socialGraph.addVertex(charlie);
// Create friendship connections
Friendship workFriend(2022, "Work");
Friendship familyMember(2020, "Family");
// Add edges with friendship data
socialGraph.addEdge(alice, bob, workFriend);
socialGraph.addEdge(alice, charlie, familyMember);
// Access edge data
Friendship connection = socialGraph.getEdge(alice, bob);
std::cout << "Alice and Bob met in: " << connection.year_met << std::endl;
std::cout << "Relationship type: " << connection.relationship_type << std::endl;
return 0;
}This example demonstrates using the adjacency list representation for a sparse graph.
#include <iostream>
#include "CinderPeak.hpp"
using namespace CinderPeak::PeakStore;
using namespace CinderPeak;
int main() {
// Create options for an undirected, weighted graph
GraphCreationOptions opts({
GraphCreationOptions::Undirected,
GraphCreationOptions::Weighted
});
// Use CinderGraph for sparse graphs
CinderGraph<int, double> network(opts);
// Add vertices
network.addVertex(1);
network.addVertex(2);
network.addVertex(3);
network.addVertex(4);
network.addVertex(5);
// Add weighted edges (distances)
network.addEdge(1, 3, 10.5);
network.addEdge(1, 4, 9.2);
network.addEdge(4, 5, 7.8);
network.addEdge(1, 2, 6.1);
std::cout << "Successfully created a sparse network graph!" << std::endl;
return 0;
}When creating graphs, you can specify various options that control the graph's behavior:
// Directed, weighted graph
GraphCreationOptions opts1({
GraphCreationOptions::Directed,
GraphCreationOptions::Weighted
});
// Undirected, unweighted graph with self-loops allowed
GraphCreationOptions opts2({
GraphCreationOptions::Undirected,
GraphCreationOptions::Unweighted,
GraphCreationOptions::SelfLoops
});
// Directed graph with parallel edges allowed
GraphCreationOptions opts3({
GraphCreationOptions::Directed,
GraphCreationOptions::ParallelEdges
});| Option | Description |
|---|---|
Directed |
Creates a directed graph where edges have direction |
Undirected |
Creates an undirected graph where edges are bidirectional |
Weighted |
Graph supports edge weights |
Unweighted |
Graph doesn't use edge weights |
SelfLoops |
Vertices can have edges to themselves |
ParallelEdges |
Multiple edges between the same pair of vertices are allowed |
// Add vertices
graph.addVertex(data);
// Add unweighted edges (for unweighted graphs)
graph.addEdge(source, destination);
// Add weighted edges (for weighted graphs)
graph.addEdge(source, destination, weight);EdgeType weight = graph.getEdge(source, destination);Guidelines:
- Use
CinderGraphfor most common applications, especially when you have many vertices but relatively few edges
CinderPeak uses an internal error handling system. When operations fail, they are logged internally:
- Adding duplicate vertices (when parallel edges aren't allowed) will log a warning but won't crash
- Accessing non-existent edges returns a default-constructed EdgeType
- Most errors are handled gracefully with appropriate logging
-
Always include required namespaces:
using namespace CinderPeak::PeakStore; using namespace CinderPeak;
-
Choose the right graph representation:
- Use
CinderGraphfor sparse graphs - Use
CinderGraphfor dense graphs or when you need O(1) edge lookups
- Use
-
Custom types must inherit from base classes:
class MyVertex : public CinderVertex { /* ... */ }; class MyEdge : public CinderEdge { /* ... */ };
-
Configure graph options appropriately:
GraphCreationOptions opts({ GraphCreationOptions::Directed, // or Undirected GraphCreationOptions::Weighted // or Unweighted });
-
Use consistent data types:
- Make sure your vertex and edge types are consistent throughout your application
- Consider using primitive types (int, double, string) for simple cases