├── .devcontainer ├── Dockerfile ├── devcontainer.json └── reinstall-cmake.sh ├── .gitignore ├── .vscode ├── c_cpp_properties.json ├── launch.json ├── settings.json └── tasks.json ├── CMakeLists.txt ├── LICENSE ├── README.ja.md ├── README.md ├── include └── another-rxcpp │ ├── internal │ ├── supports │ │ ├── observable_static_decl.inc │ │ ├── observable_static_impl.inc │ │ ├── operators_in_observables_decl.inc │ │ └── operators_in_observables_impl.inc │ └── tools │ │ ├── fn.h │ │ ├── stream_controller.h │ │ └── util.h │ ├── observable.h │ ├── observables.h │ ├── observables │ ├── blocking.h │ ├── connectable.h │ ├── defer.h │ ├── empty.h │ ├── error.h │ ├── interval.h │ ├── iterate.h │ ├── just.h │ ├── never.h │ └── range.h │ ├── observer.h │ ├── operators.h │ ├── operators │ ├── amb.h │ ├── blocking.h │ ├── delay.h │ ├── distinct_until_changed.h │ ├── filter.h │ ├── finally.h │ ├── first.h │ ├── flat_map.h │ ├── last.h │ ├── map.h │ ├── merge.h │ ├── observe_on.h │ ├── on_error_resume_next.h │ ├── publish.h │ ├── retry.h │ ├── skip_until.h │ ├── skip_while.h │ ├── subscribe.h │ ├── subscribe_on.h │ ├── take.h │ ├── take_last.h │ ├── take_until.h │ ├── take_while.h │ ├── tap.h │ ├── timeout.h │ └── zip.h │ ├── rx.h │ ├── scheduler.h │ ├── schedulers.h │ ├── schedulers │ ├── default_scheduler.h │ └── new_thread_scheduler.h │ ├── subjects.h │ ├── subjects │ ├── behavior.h │ └── subject.h │ ├── subscriber.h │ ├── subscription.h │ ├── utils.h │ └── utils │ ├── inflow_restriction.h │ ├── ready_set_go.h │ ├── sem.h │ ├── something.h │ └── unit.h └── test ├── amb.cpp ├── behavior_subject.cpp ├── blocking.cpp ├── case_1.cpp ├── case_2.cpp ├── case_3.cpp ├── case_4.cpp ├── case_5.cpp ├── case_6.cpp ├── case_7.cpp ├── common.h ├── connectable.cpp ├── defer.cpp ├── delay.cpp ├── distinct_until_changed.cpp ├── empty.cpp ├── error.cpp ├── filter.cpp ├── finally.cpp ├── first.cpp ├── flat_map.cpp ├── inflow_restriction.cpp ├── interval.cpp ├── iterate.cpp ├── just.cpp ├── last.cpp ├── main.cpp ├── map.cpp ├── merge.cpp ├── move_check.cpp ├── never.cpp ├── new_thread_scheduler.cpp ├── observable.cpp ├── observe_on.cpp ├── on_error_resume_next.cpp ├── operators_in_observable.cpp ├── range.cpp ├── ready_set_go.cpp ├── retry.cpp ├── rxcpp_compatible.cpp ├── skip_until.cpp ├── skip_while.cpp ├── something.cpp ├── subject.cpp ├── subscribe_on.cpp ├── take.cpp ├── take_last.cpp ├── take_until.cpp ├── take_while.cpp ├── tap.cpp ├── timeout.cpp └── zip.cpp /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/devcontainers/cpp:0-debian-11 2 | 3 | ARG REINSTALL_CMAKE_VERSION_FROM_SOURCE="none" 4 | 5 | # Optionally install the cmake for vcpkg 6 | COPY ./reinstall-cmake.sh /tmp/ 7 | 8 | RUN if [ "${REINSTALL_CMAKE_VERSION_FROM_SOURCE}" != "none" ]; then \ 9 | chmod +x /tmp/reinstall-cmake.sh && /tmp/reinstall-cmake.sh ${REINSTALL_CMAKE_VERSION_FROM_SOURCE}; \ 10 | fi \ 11 | && rm -f /tmp/reinstall-cmake.sh 12 | 13 | # [Optional] Uncomment this section to install additional vcpkg ports. 14 | # RUN su vscode -c "${VCPKG_ROOT}/vcpkg install " 15 | 16 | # [Optional] Uncomment this section to install additional packages. 17 | # RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 18 | # && apt-get -y install --no-install-recommends 19 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the 2 | // README at: https://github.com/devcontainers/templates/tree/main/src/cpp 3 | { 4 | "name": "C++", 5 | "build": { 6 | "dockerfile": "Dockerfile" 7 | } 8 | 9 | // Features to add to the dev container. More info: https://containers.dev/features. 10 | // "features": {}, 11 | 12 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 13 | // "forwardPorts": [], 14 | 15 | // Use 'postCreateCommand' to run commands after the container is created. 16 | // "postCreateCommand": "gcc -v", 17 | 18 | // Configure tool-specific properties. 19 | // "customizations": {}, 20 | 21 | // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. 22 | // "remoteUser": "root" 23 | } 24 | -------------------------------------------------------------------------------- /.devcontainer/reinstall-cmake.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | #------------------------------------------------------------------------------------------------------------- 3 | # Copyright (c) Microsoft Corporation. All rights reserved. 4 | # Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. 5 | #------------------------------------------------------------------------------------------------------------- 6 | # 7 | set -e 8 | 9 | CMAKE_VERSION=${1:-"none"} 10 | 11 | if [ "${CMAKE_VERSION}" = "none" ]; then 12 | echo "No CMake version specified, skipping CMake reinstallation" 13 | exit 0 14 | fi 15 | 16 | # Cleanup temporary directory and associated files when exiting the script. 17 | cleanup() { 18 | EXIT_CODE=$? 19 | set +e 20 | if [[ -n "${TMP_DIR}" ]]; then 21 | echo "Executing cleanup of tmp files" 22 | rm -Rf "${TMP_DIR}" 23 | fi 24 | exit $EXIT_CODE 25 | } 26 | trap cleanup EXIT 27 | 28 | 29 | echo "Installing CMake..." 30 | apt-get -y purge --auto-remove cmake 31 | mkdir -p /opt/cmake 32 | 33 | architecture=$(dpkg --print-architecture) 34 | case "${architecture}" in 35 | arm64) 36 | ARCH=aarch64 ;; 37 | amd64) 38 | ARCH=x86_64 ;; 39 | *) 40 | echo "Unsupported architecture ${architecture}." 41 | exit 1 42 | ;; 43 | esac 44 | 45 | CMAKE_BINARY_NAME="cmake-${CMAKE_VERSION}-linux-${ARCH}.sh" 46 | CMAKE_CHECKSUM_NAME="cmake-${CMAKE_VERSION}-SHA-256.txt" 47 | TMP_DIR=$(mktemp -d -t cmake-XXXXXXXXXX) 48 | 49 | echo "${TMP_DIR}" 50 | cd "${TMP_DIR}" 51 | 52 | curl -sSL "https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/${CMAKE_BINARY_NAME}" -O 53 | curl -sSL "https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/${CMAKE_CHECKSUM_NAME}" -O 54 | 55 | sha256sum -c --ignore-missing "${CMAKE_CHECKSUM_NAME}" 56 | sh "${TMP_DIR}/${CMAKE_BINARY_NAME}" --prefix=/opt/cmake --skip-license 57 | 58 | ln -s /opt/cmake/bin/cmake /usr/local/bin/cmake 59 | ln -s /opt/cmake/bin/ctest /usr/local/bin/ctest 60 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Linux", 5 | "includePath": [ 6 | "${workspaceFolder}/**" 7 | ], 8 | "defines": [], 9 | "compilerPath": "/usr/bin/gcc", 10 | "cStandard": "gnu11", 11 | "cppStandard": "gnu++14", 12 | "intelliSenseMode": "linux-gcc-x64", 13 | "configurationProvider": "ms-vscode.cmake-tools" 14 | } 15 | ], 16 | "version": 4 17 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // IntelliSense を使用して利用可能な属性を学べます。 3 | // 既存の属性の説明をホバーして表示します。 4 | // 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "(lldb) 起動", 9 | "type": "cppdbg", 10 | "request": "launch", 11 | "program": "${workspaceFolder}/build/another-rxcpp", 12 | "args": [], 13 | "stopAtEntry": false, 14 | "cwd": "${fileDirname}", 15 | "environment": [], 16 | "externalConsole": false, 17 | "MIMode": "lldb", 18 | "preLaunchTask": "build" 19 | }, 20 | { 21 | "name": "(gdb) 起動", 22 | "type": "cppdbg", 23 | "request": "launch", 24 | "program": "${workspaceFolder}/build/another-rxcpp", 25 | "args": [], 26 | "stopAtEntry": false, 27 | "cwd": "${fileDirname}", 28 | "environment": [], 29 | "externalConsole": false, 30 | "MIMode": "gdb", 31 | "preLaunchTask": "build" 32 | } 33 | ] 34 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "*.inc": "cpp", 4 | "atomic": "cpp", 5 | "iostream": "cpp", 6 | "ostream": "cpp", 7 | "__bit_reference": "cpp", 8 | "__config": "cpp", 9 | "__debug": "cpp", 10 | "__errc": "cpp", 11 | "__functional_base": "cpp", 12 | "__hash_table": "cpp", 13 | "__locale": "cpp", 14 | "__mutex_base": "cpp", 15 | "__node_handle": "cpp", 16 | "__nullptr": "cpp", 17 | "__split_buffer": "cpp", 18 | "__string": "cpp", 19 | "__threading_support": "cpp", 20 | "__tree": "cpp", 21 | "__tuple": "cpp", 22 | "algorithm": "cpp", 23 | "array": "cpp", 24 | "bit": "cpp", 25 | "bitset": "cpp", 26 | "cctype": "cpp", 27 | "chrono": "cpp", 28 | "clocale": "cpp", 29 | "cmath": "cpp", 30 | "complex": "cpp", 31 | "condition_variable": "cpp", 32 | "cstdarg": "cpp", 33 | "cstddef": "cpp", 34 | "cstdint": "cpp", 35 | "cstdio": "cpp", 36 | "cstdlib": "cpp", 37 | "cstring": "cpp", 38 | "ctime": "cpp", 39 | "cwchar": "cpp", 40 | "cwctype": "cpp", 41 | "deque": "cpp", 42 | "exception": "cpp", 43 | "fstream": "cpp", 44 | "functional": "cpp", 45 | "future": "cpp", 46 | "initializer_list": "cpp", 47 | "iomanip": "cpp", 48 | "ios": "cpp", 49 | "iosfwd": "cpp", 50 | "istream": "cpp", 51 | "iterator": "cpp", 52 | "limits": "cpp", 53 | "list": "cpp", 54 | "locale": "cpp", 55 | "map": "cpp", 56 | "memory": "cpp", 57 | "mutex": "cpp", 58 | "new": "cpp", 59 | "numeric": "cpp", 60 | "optional": "cpp", 61 | "queue": "cpp", 62 | "random": "cpp", 63 | "ratio": "cpp", 64 | "regex": "cpp", 65 | "set": "cpp", 66 | "sstream": "cpp", 67 | "stack": "cpp", 68 | "stdexcept": "cpp", 69 | "streambuf": "cpp", 70 | "string": "cpp", 71 | "string_view": "cpp", 72 | "system_error": "cpp", 73 | "thread": "cpp", 74 | "tuple": "cpp", 75 | "type_traits": "cpp", 76 | "typeinfo": "cpp", 77 | "unordered_map": "cpp", 78 | "unordered_set": "cpp", 79 | "utility": "cpp", 80 | "variant": "cpp", 81 | "vector": "cpp", 82 | "__functional_03": "cpp", 83 | "memory_resource": "cpp", 84 | "__functional_base_03": "cpp", 85 | "*.tcc": "cpp", 86 | "__bits": "cpp", 87 | "compare": "cpp", 88 | "concepts": "cpp" 89 | } 90 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "valgrind", 8 | "type": "shell", 9 | "command": "valgrind", 10 | "args": [ 11 | "--leak-check=full", 12 | // "--num-callers=500", 13 | // "--max-threads=5000", 14 | "${workspaceFolder}/build/another-rxcpp" 15 | ], 16 | "problemMatcher": [ 17 | "$gcc" 18 | ] 19 | }, 20 | { 21 | "label": "build", 22 | "type": "shell", 23 | "command": "cmake", 24 | "args": [ 25 | "--build", 26 | "./build" 27 | ], 28 | "problemMatcher": [ 29 | "$gcc" 30 | ] 31 | } 32 | 33 | ] 34 | } -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0.0) 2 | project(another-rxcpp VERSION 0.1.0) 3 | 4 | include(CTest) 5 | enable_testing() 6 | 7 | include_directories(include) 8 | 9 | file(GLOB sources 10 | ./test/*.cpp 11 | ) 12 | 13 | add_definitions(-DSUPPORTS_OPERATORS_IN_OBSERVABLE) 14 | add_definitions(-DSUPPORTS_RXCPP_COMPATIBLE) 15 | 16 | add_executable(another-rxcpp ${sources}) 17 | 18 | # set(CMAKE_CXX_FLAGS "-std=c++14") # for clang 19 | set(CMAKE_CXX_FLAGS "-std=c++14 -pthread -g") # for gcc 20 | set(CPACK_PROJECT_NAME ${PROJECT_NAME}) 21 | set(CPACK_PROJECT_VERSION ${PROJECT_VERSION}) 22 | include(CPack) 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 terukazu inoue 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /include/another-rxcpp/internal/supports/observable_static_decl.inc: -------------------------------------------------------------------------------- 1 | template static auto just(ARGS&&...args) noexcept; 2 | template static auto error(ARGS&&...args) noexcept; 3 | template static auto never() noexcept; 4 | template static auto empty() noexcept; 5 | template static auto range(T&&, ARGS&&...args) noexcept; 6 | template static auto interval(ARGS&&...args) noexcept; 7 | template static auto iterate(ARGS&&...args) noexcept; 8 | template static auto defer(ARGS&&...args) noexcept; -------------------------------------------------------------------------------- /include/another-rxcpp/internal/supports/observable_static_impl.inc: -------------------------------------------------------------------------------- 1 | #include "../../observables.h" 2 | 3 | namespace another_rxcpp { 4 | template auto observable::just(ARGS&&...args) noexcept { 5 | return observables::just(std::forward(args)...); 6 | } 7 | template auto observable::error(ARGS&&...args) noexcept { 8 | return observables::error(std::forward(args)...); 9 | } 10 | template auto observable::never() noexcept { 11 | return observables::never(); 12 | } 13 | template auto observable::empty() noexcept { 14 | return observables::empty(); 15 | } 16 | template auto observable::range(T&& start, ARGS&&...args) noexcept { 17 | return observables::range(std::forward(start), std::forward(args)...); 18 | } 19 | template auto observable::interval(ARGS&&...args) noexcept { 20 | return observables::interval(std::forward(args)...); 21 | } 22 | template auto observable::iterate(ARGS&&...args) noexcept { 23 | return observables::iterate(std::forward(args)...); 24 | } 25 | template auto observable::defer(ARGS&&...args) noexcept { 26 | return observables::defer(std::forward(args)...); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /include/another-rxcpp/internal/supports/operators_in_observables_decl.inc: -------------------------------------------------------------------------------- 1 | template auto flat_map(F&&, ARGS&&...) const noexcept; 2 | template auto map(F&&, ARGS&&...) const noexcept; 3 | template auto tap(F&&, ARGS&&...) const noexcept; 4 | 5 | template ::value, bool> = true> 6 | auto zip(OB, ARGS...) const noexcept; 7 | template ::value, bool> = true> 8 | auto zip(F, ARGS...) const noexcept; 9 | 10 | template auto take(ARGS&&...) const noexcept; 11 | template auto on_error_resume_next(ARGS&&...) const noexcept; 12 | template auto observe_on(ARGS&&...) const noexcept; 13 | template auto retry(ARGS&&...) const noexcept; 14 | template auto subscribe_on(ARGS&&...) const noexcept; 15 | template auto skip_until(ARGS&&...) const noexcept; 16 | template auto skip_while(ARGS&&...) const noexcept; 17 | template auto take_last(ARGS&&...) const noexcept; 18 | template auto take_until(ARGS&&...) const noexcept; 19 | template auto take_while(ARGS&&...) const noexcept; 20 | template auto merge(ARGS&&...) const noexcept; 21 | template auto amb(ARGS&&...) const noexcept; 22 | template auto timeout(ARGS&&...) const noexcept; 23 | template auto delay(ARGS&&...) const noexcept; 24 | template auto finally(ARGS&&...) const noexcept; 25 | template auto filter(ARGS&&...) const noexcept; 26 | template auto first(ARGS&&...) const noexcept; 27 | 28 | auto publish() const noexcept; 29 | auto as_blocking() const noexcept; 30 | auto distinct_until_changed() const noexcept; 31 | auto last() const noexcept; 32 | -------------------------------------------------------------------------------- /include/another-rxcpp/internal/supports/operators_in_observables_impl.inc: -------------------------------------------------------------------------------- 1 | #include "../../operators.h" 2 | 3 | namespace another_rxcpp { 4 | template 5 | template 6 | auto observable::flat_map(F&& f, ARGS&&...args) const noexcept 7 | { 8 | using OUT = decltype(f(std::declval())); 9 | return *this | operators::flat_map>(std::forward(f), std::forward(args)...); 10 | } 11 | 12 | template 13 | template 14 | auto observable::map(F&& f, ARGS&&...args) const noexcept 15 | { 16 | using OUT = decltype(f(std::declval())); 17 | return *this | operators::map>(std::forward(f), std::forward(args)...); 18 | } 19 | 20 | template 21 | template 22 | auto observable::tap(F&& f, ARGS&&...args) const noexcept 23 | { 24 | return *this | operators::tap>(std::forward(f), std::forward(args)...); 25 | } 26 | 27 | template 28 | template ::value, bool>> 29 | auto observable::zip(OB ob, ARGS...args) const noexcept 30 | { 31 | return *this | operators::zip(ob, args...); 32 | } 33 | 34 | template 35 | template ::value, bool>> 36 | auto observable::zip(F f, ARGS...args) const noexcept 37 | { 38 | using fargs = operators::zip_internal::function_parameter, ARGS...>; 39 | using fret = decltype(fargs::feval(f)); 40 | using FTYPE = typename fargs::template ftype; 41 | return *this | operators::zip(f, args...); 42 | } 43 | 44 | template 45 | template 46 | auto observable::take(ARGS&&...args) const noexcept 47 | { 48 | return *this | operators::take(std::forward(args)...); 49 | } 50 | 51 | template 52 | template 53 | auto observable::on_error_resume_next(ARGS&&...args) const noexcept 54 | { 55 | return *this | operators::on_error_resume_next(std::forward(args)...); 56 | } 57 | 58 | template 59 | template 60 | auto observable::observe_on(ARGS&&...args) const noexcept 61 | { 62 | return *this | operators::observe_on(std::forward(args)...); 63 | } 64 | 65 | template 66 | template 67 | auto observable::retry(ARGS&&...args) const noexcept 68 | { 69 | return *this | operators::retry(std::forward(args)...); 70 | } 71 | 72 | template 73 | template 74 | auto observable::subscribe_on(ARGS&&...args) const noexcept 75 | { 76 | return *this | operators::subscribe_on(std::forward(args)...); 77 | } 78 | 79 | template 80 | template 81 | auto observable::skip_until(ARGS&&...args) const noexcept 82 | { 83 | return *this | operators::skip_until(std::forward(args)...); 84 | } 85 | 86 | template 87 | template 88 | auto observable::skip_while(ARGS&&...args) const noexcept 89 | { 90 | return *this | operators::skip_while(std::forward(args)...); 91 | } 92 | 93 | template 94 | template 95 | auto observable::take_last(ARGS&&...args) const noexcept 96 | { 97 | return *this | operators::take_last(std::forward(args)...); 98 | } 99 | 100 | template 101 | template 102 | auto observable::take_until(ARGS&&...args) const noexcept 103 | { 104 | return *this | operators::take_until(std::forward(args)...); 105 | } 106 | 107 | template 108 | template 109 | auto observable::take_while(ARGS&&...args) const noexcept 110 | { 111 | return *this | operators::take_while(std::forward(args)...); 112 | } 113 | 114 | template 115 | template 116 | auto observable::merge(ARGS&&...args) const noexcept 117 | { 118 | return *this | operators::merge(std::forward(args)...); 119 | } 120 | 121 | template 122 | template 123 | auto observable::amb(ARGS&&...args) const noexcept 124 | { 125 | return *this | operators::amb(std::forward(args)...); 126 | } 127 | 128 | template 129 | template 130 | auto observable::timeout(ARGS&&...args) const noexcept 131 | { 132 | return *this | operators::timeout(std::forward(args)...); 133 | } 134 | 135 | template 136 | template 137 | auto observable::delay(ARGS&&...args) const noexcept 138 | { 139 | return *this | operators::delay(std::forward(args)...); 140 | } 141 | 142 | template 143 | template 144 | auto observable::finally(ARGS&&...args) const noexcept 145 | { 146 | return *this | operators::finally(std::forward(args)...); 147 | } 148 | 149 | template 150 | template 151 | auto observable::filter(ARGS&&...args) const noexcept 152 | { 153 | return *this | operators::filter(std::forward(args)...); 154 | } 155 | 156 | template 157 | template 158 | auto observable::first(ARGS&&...args) const noexcept 159 | { 160 | return *this | operators::first(std::forward(args)...); 161 | } 162 | 163 | template 164 | auto observable::publish() const noexcept 165 | { 166 | return *this | operators::publish(); 167 | } 168 | 169 | template 170 | auto observable::as_blocking() const noexcept 171 | { 172 | return *this | operators::blocking(); 173 | } 174 | 175 | template 176 | auto observable::distinct_until_changed() const noexcept 177 | { 178 | return *this | operators::distinct_until_changed(); 179 | } 180 | 181 | template 182 | auto observable::last() const noexcept 183 | { 184 | return *this | operators::last(); 185 | } 186 | 187 | } /* namespace another_rxcpp */ 188 | -------------------------------------------------------------------------------- /include/another-rxcpp/internal/tools/fn.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_function__) 2 | #define __another_rxcpp_h_function__ 3 | 4 | #include 5 | 6 | namespace another_rxcpp { namespace internal { 7 | 8 | template using fn = std::function; 9 | 10 | 11 | }} /* namespace another_rxcpp::internal */ 12 | #endif /* !defined(__another_rxcpp_h_function__) */ 13 | -------------------------------------------------------------------------------- /include/another-rxcpp/internal/tools/stream_controller.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_stream_controller__) 2 | #define __another_rxcpp_h_stream_controller__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include "fn.h" 8 | #include "../../subscriber.h" 9 | 10 | namespace another_rxcpp { namespace internal { 11 | 12 | template class stream_controller { 13 | public: 14 | using value_type = T; 15 | using subscriber_type = subscriber; 16 | using serial_type = int32_t; 17 | using unsubscriber_map = std::map>; 18 | using on_finalize_t = internal::fn; 19 | using on_finalizes_t = std::shared_ptr; 20 | 21 | private: 22 | struct inner { 23 | subscriber_type subscriber_; 24 | std::recursive_mutex mtx_; 25 | serial_type serial_; 26 | unsubscriber_map unsubscribers_; 27 | on_finalizes_t on_finalizes_; 28 | inner(subscriber_type sbsc) noexcept : subscriber_(sbsc), serial_(0) {} 29 | }; 30 | 31 | mutable std::shared_ptr inner_; 32 | 33 | stream_controller(std::shared_ptr inner) noexcept 34 | : inner_(inner) {} 35 | 36 | stream_controller() = delete; 37 | 38 | public: 39 | stream_controller(subscriber_type subscriber) noexcept { 40 | inner_ = std::make_shared(subscriber); 41 | subscriber.set_on_unsubscribe([inner = inner_]{ 42 | stream_controller(inner).finalize(); 43 | }); 44 | } 45 | 46 | template void set_on_finalize(F&& f) const noexcept { 47 | std::lock_guard lock(inner_->mtx_); 48 | inner_->on_finalizes_ = std::make_shared(std::forward(f)); 49 | } 50 | 51 | template observer new_observer( 52 | fn n, 53 | fn e, 54 | fn c 55 | ) const noexcept 56 | { 57 | const auto serial = [&]{ 58 | std::lock_guard lock(inner_->mtx_); 59 | return inner_->serial_++; 60 | }(); 61 | 62 | auto ob = observer( 63 | [n, serial](const In& value) { 64 | n(serial, value); 65 | }, 66 | [e, serial](std::exception_ptr err) { 67 | e(serial, err); 68 | }, 69 | [c, serial]() { 70 | c(serial); 71 | } 72 | ); 73 | 74 | { 75 | std::lock_guard lock(inner_->mtx_); 76 | inner_->unsubscribers_.insert({serial, [ob]{ ob.unsubscribe(); }}); 77 | } 78 | return ob; 79 | } 80 | 81 | void sink_next(const value_type& value) const noexcept { 82 | if(inner_->subscriber_.is_subscribed()){ 83 | inner_->subscriber_.on_next(value); 84 | } 85 | else{ 86 | finalize(); 87 | } 88 | } 89 | 90 | void sink_next(value_type&& value) const noexcept { 91 | if(inner_->subscriber_.is_subscribed()){ 92 | inner_->subscriber_.on_next(std::move(value)); 93 | } 94 | else{ 95 | finalize(); 96 | } 97 | } 98 | 99 | void sink_error(std::exception_ptr err) const noexcept { 100 | if(inner_->subscriber_.is_subscribed()){ 101 | inner_->subscriber_.on_error(err); 102 | finalize(); 103 | } 104 | else{ 105 | finalize(); 106 | } 107 | } 108 | 109 | void sink_completed(serial_type serial) const noexcept { 110 | if(inner_->subscriber_.is_subscribed()){ 111 | const auto done_all = [&]{ 112 | std::lock_guard lock(inner_->mtx_); 113 | inner_->unsubscribers_.erase(serial); 114 | return inner_->unsubscribers_.size() == 0; 115 | }(); 116 | if(done_all){ 117 | inner_->subscriber_.on_completed(); 118 | finalize(); 119 | } 120 | } 121 | else{ 122 | finalize(); 123 | } 124 | } 125 | 126 | void sink_completed_force() const noexcept { 127 | if(inner_->subscriber_.is_subscribed()){ 128 | inner_->subscriber_.on_completed(); 129 | } 130 | finalize(); 131 | } 132 | 133 | void upstream_abort_observe(serial_type serial) const noexcept { 134 | const auto f = [&]{ 135 | std::lock_guard lock(inner_->mtx_); 136 | auto it = inner_->unsubscribers_.find(serial); 137 | if(it == inner_->unsubscribers_.end()){ 138 | return on_finalize_t(); 139 | } 140 | auto f = it->second; 141 | inner_->unsubscribers_.erase(it); 142 | return f; 143 | }(); 144 | if(f){ 145 | f(); 146 | } 147 | } 148 | 149 | void finalize() const noexcept { 150 | std::lock_guard lock(inner_->mtx_); 151 | for(auto it = inner_->unsubscribers_.begin(); it != inner_->unsubscribers_.end(); it++){ 152 | it->second(); 153 | } 154 | inner_->unsubscribers_.clear(); 155 | if(inner_->subscriber_.is_subscribed()){ 156 | inner_->subscriber_.unsubscribe(); 157 | } 158 | if(inner_->on_finalizes_ && *inner_->on_finalizes_){ 159 | (*inner_->on_finalizes_)(); 160 | } 161 | inner_->on_finalizes_.reset(); 162 | } 163 | 164 | bool is_subscribed() const noexcept { 165 | return inner_->subscriber_.is_subscribed(); 166 | } 167 | }; 168 | 169 | }} /* namespace another_rxcpp::internal */ 170 | #endif /* !defined(__another_rxcpp_h_stream_controller__) */ 171 | -------------------------------------------------------------------------------- /include/another-rxcpp/internal/tools/util.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_util__) 2 | #define __another_rxcpp_h_util__ 3 | 4 | #include 5 | #include 6 | 7 | namespace another_rxcpp { namespace internal { 8 | 9 | template struct strip_const_reference { 10 | using type = typename std::remove_const::type>::type; 11 | }; 12 | 13 | template 14 | struct lambda_traits_impl { 15 | using return_type = RET; 16 | using args = std::tuple; 17 | }; 18 | 19 | template 20 | struct lambda_traits : 21 | lambda_traits {}; 22 | 23 | template 24 | struct lambda_traits : 25 | lambda_traits_impl {}; 26 | 27 | template 28 | struct lambda_traits : 29 | lambda_traits_impl {}; 30 | 31 | template 32 | using lambda_invoke_result_t = typename lambda_traits::return_type; 33 | 34 | template 35 | using lambda_arg_t = typename std::tuple_element::args>::type; 36 | 37 | template 38 | inline auto to_weak(SP sp) noexcept { 39 | return std::weak_ptr(sp); 40 | } 41 | 42 | }} /* namespace another_rxcpp::internal */ 43 | 44 | #endif /* !defined(__another_rxcpp_h_util__) */ 45 | -------------------------------------------------------------------------------- /include/another-rxcpp/observable.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_observable__) 2 | #define __another_rxcpp_h_observable__ 3 | 4 | #include 5 | #include 6 | #include "observer.h" 7 | #include "subscription.h" 8 | #include "subscriber.h" 9 | #include "internal/tools/fn.h" 10 | 11 | namespace another_rxcpp { 12 | 13 | template class observable; 14 | 15 | template struct is_observable : std::false_type {}; 16 | template struct is_observable> : std::true_type {}; 17 | 18 | template <> class observable { 19 | public: 20 | template 21 | static auto create(const typename observable::source_t& f) noexcept 22 | { 23 | return observable( 24 | std::make_shared::source_t>(f) 25 | ); 26 | } 27 | 28 | template 29 | static auto create(typename observable::source_t&& f) noexcept 30 | { 31 | return observable( 32 | std::make_shared::source_t>(std::move(f)) 33 | ); 34 | } 35 | 36 | 37 | #if defined(SUPPORTS_RXCPP_COMPATIBLE) 38 | #include "internal/supports/observable_static_decl.inc" 39 | #endif /* defined(SUPPORTS_RXCPP_COMPATIBLE) */ 40 | }; 41 | 42 | 43 | template class observable { 44 | public: 45 | using value_type = T; 46 | using observer_type = observer; 47 | using subscriber_type = subscriber; 48 | using source_t = internal::fn; 49 | using source_sp = std::shared_ptr; 50 | using next_t = typename observer_type::next_t; 51 | using error_t = typename observer_type::error_t; 52 | using completed_t = typename observer_type::completed_t; 53 | 54 | private: 55 | source_sp source_; 56 | 57 | protected: 58 | observable() = default; 59 | 60 | public: 61 | observable(source_sp source) : source_(source) {} 62 | 63 | virtual subscription subscribe(observer_type ob) const noexcept { 64 | (*source_)(ob); 65 | return subscription( 66 | [ob] { 67 | ob.unsubscribe(); 68 | }, 69 | [ob] { 70 | return ob.is_subscribed(); 71 | } 72 | ); 73 | } 74 | 75 | subscription subscribe( 76 | const next_t& n = {}, 77 | const error_t& e = {}, 78 | const completed_t& c = {} 79 | ) const noexcept { 80 | return subscribe(observer(n, e, c)); 81 | } 82 | 83 | template 84 | subscription subscribe(N&& n, E&& e, C&& c) const noexcept 85 | { 86 | return subscribe(observer( 87 | std::forward(n), 88 | std::forward(e), 89 | std::forward(c) 90 | )); 91 | } 92 | 93 | 94 | template auto operator | (F&& f) const noexcept 95 | { 96 | /** 97 | * F = auto f(observable) 98 | * ex) 99 | * auto map(std::function) 100 | * -> std::function(observable)> 101 | **/ 102 | return f(*this); 103 | } 104 | 105 | #if defined(SUPPORTS_OPERATORS_IN_OBSERVABLE) 106 | #include "internal/supports/operators_in_observables_decl.inc" 107 | #endif /* defined(SUPPORTS_OPERATORS_IN_OBSERVABLE) */ 108 | 109 | #if defined(SUPPORTS_RXCPP_COMPATIBLE) 110 | auto as_dynamic() const noexcept -> observable 111 | { 112 | return *this; 113 | } 114 | #endif /* defined(SUPPORTS_RXCPP_COMPATIBLE) */ 115 | }; 116 | 117 | } /* namespace another_rxcpp */ 118 | 119 | #if defined(SUPPORTS_RXCPP_COMPATIBLE) 120 | #include "internal/supports/observable_static_impl.inc" 121 | #endif /* defined(SUPPORTS_RXCPP_COMPATIBLE) */ 122 | 123 | #if defined(SUPPORTS_OPERATORS_IN_OBSERVABLE) 124 | #include "internal/supports/operators_in_observables_impl.inc" 125 | #endif /* SUPPORTS_OPERATORS_IN_OBSERVABLE */ 126 | 127 | #endif /* !defined(__another_rxcpp_h_observable__) */ 128 | -------------------------------------------------------------------------------- /include/another-rxcpp/observables.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_observables__) 2 | #define __another_rxcpp_h_observables__ 3 | 4 | #include "observables/just.h" 5 | #include "observables/never.h" 6 | #include "observables/error.h" 7 | #include "observables/empty.h" 8 | #include "observables/range.h" 9 | #include "observables/connectable.h" 10 | #include "observables/interval.h" 11 | #include "observables/iterate.h" 12 | #include "observables/blocking.h" 13 | #include "observables/defer.h" 14 | 15 | #endif /* !defined(__another_rxcpp_h_observables__) */ -------------------------------------------------------------------------------- /include/another-rxcpp/observables/blocking.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_blocking_observable__) 2 | #define __another_rxcpp_h_blocking_observable__ 3 | 4 | #include "../observable.h" 5 | #include "../subjects/subject.h" 6 | #include "../operators/take.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace another_rxcpp { 14 | namespace observables { 15 | 16 | template class blocking; 17 | template class calculable_blocking; 18 | 19 | template <> class blocking { 20 | public: 21 | template < 22 | typename T, 23 | std::enable_if_t::value, bool> = true 24 | > 25 | static auto create(observable src) noexcept 26 | { 27 | return blocking(src); 28 | } 29 | 30 | template < 31 | typename T, 32 | std::enable_if_t::value, bool> = true 33 | > 34 | static auto create(observable src) noexcept 35 | { 36 | return calculable_blocking(src); 37 | } 38 | }; 39 | 40 | class empty_error : public std::runtime_error { 41 | public: 42 | empty_error(const std::string& msg) noexcept : runtime_error(msg) {} 43 | }; 44 | 45 | template class blocking : public observable { 46 | friend class blocking<>; 47 | public: 48 | using value_type = typename observable::value_type; 49 | using observer_type = typename observable::observer_type; 50 | using this_type = blocking; 51 | 52 | protected: 53 | mutable observable src_; 54 | mutable std::deque queue_; 55 | mutable std::exception_ptr error_; 56 | mutable bool subscribed_; 57 | 58 | blocking(observable src) noexcept : 59 | src_(src), error_(nullptr), subscribed_(false) {} 60 | 61 | void subscribe_all() const { 62 | if(subscribed_) return; 63 | subscribed_ = true; 64 | src_.subscribe( 65 | [&](const value_type& x){ 66 | queue_.push_back(x); 67 | }, 68 | [&](std::exception_ptr err){ 69 | error_ = err; 70 | }, 71 | []{} 72 | ); 73 | } 74 | 75 | public: 76 | 77 | virtual subscription subscribe(observer_type ob) const noexcept override { 78 | subscribe_all(); 79 | std::for_each(std::begin(queue_), std::end(queue_), [ob](auto&& x){ 80 | ob.on_next(x); 81 | }); 82 | if(error_){ 83 | ob.on_error(error_); 84 | } 85 | else{ 86 | ob.on_completed(); 87 | } 88 | return subscription( 89 | []{}, 90 | []{ 91 | return false; 92 | } 93 | ); 94 | } 95 | 96 | value_type first() const noexcept(false) { 97 | subscribe_all(); 98 | if(queue_.size() == 0){ 99 | if(error_){ 100 | std::rethrow_exception(error_); 101 | } 102 | else{ 103 | throw empty_error("empty"); 104 | } 105 | } 106 | else{ 107 | auto x = queue_.front(); 108 | queue_.pop_front(); 109 | return x; 110 | } 111 | } 112 | 113 | value_type last() const noexcept(false) { 114 | subscribe_all(); 115 | if(queue_.empty()){ 116 | if(error_){ 117 | std::rethrow_exception(error_); 118 | } 119 | else{ 120 | throw empty_error("empty"); 121 | } 122 | } 123 | else{ 124 | auto x = queue_.back(); 125 | queue_.pop_back(); 126 | return x; 127 | } 128 | } 129 | 130 | std::size_t count() const noexcept(false) { 131 | subscribe_all(); 132 | return queue_.size(); 133 | } 134 | }; 135 | 136 | template class calculable_blocking : public blocking 137 | { 138 | friend class blocking<>; 139 | 140 | public: 141 | using value_type = typename blocking::value_type; 142 | 143 | private: 144 | using base = blocking; 145 | 146 | calculable_blocking(observable src) noexcept : blocking(src) {} 147 | 148 | public: 149 | value_type sum() const noexcept(false) { 150 | base::subscribe_all(); 151 | if(base::error_) std::rethrow_exception(base::error_); 152 | else if(base::queue_.empty()) throw empty_error("empty"); 153 | return std::accumulate(std::cbegin(base::queue_), std::cend(base::queue_), 0); 154 | } 155 | 156 | double average() const noexcept(false) { 157 | base::subscribe_all(); 158 | if(base::error_) std::rethrow_exception(base::error_); 159 | else if(base::queue_.empty()) throw empty_error("empty"); 160 | auto sum = std::accumulate(std::cbegin(base::queue_), std::cend(base::queue_), 0); 161 | return static_cast(sum) / static_cast(base::queue_.size()); 162 | } 163 | 164 | value_type max() const noexcept(false) { 165 | base::subscribe_all(); 166 | if(base::error_) std::rethrow_exception(base::error_); 167 | else if(base::queue_.empty()) throw empty_error("empty"); 168 | return *std::max_element(std::cbegin(base::queue_), std::cend(base::queue_)); 169 | } 170 | 171 | value_type min() const noexcept(false) { 172 | base::subscribe_all(); 173 | if(base::error_) std::rethrow_exception(base::error_); 174 | else if(base::queue_.empty()) throw empty_error("empty"); 175 | return *std::min_element(std::cbegin(base::queue_), std::cend(base::queue_)); 176 | } 177 | }; 178 | 179 | } /* namespace observables */ 180 | } /* namespace another_rxcpp */ 181 | 182 | #endif /* !defined(__another_rxcpp_h_blocking_observable__) */ 183 | -------------------------------------------------------------------------------- /include/another-rxcpp/observables/connectable.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_connectable_observable__) 2 | #define __another_rxcpp_h_connectable_observable__ 3 | 4 | #include "../observable.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace another_rxcpp { 11 | namespace observables { 12 | 13 | template class connectable; 14 | 15 | template <> class connectable { 16 | public: 17 | template static auto create(observable src) noexcept { 18 | return connectable(src); 19 | } 20 | }; 21 | 22 | template class connectable : public observable { 23 | friend class connectable<>; 24 | public: 25 | using value_type = typename observable::value_type; 26 | using source_observable = observable; 27 | using observer_type = typename observable::observer_type; 28 | using serial_type = int32_t; 29 | using observer_map = std::unordered_map; 30 | 31 | private: 32 | struct member { 33 | source_observable source_; 34 | observer_map observers_; 35 | std::recursive_mutex mtx_; 36 | serial_type serial_; 37 | subscription subscription_; 38 | member(source_observable&& source): source_(std::move(source)), serial_(0) {} 39 | member(const source_observable& source): source_(source), serial_(0) {} 40 | }; 41 | std::shared_ptr m_; 42 | 43 | public: 44 | connectable() = default; /* do not use this normally! */ 45 | connectable(const source_observable& source) noexcept : 46 | m_(std::make_shared(source)) 47 | {} 48 | connectable(source_observable&& source) noexcept : 49 | m_(std::make_shared(std::move(source))) 50 | {} 51 | 52 | 53 | virtual subscription subscribe(observer_type ob) const noexcept override { 54 | auto m = m_; 55 | 56 | const auto serial = [&]{ 57 | std::lock_guard lock(m->mtx_); 58 | return m->serial_++; 59 | }(); 60 | 61 | m->observers_.insert({serial, ob}); 62 | 63 | return subscription( 64 | [m, serial]{ 65 | std::lock_guard lock(m->mtx_); 66 | auto it = m->observers_.find(serial); 67 | if(it != m->observers_.end()){ 68 | m->observers_.erase(it); 69 | // TODO: What should I do? 70 | // enable below if not forever 71 | // if(m->observers_.size() == 0){ 72 | // m->subscription_.unsubscribe(); 73 | // } 74 | } 75 | }, 76 | [m, serial]{ 77 | std::lock_guard lock(m->mtx_); 78 | auto it = m->observers_.find(serial); 79 | if(it != m->observers_.end()){ 80 | return it->second.is_subscribed(); 81 | } 82 | else{ 83 | return false; 84 | } 85 | } 86 | ); 87 | } 88 | 89 | subscription connect() const noexcept { 90 | auto m = m_; 91 | 92 | auto collect = [m](bool clear){ 93 | std::lock_guard lock(m->mtx_); 94 | std::vector ret; 95 | std::for_each(m->observers_.begin(), m->observers_.end(), [&ret](auto& it){ 96 | ret.push_back(it.second); 97 | }); 98 | if(clear) m->observers_.clear(); 99 | return ret; 100 | }; 101 | 102 | auto sbsc = m->source_.subscribe( 103 | [collect](const value_type& x) { 104 | auto obs = collect(false); 105 | std::for_each(obs.begin(), obs.end(), [&x](auto& ob){ 106 | ob.on_next(x); 107 | }); 108 | }, 109 | [collect](std::exception_ptr err){ 110 | auto obs = collect(true); 111 | std::for_each(obs.begin(), obs.end(), [&err](auto& ob){ 112 | ob.on_error(err); 113 | }); 114 | }, 115 | [collect](){ 116 | auto obs = collect(true); 117 | std::for_each(obs.begin(), obs.end(), [](auto& ob){ 118 | ob.on_completed(); 119 | }); 120 | } 121 | ); 122 | 123 | std::lock_guard lock(m->mtx_); 124 | auto sbsc2 = subscription( 125 | [sbsc, m]{ 126 | sbsc.unsubscribe(); 127 | std::lock_guard lock(m->mtx_); 128 | m->observers_.clear(); 129 | m->subscription_ = subscription(); 130 | }, 131 | [sbsc]{ 132 | return sbsc.is_subscribed(); 133 | } 134 | ); 135 | m->subscription_ = sbsc2; 136 | return sbsc2; 137 | } 138 | }; 139 | 140 | } /* namespace observables */ 141 | } /* namespace another_rxcpp */ 142 | 143 | #endif /* !defined(__another_rxcpp_h_connectable_observable__) */ 144 | -------------------------------------------------------------------------------- /include/another-rxcpp/observables/defer.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_defer__) 2 | #define __another_rxcpp_h_defer__ 3 | 4 | #include "../internal/tools/stream_controller.h" 5 | #include "../observable.h" 6 | #include "../schedulers/default_scheduler.h" 7 | 8 | namespace another_rxcpp { 9 | namespace observables { 10 | 11 | template 12 | inline auto defer(F&& fn, scheduler::creator_fn sccr = schedulers::default_scheduler()) noexcept 13 | -> decltype(fn()) 14 | { 15 | using Source = decltype(fn()); 16 | using Item = typename Source::value_type; 17 | return observable<>::create([fn, sccr](subscriber s) { 18 | auto sctl = internal::stream_controller(s); 19 | auto scdl = sccr(); 20 | sctl.set_on_finalize([scdl]{ 21 | scdl.abort(); 22 | }); 23 | scdl.schedule([fn, sctl]() { 24 | fn().subscribe(sctl.template new_observer( 25 | [sctl](auto, const Item& x){ 26 | sctl.sink_next(x); 27 | }, 28 | [sctl](auto, std::exception_ptr err){ 29 | sctl.sink_error(err); 30 | }, 31 | [sctl](auto serial) { 32 | sctl.sink_completed(serial); 33 | } 34 | )); 35 | }); 36 | }); 37 | } 38 | 39 | } /* namespace observables */ 40 | } /* namespace another_rxcpp */ 41 | 42 | #endif /* !defined(__another_rxcpp_h_defer__) */ -------------------------------------------------------------------------------- /include/another-rxcpp/observables/empty.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_empty__) 2 | #define __another_rxcpp_h_empty__ 3 | 4 | #include "../observable.h" 5 | 6 | namespace another_rxcpp { 7 | namespace observables { 8 | 9 | template 10 | inline auto empty() noexcept 11 | -> observable 12 | { 13 | return observable<>::create([](subscriber s){ 14 | s.on_completed(); 15 | }); 16 | } 17 | 18 | } /* namespace observables */ 19 | } /* namespace another_rxcpp */ 20 | 21 | #endif /* !defined(__another_rxcpp_h_empty__) */ -------------------------------------------------------------------------------- /include/another-rxcpp/observables/error.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_error__) 2 | #define __another_rxcpp_h_error__ 3 | 4 | #include "../observable.h" 5 | 6 | namespace another_rxcpp { 7 | namespace observables { 8 | 9 | 10 | template 11 | inline auto error(std::exception_ptr err) noexcept 12 | -> observable 13 | { 14 | return observable<>::create([err](subscriber s){ 15 | s.on_error(err); 16 | }); 17 | } 18 | 19 | template 20 | inline auto error(const ERR& err) 21 | -> observable 22 | { 23 | return error(std::make_exception_ptr(err)); 24 | } 25 | 26 | 27 | } /* namespace observables */ 28 | } /* namespace another_rxcpp */ 29 | 30 | #endif /* !defined(__another_rxcpp_h_error__) */ -------------------------------------------------------------------------------- /include/another-rxcpp/observables/interval.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_interval__) 2 | #define __another_rxcpp_h_interval__ 3 | 4 | #include "../observable.h" 5 | #include "../internal/tools/util.h" 6 | #include "../schedulers/default_scheduler.h" 7 | #include 8 | 9 | namespace another_rxcpp { 10 | namespace observables { 11 | 12 | template 13 | auto interval(std::chrono::milliseconds msec, scheduler::creator_fn sccr = schedulers::default_scheduler()) noexcept 14 | -> observable 15 | { 16 | return observable<>::create([msec, sccr](subscriber s){ 17 | auto scdl = sccr(); 18 | scdl.schedule([s, msec, scdl](){ 19 | T n = 1; 20 | while(s.is_subscribed()){ 21 | std::this_thread::sleep_for(msec); 22 | s.on_next(n); 23 | n++; 24 | } 25 | scdl.abort(); 26 | }); 27 | }); 28 | } 29 | 30 | } /* namespace observables */ 31 | } /* namespace another_rxcpp */ 32 | 33 | #endif /* !defined(__another_rxcpp_h_interval__) */ 34 | -------------------------------------------------------------------------------- /include/another-rxcpp/observables/iterate.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_iterate__) 2 | #define __another_rxcpp_h_iterate__ 3 | 4 | #include "../observable.h" 5 | #include "../internal/tools/util.h" 6 | 7 | namespace another_rxcpp { 8 | namespace observables { 9 | 10 | template 11 | auto iterate(T arr) noexcept 12 | -> observable 13 | { 14 | using TT = typename T::value_type; 15 | return observable<>::create([arr](subscriber s){ 16 | for(auto it = std::cbegin(arr); it != std::cend(arr); it++) { 17 | if(!s.is_subscribed()) return; 18 | s.on_next(*it); 19 | } 20 | s.on_completed(); 21 | }); 22 | } 23 | 24 | } /* namespace observables */ 25 | } /* namespace another_rxcpp */ 26 | 27 | #endif /* !defined(__another_rxcpp_h_iterate__) */ -------------------------------------------------------------------------------- /include/another-rxcpp/observables/just.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_just__) 2 | #define __another_rxcpp_h_just__ 3 | 4 | #include "../observable.h" 5 | #include "../internal/tools/util.h" 6 | #include "../schedulers/default_scheduler.h" 7 | 8 | namespace another_rxcpp { 9 | namespace observables { 10 | 11 | template 12 | inline auto just(T&& value, scheduler::creator_fn sccr = schedulers::default_scheduler()) noexcept 13 | -> observable::type> 14 | { 15 | using TT = typename internal::strip_const_reference::type; 16 | auto v = std::make_shared(std::forward(value)); 17 | return observable<>::create([v, sccr](subscriber s) { 18 | auto scdl = sccr(); 19 | scdl.schedule([v, s, scdl]() { 20 | s.on_next(*v); 21 | s.on_completed(); 22 | scdl.abort(); 23 | }); 24 | }); 25 | } 26 | 27 | } /* namespace observables */ 28 | } /* namespace another_rxcpp */ 29 | 30 | #endif /* !defined(__another_rxcpp_h_just__) */ -------------------------------------------------------------------------------- /include/another-rxcpp/observables/never.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_never__) 2 | #define __another_rxcpp_h_never__ 3 | 4 | #include "../observable.h" 5 | 6 | namespace another_rxcpp { 7 | namespace observables { 8 | 9 | template 10 | inline auto never() noexcept 11 | -> observable 12 | { 13 | return observable<>::create([](subscriber s){ 14 | }); 15 | } 16 | 17 | } /* namespace observables */ 18 | } /* namespace another_rxcpp */ 19 | 20 | #endif /* !defined(__another_rxcpp_h_never__) */ -------------------------------------------------------------------------------- /include/another-rxcpp/observables/range.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_range__) 2 | #define __another_rxcpp_h_range__ 3 | 4 | #include "../observable.h" 5 | #include "../internal/tools/util.h" 6 | 7 | namespace another_rxcpp { 8 | namespace observables { 9 | 10 | template 11 | auto range(T start, T end) noexcept 12 | -> observable::type> 13 | { 14 | using TT = typename internal::strip_const_reference::type; 15 | return observable<>::create([start, end](subscriber s){ 16 | for(TT i = start; i <= end; i++) { 17 | if(!s.is_subscribed()) return; 18 | s.on_next(i); 19 | } 20 | s.on_completed(); 21 | }); 22 | } 23 | 24 | } /* namespace observables */ 25 | } /* namespace another_rxcpp */ 26 | 27 | #endif /* !defined(__another_rxcpp_h_range__) */ -------------------------------------------------------------------------------- /include/another-rxcpp/observer.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_observer__) 2 | #define __another_rxcpp_h_observer__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "internal/tools/fn.h" 9 | #include "internal/tools/util.h" 10 | 11 | namespace another_rxcpp { 12 | 13 | template struct observer; 14 | 15 | namespace internal { 16 | template class stream_controller; 17 | } 18 | 19 | template <> struct observer { 20 | template using next_t = internal::fn; 21 | using error_t = internal::fn; 22 | using completed_t = internal::fn; 23 | using unsubscribe_t = internal::fn; 24 | }; 25 | 26 | template struct observer { 27 | using value_type = T; 28 | using next_t = observer<>::next_t; 29 | using error_t = observer<>::error_t; 30 | using completed_t = observer<>::completed_t; 31 | using unsubscribe_t = observer<>::unsubscribe_t; 32 | 33 | using next_sp = std::shared_ptr; 34 | using error_sp = std::shared_ptr; 35 | using completed_sp = std::shared_ptr; 36 | using unsubscribe_sp = std::shared_ptr; 37 | 38 | private: 39 | struct inner { 40 | std::recursive_mutex mtx_; 41 | next_sp next_; 42 | error_sp error_; 43 | completed_sp completed_; 44 | unsubscribe_sp unsubscribe_; 45 | std::atomic_bool is_subscribed_; 46 | inner() : is_subscribed_(true) {} 47 | }; 48 | 49 | mutable std::shared_ptr inner_; 50 | 51 | friend class internal::stream_controller; 52 | 53 | template void set_on_unsubscribe(Unsb&& f) const noexcept { 54 | inner_->unsubscribe_ = std::make_shared(std::forward(f)); 55 | } 56 | 57 | auto fetch_and_reset_all() const noexcept { 58 | std::lock_guard lock(inner_->mtx_); 59 | auto e = inner_->error_; 60 | auto c = inner_->completed_; 61 | auto u = inner_->unsubscribe_; 62 | inner_->next_.reset(); 63 | inner_->error_.reset(); 64 | inner_->completed_.reset(); 65 | inner_->unsubscribe_.reset(); 66 | return std::make_tuple(e, c, u); 67 | } 68 | 69 | void set_unsubscribed() const noexcept { 70 | inner_->is_subscribed_ = false; 71 | } 72 | 73 | public: 74 | template observer(N&& n, E&& e, C&& c) noexcept { 75 | inner_ = std::make_shared(); 76 | inner_->next_ = std::make_shared(std::forward(n)); 77 | inner_->error_ = std::make_shared(std::forward(e)); 78 | inner_->completed_ = std::make_shared(std::forward(c)); 79 | } 80 | 81 | observer(const next_t& n = {}, const error_t& e = {}, const completed_t& c = {}) noexcept { 82 | inner_ = std::make_shared(); 83 | inner_->next_ = std::make_shared(n); 84 | inner_->error_ = std::make_shared(e); 85 | inner_->completed_ = std::make_shared(c); 86 | } 87 | 88 | void on_next(const value_type& value) const noexcept { 89 | auto n = inner_->next_; 90 | if(n && *n) (*n)(value); 91 | } 92 | 93 | void on_next(value_type&& value) const noexcept { 94 | auto n = inner_->next_; 95 | if(n && *n) (*n)(std::move(value)); 96 | } 97 | 98 | void on_error(std::exception_ptr err) const noexcept { 99 | auto ecu = fetch_and_reset_all(); 100 | auto e = std::get<0>(ecu); 101 | auto u = std::get<2>(ecu); 102 | if(e && *e) (*e)(err); 103 | if(u && *u) (*u)(); 104 | set_unsubscribed(); 105 | } 106 | 107 | void on_completed() const noexcept { 108 | auto ecu = fetch_and_reset_all(); 109 | auto c = std::get<1>(ecu); 110 | auto u = std::get<2>(ecu); 111 | if(c && *c) (*c)(); 112 | if(u && *u) (*u)(); 113 | set_unsubscribed(); 114 | } 115 | 116 | void unsubscribe() const noexcept { 117 | auto ecu = fetch_and_reset_all(); 118 | auto u = std::get<2>(ecu); 119 | if(u && *u) (*u)(); 120 | set_unsubscribed(); 121 | } 122 | 123 | bool is_subscribed() const noexcept { 124 | return inner_->is_subscribed_; 125 | } 126 | }; 127 | 128 | } /* namespace another_rxcpp */ 129 | #endif /* !defined(__another_rxcpp_h_observable__) */ 130 | -------------------------------------------------------------------------------- /include/another-rxcpp/operators.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_operators__) 2 | #define __another_rxcpp_h_operators__ 3 | 4 | #include "operators/flat_map.h" 5 | #include "operators/map.h" 6 | #include "operators/take.h" 7 | #include "operators/publish.h" 8 | #include "operators/on_error_resume_next.h" 9 | #include "operators/retry.h" 10 | #include "operators/observe_on.h" 11 | #include "operators/skip_while.h" 12 | #include "operators/skip_until.h" 13 | #include "operators/subscribe_on.h" 14 | #include "operators/take_until.h" 15 | #include "operators/take_while.h" 16 | #include "operators/merge.h" 17 | #include "operators/amb.h" 18 | #include "operators/distinct_until_changed.h" 19 | #include "operators/delay.h" 20 | #include "operators/tap.h" 21 | #include "operators/finally.h" 22 | #include "operators/last.h" 23 | #include "operators/take_last.h" 24 | #include "operators/timeout.h" 25 | #include "operators/blocking.h" 26 | #include "operators/zip.h" 27 | #include "operators/first.h" 28 | #include "operators/filter.h" 29 | 30 | #include "operators/subscribe.h" 31 | 32 | #endif /* !defined(__another_rxcpp_h_operators__) */ -------------------------------------------------------------------------------- /include/another-rxcpp/operators/amb.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_amb__) 2 | #define __another_rxcpp_h_amb__ 3 | 4 | #include "../observable.h" 5 | #include "../internal/tools/util.h" 6 | #include "../internal/tools/stream_controller.h" 7 | #include "../schedulers/default_scheduler.h" 8 | #include 9 | #include 10 | 11 | namespace another_rxcpp { 12 | namespace operators { 13 | 14 | namespace amb_internal { 15 | template 16 | auto amb(scheduler::creator_fn sccr, std::vector>& arr, OB ob) noexcept { 17 | arr.push_back(ob); 18 | return [sccr, arr = std::make_shared>>(std::move(arr))](auto src) { 19 | arr->push_back(src); 20 | return observable<>::create([sccr, arr](subscriber s) { 21 | auto sctl = internal::stream_controller(s); 22 | using serial_type = typename internal::stream_controller::serial_type; 23 | auto mtx = std::make_shared(); 24 | auto winner = std::make_shared>(); 25 | auto is_win_next = [winner, mtx](serial_type serial) { 26 | std::lock_guard lock(*mtx); 27 | if(*winner) { 28 | return **winner == serial; 29 | } 30 | *winner = std::make_shared(serial); 31 | return true; 32 | }; 33 | auto scdl = sccr(); 34 | sctl.set_on_finalize([scdl]{ 35 | scdl.abort(); 36 | }); 37 | 38 | // prepare subscribers 39 | auto subscribers = [sctl, scdl, arr, is_win_next] { 40 | auto re = std::vector>(); 41 | for(auto i = 0; i < arr->size(); i++){ 42 | re.push_back( 43 | sctl.template new_observer( 44 | [sctl, scdl, is_win_next](auto serial, const T& x) { 45 | if(is_win_next(serial)) { 46 | scdl.schedule([sctl, x]{ 47 | sctl.sink_next(x); 48 | }); 49 | } 50 | else{ 51 | sctl.upstream_abort_observe(serial); 52 | } 53 | }, 54 | [sctl, scdl, is_win_next](auto serial, std::exception_ptr err) { 55 | if(is_win_next(serial)) { 56 | scdl.schedule([sctl, err]{ 57 | sctl.sink_error(err); 58 | }); 59 | } 60 | else{ 61 | sctl.upstream_abort_observe(serial); 62 | } 63 | }, 64 | [sctl, scdl, is_win_next](auto serial) { 65 | if(is_win_next(serial)) { 66 | scdl.schedule([sctl, serial]{ 67 | sctl.sink_completed(serial); 68 | }); 69 | } 70 | else{ 71 | sctl.upstream_abort_observe(serial); 72 | } 73 | } 74 | ) 75 | ); 76 | } 77 | return re; 78 | }(); 79 | 80 | for(auto i = 0; i < arr->size(); i++){ 81 | (*arr)[i].subscribe(subscribers[i]); 82 | } 83 | }); 84 | }; 85 | } 86 | 87 | template 88 | auto amb(scheduler::creator_fn sccr, std::vector>& arr, OB ob, ARGS...args) noexcept { 89 | arr.push_back(ob); 90 | return amb(sccr, arr, args...); 91 | } 92 | } /* namespace amb_internal */ 93 | 94 | template ::value, bool> = true> 95 | auto amb(OB ob, ARGS...args) noexcept { 96 | using T = typename OB::value_type; 97 | std::vector> arr; 98 | return amb_internal::amb(schedulers::default_scheduler(), arr, ob, args...); 99 | } 100 | 101 | template 102 | auto amb(scheduler::creator_fn sccr, OB ob, ARGS...args) noexcept { 103 | using T = typename OB::value_type; 104 | std::vector> arr; 105 | return amb_internal::amb(sccr, arr, ob, args...); 106 | } 107 | 108 | } /* namespace operators */ 109 | } /* namespace another_rxcpp */ 110 | 111 | #endif /* !defined(__another_rxcpp_h_amb__) */ -------------------------------------------------------------------------------- /include/another-rxcpp/operators/blocking.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_blocking__) 2 | #define __another_rxcpp_h_blocking__ 3 | 4 | #include "../observables/blocking.h" 5 | 6 | namespace another_rxcpp { 7 | namespace operators { 8 | 9 | inline auto blocking() noexcept 10 | { 11 | return [](auto src){ 12 | using OUT_OB = decltype(src); 13 | using OUT = typename OUT_OB::value_type; 14 | return observables::blocking<>::create(src); 15 | }; 16 | } 17 | 18 | } /* namespace operators */ 19 | } /* namespace another_rxcpp */ 20 | 21 | #endif /* !defined(__another_rxcpp_h_blocking__) */ -------------------------------------------------------------------------------- /include/another-rxcpp/operators/delay.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_delay__) 2 | #define __another_rxcpp_h_delay__ 3 | 4 | #include "../observable.h" 5 | #include "../scheduler.h" 6 | #include "../schedulers/default_scheduler.h" 7 | #include "../internal/tools/util.h" 8 | #include "../internal/tools/stream_controller.h" 9 | 10 | #include 11 | #include 12 | 13 | namespace another_rxcpp { 14 | namespace operators { 15 | 16 | inline auto delay(std::chrono::milliseconds msec, scheduler::creator_fn sccr = schedulers::default_scheduler()) noexcept 17 | { 18 | return [msec, sccr](auto source){ 19 | using Source = decltype(source); 20 | using Item = typename Source::value_type; 21 | return observable<>::create([source, msec, sccr](subscriber s) { 22 | auto sctl = internal::stream_controller(s); 23 | auto scdl = sccr(); 24 | sctl.set_on_finalize([scdl]{ 25 | scdl.abort(); 26 | }); 27 | scdl.schedule([source, msec, sctl](){ 28 | source.subscribe(sctl.template new_observer( 29 | [sctl, msec](auto, const Item& x){ 30 | std::this_thread::sleep_for(msec); 31 | sctl.sink_next(x); 32 | }, 33 | [sctl](auto, std::exception_ptr err){ 34 | sctl.sink_error(err); 35 | }, 36 | [sctl](auto serial) { 37 | sctl.sink_completed(serial); 38 | } 39 | )); 40 | }); 41 | }); 42 | }; 43 | } 44 | 45 | } /* namespace operators */ 46 | } /* namespace another_rxcpp */ 47 | 48 | #endif /* !defined(__another_rxcpp_h_delay__) */ 49 | -------------------------------------------------------------------------------- /include/another-rxcpp/operators/distinct_until_changed.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_distinct_until_changed__) 2 | #define __another_rxcpp_h_distinct_until_changed__ 3 | 4 | #include "../internal/tools/stream_controller.h" 5 | #include "../observable.h" 6 | 7 | namespace another_rxcpp { 8 | namespace operators { 9 | 10 | inline auto distinct_until_changed() noexcept 11 | { 12 | return [](auto source){ 13 | using Source = decltype(source); 14 | using Item = typename Source::value_type; 15 | return observable<>::create([source](subscriber s) { 16 | auto sctl = internal::stream_controller(s); 17 | 18 | auto mtx = std::make_shared(); 19 | auto last_value = std::make_shared>(); 20 | 21 | source.subscribe(sctl.template new_observer( 22 | [sctl, mtx, last_value](auto, const Item& x){ 23 | const bool bNext = [&](){ 24 | std::lock_guard lock(*mtx); 25 | if(!*last_value){ 26 | *last_value = std::make_shared(x); 27 | return true; 28 | } 29 | if(**last_value != x){ 30 | **last_value = x; 31 | return true; 32 | } 33 | return false; 34 | }(); 35 | if(bNext){ 36 | sctl.sink_next(x); 37 | } 38 | }, 39 | [sctl](auto, std::exception_ptr err){ 40 | sctl.sink_error(err); 41 | }, 42 | [sctl](auto serial) { 43 | sctl.sink_completed(serial); 44 | } 45 | )); 46 | }); 47 | }; 48 | } 49 | 50 | } /* namespace operators */ 51 | } /* namespace another_rxcpp */ 52 | 53 | #endif /* !defined(__another_rxcpp_h_distinct_until_changed__) */ -------------------------------------------------------------------------------- /include/another-rxcpp/operators/filter.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_filter__) 2 | #define __another_rxcpp_h_filter__ 3 | 4 | #include "../internal/tools/stream_controller.h" 5 | #include "../observable.h" 6 | 7 | namespace another_rxcpp { 8 | namespace operators { 9 | 10 | template auto filter(F f) noexcept 11 | { 12 | return [f](auto source){ 13 | using Source = decltype(source); 14 | using Item = typename Source::value_type; 15 | return observable<>::create([source, f](subscriber s) { 16 | auto sctl = internal::stream_controller(s); 17 | 18 | source.subscribe(sctl.template new_observer( 19 | [sctl, f](auto, const Item& x){ 20 | try{ 21 | if(f(x)){ 22 | sctl.sink_next(x); 23 | } 24 | } 25 | catch(...){ 26 | sctl.sink_error(std::current_exception()); 27 | } 28 | }, 29 | [sctl](auto, std::exception_ptr err){ 30 | sctl.sink_error(err); 31 | }, 32 | [sctl](auto serial) { 33 | sctl.sink_completed(serial); 34 | } 35 | )); 36 | }); 37 | }; 38 | } 39 | 40 | } /* namespace operators */ 41 | } /* namespace another_rxcpp */ 42 | 43 | #endif /* !defined(__another_rxcpp_h_filter__) */ -------------------------------------------------------------------------------- /include/another-rxcpp/operators/finally.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_finally__) 2 | #define __another_rxcpp_h_finally__ 3 | 4 | #include "../internal/tools/stream_controller.h" 5 | #include "../observable.h" 6 | 7 | namespace another_rxcpp { 8 | namespace operators { 9 | 10 | template auto finally(F f) noexcept 11 | { 12 | return [f](auto source){ 13 | using Source = decltype(source); 14 | using Item = typename Source::value_type; 15 | return observable<>::create([source, f](subscriber s) { 16 | auto sctl = internal::stream_controller(s); 17 | source.subscribe(sctl.template new_observer( 18 | [sctl](auto, const Item& x){ 19 | sctl.sink_next(x); 20 | }, 21 | [sctl, f](auto, std::exception_ptr err){ 22 | sctl.sink_error(err); 23 | f(); 24 | }, 25 | [sctl, f](auto serial) { 26 | sctl.sink_completed(serial); 27 | f(); 28 | } 29 | )); 30 | }); 31 | }; 32 | } 33 | 34 | } /* namespace operators */ 35 | } /* namespace another_rxcpp */ 36 | 37 | #endif /* !defined(__another_rxcpp_h_finally__) */ 38 | -------------------------------------------------------------------------------- /include/another-rxcpp/operators/first.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_first__) 2 | #define __another_rxcpp_h_first__ 3 | 4 | #include "../observable.h" 5 | #include "take.h" 6 | 7 | namespace another_rxcpp { 8 | namespace operators { 9 | 10 | inline auto first() noexcept 11 | { 12 | return take(1); 13 | } 14 | 15 | } /* namespace operators */ 16 | } /* namespace another_rxcpp */ 17 | 18 | #endif /* !defined(__another_rxcpp_h_first__) */ 19 | -------------------------------------------------------------------------------- /include/another-rxcpp/operators/flat_map.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_flat_map__) 2 | #define __another_rxcpp_h_flat_map__ 3 | 4 | #include "../internal/tools/stream_controller.h" 5 | #include "../internal/tools/util.h" 6 | #include "../observable.h" 7 | 8 | namespace another_rxcpp { 9 | namespace operators { 10 | 11 | template auto flat_map(F f) noexcept 12 | { 13 | using OutObs = internal::lambda_invoke_result_t; 14 | using Out = typename OutObs::value_type; 15 | return [f](auto source){ 16 | return observable<>::create([source, f](auto s) { 17 | using Source = decltype(source); 18 | using In = typename Source::value_type; 19 | 20 | auto sctl = internal::stream_controller(s); 21 | 22 | source.subscribe(sctl.template new_observer( 23 | [sctl, f](auto, const In& x) { 24 | try{ 25 | f(x).subscribe(sctl.template new_observer( 26 | [sctl](auto, const Out& x){ 27 | sctl.sink_next(x); 28 | }, 29 | [sctl](auto, std::exception_ptr err){ 30 | sctl.sink_error(err); 31 | }, 32 | [sctl](auto serial) { 33 | sctl.sink_completed(serial); 34 | } 35 | )); 36 | } 37 | catch(...){ 38 | sctl.sink_error(std::current_exception()); 39 | } 40 | }, 41 | [sctl](auto, std::exception_ptr err){ 42 | sctl.sink_error(err); 43 | }, 44 | [sctl](auto serial){ 45 | sctl.sink_completed(serial); 46 | } 47 | )); 48 | }); 49 | }; 50 | } 51 | 52 | } /* namespace operators */ 53 | } /* namespace another_rxcpp */ 54 | 55 | #endif /* !defined(__another_rxcpp_h_flat_map__) */ -------------------------------------------------------------------------------- /include/another-rxcpp/operators/last.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_last__) 2 | #define __another_rxcpp_h_last__ 3 | 4 | #include "../observable.h" 5 | #include "take_last.h" 6 | 7 | namespace another_rxcpp { 8 | namespace operators { 9 | 10 | inline auto last() noexcept 11 | { 12 | return take_last(1); 13 | } 14 | 15 | } /* namespace operators */ 16 | } /* namespace another_rxcpp */ 17 | 18 | #endif /* !defined(__another_rxcpp_h_last__) */ 19 | -------------------------------------------------------------------------------- /include/another-rxcpp/operators/map.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_map__) 2 | #define __another_rxcpp_h_map__ 3 | 4 | #include "../internal/tools/stream_controller.h" 5 | #include "../internal/tools/util.h" 6 | #include "../observable.h" 7 | 8 | namespace another_rxcpp { namespace operators { 9 | 10 | template auto map(F f) noexcept 11 | { 12 | using Out = internal::lambda_invoke_result_t; 13 | 14 | return [f](auto source){ 15 | using Source = decltype(source); 16 | using In = typename Source::value_type; 17 | return observable<>::create([source, f](auto s){ 18 | auto sctl = internal::stream_controller(s); 19 | 20 | source.subscribe(sctl.template new_observer( 21 | [sctl, f](auto, const In& x) { 22 | try{ 23 | sctl.sink_next(f(x)); 24 | } 25 | catch(...){ 26 | sctl.sink_error(std::current_exception()); 27 | } 28 | }, 29 | [sctl](auto, std::exception_ptr err){ 30 | sctl.sink_error(err); 31 | }, 32 | [sctl](auto serial){ 33 | sctl.sink_completed(serial); 34 | } 35 | )); 36 | }); 37 | }; 38 | } 39 | 40 | }} /* namespace another_rxcpp::operators */ 41 | #endif /* !defined(__another_rxcpp_h_map__) */ 42 | -------------------------------------------------------------------------------- /include/another-rxcpp/operators/merge.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_merge__) 2 | #define __another_rxcpp_h_merge__ 3 | 4 | #include "../observable.h" 5 | #include "../internal/tools/util.h" 6 | #include "../internal/tools/stream_controller.h" 7 | #include "../schedulers/default_scheduler.h" 8 | #include 9 | #include 10 | 11 | namespace another_rxcpp { 12 | namespace operators { 13 | 14 | namespace merge_internal { 15 | template 16 | auto merge(scheduler::creator_fn sccr, std::vector>& arr, OB ob) noexcept { 17 | arr.push_back(ob); 18 | return [sccr, arr = std::make_shared>>(std::move(arr))](auto src) { 19 | arr->push_back(src); 20 | return observable<>::create([sccr, arr](subscriber s) { 21 | auto sctl = internal::stream_controller(s); 22 | auto scdl = sccr(); 23 | sctl.set_on_finalize([scdl]{ 24 | scdl.abort(); 25 | }); 26 | 27 | // prepare subscribers 28 | auto subscribers = [sctl, scdl, arr]{ 29 | auto re = std::vector>(); 30 | for(auto i = 0; i < arr->size(); i++){ 31 | re.push_back( 32 | sctl.template new_observer( 33 | [sctl, scdl](auto, const T& x) { 34 | scdl.schedule([sctl, x]{ 35 | sctl.sink_next(x); 36 | }); 37 | }, 38 | [sctl, scdl](auto, std::exception_ptr err){ 39 | scdl.schedule([sctl, err]{ 40 | sctl.sink_error(err); 41 | }); 42 | }, 43 | [sctl, scdl](auto serial){ 44 | scdl.schedule([sctl, serial]{ 45 | sctl.sink_completed(serial); 46 | }); 47 | } 48 | ) 49 | ); 50 | } 51 | return re; 52 | }(); 53 | 54 | for(auto i = 0; i < arr->size(); i++){ 55 | (*arr)[i].subscribe(subscribers[i]); 56 | } 57 | }); 58 | }; 59 | } 60 | 61 | template 62 | auto merge(scheduler::creator_fn sccr, std::vector>& arr, OB ob, ARGS...args){ 63 | arr.push_back(ob); 64 | return merge(sccr, arr, args...); 65 | } 66 | } /* merge_internal */ 67 | 68 | template ::value, bool> = true> 69 | auto merge(OB ob, ARGS...args) { 70 | using T = typename OB::value_type; 71 | std::vector> arr; 72 | return merge_internal::merge(schedulers::default_scheduler(), arr, ob, args...); 73 | } 74 | 75 | template 76 | auto merge(scheduler::creator_fn sccr, OB ob, ARGS...args) { 77 | using T = typename OB::value_type; 78 | std::vector> arr; 79 | return merge_internal::merge(sccr, arr, ob, args...); 80 | } 81 | 82 | } /* namespace operators */ 83 | } /* namespace another_rxcpp */ 84 | 85 | #endif /* !defined(__another_rxcpp_h_merge__) */ -------------------------------------------------------------------------------- /include/another-rxcpp/operators/observe_on.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_observe_on__) 2 | #define __another_rxcpp_h_observe_on__ 3 | 4 | #include "../internal/tools/stream_controller.h" 5 | #include "../observable.h" 6 | #include "../scheduler.h" 7 | 8 | namespace another_rxcpp { 9 | namespace operators { 10 | 11 | inline auto observe_on(scheduler::creator_fn sccr) noexcept 12 | { 13 | return [sccr](auto source){ 14 | using Source = decltype(source); 15 | using Item = typename Source::value_type; 16 | return observable<>::create([source, sccr](subscriber s) { 17 | auto sctl = internal::stream_controller(s); 18 | auto scdl = sccr(); 19 | sctl.set_on_finalize([scdl]{ 20 | scdl.abort(); 21 | }); 22 | source.subscribe(sctl.template new_observer( 23 | [sctl, scdl](auto, const Item& x) { 24 | scdl.schedule([sctl, x]() mutable { 25 | sctl.sink_next(std::move(x)); 26 | }); 27 | }, 28 | [sctl, scdl](auto, std::exception_ptr err){ 29 | scdl.schedule([sctl, err]() { 30 | sctl.sink_error(err); 31 | }); 32 | }, 33 | [sctl, scdl](auto serial){ 34 | scdl.schedule([sctl, serial]() { 35 | sctl.sink_completed(serial); 36 | }); 37 | } 38 | )); 39 | }); 40 | }; 41 | } 42 | 43 | } /* namespace operators */ 44 | } /* namespace another_rxcpp */ 45 | 46 | #endif /* !defined(__another_rxcpp_h_observe_on__) */ -------------------------------------------------------------------------------- /include/another-rxcpp/operators/on_error_resume_next.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_on_error_resume_next__) 2 | #define __another_rxcpp_h_on_error_resume_next__ 3 | 4 | #include "../internal/tools/stream_controller.h" 5 | #include "../observable.h" 6 | 7 | namespace another_rxcpp { 8 | namespace operators { 9 | 10 | template auto on_error_resume_next(NEXT_FN f) noexcept 11 | { 12 | return [f](auto source){ 13 | using Source = decltype(source); 14 | using Item = typename Source::value_type; 15 | return observable<>::create([source, f](subscriber s) { 16 | auto sctl = internal::stream_controller(s); 17 | 18 | source.subscribe(sctl.template new_observer( 19 | [sctl](auto, const Item& x) { 20 | sctl.sink_next(x); 21 | }, 22 | [sctl, f](auto serial, std::exception_ptr err){ 23 | sctl.upstream_abort_observe(serial); 24 | try{ 25 | f(err).subscribe(sctl.template new_observer( 26 | [sctl](auto, const Item& x){ 27 | sctl.sink_next(x); 28 | }, 29 | [sctl](auto, std::exception_ptr err){ 30 | sctl.sink_error(err); 31 | }, 32 | [sctl](auto serial) { 33 | sctl.sink_completed(serial); 34 | } 35 | )); 36 | } 37 | catch(...){ 38 | sctl.sink_error(std::current_exception()); 39 | } 40 | }, 41 | [sctl](auto serial){ 42 | sctl.sink_completed(serial); 43 | } 44 | )); 45 | }); 46 | }; 47 | } 48 | 49 | } /* namespace operators */ 50 | } /* namespace another_rxcpp */ 51 | 52 | #endif /* !defined(__another_rxcpp_h_on_error_resume_next__) */ -------------------------------------------------------------------------------- /include/another-rxcpp/operators/publish.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_publish__) 2 | #define __another_rxcpp_h_publish__ 3 | 4 | #include "../observables/connectable.h" 5 | 6 | namespace another_rxcpp { 7 | namespace operators { 8 | 9 | inline auto publish() noexcept 10 | { 11 | return [](auto source){ 12 | using Source = decltype(source); 13 | using Out = typename Source::value_type; 14 | return observables::connectable<>::create(source); 15 | }; 16 | } 17 | 18 | } /* namespace operators */ 19 | } /* namespace another_rxcpp */ 20 | 21 | #endif /* !defined(__another_rxcpp_h_publish__) */ -------------------------------------------------------------------------------- /include/another-rxcpp/operators/retry.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_retry__) 2 | #define __another_rxcpp_h_retry__ 3 | 4 | #include "../internal/tools/stream_controller.h" 5 | #include "../observable.h" 6 | 7 | namespace another_rxcpp { 8 | namespace operators { 9 | 10 | namespace retry_internal { 11 | 12 | template 13 | void do_retry( 14 | internal::stream_controller sctl, 15 | observable source, 16 | const std::size_t max_retry_count, 17 | const std::size_t count 18 | ) noexcept 19 | { 20 | 21 | source.subscribe(sctl.template new_observer( 22 | [sctl](auto, const Item& x){ 23 | sctl.sink_next(x); 24 | }, 25 | [sctl, source, max_retry_count, count](auto serial, std::exception_ptr err){ 26 | sctl.upstream_abort_observe(serial); 27 | if(max_retry_count == 0 || count < max_retry_count){ 28 | do_retry(sctl, source, max_retry_count, count + 1); 29 | } 30 | else{ 31 | sctl.sink_error(err); 32 | } 33 | }, 34 | [sctl](auto serial){ 35 | sctl.sink_completed(serial); 36 | } 37 | )); 38 | } 39 | 40 | } /* namespace retry_internal */ 41 | 42 | inline auto retry(std::size_t max_retry_count = 0 /* infinite */) 43 | { 44 | return [max_retry_count](auto source){ 45 | using Source = decltype(source); 46 | using Item = typename Source::value_type; 47 | 48 | return observable<>::create([source, max_retry_count](subscriber s) { 49 | auto sctl = internal::stream_controller(s); 50 | retry_internal::do_retry(sctl, source, max_retry_count, 0); 51 | }); 52 | }; 53 | } 54 | 55 | } /* namespace operators */ 56 | } /* namespace another_rxcpp */ 57 | 58 | #endif /* !defined(__another_rxcpp_h_retry__) */ -------------------------------------------------------------------------------- /include/another-rxcpp/operators/skip_until.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_skip_until__) 2 | #define __another_rxcpp_h_skip_until__ 3 | 4 | #include "../observable.h" 5 | #include "../internal/tools/stream_controller.h" 6 | 7 | namespace another_rxcpp { 8 | namespace operators { 9 | 10 | template auto skip_until(TRIGGER_OB trigger) noexcept 11 | { 12 | return [trigger](auto source) { 13 | using Source = decltype(source); 14 | using Item = typename Source::value_type; 15 | using TriggerItem = typename TRIGGER_OB::value_type; 16 | return observable<>::create([source, trigger](subscriber s) { 17 | auto sctl = internal::stream_controller(s); 18 | auto enable = std::make_shared(false); 19 | 20 | auto obs_trigger = sctl.template new_observer( 21 | [sctl, enable](auto serial, auto) { 22 | sctl.upstream_abort_observe(serial); 23 | (*enable) = true; 24 | }, 25 | [sctl](auto, std::exception_ptr err){ 26 | sctl.sink_error(err); 27 | }, 28 | [sctl](auto serial){ 29 | sctl.sink_completed(serial); 30 | } 31 | ); 32 | 33 | auto obs_source = sctl.template new_observer( 34 | [sctl, enable](auto, const Item& x) { 35 | if(*enable){ 36 | sctl.sink_next(x); 37 | } 38 | }, 39 | [sctl](auto, std::exception_ptr err){ 40 | sctl.sink_error(err); 41 | }, 42 | [sctl](auto serial){ 43 | sctl.sink_completed_force(); // trigger also unsubscribe 44 | } 45 | ); 46 | 47 | trigger.subscribe(obs_trigger); 48 | source.subscribe(obs_source); 49 | }); 50 | }; 51 | } 52 | 53 | } /* namespace operators */ 54 | } /* namespace another_rxcpp */ 55 | 56 | #endif /* !defined(__another_rxcpp_h_skip_until__) */ -------------------------------------------------------------------------------- /include/another-rxcpp/operators/skip_while.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_skip_while__) 2 | #define __another_rxcpp_h_skip_while__ 3 | 4 | #include "../observable.h" 5 | #include "../internal/tools/util.h" 6 | #include "../internal/tools/stream_controller.h" 7 | 8 | namespace another_rxcpp { 9 | namespace operators { 10 | 11 | template auto skip_while(F f) noexcept 12 | { 13 | return [f](auto source){ 14 | using Source = decltype(source); 15 | using Item = typename Source::value_type; 16 | return observable<>::create([source, f](subscriber s) { 17 | auto sctl = internal::stream_controller(s); 18 | auto skip = std::make_shared(true); 19 | source.subscribe(sctl.template new_observer( 20 | [sctl, f, skip](auto, const Item& x){ 21 | try{ 22 | if(*skip){ 23 | *skip = f(x); 24 | } 25 | if(!*skip){ 26 | sctl.sink_next(x); 27 | } 28 | } 29 | catch(...){ 30 | sctl.sink_error(std::current_exception()); 31 | } 32 | }, 33 | [sctl](auto, std::exception_ptr err){ 34 | sctl.sink_error(err); 35 | }, 36 | [sctl](auto serial){ 37 | sctl.sink_completed(serial); 38 | } 39 | )); 40 | }); 41 | }; 42 | } 43 | 44 | } /* namespace operators */ 45 | } /* namespace another_rxcpp */ 46 | 47 | #endif /* !defined(__another_rxcpp_h_skip_while__) */ -------------------------------------------------------------------------------- /include/another-rxcpp/operators/subscribe.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_subscribe__) 2 | #define __another_rxcpp_h_subscribe__ 3 | 4 | #include "../observer.h" 5 | 6 | namespace another_rxcpp { 7 | namespace operators { 8 | 9 | template 10 | auto subscribe(const observer& ob) 11 | { 12 | return [ob](auto&& src) mutable /* for move */ { 13 | return src.subscribe(std::move(ob)); 14 | }; 15 | } 16 | 17 | template 18 | auto subscribe(observer&& ob) 19 | { 20 | return [ob = std::move(ob)](auto&& src) mutable /* for move */ { 21 | return src.subscribe(std::move(ob)); 22 | }; 23 | } 24 | 25 | template 26 | auto subscribe( 27 | ON_NEXT&& next, 28 | const observer<>::error_t& error, 29 | const observer<>::completed_t& completed) noexcept 30 | { 31 | return [next = std::forward(next), error, completed](auto&& src) mutable /* for move */ { 32 | using src_type = typename internal::strip_const_reference::type; 33 | using observer_type = typename src_type::observer_type; 34 | return src.subscribe({ 35 | std::move(next), 36 | std::move(error), 37 | std::move(completed) 38 | }); 39 | }; 40 | } 41 | 42 | } /* namespace operators */ 43 | } /* namespace another_rxcpp */ 44 | 45 | #endif /* !defined(__another_rxcpp_h_subscribe__) */ -------------------------------------------------------------------------------- /include/another-rxcpp/operators/subscribe_on.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_subscribe_on__) 2 | #define __another_rxcpp_h_subscribe_on__ 3 | 4 | #include "../observable.h" 5 | #include "../scheduler.h" 6 | #include "../internal/tools/stream_controller.h" 7 | 8 | namespace another_rxcpp { 9 | namespace operators { 10 | 11 | inline auto subscribe_on(scheduler::creator_fn sccr) noexcept 12 | { 13 | return [sccr](auto source){ 14 | using Source = decltype(source); 15 | using Item = typename Source::value_type; 16 | return observable<>::create([source, sccr](subscriber s) { 17 | auto sctl = internal::stream_controller(s); 18 | auto scdl = sccr(); 19 | sctl.set_on_finalize([scdl]{ 20 | scdl.abort(); 21 | }); 22 | scdl.schedule([sctl, source]() { 23 | source.subscribe(sctl.template new_observer( 24 | [sctl](auto, const Item& x) { 25 | sctl.sink_next(x); 26 | }, 27 | [sctl](auto, std::exception_ptr err){ 28 | sctl.sink_error(err); 29 | }, 30 | [sctl](auto serial){ 31 | sctl.sink_completed(serial); 32 | } 33 | )); 34 | }); 35 | }); 36 | }; 37 | } 38 | 39 | } /* namespace operators */ 40 | } /* namespace another_rxcpp */ 41 | 42 | #endif /* !defined(__another_rxcpp_h_subscribe_on__) */ -------------------------------------------------------------------------------- /include/another-rxcpp/operators/take.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_take__) 2 | #define __another_rxcpp_h_take__ 3 | 4 | #include "../internal/tools/stream_controller.h" 5 | #include "../observable.h" 6 | #include 7 | 8 | namespace another_rxcpp { 9 | namespace operators { 10 | 11 | inline auto take(std::size_t n) noexcept 12 | { 13 | return [n](auto source){ 14 | using Source = decltype(source); 15 | using Item = typename Source::value_type; 16 | return observable<>::create([source, n](subscriber s) { 17 | auto sctl = internal::stream_controller(s); 18 | auto counter = std::make_shared(1); 19 | 20 | source.subscribe(sctl.template new_observer( 21 | [sctl, n, counter](auto serial, const Item& x) { 22 | const auto now = counter->fetch_add(1); 23 | if(now == n) { 24 | sctl.upstream_abort_observe(serial); 25 | sctl.sink_next(x); 26 | sctl.sink_completed(serial); 27 | } 28 | else if(now < n){ 29 | sctl.sink_next(x); 30 | } 31 | }, 32 | [sctl](auto, std::exception_ptr err){ 33 | sctl.sink_error(err); 34 | }, 35 | [sctl](auto serial){ 36 | sctl.sink_completed(serial); 37 | } 38 | )); 39 | }); 40 | }; 41 | } 42 | 43 | } /* namespace operators */ 44 | } /* namespace another_rxcpp */ 45 | 46 | #endif /* !defined(__another_rxcpp_h_take__) */ -------------------------------------------------------------------------------- /include/another-rxcpp/operators/take_last.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_take_last__) 2 | #define __another_rxcpp_h_take_last__ 3 | 4 | #include "../observable.h" 5 | #include "../internal/tools/util.h" 6 | #include "../internal/tools/stream_controller.h" 7 | #include 8 | #include 9 | 10 | namespace another_rxcpp { 11 | namespace operators { 12 | 13 | inline auto take_last(std::size_t n) noexcept 14 | { 15 | return [n](auto source){ 16 | using Source = decltype(source); 17 | using Item = typename Source::value_type; 18 | return observable<>::create([source, n](subscriber s) { 19 | auto sctl = internal::stream_controller(s); 20 | auto mtx = std::make_shared(); 21 | auto arr = std::make_shared>(); 22 | source.subscribe(sctl.template new_observer( 23 | [n, mtx, arr](auto, const Item& x) { 24 | std::lock_guard lock(*mtx); 25 | arr->push(x); 26 | if(arr->size() > n) arr->pop(); 27 | }, 28 | [sctl](auto, std::exception_ptr err){ 29 | sctl.sink_error(err); 30 | }, 31 | [sctl, n, mtx, arr](auto serial){ 32 | { 33 | /** Should I emit even with a small number of elements? */ 34 | std::lock_guard lock(*mtx); 35 | while(!arr->empty()) { 36 | sctl.sink_next(arr->front()); 37 | arr->pop(); 38 | } 39 | } 40 | sctl.sink_completed(serial); 41 | } 42 | )); 43 | }); 44 | }; 45 | } 46 | 47 | } /* namespace operators */ 48 | } /* namespace another_rxcpp */ 49 | 50 | #endif /* !defined(__another_rxcpp_h_take_last__) */ 51 | -------------------------------------------------------------------------------- /include/another-rxcpp/operators/take_until.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_take_until__) 2 | #define __another_rxcpp_h_take_until__ 3 | 4 | #include "../observable.h" 5 | #include "../internal/tools/stream_controller.h" 6 | 7 | namespace another_rxcpp { 8 | namespace operators { 9 | 10 | template auto take_until(TRIGGER_OB trigger) noexcept 11 | { 12 | return [trigger](auto source) { 13 | using Source = decltype(source); 14 | using Item = typename Source::value_type; 15 | using TriggerItem = typename TRIGGER_OB::value_type; 16 | return observable<>::create([source, trigger](subscriber s) { 17 | auto sctl = internal::stream_controller(s); 18 | 19 | auto obs_trigger = sctl.template new_observer( 20 | [sctl](auto serial, auto) { 21 | sctl.sink_completed_force(); 22 | }, 23 | [sctl](auto, std::exception_ptr err){ 24 | sctl.sink_error(err); 25 | }, 26 | [sctl](auto serial){ 27 | sctl.sink_completed(serial); 28 | } 29 | ); 30 | 31 | auto obs_source = sctl.template new_observer( 32 | [sctl](auto, const Item& x) { 33 | sctl.sink_next(x); 34 | }, 35 | [sctl](auto, std::exception_ptr err){ 36 | sctl.sink_error(err); 37 | }, 38 | [sctl](auto serial){ 39 | sctl.sink_completed_force(); // trigger also unsubscribe 40 | } 41 | ); 42 | 43 | trigger.subscribe(obs_trigger); 44 | source.subscribe(obs_source); 45 | }); 46 | }; 47 | } 48 | 49 | } /* namespace operators */ 50 | } /* namespace another_rxcpp */ 51 | 52 | #endif /* !defined(__another_rxcpp_h_take_until__) */ -------------------------------------------------------------------------------- /include/another-rxcpp/operators/take_while.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_take_while__) 2 | #define __another_rxcpp_h_take_while__ 3 | 4 | #include "../observable.h" 5 | #include "../internal/tools/util.h" 6 | #include "../internal/tools/stream_controller.h" 7 | 8 | namespace another_rxcpp { 9 | namespace operators { 10 | 11 | template auto take_while(F f) noexcept 12 | { 13 | return [f](auto source){ 14 | using Source = decltype(source); 15 | using Item = typename Source::value_type; 16 | return observable<>::create([source, f](subscriber s) { 17 | auto sctl = internal::stream_controller(s); 18 | source.subscribe(sctl.template new_observer( 19 | [sctl, f](auto serial, const Item& x){ 20 | try{ 21 | if(f(x)){ 22 | sctl.sink_next(x); 23 | } 24 | else{ 25 | sctl.sink_completed(serial); 26 | } 27 | } 28 | catch(...){ 29 | sctl.sink_error(std::current_exception()); 30 | } 31 | }, 32 | [sctl](auto, std::exception_ptr err){ 33 | sctl.sink_error(err); 34 | }, 35 | [sctl](auto serial){ 36 | sctl.sink_completed(serial); 37 | } 38 | )); 39 | }); 40 | }; 41 | } 42 | 43 | } /* namespace operators */ 44 | } /* namespace another_rxcpp */ 45 | 46 | #endif /* !defined(__another_rxcpp_h_take_while__) */ -------------------------------------------------------------------------------- /include/another-rxcpp/operators/tap.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_tap__) 2 | #define __another_rxcpp_h_tap__ 3 | 4 | #include "../observable.h" 5 | #include "../internal/tools/util.h" 6 | #include "../internal/tools/stream_controller.h" 7 | 8 | namespace another_rxcpp { 9 | namespace operators { 10 | 11 | template 12 | auto tap(observer obs) noexcept 13 | { 14 | return [obs](auto source){ 15 | using Source = decltype(source); 16 | using Item = typename Source::value_type; 17 | return observable<>::create([source, obs](subscriber s) { 18 | auto sctl = internal::stream_controller(s); 19 | source.subscribe(sctl.template new_observer( 20 | [sctl, obs](auto, const Item& x){ 21 | obs.on_next(x); 22 | sctl.sink_next(x); 23 | }, 24 | [sctl, obs](auto, std::exception_ptr err){ 25 | obs.on_error(err); 26 | sctl.sink_error(err); 27 | }, 28 | [sctl, obs](auto serial) { 29 | obs.on_completed(); 30 | sctl.sink_completed(serial); 31 | } 32 | )); 33 | }); 34 | }; 35 | } 36 | 37 | template 38 | auto tap( 39 | ON_NEXT n, 40 | observer<>::error_t e = {}, 41 | observer<>::completed_t c = {} 42 | ) noexcept 43 | { 44 | return [n, e, c](auto source){ 45 | using Source = decltype(source); 46 | using Item = typename Source::value_type; 47 | return observable<>::create([source, n, e, c](subscriber s) { 48 | auto sctl = internal::stream_controller(s); 49 | source.subscribe(sctl.template new_observer( 50 | [sctl, n](auto, const Item& x){ 51 | n(x); 52 | sctl.sink_next(x); 53 | }, 54 | [sctl, e](auto, std::exception_ptr err){ 55 | if(e) e(err); 56 | sctl.sink_error(err); 57 | }, 58 | [sctl, c](auto serial) { 59 | if(c) c(); 60 | sctl.sink_completed(serial); 61 | } 62 | )); 63 | }); 64 | }; 65 | } 66 | 67 | } /* namespace operators */ 68 | } /* namespace another_rxcpp */ 69 | 70 | #endif /* !defined(__another_rxcpp_h_tap__) */ 71 | -------------------------------------------------------------------------------- /include/another-rxcpp/operators/timeout.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_timeout__) 2 | #define __another_rxcpp_h_timeout__ 3 | 4 | #include "../internal/tools/stream_controller.h" 5 | #include "../observable.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | namespace another_rxcpp { 13 | namespace operators { 14 | 15 | class timeout_error : public std::runtime_error { 16 | public: 17 | timeout_error(const std::string& msg) : runtime_error(msg) {} 18 | }; 19 | 20 | inline auto timeout(std::chrono::milliseconds msec) noexcept 21 | { 22 | return [msec](auto source){ 23 | using Source = decltype(source); 24 | using Item = typename Source::value_type; 25 | return observable<>::create([source, msec](subscriber s) { 26 | auto sctl = internal::stream_controller(s); 27 | 28 | using namespace another_rxcpp::internal; 29 | struct member { 30 | std::mutex mtx_; 31 | std::condition_variable cond_; 32 | }; 33 | auto m = std::make_shared(); 34 | 35 | std::thread([sctl, m, msec]{ 36 | std::unique_lock lock(m->mtx_); 37 | while(sctl.is_subscribed()){ 38 | if(m->cond_.wait_for(lock, msec) == std::cv_status::timeout){ 39 | if(sctl.is_subscribed()){ 40 | sctl.sink_error(std::make_exception_ptr(timeout_error("timeout"))); 41 | } 42 | break; 43 | } 44 | } 45 | }).detach(); 46 | 47 | source.subscribe(sctl.template new_observer( 48 | [sctl, m](auto, const Item& x){ 49 | m->cond_.notify_one(); 50 | sctl.sink_next(x); 51 | }, 52 | [sctl, m](auto, std::exception_ptr err){ 53 | sctl.sink_error(err); 54 | m->cond_.notify_one(); 55 | }, 56 | [sctl, m](auto serial) { 57 | sctl.sink_completed(serial); 58 | m->cond_.notify_one(); 59 | } 60 | )); 61 | }); 62 | }; 63 | } 64 | 65 | } /* namespace operators */ 66 | } /* namespace another_rxcpp */ 67 | 68 | #endif /* !defined(__another_rxcpp_h_timeout__) */ 69 | -------------------------------------------------------------------------------- /include/another-rxcpp/operators/zip.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_zip__) 2 | #define __another_rxcpp_h_zip__ 3 | 4 | #include "../observable.h" 5 | #include "../internal/tools/stream_controller.h" 6 | #include "../internal/tools/util.h" 7 | #include "../internal/tools/fn.h" 8 | #include "../scheduler.h" 9 | #include 10 | #include 11 | 12 | namespace another_rxcpp { 13 | namespace operators { 14 | 15 | namespace zip_internal { 16 | 17 | /** 18 | * types emitted by observable generated by zip_main 19 | * value_type = tuple< OB1::value_type, OB2::value_type... > 20 | **/ 21 | template struct result_type_impl; 22 | 23 | template 24 | struct result_type_impl, OB, ARGS...> : 25 | result_type_impl, ARGS...> {}; 26 | 27 | template 28 | struct result_type_impl> { 29 | using type = std::tuple; 30 | }; 31 | 32 | template struct result_type { 33 | using type = typename result_type_impl, ARGS...>::type; 34 | }; 35 | 36 | 37 | /** 38 | * create values queue (use for each sources) 39 | * tuple< shared_ptr>, shared_ptr>, ...> 40 | **/ 41 | template 42 | auto create_values_queue_internal(TPL tpl) 43 | { 44 | return tpl; 45 | } 46 | 47 | template 48 | auto create_values_queue_internal(TPL tpl, OB /* ob */, ARGS...args) 49 | { 50 | return create_values_queue_internal( 51 | std::tuple_cat( 52 | tpl, 53 | std::make_tuple( 54 | std::make_shared>() 55 | ) 56 | ), 57 | args...); 58 | } 59 | 60 | template 61 | auto create_values_queue(ARGS...args) 62 | { 63 | return create_values_queue_internal(std::tuple<>(), args...); 64 | } 65 | 66 | /** 67 | * prepare subscribers 68 | **/ 69 | template 70 | auto prepare_subscribers_impl(TPL tpl, std::function nextfn, std::shared_ptr mtx, internal::stream_controller, const VALQ&) 71 | { 72 | return tpl; 73 | } 74 | 75 | template 76 | auto prepare_subscribers_impl(TPL tpl, std::function nextfn, std::shared_ptr mtx, internal::stream_controller sctl, const VALQ& valq, OB ob, ARGS...args) 77 | { 78 | auto values = std::get(valq); 79 | 80 | using Source = decltype(ob); 81 | using Item = typename Source::value_type; 82 | 83 | auto tpl2 = std::tuple_cat( 84 | tpl, 85 | std::make_tuple( 86 | sctl.template new_observer( 87 | [values, nextfn, mtx](auto, Item x){ 88 | { 89 | std::unique_lock lock(*mtx); 90 | values->push(std::move(x)); 91 | } 92 | nextfn(); 93 | }, 94 | [sctl](auto, std::exception_ptr err){ 95 | sctl.sink_error(err); 96 | }, 97 | [sctl](auto serial) { 98 | sctl.sink_completed(serial); 99 | } 100 | ) 101 | ) 102 | ); 103 | 104 | return prepare_subscribers_impl(tpl2, nextfn, mtx, sctl, valq, args...); 105 | } 106 | 107 | template 108 | auto prepare_subscribers(std::function nextfn, std::shared_ptr mtx, internal::stream_controller sctl, const VALQ& valq, ARGS...args) 109 | { 110 | return prepare_subscribers_impl<0>(std::tuple<>(), nextfn, mtx, sctl, valq, args...); 111 | } 112 | 113 | /** 114 | * subscribe each observables 115 | **/ 116 | template 117 | void subscribe_impl(const SBS& sbs, const VALQ&) 118 | { /** nothing to do */ } 119 | 120 | template 121 | void subscribe_impl(const SBS& sbs, const VALQ& valq, OB ob, ARGS...args) 122 | { 123 | ob.subscribe(std::get(sbs)); 124 | subscribe_impl(sbs, valq, args...); 125 | } 126 | 127 | template 128 | void subscribe(const SBS& sbs, const VALQ& valq, ARGS...args) 129 | { 130 | subscribe_impl<0>(sbs, valq, args...); 131 | } 132 | 133 | 134 | /** 135 | * check all values are ready 136 | **/ 137 | template 138 | bool ready_values_impl(const VALQ& valq) 139 | { 140 | return true; 141 | } 142 | 143 | template 144 | bool ready_values_impl(const VALQ& valq, OB /* ob */, ARGS...args) 145 | { 146 | return std::get(valq)->size() > 0 && ready_values_impl(valq, args...); 147 | } 148 | 149 | template 150 | bool ready_values(const VALQ& valq, ARGS...args) 151 | { 152 | return ready_values_impl<0>(valq, args...); 153 | } 154 | 155 | 156 | /** 157 | * get all the first values & pop 158 | **/ 159 | template 160 | auto get_values_impl(TPL tpl, const VALQ& valq) 161 | { 162 | return tpl; 163 | } 164 | 165 | template 166 | auto get_values_impl(TPL tpl, const VALQ& valq, OB /* ob */, ARGS...args) 167 | { 168 | auto values = std::get(valq); 169 | auto v = values->front(); 170 | values->pop(); 171 | return get_values_impl( 172 | std::tuple_cat(tpl, std::make_tuple(std::move(v))), 173 | valq, 174 | args... 175 | ); 176 | } 177 | 178 | template 179 | auto get_values(const VALQ& valq, ARGS...args) 180 | { 181 | return get_values_impl<0>(std::tuple<>(), valq, args...); 182 | } 183 | 184 | 185 | /** 186 | * std::apply 187 | * http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3802.pdf 188 | **/ 189 | template 190 | auto apply_impl(F&& f, Tuple&& t, std::index_sequence) 191 | { 192 | return std::forward(f)(std::get(std::forward(t))...); 193 | } 194 | 195 | template 196 | auto apply(F&& f, Tuple&& t) 197 | { 198 | using Indices = std::make_index_sequence>::value>; 199 | return apply_impl(std::forward(f), std::forward(t), Indices{}); 200 | } 201 | 202 | 203 | /** 204 | * zip main function 205 | */ 206 | template 207 | auto zip_main(ARGS...args) { 208 | return [args...](auto src) { 209 | using rtype = typename zip_internal::result_type::type; 210 | return observable<>::create([src, args...](subscriber s) { 211 | auto sctl = internal::stream_controller(s); 212 | auto valq = zip_internal::create_values_queue(src, args...); 213 | auto mtx = std::make_shared(); 214 | auto nextfn = [sctl, valq, mtx, src, args...]{ 215 | std::unique_lock lock(*mtx); 216 | while(zip_internal::ready_values(valq, src, args...)){ 217 | sctl.sink_next(zip_internal::get_values(valq, src, args...)); 218 | } 219 | }; 220 | auto sbs = zip_internal::prepare_subscribers(nextfn, mtx, sctl, valq, src, args...); 221 | zip_internal::subscribe(sbs, valq, src, args...); 222 | }); 223 | }; 224 | } 225 | 226 | /** 227 | * function type generator 228 | **/ 229 | template struct bundle; 230 | 231 | template struct function_parameter_impl; 232 | 233 | template 234 | struct function_parameter_impl> 235 | { 236 | /** evaluate the return value if you pass BARGS... */ 237 | template static auto feval(F f) { 238 | return decltype(f(std::declval()...)){}; 239 | } 240 | 241 | /** generate function type */ 242 | template 243 | using ftype = internal::fn; 244 | }; 245 | 246 | template 247 | struct function_parameter_impl, OB, ARGS...> : 248 | function_parameter_impl, ARGS...> {}; 249 | 250 | template struct function_parameter : 251 | function_parameter_impl, ARGS...> {}; 252 | } /* zip_internal */ 253 | 254 | template ::value, bool> = true> 255 | auto zip(X x, ARGS...args) 256 | { 257 | return zip_internal::zip_main(std::forward(x), std::forward(args)...); 258 | } 259 | 260 | template ::value, bool> = true> 261 | auto zip(X x, ARGS...args) 262 | { 263 | return [x, args...](auto src){ 264 | using FRET = internal::lambda_invoke_result_t; 265 | return observable<>::create([x, args..., src](subscriber s){ 266 | auto sctl = internal::stream_controller(s); 267 | auto obs = zip_internal::zip_main(args...)(src); 268 | 269 | using Source = decltype(obs); 270 | using Item = typename Source::value_type; 271 | 272 | 273 | obs.subscribe(sctl.template new_observer( 274 | [sctl, x](auto, Item r){ 275 | sctl.sink_next(zip_internal::apply(x, r)); 276 | }, 277 | [sctl](auto, std::exception_ptr err){ 278 | sctl.sink_error(err); 279 | }, 280 | [sctl](auto serial) { 281 | sctl.sink_completed(serial); 282 | } 283 | )); 284 | }); 285 | }; 286 | } 287 | 288 | } /* namespace operators */ 289 | } /* namespace another_rxcpp */ 290 | 291 | #endif /* !defined(__another_rxcpp_h_zip__) */ -------------------------------------------------------------------------------- /include/another-rxcpp/rx.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_another_rxcpp__) 2 | #define __another_rxcpp_h_another_rxcpp__ 3 | 4 | #include "observable.h" 5 | #include "observables.h" 6 | #include "subjects.h" 7 | #include "operators.h" 8 | #include "schedulers.h" 9 | // #include "utils.h" 10 | 11 | #if defined(SUPPORTS_RXCPP_COMPATIBLE) 12 | namespace rxcpp { 13 | using namespace another_rxcpp; 14 | using namespace another_rxcpp::observables; 15 | using namespace another_rxcpp::schedulers; 16 | namespace schedulers = another_rxcpp::schedulers; 17 | // namespace subjects = another_rxcpp::subjects; 18 | namespace sources = another_rxcpp::observables; 19 | namespace util {} 20 | } 21 | #endif 22 | 23 | #endif /* !defined(__another_rxcpp_h_another_rxcpp__) */ -------------------------------------------------------------------------------- /include/another-rxcpp/scheduler.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_scheduler__) 2 | #define __another_rxcpp_h_scheduler__ 3 | 4 | #include "internal/tools/fn.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace another_rxcpp { 11 | 12 | class scheduler_interface { 13 | public: 14 | enum class schedule_type {direct, queuing}; 15 | using function_type = internal::fn; 16 | using call_in_context_fn_t = internal::fn; 17 | 18 | private: 19 | schedule_type schedule_type_; 20 | 21 | protected: 22 | scheduler_interface(schedule_type stype) noexcept : schedule_type_(stype) {} 23 | 24 | public: 25 | virtual ~scheduler_interface() = default; 26 | 27 | schedule_type get_schedule_type() const noexcept { return schedule_type_; } 28 | 29 | virtual void run(call_in_context_fn_t call_in_context) noexcept = 0; 30 | virtual void schedule(const function_type& f) noexcept = 0; 31 | virtual void detach() noexcept = 0; 32 | }; 33 | 34 | class scheduler { 35 | public: 36 | using function_type = typename scheduler_interface::function_type; 37 | using creator_fn = std::function; 38 | enum class interface_type { direct, queuing }; 39 | 40 | private: 41 | using isp = std::shared_ptr; 42 | 43 | struct member { 44 | isp interface_; 45 | std::mutex mtx_; 46 | std::condition_variable cond_; 47 | std::queue queue_; 48 | bool abort_ = false; 49 | }; 50 | std::shared_ptr m_; 51 | 52 | public: 53 | template scheduler(ISP isp) noexcept : 54 | m_(std::make_shared()) 55 | { 56 | m_->interface_ = std::dynamic_pointer_cast(isp); 57 | 58 | if(m_->interface_->get_schedule_type() == scheduler_interface::schedule_type::queuing){ 59 | auto m = m_; 60 | m_->interface_->run([m](){ 61 | while(true){ 62 | auto q = [m]() -> std::queue { 63 | std::unique_lock lock(m->mtx_); 64 | m->cond_.wait(lock, [m]{ return !m->queue_.empty() || m->abort_; }); 65 | if(m->abort_){ 66 | return std::queue(); 67 | } 68 | return std::move(m->queue_); 69 | }(); 70 | if(q.empty()) break; 71 | while(!q.empty()){ 72 | m->interface_->schedule(q.front()); 73 | q.pop(); 74 | } 75 | } 76 | }); 77 | } 78 | } 79 | 80 | scheduler(const scheduler& src) noexcept { 81 | *this = src; 82 | } 83 | 84 | scheduler& operator= (const scheduler& src) noexcept { 85 | m_ = src.m_; 86 | return *this; 87 | } 88 | 89 | virtual ~scheduler() noexcept { 90 | } 91 | 92 | template void schedule(F&& f) const noexcept { 93 | if(m_->interface_->get_schedule_type() == scheduler_interface::schedule_type::queuing){ 94 | std::unique_lock lock(m_->mtx_); 95 | m_->queue_.push(std::forward(f)); 96 | lock.unlock(); 97 | m_->cond_.notify_one(); 98 | } 99 | else{ 100 | /* m_->interface_->get_schedule_type() == scheduler_interface::schedule_type::direct */ 101 | m_->interface_->schedule(std::forward(f)); 102 | } 103 | } 104 | 105 | void abort() const noexcept { 106 | if(m_->interface_->get_schedule_type() == scheduler_interface::schedule_type::queuing){ 107 | { 108 | std::unique_lock lock(m_->mtx_); 109 | m_->abort_ = true; 110 | } 111 | m_->cond_.notify_one(); 112 | m_->interface_->detach(); 113 | } 114 | } 115 | }; 116 | 117 | } /* namespace another_rxcpp */ 118 | 119 | #endif /* !defined(__another_rxcpp_h_scheduler__) */ 120 | -------------------------------------------------------------------------------- /include/another-rxcpp/schedulers.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_schedulers__) 2 | #define __another_rxcpp_h_schedulers__ 3 | 4 | #include "schedulers/default_scheduler.h" 5 | #include "schedulers/new_thread_scheduler.h" 6 | 7 | #endif /* !defined(__another_rxcpp_h_schedulers__) */ -------------------------------------------------------------------------------- /include/another-rxcpp/schedulers/default_scheduler.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_default_scheduler__) 2 | #define __another_rxcpp_h_default_scheduler__ 3 | 4 | #include "../scheduler.h" 5 | 6 | namespace another_rxcpp { 7 | namespace schedulers { 8 | 9 | class default_scheduler_interface : public scheduler_interface { 10 | public: 11 | default_scheduler_interface() noexcept : 12 | scheduler_interface(schedule_type::direct) {} 13 | virtual ~default_scheduler_interface() = default; 14 | 15 | virtual void run(call_in_context_fn_t call_in_context) noexcept override { 16 | call_in_context(); 17 | } 18 | 19 | virtual void detach() noexcept override { 20 | } 21 | 22 | virtual void schedule(const function_type& f) noexcept override { 23 | f(); 24 | } 25 | }; 26 | 27 | inline auto default_scheduler() noexcept { 28 | return []{ 29 | return scheduler( 30 | std::make_shared() 31 | ); 32 | }; 33 | } 34 | 35 | } /* namespace schedulers */ 36 | } /* namespace another_rxcpp */ 37 | 38 | #endif /* !defined(__another_rxcpp_h_default_scheduler__) */ -------------------------------------------------------------------------------- /include/another-rxcpp/schedulers/new_thread_scheduler.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_new_thread_scheduler__) 2 | #define __another_rxcpp_h_new_thread_scheduler__ 3 | 4 | #include "../scheduler.h" 5 | #include 6 | #include 7 | #include 8 | 9 | namespace another_rxcpp { 10 | namespace schedulers { 11 | 12 | class new_thread_scheduler_interface : public scheduler_interface 13 | { 14 | private: 15 | std::thread thread_; 16 | 17 | public: 18 | new_thread_scheduler_interface() noexcept : 19 | scheduler_interface(schedule_type::queuing) {} 20 | 21 | virtual ~new_thread_scheduler_interface() = default; 22 | 23 | virtual void run(call_in_context_fn_t call_in_context) noexcept override { 24 | thread_ = std::thread([call_in_context](){ 25 | call_in_context(); 26 | }); 27 | } 28 | 29 | virtual void detach() noexcept override { 30 | thread_.detach(); 31 | } 32 | 33 | virtual void schedule(const function_type& f) noexcept override { 34 | f(); 35 | } 36 | }; 37 | 38 | inline auto new_thread_scheduler() { 39 | return [](){ 40 | return scheduler( 41 | std::make_shared() 42 | ); 43 | }; 44 | } 45 | 46 | #if defined(SUPPORTS_RXCPP_COMPATIBLE) 47 | inline auto observe_on_new_thread() { 48 | return new_thread_scheduler(); 49 | } 50 | #endif /* defined(SUPPORTS_RXCPP_COMPATIBLE) */ 51 | 52 | } /* namespace schedulers */ 53 | } /* namespace another_rxcpp */ 54 | 55 | #endif /* !defined(__another_rxcpp_h_new_thread_scheduler__) */ -------------------------------------------------------------------------------- /include/another-rxcpp/subjects.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_subjects__) 2 | #define __another_rxcpp_h_subjects__ 3 | 4 | #include "subjects/subject.h" 5 | #include "subjects/behavior.h" 6 | 7 | #endif /* !defined(__another_rxcpp_h_subjects__) */ -------------------------------------------------------------------------------- /include/another-rxcpp/subjects/behavior.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_behavior__) 2 | #define __another_rxcpp_h_behavior__ 3 | 4 | #include "../internal/tools/stream_controller.h" 5 | #include "../observable.h" 6 | #include "subject.h" 7 | 8 | namespace another_rxcpp { 9 | namespace subjects { 10 | 11 | template class behavior : public subject { 12 | private: 13 | struct member { 14 | T last_; 15 | std::mutex mtx_; 16 | subscription subscription_; 17 | member(const T& last) : last_(last) {} 18 | ~member() { 19 | subscription_.unsubscribe(); 20 | } 21 | }; 22 | std::shared_ptr m_; 23 | 24 | void initialize() { 25 | auto m = m_; 26 | m->subscription_ = as_observable().subscribe({ 27 | [m](const T& x){ 28 | std::lock_guard lock(m->mtx_); 29 | m->last_ = std::move(x); 30 | }, 31 | [](std::exception_ptr) { 32 | }, 33 | [](){} 34 | }); 35 | } 36 | 37 | protected: 38 | 39 | public: 40 | behavior(T&& initial_value) noexcept : 41 | m_(std::make_shared(std::move(initial_value))) 42 | { 43 | initialize(); 44 | } 45 | 46 | behavior(const T& initial_value) noexcept : 47 | m_(std::make_shared(initial_value)) 48 | { 49 | initialize(); 50 | } 51 | 52 | virtual ~behavior() = default; 53 | 54 | virtual observable as_observable() const noexcept override { 55 | auto src = subject::as_observable(); 56 | auto m = m_; 57 | auto base_completed = subject::completed(); 58 | auto base_error = subject::error(); 59 | return observable<>::create([src, m, base_completed, base_error](subscriber s){ 60 | auto sctl = internal::stream_controller(s); 61 | if(base_completed){ 62 | if(base_error != nullptr){ 63 | sctl.sink_error(base_error); 64 | } 65 | else{ 66 | sctl.sink_completed_force(); 67 | } 68 | } 69 | else{ 70 | sctl.sink_next([m](){ 71 | std::lock_guard lock(m->mtx_); 72 | return m->last_; 73 | }()); 74 | src.subscribe(sctl.template new_observer( 75 | [sctl](auto, const T& x){ 76 | sctl.sink_next(x); 77 | }, 78 | [sctl](auto, std::exception_ptr err){ 79 | sctl.sink_error(err); 80 | }, 81 | [sctl](auto serial) { 82 | sctl.sink_completed(serial); 83 | } 84 | )); 85 | } 86 | }); 87 | } 88 | }; 89 | 90 | } /* namespace subjects */ 91 | } /* namespace another_rxcpp */ 92 | 93 | #endif /* !defined(__another_rxcpp_h_behavior__) */ 94 | -------------------------------------------------------------------------------- /include/another-rxcpp/subjects/subject.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_subject__) 2 | #define __another_rxcpp_h_subject__ 3 | 4 | #include "../observable.h" 5 | #include "../observables/connectable.h" 6 | #include "../observables/error.h" 7 | #include "../operators/on_error_resume_next.h" 8 | #include "../operators/publish.h" 9 | #include 10 | #include 11 | 12 | namespace another_rxcpp { 13 | namespace subjects { 14 | 15 | template class subject { 16 | public: 17 | using value_type = T; 18 | using source_type = observables::connectable; 19 | using observer_type = observer; 20 | using subscriber_type = subscriber; 21 | 22 | private: 23 | struct member { 24 | source_type source_; 25 | subscriber_type subscriber_; 26 | std::exception_ptr error_ = nullptr; 27 | subscription subscription_; 28 | std::recursive_mutex mtx_; 29 | std::vector> sctls_; 30 | ~member() { 31 | subscription_.unsubscribe(); 32 | std::lock_guard lock(mtx_); 33 | std::for_each(sctls_.begin(), sctls_.end(), [](auto& sctl){ 34 | sctl.finalize(); 35 | }); 36 | } 37 | }; 38 | std::shared_ptr m_; 39 | 40 | protected: 41 | bool completed() const noexcept { return !m_->subscription_.is_subscribed(); } 42 | std::exception_ptr error() const noexcept { return m_->error_; } 43 | 44 | public: 45 | subject() noexcept : 46 | m_(std::make_shared()) 47 | { 48 | std::weak_ptr m = m_; 49 | m_->source_ = observable<>::create([m](subscriber_type s){ 50 | auto mm = m.lock(); 51 | if(mm) mm->subscriber_ = s; 52 | }) 53 | | operators::on_error_resume_next([m](std::exception_ptr err){ 54 | auto mm = m.lock(); 55 | if(mm) mm->error_ = err; 56 | return observables::error(err); 57 | }) 58 | | operators::publish(); 59 | m_->subscription_ = m_->source_.connect(); 60 | } 61 | 62 | virtual ~subject() = default; 63 | 64 | auto as_subscriber() const noexcept { 65 | return m_->subscriber_; 66 | } 67 | 68 | virtual observable as_observable() const noexcept { 69 | auto m = m_; 70 | return observable<>::create([m](subscriber_type s) { 71 | auto sctl = internal::stream_controller(s); 72 | { 73 | std::lock_guard lock(m->mtx_); 74 | m->sctls_.push_back(sctl); 75 | } 76 | sctl.set_on_finalize([m]{}); /** hold on `m` until on finalize */ 77 | m->source_.subscribe(sctl.template new_observer( 78 | [sctl](auto, const value_type& x){ 79 | sctl.sink_next(x); 80 | }, 81 | [sctl](auto, std::exception_ptr err){ 82 | sctl.sink_error(err); 83 | }, 84 | [sctl](auto serial) { 85 | sctl.sink_completed(serial); 86 | } 87 | )); 88 | if(m->error_){ 89 | sctl.sink_error(m->error_); 90 | } 91 | else if(!m->subscription_.is_subscribed()){ 92 | sctl.sink_completed_force(); 93 | } 94 | }); 95 | } 96 | 97 | 98 | 99 | #if defined(SUPPORTS_RXCPP_COMPATIBLE) 100 | auto get_subscriber() const { return as_subscriber(); } 101 | auto get_observable() const { return as_observable(); } 102 | #endif /* defined(SUPPORTS_RXCPP_COMPATIBLE) */ 103 | }; 104 | 105 | } /* namespace subjects */ 106 | } /* namespace another_rxcpp */ 107 | 108 | #endif /* !defined(__another_rxcpp_h_subject__) */ 109 | -------------------------------------------------------------------------------- /include/another-rxcpp/subscriber.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_subscriber__) 2 | #define __another_rxcpp_h_subscriber__ 3 | 4 | #include "observer.h" 5 | 6 | namespace another_rxcpp { 7 | 8 | template using subscriber = observer; 9 | 10 | } /* namespace another_rxcpp */ 11 | #endif /* !defined(__another_rxcpp_h_subscriber__) */ 12 | -------------------------------------------------------------------------------- /include/another-rxcpp/subscription.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_subscription__) 2 | #define __another_rxcpp_h_subscription__ 3 | 4 | #include "internal/tools/fn.h" 5 | 6 | namespace another_rxcpp { 7 | 8 | class subscription { 9 | private: 10 | struct member { 11 | const internal::fn unsubscribe_; 12 | const internal::fn is_subscribed_; 13 | template 14 | member(Unsb&& unsb, Issb&& issb) noexcept : 15 | unsubscribe_(std::forward(unsb)), 16 | is_subscribed_(std::forward(issb)) {} 17 | }; 18 | 19 | std::shared_ptr m_; 20 | 21 | public: 22 | subscription() noexcept = default; 23 | 24 | template 25 | subscription(Unsb&& unsb, Issb&& issb) noexcept : 26 | m_(std::make_shared( 27 | std::forward(unsb), 28 | std::forward(issb) 29 | )){} 30 | 31 | subscription(const subscription& src) noexcept : m_(src.m_){} 32 | 33 | const subscription& operator = (const subscription& src) { 34 | m_ = src.m_; 35 | return *this; 36 | } 37 | 38 | void unsubscribe() const noexcept { 39 | auto m = m_; 40 | if(m && m->unsubscribe_) m->unsubscribe_(); 41 | } 42 | 43 | bool is_subscribed() const noexcept { 44 | auto m = m_; 45 | return m && m->is_subscribed_ ? m->is_subscribed_() : false; 46 | } 47 | }; 48 | 49 | } /* namespace another_rxcpp */ 50 | #endif /* !defined(__another_rxcpp_h_subscription__) */ 51 | -------------------------------------------------------------------------------- /include/another-rxcpp/utils.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_utils__) 2 | #define __another_rxcpp_h_utils__ 3 | 4 | #include "utils/ready_set_go.h" 5 | #include "utils/unit.h" 6 | #include "utils/inflow_restriction.h" 7 | #include "utils/sem.h" 8 | #include "utils/something.h" 9 | 10 | #endif /* !defined(__another_rxcpp_h_utils__) */ -------------------------------------------------------------------------------- /include/another-rxcpp/utils/inflow_restriction.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_inflow_restriction__) 2 | #define __another_rxcpp_h_inflow_restriction__ 3 | 4 | #include "../observable.h" 5 | #include "../internal/tools/stream_controller.h" 6 | #include "sem.h" 7 | 8 | namespace another_rxcpp { 9 | namespace utils { 10 | 11 | template 12 | class inflow_restriction { 13 | private: 14 | using sem_type = utils::sem; 15 | std::shared_ptr sem_; 16 | 17 | public: 18 | inflow_restriction() noexcept : 19 | sem_(std::make_shared()) {} 20 | inflow_restriction(const inflow_restriction&) = delete; 21 | ~inflow_restriction() = default; 22 | 23 | template 24 | auto enter(observable o) noexcept 25 | { 26 | auto sem = sem_; 27 | return observable<>::create([sem, o](subscriber s){ 28 | auto sctl = internal::stream_controller(s); 29 | sctl.set_on_finalize([sem](){ 30 | sem->unlock(); 31 | }); 32 | sem->lock(); 33 | if(!sctl.is_subscribed()) { 34 | sem->unlock(); 35 | return; 36 | } 37 | o.subscribe(sctl.template new_observer( 38 | [sctl](auto, const T& v){ 39 | sctl.sink_next(v); 40 | }, 41 | [sctl, sem](auto, std::exception_ptr e){ 42 | sctl.sink_error(e); 43 | sem->unlock(); 44 | }, 45 | [sctl, sem](auto serial){ 46 | sctl.sink_completed(serial); 47 | sem->unlock(); 48 | } 49 | )); 50 | }); 51 | } 52 | }; 53 | 54 | } /* namespace utils */ 55 | } /* namespace another_rxcpp */ 56 | 57 | #endif /* !defined(__another_rxcpp_h_ready_set_go__) */ -------------------------------------------------------------------------------- /include/another-rxcpp/utils/ready_set_go.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_ready_set_go__) 2 | #define __another_rxcpp_h_ready_set_go__ 3 | 4 | #include "../observable.h" 5 | #include "../internal/tools/fn.h" 6 | #include "../internal/tools/stream_controller.h" 7 | #include 8 | 9 | namespace another_rxcpp { 10 | namespace utils { 11 | 12 | template 13 | auto ready_set_go(internal::fn f, OB o) noexcept 14 | { 15 | using T = typename OB::value_type; 16 | return observable<>::create([f, o](subscriber s){ 17 | auto sctl = internal::stream_controller(s); 18 | o.subscribe(sctl.template new_observer( 19 | [sctl](auto, const T& x) { 20 | sctl.sink_next(x); 21 | }, 22 | [sctl](auto, std::exception_ptr err){ 23 | sctl.sink_error(err); 24 | }, 25 | [sctl](auto serial){ 26 | sctl.sink_completed(serial); 27 | } 28 | )); 29 | try{ 30 | f(); 31 | } 32 | catch(...){ 33 | sctl.sink_error(std::current_exception()); 34 | } 35 | }); 36 | } 37 | 38 | } /* namespace utils */ 39 | } /* namespace another_rxcpp */ 40 | 41 | #endif /* !defined(__another_rxcpp_h_ready_set_go__) */ -------------------------------------------------------------------------------- /include/another-rxcpp/utils/sem.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_sem__) 2 | #define __another_rxcpp_h_sem__ 3 | 4 | #include 5 | #include 6 | 7 | namespace another_rxcpp { 8 | namespace utils { 9 | 10 | template class sem { 11 | static_assert(N > 0, "the count max in a semaphore must be grater than 1"); 12 | private: 13 | static constexpr int count_max_ = N; 14 | volatile int count_; 15 | std::mutex mtx_; 16 | std::condition_variable cond_; 17 | 18 | public: 19 | explicit sem(int initial_count = count_max_) noexcept : count_(initial_count) {} 20 | sem(const sem&) = delete; /** non copyable */ 21 | ~sem() = default; 22 | 23 | /** BasicLockable */ 24 | void lock() noexcept { 25 | std::unique_lock lock(mtx_); 26 | cond_.wait(lock, [&]{ 27 | return count_ > 0; 28 | }); 29 | count_--; 30 | } 31 | 32 | void unlock() noexcept { 33 | std::lock_guard lock(mtx_); 34 | if (count_ < count_max_) { 35 | count_++; 36 | cond_.notify_one(); 37 | } 38 | } 39 | 40 | /** Lockable */ 41 | bool try_lock() noexcept { 42 | std::lock_guard lock(mtx_); 43 | if(count_ > 0) { 44 | count_--; 45 | return true; 46 | } 47 | else { 48 | return false; 49 | } 50 | } 51 | }; 52 | 53 | } /* namespace utils */ 54 | } /* namespace another_rxcpp */ 55 | 56 | #endif /* !defined(__another_rxcpp_h_sem__) */ -------------------------------------------------------------------------------- /include/another-rxcpp/utils/something.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_something__) 2 | #define __another_rxcpp_h_something__ 3 | 4 | #include "../observable.h" 5 | #include "../observables/just.h" 6 | #include "../observables/error.h" 7 | #include "../internal/tools/util.h" 8 | #include 9 | 10 | namespace another_rxcpp { 11 | namespace utils { 12 | 13 | template class something; 14 | 15 | template <> class something { 16 | private: 17 | class retry_trigger : public std::exception {}; 18 | public: 19 | [[noreturn]] static void retry() noexcept(false) { 20 | throw retry_trigger{}; 21 | } 22 | template::type> 23 | static something success(T&& v) noexcept { 24 | return something( 25 | observables::just(std::forward(v)) 26 | ); 27 | } 28 | template static something error(std::exception_ptr err) noexcept { 29 | return something( 30 | observables::error(err) 31 | ); 32 | } 33 | }; 34 | 35 | template class something { 36 | friend class something<>; 37 | private: 38 | observable o_; 39 | something(observable o) noexcept : o_(o) {} 40 | public: 41 | observable proceed() noexcept { return o_; } 42 | }; 43 | 44 | } /* namespace utils */ 45 | } /* namespace another_rxcpp */ 46 | 47 | #endif /* !defined(__another_rxcpp_h_something__) */ -------------------------------------------------------------------------------- /include/another-rxcpp/utils/unit.h: -------------------------------------------------------------------------------- 1 | #if !defined(__another_rxcpp_h_unit__) 2 | #define __another_rxcpp_h_unit__ 3 | 4 | #include "../observable.h" 5 | 6 | namespace another_rxcpp { 7 | namespace utils { 8 | 9 | struct unit {}; 10 | 11 | } /* namespace utils */ 12 | } /* namespace another_rxcpp */ 13 | 14 | #endif /* !defined(__another_rxcpp_h_unit__) */ -------------------------------------------------------------------------------- /test/amb.cpp: -------------------------------------------------------------------------------- 1 | #if defined(SUPPORTS_OPERATORS_IN_OBSERVABLE) || defined(SUPPORTS_RXCPP_COMPATIBLE) 2 | #include 3 | #else 4 | #include 5 | #include 6 | #endif 7 | 8 | #include 9 | #include "common.h" 10 | #include 11 | 12 | using namespace another_rxcpp; 13 | using namespace another_rxcpp::operators; 14 | 15 | void test_amb() { 16 | 17 | log() << "test_amb -- begin" << std::endl; 18 | 19 | std::random_device seed_gen; 20 | std::mt19937 engine(seed_gen()); 21 | std::uniform_real_distribution<> rand(0.1, 0.5); 22 | thread_group threads; 23 | 24 | auto emitter = [threads](int sleep_time) { 25 | return observable<>::create([sleep_time, threads](subscriber s){ 26 | threads.push([s, sleep_time](){ 27 | for(int i = 0; i < 10; i++){ 28 | if(!s.is_subscribed()) break; 29 | std::this_thread::sleep_for(std::chrono::milliseconds(sleep_time)); 30 | s.on_next(i); 31 | } 32 | s.on_completed(); 33 | }); 34 | }); 35 | }; 36 | 37 | auto emitter2 = emitter(rand(engine) * 100) 38 | | map([](int x){ 39 | return x + 1000; 40 | }); 41 | 42 | auto emitter3 = emitter(rand(engine) * 100) 43 | | map([](int x){ 44 | return x + 2000; 45 | }); 46 | 47 | auto o = emitter(rand(engine) * 100) | amb(emitter2, emitter3); 48 | 49 | auto x = doSubscribe(o); 50 | 51 | while(x.is_subscribed()) {} 52 | 53 | threads.join_all(); 54 | 55 | log() << "test_amb -- end" << std::endl << std::endl; 56 | } -------------------------------------------------------------------------------- /test/behavior_subject.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include "common.h" 5 | 6 | using namespace another_rxcpp; 7 | using namespace another_rxcpp::operators; 8 | 9 | void test_behavior_subject() { 10 | log() << "test_behavior_subject -- begin" << std::endl; 11 | 12 | { 13 | subjects::behavior sbj(100); 14 | doSubscribe(sbj.as_observable()); 15 | sbj.as_subscriber().on_next(1); 16 | doSubscribe(sbj.as_observable()); 17 | sbj.as_subscriber().on_next(2); 18 | doSubscribe(sbj.as_observable()); 19 | sbj.as_subscriber().on_completed(); 20 | doSubscribe(sbj.as_observable()); 21 | } 22 | 23 | { 24 | const std::string x = "abc"; 25 | subjects::behavior sbj(x); 26 | doSubscribe(sbj.as_observable()); 27 | sbj.as_subscriber().on_completed(); 28 | } 29 | 30 | log() << "test_behavior_subject -- end" << std::endl << std::endl; 31 | } -------------------------------------------------------------------------------- /test/blocking.cpp: -------------------------------------------------------------------------------- 1 | #if defined(SUPPORTS_OPERATORS_IN_OBSERVABLE) || defined(SUPPORTS_RXCPP_COMPATIBLE) 2 | #include 3 | #else 4 | #include 5 | #include 6 | #endif 7 | 8 | #include 9 | #include 10 | #include "common.h" 11 | 12 | using namespace another_rxcpp; 13 | using namespace another_rxcpp::operators; 14 | 15 | void test_blocking() { 16 | log() << "test_blocking -- begin" << std::endl; 17 | 18 | log() << "range 1 - 10" << std::endl; 19 | { 20 | auto o = observables::range(1, 10) | blocking(); 21 | log() << " first : " << o.first() << std::endl; 22 | log() << " first : " << o.first() << std::endl; 23 | log() << " first : " << o.first() << std::endl; 24 | log() << " first : " << o.first() << std::endl; 25 | log() << " last : " << o.last() << std::endl; 26 | } 27 | { 28 | auto o = observables::range(1, 10) | blocking(); 29 | log() << " count : " << o.count() << std::endl; 30 | } 31 | { 32 | auto o = observables::range(1, 10) | blocking(); 33 | log() << " sum : " << o.sum() << std::endl; 34 | } 35 | { 36 | auto o = observables::range(1, 10) | blocking(); 37 | log() << "average : " << o.average() << std::endl; 38 | } 39 | { 40 | auto o = observables::range(1, 10) | blocking(); 41 | log() << " min : " << o.min() << std::endl; 42 | } 43 | { 44 | auto o = observables::range(1, 10) | blocking(); 45 | log() << " max : " << o.max() << std::endl; 46 | } 47 | 48 | log() << "string array" << std::endl; 49 | auto arr = {std::string("a"), std::string("b")}; 50 | { 51 | auto o = observables::iterate(arr) | blocking(); 52 | log() << " first : " << o.first() << std::endl; 53 | } 54 | { 55 | auto o = observables::iterate(arr) | blocking(); 56 | log() << " last : " << o.last() << std::endl; 57 | } 58 | { 59 | auto o = observables::iterate(arr) | blocking(); 60 | log() << " count : " << o.count() << std::endl; 61 | } 62 | 63 | { 64 | log() << "emit error" << std::endl; 65 | auto o = observable<>::create([](subscriber s){ 66 | s.on_next(1); 67 | s.on_error(std::make_exception_ptr(std::exception())); 68 | }) | blocking(); 69 | try{ 70 | log() << " count : " << o.count() << std::endl; 71 | } 72 | catch(...){ 73 | log() << "exception" << std::endl; 74 | } 75 | } 76 | 77 | { 78 | log() << "emit empty" << std::endl; 79 | auto o = observable<>::create([](subscriber s){ 80 | s.on_completed(); 81 | }) | blocking(); 82 | try{ 83 | log() << " count : " << o.count() << std::endl; 84 | log() << " first : " << o.first() << std::endl; 85 | } 86 | catch(...){ 87 | log() << "exception" << std::endl; 88 | } 89 | } 90 | 91 | log() << "test_blocking -- end" << std::endl << std::endl; 92 | } -------------------------------------------------------------------------------- /test/case_1.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "common.h" 8 | 9 | using namespace another_rxcpp; 10 | using namespace another_rxcpp::operators; 11 | 12 | void test_case_1() { 13 | log() << "test_case_1 -- begin" << std::endl; 14 | 15 | subjects::subject sbj; 16 | 17 | auto o = sbj.as_observable() 18 | | flat_map([](int x){ 19 | log() << x << std::endl; 20 | if(x == 10) return observables::just(10); 21 | return observables::never(); 22 | }) 23 | | take(1); 24 | 25 | auto x = doSubscribe(o); 26 | 27 | sbj.as_subscriber().on_next(1); 28 | sbj.as_subscriber().on_next(3); 29 | sbj.as_subscriber().on_next(10); 30 | sbj.as_subscriber().on_next(5); 31 | sbj.as_subscriber().on_next(6); 32 | 33 | while(x.is_subscribed()) {} 34 | 35 | log() << "test_case_1 -- end" << std::endl << std::endl; 36 | } -------------------------------------------------------------------------------- /test/case_2.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "common.h" 6 | 7 | using namespace another_rxcpp; 8 | using namespace another_rxcpp::operators; 9 | 10 | static observable _subject(thread_group threads) { 11 | auto t = [](){ 12 | subjects::subject sbj; 13 | return std::make_tuple(sbj.as_subscriber(), sbj.as_observable()); 14 | }(); 15 | auto s = std::get<0>(t); 16 | auto o = std::get<1>(t); 17 | return utils::ready_set_go([s, threads](){ 18 | threads.push([s](){ 19 | std::this_thread::sleep_for(std::chrono::seconds(1)); 20 | s.on_next(1); 21 | s.on_completed(); 22 | }); 23 | }, o); 24 | } 25 | 26 | static observable _behavior(thread_group threads) { 27 | auto t = [](){ 28 | subjects::behavior sbj(99); 29 | return std::make_tuple(sbj.as_subscriber(), sbj.as_observable()); 30 | }(); 31 | auto s = std::get<0>(t); 32 | auto o = std::get<1>(t); 33 | return utils::ready_set_go([s, threads](){ 34 | threads.push([s](){ 35 | std::this_thread::sleep_for(std::chrono::seconds(1)); 36 | s.on_next(1); 37 | s.on_completed(); 38 | }); 39 | }, o); 40 | } 41 | 42 | static observable _subject_error() { 43 | auto t = [](){ 44 | subjects::subject sbj; 45 | return std::make_tuple(sbj.as_subscriber(), sbj.as_observable()); 46 | }(); 47 | auto s = std::get<0>(t); 48 | auto o = std::get<1>(t); 49 | s.on_error(std::make_exception_ptr(std::exception())); 50 | return o; 51 | } 52 | 53 | void test_case_2() { 54 | log() << "test_case_2 -- begin" << std::endl; 55 | 56 | { 57 | log() << "_subject" << std::endl; 58 | thread_group threads; 59 | auto x = doSubscribe(_subject(threads)); 60 | while(x.is_subscribed()) {} 61 | threads.join_all(); 62 | } 63 | 64 | { 65 | log() << "_behavior" << std::endl; 66 | thread_group threads; 67 | auto x = doSubscribe(_behavior(threads)); 68 | while(x.is_subscribed()) {} 69 | threads.join_all(); 70 | } 71 | 72 | { 73 | log() << "_subject_error" << std::endl; 74 | auto x = doSubscribe(_subject_error()); 75 | while(x.is_subscribed()) {} 76 | } 77 | 78 | log() << "test_case_2 -- end" << std::endl << std::endl; 79 | } -------------------------------------------------------------------------------- /test/case_3.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "common.h" 3 | 4 | using namespace another_rxcpp; 5 | using namespace another_rxcpp::operators; 6 | 7 | void test_case_3() { 8 | log() << "test_case_3 -- begin" << std::endl; 9 | 10 | auto sp = std::make_shared(0); 11 | auto o = observable<>::create([sp](subscriber s){ 12 | s.on_next(*sp); 13 | s.on_completed(); 14 | *sp = *sp + 1; 15 | }); 16 | 17 | doSubscribe(o); 18 | doSubscribe(o); 19 | 20 | log() << "test_case_3 -- end" << std::endl << std::endl; 21 | } -------------------------------------------------------------------------------- /test/case_4.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "common.h" 12 | #include 13 | #include 14 | 15 | using namespace another_rxcpp; 16 | using namespace another_rxcpp::operators; 17 | 18 | struct emitters { 19 | mutable std::shared_ptr> run = std::make_shared>(0); 20 | 21 | auto loop(thread_group threads) const { 22 | return observable<>::create([run = run, threads](auto s){ 23 | threads.push([s, run]{ 24 | (*run)++; 25 | for(int i = 0; s.is_subscribed(); i++){ 26 | log() << "emit: " << i << std::endl; 27 | s.on_next(i); 28 | std::this_thread::sleep_for(std::chrono::milliseconds(200)); 29 | } 30 | (*run)--; 31 | log() << "!!!break!!!" << std::endl; 32 | }); 33 | }); 34 | } 35 | 36 | auto defer_error(thread_group threads) const { 37 | return observable<>::create([run = run, threads](auto s){ 38 | threads.push([s, run]{ 39 | s.on_next(0); 40 | (*run)++; 41 | std::this_thread::sleep_for(std::chrono::milliseconds(2000)); 42 | log() << "!!!defer_error!!!" << std::endl; 43 | s.on_error(std::make_exception_ptr(std::exception())); 44 | (*run)--; 45 | }); 46 | }); 47 | } 48 | 49 | }; 50 | 51 | void test_case_4() { 52 | log() << "test_case_4 -- begin" << std::endl; 53 | 54 | { 55 | log() << "case 4-1" << std::endl; 56 | emitters emitters; 57 | thread_group threads; 58 | auto sbj = subjects::subject(); 59 | auto sbsc = emitters.loop(threads) 60 | | take_until(sbj.as_observable()) 61 | | flat_map([sbj](int x){ 62 | if(x == 4) { 63 | log() << "subject next!!" << std::endl; 64 | sbj.as_subscriber().on_next(1); 65 | } 66 | return observables::empty(); 67 | }) 68 | | subscribe( 69 | [](auto x){ log() << "next: " << x << std::endl; }, 70 | [](auto e){ log() << "error" << std::endl; }, 71 | [](){ log() << "complete" << std::endl; } 72 | ); 73 | while(sbsc.is_subscribed()) {} 74 | threads.join_all(); 75 | } 76 | 77 | { 78 | log() << "case 4-2" << std::endl; 79 | emitters emitters; 80 | thread_group threads; 81 | auto sbj = subjects::subject(); 82 | auto looper = emitters.loop(threads); 83 | auto sbsc = emitters.defer_error(threads) 84 | | flat_map([looper](int){ 85 | return looper; 86 | }) 87 | | flat_map([](int x){ 88 | if(x == 5){ 89 | log() << "!!!error!!!" << std::endl; 90 | return observables::error(std::make_exception_ptr(std::exception())); 91 | } 92 | return observables::just(x); 93 | }) 94 | | retry(1) 95 | | take_until(sbj.as_observable()) 96 | | flat_map([sbj](int x){ 97 | if(x == 4) { 98 | log() << "subject next!!" << std::endl; 99 | sbj.as_subscriber().on_next(1); 100 | return observables::just(x); 101 | } 102 | return observables::never(); 103 | }) 104 | | take(1) 105 | | subscribe( 106 | [](auto x){ log() << "next: " << x << std::endl; }, 107 | [](auto e){ log() << "error" << std::endl; }, 108 | [](){ log() << "complete" << std::endl; } 109 | ); 110 | 111 | while(sbsc.is_subscribed()) {} 112 | threads.join_all(); 113 | 114 | log() << "emitters.run = " << *emitters.run << std::endl; 115 | } 116 | 117 | 118 | { 119 | log() << "case 4-3" << std::endl; 120 | emitters emitters; 121 | thread_group threads; 122 | auto sbj = subjects::subject(); 123 | auto looper = emitters.loop(threads); 124 | auto sbsc = emitters.defer_error(threads) 125 | | flat_map([looper](int){ 126 | return looper; 127 | }) 128 | | flat_map([](int x){ 129 | if(x == 5){ 130 | log() << "!!!error!!!" << std::endl; 131 | return observables::error(std::make_exception_ptr(std::exception())); 132 | } 133 | return observables::just(x); 134 | }) 135 | | retry(1) 136 | | take_until(sbj.as_observable()) 137 | | flat_map([sbj](int x){ 138 | if(x == 4) { 139 | log() << "subject next!!" << std::endl; 140 | sbj.as_subscriber().on_next(1); 141 | return observables::just(x); 142 | } 143 | return observables::empty(); 144 | }) 145 | | subscribe( 146 | [](auto x){ log() << "next: " << x << std::endl; }, 147 | [](auto e){ log() << "error" << std::endl; }, 148 | [](){ log() << "complete" << std::endl; } 149 | ); 150 | 151 | while(sbsc.is_subscribed()) {} 152 | threads.join_all(); 153 | 154 | log() << "emitters.run = " << *emitters.run << std::endl; 155 | } 156 | 157 | log() << "test_case_4 -- end" << std::endl << std::endl; 158 | } -------------------------------------------------------------------------------- /test/case_5.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "common.h" 7 | #include 8 | 9 | using namespace another_rxcpp; 10 | using namespace another_rxcpp::operators; 11 | 12 | 13 | void test_case_5() { 14 | log() << "test_case_5 -- begin" << std::endl; 15 | 16 | auto periodical = observables::interval(std::chrono::milliseconds(100), schedulers::default_scheduler()) 17 | | tap([](auto x){ 18 | log() << "tap " << x << std::endl; 19 | }); 20 | 21 | /** check recursive unsubscribe in flat_map */ 22 | 23 | auto sbsc = periodical 24 | | flat_map([periodical](int x){ 25 | return periodical 26 | | flat_map([periodical](int x){ 27 | return periodical; 28 | }); 29 | }) 30 | | take(1) 31 | | subscribe( 32 | [](auto x) { 33 | log() << "next: " << x << std::endl; 34 | }, 35 | [](auto) {}, 36 | []() { 37 | log() << "completed" << std::endl; 38 | } 39 | ); 40 | 41 | while(sbsc.is_subscribed()) {} 42 | 43 | log() << "test_case_5 -- end" << std::endl << std::endl; 44 | } -------------------------------------------------------------------------------- /test/case_6.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "common.h" 6 | #include 7 | 8 | using namespace another_rxcpp; 9 | using namespace another_rxcpp::operators; 10 | 11 | 12 | void test_case_6() { 13 | log() << "test_case_6 -- begin" << std::endl; 14 | 15 | { 16 | subjects::subject sbj; 17 | auto vec = std::make_shared>(std::vector{1, 2, 3, 4, 5, 6}); 18 | 19 | auto getNext = [sbj, vec] { 20 | if(vec->size() == 0){ 21 | sbj.as_subscriber().on_completed(); 22 | } else { 23 | auto x = vec->front(); 24 | vec->erase(vec->begin()); 25 | sbj.as_subscriber().on_next(x); 26 | } 27 | }; 28 | 29 | auto sbsc = utils::ready_set_go([getNext]{ 30 | getNext(); 31 | }, sbj.as_observable()) 32 | .observe_on(schedulers::new_thread_scheduler()) 33 | .flat_map([getNext](int x){ 34 | if(x == 2){ 35 | return observables::error(std::make_exception_ptr(std::exception())) 36 | .on_error_resume_next([](auto e){ 37 | return observables::just(-1); 38 | }); 39 | } 40 | else return observables::just(x); 41 | }) 42 | .map([getNext](auto x){ 43 | log() << "map: " << x << std::endl; 44 | getNext(); 45 | return x; 46 | }) 47 | .last() 48 | .subscribe( 49 | [](auto x) { 50 | log() << "next: " << x << std::endl; 51 | }, 52 | [](auto) { 53 | log() << "error" << std::endl; 54 | }, 55 | []{ 56 | log() << "completed" << std::endl; 57 | } 58 | ); 59 | 60 | while(sbsc.is_subscribed()) {} 61 | } 62 | 63 | { 64 | subjects::subject sbj; 65 | auto vec = std::make_shared>(std::vector{1, 2, 3, 4, 5, 6}); 66 | 67 | auto getNext = [sbj, vec]() { 68 | if(vec->size() == 0){ 69 | sbj.as_subscriber().on_completed(); 70 | } else { 71 | auto x = vec->front(); 72 | vec->erase(vec->begin()); 73 | sbj.as_subscriber().on_next(x); 74 | } 75 | }; 76 | 77 | auto sbsc = utils::ready_set_go([getNext]{ 78 | getNext(); 79 | }, sbj.as_observable()) 80 | .flat_map([getNext](int x){ 81 | if(x == 2){ 82 | return observables::error(std::make_exception_ptr(std::exception())); 83 | } 84 | else return observables::just(x); 85 | }) 86 | .map([getNext](auto x){ 87 | log() << "map: " << x << std::endl; 88 | getNext(); 89 | return x; 90 | }) 91 | .last() 92 | .subscribe( 93 | [](auto x) { 94 | log() << "next: " << x << std::endl; 95 | }, 96 | [](auto) { 97 | log() << "error" << std::endl; 98 | }, 99 | []{ 100 | log() << "completed" << std::endl; 101 | } 102 | ); 103 | 104 | while(sbsc.is_subscribed()) {} 105 | } 106 | 107 | log() << "test_case_6 -- end" << std::endl << std::endl; 108 | } -------------------------------------------------------------------------------- /test/case_7.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "common.h" 6 | #include 7 | 8 | using namespace another_rxcpp; 9 | using namespace another_rxcpp::operators; 10 | 11 | struct watier : public std::enable_shared_from_this { 12 | subjects::subject breaker_subject; 13 | 14 | virtual ~watier() { 15 | log() << "~watier" << std::endl; 16 | } 17 | 18 | auto wait_subscribe(std::string memo){ 19 | auto THIS = shared_from_this(); 20 | return observable<>::create([THIS, memo](subscriber s){ 21 | observables::interval(std::chrono::milliseconds(700), schedulers::new_thread_scheduler()) 22 | .take_until(THIS->breaker_subject.as_observable()) 23 | .subscribe( 24 | [s, memo](auto x) { 25 | log() << memo << " next: " << x << std::endl; 26 | }, 27 | [s, memo](auto err) { 28 | log() << memo << " error" << std::endl; 29 | s.on_error(err); 30 | }, 31 | [s, memo]{ 32 | log() << memo << " completed" << std::endl; 33 | s.on_next(999); 34 | s.on_completed(); 35 | } 36 | ); 37 | }); 38 | } 39 | 40 | auto wait_stream_controller(std::string memo){ 41 | auto THIS = shared_from_this(); 42 | return observable<>::create([THIS, memo](subscriber s){ 43 | internal::stream_controller sctl(s); 44 | observables::interval(std::chrono::milliseconds(700), schedulers::new_thread_scheduler()) 45 | .take_until(THIS->breaker_subject.as_observable()) 46 | .subscribe(sctl.new_observer( 47 | [sctl, memo](auto, auto x) { 48 | log() << memo << " next: " << x << std::endl; 49 | }, 50 | [sctl, memo](auto, auto err) { 51 | log() << memo << " error" << std::endl; 52 | sctl.sink_error(err); 53 | }, 54 | [sctl, memo](auto serial){ 55 | log() << memo << " completed" << std::endl; 56 | sctl.sink_next(999); 57 | sctl.sink_completed(serial); 58 | } 59 | )); 60 | }); 61 | } 62 | }; 63 | 64 | auto emit_error(int ms){ 65 | return observables::just(1) 66 | .observe_on(schedulers::new_thread_scheduler()) 67 | .flat_map([ms](auto){ 68 | return observable<>::create([ms](subscriber s){ 69 | s.on_next(1); 70 | std::this_thread::sleep_for(std::chrono::milliseconds(ms)); 71 | log() << "emit_error" << std::endl; 72 | s.on_error(std::make_exception_ptr(std::runtime_error("error"))); 73 | }); 74 | }); 75 | } 76 | 77 | void test_case_7() { 78 | log() << "test_case_7 -- begin" << std::endl; 79 | 80 | { 81 | log() << "#1 -- begin" << std::endl; 82 | { 83 | auto w = std::make_shared(); 84 | auto sbsc = emit_error(1000) 85 | .flat_map([w](auto){ 86 | return w->wait_subscribe("#1 watier1") 87 | .amb(schedulers::new_thread_scheduler(), w->wait_subscribe("#1 watier2")); 88 | }) 89 | .on_error_resume_next([w](auto){ 90 | log() << "#1 on_error_resume_next" << std::endl; 91 | std::thread([w]{ 92 | wait(100); 93 | log() << "#1 break!" << std::endl; 94 | w->breaker_subject.as_subscriber().on_next(1); 95 | }).detach(); 96 | return observables::just(123456); 97 | }) 98 | .subscribe( 99 | [](auto x) { 100 | log() << "#1 next: " << x << std::endl; 101 | }, 102 | [](auto err) { 103 | log() << "#1 error" << std::endl; 104 | }, 105 | []{ 106 | log() << "#1 completed" << std::endl; 107 | } 108 | ); 109 | while(sbsc.is_subscribed()) {} 110 | } 111 | log() << "#1-- end" << std::endl; 112 | } 113 | 114 | wait(1000); 115 | 116 | { 117 | log() << "#2 -- begin" << std::endl; 118 | { 119 | auto w = std::make_shared(); 120 | auto sbsc = emit_error(1000) 121 | .flat_map([w](auto){ 122 | return w->wait_stream_controller("#2 watier1") 123 | .amb(schedulers::new_thread_scheduler(), w->wait_stream_controller("#2 watier2")); 124 | }) 125 | .on_error_resume_next([w](auto){ 126 | log() << "#2 on_error_resume_next" << std::endl; 127 | std::thread([w]{ 128 | wait(100); 129 | log() << "#2 break!" << std::endl; 130 | w->breaker_subject.as_subscriber().on_next(1); 131 | }).detach(); 132 | return observables::just(123456); 133 | }) 134 | .subscribe( 135 | [](auto x) { 136 | log() << "#2 next: " << x << std::endl; 137 | }, 138 | [](auto err) { 139 | log() << "#2 error" << std::endl; 140 | }, 141 | []{ 142 | log() << "#2 completed" << std::endl; 143 | } 144 | ); 145 | while(sbsc.is_subscribed()) {} 146 | } 147 | log() << "#2-- end" << std::endl; 148 | } 149 | 150 | wait(1000); 151 | 152 | log() << "test_case_7 -- end" << std::endl << std::endl; 153 | } -------------------------------------------------------------------------------- /test/common.h: -------------------------------------------------------------------------------- 1 | #if !defined(__h_common__) 2 | #define __h_common__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | using namespace another_rxcpp; 15 | using namespace another_rxcpp::operators; 16 | 17 | inline std::ostream& log() { 18 | return std::cout << "(" << std::hex << std::this_thread::get_id() << ") " << std::dec; 19 | } 20 | 21 | class thread_group { 22 | mutable std::shared_ptr> threads_; 23 | mutable std::shared_ptr mtx_; 24 | mutable bool closed_; 25 | 26 | public: 27 | thread_group() noexcept : 28 | threads_(std::make_shared>()), 29 | mtx_(std::make_shared()), 30 | closed_(false) 31 | {} 32 | 33 | template void push(F&& f) const noexcept { 34 | std::unique_lock lock(*mtx_); 35 | if(closed_) { 36 | log() << "thread_group closed" << std::endl; 37 | return; 38 | } 39 | threads_->push_back(std::thread(std::forward(f))); 40 | } 41 | 42 | void join_all() const noexcept { 43 | auto&& threads = [&](){ 44 | std::unique_lock lock(*mtx_); 45 | closed_ = true; 46 | std::vector threads; 47 | std::move(threads_->begin(), threads_->end(), std::back_inserter(threads)); 48 | return threads; 49 | }(); 50 | std::for_each(threads.begin(), threads.end(), [](auto&& t){ t.join(); }); 51 | } 52 | }; 53 | 54 | inline void setTimeout(std::function f, int x) { 55 | observables::just(0) 56 | .observe_on(schedulers::new_thread_scheduler()) 57 | .delay(std::chrono::milliseconds(x)) 58 | .subscribe([f](auto){ 59 | f(); 60 | }); 61 | } 62 | 63 | inline void wait(int ms) { 64 | log() << "wait " << ms << "ms" << std::endl; 65 | std::this_thread::sleep_for(std::chrono::milliseconds(ms)); 66 | log() << "awake" << std::endl; 67 | } 68 | 69 | 70 | template 71 | auto ovalue(T&& value, int delay = 0) { 72 | if(delay == 0){ 73 | return observables::just(std::forward(value)); 74 | } 75 | else{ 76 | return observables::just(std::forward(value)) 77 | .observe_on(schedulers::new_thread_scheduler()) 78 | .delay(std::chrono::milliseconds(delay)); 79 | } 80 | } 81 | 82 | inline auto interval_range(int from, int to, int msec, thread_group threads) { 83 | return observable<>::create([from, to, msec, threads](subscriber s){ 84 | threads.push([from, to, msec, s]{ 85 | for(int i = from; i <= to; i++){ 86 | if(!s.is_subscribed()){ 87 | log() << "interval_range break" << std::endl; 88 | return; 89 | } 90 | std::this_thread::sleep_for(std::chrono::milliseconds(msec)); 91 | if(!s.is_subscribed()){ 92 | log() << "interval_range break" << std::endl; 93 | return; 94 | } 95 | log() << "interval_range emit " << i << std::endl; 96 | s.on_next(i); 97 | } 98 | log() << "interval_range complete" << std::endl; 99 | s.on_completed(); 100 | }); 101 | }); 102 | } 103 | 104 | template auto doSubscribe(T&& source) { 105 | using TT = typename another_rxcpp::internal::strip_const_reference::type; 106 | log() << "doSubscribe" << std::endl; 107 | using value_type = typename TT::value_type; 108 | return source 109 | | subscribe([](const value_type& x) { 110 | // .subscribe([](value_type&& x) { 111 | log() << " [on_next] " << x << std::endl; 112 | }, [](std::exception_ptr err) { 113 | std::string w = [err](){ 114 | try{ std::rethrow_exception(err); } 115 | catch(std::exception& e){ 116 | return e.what(); 117 | } 118 | catch(...){ 119 | return "unknown"; 120 | } 121 | }(); 122 | log() << " [on_error] " << w << std::endl; 123 | }, []() { 124 | log() << " [on_completed] " << std::endl; 125 | }); 126 | } 127 | 128 | #endif /* !defined(__h_common__) */ -------------------------------------------------------------------------------- /test/connectable.cpp: -------------------------------------------------------------------------------- 1 | #if defined(SUPPORTS_OPERATORS_IN_OBSERVABLE) || defined(SUPPORTS_RXCPP_COMPATIBLE) 2 | #include 3 | #else 4 | #include 5 | #include 6 | #endif 7 | 8 | #include 9 | #include "common.h" 10 | 11 | using namespace another_rxcpp; 12 | using namespace another_rxcpp::operators; 13 | 14 | void test_connectable() { 15 | log() << "test_connectable -- begin" << std::endl; 16 | 17 | thread_group threads; 18 | 19 | auto o = observable<>::create([threads](subscriber s){ 20 | threads.push([s](){ 21 | for(int i = 0; i < 100; i++){ 22 | if(!s.is_subscribed()) break; 23 | std::this_thread::sleep_for(std::chrono::milliseconds(200)); 24 | log() << "emit " << i << std::endl; 25 | s.on_next(i); 26 | } 27 | log() << "break!" << std::endl; 28 | }); 29 | }); 30 | 31 | auto oo = o | publish(); 32 | auto s1 = doSubscribe(oo); 33 | auto s2 = doSubscribe(oo); 34 | auto s3 = doSubscribe(oo); 35 | 36 | wait(1000); 37 | auto cc = oo.connect(); 38 | 39 | wait(800); 40 | s1.unsubscribe(); 41 | 42 | wait(800); 43 | s2.unsubscribe(); 44 | 45 | auto s4 = doSubscribe(oo); 46 | 47 | wait(800); 48 | s3.unsubscribe(); 49 | 50 | wait(3000); 51 | s4.unsubscribe(); 52 | 53 | wait(800); 54 | cc.unsubscribe(); 55 | threads.join_all(); 56 | 57 | log() << "test_connectable -- end" << std::endl << std::endl; 58 | } -------------------------------------------------------------------------------- /test/defer.cpp: -------------------------------------------------------------------------------- 1 | #if defined(SUPPORTS_OPERATORS_IN_OBSERVABLE) || defined(SUPPORTS_RXCPP_COMPATIBLE) 2 | #include 3 | #else 4 | #include 5 | #include 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | #include "common.h" 12 | 13 | using namespace another_rxcpp; 14 | using namespace another_rxcpp::operators; 15 | 16 | void test_defer() { 17 | log() << "test_defer -- begin" << std::endl; 18 | 19 | auto obs_range = observables::defer([](){ 20 | log() << "create range observable" << std::endl; 21 | return observables::range(1, 3); 22 | }); 23 | 24 | log() << "range #1" << std::endl; 25 | doSubscribe(obs_range); 26 | 27 | log() << "range #2" << std::endl; 28 | doSubscribe(obs_range); 29 | 30 | auto obs_interval = observables::defer([](){ 31 | log() << "create interval observable" << std::endl; 32 | return observables::interval(std::chrono::milliseconds(200), schedulers::new_thread_scheduler()); 33 | }); 34 | 35 | { 36 | log() << "interval #1" << std::endl; 37 | auto x = doSubscribe(obs_interval); 38 | wait(2000); 39 | x.unsubscribe(); 40 | wait(2000); 41 | } 42 | 43 | { 44 | log() << "interval #2" << std::endl; 45 | auto x = doSubscribe(obs_interval); 46 | wait(2000); 47 | x.unsubscribe(); 48 | wait(2000); 49 | } 50 | 51 | log() << "test_defer -- end" << std::endl << std::endl; 52 | } -------------------------------------------------------------------------------- /test/delay.cpp: -------------------------------------------------------------------------------- 1 | #if defined(SUPPORTS_OPERATORS_IN_OBSERVABLE) || defined(SUPPORTS_RXCPP_COMPATIBLE) 2 | #include 3 | #else 4 | #include 5 | #include 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | #include "common.h" 12 | 13 | using namespace another_rxcpp; 14 | using namespace another_rxcpp::operators; 15 | 16 | void test_delay() { 17 | 18 | log() << "test_delay -- begin" << std::endl; 19 | 20 | thread_group threads; 21 | 22 | auto emitter = observable<>::create([threads](subscriber s){ 23 | threads.push([s](){ 24 | for(int i = 0; i <= 5; i++){ 25 | if(!s.is_subscribed()) break; 26 | std::this_thread::sleep_for(std::chrono::milliseconds(200)); 27 | log() << "emit " << i << std::endl; 28 | s.on_next(i); 29 | } 30 | s.on_completed(); 31 | }); 32 | }); 33 | 34 | 35 | auto o = emitter 36 | | observe_on(schedulers::new_thread_scheduler()) 37 | | delay(std::chrono::milliseconds(500)); 38 | 39 | auto x = doSubscribe(o); 40 | 41 | while(x.is_subscribed()) {} 42 | threads.join_all(); 43 | 44 | log() << "test_delay -- end" << std::endl << std::endl; 45 | } -------------------------------------------------------------------------------- /test/distinct_until_changed.cpp: -------------------------------------------------------------------------------- 1 | #if defined(SUPPORTS_OPERATORS_IN_OBSERVABLE) || defined(SUPPORTS_RXCPP_COMPATIBLE) 2 | #include 3 | #else 4 | #include 5 | #include 6 | #endif 7 | 8 | #include 9 | #include 10 | #include "common.h" 11 | #include 12 | 13 | using namespace another_rxcpp; 14 | using namespace another_rxcpp::operators; 15 | 16 | void test_distinct_until_changed() { 17 | log() << "test_distinct_until_changed -- begin" << std::endl; 18 | 19 | subjects::behavior sbj(100); 20 | doSubscribe(sbj.as_observable() | distinct_until_changed()); 21 | sbj.as_subscriber().on_next(100); 22 | sbj.as_subscriber().on_next(1); 23 | sbj.as_subscriber().on_next(1); 24 | sbj.as_subscriber().on_next(2); 25 | sbj.as_subscriber().on_next(2); 26 | sbj.as_subscriber().on_next(3); 27 | sbj.as_subscriber().on_next(4); 28 | sbj.as_subscriber().on_completed(); 29 | 30 | log() << "test_distinct_until_changed -- end" << std::endl << std::endl; 31 | } -------------------------------------------------------------------------------- /test/empty.cpp: -------------------------------------------------------------------------------- 1 | #if defined(SUPPORTS_OPERATORS_IN_OBSERVABLE) || defined(SUPPORTS_RXCPP_COMPATIBLE) 2 | #include 3 | #else 4 | #include 5 | #include 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | #include "common.h" 12 | 13 | using namespace another_rxcpp; 14 | using namespace another_rxcpp::operators; 15 | 16 | void test_empty() { 17 | log() << "test_empty -- begin" << std::endl; 18 | 19 | auto o = observables::just(1) 20 | | flat_map([](int x){ 21 | log() << x << std::endl; 22 | return observables::empty(); 23 | }); 24 | 25 | doSubscribe(o); 26 | 27 | log() << "test_empty -- end" << std::endl << std::endl; 28 | } -------------------------------------------------------------------------------- /test/error.cpp: -------------------------------------------------------------------------------- 1 | #if defined(SUPPORTS_OPERATORS_IN_OBSERVABLE) || defined(SUPPORTS_RXCPP_COMPATIBLE) 2 | #include 3 | #else 4 | #include 5 | #include 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | #include "common.h" 12 | 13 | using namespace another_rxcpp; 14 | using namespace another_rxcpp::operators; 15 | 16 | struct my_err : public std::exception { 17 | std::string s = "my_err"; 18 | virtual const char* what() const noexcept { return s.c_str(); } 19 | }; 20 | 21 | 22 | void test_error() { 23 | log() << "test_error -- begin" << std::endl; 24 | 25 | { 26 | auto o = observables::just(1) 27 | | flat_map([](int x){ 28 | log() << x << std::endl; 29 | return observables::error(std::make_exception_ptr(my_err())); 30 | }); 31 | doSubscribe(o); 32 | } 33 | 34 | { 35 | auto o = observables::just(1) 36 | | flat_map([](int x){ 37 | log() << x << std::endl; 38 | return observables::error(my_err()); 39 | }); 40 | doSubscribe(o); 41 | } 42 | 43 | 44 | log() << "test_error -- end" << std::endl << std::endl; 45 | } -------------------------------------------------------------------------------- /test/filter.cpp: -------------------------------------------------------------------------------- 1 | #if defined(SUPPORTS_OPERATORS_IN_OBSERVABLE) || defined(SUPPORTS_RXCPP_COMPATIBLE) 2 | #include 3 | #else 4 | #include 5 | #include 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | #include "common.h" 12 | 13 | using namespace another_rxcpp; 14 | using namespace another_rxcpp::operators; 15 | 16 | void test_filter() { 17 | log() << "test_filter -- begin" << std::endl; 18 | 19 | auto o = observables::range(1, 100); 20 | 21 | doSubscribe(o | filter([](int x){ return x < 5; })); 22 | 23 | auto x = doSubscribe( 24 | o 25 | | flat_map([](int x){ 26 | return ovalue(x, 100); 27 | }) 28 | | filter([](int x){ 29 | return x > 10 && x < 20; 30 | }) 31 | ); 32 | while(x.is_subscribed()) {} 33 | 34 | log() << "test_filter -- end" << std::endl << std::endl; 35 | } -------------------------------------------------------------------------------- /test/finally.cpp: -------------------------------------------------------------------------------- 1 | #if defined(SUPPORTS_OPERATORS_IN_OBSERVABLE) || defined(SUPPORTS_RXCPP_COMPATIBLE) 2 | #include 3 | #else 4 | #include 5 | #include 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "common.h" 13 | 14 | using namespace another_rxcpp; 15 | using namespace another_rxcpp::operators; 16 | using namespace another_rxcpp::schedulers; 17 | 18 | void test_finally() { 19 | log() << "test_finally -- begin" << std::endl; 20 | 21 | auto o = observables::just(1) 22 | | map([](int x){ 23 | log() << x << std::endl; 24 | return x + 1; 25 | }) 26 | | finally([](){ 27 | log() << "finally #1" << std::endl; 28 | }) 29 | | flat_map([](int x){ 30 | log() << x << std::endl; 31 | return observables::just(x); 32 | }) 33 | | finally([](){ 34 | log() << "finally #2" << std::endl; 35 | }); 36 | 37 | auto x = doSubscribe(o); 38 | 39 | while(x.is_subscribed()) {} 40 | 41 | log() << "test_finally -- end" << std::endl << std::endl; 42 | } -------------------------------------------------------------------------------- /test/first.cpp: -------------------------------------------------------------------------------- 1 | #if defined(SUPPORTS_OPERATORS_IN_OBSERVABLE) || defined(SUPPORTS_RXCPP_COMPATIBLE) 2 | #include 3 | #else 4 | #include 5 | #include 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | #include "common.h" 12 | 13 | using namespace another_rxcpp; 14 | using namespace another_rxcpp::operators; 15 | 16 | void test_first() { 17 | log() << "test_first -- begin" << std::endl; 18 | 19 | doSubscribe(observables::range(1, 100) | first()); 20 | 21 | thread_group threads; 22 | 23 | auto x = doSubscribe( 24 | interval_range(1, 100, 100, threads) 25 | | first() 26 | ); 27 | while(x.is_subscribed()) {} 28 | threads.join_all(); 29 | 30 | log() << "test_first -- end" << std::endl << std::endl; 31 | } -------------------------------------------------------------------------------- /test/flat_map.cpp: -------------------------------------------------------------------------------- 1 | #if defined(SUPPORTS_OPERATORS_IN_OBSERVABLE) || defined(SUPPORTS_RXCPP_COMPATIBLE) 2 | #include 3 | #else 4 | #include 5 | #include 6 | #endif 7 | 8 | #include 9 | #include "common.h" 10 | 11 | using namespace another_rxcpp; 12 | using namespace another_rxcpp::operators; 13 | 14 | void test_flat_map() { 15 | log() << "test_flat_map -- begin" << std::endl; 16 | 17 | auto o = ovalue(1) 18 | | flat_map([](int x){ 19 | log() << x << std::endl; 20 | return ovalue(x + 1); 21 | }) 22 | | flat_map([](int x){ 23 | log() << x << std::endl; 24 | return ovalue(std::string("abc")); 25 | }); 26 | doSubscribe(o); 27 | 28 | log() << "test_flat_map -- end" << std::endl << std::endl; 29 | } -------------------------------------------------------------------------------- /test/inflow_restriction.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "common.h" 13 | 14 | using namespace another_rxcpp; 15 | using namespace another_rxcpp::schedulers; 16 | using namespace another_rxcpp::operators; 17 | using namespace another_rxcpp::utils; 18 | 19 | void test_inflow_restriction() { 20 | log() << "test_inflow_restriction -- begin" << std::endl; 21 | 22 | enum class result { success, failure }; 23 | struct long_api { 24 | std::shared_ptr mtx_ = std::make_shared(); 25 | std::shared_ptr count_ = std::make_shared(0); 26 | observable call() { 27 | auto mtx = mtx_; 28 | auto count = count_; 29 | return observables::just(unit{}, new_thread_scheduler()) 30 | | tap([mtx, count](unit){ 31 | std::lock_guard lock(*mtx); 32 | (*count)++; 33 | const int x = (*count)++; 34 | std::cout << std::this_thread::get_id() << " : enter #" << x << std::endl; 35 | }) 36 | | delay(std::chrono::seconds(1), new_thread_scheduler()) 37 | | map([](unit){ 38 | return result::success; 39 | }) 40 | | tap([mtx, count](result){ 41 | std::lock_guard lock(*mtx); 42 | const int x = (*count)--; 43 | std::cout << std::this_thread::get_id() << " : leave #" << x << std::endl; 44 | }); 45 | } 46 | }; 47 | 48 | { 49 | log() << "without inflow_restriction" << std::endl; 50 | auto mtx = std::make_shared(); 51 | auto list = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; 52 | auto api = std::make_shared(); 53 | 54 | auto sbsc = ( 55 | observables::iterate(list) 56 | | flat_map([api](int n){ 57 | return api->call() 58 | | map([n](result){ 59 | return n; 60 | }); 61 | }) 62 | ) 63 | .subscribe({ 64 | [mtx](const int& x){ 65 | std::lock_guard lock(*mtx); 66 | log() << "next " << x << std::endl; 67 | }, 68 | [mtx](std::exception_ptr){ 69 | std::lock_guard lock(*mtx); 70 | log() << "error " << std::endl; 71 | }, 72 | [mtx](){ 73 | std::lock_guard lock(*mtx); 74 | log() << "completed " << std::endl; 75 | } 76 | }); 77 | 78 | while(sbsc.is_subscribed()) {} 79 | } 80 | 81 | { 82 | log() << "use inflow_restriction" << std::endl; 83 | auto mtx = std::make_shared(); 84 | auto list = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; 85 | auto api = std::make_shared(); 86 | auto ifr = std::make_shared>(); 87 | 88 | auto sbsc = ( 89 | observables::iterate(list) 90 | | flat_map([ifr, api](int n){ 91 | return ifr->enter(api->call()) 92 | | map([n](result){ 93 | return n; 94 | }); 95 | }) 96 | ) 97 | .subscribe({ 98 | [mtx](const int& x){ 99 | std::lock_guard lock(*mtx); 100 | log() << "next " << x << std::endl; 101 | }, 102 | [mtx](std::exception_ptr){ 103 | std::lock_guard lock(*mtx); 104 | log() << "error " << std::endl; 105 | }, 106 | [mtx](){ 107 | std::lock_guard lock(*mtx); 108 | log() << "completed " << std::endl; 109 | } 110 | }); 111 | 112 | while(sbsc.is_subscribed()) {} 113 | } 114 | 115 | { 116 | log() << "use inflow_restriction + unsubscribe" << std::endl; 117 | auto mtx = std::make_shared(); 118 | auto list = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; 119 | auto api = std::make_shared(); 120 | auto ifr = std::make_shared>(); 121 | 122 | auto sbsc = ( 123 | observables::iterate(list) 124 | | flat_map([ifr, api](int n){ 125 | return ifr->enter(api->call()) 126 | | tap([n](auto){ 127 | log() << "tap: " << n << std::endl; 128 | }) 129 | | map([n](result){ 130 | return n; 131 | }); 132 | }) 133 | ) 134 | .take(5) 135 | .subscribe({ 136 | [mtx](const int& x){ 137 | std::lock_guard lock(*mtx); 138 | log() << "next " << x << std::endl; 139 | }, 140 | [mtx](std::exception_ptr){ 141 | std::lock_guard lock(*mtx); 142 | log() << "error " << std::endl; 143 | }, 144 | [mtx](){ 145 | std::lock_guard lock(*mtx); 146 | log() << "completed " << std::endl; 147 | } 148 | }); 149 | 150 | while(sbsc.is_subscribed()) {} 151 | } 152 | 153 | log() << "test_inflow_restriction -- end" << std::endl << std::endl; 154 | } -------------------------------------------------------------------------------- /test/interval.cpp: -------------------------------------------------------------------------------- 1 | #if defined(SUPPORTS_OPERATORS_IN_OBSERVABLE) || defined(SUPPORTS_RXCPP_COMPATIBLE) 2 | #include 3 | #else 4 | #include 5 | #include 6 | #endif 7 | 8 | #include 9 | #include 10 | #include "common.h" 11 | 12 | using namespace another_rxcpp; 13 | 14 | void test_interval() { 15 | log() << "test_interval -- begin" << std::endl; 16 | 17 | auto x = doSubscribe(observables::interval(std::chrono::milliseconds(200), schedulers::new_thread_scheduler())); 18 | wait(2000); 19 | x.unsubscribe(); 20 | wait(2000); 21 | 22 | log() << "test_interval -- end" << std::endl << std::endl; 23 | } -------------------------------------------------------------------------------- /test/iterate.cpp: -------------------------------------------------------------------------------- 1 | #if defined(SUPPORTS_OPERATORS_IN_OBSERVABLE) || defined(SUPPORTS_RXCPP_COMPATIBLE) 2 | #include 3 | #else 4 | #include 5 | #include 6 | #endif 7 | 8 | #include 9 | #include "common.h" 10 | #include 11 | 12 | using namespace another_rxcpp; 13 | using namespace another_rxcpp::operators; 14 | 15 | void test_iterate() { 16 | log() << "test_iterate -- begin" << std::endl; 17 | 18 | std::vector arr = {1, 10, 1000, 10000, 100000}; 19 | auto o = observables::iterate(arr); 20 | 21 | doSubscribe(o); 22 | 23 | log() << "test_iterate -- end" << std::endl << std::endl; 24 | } -------------------------------------------------------------------------------- /test/just.cpp: -------------------------------------------------------------------------------- 1 | #if defined(SUPPORTS_OPERATORS_IN_OBSERVABLE) || defined(SUPPORTS_RXCPP_COMPATIBLE) 2 | #include 3 | #else 4 | #include 5 | #include 6 | #endif 7 | 8 | #include 9 | #include "common.h" 10 | 11 | using namespace another_rxcpp; 12 | using namespace another_rxcpp::operators; 13 | 14 | void test_just() { 15 | log() << "test_just -- begin" << std::endl; 16 | 17 | doSubscribe(observables::just(1)); 18 | doSubscribe(observables::just(std::string("abc"))); 19 | doSubscribe(observables::just(1.23)); 20 | 21 | log() << "test_just -- end" << std::endl << std::endl; 22 | } -------------------------------------------------------------------------------- /test/last.cpp: -------------------------------------------------------------------------------- 1 | #if defined(SUPPORTS_OPERATORS_IN_OBSERVABLE) || defined(SUPPORTS_RXCPP_COMPATIBLE) 2 | #include 3 | #else 4 | #include 5 | #include 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | #include "common.h" 12 | 13 | using namespace another_rxcpp; 14 | using namespace another_rxcpp::operators; 15 | 16 | void test_last() { 17 | log() << "test_last -- begin" << std::endl; 18 | 19 | auto o = observables::range(1, 100); 20 | 21 | doSubscribe(observables::range(1, 100) | last()); 22 | 23 | thread_group threads; 24 | 25 | auto x = doSubscribe( 26 | interval_range(1, 10, 100, threads) 27 | | last() 28 | ); 29 | while(x.is_subscribed()) {} 30 | threads.join_all(); 31 | 32 | log() << "test_last -- end" << std::endl << std::endl; 33 | } -------------------------------------------------------------------------------- /test/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "common.h" 5 | 6 | #define DO(f) { extern void f(); f(); } 7 | 8 | int main() { 9 | log() << "**** start ****" << std::endl; 10 | 11 | DO(test_observable) 12 | DO(test_just) 13 | DO(test_range) 14 | DO(test_take) 15 | DO(test_flat_map) 16 | DO(test_map) 17 | DO(test_connectable) 18 | DO(test_subject) 19 | DO(test_error) 20 | DO(test_never) 21 | DO(test_empty) 22 | DO(test_on_error_resume_next) 23 | DO(test_retry) 24 | DO(test_observe_on) 25 | DO(test_subscribe_on) 26 | DO(test_new_thread_scheduler) 27 | DO(test_take_until) 28 | DO(test_take_while) 29 | DO(test_merge) 30 | DO(test_amb) 31 | DO(test_behavior_subject) 32 | DO(test_distinct_until_changed) 33 | DO(test_interval) 34 | DO(test_delay) 35 | DO(test_tap) 36 | DO(test_finally) 37 | DO(test_take_last) 38 | DO(test_last) 39 | DO(test_timeout) 40 | DO(test_skip_until) 41 | DO(test_skip_while) 42 | DO(test_iterate) 43 | DO(test_blocking) 44 | DO(test_ready_set_go) 45 | DO(test_inflow_restriction) 46 | DO(test_zip) 47 | DO(test_first) 48 | DO(test_filter) 49 | DO(test_something) 50 | DO(test_defer) 51 | 52 | DO(test_case_1) 53 | DO(test_case_2) 54 | DO(test_case_3) 55 | DO(test_case_4) 56 | DO(test_case_5) 57 | DO(test_case_6) 58 | DO(test_case_7) 59 | 60 | DO(move_check) 61 | 62 | #if defined(SUPPORTS_OPERATORS_IN_OBSERVABLE) 63 | DO(test_operators_in_observable) 64 | #endif /* defined(SUPPORTS_OPERATORS_IN_OBSERVABLE) */ 65 | 66 | #if defined(SUPPORTS_RXCPP_COMPATIBLE) 67 | DO(test_rxcpp_compatible) 68 | #endif /* defined(SUPPORTS_RXCPP_COMPATIBLE) */ 69 | 70 | log() << "**** finish ****" << std::endl; 71 | } 72 | 73 | -------------------------------------------------------------------------------- /test/map.cpp: -------------------------------------------------------------------------------- 1 | #if defined(SUPPORTS_OPERATORS_IN_OBSERVABLE) || defined(SUPPORTS_RXCPP_COMPATIBLE) 2 | #include 3 | #else 4 | #include 5 | #include 6 | #endif 7 | 8 | #include 9 | #include "common.h" 10 | 11 | using namespace another_rxcpp; 12 | using namespace another_rxcpp::operators; 13 | 14 | void test_map() { 15 | log() << "test_map -- begin" << std::endl; 16 | 17 | auto o = ovalue(1) 18 | | map([](int x){ 19 | log() << x << std::endl; 20 | return x + 1; 21 | }) 22 | | map([](int x){ 23 | log() << x << std::endl; 24 | return std::string("abc"); 25 | }); 26 | doSubscribe(o); 27 | 28 | log() << "test_map -- end" << std::endl << std::endl; 29 | } -------------------------------------------------------------------------------- /test/merge.cpp: -------------------------------------------------------------------------------- 1 | #if defined(SUPPORTS_OPERATORS_IN_OBSERVABLE) || defined(SUPPORTS_RXCPP_COMPATIBLE) 2 | #include 3 | #else 4 | #include 5 | #include 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "common.h" 13 | 14 | using namespace another_rxcpp; 15 | using namespace another_rxcpp::operators; 16 | 17 | void test_merge() { 18 | 19 | log() << "test_merge -- begin" << std::endl; 20 | 21 | { 22 | log() << "completed" << std::endl; 23 | 24 | auto sbj1 = subjects::subject(); 25 | auto sbj2 = subjects::subject(); 26 | auto o = sbj1.as_observable() 27 | | merge(sbj2.as_observable()); 28 | auto x = doSubscribe(o); 29 | 30 | sbj1.as_subscriber().on_next(11); 31 | sbj2.as_subscriber().on_next(21); 32 | sbj1.as_subscriber().on_next(12); 33 | sbj2.as_subscriber().on_next(22); 34 | sbj1.as_subscriber().on_completed(); 35 | sbj2.as_subscriber().on_next(23); 36 | sbj2.as_subscriber().on_completed(); 37 | } 38 | 39 | { 40 | log() << "error" << std::endl; 41 | 42 | auto sbj1 = subjects::subject(); 43 | auto sbj2 = subjects::subject(); 44 | auto o = sbj1.as_observable() 45 | | merge(sbj2.as_observable()); 46 | auto x = doSubscribe(o); 47 | 48 | sbj1.as_subscriber().on_next(11); 49 | sbj2.as_subscriber().on_next(21); 50 | sbj1.as_subscriber().on_next(12); 51 | sbj2.as_subscriber().on_next(22); 52 | sbj1.as_subscriber().on_error(std::make_exception_ptr(std::exception())); 53 | sbj2.as_subscriber().on_next(23); 54 | sbj2.as_subscriber().on_completed(); 55 | } 56 | 57 | { 58 | log() << "multi thread #1" << std::endl; 59 | 60 | thread_group threads; 61 | 62 | auto emitter = observable<>::create([threads](subscriber s){ 63 | threads.push([s](){ 64 | for(int i = 0; i < 10; i++){ 65 | if(!s.is_subscribed()) break; 66 | std::this_thread::sleep_for(std::chrono::milliseconds(300)); 67 | s.on_next(i); 68 | } 69 | s.on_completed(); 70 | }); 71 | }); 72 | 73 | auto emitter2 = emitter 74 | | map([](int x){ 75 | return x + 1000; 76 | }); 77 | 78 | auto emitter3 = emitter 79 | | map([](int x){ 80 | return x + 2000; 81 | }); 82 | 83 | auto o = emitter | merge(emitter2, emitter3); 84 | 85 | auto x = doSubscribe(o); 86 | 87 | while(x.is_subscribed()) {} 88 | threads.join_all(); 89 | } 90 | 91 | 92 | { 93 | log() << "multi thread #2" << std::endl; 94 | 95 | thread_group threads; 96 | 97 | auto sbj1 = subjects::subject(); 98 | auto sbj2 = subjects::subject(); 99 | auto sbj3 = subjects::subject(); 100 | 101 | auto o = sbj1.as_observable() | merge(schedulers::new_thread_scheduler(), sbj2.as_observable(), sbj3.as_observable()); 102 | 103 | auto x = doSubscribe(o); 104 | 105 | threads.push([sbj1](){ 106 | for(int i = 100; i < 110; i++){ 107 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); 108 | sbj1.as_subscriber().on_next(i); 109 | } 110 | sbj1.as_subscriber().on_completed(); 111 | }); 112 | 113 | threads.push([sbj2](){ 114 | for(int i = 200; i < 210; i++){ 115 | std::this_thread::sleep_for(std::chrono::milliseconds(120)); 116 | sbj2.as_subscriber().on_next(i); 117 | } 118 | sbj2.as_subscriber().on_completed(); 119 | }); 120 | 121 | threads.push([sbj3](){ 122 | for(int i = 300; i < 310; i++){ 123 | std::this_thread::sleep_for(std::chrono::milliseconds(140)); 124 | sbj3.as_subscriber().on_next(i); 125 | } 126 | sbj3.as_subscriber().on_completed(); 127 | }); 128 | 129 | while(x.is_subscribed()) {} 130 | threads.join_all(); 131 | } 132 | 133 | 134 | log() << "test_merge -- end" << std::endl << std::endl; 135 | } -------------------------------------------------------------------------------- /test/move_check.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "common.h" 6 | 7 | using namespace another_rxcpp; 8 | using namespace another_rxcpp::operators; 9 | 10 | struct A { 11 | static int ctor_default; 12 | static int ctor_copy; 13 | static int ctor_move; 14 | static int dtor; 15 | A() { 16 | ctor_default++; 17 | log() << "A::ctor default" << std::endl; 18 | } 19 | A(const A&) { 20 | ctor_copy++; 21 | log() << "A::ctor copy" << std::endl; 22 | } 23 | A(A&&) { 24 | ctor_move++; 25 | log() << "A::ctor move" << std::endl; 26 | } 27 | ~A() { 28 | dtor++; 29 | log() << "A::dtor" << std::endl; 30 | } 31 | operator int () const { return 1; } 32 | 33 | static void reset() { 34 | ctor_default = 0; 35 | ctor_copy = 0; 36 | ctor_move = 0; 37 | dtor = 0; 38 | } 39 | 40 | static void dump() { 41 | log() 42 | << "d: " << ctor_default 43 | << ", c: " << ctor_copy 44 | << ", m: " << ctor_move 45 | << ", +-: " << (ctor_default + ctor_copy + ctor_move - dtor) 46 | << std::endl; 47 | } 48 | }; 49 | 50 | int A::ctor_default; 51 | int A::ctor_copy; 52 | int A::ctor_move; 53 | int A::dtor; 54 | 55 | 56 | void move_check() { 57 | log() << "move_check -- begin" << std::endl; 58 | 59 | { 60 | log () << "** just **" << std::endl; 61 | A::reset(); 62 | doSubscribe(observables::just(A())); 63 | A::dump(); 64 | } 65 | 66 | 67 | { 68 | log () << "** just twice **" << std::endl; 69 | A::reset(); 70 | { 71 | auto o = observables::just(A()); 72 | doSubscribe(o); 73 | doSubscribe(o); 74 | } 75 | A::dump(); 76 | } 77 | 78 | auto o = observable<>::create([](subscriber s){ 79 | s.on_next(A()); 80 | s.on_completed(); 81 | }); 82 | 83 | { 84 | log () << "** plain **" << std::endl; 85 | A::reset(); 86 | doSubscribe(o); 87 | A::dump(); 88 | } 89 | 90 | { 91 | log () << "** flat_map **" << std::endl; 92 | A::reset(); 93 | doSubscribe(o | flat_map([&](const A&){ 94 | return o; 95 | })); 96 | A::dump(); 97 | } 98 | 99 | { 100 | log () << "** map **" << std::endl; 101 | A::reset(); 102 | doSubscribe(o | map([](const A&){ 103 | return A(); 104 | })); 105 | A::dump(); 106 | } 107 | 108 | log() << "move_check -- end" << std::endl << std::endl; 109 | } -------------------------------------------------------------------------------- /test/never.cpp: -------------------------------------------------------------------------------- 1 | #if defined(SUPPORTS_OPERATORS_IN_OBSERVABLE) || defined(SUPPORTS_RXCPP_COMPATIBLE) 2 | #include 3 | #else 4 | #include 5 | #include 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | #include "common.h" 12 | 13 | using namespace another_rxcpp; 14 | using namespace another_rxcpp::operators; 15 | 16 | void test_never() { 17 | log() << "test_never -- begin" << std::endl; 18 | 19 | auto o = observables::just(1) 20 | | flat_map([](int x){ 21 | log() << x << std::endl; 22 | return observables::never(); 23 | }); 24 | 25 | auto x = doSubscribe(o); 26 | x.unsubscribe(); 27 | 28 | log() << "test_never -- end" << std::endl << std::endl; 29 | } -------------------------------------------------------------------------------- /test/new_thread_scheduler.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "common.h" 9 | 10 | using namespace another_rxcpp; 11 | using namespace another_rxcpp::operators; 12 | using namespace another_rxcpp::schedulers; 13 | 14 | void test_new_thread_scheduler() { 15 | log() << "test_new_thread_scheduler -- begin" << std::endl; 16 | 17 | auto o = observables::range(1, 10) 18 | | observe_on(new_thread_scheduler()) 19 | | flat_map([](int x){ 20 | log() << x << std::endl; 21 | return observables::just(x); 22 | }); 23 | 24 | auto x = doSubscribe(o); 25 | 26 | while(x.is_subscribed()) {} 27 | 28 | log() << "test_new_thread_scheduler -- end" << std::endl << std::endl; 29 | } -------------------------------------------------------------------------------- /test/observable.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include "common.h" 6 | 7 | using namespace another_rxcpp; 8 | using namespace another_rxcpp::operators; 9 | 10 | void test_observable() { 11 | log() << "test_observable -- begin" << std::endl; 12 | 13 | 14 | { 15 | log() << "#1" << std::endl; 16 | auto ob = ovalue(123) 17 | | flat_map([](int x){ 18 | log() << x << std::endl; 19 | return ovalue(std::string("abc")) 20 | | map([](std::string x){ 21 | log() << x << std::endl; 22 | return 456; 23 | }); 24 | }); 25 | doSubscribe(ob); 26 | } 27 | 28 | { 29 | auto ob = ovalue(1) 30 | | flat_map([](int64_t x){ 31 | log() << x << std::endl; 32 | return ovalue(std::string("abc"), 500); 33 | }) 34 | | flat_map([](std::string x){ 35 | log() << x << std::endl; 36 | return ovalue(5); 37 | }) 38 | | flat_map([](int x){ 39 | log() << x << std::endl; 40 | return ovalue(x + 1, 500); 41 | }) 42 | | flat_map([](int x){ 43 | log() << x << std::endl; 44 | return ovalue(x + 1); 45 | }); 46 | { 47 | log() << "#3 wait until is_subscribed() == true" << std::endl; 48 | auto x = doSubscribe(ob); 49 | while(x.is_subscribed()) {} 50 | } 51 | { 52 | log() << "#4 unsubscribe after 5000ms" << std::endl; 53 | auto x = doSubscribe(ob); 54 | wait(5000); 55 | x.unsubscribe(); 56 | } 57 | { 58 | log() << "#5 unsubscribe after 700ms" << std::endl; 59 | auto x = doSubscribe(ob); 60 | wait(700); 61 | x.unsubscribe(); 62 | } 63 | } 64 | 65 | log() << "test_observable -- end" << std::endl << std::endl; 66 | } 67 | -------------------------------------------------------------------------------- /test/observe_on.cpp: -------------------------------------------------------------------------------- 1 | #if defined(SUPPORTS_OPERATORS_IN_OBSERVABLE) || defined(SUPPORTS_RXCPP_COMPATIBLE) 2 | #include 3 | #else 4 | #include 5 | #include 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "common.h" 14 | 15 | using namespace another_rxcpp; 16 | using namespace another_rxcpp::operators; 17 | using namespace another_rxcpp::schedulers; 18 | 19 | void test_observe_on() { 20 | log() << "test_observe_on -- begin" << std::endl; 21 | 22 | auto o = observables::range(1, 10) 23 | | observe_on(default_scheduler()) 24 | | flat_map([](int x){ 25 | log() << x << std::endl; 26 | return observables::just(x); 27 | }); 28 | 29 | auto x = doSubscribe(o); 30 | 31 | while(x.is_subscribed()) {} 32 | 33 | log() << "test_observe_on -- end" << std::endl << std::endl; 34 | } -------------------------------------------------------------------------------- /test/on_error_resume_next.cpp: -------------------------------------------------------------------------------- 1 | #if defined(SUPPORTS_OPERATORS_IN_OBSERVABLE) || defined(SUPPORTS_RXCPP_COMPATIBLE) 2 | #include 3 | #else 4 | #include 5 | #include 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "common.h" 13 | 14 | using namespace another_rxcpp; 15 | using namespace another_rxcpp::operators; 16 | 17 | void test_on_error_resume_next() { 18 | log() << "test_on_error_resume_next -- begin" << std::endl; 19 | 20 | auto o = ovalue(1) 21 | | flat_map([](int x){ 22 | log() << x << std::endl; 23 | return observables::error(std::make_exception_ptr(std::exception())); 24 | }) 25 | | on_error_resume_next([](std::exception_ptr err){ 26 | log() << "on_error_resume_next #1" << std::endl; 27 | return ovalue(2); 28 | }) 29 | | map([](int x){ 30 | log() << x << std::endl; 31 | throw std::exception(); 32 | return x + 1; 33 | }) 34 | | on_error_resume_next([](std::exception_ptr err){ 35 | log() << "on_error_resume_next #2" << std::endl; 36 | throw std::exception(); 37 | return ovalue(3); 38 | }); 39 | 40 | doSubscribe(o); 41 | 42 | log() << "test_on_error_resume_next -- end" << std::endl << std::endl; 43 | } -------------------------------------------------------------------------------- /test/operators_in_observable.cpp: -------------------------------------------------------------------------------- 1 | #if defined(SUPPORTS_OPERATORS_IN_OBSERVABLE) 2 | 3 | #include 4 | #include "common.h" 5 | 6 | using namespace another_rxcpp; 7 | using namespace another_rxcpp::operators; 8 | 9 | void test_operators_in_observable() { 10 | log() << "test_operators_in_observable -- begin" << std::endl; 11 | 12 | auto o = ovalue(1) 13 | .flat_map([](const auto& x){ 14 | log() << x << std::endl; 15 | return observables::error(std::make_exception_ptr(std::exception())); 16 | }) 17 | .on_error_resume_next([](auto err){ 18 | log() << "operators_in_observable #1" << std::endl; 19 | return ovalue(2); 20 | }) 21 | .map([](const auto& x){ 22 | log() << x << std::endl; 23 | throw std::exception(); 24 | return std::string("abc"); 25 | }) 26 | .on_error_resume_next([](auto err){ 27 | log() << "operators_in_observable #2" << std::endl; 28 | throw std::exception(); 29 | return ovalue(std::string("abc")); 30 | }); 31 | 32 | doSubscribe(o); 33 | 34 | log() << "test_operators_in_observable -- end" << std::endl << std::endl; 35 | } 36 | 37 | #endif /* defined(SUPPORTS_OPERATORS_IN_OBSERVABLE) */ -------------------------------------------------------------------------------- /test/range.cpp: -------------------------------------------------------------------------------- 1 | #if defined(SUPPORTS_OPERATORS_IN_OBSERVABLE) || defined(SUPPORTS_RXCPP_COMPATIBLE) 2 | #include 3 | #else 4 | #include 5 | #include 6 | #endif 7 | 8 | #include 9 | #include "common.h" 10 | 11 | using namespace another_rxcpp; 12 | 13 | void test_range() { 14 | log() << "test_range -- begin" << std::endl; 15 | 16 | log() << "1 - 10" << std::endl; 17 | doSubscribe(observables::range(1, 10)); 18 | 19 | log() << "-10 - 5" << std::endl; 20 | doSubscribe(observables::range(-10, 5)); 21 | 22 | log() << "test_range -- end" << std::endl << std::endl; 23 | } -------------------------------------------------------------------------------- /test/ready_set_go.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include "common.h" 6 | 7 | using namespace another_rxcpp; 8 | using namespace another_rxcpp::operators; 9 | 10 | void test_ready_set_go() { 11 | log() << "test_ready_set_go -- begin" << std::endl; 12 | 13 | { 14 | subjects::subject sbj; 15 | auto s = sbj.as_subscriber(); 16 | auto o = utils::ready_set_go([=](){ 17 | s.on_next(1); 18 | }, sbj.as_observable()); 19 | 20 | auto x = doSubscribe(o); 21 | 22 | s.on_next(2); 23 | s.on_next(3); 24 | s.on_completed(); 25 | while(x.is_subscribed()) {} 26 | } 27 | 28 | { 29 | subjects::subject sbj; 30 | auto s = sbj.as_subscriber(); 31 | auto o = utils::ready_set_go([=](){ 32 | s.on_next(1); 33 | }, sbj.as_observable()); 34 | 35 | auto x = doSubscribe(o); 36 | 37 | s.on_next(2); 38 | s.on_next(3); 39 | x.unsubscribe(); 40 | } 41 | 42 | { 43 | subjects::subject sbj; 44 | auto s = sbj.as_subscriber(); 45 | auto o = utils::ready_set_go([=](){ 46 | s.on_next(1); 47 | throw std::exception(); 48 | }, sbj.as_observable()); 49 | 50 | auto x = doSubscribe(o); 51 | 52 | s.on_next(2); 53 | s.on_next(3); 54 | s.on_completed(); 55 | while(x.is_subscribed()) {} 56 | } 57 | 58 | log() << "test_ready_set_go -- end" << std::endl << std::endl; 59 | } -------------------------------------------------------------------------------- /test/retry.cpp: -------------------------------------------------------------------------------- 1 | #if defined(SUPPORTS_OPERATORS_IN_OBSERVABLE) || defined(SUPPORTS_RXCPP_COMPATIBLE) 2 | #include 3 | #else 4 | #include 5 | #include 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "common.h" 14 | 15 | using namespace another_rxcpp; 16 | using namespace another_rxcpp::operators; 17 | 18 | void test_retry() { 19 | 20 | log() << "test_retry -- begin" << std::endl; 21 | 22 | auto counter = std::make_shared(0); 23 | 24 | auto o = observables::range(0, 10) 25 | | flat_map([counter](int x){ 26 | log() << "value = " << x << std::endl; 27 | if(x == 3){ 28 | (*counter)++; 29 | log() << "counter = " << *counter << std::endl; 30 | if(*counter > 5){ 31 | return observables::just(x); 32 | } 33 | else{ 34 | log() << "retry" << std::endl; 35 | return observables::error(std::make_exception_ptr(std::exception())); 36 | } 37 | } 38 | else return observables::just(x); 39 | }); 40 | 41 | log() << "retry()" << std::endl; 42 | doSubscribe(o | retry()); 43 | 44 | *counter = 0; 45 | log() << "retry(1)" << std::endl; 46 | doSubscribe(o | retry(1)); 47 | 48 | log() << "test_retry -- end" << std::endl << std::endl; 49 | } -------------------------------------------------------------------------------- /test/rxcpp_compatible.cpp: -------------------------------------------------------------------------------- 1 | #if defined(SUPPORTS_RXCPP_COMPATIBLE) 2 | 3 | #include 4 | #include 5 | #include 6 | #include "common.h" 7 | 8 | using namespace another_rxcpp; 9 | using namespace another_rxcpp::operators; 10 | 11 | void test_rxcpp_compatible() { 12 | log() << "test_rxcpp_compatible -- begin" << std::endl; 13 | 14 | auto o = ovalue(1); 15 | 16 | auto test = observable<>::just(12) 17 | .flat_map([](const auto&){ 18 | return observable<>::error(std::exception()); 19 | }) 20 | .flat_map([](const auto&){ 21 | return observable<>::never(); 22 | }) 23 | .flat_map([](const auto&){ 24 | return observable<>::range(1, 5); 25 | }) 26 | .flat_map([](const auto&){ 27 | return observable<>::empty(); 28 | }) 29 | .flat_map([](const auto&){ 30 | return observable<>::interval(std::chrono::milliseconds(100)); 31 | }) 32 | .flat_map([](const auto&){ 33 | auto arr = {1, 2, 3}; 34 | return observable<>::iterate(arr); 35 | }) 36 | .flat_map([](const auto&){ 37 | return observable<>::defer([](){ 38 | return observable<>::range(1, 3); 39 | }); 40 | }) 41 | .amb(o) 42 | .as_dynamic() 43 | .delay(std::chrono::seconds(1)) 44 | .distinct_until_changed() 45 | .finally([](){}) 46 | .flat_map([](const auto& x){ return observable<>::just(x); }) 47 | .last() 48 | .map([](const auto& x){ return x + 1; }) 49 | .first() 50 | .filter([](const auto& x){ 51 | return x < 1; 52 | }) 53 | .merge(o) 54 | .observe_on(schedulers::observe_on_new_thread()) 55 | .on_error_resume_next([](auto err){ return observable<>::error(err); }) 56 | .publish() 57 | .retry() 58 | .retry(123) 59 | .subscribe_on(schedulers::observe_on_new_thread()) 60 | .skip_until(o) 61 | .skip_while([](const auto& x) { return true; }) 62 | .take_last(1) 63 | .take_until(o) 64 | .take_while([](const auto& x) { return true; }) 65 | .take(100) 66 | .tap([](const auto&){}) 67 | .timeout(std::chrono::hours(2)) 68 | .zip(o, o, o) 69 | .zip([](const auto& a, const auto& b, const auto& c, const auto& d){ 70 | return std::get<0>(a); 71 | }, o, o, o) 72 | .as_blocking(); 73 | 74 | log() << "test_rxcpp_compatible -- end" << std::endl << std::endl; 75 | } 76 | 77 | #endif /* defined(SUPPORTS_RXCPP_COMPATIBLE) */ -------------------------------------------------------------------------------- /test/skip_until.cpp: -------------------------------------------------------------------------------- 1 | #if defined(SUPPORTS_OPERATORS_IN_OBSERVABLE) || defined(SUPPORTS_RXCPP_COMPATIBLE) 2 | #include 3 | #else 4 | #include 5 | #include 6 | #endif 7 | 8 | #include 9 | #include 10 | #include "common.h" 11 | 12 | using namespace another_rxcpp; 13 | using namespace another_rxcpp::operators; 14 | 15 | void test_skip_until() { 16 | 17 | log() << "test_skip_until -- begin" << std::endl; 18 | 19 | thread_group threads; 20 | subjects::subject trigger; 21 | auto emitter = observable<>::create([threads](subscriber s){ 22 | threads.push([s](){ 23 | for(int i = 0; i < 10; i++){ 24 | if(!s.is_subscribed()) break; 25 | std::this_thread::sleep_for(std::chrono::milliseconds(300)); 26 | s.on_next(i); 27 | } 28 | s.on_completed(); 29 | }); 30 | }); 31 | 32 | 33 | auto o = emitter | skip_until(trigger.as_observable()); 34 | 35 | auto x = doSubscribe(o); 36 | 37 | setTimeout([trigger](){ 38 | trigger.as_subscriber().on_next(1); 39 | trigger.as_subscriber().on_completed(); 40 | }, 2000); 41 | 42 | while(x.is_subscribed()) {} 43 | threads.join_all(); 44 | 45 | log() << "test_skip_until -- end" << std::endl << std::endl; 46 | } -------------------------------------------------------------------------------- /test/skip_while.cpp: -------------------------------------------------------------------------------- 1 | #if defined(SUPPORTS_OPERATORS_IN_OBSERVABLE) || defined(SUPPORTS_RXCPP_COMPATIBLE) 2 | #include 3 | #else 4 | #include 5 | #include 6 | #endif 7 | 8 | #include 9 | #include 10 | #include "common.h" 11 | 12 | using namespace another_rxcpp; 13 | using namespace another_rxcpp::operators; 14 | 15 | void test_skip_while() { 16 | log() << "test_skip_while -- begin" << std::endl; 17 | 18 | auto o = observables::range(1, 100); 19 | 20 | doSubscribe(o | skip_while([](auto x){ 21 | return x < 90; 22 | })); 23 | 24 | log() << "test_skip_while -- end" << std::endl << std::endl; 25 | } -------------------------------------------------------------------------------- /test/something.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "common.h" 9 | 10 | using namespace another_rxcpp; 11 | using namespace another_rxcpp::operators; 12 | using namespace another_rxcpp::utils; 13 | 14 | void test_something() { 15 | 16 | log() << "test_something -- begin" << std::endl; 17 | 18 | auto counter = std::make_shared(0); 19 | 20 | auto o = observables::range(0, 10) 21 | | map([counter](int x){ 22 | if(x == 3){ 23 | (*counter)++; 24 | log() << "counter / value = " << *counter << " / " << x << std::endl; 25 | if(*counter < 3){ 26 | log() << "retry" << std::endl; 27 | something<>::retry(); 28 | } 29 | else{ 30 | log() << "emit error" << std::endl; 31 | return something<>::error(std::make_exception_ptr(std::exception())); 32 | } 33 | } 34 | log() << "counter / value = " << *counter << " / " << x << std::endl; 35 | return something<>::success(x); 36 | }) 37 | | retry() 38 | | flat_map([](something x){ 39 | return x.proceed(); 40 | }); 41 | 42 | log() << "something()" << std::endl; 43 | doSubscribe(o); 44 | 45 | log() << "test_something -- end" << std::endl << std::endl; 46 | } -------------------------------------------------------------------------------- /test/subject.cpp: -------------------------------------------------------------------------------- 1 | #if defined(SUPPORTS_OPERATORS_IN_OBSERVABLE) || defined(SUPPORTS_RXCPP_COMPATIBLE) 2 | #include 3 | #else 4 | #include 5 | #include 6 | #endif 7 | 8 | #include 9 | #include "common.h" 10 | 11 | using namespace another_rxcpp; 12 | using namespace another_rxcpp::operators; 13 | 14 | void test_subject() { 15 | log() << "test_subject -- begin" << std::endl; 16 | 17 | thread_group threads; 18 | subjects::subject sbj; 19 | 20 | threads.push([sbj]() mutable { 21 | int i = 0; 22 | while(sbj.as_subscriber().is_subscribed()){ 23 | std::this_thread::sleep_for(std::chrono::seconds(1)); 24 | sbj.as_subscriber().on_next(i++); 25 | } 26 | log() << "emit done" << std::endl; 27 | }); 28 | 29 | wait(2500); 30 | auto s1 = doSubscribe(sbj.as_observable()); 31 | 32 | wait(1000); 33 | auto s2 = doSubscribe(sbj.as_observable()); 34 | 35 | wait(1000); 36 | auto s3 = doSubscribe(sbj.as_observable()); 37 | 38 | wait(500); 39 | s1.unsubscribe(); 40 | 41 | wait(700); 42 | s2.unsubscribe(); 43 | 44 | wait(900); 45 | s3.unsubscribe(); 46 | 47 | auto s4 = doSubscribe(sbj.as_observable()); 48 | 49 | wait(2000); 50 | 51 | sbj.as_subscriber().on_completed(); 52 | doSubscribe(sbj.as_observable()); 53 | 54 | subjects::subject sbj2; 55 | sbj2.as_subscriber().on_error(std::make_exception_ptr(std::exception())); 56 | doSubscribe(sbj2.as_observable()); 57 | 58 | threads.join_all(); 59 | 60 | log() << "test_subject -- end" << std::endl << std::endl; 61 | } -------------------------------------------------------------------------------- /test/subscribe_on.cpp: -------------------------------------------------------------------------------- 1 | #if defined(SUPPORTS_OPERATORS_IN_OBSERVABLE) || defined(SUPPORTS_RXCPP_COMPATIBLE) 2 | #include 3 | #else 4 | #include 5 | #include 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "common.h" 14 | 15 | using namespace another_rxcpp; 16 | using namespace another_rxcpp::operators; 17 | using namespace another_rxcpp::schedulers; 18 | 19 | void test_subscribe_on() { 20 | log() << "test_subscribe_on -- begin" << std::endl; 21 | 22 | auto o = observables::range(1, 10) 23 | | subscribe_on(default_scheduler()) 24 | | flat_map([](int x){ 25 | log() << x << std::endl; 26 | return observables::just(x); 27 | }); 28 | 29 | auto x = doSubscribe(o); 30 | 31 | while(x.is_subscribed()) {} 32 | 33 | log() << "test_subscribe_on -- end" << std::endl << std::endl; 34 | } -------------------------------------------------------------------------------- /test/take.cpp: -------------------------------------------------------------------------------- 1 | #if defined(SUPPORTS_OPERATORS_IN_OBSERVABLE) || defined(SUPPORTS_RXCPP_COMPATIBLE) 2 | #include 3 | #else 4 | #include 5 | #include 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | #include "common.h" 12 | 13 | using namespace another_rxcpp; 14 | using namespace another_rxcpp::operators; 15 | 16 | void test_take() { 17 | log() << "test_take -- begin" << std::endl; 18 | 19 | auto o = observables::range(1, 10); 20 | 21 | doSubscribe(o | take(0)); 22 | doSubscribe(o | take(1)); 23 | doSubscribe(o | take(5)); 24 | 25 | thread_group threads; 26 | 27 | auto x = doSubscribe( 28 | interval_range(1, 10, 100, threads) 29 | | take(2) 30 | ); 31 | while(x.is_subscribed()) {} 32 | threads.join_all(); 33 | 34 | log() << "test_take -- end" << std::endl << std::endl; 35 | } -------------------------------------------------------------------------------- /test/take_last.cpp: -------------------------------------------------------------------------------- 1 | #if defined(SUPPORTS_OPERATORS_IN_OBSERVABLE) || defined(SUPPORTS_RXCPP_COMPATIBLE) 2 | #include 3 | #else 4 | #include 5 | #include 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | #include "common.h" 12 | 13 | using namespace another_rxcpp; 14 | using namespace another_rxcpp::operators; 15 | 16 | void test_take_last() { 17 | log() << "test_take_last -- begin" << std::endl; 18 | 19 | auto o = observables::range(1, 100); 20 | 21 | doSubscribe(o | take_last(0)); 22 | doSubscribe(o | take_last(1)); 23 | doSubscribe(o | take_last(5)); 24 | 25 | thread_group threads; 26 | 27 | auto x = doSubscribe( 28 | interval_range(1, 10, 10, threads) 29 | | take_last(5) 30 | ); 31 | while(x.is_subscribed()) {} 32 | threads.join_all(); 33 | 34 | log() << "test_take_last -- end" << std::endl << std::endl; 35 | } -------------------------------------------------------------------------------- /test/take_until.cpp: -------------------------------------------------------------------------------- 1 | #if defined(SUPPORTS_OPERATORS_IN_OBSERVABLE) || defined(SUPPORTS_RXCPP_COMPATIBLE) 2 | #include 3 | #else 4 | #include 5 | #include 6 | #endif 7 | 8 | #include 9 | #include 10 | #include "common.h" 11 | 12 | using namespace another_rxcpp; 13 | using namespace another_rxcpp::operators; 14 | 15 | void test_take_until() { 16 | 17 | log() << "test_take_until -- begin" << std::endl; 18 | 19 | thread_group threads; 20 | subjects::subject trigger; 21 | auto emitter = observable<>::create([threads](subscriber s){ 22 | threads.push([s](){ 23 | for(int i = 0; i < 100; i++){ 24 | if(!s.is_subscribed()) break; 25 | std::this_thread::sleep_for(std::chrono::milliseconds(300)); 26 | s.on_next(i); 27 | } 28 | s.on_completed(); 29 | }); 30 | }); 31 | 32 | 33 | auto o = emitter | take_until(trigger.as_observable()); 34 | 35 | auto x = doSubscribe(o); 36 | 37 | setTimeout([trigger](){ 38 | trigger.as_subscriber().on_next(1); 39 | trigger.as_subscriber().on_completed(); 40 | }, 2000); 41 | 42 | while(x.is_subscribed()) {} 43 | threads.join_all(); 44 | 45 | log() << "test_take_until -- end" << std::endl << std::endl; 46 | } -------------------------------------------------------------------------------- /test/take_while.cpp: -------------------------------------------------------------------------------- 1 | #if defined(SUPPORTS_OPERATORS_IN_OBSERVABLE) || defined(SUPPORTS_RXCPP_COMPATIBLE) 2 | #include 3 | #else 4 | #include 5 | #include 6 | #endif 7 | 8 | #include 9 | #include 10 | #include "common.h" 11 | 12 | using namespace another_rxcpp; 13 | using namespace another_rxcpp::operators; 14 | 15 | void test_take_while() { 16 | log() << "test_take_while -- begin" << std::endl; 17 | 18 | auto o = observables::range(1, 100); 19 | 20 | doSubscribe(o | take_while([](auto x){ 21 | return x < 10; 22 | })); 23 | 24 | log() << "test_take_while -- end" << std::endl << std::endl; 25 | } -------------------------------------------------------------------------------- /test/tap.cpp: -------------------------------------------------------------------------------- 1 | #if defined(SUPPORTS_OPERATORS_IN_OBSERVABLE) || defined(SUPPORTS_RXCPP_COMPATIBLE) 2 | #include 3 | #else 4 | #include 5 | #include 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | #include "common.h" 12 | 13 | using namespace another_rxcpp; 14 | using namespace another_rxcpp::operators; 15 | 16 | void test_tap() { 17 | log() << "test_tap -- begin" << std::endl; 18 | 19 | auto o = observables::range(1, 10) 20 | | tap({ 21 | [](auto&& x) { log() << "tap next " << x << std::endl; }, 22 | [](auto err) { log() << "tap error" << std::endl; }, 23 | []() { log() << "tap completed" << std::endl; } 24 | }) 25 | | map([](int x){ 26 | return x * 10; 27 | }); 28 | doSubscribe(o); 29 | 30 | 31 | #if defined(SUPPORTS_OPERATORS_IN_OBSERVABLE) 32 | { 33 | auto oo = observables::range(1, 10) 34 | .tap( 35 | [](auto x) { log() << "tap next " << x << std::endl; }, 36 | [](auto err) { log() << "tap error" << std::endl; }, 37 | []() { log() << "tap completed" << std::endl; } 38 | ) 39 | .map([](auto x){ 40 | return x * 10; 41 | }); 42 | doSubscribe(oo); 43 | } 44 | #endif /* defined(SUPPORTS_OPERATORS_IN_OBSERVABLE) */ 45 | 46 | log() << "test_tap -- end" << std::endl << std::endl; 47 | } -------------------------------------------------------------------------------- /test/timeout.cpp: -------------------------------------------------------------------------------- 1 | #if defined(SUPPORTS_OPERATORS_IN_OBSERVABLE) || defined(SUPPORTS_RXCPP_COMPATIBLE) 2 | #include 3 | #else 4 | #include 5 | #include 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "common.h" 15 | 16 | using namespace another_rxcpp; 17 | using namespace another_rxcpp::operators; 18 | 19 | void test_timeout() { 20 | log() << "test_timeout -- begin" << std::endl; 21 | 22 | thread_group threads; 23 | auto o = observable<>::create([threads](subscriber s){ 24 | threads.push([s]{ 25 | for(int i = 1; i <= 10; i++){ 26 | if(!s.is_subscribed()){ 27 | log() << "interval_range break" << std::endl; 28 | return; 29 | } 30 | std::this_thread::sleep_for(std::chrono::milliseconds(i * 100)); 31 | if(!s.is_subscribed()){ 32 | log() << "interval_range break" << std::endl; 33 | return; 34 | } 35 | log() << "interval_range emit " << i << std::endl; 36 | s.on_next(i); 37 | } 38 | log() << "interval_range complete" << std::endl; 39 | s.on_completed(); 40 | }); 41 | }) 42 | | timeout(std::chrono::milliseconds(500)); 43 | 44 | auto x = doSubscribe(o); 45 | 46 | while(x.is_subscribed()) {} 47 | threads.join_all(); 48 | 49 | log() << "test_timeout -- end" << std::endl << std::endl; 50 | } -------------------------------------------------------------------------------- /test/zip.cpp: -------------------------------------------------------------------------------- 1 | #if defined(SUPPORTS_OPERATORS_IN_OBSERVABLE) || defined(SUPPORTS_RXCPP_COMPATIBLE) 2 | #include 3 | #else 4 | #include 5 | #include 6 | #endif 7 | 8 | #include 9 | #include 10 | 11 | #include "common.h" 12 | #include 13 | 14 | using namespace another_rxcpp; 15 | using namespace another_rxcpp::operators; 16 | 17 | static auto emitter(thread_group threads) { 18 | return observable<>::create([threads](subscriber s){ 19 | threads.push([s](){ 20 | for(int i = 0; i < 10; i++){ 21 | if(!s.is_subscribed()) break; 22 | std::this_thread::sleep_for(std::chrono::milliseconds(300)); 23 | s.on_next(i); 24 | } 25 | s.on_completed(); 26 | }); 27 | }); 28 | } 29 | 30 | static auto emitter2(thread_group threads) { 31 | return emitter(threads) 32 | | map([](int x){ 33 | return double(x + 11.1); 34 | }); 35 | } 36 | 37 | static auto emitter3(thread_group threads) { 38 | return emitter(threads) 39 | | map([](int x){ 40 | std::stringstream ss; 41 | ss << "s" << x; 42 | return ss.str(); 43 | }); 44 | } 45 | 46 | void test_zip() { 47 | 48 | log() << "test_zip -- begin" << std::endl; 49 | 50 | { 51 | auto o = observables::range(0, 9) 52 | | zip(observables::range(10, 19), observables::range(20, 29)) 53 | | map([](std::tuple tp){ 54 | log() << "[0] " << std::get<0>(tp) << std::endl; 55 | log() << "[1] " << std::get<1>(tp) << std::endl; 56 | log() << "[2] " << std::get<2>(tp) << std::endl; 57 | return std::get<0>(tp); 58 | }); 59 | auto x = doSubscribe(o); 60 | while(x.is_subscribed()) {} 61 | } 62 | 63 | { 64 | thread_group threads; 65 | auto o = emitter(threads) 66 | | zip(emitter2(threads), emitter3(threads)) 67 | | map([](std::tuple tp){ 68 | log() << "[0] " << std::get<0>(tp) << std::endl; 69 | log() << "[1] " << std::get<1>(tp) << std::endl; 70 | log() << "[2] " << std::get<2>(tp) << std::endl; 71 | return std::get<0>(tp); 72 | }); 73 | auto x = doSubscribe(o); 74 | while(x.is_subscribed()) {} 75 | threads.join_all(); 76 | } 77 | 78 | { 79 | thread_group threads; 80 | auto o = emitter(threads) 81 | | zip([](int a, double b, std::string c){ 82 | std::stringstream ss; 83 | ss << a << ", " << b << ", " << c; 84 | return ss.str(); 85 | }, emitter2(threads), emitter3(threads)); 86 | 87 | auto x = doSubscribe(o); 88 | while(x.is_subscribed()) {} 89 | threads.join_all(); 90 | } 91 | 92 | log() << "test_zip -- end" << std::endl << std::endl; 93 | } --------------------------------------------------------------------------------