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
6 changes: 6 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,9 @@ jobs:
- uses: actions/checkout@v3
with:
fetch-depth: 0

- name: Install Qt
uses: jurplel/install-qt-action@v3

- name: Install project and build
env:
Expand All @@ -291,4 +294,7 @@ jobs:

cmake -B sample_build -S src/examples/rpp/package
cmake --build sample_build --parallel 2 --config Release

cmake -B qt_sample_build -S src/examples/rppqt/package
cmake --build qt_sample_build --parallel 2 --config Release

7 changes: 6 additions & 1 deletion BUILDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ But RPP is header-only library, so, without enabling any extra options is just c
- `RPP_BUILD_TESTS` - (ON/OFF) build unit tests (default OFF)
- `RPP_BUILD_EXAMPLES` - (ON/OFF) build examples of usage of RPP (default OFF)
- `RPP_BUILD_SFML_CODE` - (ON/OFF) build RPP code related to SFML or not (default OFF) - requires SFML to be installed
- `RPP_BUILD_QT_CODE` - (ON/OFF) build RPPQT related code (examples/tests)(rppqt module doesn't requires this one) (default OFF) - requires QT5/6 to be installed

By default, it provides rpp and rppqt INTERFACE modules.

<!-- ### Building on Apple Silicon

Expand Down Expand Up @@ -76,7 +78,9 @@ This project exports a CMake package to be used with the [`find_package`][3]
command of CMake:

* Package name: `RPP`
* Target name: `RPP::rpp`
* Target names:
- `RPP::rpp` - main rpp INTERFACE target
- `RPP:rppqt` - additional INTERFACE target to extend functionality for QT. rppqt doesn't include QT or even doesn't link with that.

Example usage:

Expand All @@ -87,6 +91,7 @@ find_package(RPP REQUIRED)
target_link_libraries(
project_target PRIVATE
RPP::rpp
RPP::rppqt
)
```

Expand Down
47 changes: 17 additions & 30 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,17 @@
[![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=victimsnino_ReactivePlusPlus&metric=sqale_rating)](https://sonarcloud.io/summary/new_code?id=victimsnino_ReactivePlusPlus)
![GitHub commit activity](https://img.shields.io/github/commit-activity/m/victimsnino/ReactivePlusPlus)

## Usage:

ReactivePlusPlus is reactive programming library for C++ language inspired by "official implementation" ([RxCpp](https://github.com/ReactiveX/RxCpp)) and original idea ([ReactiveX](https://reactivex.io/))
ReactivePlusPlus is reactive programming library for C++ language inspired by "official implementation" ([RxCpp](https://github.com/ReactiveX/RxCpp)) and original idea ([ReactiveX](https://reactivex.io/)) that only depends on standard library and C++20 features.

See the [BUILDING](BUILDING.md) document to know how to build RPP.
If you are going to know more details about developing for RPP check [HACKING](HACKING.md) document.

## Example:

In short: ReactivePlusPlus is library for building asynchronous event-driven streams of data with help of sequences of primitive operators in the declarative form. For example:

```cpp
rpp::source::from_callable(&::getchar)
.repeat()
Expand All @@ -21,46 +28,26 @@ rpp::source::from_callable(&::getchar)
.map(&::toupper)
.subscribe([](char v) { std::cout << v; });
```
## Features:

Main advantages of ReactivePlusPlus are that it is written in Modern C++ with Performance and Usage in mind. As a result it is fast, readable, easy to use and well-documented.
Main advantages of ReactivePlusPlus are that it is written in Modern C++ with Performance and Usage in mind. As a result it is fast, readable, easy to use and well-documented. And it is proven with [continous benchmarking results and comparison with RxCpp](https://victimsnino.github.io/ReactivePlusPlus/benchmark)

**NOTE**: ReactivePlusPlus is library for C++20. So, it works only on compilers that supports most C++20 features. CI uses gcc-10, clang-11, visual studio 2022

# What about existing Reactive Extension libraries for C++?

Reactive programming is excelent programming paradigm and approach for creation of multi-threading and real-time programs which reacts on some events. Unfortunately, there is only one stable and fully-implemented library at the moment of creation of ReactivePlusPlus - [RxCpp](https://github.com/ReactiveX/RxCpp).

[RxCpp](https://github.com/ReactiveX/RxCpp) is great and awesome library and perfect implementation of ReactiveX approach. However RxCpp has some disadvantages:
- It is a bit **"old" library written in C++11** with some parts written in the **pre-C++11 style** (mess of old-style classes and wrappers)
- **Issue** with **template parameters**: `rxcpp::observable` contains **full chain of operators** as second template parameter... where each operator has a bunch of another template parameters itself. It forces **IDEs** works **slower** while parsing resulting type of observable. Also it forces to generate **heavier binaries and debug symbols and slower build time**.
- It has high perfomance cost due to tremendous amount of usage of heap.
- Some parts of code written with non-effective logic (e.g. `timeout` operator - rpp version faster in about 17000-25000%...)
## Implementation status:

Another implementation of RX for c++: [another-rxcpp](https://github.com/CODIANZ/another-rxcpp). It partly solves issues of RxCpp via **eliminating of template parameter** with help of **type-erasing** and making each callback as `std::function`. As a result issue with templates resvoled, but this approach has disadvantages related to runtime: resulting size of observers/observables becomes greater due to heavy `std::function` object, usage of heap for storing everything causes perfomance issues, implementation is just pretty simple and provides a lot of copies of passed objects.
Currently ReactivePlusPlus is still under development but it has a lot of implemented operators for now. List of implemented features can be found in [API Reference](https://victimsnino.github.io/ReactivePlusPlus/docs/html/group__rpp.html) with very detailed documentation for each of them

## Why ReactivePlusPlus?

**ReactivePlusPlus** tries to solve all mentioned issues:
- **ReactivePlusPlus** written in **Modern C++ (C++20)** with concepts which makes code-base a lot more understandable and clean:
- Concepts provide more clear errors and checks: you will understand that pass something incorrect before compilation in IDE or during compilation with understandable errors instead of _"invalid template class map_invalid_t"_
- Everywhere while possible used deduction of template arguments, for example, type of values of observable by type of subscriber used in on_subscribe and etc
- **ReactivePlusPlus** keeps balance between performance and type-erasing mechanism: Read about it in [**"Performance vs Flexibility: Specific vs Dynamic"**](https://victimsnino.github.io/ReactivePlusPlus/docs/html/specific_vs_dynamic.html)
- **ReactivePlusPlus** is fast: every part of code written with perfomance in mind. Starting from tests over amount of copies/move and finishing to Continous Benchmarking. Benchmarks prove that RPP faster than RxCPP in most cases: [Continous benchmarking results](https://victimsnino.github.io/ReactivePlusPlus/benchmark) (check timeout operator comparison ;-))
On a par with this ReactivePlusPlus provides native support for QT via RppQt target (extension over original RPP). List of implemented features can be found in [QT API Reference](https://victimsnino.github.io/ReactivePlusPlus/docs/html/group__rppqt.html) with very detailed documentation for each of them.


# Useful links
- [Why ReactivePlusPlus? What about existing Reactive Extension libraries for C++?](https://victimsnino.github.io/ReactivePlusPlus/docs/html/why_rpp.html)
- [Manual and doxygen documentation](https://victimsnino.github.io/ReactivePlusPlus/docs/html/index.html)
- [Examples](https://github.com/victimsnino/ReactivePlusPlus/tree/main/src/examples)
- [Continous benchmarking results, comparison of `dynamic` and `specific` and comparison with RxCpp](https://victimsnino.github.io/ReactivePlusPlus/benchmark)
- [Articles](https://github.com/victimsnino/ReactivePlusPlus/blob/main/docs/Articles.md)

# Building and installing

See the [BUILDING](BUILDING.md) document.
If you are going to know more details about developing for RPP check [HACKING](HACKING.md) document.

# Contributing

See the [CONTRIBUTING](CONTRIBUTING.md) document.
- [Articles/Turorials/Guides](https://github.com/victimsnino/ReactivePlusPlus/blob/main/docs/Articles.md)
- [CONTRIBUTING](CONTRIBUTING.md) document.

# Licensing

Expand Down
20 changes: 13 additions & 7 deletions cmake/install-rules.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@ include(GNUInstallDirs)
set(package RPP)

install(
DIRECTORY src/rpp
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
COMPONENT RPP_Development
DIRECTORY
src/rpp
src/rppqt
DESTINATION
"${CMAKE_INSTALL_INCLUDEDIR}"
COMPONENT
RPP_Development
)

install(
Expand All @@ -16,6 +20,12 @@ install(
INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/rpp"
)

install(
TARGETS rppqt
EXPORT RPPTargets
INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/rppqt"
)

write_basic_package_version_file(
"${package}ConfigVersion.cmake"
COMPATIBILITY SameMajorVersion
Expand Down Expand Up @@ -46,7 +56,3 @@ install(
DESTINATION "${RPP_INSTALL_CMAKEDIR}"
COMPONENT RPP_Development
)

# if(PROJECT_IS_TOP_LEVEL)
# include(CPack)
# endif()
2 changes: 1 addition & 1 deletion cmake/variables.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ endif()

# ------------ Options to tweak ---------------------
option(RPP_BUILD_SFML_CODE "Enable SFML support in examples/code." OFF)
option(RPP_BUILD_QT_CODE "Enable QT support and add rppqt library." OFF)
option(RPP_BUILD_QT_CODE "Enable QT support in examples/code." OFF)

if (RPP_DEVELOPER_MODE)
option(RPP_BUILD_TESTS "Build unit tests tree." OFF)
Expand Down
1 change: 1 addition & 0 deletions docs/Docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

- \subpage quick_start - fast overview how to work with RPP
- \subpage advanced - everything about RPP in details
- \subpage why_rpp - why ReactivePlusPlus?
- \subpage specific_vs_dynamic - overview of the concept of `specific_` and `dynamic_` types used in **RPP** and how it affects performance
- \subpage memory_model - overview of new concept used in RPP related to copy/move/heap usage for objects passed inside RPP
- \subpage exception_guarantee - overview of exception guarantees provided by RPP
Expand Down
22 changes: 22 additions & 0 deletions docs/WhyRpp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Why ReactivePlusPlus? {#why_rpp}

## What about existing Reactive Extension libraries for C++?

Reactive programming is excelent programming paradigm and approach for creation of multi-threading and real-time programs which reacts on some events. Unfortunately, there is only one stable and fully-implemented library at the moment of creation of ReactivePlusPlus - [RxCpp](https://github.com/ReactiveX/RxCpp).

[RxCpp](https://github.com/ReactiveX/RxCpp) is great and awesome library and perfect implementation of ReactiveX approach. However RxCpp has some disadvantages:
- It is a bit **"old" library written in C++11** with some parts written in the **pre-C++11 style** (mess of old-style classes and wrappers)
- **Issue** with **template parameters**: `rxcpp::observable` contains **full chain of operators** as second template parameter... where each operator has a bunch of another template parameters itself. It forces **IDEs** works **slower** while parsing resulting type of observable. Also it forces to generate **heavier binaries and debug symbols and slower build time**.
- It has high perfomance cost due to tremendous amount of usage of heap.
- Some parts of code written with non-effective logic (e.g. `timeout` operator - rpp version faster in about 17000-25000%...)

Another implementation of RX for c++: [another-rxcpp](https://github.com/CODIANZ/another-rxcpp). It partly solves issues of RxCpp via **eliminating of template parameter** with help of **type-erasing** and making each callback as `std::function`. As a result issue with templates resvoled, but this approach has disadvantages related to runtime: resulting size of observers/observables becomes greater due to heavy `std::function` object, usage of heap for storing everything causes perfomance issues, implementation is just pretty simple and provides a lot of copies of passed objects.

## Why ReactivePlusPlus?

**ReactivePlusPlus** tries to solve all mentioned issues:
- **ReactivePlusPlus** written in **Modern C++ (C++20)** with concepts which makes code-base a lot more understandable and clean:
- Concepts provide more clear errors and checks: you will understand that pass something incorrect before compilation in IDE or during compilation with understandable errors instead of _"invalid template class map_invalid_t"_
- Everywhere while possible used deduction of template arguments, for example, type of values of observable by type of subscriber used in on_subscribe and etc
- **ReactivePlusPlus** keeps balance between performance and type-erasing mechanism: Read about it in [**"Performance vs Flexibility: Specific vs Dynamic"**](https://victimsnino.github.io/ReactivePlusPlus/docs/html/specific_vs_dynamic.html)
- **ReactivePlusPlus** is fast: every part of code written with perfomance in mind. Starting from tests over amount of copies/move and finishing to Continous Benchmarking. Benchmarks prove that RPP faster than RxCPP in most cases: [Continous benchmarking results](https://victimsnino.github.io/ReactivePlusPlus/benchmark) (check timeout operator comparison ;-))
5 changes: 1 addition & 4 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,7 @@
#

add_subdirectory(rpp)

if(RPP_BUILD_QT_CODE)
add_subdirectory(rppqt)
endif()
add_subdirectory(rppqt)

if (RPP_BUILD_BENCHMARKS)
add_subdirectory(benchmarks)
Expand Down
3 changes: 3 additions & 0 deletions src/examples/rppqt/interactive_window/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Simple application which appends inserted text to "result" via applying some flags:

![w](./example.png)
Binary file added src/examples/rppqt/interactive_window/example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 7 additions & 16 deletions src/examples/rppqt/interactive_window/interactive_window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,13 @@ int main(int argc, char* argv[])

window.show();

auto remove_spaces_obs = rppqt::source::from_signal(remove_spaces_checkbox, &QCheckBox::stateChanged).
start_with(static_cast<int>(remove_spaces_checkbox.isChecked()));
auto lower_case_obs = rppqt::source::from_signal(lower_checkbox, &QCheckBox::stateChanged).
start_with(static_cast<int>(lower_checkbox.isChecked()));
auto remove_spaces_obs = rppqt::source::from_signal(remove_spaces_checkbox, &QCheckBox::stateChanged)
.start_with(static_cast<int>(remove_spaces_checkbox.isChecked()));
auto lower_case_obs = rppqt::source::from_signal(lower_checkbox, &QCheckBox::stateChanged)
.start_with(static_cast<int>(lower_checkbox.isChecked()));

auto text_to_append_obs = rppqt::source::from_signal(text_input, &QTextEdit::textChanged)
.map([&](const auto&)
{
return text_input.toPlainText();
})
.map([&](const auto&) { return text_input.toPlainText(); })
.combine_latest([](const QString& current_string, bool is_remove_spaces)
{
return is_remove_spaces ? current_string.simplified().remove(' ') : current_string;
Expand All @@ -68,10 +65,7 @@ int main(int argc, char* argv[])


rppqt::source::from_signal(push_text, &QPushButton::pressed)
.with_latest_from([&](const auto&, const QString& text_to_append)
{
return text_to_append;
},
.with_latest_from([&](const auto&, const QString& text_to_append) { return text_to_append; },
text_to_append_obs)
.tap([&](const auto&) { text_input.setText(""); })
.scan(QString{},
Expand All @@ -86,10 +80,7 @@ int main(int argc, char* argv[])
return current_text + QString("<span style=\" color:#ff0000;\">%1</span>").arg(preview_text_to_append);
},
text_to_append_obs)
.subscribe([&](const QString& current_text)
{
result.setText(current_text);
},
.subscribe([&](const QString& current_text) { result.setText(current_text); },
[](const std::exception_ptr& err)
{
try
Expand Down
14 changes: 14 additions & 0 deletions src/examples/rppqt/package/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
cmake_minimum_required(VERSION 3.12)
project(test_package_qt LANGUAGES CXX)

find_package(RPP REQUIRED CONFIG)

set(RPP_BUILD_QT_CODE ON)
set(RPP_BUILD_EXAMPLES ON)
include(../../../../cmake/dependencies.cmake)

add_executable(${PROJECT_NAME} ../interactive_window/interactive_window.cpp)
target_link_libraries(${PROJECT_NAME} PRIVATE RPP::rppqt)
target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_20)

rpp_add_qt_support_to_executable(${PROJECT_NAME})