Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@

.vscode/
build/
docs/

# Cabin package build directory
/cabin-out
48 changes: 48 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -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
57 changes: 43 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
102 changes: 102 additions & 0 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
@@ -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 <flow/core/Env.hpp>
#include <flow/core/Graph.hpp>
#include <flow/core/NodeFactory.hpp>

int main() {
auto factory = std::make_shared<flow::NodeFactory>();
auto env = flow::Env::Create(factory);
auto graph = std::make_shared<flow::Graph>("MyGraph", env);

// ...

return 0;
}
```

### 2. Creating Nodes

```cpp
// Create nodes via factory
auto source_node = factory->CreateNode(flow::TypeName_v<NumberSourceNode>, UUID{}, "Source", env);
auto processing_node = factory->CreateNode(flow::TypeName_v<MultiplyNode>, UUID{}, "Multiply", env);
auto output_node = factory->CreateNode(flow::TypeName_v<PrintNode>, 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> 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<CustomNode>("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
10 changes: 9 additions & 1 deletion include/flow/core/Connection.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,15 @@ using SharedConnection = std::shared_ptr<class Connection>;
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;

/**
Expand Down
11 changes: 11 additions & 0 deletions include/flow/core/Graph.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
14 changes: 14 additions & 0 deletions include/flow/core/IndexableName.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 9 additions & 0 deletions include/flow/core/Port.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#pragma once

#include "Core.hpp"
#include "Event.hpp"
#include "IndexableName.hpp"
#include "NodeData.hpp"

Expand Down Expand Up @@ -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<const IndexableName&, const SharedNodeData&, bool> OnSetData;

private:
std::shared_ptr<INodeData> _data;

Expand Down
14 changes: 5 additions & 9 deletions include/flow/core/TypeConversion.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,17 +76,13 @@ class TypeRegistry
void RegisterUnidirectionalConversion(const ConversionFunc& converter = Convert<From, To>);

/**
* @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<typename T, typename U>
void RegisterBidirectionalConversion(const ConversionFunc& from_to_converter = Convert<T, U>,
Expand Down
9 changes: 9 additions & 0 deletions include/flow/core/UUID.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand Down
5 changes: 5 additions & 0 deletions src/Port.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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); }
Expand Down