diff --git a/.gitignore b/.gitignore index 9c42e71..1a11557 100644 --- a/.gitignore +++ b/.gitignore @@ -33,7 +33,6 @@ .vscode/ build/ -docs/ # Cabin package build directory /cabin-out diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..1ee87fc --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,48 @@ +# Contributing to Flow Core + +We love your input! We want to make contributing to Flow Core as easy and transparent as possible, whether it's: + +- Reporting a bug +- Discussing the current state of the code +- Submitting a fix +- Proposing new features +- Becoming a maintainer + +## Development Process + +We use GitHub to host code, to track issues and feature requests, as well as accept pull requests. + +1. Fork the repo and create your branch from `main` +2. If you've added code that should be tested, add tests +3. If you've changed APIs, update the documentation +4. Ensure the test suite passes +5. Make sure your code follows our coding standards +6. Issue that pull request! + +## Coding Standards + +- Use C++20 features appropriately +- Follow the existing code style +- Write meaningful commit messages +- Document new code using Doxygen-style comments +- Include unit tests for new features + +## License + +By contributing, you agree that your contributions will be licensed under the MIT License. + +## Bug Reports + +Please use the GitHub issue tracker and include: + +- A quick summary and/or background +- Steps to reproduce +- Expected behavior +- Actual behavior +- Notes (possibly including why you think this might be happening) + +## Pull Request Process + +1. Update the README.md with details of changes if applicable +2. Update the docs/ with relevant documentation +3. The PR will be merged once you have the sign-off of at least one maintainer diff --git a/README.md b/README.md index ef6dce9..10d3c7d 100644 --- a/README.md +++ b/README.md @@ -4,37 +4,66 @@ [![License: MIT](https://img.shields.io/github/license/InFlowStructure/flow-core)](https://github.com/InFlowStructure/flow-core/blob/main/LICENSE) [![Language: C++20](https://img.shields.io/badge/Language-C%2B%2B20%20-blue)](https://en.cppreference.com/w/cpp/20) -A cross-platform C++20 graph based code engine for building easily modified code flows on the fly. Intended as a Low-Code / No-Code solution. +## Overview + +Flow Core is a cross-platform C++20 graph-based code engine designed for building dynamically modifiable code flows. It serves as a foundation for Low-Code/No-Code solutions by providing a flexible and extensible graph computation system. + +## Features + +- Dynamic node-based computation graph +- Runtime module loading system +- Thread-safe execution environment +- Type-safe data propagation +- JSON serialization support +- Cross-platform compatibility + +## Requirements + +- C++20 compliant compiler +- CMake 3.15 or higher +- Modern operating system (Windows, macOS, Linux) + +## Dependencies + +Flow Core relies on these open-source libraries: +- [nlohmann_json](https://github.com/nlohmann/json) - Modern JSON handling +- [thread-pool](https://github.com/bshoshany/thread-pool) - Efficient thread management ## Building -To build the project with CMake, simply run +### Basic Build ```bash cmake -B build cmake --build build --parallel ``` -To build tests, configure the build directory with the following +### Build with Tests ```bash cmake -B build -Dflow-core_BUILD_TESTS=ON +cmake --build build --parallel ``` -## Installing +## Installation -To install, configure the cmake build as follows: +Configure and install: ```bash cmake -B build -Dflow-core_INSTALL=ON -``` -Then, simply build the project normally, then run one of -```bash +cmake --build build --parallel cmake --install build ``` -> [!TIP] -> Try adding the `--config` flag with the appropriate build type. +## Getting Started -## Dependencies +Check out our [documentation](docs/getting-started.md) for: +- Basic concepts and architecture +- Creating your first flow +- Building custom nodes +- Best practices and examples + +## License + +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. + +## Contributing -This project depends on the following open source projects: -- [nlohmann_json](https://github.com/nlohmann/json) -- [thread-pool](https://github.com/bshoshany/thread-pool) +We welcome contributions! Please see our [Contributing Guidelines](CONTRIBUTING.md) for details on how to submit pull requests, report issues, and contribute to the project. diff --git a/docs/getting-started.md b/docs/getting-started.md new file mode 100644 index 0000000..9c57e49 --- /dev/null +++ b/docs/getting-started.md @@ -0,0 +1,102 @@ +# Getting Started with Flow Core + +## Basic Concepts + +Flow Core is built around these key concepts: + +- **Nodes**: Basic computation units +- **Connections**: Data flow between nodes +- **Graph**: Network of connected nodes +- **Flow**: Complete execution sequence + +## Creating Your First Flow + +### 1. Basic Setup + +```cpp +#include +#include +#include + +int main() { + auto factory = std::make_shared(); + auto env = flow::Env::Create(factory); + auto graph = std::make_shared("MyGraph", env); + + // ... + + return 0; +} +``` + +### 2. Creating Nodes + +```cpp +// Create nodes via factory +auto source_node = factory->CreateNode(flow::TypeName_v, UUID{}, "Source", env); +auto processing_node = factory->CreateNode(flow::TypeName_v, UUID{}, "Multiply", env); +auto output_node = factory->CreateNode(flow::TypeName_v, UUID{}, "Output", env); + +// Add nodes to graph +graph->AddNode(source_node); +graph->AddNode(processing_node); +graph->AddNode(output_node); +``` + +### 3. Connecting Nodes + +```cpp +// Connect nodes to create a flow +graph->ConnectNodes(source_node->ID(), "output", processing_node->ID(), "input"); +graph->ConnectNodes(processing_node->ID(), "output", output_node->ID(), "input"); + +// Start the flow +graph->Run(); +``` + +## Building Custom Nodes + +### 1. Define Node Class + +```cpp +// filepath: custom_node.hpp + +class CustomNode : public flow::Node +{ + public: + CustomNode(const UUID& uuid, std::string_view class_name, std::string_view name, std::shared_ptr env) + : Node(uuid, class_name, name, std::move(env)) + { + // Add inputs and outputs + } + + virtual ~CustomNode() = default; + + void Compute() override + { + // Your computation logic + } +}; +``` + +### 2. Register Node + +```cpp +factory->RegisterNodeClass("Custom Category", "Custom Node"); +``` + +## Best Practices + +1. **Type Safety** + + - Always use proper type checking + - Validate inputs before processing + +2. **Performance** + + - Minimize allocations in Compute() methods + - Use thread-safe operations when needed + +3. **Testing** + - Write unit tests for custom nodes + - Test edge cases and error conditions diff --git a/include/flow/core/Connection.hpp b/include/flow/core/Connection.hpp index 6427c65..c7292b6 100644 --- a/include/flow/core/Connection.hpp +++ b/include/flow/core/Connection.hpp @@ -31,7 +31,15 @@ using SharedConnection = std::shared_ptr; class Connection { public: - Connection() = delete; + /** + * @brief Deleted default constructor. + * @note Connections must be constructed with valid node and port information. + */ + Connection() = delete; + + /** + * @brief Default destructor. + */ ~Connection() = default; /** diff --git a/include/flow/core/Graph.hpp b/include/flow/core/Graph.hpp index 871b5b1..2b535e5 100644 --- a/include/flow/core/Graph.hpp +++ b/include/flow/core/Graph.hpp @@ -194,7 +194,18 @@ class Graph return node && _nodes.contains(node->ID()); } + /** + * @brief Convert graph state to JSON. + * @param j JSON object to store state in. + * @param g Graph to serialize. + */ friend void to_json(json& j, const Graph& g); + + /** + * @brief Restore graph state from JSON. + * @param j JSON object containing serialized state. + * @param g Graph to restore state into. + */ friend void from_json(const json& j, Graph& g); public: diff --git a/include/flow/core/IndexableName.hpp b/include/flow/core/IndexableName.hpp index 17e1ab3..d17aaa8 100644 --- a/include/flow/core/IndexableName.hpp +++ b/include/flow/core/IndexableName.hpp @@ -95,9 +95,23 @@ class IndexableName constexpr IndexableName& operator=(const IndexableName&) = default; constexpr IndexableName& operator=(IndexableName&&) = default; + /** + * @brief Three-way comparison operator. + * @param other IndexableName to compare against. + * @returns Strong ordering based on hash value. + */ constexpr auto operator<=>(const IndexableName& other) const { return _value <=> other._value; }; + /** + * @brief Convert to size_t hash value. + * @returns The hash value of this IndexableName. + */ constexpr operator std::size_t() const { return _value; } + + /** + * @brief Convert to string view. + * @returns String view of the original name. + */ constexpr operator std::string_view() const { return _name; } /// Get the hash value diff --git a/include/flow/core/Port.hpp b/include/flow/core/Port.hpp index 4b9a61e..a237918 100644 --- a/include/flow/core/Port.hpp +++ b/include/flow/core/Port.hpp @@ -4,6 +4,7 @@ #pragma once #include "Core.hpp" +#include "Event.hpp" #include "IndexableName.hpp" #include "NodeData.hpp" @@ -114,6 +115,14 @@ class Port */ void SetCaption(std::string new_caption); + /** + * @brief Event triggered when port data is modified. + * @param key The port's unique key. + * @param data The new data set on the port. + * @param output Flag indicating if this change should propagate. + */ + Event OnSetData; + private: std::shared_ptr _data; diff --git a/include/flow/core/TypeConversion.hpp b/include/flow/core/TypeConversion.hpp index 959083b..f521793 100644 --- a/include/flow/core/TypeConversion.hpp +++ b/include/flow/core/TypeConversion.hpp @@ -76,17 +76,13 @@ class TypeRegistry void RegisterUnidirectionalConversion(const ConversionFunc& converter = Convert); /** - * @brief Register conversions in both directions between two types. + * @brief Register conversions between two types with custom converters. * - * @details Registers both T->U and U->T conversions. For same-type conversions, - * automatically handles reference and const variations through - * RegisterUnidirectionalConversion. + * @tparam T First type to convert between. + * @tparam U Second type to convert between. * - * @tparam T First type in the conversion pair - * @tparam U Second type in the conversion pair - * - * @param from_to_converter Conversion function from T to U - * @param to_from_converter Conversion function from U to T + * @param from_to_converter Custom conversion function from T to U. + * @param to_from_converter Custom conversion function from U to T. */ template void RegisterBidirectionalConversion(const ConversionFunc& from_to_converter = Convert, diff --git a/include/flow/core/UUID.hpp b/include/flow/core/UUID.hpp index 2917f33..769585e 100644 --- a/include/flow/core/UUID.hpp +++ b/include/flow/core/UUID.hpp @@ -35,8 +35,17 @@ class UUID UUID& operator=(const UUID&) = default; UUID& operator=(UUID&&) = default; + /** + * @brief Convert UUID to string representation. + * @returns String representation of the UUID in standard format. + */ operator std::string() const; + /** + * @brief Three-way comparison operator. + * @param other UUID to compare against. + * @returns Strong ordering based on internal byte representation. + */ constexpr auto operator<=>(const UUID& other) const = default; /** diff --git a/src/Port.cpp b/src/Port.cpp index 0ae4460..99fa719 100644 --- a/src/Port.cpp +++ b/src/Port.cpp @@ -42,6 +42,11 @@ void Port::SetData(SharedNodeData data, bool output) { _data->FromPointer(data->AsPointer()); } + + if (OnSetData) + { + OnSetData(_key, _data, output); + } } void Port::SetCaption(std::string new_caption) { _caption = std::move(new_caption); }