24 Commits

Author SHA1 Message Date
f29522985d Fix README for lcd example. 2025-09-21 12:04:00 -04:00
606e61c98e Add SSD1306 data sheet. 2025-09-21 12:00:11 -04:00
d36de58e16 Verify project names 2025-09-21 11:58:51 -04:00
acf99da972 Remove esp-idf no std template example. 2025-09-21 11:55:15 -04:00
10fd633a2f WIP 2 2025-09-21 11:54:43 -04:00
79c6da4c60 [esp] Add Rust no-std example. 2025-09-21 09:35:04 -04:00
bb28b1e2ef [esp] Add Rust example with std. 2025-07-06 13:22:06 -04:00
20efb62615 [cpp] Fix root project build and dependencies. 2025-07-05 13:42:59 -04:00
edde77b9c3 [esp] Remove arduino-esp32 from LCD example. 2025-03-14 09:46:47 -04:00
5565ad5170 [esp] Add I2C LCD example. 2025-03-01 18:36:03 -05:00
6cd7d7db29 [esp] Port temp-humidity-web example to cmake. 2025-02-08 12:50:06 -05:00
17c559a31f [esp] Add ESP-IDF cmake example. 2025-02-08 12:47:01 -05:00
e6ba60da89 [esp] Add temperature and humidity example. 2025-02-01 23:49:53 -05:00
8bf174d256 [esp] Add ESP examples. 2025-02-01 14:33:40 -05:00
5f9f508581 [cpp] Remove ignores 2022-12-24 10:18:19 -05:00
3b6ecaa5e9 [cpp] Add Qt Desginer widget plugin examples 2022-12-24 10:16:30 -05:00
de652bad32 [cpp] Add catch and qt examples 2022-12-18 08:57:41 -05:00
d1fb33c58e [dotnet] Add dotnet projects and examples
+ Sitemap generator I created while learning the dispose pattern
+ Testing project for learning general C#
2022-05-04 14:59:17 -04:00
6dbac7559a [cpp] Update READMEs for C++ projects and examples 2022-05-04 12:54:06 -04:00
34f12250ab [cpp] Update weighted graph
+ totalWeight is now tracked for BFS & DFS traversals
+ Refactor graph search info structs
2022-04-14 14:37:53 -04:00
4b47630548 [cpp] Clean up graph implementations 2022-04-13 21:15:03 -04:00
6986c73651 [cpp] Add example of using condition_variables 2022-04-03 14:06:36 -04:00
92b3af7813 [cpp] Add example and solution for livelocks 2022-04-02 19:12:40 -04:00
6c0018c469 [cpp] Add example and solution for deadlocks 2022-04-02 12:05:06 -04:00
251 changed files with 19280 additions and 340 deletions

2
.gitignore vendored
View File

@@ -10,3 +10,5 @@
**/Makefile **/Makefile
**/*.cbp **/*.cbp
**/node-modules/ **/node-modules/
**/CMakeLists.txt.user
**/catch2/bin/

View File

@@ -2,14 +2,16 @@
This repository is a collection of useful code snippets and configurations. This repository is a collection of useful code snippets and configurations.
``` ```bash
github.com/shaunrd0/klips/ shaunrd0/klips/
├── ansible # Ansible roles, playbooks, and examples ├── ansible # Ansible roles, playbooks, and examples
├── blockchain # Blockchain related project templates and examples ├── blockchain # Blockchain related project templates and examples
├── cpp # C++ programs, datastructures, and other examples ├── cpp # C++ programs, datastructures, and other examples
├── dotnet # .NET projects and examples
├── esp # ESP32 projects and examples
├── figlet # Figlet fonts I like :) ├── figlet # Figlet fonts I like :)
├── javascript # Javascript projects and examples ├── javascript # Javascript projects and examples
├── python # Python scripts or tools I've made ├── python # Python scripts and tools I've made
├── README.md ├── scripts # Bash scripts
└── scripts # Bash scripts └── README.md
``` ```

View File

@@ -1,4 +1,4 @@
# Ansible # ansible
A few simple roles / plays I've put together in learning how to use Ansible can be found under their corresponding directories. A few simple roles / plays I've put together in learning how to use Ansible can be found under their corresponding directories.

View File

@@ -1,3 +1,4 @@
# blockchain
A template project for getting started working on the Ethereum blockchain. A template project for getting started working on the Ethereum blockchain.
This project comes with basic packages for compiling and deploying Solidity contracts with Truffle. This project comes with basic packages for compiling and deploying Solidity contracts with Truffle.

View File

@@ -1,6 +1,6 @@
################################################################################ ################################################################################
## Author: Shaun Reed ## ## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ## ## Legal: All Content (c) 2025 Shaun Reed, all rights reserved ##
## About: A root project for practicing C++ ## ## About: A root project for practicing C++ ##
## This project can be built to debug and run all nested projects ## ## This project can be built to debug and run all nested projects ##
## Or, any subdirectory with a project() statement can be selected ## ## Or, any subdirectory with a project() statement can be selected ##
@@ -16,14 +16,49 @@ project(
DESCRIPTION "A root project for several small cpp practice projects" DESCRIPTION "A root project for several small cpp practice projects"
LANGUAGES CXX LANGUAGES CXX
) )
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
add_compile_options("-Wall") add_compile_options("-Wall")
option(KLIPS_CCACHE "Enable ccache" ON)
if (KLIPS_CCACHE)
find_program(SCCACHE_PATH sccache)
if(SCCACHE_PATH)
message(STATUS "[Klips] Found sccache: ${SCCACHE_PATH}")
set(CMAKE_CXX_COMPILER_LAUNCHER ${SCCACHE_PATH})
set(CMAKE_C_COMPILER_LAUNCHER ${SCCACHE_PATH})
else()
message(STATUS "[Klips] Failed to find sccache, falling back to ccache.")
find_program(CCACHE_PATH ccache)
if(CCACHE_PATH)
message(STATUS "[Klips] Found ccache: ${CCACHE_PATH}")
set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE_PATH})
set(CMAKE_C_COMPILER_LAUNCHER ${CCACHE_PATH})
else()
message(WARNING "[Klips] Failed to find sccache and ccache. Compilation will not be cached.")
endif()
endif()
endif()
add_subdirectory(algorithms) add_subdirectory(algorithms)
add_subdirectory(catch2)
add_subdirectory(cmake-example) add_subdirectory(cmake-example)
add_subdirectory(cryptography) add_subdirectory(cryptography)
add_subdirectory(datastructs) add_subdirectory(datastructs)
add_subdirectory(graphics) add_subdirectory(graphics)
add_subdirectory(multithreading) add_subdirectory(multithreading)
add_subdirectory(patterns) add_subdirectory(patterns)
find_package(Qt6 COMPONENTS UiPlugin Core Gui Widgets)
if (NOT Qt6_FOUND)
message(
WARNING
"[Klips] Qt examples will not be built.\n"
"On Ubuntu 24.04 Qt6 can be installed using apt:\n"
" sudo apt install qt6-base-dev qt6-tools-dev\n"
)
else()
add_subdirectory(qt)
endif()

View File

@@ -1,4 +1,4 @@
# Cpp # cpp
```bash ```bash
shaunrd0/klips/cpp/ shaunrd0/klips/cpp/
@@ -7,7 +7,9 @@ shaunrd0/klips/cpp/
├── cryptography # Examples of encrypting / decrypting using ciphers in C++ ├── cryptography # Examples of encrypting / decrypting using ciphers in C++
├── datastructs # Collection of useful datastructures written in C++ ├── datastructs # Collection of useful datastructures written in C++
├── graphics # Examples of graphics projects written in C++ ├── graphics # Examples of graphics projects written in C++
├── multithreading # Basic multithreading examples in C++
├── patterns # Examples of various design patterns written in C++ ├── patterns # Examples of various design patterns written in C++
├── qt # Qt project examples using C++
└── README.md └── README.md
``` ```

View File

@@ -14,6 +14,7 @@ project (
DESCRIPTION "A project for practicing various algorithms in C++" DESCRIPTION "A project for practicing various algorithms in C++"
LANGUAGES CXX LANGUAGES CXX
) )
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)

View File

@@ -14,6 +14,7 @@ project (
DESCRIPTION "A project for practicing algorithms using graphs in C++" DESCRIPTION "A project for practicing algorithms using graphs in C++"
LANGUAGES CXX LANGUAGES CXX
) )
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)

View File

@@ -14,6 +14,7 @@ project(
DESCRIPTION "Practice implementing and using object graphs in C++" DESCRIPTION "Practice implementing and using object graphs in C++"
LANGUAGES CXX LANGUAGES CXX
) )
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable( add_executable(
algo-graphs-object graph.cpp algo-graphs-object graph.cpp

View File

@@ -1,8 +1,7 @@
/*############################################################################## /*##############################################################################
## Author: Shaun Reed ## ## Author: Shaun Reed ##
## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ## ## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: An example of an object graph implementation ## ## About: Driver program to test object graph implementation ##
## Algorithms in this example are found in MIT Intro to Algorithms ##
## ## ## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ## ## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################ ################################################################################

View File

@@ -1,7 +1,8 @@
/*############################################################################## /*##############################################################################
## Author: Shaun Reed ## ## Author: Shaun Reed ##
## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ## ## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Driver program to test object graph implementation ## ## About: An example of an object graph implementation ##
## Algorithms in this example are found in MIT Intro to Algorithms ##
## ## ## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ## ## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################ ################################################################################
@@ -183,4 +184,3 @@ std::vector<Node> Graph::TopologicalSort(const Node &startNode) const
// + Output is handled in main as FILO, similar to a stack // + Output is handled in main as FILO, similar to a stack
return order; return order;
} }

View File

@@ -1,6 +1,6 @@
/*############################################################################## /*##############################################################################
## Author: Shaun Reed ## ## Author: Shaun Reed ##
## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ## ## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: An example of an object graph implementation ## ## About: An example of an object graph implementation ##
## Algorithms in this example are found in MIT Intro to Algorithms ## ## Algorithms in this example are found in MIT Intro to Algorithms ##
## ## ## ##
@@ -10,51 +10,14 @@
#ifndef LIB_GRAPH_HPP #ifndef LIB_GRAPH_HPP
#define LIB_GRAPH_HPP #define LIB_GRAPH_HPP
#include <iostream>
#include <algorithm> #include <algorithm>
#include <iostream>
#include <map> #include <map>
#include <queue>
#include <unordered_map>
#include <unordered_set>
#include <utility> #include <utility>
#include <vector> #include <vector>
#include <queue>
#include <unordered_set>
#include <unordered_map>
/******************************************************************************/
// Structures for tracking information gathered from various traversals
struct Node;
// Color represents the discovery status of any given node
// + White is undiscovered, Gray is in progress, Black is fully discovered
enum Color {White, Gray, Black};
// Information used in all searches
struct SearchInfo {
// Coloring of the nodes is used in both DFS and BFS
Color discovered = White;
};
// Information that is only used in BFS
struct BFS : SearchInfo {
// Used to represent distance from start node
int distance = 0;
// Used to represent the parent node that discovered this node
// + If we use this node as the starting point, this will remain a nullptr
const Node *predecessor = nullptr;
};
// Information that is only used in DFS
struct DFS : SearchInfo {
// Create a pair to track discovery / finish time
// + Discovery time is the iteration the node is first discovered
// + Finish time is the iteration the node has been checked completely
// ++ A finished node has considered all adjacent nodes
std::pair<int, int> discoveryFinish;
};
// Store search information in unordered_maps so we can pass it around easily
// + Allows each node to store relative information on the traversal
using InfoBFS = std::unordered_map<int, struct BFS>;
using InfoDFS = std::unordered_map<int, struct DFS>;
/******************************************************************************/ /******************************************************************************/
@@ -63,14 +26,16 @@ struct Node {
public: public:
// Constructors // Constructors
Node(const Node &rhs) = default; Node(const Node &rhs) = default;
Node & operator=(Node rhs) { Node & operator=(Node rhs)
{
if (this == &rhs) return *this; if (this == &rhs) return *this;
swap(*this, rhs); swap(*this, rhs);
return *this; return *this;
} }
Node(int num, std::vector<int> adj) : number(num), adjacent(std::move(adj)) {} Node(int num, std::vector<int> adj) : number(num), adjacent(std::move(adj)) {}
friend void swap(Node &a, Node &b) { friend void swap(Node &a, Node &b)
{
std::swap(a.number, b.number); std::swap(a.number, b.number);
std::swap(a.adjacent, b.adjacent); std::swap(a.adjacent, b.adjacent);
} }
@@ -85,8 +50,61 @@ public:
}; };
/******************************************************************************/
// Base struct for storing traversal information on all nodes
// Color represents the discovery status of any given node
enum Color {
// Node is marked as undiscovered
White,
// Node discovery is in progress; Some adjacent nodes have not been checked
Gray,
// Node has been discovered; All adjacent nodes have been checked
Black
};
// Information used in all searches tracked for each node
struct NodeInfo {
// Coloring of the nodes is used in both DFS and BFS
Color discovered = White;
};
/******************************************************************************/
// BFS search information struct
// Node information that is only used in BFS
struct BFS : NodeInfo {
// Used to represent distance from start node
int distance = 0;
// Used to represent the parent node that discovered this node
// + If we use this node as the starting point, this will remain a nullptr
const Node *predecessor = nullptr;
};
// Store search information in unordered_maps so we can pass it around easily
// + Allows each node to store relative information on the traversal
using InfoBFS = std::unordered_map<int, struct BFS>;
/******************************************************************************/
// DFS search information struct
// Node information that is only used in DFS
struct DFS : NodeInfo {
// Create a pair to track discovery / finish time
// + Discovery time is the iteration the node is first discovered
// + Finish time is the iteration the node has been checked completely
// ++ A finished node has considered all adjacent nodes
std::pair<int, int> discoveryFinish;
};
using InfoDFS = std::unordered_map<int, struct DFS>;
/******************************************************************************/ /******************************************************************************/
// Graph class declaration // Graph class declaration
class Graph { class Graph {
public: public:
// Constructor // Constructor
@@ -101,7 +119,7 @@ public:
// An alternate DFS that checks each node of the graph beginning at startNode // An alternate DFS that checks each node of the graph beginning at startNode
InfoDFS DFS(const Node &startNode) const; InfoDFS DFS(const Node &startNode) const;
// Visit function is used in both versions of DFS // Visit function is used in both versions of DFS
void DFSVisit(int &time, const Node& startNode, InfoDFS &searchInfo) const; void DFSVisit(int &time, const Node& startNode, InfoDFS &dfs) const;
// Topological sort, using DFS // Topological sort, using DFS
std::vector<Node> TopologicalSort(const Node &startNode) const; std::vector<Node> TopologicalSort(const Node &startNode) const;

View File

@@ -14,6 +14,7 @@ project(
DESCRIPTION "Practice implementing and using simple graphs in C++" DESCRIPTION "Practice implementing and using simple graphs in C++"
LANGUAGES CXX LANGUAGES CXX
) )
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable( add_executable(
algo-graphs-simple graph.cpp algo-graphs-simple graph.cpp

View File

@@ -1,6 +1,6 @@
/*############################################################################## /*##############################################################################
## Author: Shaun Reed ## ## Author: Shaun Reed ##
## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ## ## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Driver program to test a simple graph implementation ## ## About: Driver program to test a simple graph implementation ##
## ## ## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ## ## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##

View File

@@ -1,6 +1,6 @@
/*############################################################################## /*##############################################################################
## Author: Shaun Reed ## ## Author: Shaun Reed ##
## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ## ## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: An example of a simple graph implementation ## ## About: An example of a simple graph implementation ##
## Algorithms in this example are found in MIT Intro to Algorithms ## ## Algorithms in this example are found in MIT Intro to Algorithms ##
## ## ## ##
@@ -8,9 +8,8 @@
################################################################################ ################################################################################
*/ */
#include <algorithm>
#include "lib-graph.hpp" #include "lib-graph.hpp"
#include <algorithm>
void Graph::BFS(int startNode) void Graph::BFS(int startNode)
{ {

View File

@@ -1,6 +1,6 @@
/*############################################################################# /*#############################################################################
## Author: Shaun Reed ## ## Author: Shaun Reed ##
## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ## ## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: An example of a simple graph implementation ## ## About: An example of a simple graph implementation ##
## Algorithms in this example are found in MIT Intro to Algorithms ## ## Algorithms in this example are found in MIT Intro to Algorithms ##
## ## ## ##
@@ -12,9 +12,10 @@
#include <iostream> #include <iostream>
#include <queue> #include <queue>
#include <vector>
#include <unordered_map> #include <unordered_map>
#include <unordered_set> #include <unordered_set>
#include <vector>
#include <cstdint>
class Graph { class Graph {

View File

@@ -14,5 +14,6 @@ project(
DESCRIPTION "Practice implementing and using templated graphs in C++" DESCRIPTION "Practice implementing and using templated graphs in C++"
LANGUAGES CXX LANGUAGES CXX
) )
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable(algo-graphs-templated graph.cpp) add_executable(algo-graphs-templated graph.cpp)

View File

@@ -1,7 +1,7 @@
/*############################################################################## /*##############################################################################
## Author: Shaun Reed ## ## Author: Shaun Reed ##
## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ## ## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: An example of a weighted graph implementation ## ## About: Driver program to test templated object graph implementation ##
## Algorithms in this example are found in MIT Intro to Algorithms ## ## Algorithms in this example are found in MIT Intro to Algorithms ##
## ## ## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ## ## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##

View File

@@ -1,7 +1,7 @@
/*############################################################################## /*##############################################################################
## Author: Shaun Reed ## ## Author: Shaun Reed ##
## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ## ## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: An example of an object graph implementation ## ## About: An example of a templated object graph implementation ##
## Algorithms in this example are found in MIT Intro to Algorithms ## ## Algorithms in this example are found in MIT Intro to Algorithms ##
## ## ## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ## ## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
@@ -10,38 +10,89 @@
#ifndef LIB_GRAPH_HPP #ifndef LIB_GRAPH_HPP
#define LIB_GRAPH_HPP #define LIB_GRAPH_HPP
#include <iostream>
#include <algorithm> #include <algorithm>
#include <iostream>
#include <map> #include <map>
#include <queue>
#include <unordered_map>
#include <unordered_set>
#include <utility> #include <utility>
#include <vector> #include <vector>
#include <queue> #include <cstdint>
#include <unordered_set>
#include <unordered_map>
/******************************************************************************/
// Node structure for representing a graph
template <typename T>
struct Node {
public:
template <typename> friend class Graph;
template <typename> friend class InfoMST;
// Constructors
Node(const Node &rhs) = default;
Node & operator=(Node rhs)
{
if (this == &rhs) return *this;
swap(*this, rhs);
return *this;
}
Node(T data, const std::vector<std::pair<T, int>> &adj) : data_(data)
{
// Place each adjacent node in vector into our unordered_map of edges
for (const auto &i : adj) adjacent_.emplace(i.first, i.second);
}
friend void swap(Node &a, Node &b)
{
std::swap(a.data_, b.data_);
std::swap(a.adjacent_, b.adjacent_);
}
// Operators
// Define operator== for std::find; And comparisons between nodes
bool operator==(const Node<T> &b) const { return this->data_ == b.data_;}
// Define an operator!= for comparing nodes for inequality
bool operator!=(const Node<T> &b) const { return this->data_ != b.data_;}
// Accessors
inline T GetData() const { return data_;}
inline std::unordered_map<int, int> GetAdjacent() const { return adjacent_;}
private:
T data_;
// Adjacent stored in an unordered_map<adj.number, edgeWeight>
std::unordered_map<T, int> adjacent_;
};
/******************************************************************************/ /******************************************************************************/
// Base struct for storing traversal information on all nodes // Base struct for storing traversal information on all nodes
template <typename T> struct Node;
// Color represents the discovery status of any given node // Color represents the discovery status of any given node
// + White is undiscovered, Gray is in progress, Black is fully discovered enum Color {
enum Color {White, Gray, Black}; // Node is marked as undiscovered
White,
// Node discovery is in progress; Some adjacent nodes have not been checked
Gray,
// Node has been discovered; All adjacent nodes have been checked
Black
};
// Information used in all searches // Information used in all searches tracked for each node
struct SearchInfo { struct NodeInfo {
// Coloring of the nodes is used in both DFS and BFS // Coloring of the nodes is used in both DFS and BFS
Color discovered = White; Color discovered = White;
}; };
/******************************************************************************/ /******************************************************************************/
// BFS search information structs // BFS search information struct
// Information that is only used in BFS // Node information that is only used in BFS
template <typename T> template <typename T>
struct BFS : SearchInfo { struct BFS : NodeInfo {
// Used to represent distance from start node // Used to represent distance from start node
int distance = 0; int distance = 0;
// Used to represent the parent node that discovered this node // Used to represent the parent node that discovered this node
@@ -49,12 +100,16 @@ struct BFS : SearchInfo {
const Node<T> *predecessor = nullptr; const Node<T> *predecessor = nullptr;
}; };
// Store search information in unordered_maps so we can pass it around easily
// + Allows each node to store relative information on the traversal
template <typename T> using InfoBFS = std::unordered_map<T, struct BFS<T>>;
/******************************************************************************/ /******************************************************************************/
// DFS search information structs // DFS search information struct
// Information that is only used in DFS // Node information that is only used in DFS
struct DFS : SearchInfo { struct DFS : NodeInfo {
// Create a pair to track discovery / finish time // Create a pair to track discovery / finish time
// + Discovery time is the iteration the node is first discovered // + Discovery time is the iteration the node is first discovered
// + Finish time is the iteration the node has been checked completely // + Finish time is the iteration the node has been checked completely
@@ -62,22 +117,16 @@ struct DFS : SearchInfo {
std::pair<int, int> discoveryFinish; std::pair<int, int> discoveryFinish;
}; };
template <typename T> using InfoDFS = std::unordered_map<T, struct DFS>;
/******************************************************************************/ /******************************************************************************/
// Alias types for storing search information structures // MST search information struct
// Store search information in unordered_maps so we can pass it around easily
// + Allows each node to store relative information on the traversal
template <typename T> using InfoBFS = std::unordered_map<T, struct BFS<T>>;
template <typename T> using InfoDFS = std::unordered_map<T, struct DFS>;
// Edges stored as multimap<weight, pair<nodeA.data_, nodeB.data_>> // Edges stored as multimap<weight, pair<nodeA.data_, nodeB.data_>>
template <typename T> using Edges = std::multimap<int, std::pair<T, T>>; template <typename T> using Edges = std::multimap<int, std::pair<T, T>>;
struct MST : NodeInfo {
/******************************************************************************/
// MST search information structs
struct MST : SearchInfo {
int32_t parent = INT32_MIN; int32_t parent = INT32_MIN;
int rank = 0; int rank = 0;
}; };
@@ -86,7 +135,8 @@ template <typename T>
struct InfoMST { struct InfoMST {
template <typename> friend class Graph; template <typename> friend class Graph;
explicit InfoMST(const std::vector<Node<T>> &nodes) { explicit InfoMST(const std::vector<Node<T>> &nodes)
{
for (const auto &node : nodes) { for (const auto &node : nodes) {
// Initialize the default values for forest tracked by this struct // Initialize the default values for forest tracked by this struct
// + This data is used in KruskalMST() to find the MST // + This data is used in KruskalMST() to find the MST
@@ -157,51 +207,6 @@ private:
}; };
/******************************************************************************/
// Node structure for representing a graph
template <typename T>
struct Node {
public:
template <typename> friend class Graph;
template <typename> friend class InfoMST;
// Constructors
Node(const Node &rhs) = default;
Node & operator=(Node rhs) {
if (this == &rhs) return *this;
swap(*this, rhs);
return *this;
}
Node(T data, const std::vector<std::pair<T, int>> &adj)
: data_(data)
{
// Place each adjacent node in vector into our unordered_map of edges
for (const auto &i : adj) adjacent_.emplace(i.first, i.second);
}
friend void swap(Node &a, Node &b) {
std::swap(a.data_, b.data_);
std::swap(a.adjacent_, b.adjacent_);
}
// Operators
// Define operator== for std::find; And comparisons between nodes
bool operator==(const Node<T> &b) const { return this->data_ == b.data_;}
// Define an operator!= for comparing nodes for inequality
bool operator!=(const Node<T> &b) const { return this->data_ != b.data_;}
// Accessors
inline T GetData() const { return data_;}
inline std::unordered_map<int, int> GetAdjacent() const { return adjacent_;}
private:
T data_;
// Adjacent stored in an unordered_map<adj.number, edgeWeight>
std::unordered_map<T, int> adjacent_;
};
/******************************************************************************/ /******************************************************************************/
// Templated graph class // Templated graph class
@@ -209,7 +214,7 @@ template <class T>
class Graph { class Graph {
public: public:
// Constructor // Constructor
Graph(std::vector<Node<T>> nodes) : nodes_(std::move(nodes)) {} explicit Graph(std::vector<Node<T>> nodes) : nodes_(std::move(nodes)) {}
// Breadth First Search // Breadth First Search
InfoBFS<T> BFS(const Node<T>& startNode) const; InfoBFS<T> BFS(const Node<T>& startNode) const;

View File

@@ -14,6 +14,7 @@ project(
DESCRIPTION "Practice implementing and using weighted graphs in C++" DESCRIPTION "Practice implementing and using weighted graphs in C++"
LANGUAGES CXX LANGUAGES CXX
) )
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable( add_executable(
algo-graphs-weighted graph.cpp algo-graphs-weighted graph.cpp

View File

@@ -1,8 +1,7 @@
/*############################################################################## /*##############################################################################
## Author: Shaun Reed ## ## Author: Shaun Reed ##
## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ## ## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: An example of a weighted graph implementation ## ## About: Driver program to test weighted graph implementation ##
## Algorithms in this example are found in MIT Intro to Algorithms ##
## ## ## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ## ## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################ ################################################################################
@@ -157,11 +156,19 @@ int main (const int argc, const char * argv[])
{9, {{3, 2}, {7, 6}}} {9, {{3, 2}, {7, 6}}}
} }
); );
std::cout << "\nChecking weight traversing graph from node 1 using DFS...\n";
InfoDFS resultDFS = graphMST.DFS(graphMST.GetNodeCopy(1));
std::cout << "DFS total weight traversed: " << resultDFS.totalWeight << std::endl;
std::cout << "\nChecking weight traversing graph from node 1 using BFS...\n";
InfoBFS resultBFS = graphMST.BFS(graphMST.GetNodeCopy(1));
std::cout << "BFS total weight traversed: " << resultBFS.totalWeight << std::endl;
InfoMST resultMST = graphMST.KruskalMST(); InfoMST resultMST = graphMST.KruskalMST();
std::cout << "Finding MST using Kruskal's...\n\nMST result: \n"; std::cout << "\n\nFinding MST using Kruskal's...\n\nMST result: \n";
for (const auto &edge : resultMST.edgesMST) { for (const auto &edge : resultMST.edgesMST) {
std::cout << "Connected nodes: " << edge.second.first << "->" std::cout << "Connected nodes: " << edge.second.first << "->"
<< edge.second.second << " with weight of " << edge.first << "\n"; << edge.second.second << " with weight of " << edge.first << "\n";
} }
std::cout << "Total MST weight: " << resultMST.weightMST << std::endl; std::cout << "Total MST weight: " << resultMST.totalWeight << std::endl;
} }

View File

@@ -1,7 +1,8 @@
/*############################################################################## /*##############################################################################
## Author: Shaun Reed ## ## Author: Shaun Reed ##
## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ## ## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Driver program to test object graph implementation ## ## About: An example of a weighted graph implementation ##
## Algorithms in this example are found in MIT Intro to Algorithms ##
## ## ## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ## ## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################ ################################################################################
@@ -13,13 +14,13 @@
InfoBFS Graph::BFS(const Node& startNode) const InfoBFS Graph::BFS(const Node& startNode) const
{ {
// Create local object to track the information gathered during traversal // Create local object to track the information gathered during traversal
InfoBFS searchInfo; InfoBFS bfs;
// Create a queue to visit discovered nodes in FIFO order // Create a queue to visit discovered nodes in FIFO order
std::queue<const Node *> visitQueue; std::queue<const Node *> visitQueue;
// Mark the startNode as in progress until we finish checking adjacent nodes // Mark the startNode as in progress until we finish checking adjacent nodes
searchInfo[startNode.number].discovered = Gray; bfs.nodeInfo[startNode.number].discovered = Gray;
// Visit the startNode // Visit the startNode
visitQueue.push(&startNode); visitQueue.push(&startNode);
@@ -30,17 +31,17 @@ InfoBFS Graph::BFS(const Node& startNode) const
const Node * thisNode = visitQueue.front(); const Node * thisNode = visitQueue.front();
visitQueue.pop(); visitQueue.pop();
std::cout << "Visiting node " << thisNode->number << std::endl; std::cout << "Visiting node " << thisNode->number << std::endl;
// Check if we have already discovered all the adjacentNodes to thisNode // Check if we have already discovered all the adjacentNodes to thisNode
for (const auto &adjacent : thisNode->adjacent) { for (const auto &adjacent : thisNode->adjacent) {
if (searchInfo[adjacent.first].discovered == White) { if (bfs.nodeInfo[adjacent.first].discovered == White) {
std::cout << "Found undiscovered adjacentNode: " << adjacent.first std::cout << "Found undiscovered adjacentNode: " << adjacent.first
<< "\n"; << " with weight of " << adjacent.second << std::endl;
bfs.totalWeight += adjacent.second;
// Mark the adjacent node as in progress // Mark the adjacent node as in progress
searchInfo[adjacent.first].discovered = Gray; bfs.nodeInfo[adjacent.first].discovered = Gray;
searchInfo[adjacent.first].distance = bfs.nodeInfo[adjacent.first].distance =
searchInfo[thisNode->number].distance + 1; bfs.nodeInfo[thisNode->number].distance + 1;
searchInfo[adjacent.first].predecessor = bfs.nodeInfo[adjacent.first].predecessor =
&GetNode(thisNode->number); &GetNode(thisNode->number);
// Add the discovered node the the visitQueue // Add the discovered node the the visitQueue
@@ -48,11 +49,11 @@ InfoBFS Graph::BFS(const Node& startNode) const
} }
} }
// We are finished with this node and the adjacent nodes; Mark it discovered // We are finished with this node and the adjacent nodes; Mark it discovered
searchInfo[thisNode->number].discovered = Black; bfs.nodeInfo[thisNode->number].discovered = Black;
} }
// Return the information gathered from this search, JIC caller needs it // Return the information gathered from this search, JIC caller needs it
return searchInfo; return bfs;
} }
std::deque<Node> Graph::PathBFS(const Node &start, const Node &finish) const std::deque<Node> Graph::PathBFS(const Node &start, const Node &finish) const
@@ -61,8 +62,8 @@ std::deque<Node> Graph::PathBFS(const Node &start, const Node &finish) const
// + If the caller modifies these, it will not impact the graph's data // + If the caller modifies these, it will not impact the graph's data
std::deque<Node> path; std::deque<Node> path;
InfoBFS searchInfo = BFS(start); InfoBFS bfs = BFS(start);
const Node * next = searchInfo[finish.number].predecessor; const Node * next = bfs.nodeInfo[finish.number].predecessor;
bool isValid = false; bool isValid = false;
do { do {
// If we have reached the start node, we have found a valid path // If we have reached the start node, we have found a valid path
@@ -73,7 +74,7 @@ std::deque<Node> Graph::PathBFS(const Node &start, const Node &finish) const
path.emplace_front(*next); path.emplace_front(*next);
// Move to the next node // Move to the next node
next = searchInfo[next->number].predecessor; next = bfs.nodeInfo[next->number].predecessor;
} while (next != nullptr); } while (next != nullptr);
// Use emplace_back to call Node copy constructor // Use emplace_back to call Node copy constructor
path.emplace_back(finish); path.emplace_back(finish);
@@ -88,85 +89,83 @@ std::deque<Node> Graph::PathBFS(const Node &start, const Node &finish) const
InfoDFS Graph::DFS() const InfoDFS Graph::DFS() const
{ {
// Track the nodes we have discovered // Track the nodes we have discovered
InfoDFS searchInfo; InfoDFS dfs;
int time = 0; int time = 0;
// Visit each node in the graph // Visit each node in the graph
for (const auto & node : nodes_) { for (const auto & node : nodes_) {
std::cout << "Visiting node " << node.number << std::endl; std::cout << "Visiting node " << node.number << std::endl;
// If the node is undiscovered, visit it // If the node is undiscovered, visit it
if (searchInfo[node.number].discovered == White) { if (dfs.nodeInfo[node.number].discovered == White) {
std::cout << "Found undiscovered node: " << node.number << std::endl; std::cout << "Found undiscovered node: " << node.number << std::endl;
// Visiting the undiscovered node will check it's adjacent nodes // Visiting the undiscovered node will check it's adjacent nodes
DFSVisit(time, node, searchInfo); DFSVisit(time, node, dfs);
} }
} }
return searchInfo; return dfs;
} }
InfoDFS Graph::DFS(const Node &startNode) const InfoDFS Graph::DFS(const Node &startNode) const
{ {
// Track the nodes we have discovered // Track the nodes we have discovered
InfoDFS searchInfo; InfoDFS dfs;
int time = 0; int time = 0;
auto startIter = std::find(nodes_.begin(), nodes_.end(), auto startIter =
Node(startNode.number, {}) std::find(nodes_.begin(), nodes_.end(), Node(startNode.number, { }));
);
// beginning at startNode, visit each node in the graph until we reach the end // beginning at startNode, visit each node in the graph until we reach the end
while (startIter != nodes_.end()) { while (startIter != nodes_.end()) {
std::cout << "Visiting node " << startIter->number << std::endl; std::cout << "Visiting node " << startIter->number << std::endl;
// If the startIter is undiscovered, visit it // If the startIter is undiscovered, visit it
if (searchInfo[startIter->number].discovered == White) { if (dfs.nodeInfo[startIter->number].discovered == White) {
std::cout << "Found undiscovered node: " << startIter->number << std::endl; std::cout << "Found undiscovered node: " << startIter->number
<< std::endl;
// Visiting the undiscovered node will check it's adjacent nodes // Visiting the undiscovered node will check it's adjacent nodes
DFSVisit(time, *startIter, searchInfo); DFSVisit(time, *startIter, dfs);
} }
startIter++; startIter++;
} }
// Once we reach the last node, check the beginning for unchecked nodes // Once we reach the last node, check the beginning for unchecked nodes
startIter = nodes_.begin(); startIter = nodes_.begin();
// Once we reach the initial startNode, we have checked all nodes // Once we reach the initial startNode, we have checked all nodes
while (*startIter != startNode) { while (*startIter != startNode) {
std::cout << "Visiting node " << startIter->number << std::endl; std::cout << "Visiting node " << startIter->number << std::endl;
// If the startIter is undiscovered, visit it // If the startIter is undiscovered, visit it
if (searchInfo[startIter->number].discovered == White) { if (dfs.nodeInfo[startIter->number].discovered == White) {
std::cout << "Found undiscovered node: " << startIter->number << std::endl; std::cout << "Found undiscovered node: " << startIter->number << std::endl;
// Visiting the undiscovered node will check it's adjacent nodes // Visiting the undiscovered node will check it's adjacent nodes
DFSVisit(time, *startIter, searchInfo); DFSVisit(time, *startIter, dfs);
} }
startIter++; startIter++;
} }
return searchInfo; return dfs;
} }
void Graph::DFSVisit(int &time, const Node& startNode, InfoDFS &searchInfo) const void Graph::DFSVisit(int &time, const Node& startNode, InfoDFS &dfs) const
{ {
searchInfo[startNode.number].discovered = Gray; dfs.nodeInfo[startNode.number].discovered = Gray;
time++; time++;
searchInfo[startNode.number].discoveryFinish.first = time; dfs.nodeInfo[startNode.number].discoveryFinish.first = time;
// Check the adjacent nodes of the startNode // Check the adjacent nodes of the startNode
for (const auto & adjacent : startNode.adjacent) { for (const auto & adjacent : startNode.adjacent) {
auto iter = std::find(nodes_.begin(), nodes_.end(), const auto node = GetNode(adjacent.first);
Node(adjacent.first, {}));
// If the adjacentNode is undiscovered, visit it // If the adjacentNode is undiscovered, visit it
// + Offset by 1 to account for 0 index of discovered vector // + Offset by 1 to account for 0 index of discovered vector
if (searchInfo[iter->number].discovered == White) { if (dfs.nodeInfo[node.number].discovered == White) {
std::cout << "Found undiscovered adjacentNode: " std::cout << "Found undiscovered adjacentNode: " << adjacent.first
<< GetNode(adjacent.first).number << std::endl; << " with weight of " << adjacent.second << std::endl;
// Visiting the undiscovered node will check it's adjacent nodes // Visiting the undiscovered node will check it's adjacent nodes
DFSVisit(time, *iter, searchInfo); dfs.totalWeight += adjacent.second;
DFSVisit(time, node, dfs);
} }
} }
searchInfo[startNode.number].discovered = Black; dfs.nodeInfo[startNode.number].discovered = Black;
time++; time++;
searchInfo[startNode.number].discoveryFinish.second = time; dfs.nodeInfo[startNode.number].discoveryFinish.second = time;
} }
std::vector<Node> Graph::TopologicalSort(const Node &startNode) const std::vector<Node> Graph::TopologicalSort(const Node &startNode) const
@@ -176,8 +175,8 @@ std::vector<Node> Graph::TopologicalSort(const Node &startNode) const
std::vector<Node> order(nodes_); std::vector<Node> order(nodes_);
auto comp = [&topological](const Node &a, const Node &b) { auto comp = [&topological](const Node &a, const Node &b) {
return (topological[a.number].discoveryFinish.second < return (topological.nodeInfo[a.number].discoveryFinish.second <
topological[b.number].discoveryFinish.second); topological.nodeInfo[b.number].discoveryFinish.second);
}; };
std::sort(order.begin(), order.end(), comp); std::sort(order.begin(), order.end(), comp);
@@ -189,27 +188,26 @@ std::vector<Node> Graph::TopologicalSort(const Node &startNode) const
InfoMST Graph::KruskalMST() const InfoMST Graph::KruskalMST() const
{ {
InfoMST searchInfo(nodes_); InfoMST mst(nodes_);
// The ctor for InfoMST initializes all edges within the graph into a multimap // The ctor for InfoMST initializes all edges within the graph into a multimap
// + Key for multimap is edge weight, so they're already sorted in ascending // + Key for multimap is edge weight, so they're already sorted in ascending
// For each edge in the graph, check if they are part of the same tree // For each edge in the graph, check if they are part of the same tree
// + Since we do not want to create a cycle in the MST forest - // + Since we do not want to create a cycle in the MST forest -
// + we don't connect nodes that are part of the same tree // + we don't connect nodes that are part of the same tree
for (const auto &edge : searchInfo.edges) { for (const auto &edge : mst.edges) {
// Two integers representing the node.number for the connected nodes // Two integers representing the node.number for the connected nodes
const int u = edge.second.first; const int u = edge.second.first;
const int v = edge.second.second; const int v = edge.second.second;
// Check if the nodes are of the same tree // Check if the nodes are of the same tree
if (searchInfo.FindSet(u) != searchInfo.FindSet(v)) { if (mst.FindSet(u) != mst.FindSet(v)) {
// If they are not, add the edge to our MST // If they are not, add the edge to our MST
searchInfo.edgesMST.emplace(edge); mst.edgesMST.emplace(edge);
searchInfo.weightMST += edge.first; mst.totalWeight += edge.first;
// Update the forest to reflect this change // Update the forest to reflect this change
searchInfo.Union(u, v); mst.Union(u, v);
} }
} }
return searchInfo; return mst;
} }

View File

@@ -1,7 +1,7 @@
/*############################################################################## /*##############################################################################
## Author: Shaun Reed ## ## Author: Shaun Reed ##
## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ## ## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: An example of an object graph implementation ## ## About: An example of a weighted graph implementation ##
## Algorithms in this example are found in MIT Intro to Algorithms ## ## Algorithms in this example are found in MIT Intro to Algorithms ##
## ## ## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ## ## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
@@ -10,56 +10,15 @@
#ifndef LIB_GRAPH_HPP #ifndef LIB_GRAPH_HPP
#define LIB_GRAPH_HPP #define LIB_GRAPH_HPP
#include <iostream>
#include <algorithm> #include <algorithm>
#include <iostream>
#include <map> #include <map>
#include <queue>
#include <unordered_map>
#include <unordered_set>
#include <utility> #include <utility>
#include <vector> #include <vector>
#include <queue> #include <cstdint>
#include <unordered_set>
#include <unordered_map>
/******************************************************************************/
// Structures for tracking information gathered from various traversals
struct Node;
// Color represents the discovery status of any given node
// + White is undiscovered, Gray is in progress, Black is fully discovered
enum Color {White, Gray, Black};
// Information used in all searches
struct SearchInfo {
// Coloring of the nodes is used in both DFS and BFS
Color discovered = White;
};
// Information that is only used in BFS
struct BFS : SearchInfo {
// Used to represent distance from start node
int distance = 0;
// Used to represent the parent node that discovered this node
// + If we use this node as the starting point, this will remain a nullptr
const Node *predecessor = nullptr;
};
// Information that is only used in DFS
struct DFS : SearchInfo {
// Create a pair to track discovery / finish time
// + Discovery time is the iteration the node is first discovered
// + Finish time is the iteration the node has been checked completely
// ++ A finished node has considered all adjacent nodes
std::pair<int, int> discoveryFinish;
};
struct MST : SearchInfo {
int32_t parent = INT32_MIN;
int rank = 0;
};
// Store search information in unordered_maps so we can pass it around easily
// + Allows each node to store relative information on the traversal
using InfoBFS = std::unordered_map<int, struct BFS>;
using InfoDFS = std::unordered_map<int, struct DFS>;
/******************************************************************************/ /******************************************************************************/
@@ -69,7 +28,8 @@ struct Node {
public: public:
// Constructors // Constructors
Node(const Node &rhs) = default; Node(const Node &rhs) = default;
Node & operator=(Node rhs) { Node & operator=(Node rhs)
{
if (this == &rhs) return *this; if (this == &rhs) return *this;
swap(*this, rhs); swap(*this, rhs);
return *this; return *this;
@@ -80,7 +40,8 @@ public:
for (const auto &i : adj) adjacent.emplace(i.first, i.second); for (const auto &i : adj) adjacent.emplace(i.first, i.second);
} }
friend void swap(Node &a, Node &b) { friend void swap(Node &a, Node &b)
{
std::swap(a.number, b.number); std::swap(a.number, b.number);
std::swap(a.adjacent, b.adjacent); std::swap(a.adjacent, b.adjacent);
} }
@@ -95,9 +56,80 @@ public:
bool operator!=(const Node &b) const { return this->number != b.number;} bool operator!=(const Node &b) const { return this->number != b.number;}
}; };
/******************************************************************************/
// Base struct for storing traversal information on all nodes
// Color represents the discovery status of any given node
enum Color {
// Node is marked as undiscovered
White,
// Node discovery is in progress; Some adjacent nodes have not been checked
Gray,
// Node has been discovered; All adjacent nodes have been checked
Black
};
// Information used in all searches tracked for each node
struct NodeInfo {
// Coloring of the nodes is used in both DFS and BFS
Color discovered = White;
};
// Template for tracking graph information gathered during traversals
// + Used for DFS, BFS, and MST
template <typename T>
struct GraphInfo {
// Store search information in unordered_maps so we can pass it around easily
// + Allows each node to store relative information on the traversal
std::unordered_map<int, T> nodeInfo;
// Track total weight for all traversals
int totalWeight = 0;
};
/******************************************************************************/
// BFS search information struct
// Node search information that is only used in BFS
struct BFS : NodeInfo {
// Used to represent distance from start node
int distance = 0;
// Used to represent the parent node that discovered this node
// + If we use this node as the starting point, this will remain a nullptr
const Node *predecessor = nullptr;
};
struct InfoBFS : GraphInfo<BFS> {/* Members inherited from GraphInfo */};
/******************************************************************************/
// DFS search information struct
// Information that is only used in DFS
struct DFS : NodeInfo {
// Create a pair to track discovery / finish time
// + Discovery time is the iteration the node is first discovered
// + Finish time is the iteration the node has been checked completely
// ++ A finished node has considered all adjacent nodes
std::pair<int, int> discoveryFinish;
};
struct InfoDFS : GraphInfo<DFS> {/* Members inherited from GraphInfo */};
/******************************************************************************/
// MST search information struct
struct MST : NodeInfo {
int32_t parent = INT32_MIN;
int rank = 0;
};
using Edges = std::multimap<int, std::pair<int, int>>; using Edges = std::multimap<int, std::pair<int, int>>;
struct InfoMST { struct InfoMST : GraphInfo<MST>{
explicit InfoMST(const std::vector<Node> &nodes) { explicit InfoMST(const std::vector<Node> &nodes)
{
for (const auto &node : nodes) { for (const auto &node : nodes) {
// Initialize the default values for forest tracked by this struct // Initialize the default values for forest tracked by this struct
// + This data is used in KruskalMST() to find the MST // + This data is used in KruskalMST() to find the MST
@@ -113,20 +145,17 @@ struct InfoMST {
} }
} }
std::unordered_map<int, struct MST> searchInfo;
// All of the edges within our graph // All of the edges within our graph
// + Since each node stores its own edges, this is initialized in InfoMST ctor // + Since each node stores its own edges, this is initialized in InfoMST ctor
Edges edges; Edges edges;
// A multimap of the edges found for our MST // A multimap of the edges found for our MST
Edges edgesMST; Edges edgesMST;
// The total weight of our resulting MST
int weightMST = 0;
void MakeSet(int x) void MakeSet(int x)
{ {
searchInfo[x].parent = x; nodeInfo[x].parent = x;
searchInfo[x].rank = 0; nodeInfo[x].rank = 0;
} }
void Union(int x, int y) void Union(int x, int y)
@@ -136,29 +165,30 @@ struct InfoMST {
void Link(int x, int y) void Link(int x, int y)
{ {
if (searchInfo[x].rank > searchInfo[y].rank) { if (nodeInfo[x].rank > nodeInfo[y].rank) {
searchInfo[y].parent = x; nodeInfo[y].parent = x;
} }
else { else {
searchInfo[x].parent = y; nodeInfo[x].parent = y;
if (searchInfo[x].rank == searchInfo[y].rank) { if (nodeInfo[x].rank == nodeInfo[y].rank) {
searchInfo[y].rank += 1; nodeInfo[y].rank += 1;
} }
} }
} }
int FindSet(int x) int FindSet(int x)
{ {
if (x != searchInfo[x].parent) { if (x != nodeInfo[x].parent) {
searchInfo[x].parent = FindSet(searchInfo[x].parent); nodeInfo[x].parent = FindSet(nodeInfo[x].parent);
} }
return searchInfo[x].parent; return nodeInfo[x].parent;
} }
}; };
/******************************************************************************/ /******************************************************************************/
// Graph class declaration // Graph class declaration
class Graph { class Graph {
public: public:
// Constructor // Constructor
@@ -173,7 +203,7 @@ public:
// An alternate DFS that checks each node of the graph beginning at startNode // An alternate DFS that checks each node of the graph beginning at startNode
InfoDFS DFS(const Node &startNode) const; InfoDFS DFS(const Node &startNode) const;
// Visit function is used in both versions of DFS // Visit function is used in both versions of DFS
void DFSVisit(int &time, const Node& startNode, InfoDFS &searchInfo) const; void DFSVisit(int &time, const Node& startNode, InfoDFS &dfs) const;
// Topological sort, using DFS // Topological sort, using DFS
std::vector<Node> TopologicalSort(const Node &startNode) const; std::vector<Node> TopologicalSort(const Node &startNode) const;
// Kruskal's MST // Kruskal's MST

View File

@@ -14,6 +14,7 @@ project (
DESCRIPTION "A project for practicing various sorting algorithms in C++" DESCRIPTION "A project for practicing various sorting algorithms in C++"
LANGUAGES CXX LANGUAGES CXX
) )
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)

View File

@@ -14,6 +14,7 @@ project (
DESCRIPTION "A project for practicing insertion sort in C++" DESCRIPTION "A project for practicing insertion sort in C++"
LANGUAGES CXX LANGUAGES CXX
) )
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable( add_executable(
algo-sort-insertion insertion-sort.cpp algo-sort-insertion insertion-sort.cpp

View File

@@ -14,6 +14,8 @@ project (
DESCRIPTION "A project for practicing merge sort in C++" DESCRIPTION "A project for practicing merge sort in C++"
LANGUAGES CXX LANGUAGES CXX
) )
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable( add_executable(
algo-sort-merge merge-sort.cpp algo-sort-merge merge-sort.cpp
lib-merge.cpp lib-merge.h lib-merge.cpp lib-merge.h

View File

@@ -12,6 +12,7 @@
#include <algorithm> #include <algorithm>
#include <iostream> #include <iostream>
#include <vector> #include <vector>
#include <cstdint>
void MergeSort(std::vector<int> &array, size_t lhs, size_t rhs) void MergeSort(std::vector<int> &array, size_t lhs, size_t rhs)
{ {

View File

@@ -14,6 +14,7 @@ project (
DESCRIPTION "A project for practicing quick sort in C++" DESCRIPTION "A project for practicing quick sort in C++"
LANGUAGES CXX LANGUAGES CXX
) )
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable( add_executable(
algo-sort-quick quick-sort.cpp algo-sort-quick quick-sort.cpp

View File

@@ -14,6 +14,7 @@ project (
DESCRIPTION "A project for practicing radix sort in C++" DESCRIPTION "A project for practicing radix sort in C++"
LANGUAGES CXX LANGUAGES CXX
) )
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable( add_executable(
algo-sort-radix radix-sort.cpp algo-sort-radix radix-sort.cpp

View File

@@ -14,6 +14,7 @@ project (
DESCRIPTION "A project for practicing selection sort in C++" DESCRIPTION "A project for practicing selection sort in C++"
LANGUAGES CXX LANGUAGES CXX
) )
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable( add_executable(
algo-sort-select select-sort.cpp algo-sort-select select-sort.cpp

View File

@@ -14,6 +14,7 @@ project (
DESCRIPTION "A project for practicing algorithms using trees in C++" DESCRIPTION "A project for practicing algorithms using trees in C++"
LANGUAGES CXX LANGUAGES CXX
) )
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)

View File

@@ -14,6 +14,7 @@ project (
DESCRIPTION "A project for testing BST algorithms" DESCRIPTION "A project for testing BST algorithms"
LANGUAGES CXX LANGUAGES CXX
) )
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable( add_executable(
algo-trees-bst driver.cpp algo-trees-bst driver.cpp

View File

@@ -14,6 +14,7 @@ project (
DESCRIPTION "A project for testing red-black tree algorithms" DESCRIPTION "A project for testing red-black tree algorithms"
LANGUAGES CXX LANGUAGES CXX
) )
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable( add_executable(
algo-trees-redblack driver.cpp algo-trees-redblack driver.cpp

View File

@@ -12,6 +12,7 @@
#define REDBLACK_H #define REDBLACK_H
#include <iostream> #include <iostream>
#include <cstdint>
enum Color {Black, Red}; enum Color {Black, Red};

35
cpp/catch2/CMakeLists.txt Normal file
View File

@@ -0,0 +1,35 @@
################################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Practice project for testing with catch2 framework ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
cmake_minimum_required(VERSION 3.15)
project(
#[[NAME]] Catch2
VERSION 1.0
DESCRIPTION "Practice project for learning Catch2"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_compile_options(-Wall)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
Include(FetchContent)
FetchContent_Declare(
Catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
GIT_TAG v3.4.0
)
FetchContent_MakeAvailable(Catch2)
add_library(klips SHARED src/klips.cpp)
target_include_directories(klips PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
add_executable(test_klips src/test_klips.cpp)
target_link_libraries(test_klips PUBLIC Catch2::Catch2WithMain klips)

View File

@@ -0,0 +1,10 @@
#ifndef KLIPS_KLIPS_H
#define KLIPS_KLIPS_H
class klips { };
unsigned int factorial(unsigned int);
#endif // KLIPS_KLIPS_H

View File

@@ -0,0 +1,29 @@
// Authored by 康桓瑋 on SO: https://stackoverflow.com/a/56766138
#ifndef CATCH2_TYPE_NAME_HPP
#include <string_view>
template <typename T>
constexpr auto type_name() {
std::string_view name, prefix, suffix;
#ifdef __clang__
name = __PRETTY_FUNCTION__;
prefix = "auto type_name() [T = ";
suffix = "]";
#elif defined(__GNUC__)
name = __PRETTY_FUNCTION__;
prefix = "constexpr auto type_name() [with T = ";
suffix = "]";
#elif defined(_MSC_VER)
name = __FUNCSIG__;
prefix = "auto __cdecl type_name<";
suffix = ">(void)";
#endif
name.remove_prefix(prefix.size());
name.remove_suffix(suffix.size());
return name;
}
#define CATCH2_TYPE_NAME_HPP
#endif // CATCH2_TYPE_NAME_HPP

4
cpp/catch2/src/klips.cpp Normal file
View File

@@ -0,0 +1,4 @@
unsigned int factorial( unsigned int number ) {
return number <= 1 ? number : factorial(number-1)*number;
}

View File

@@ -0,0 +1,148 @@
#define CATCH_CONFIG_MAIN
#include <iostream>
#include "catch2/catch_all.hpp"
#include "klips.hpp"
#include "type_name.hpp"
#define TT() std::cout << "T = " << type_name<TestType>() << std::endl;
#define TD(x) \
std::cout << type_name<decltype(x)>() << " " << #x << " = " << x << std::endl;
#define T(x) std::cout << "T = " << type_name<x>() << std::endl;
TEST_CASE("factorials are computed", "[factorial]") {
REQUIRE(factorial(1) == 1);
REQUIRE(factorial(2) == 2);
REQUIRE(factorial(3) == 6);
REQUIRE(factorial(10) == 3628800);
}
TEST_CASE("Generators") {
auto i = GENERATE(1, 3, 5);
TD(i);
}
TEST_CASE("Generators 2") {
auto i = GENERATE(1, 2);
SECTION("one") {
auto j = GENERATE(-3, -2);
REQUIRE(j < i);
std::cout << "i = " << i << "; j = " << j << std::endl;
}
SECTION("two") {
auto k = GENERATE(4, 5, 6);
REQUIRE(i != k);
std::cout << "i = " << i << "; k = " << k << std::endl;
}
}
TEST_CASE("Complex mix of sections and generates") {
auto i = GENERATE(1, 2);
SECTION("A") {
std::cout << "i = " << i << "; A passed" << std::endl;
SUCCEED("A");
}
std::cout << "left A\n";
auto j = GENERATE(3, 4);
std::cout << "i = " << i << "; j = " << j << std::endl;
SECTION("B") {
std::cout << "i = " << i << "; j = " << j << "; B passed;" << std::endl;
SUCCEED("B");
}
auto k = GENERATE(5, 6);
std::cout << "i = " << i << "; k = " << k << std::endl;
SUCCEED();
}
TEST_CASE("Test generaators 3", "[test]") { GENERATE(values({1, 2})); }
TEMPLATE_TEST_CASE("Testing template tests", "[test][template]", int8_t,
int16_t, int32_t, int64_t) {
TT();
}
template <typename T> struct Foo {
size_t size() { return 0; }
};
template <typename T> struct Test {
T test() {
T x;
return x;
}
};
TEMPLATE_PRODUCT_TEST_CASE("A Template product test case",
"[template][product]", (std::vector, Test),
(int, float)) {
TT();
}
TEMPLATE_PRODUCT_TEST_CASE("Product with differing arities",
"[template][product]", std::tuple,
(int, (int, double), (int, double, float))) {
TT();
}
using types = std::tuple<int8_t, int16_t, int32_t, int64_t>;
TEMPLATE_LIST_TEST_CASE("Testing template list tests", "[test][template][list]",
types) {
TT();
}
TEMPLATE_TEST_CASE_SIG(
"TemplateTestSig: arrays can be created from NTTP arguments",
"[vector][template][nttp]", ((typename T, int V), T, V), (int, 5),
(float, 4), (std::string, 15), ((std::tuple<int, float>), 6)) {
T(T);
std::cout << "V = " << V;
std::array<T, V> v;
REQUIRE(v.size() > 1);
}
template <typename T, size_t S> struct Bar {
size_t size() { return S; }
};
TEMPLATE_PRODUCT_TEST_CASE_SIG(
"A Template product test case with array signature",
"[template][product][nttp]", ((typename T, size_t S), T, S),
(std::array, Bar), ((int, 9), (float, 42))) {
TT();
TestType x;
REQUIRE(x.size() > 0);
}
template <typename T> struct test_config_get {
template <bool must_find> void run() {
// Config c{};
// std::string key{"the_key"};
// std::string value{"the_value"};
// c.set(key, value);
if constexpr (must_find) {
std::cout << "Test 1 ran";
} else {
std::cout << "Test 2 ran";
}
}
};
template <> template <bool must_find> void test_config_get<std::string>::run() {
if constexpr (must_find) {
std::cout << "Test 1 ran for strings";
} else {
std::cout << "Test 2 ran for strings";
}
}
TEMPLATE_PRODUCT_TEST_CASE("Test", "[test]", test_config_get,
(int, std::string)) {
TT();
// TestType t;
test_config_get<int> s;
s.template run<true>();
// TestType t;
// t.run<true>();
}

View File

@@ -22,6 +22,7 @@ project (
DESCRIPTION "A basic CMake template for C++ projects" DESCRIPTION "A basic CMake template for C++ projects"
LANGUAGES CXX LANGUAGES CXX
) )
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
# Include any directories the compiler may need # Include any directories the compiler may need
include_directories(./include) include_directories(./include)

View File

@@ -14,6 +14,7 @@ project (
DESCRIPTION "A project for practicing cryptography in C++" DESCRIPTION "A project for practicing cryptography in C++"
LANGUAGES CXX LANGUAGES CXX
) )
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)

View File

@@ -14,6 +14,7 @@ project(
DESCRIPTION "Practice implementing columnar transposition in C++" DESCRIPTION "Practice implementing columnar transposition in C++"
LANGUAGES CXX LANGUAGES CXX
) )
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable( add_executable(
crypto-columnar-transposition driver.cpp crypto-columnar-transposition driver.cpp

View File

@@ -14,6 +14,7 @@ project (
DESCRIPTION "A project for practicing various data structures in C++" DESCRIPTION "A project for practicing various data structures in C++"
LANGUAGES CXX LANGUAGES CXX
) )
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)

View File

@@ -14,6 +14,7 @@ project (
DESCRIPTION "A project for testing a basic implementation of a BST" DESCRIPTION "A project for testing a basic implementation of a BST"
LANGUAGES CXX LANGUAGES CXX
) )
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable( add_executable(
data-bst driver.cpp data-bst driver.cpp

View File

@@ -10,6 +10,7 @@
#include "bst.h" #include "bst.h"
#include <cstdint>
/******************************************************************************** /********************************************************************************
* Constructors, Destructors, Operators * Constructors, Destructors, Operators

View File

@@ -14,6 +14,7 @@ project (
DESCRIPTION "Project for testing circular doubly linked list implementation" DESCRIPTION "Project for testing circular doubly linked list implementation"
LANGUAGES CXX LANGUAGES CXX
) )
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable( add_executable(
data-circular-doubly-linked-list driver.cpp data-circular-doubly-linked-list driver.cpp

View File

@@ -10,6 +10,7 @@
#include "circledoublelist.h" #include "circledoublelist.h"
#include <cstdint>
/****************************************************************************** /******************************************************************************
* Constructors, Destructors, Operators * Constructors, Destructors, Operators

View File

@@ -14,6 +14,7 @@ project (
DESCRIPTION "Project for testing circular singly linked list implementation" DESCRIPTION "Project for testing circular singly linked list implementation"
LANGUAGES CXX LANGUAGES CXX
) )
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable( add_executable(
data-circular-singly-linked-list driver.cpp data-circular-singly-linked-list driver.cpp

View File

@@ -10,6 +10,7 @@
#include "circlesinglelist.h" #include "circlesinglelist.h"
#include <cstdint>
/****************************************************************************** /******************************************************************************
* Constructors, Destructors, Operators * Constructors, Destructors, Operators

View File

@@ -14,6 +14,7 @@ project (
DESCRIPTION "A project for testing a doubly linked list implementation" DESCRIPTION "A project for testing a doubly linked list implementation"
LANGUAGES CXX LANGUAGES CXX
) )
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable( add_executable(
data-doubly-linked-list driver.cpp data-doubly-linked-list driver.cpp

View File

@@ -10,6 +10,7 @@
#include "doublelist.h" #include "doublelist.h"
#include <cstdint>
/****************************************************************************** /******************************************************************************
* Constructors, Destructors, Operators * Constructors, Destructors, Operators

View File

@@ -14,6 +14,7 @@ project (
DESCRIPTION "A project for testing a max heap implementation" DESCRIPTION "A project for testing a max heap implementation"
LANGUAGES CXX LANGUAGES CXX
) )
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable( add_executable(
data-max-heap driver.cpp data-max-heap driver.cpp

View File

@@ -10,6 +10,7 @@
#include "maxheap.h" #include "maxheap.h"
#include <cstdint>
/******************************************************************************** /********************************************************************************
* Constructors, Destructors, Operators * Constructors, Destructors, Operators

View File

@@ -14,6 +14,7 @@ project (
DESCRIPTION "Project for testing queue implementation" DESCRIPTION "Project for testing queue implementation"
LANGUAGES CXX LANGUAGES CXX
) )
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable( add_executable(
data-queue driver.cpp data-queue driver.cpp

View File

@@ -10,6 +10,7 @@
#include "queuelist.h" #include "queuelist.h"
#include <cstdint>
/****************************************************************************** /******************************************************************************
* Constructors, Destructors, Operators * Constructors, Destructors, Operators

View File

@@ -14,6 +14,7 @@ project (
DESCRIPTION "A project for testing a singly linked list implementation" DESCRIPTION "A project for testing a singly linked list implementation"
LANGUAGES CXX LANGUAGES CXX
) )
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable( add_executable(
data-singly-linked-list driver.cpp data-singly-linked-list driver.cpp

View File

@@ -10,6 +10,7 @@
#include "singlelist.h" #include "singlelist.h"
#include <cstdint>
/****************************************************************************** /******************************************************************************
* Constructors, Destructors, Operators * Constructors, Destructors, Operators

View File

@@ -14,6 +14,7 @@ project (
DESCRIPTION "A project for testing a Stack implementation" DESCRIPTION "A project for testing a Stack implementation"
LANGUAGES CXX LANGUAGES CXX
) )
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable( add_executable(
data-stack driver.cpp data-stack driver.cpp

View File

@@ -10,6 +10,7 @@
#include "stacklist.h" #include "stacklist.h"
#include <cstdint>
/****************************************************************************** /******************************************************************************
* Constructors, Destructors, Operators * Constructors, Destructors, Operators

View File

@@ -14,6 +14,7 @@ project (
DESCRIPTION "A project for practicing templated data structures in C++" DESCRIPTION "A project for practicing templated data structures in C++"
LANGUAGES CXX LANGUAGES CXX
) )
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_subdirectory(doublelist) add_subdirectory(doublelist)
add_subdirectory(queuelist) add_subdirectory(queuelist)

View File

@@ -14,5 +14,6 @@ project (
DESCRIPTION "A project for practicing templated doubly linked list implementations" DESCRIPTION "A project for practicing templated doubly linked list implementations"
LANGUAGES CXX LANGUAGES CXX
) )
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable(data-templates-doubly-linked-list driver.cpp) add_executable(data-templates-doubly-linked-list driver.cpp)

View File

@@ -14,5 +14,6 @@ project (
DESCRIPTION "A project for practicing templated queue implementations" DESCRIPTION "A project for practicing templated queue implementations"
LANGUAGES CXX LANGUAGES CXX
) )
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable(data-templates-queue driver.cpp) add_executable(data-templates-queue driver.cpp)

View File

@@ -14,5 +14,6 @@ project (
DESCRIPTION "A project for practicing templated Stack implementations" DESCRIPTION "A project for practicing templated Stack implementations"
LANGUAGES CXX LANGUAGES CXX
) )
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable(data-templates-stack driver.cpp) add_executable(data-templates-stack driver.cpp)

View File

@@ -14,5 +14,6 @@ project (
DESCRIPTION "A project for practicing templated Vector implementations" DESCRIPTION "A project for practicing templated Vector implementations"
LANGUAGES CXX LANGUAGES CXX
) )
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable(data-templates-vectors driver.cpp) add_executable(data-templates-vectors driver.cpp)

View File

@@ -14,6 +14,7 @@ project (
DESCRIPTION "A project for testing a basic Vector implementation" DESCRIPTION "A project for testing a basic Vector implementation"
LANGUAGES CXX LANGUAGES CXX
) )
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable( add_executable(
data-vectors driver.cpp data-vectors driver.cpp

View File

@@ -10,6 +10,7 @@
#include "vector.h" #include "vector.h"
#include <cstdint>
/****************************************************************************** /******************************************************************************
* Constructors, Destructors, Operators * Constructors, Destructors, Operators

View File

@@ -1,6 +1,6 @@
################################################################################ ################################################################################
## Author: Shaun Reed ## ## Author: Shaun Reed ##
## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ## ## Legal: All Content (c) 2025 Shaun Reed, all rights reserved ##
## About: A root project for practicing C++ ## ## About: A root project for practicing C++ ##
## This project can be built to debug and run all nested projects ## ## This project can be built to debug and run all nested projects ##
## Or, any subdirectory with a project() statement can be selected ## ## Or, any subdirectory with a project() statement can be selected ##
@@ -16,6 +16,7 @@ project(
DESCRIPTION "A root project for practicing graphics programming in C++" DESCRIPTION "A root project for practicing graphics programming in C++"
LANGUAGES CXX LANGUAGES CXX
) )
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)

View File

@@ -1,6 +1,6 @@
################################################################################ ################################################################################
## Author: Shaun Reed ## ## Author: Shaun Reed ##
## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ## ## Legal: All Content (c) 2025 Shaun Reed, all rights reserved ##
## ## ## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ## ## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################ ################################################################################
@@ -8,40 +8,46 @@
# Define CMake version # Define CMake version
cmake_minimum_required(VERSION 3.15) cmake_minimum_required(VERSION 3.15)
include(FetchContent)
project( project(
#[[NAME]] OpenGL-Cmake #[[NAME]] OpenGL-Cmake
DESCRIPTION "Example project for building OpenGL projects with CMake" DESCRIPTION "Example project for building OpenGL projects with CMake"
LANGUAGES CXX LANGUAGES CXX
) )
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_library(graphics-lib-opengl src/lib-opengl-test.cpp) add_library(graphics-lib-opengl src/lib-opengl-test.cpp)
target_include_directories(graphics-lib-opengl PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src) target_include_directories(graphics-lib-opengl PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src)
# Find OpenGL package # Find OpenGL package
find_package(OpenGL REQUIRED) find_package(OpenGL)
if (OPENGL_FOUND) if (NOT OPENGL_FOUND)
# Link opengl-test executable to OpenGL
target_include_directories(graphics-lib-opengl PUBLIC ${OPENGL_INCLUDE_DIR})
target_link_libraries(graphics-lib-opengl PUBLIC ${OPENGL_LIBRARIES})
else()
message( message(
"Error: CMake was unable to find the OpenGL package\n" "[Klips] Error: CMake was unable to find OpenGL.\n"
"Please install OpenGL and try again\n" "On Ubuntu 24.04 OpenGL can be installed using apt:\n"
" sudo apt install libopengl-dev libgl1-mesa-dev mesa-common-dev libglu1-mesa-dev\n"
) )
endif() endif()
# Link opengl-test executable to OpenGL
message(STATUS "[Klips] Found OpenGL: ${OPENGL_INCLUDE_DIR}")
target_include_directories(graphics-lib-opengl PUBLIC ${OPENGL_INCLUDE_DIR})
target_link_libraries(graphics-lib-opengl PUBLIC ${OPENGL_LIBRARIES})
# Find GLUT package find_package(GLUT QUIET)
find_package(GLUT REQUIRED) if(NOT GLUT_FOUND)
if (GLUT_FOUND) message(
FATAL_ERROR
"[Klips] Failed to fetch GLUT. Could not find dependency X11 input libraries.\n"
"On Ubuntu 24.04 Xi can be installed using apt:\n"
" sudo apt install libxi-dev\n"
"Alternatively, on Ubuntu 24.04 GLUT can be installed with apt:\n"
" sudo apt install freeglut3-dev\n"
)
endif()
message(STATUS "[Klips] Found GLUT: ${GLUT_INCLUDE_DIR}")
# Link lib-opengl-test executable to GLUT # Link lib-opengl-test executable to GLUT
target_include_directories(graphics-lib-opengl PUBLIC ${GLUT_INCLUDE_DIR}) target_include_directories(graphics-lib-opengl PUBLIC ${GLUT_INCLUDE_DIR})
target_link_libraries(graphics-lib-opengl PUBLIC ${GLUT_LIBRARIES}) target_link_libraries(graphics-lib-opengl PUBLIC ${GLUT_LIBRARIES})
else()
message(
"Error: CMake was unable to find the GLUT package\n"
"Please install GLUT (freeglut3-dev) and try again\n"
)
endif()
# Add test executable # Add test executable
add_executable(graphics-cmake-opengl apps/test-gl.cpp) add_executable(graphics-cmake-opengl apps/test-gl.cpp)

View File

@@ -1,6 +1,6 @@
################################################################################ ################################################################################
## Author: Shaun Reed ## ## Author: Shaun Reed ##
## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ## ## Legal: All Content (c) 2025 Shaun Reed, all rights reserved ##
## ## ## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ## ## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################ ################################################################################
@@ -13,6 +13,7 @@ project(
DESCRIPTION "Example project for building SDL projects with CMake" DESCRIPTION "Example project for building SDL projects with CMake"
LANGUAGES CXX LANGUAGES CXX
) )
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
# Add Library # Add Library
add_library( add_library(
@@ -27,10 +28,16 @@ target_include_directories( # When calling library, include a directo
) )
# Search for SDL2 package # Search for SDL2 package
find_package(SDL2 REQUIRED sdl2) find_package(SDL2 QUIET)
if (NOT SDL2_FOUND)
message(FATAL_ERROR
"[Klips] Failed to find SDL2.\n"
"On Ubuntu 24.04 SDL2 can be installed using apt:\n"
" sudo apt install libsdl2-dev\n"
)
endif()
message(STATUS "[Klips] Found SDL2: ${SDL2_INCLUDE_DIRS}")
# If SDL2 was found successfully, link to lib-sdl-test
if (SDL2_FOUND)
# Any target that links with this library will also link to SDL2 # Any target that links with this library will also link to SDL2
# + Because we choose PUBLIC visibility # + Because we choose PUBLIC visibility
target_include_directories(graphics-lib-sdl PUBLIC ${SDL2_INCLUDE_DIRS}) target_include_directories(graphics-lib-sdl PUBLIC ${SDL2_INCLUDE_DIRS})
@@ -48,10 +55,3 @@ if (SDL2_FOUND)
PRIVATE # Visibility PRIVATE # Visibility
graphics-lib-sdl # Library to link graphics-lib-sdl # Library to link
) )
else()
message(
"Error: CMake was unable to find SDL2 package.\n"
"Please install the libsdl2-dev package and try again.\n"
)
endif()

View File

@@ -14,5 +14,12 @@ project(
DESCRIPTION "Practice with multithreaded programming in C++" DESCRIPTION "Practice with multithreaded programming in C++"
LANGUAGES CXX LANGUAGES CXX
) )
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
add_compile_options("-Wall")
add_subdirectory(conditions)
add_subdirectory(deadlock)
add_subdirectory(livelock)
add_subdirectory(race-condition) add_subdirectory(race-condition)

View File

@@ -0,0 +1,25 @@
# Multithreading
A few basic multithreading programs written in C++ while learning about
the [concurrency support library](https://en.cppreference.com/w/cpp/thread)
```
klips/cpp/multithreading
.
├── conditions # Using condition_variable to control job execution flow
├── deadlock # Example of problem and solution for deadlocks
├── livelock # Example of problem and solution for livelocks
├── race-condition # Example of problem and solution for race conditions
└── README.md
```
We can build the examples with the following commands.
```bash
cd /path/to/klips/cpp/multithreading/
mkdir build && cd build
cmake .. && cmake --build .
ls bin/
multithread-conditions multithread-deadlock multithread-livelock multithread-race-condition
```

View File

@@ -0,0 +1,27 @@
################################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: An example of condition_variables in multithreaded C++ ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
cmake_minimum_required(VERSION 3.16)
# std::scoped_lock requires C++17
set(CMAKE_CXX_STANDARD 17)
add_compile_options("-Wall")
project(
#[[NAME]] ConditionVariables
VERSION 1.0
DESCRIPTION "Example of condition_variables in multithreaded C++"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable(
multithread-conditions driver.cpp
)
target_link_libraries(multithread-conditions pthread)

View File

@@ -0,0 +1,62 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: An example of condition_variables in multithreaded C++ ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#include <chrono>
#include <condition_variable>
#include <iostream>
#include <mutex>
#include <thread>
static std::mutex mtx;
std::condition_variable cv;
bool processing = false;
// Starts a job that waits for kick-off from main
// + When job finishes, handoff result back to main via processing bool
void job(int32_t & shared) {
std::unique_lock uniqueLock(mtx);
cv.wait(uniqueLock, []()->bool {return processing;});
std::cout << std::this_thread::get_id()
<< " thread_A: Initial value of shared = " << shared << std::endl;
while (shared < INT32_MAX) {
shared++;
}
// We're no longer processing data
processing = false;
std::cout << std::this_thread::get_id()
<< " thread_A: Done working." << std::endl;
uniqueLock.unlock(); // Important! Unlock uniqueLock before we notify
// Notify main that we've finished, so it can proceed
cv.notify_one();
}
int main(const int argc, const char * argv[]) {
std::cout << "main() thread id: " << std::this_thread::get_id() << std::endl;
int32_t share = 0;
std::thread thread_A(job, std::ref(share));
mtx.lock();
std::this_thread::sleep_for(std::chrono::seconds(3));
share = INT32_MAX / 2;
processing = true;
mtx.unlock();
// Notify thread_A that its work can begin
cv.notify_one();
// Wait until thread_A finishes its work
std::unique_lock uniqueLock(mtx);
// Block execution until we are not processing
cv.wait(uniqueLock, []()->bool { return !processing;});
std::cout << std::this_thread::get_id() << " main(): final value of shared = "
<< share << std::endl;
thread_A.join();
return 0;
}

View File

@@ -0,0 +1,27 @@
################################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: An example and solution for deadlocks in C++ ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
cmake_minimum_required(VERSION 3.16)
# std::scoped_lock requires C++17
set(CMAKE_CXX_STANDARD 17)
add_compile_options("-Wall")
project(
#[[NAME]] Deadlock
VERSION 1.0
DESCRIPTION "Example and solution for deadlocks in C++"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable(
multithread-deadlock driver.cpp
)
target_link_libraries(multithread-deadlock pthread)

View File

@@ -0,0 +1,189 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: An example and solution for deadlocks in C++ ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#include <chrono>
#include <iostream>
#include <mutex>
#include <sstream>
#include <thread>
static std::mutex mtx_A, mtx_B, output;
// Helper function to output thread ID and string associated with mutex name
// + This must also be thread-safe, since we want threads to produce output
// + There is no bug or issue here; This is just in support of example output
void print_safe(const std::string & s) {
std::scoped_lock<std::mutex> scopedLock(output);
std::cout << s << std::endl;
}
// Helper function to convert std::thread::id to string
std::string id_string(const std::thread::id & id) {
std::stringstream stream;
stream << id;
return stream.str();
}
// In the two threads within this function, we have a problem
// + The mutex locks are acquired in reverse order, so they collide
// + This is called a deadlock; The program will *never* finish
void problem() {
std::thread thread_A([]()->void {
mtx_A.lock();
print_safe(id_string(std::this_thread::get_id()) + " thread_A: Locked A");
std::this_thread::sleep_for(std::chrono::seconds(1));
mtx_B.lock(); // We can't lock B! thread_B is using it
// The program will never reach this point in execution; We are in deadlock
print_safe(id_string(std::this_thread::get_id())
+ " thread_A: B has been unlocked, we can proceed!\n Locked B"
);
std::this_thread::sleep_for(std::chrono::seconds(1));
print_safe(id_string(std::this_thread::get_id())
+ " thread_A: Unlocking A, B..."
);
mtx_A.unlock();
mtx_B.unlock();
});
std::thread thread_B([]()->void {
mtx_B.lock();
print_safe(id_string(std::this_thread::get_id()) + " thread_B: Locked B");
std::this_thread::sleep_for(std::chrono::seconds(1));
mtx_A.lock(); // We can't lock A! thread_A is using it
// The program will never reach this point in execution; We are in deadlock
print_safe(id_string(std::this_thread::get_id())
+ " thread_B: A has been unlocked, we can proceed!\n Locked A"
);
std::this_thread::sleep_for(std::chrono::seconds(1));
print_safe(id_string(std::this_thread::get_id())
+ " thread_B: Unlocking B, A..."
);
mtx_B.unlock();
mtx_A.unlock();
});
// This offers a way out of the deadlock, so we can proceed to the solution
std::this_thread::sleep_for(std::chrono::seconds(2));
char input;
print_safe("\n"
+ id_string(std::this_thread::get_id())
+ " problem(): We are in a deadlock. \n"
+ " Enter y/Y to continue to the solution...\n"
);
while (std::cin >> input) {
if (input != 'Y' && input != 'y') continue;
else break;
}
print_safe(id_string(std::this_thread::get_id())
+ " problem(): Unlocking A, B..."
);
mtx_A.unlock();
mtx_B.unlock();
thread_A.join();
thread_B.join();
}
// std::lock will lock N mutex locks
// + If either is in use, execution will block until both are available to lock
void solution_A() {
std::thread thread_A([]()->void {
std::lock(mtx_A, mtx_B);
print_safe(id_string(std::this_thread::get_id()) + ": Locked A, B");
std::this_thread::sleep_for(std::chrono::seconds(1));
print_safe(id_string(std::this_thread::get_id()) + ": Unlocking A, B...");
mtx_A.unlock();
mtx_B.unlock();
});
std::thread thread_B([]()->void {
std::lock(mtx_B, mtx_A);
print_safe(id_string(std::this_thread::get_id()) + ": Locked B, A");
std::this_thread::sleep_for(std::chrono::seconds(1));
print_safe(id_string(std::this_thread::get_id()) + ": Unlocking B, A...");
mtx_B.unlock();
mtx_A.unlock();
});
thread_A.join();
thread_B.join();
}
// std::lock_guard is a C++11 object which can be constructed with 1 mutex
// + When the program leaves the scope of the guard, the mutex is unlocked
void solution_B() {
std::thread thread_A([]()->void {
// lock_guard will handle unlocking when program leaves this scope
std::lock_guard<std::mutex> guard_A(mtx_A), guard_B(mtx_B);
print_safe(id_string(std::this_thread::get_id()) + ": Locked A, B");
std::this_thread::sleep_for(std::chrono::seconds(1));
print_safe(id_string(std::this_thread::get_id()) + ": Unlocking A, B...");
// We don't need to explicitly unlock either mutex
});
std::thread thread_B([]()->void {
std::lock_guard<std::mutex> guard_B(mtx_B), guard_A(mtx_A);
print_safe(id_string(std::this_thread::get_id()) + ": Locked B, A");
std::this_thread::sleep_for(std::chrono::seconds(1));
print_safe(id_string(std::this_thread::get_id()) + ": Unlocking B, A...");
// We don't need to explicitly unlock either mutex
});
thread_A.join();
thread_B.join();
}
// std::scoped_lock is a C++17 object that can be constructed with N mutex
// + When the program leaves this scope, all N mutex will be unlocked
void solution_C() {
std::thread thread_A([]()->void {
// scoped_lock will handle unlocking when program leaves this scope
std::scoped_lock scopedLock(mtx_A, mtx_B);
print_safe(id_string(std::this_thread::get_id()) + ": Locked A, B");
std::this_thread::sleep_for(std::chrono::seconds(1));
print_safe(id_string(std::this_thread::get_id()) + ": Unlocking A, B...");
// We don't need to explicitly unlock either mutex
});
std::thread thread_B([]()->void {
std::scoped_lock scopedLock(mtx_B, mtx_A);
print_safe(id_string(std::this_thread::get_id()) + ": Locked B, A");
std::this_thread::sleep_for(std::chrono::seconds(1));
print_safe(id_string(std::this_thread::get_id()) + ": Unlocking B, A...");
// We don't need to explicitly unlock either mutex
});
thread_A.join();
thread_B.join();
}
int main(const int argc, const char * argv[]) {
std::cout << "main() thread id: " << std::this_thread::get_id() << std::endl;
problem();
print_safe("\nsolution_A, using std::lock\n");
solution_A();
print_safe("\nsolution_B, using std::lock_guard\n");
solution_B();
print_safe("\nsolution_C, using std::scoped_lock\n");
solution_C();
return 0;
}

View File

@@ -0,0 +1,27 @@
################################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: An example and solution for livelocks in C++ ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
cmake_minimum_required(VERSION 3.16)
# std::scoped_lock requires C++17
set(CMAKE_CXX_STANDARD 17)
add_compile_options("-Wall")
project(
#[[NAME]] LiveLock
VERSION 1.0
DESCRIPTION "Example and solution for livelocks in C++"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable(
multithread-livelock driver.cpp
)
target_link_libraries(multithread-livelock pthread)

View File

@@ -0,0 +1,117 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: An example and solution for livelocks in C++ ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#include <chrono>
#include <iostream>
#include <mutex>
#include <thread>
#include <vector>
static std::mutex mtx_A, mtx_B, output;
// Helper function to output thread ID and string associated with mutex name
// + This must also be thread-safe, since we want threads to produce output
// + There is no bug or issue here; This is just in support of example output
void print_safe(const std::string & s) {
std::scoped_lock<std::mutex> scopedLock(output);
std::cout << s << std::endl;
}
void problem() {
// Construct a vector with 5 agreed-upon times to synchronize loops in threads
typedef std::chrono::time_point<std::chrono::steady_clock,
std::chrono::steady_clock::duration> time_point;
std::vector<time_point> waitTime(6);
for (uint8_t i = 0; i < 6; i++) {
waitTime[i] = std::chrono::steady_clock::now()+std::chrono::seconds(1+i);
}
std::thread thread_A([waitTime]()->void {
uint8_t count = 0; // Used to select time slot from waitTime vector
bool done = false;
while (!done) {
count++;
std::lock_guard l(mtx_A);
std::cout << std::this_thread::get_id() << " thread_A: Lock A\n";
// Wait until the next time slot to continue
// + Helps to show example of livelock by ensuring B is not available
std::this_thread::sleep_until(waitTime[count]);
std::cout << std::this_thread::get_id() << " thread_A: Requesting B\n";
if (mtx_B.try_lock()) {
done = true;
std::cout << std::this_thread::get_id()
<< " thread_A: Acquired locks for A, B! Done.\n";
}
else {
std::cout << std::this_thread::get_id()
<< " thread_A: Can't lock B, unlocking A\n";
}
}
mtx_B.unlock();
});
std::thread thread_B([waitTime]()->void {
// As an example, enter livelock for only 5 iterations
// + Also used to select time slot from waitTime vector
uint8_t count = 0;
bool done = false;
while (!done && count < 5) {
count++;
std::lock_guard l(mtx_B);
// Wait until the next time slot to continue
// + Helps to show example of livelock by ensuring A is not available
std::this_thread::sleep_until(waitTime[count]);
if (mtx_A.try_lock()) {
// The program will never reach this point in the code
// + The only reason livelock ends is because count > 5
done = true;
}
}
});
thread_A.join();
thread_B.join();
}
// The solution below uses std::scoped_lock to avoid the livelock problem
void solution() {
std::thread thread_A([]()->void {
for (int i = 0; i < 5; i++) {
// Increase wait time with i
// + To encourage alternating lock ownership between threads
std::this_thread::sleep_for(std::chrono::milliseconds(100 * i));
std::scoped_lock l(mtx_A, mtx_B);
std::cout << std::this_thread::get_id()
<< " thread_A: Acquired locks for A, B!" << std::endl;
}
});
std::thread thread_B([]()->void {
for (int i = 0; i < 5; i++) {
std::this_thread::sleep_for(std::chrono::milliseconds(100 * i));
std::scoped_lock l(mtx_B, mtx_A);
std::cout << std::this_thread::get_id()
<< " thread_B: Acquired locks for B, A!" << std::endl;
}
});
thread_A.join();
thread_B.join();
}
int main(const int argc, const char * argv[]) {
std::cout << "main() thread id: " << std::this_thread::get_id() << std::endl;
problem();
std::cout << "\nSolution:\n\n";
solution();
return 0;
}

View File

@@ -14,6 +14,7 @@ project(
DESCRIPTION "Example and solution for race conditions" DESCRIPTION "Example and solution for race conditions"
LANGUAGES CXX LANGUAGES CXX
) )
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable( add_executable(
multithread-race-condition driver.cpp multithread-race-condition driver.cpp

View File

@@ -14,6 +14,7 @@ project(
DESCRIPTION "A project for practicing various design patterns in C++" DESCRIPTION "A project for practicing various design patterns in C++"
LANGUAGES CXX LANGUAGES CXX
) )
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)

View File

@@ -13,6 +13,7 @@ project(
DESCRIPTION "An example of the abstract factory design pattern in C++" DESCRIPTION "An example of the abstract factory design pattern in C++"
LANGUAGES CXX LANGUAGES CXX
) )
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_compile_options("-Wall") add_compile_options("-Wall")
add_executable( add_executable(

View File

@@ -14,6 +14,7 @@ project(
DESCRIPTION "An example of the adapter design pattern in C++" DESCRIPTION "An example of the adapter design pattern in C++"
LANGUAGES CXX LANGUAGES CXX
) )
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_compile_options("-Wall") add_compile_options("-Wall")
add_executable( add_executable(

View File

@@ -2,6 +2,7 @@
#ifndef ADAPTER_HPP #ifndef ADAPTER_HPP
#define ADAPTER_HPP #define ADAPTER_HPP
#include <ctime>
#include <random> #include <random>
// Target implementation to adapt to a new interface // Target implementation to adapt to a new interface

View File

@@ -14,6 +14,7 @@ project(
DESCRIPTION "An example of the bridge design pattern in C++" DESCRIPTION "An example of the bridge design pattern in C++"
LANGUAGES CXX LANGUAGES CXX
) )
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_compile_options("-Wall") add_compile_options("-Wall")
add_executable( add_executable(

View File

@@ -14,6 +14,7 @@ project(
DESCRIPTION "An example of the factory design pattern in C++" DESCRIPTION "An example of the factory design pattern in C++"
LANGUAGES CXX LANGUAGES CXX
) )
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_compile_options("-Wall") add_compile_options("-Wall")
add_executable( add_executable(

View File

@@ -14,6 +14,7 @@ project(
DESCRIPTION "An example of the state design pattern in C++" DESCRIPTION "An example of the state design pattern in C++"
LANGUAGES CXX LANGUAGES CXX
) )
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_compile_options("-Wall") add_compile_options("-Wall")
add_executable( add_executable(

View File

@@ -14,6 +14,7 @@ project(
DESCRIPTION "An example of the prototype design pattern in C++" DESCRIPTION "An example of the prototype design pattern in C++"
LANGUAGES CXX LANGUAGES CXX
) )
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_compile_options("-Wall") add_compile_options("-Wall")
add_executable( add_executable(

View File

@@ -14,6 +14,7 @@ project(
DESCRIPTION "An example of the singleton design pattern in C++" DESCRIPTION "An example of the singleton design pattern in C++"
LANGUAGES CXX LANGUAGES CXX
) )
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable( add_executable(
patterns-singleton main.cpp patterns-singleton main.cpp

View File

@@ -14,6 +14,7 @@ project(
DESCRIPTION "An example of the state design pattern in C++" DESCRIPTION "An example of the state design pattern in C++"
LANGUAGES CXX LANGUAGES CXX
) )
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_compile_options("-Wall") add_compile_options("-Wall")
add_executable( add_executable(

View File

@@ -14,6 +14,7 @@ project(
DESCRIPTION "An example of the visitor design pattern in C++" DESCRIPTION "An example of the visitor design pattern in C++"
LANGUAGES CXX LANGUAGES CXX
) )
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_compile_options("-Wall") add_compile_options("-Wall")
add_executable( add_executable(

25
cpp/qt/CMakeLists.txt Normal file
View File

@@ -0,0 +1,25 @@
################################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: A root project for practicing Qt 6 projects in C++ ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
cmake_minimum_required(VERSION 3.15)
project(
#[[NAME]] Klips
VERSION 1.0
DESCRIPTION "A root project for several small Qt6 practice projects"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
add_compile_options("-Wall")
add_subdirectory(designer)
add_subdirectory(designer-plugin)
add_subdirectory(designer-plugin-collection)
add_subdirectory(slots)

32
cpp/qt/README.md Normal file
View File

@@ -0,0 +1,32 @@
# Cpp
```bash
shaunrd0/klips/cpp/qt/
├── designer # Using Qt Designer to create application GUI
├── designer-plugin # Adding custom widgets as Qt Designer plugins
├── designer-plugin-collection # Adding a collection of widget plugins to Qt Designer
└── README.md
```
This directory contains a `CMakeLists.txt`, which can be selected to open as a
project within your preferred IDE. From there, all nested examples can be built,
debugged, and ran.
The plugin examples will need to be installed for Qt Designer integration to work.
On Linux, Qt Designer looks under `/some/path/to/Qt/Tools/QtCreator/lib/Qt/plugins/designer/`.
On windows or Mac, this path may differ. Unfortunately I don't have these machines to test for myself.
```bash
cd klips/cpp/qt/designer-plugin-collection
mkdir build && cd build
cmake .. && cmake --build . --target install
```
After installing the plugin collection example above, we can open Qt Creator and navigate to the Designer.
We should see the custom collection is available within the Designer, and the contents of the widgets render correctly in the application view.
![side-panel-view.png](side-panel-view.png)
![plugin-render-view.png](plugin-render-view.png)

View File

@@ -0,0 +1,127 @@
################################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Example of making a collection of widget plugins for Qt Designer ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
cmake_minimum_required(VERSION 3.15)
project(
#[[NAME]] DesignerPluginCollection
VERSION 1.0
DESCRIPTION "Example of a widget plugin collection for Qt Designer"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
# Lowercase string to use as a slug for executable names for identification.
string(TOLOWER ${PROJECT_NAME} PROJECT_NAME_LOWER)
include(GenerateExportHeader)
add_compile_options(-Wall)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_SHARED_MODULE_PREFIX "")
set(QT_DIR "$ENV{HOME}/Code/Clones/Qt/" CACHE PATH "Path to Qt6")
# Qt Designer will look in different locations if WIN / Unix.
# These paths are for using Qt Designer integrated within Qt Creator.
# Standalone Qt Designer may use different paths.
if (WIN32)
set(QT_PLUGIN_INSTALL_DIR
"${QT_DIR}/Tools/QtCreator/bin/plugins/designer"
)
# This path may be different on windows. I have not tested this.
set(QT_PLUGIN_LIBRARY_DIR
"${QT_DIR}/Tools/QtCreator/lib/Qt/lib"
)
else()
set(QT_PLUGIN_INSTALL_DIR
"${QT_DIR}/Tools/QtCreator/lib/Qt/plugins/designer"
)
set(QT_PLUGIN_LIBRARY_DIR
"${QT_DIR}/Tools/QtCreator/lib/Qt/lib"
)
endif()
# This should be set to your Qt6 installation directory.
set(QT_INSTALL_DIR "${QT_DIR}/6.3.1/gcc_64/" CACHE PATH "Path to Qt6 install")
list(APPEND CMAKE_PREFIX_PATH "${QT_INSTALL_DIR}")
find_package(Qt6 COMPONENTS UiPlugin Core Gui Widgets)
if (NOT Qt6_FOUND)
message(
FATAL_ERROR
"[Klips] Error: CMake was unable to find Qt6 libraries.\n"
"The example will not be built until the build is configured with these packages installed.\n"
"On Ubuntu 24.04 Qt6 can be installed using apt:\n"
" sudo apt-get install qt6-base-dev qt6-tools-dev\n"
)
endif()
# Creating a library with two plugins for the collection.
set(WIDGET_PLUGIN_LIBRARY widget-plugin-library_${PROJECT_NAME_LOWER})
qt_add_library(${WIDGET_PLUGIN_LIBRARY}
textview.cpp textview.h
widgetplugin.cpp widgetplugin.h
)
target_sources(${WIDGET_PLUGIN_LIBRARY} PRIVATE
textview.cpp textview.h
treeview.cpp treeview.h
widgetplugin.cpp widgetplugin.h
)
set_target_properties(${WIDGET_PLUGIN_LIBRARY} PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
)
target_link_libraries(${WIDGET_PLUGIN_LIBRARY}
PUBLIC Qt::UiPlugin Qt::Core Qt::Gui Qt::Widgets
)
install(TARGETS ${WIDGET_PLUGIN_LIBRARY}
RUNTIME DESTINATION "${QT_PLUGIN_LIBRARY_DIR}"
BUNDLE DESTINATION "${QT_PLUGIN_LIBRARY_DIR}"
LIBRARY DESTINATION "${QT_PLUGIN_LIBRARY_DIR}"
)
generate_export_header(${WIDGET_PLUGIN_LIBRARY}
BASE_NAME widget_plugin_library
EXPORT_FILE_NAME "${CMAKE_CURRENT_BINARY_DIR}/widget-plugin-library_export.h"
)
# Creating the collection
set(WIDGET_PLUGIN_COLLECTION widget-plugin-collection_${PROJECT_NAME_LOWER})
qt_add_library(${WIDGET_PLUGIN_COLLECTION}
widgetplugincollection.cpp widgetplugincollection.h
)
target_link_libraries(${WIDGET_PLUGIN_COLLECTION}
Qt6::Widgets Qt6::UiPlugin ${WIDGET_PLUGIN_LIBRARY}
)
install(TARGETS ${WIDGET_PLUGIN_COLLECTION}
RUNTIME DESTINATION "${QT_PLUGIN_INSTALL_DIR}"
BUNDLE DESTINATION "${QT_PLUGIN_INSTALL_DIR}"
LIBRARY DESTINATION "${QT_PLUGIN_INSTALL_DIR}"
)
# Application that will use the widget plugin
set(APP_DIR ${CMAKE_CURRENT_SOURCE_DIR})
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/app-dir.h.in"
"${CMAKE_CURRENT_SOURCE_DIR}/app-dir.h"
@ONLY
)
set(WIDGET_APP widget-app_${PROJECT_NAME_LOWER})
qt_add_executable(${WIDGET_APP}
widgetapp.cpp widgetapp.h widgetapp.ui
main.cpp
)
target_link_libraries(${WIDGET_APP}
PRIVATE Qt::Widgets ${WIDGET_PLUGIN_LIBRARY}
)

View File

@@ -0,0 +1,6 @@
#ifndef APPDIR_H_IN
#define APPDIR_H_IN
#define APP_DIR "/media/shaun/Storage/Code/klips/cpp/qt/designer-plugin-collection"
#endif // APPDIR_H_IN

View File

@@ -0,0 +1,6 @@
#ifndef APPDIR_H_IN
#define APPDIR_H_IN
#define APP_DIR "@APP_DIR@"
#endif // APPDIR_H_IN

View File

@@ -0,0 +1,18 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Main driver fprogram for practice using signals and slots in Qt ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#include "widgetapp.h"
#include <QApplication>
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
WidgetApp widgetApp;
widgetApp.show();
return app.exec();
}

View File

@@ -0,0 +1,10 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Text viewer for signals and slots examples ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#include "textview.h"

View File

@@ -0,0 +1,44 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Text viewer for signals and slots examples ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#ifndef KLIPS_TEXTVIEW_H
#define KLIPS_TEXTVIEW_H
#include "widget-plugin-library_export.h"
#include <QPlainTextEdit>
class WIDGET_PLUGIN_LIBRARY_EXPORT TextView : public QPlainTextEdit {
Q_OBJECT
public:
explicit TextView(QWidget *parent = nullptr) : QPlainTextEdit(parent) {
appendPlainText("This is an example of a custom QTextView widget.");
}
~TextView() = default;
QString includeFile() const { return QStringLiteral("text-view.h"); };
public:
signals:
void sendTest();
private:
signals:
void sentTestPrivate();
public slots:
void test() { appendPlainText("Test signal received by TextView."); }
void testArgs(const QString &message) { appendPlainText(message); }
private slots:
void testPrivate() {}
};
#endif // KLIPS_TEXTVIEW_H

View File

@@ -0,0 +1,10 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Tree viewer ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#include "treeview.h"

View File

@@ -0,0 +1,36 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Tree viewer ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#ifndef KLIPS_TREEVIEW_H
#define KLIPS_TREEVIEW_H
#include "widget-plugin-library_export.h"
#include <app-dir.h>
#include <QFileSystemModel>
#include <QSortFilterProxyModel>
#include <QTreeView>
class WIDGET_PLUGIN_LIBRARY_EXPORT TreeView : public QTreeView {
Q_OBJECT
public:
explicit TreeView(QWidget *parent = nullptr) : QTreeView(parent) {
QFileSystemModel *model = new QFileSystemModel;
QSortFilterProxyModel *proxy = new QSortFilterProxyModel(this);
QModelIndex rootModelIndex = model->setRootPath(APP_DIR);
proxy->setSourceModel(model);
setModel(proxy);
setRootIndex(proxy->mapFromSource(rootModelIndex));
}
~TreeView() = default;
};
#endif // KLIPS_TREEVIEW_H

View File

@@ -0,0 +1,15 @@
/*##############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Application that uses widget from the collection ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
*/
#include "widgetapp.h"
WidgetApp::WidgetApp(QWidget *parent) : QMainWindow(parent) {
m_widgetApp = new Ui::MainWindow;
m_widgetApp->setupUi(this);
}

Some files were not shown because too many files have changed in this diff Show More