├── src ├── delay │ ├── delay.cpp │ ├── stream.cpp │ ├── delayasync.cpp │ ├── streamasync.cpp │ ├── test1.cpp │ ├── CMakeLists.txt │ ├── delayasync.h │ ├── delay.h │ ├── delay.t.cpp │ ├── delayasync.t.cpp │ ├── stream.b.cpp │ ├── streamasync.b.cpp │ ├── stream.h │ ├── streamasync.h │ ├── stream.t.cpp │ └── streamasync.t.cpp ├── CMakeLists.txt ├── co_fun │ ├── co_fun.cpp │ ├── lazy.cpp │ ├── holder.cpp │ ├── stream.cpp │ ├── thunk.cpp │ ├── co_fun.t.cpp │ ├── co_fun.h │ ├── holder.t.cpp │ ├── CMakeLists.txt │ ├── lazy.h │ ├── thunk.h │ ├── holder.h │ ├── lazy.t.cpp │ ├── stream.b.cpp │ ├── thunk.t.cpp │ ├── stream.h │ └── stream.t.cpp └── examples │ ├── main.cpp │ └── CMakeLists.txt ├── .gitignore ├── cmake └── Config.cmake.in ├── extern └── CMakeLists.txt ├── .gitmodules ├── etc ├── gcc-10-toolchain.cmake ├── gcc-11-toolchain.cmake ├── clang-11-toolchain.cmake ├── clang-12-toolchain.cmake ├── llvm-master-toolchain.cmake └── common-flags.cmake ├── doc └── model.org ├── CMakeLists.txt ├── README.org ├── Makefile ├── .clang-format └── LICENSE /src/delay/delay.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | -------------------------------------------------------------------------------- /src/delay/stream.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | -------------------------------------------------------------------------------- /src/delay/delayasync.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | -------------------------------------------------------------------------------- /src/delay/streamasync.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /compile_commands.json 2 | /.cache/ 3 | /.vscode/ 4 | /build/ 5 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(delay) 2 | add_subdirectory(co_fun) 3 | add_subdirectory(examples) 4 | -------------------------------------------------------------------------------- /src/co_fun/co_fun.cpp: -------------------------------------------------------------------------------- 1 | // co_fun.cpp -*-C++-*- 2 | #include 3 | -------------------------------------------------------------------------------- /src/co_fun/lazy.cpp: -------------------------------------------------------------------------------- 1 | // lazy.cpp -*-C++-*- 2 | #include 3 | -------------------------------------------------------------------------------- /src/co_fun/holder.cpp: -------------------------------------------------------------------------------- 1 | // holder.cpp -*-C++-*- 2 | #include 3 | -------------------------------------------------------------------------------- /src/co_fun/stream.cpp: -------------------------------------------------------------------------------- 1 | // stream.cpp -*-C++-*- 2 | #include 3 | -------------------------------------------------------------------------------- /src/co_fun/thunk.cpp: -------------------------------------------------------------------------------- 1 | // thunk.cpp -*-C++-*- 2 | #include 3 | -------------------------------------------------------------------------------- /src/delay/test1.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "gtest/gtest.h" 4 | 5 | TEST(SampleTest, AssertionTrue) { 6 | ASSERT_EQ(1, 1); 7 | } 8 | -------------------------------------------------------------------------------- /cmake/Config.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | include("${CMAKE_CURRENT_LIST_DIR}/@TARGETS_EXPORT_NAME@.cmake") 4 | check_required_components("@PROJECT_NAME@") 5 | -------------------------------------------------------------------------------- /src/examples/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | int main(int /*argc*/, char** /*argv*/) 6 | { 7 | using namespace co_fun; 8 | 9 | 10 | } 11 | -------------------------------------------------------------------------------- /extern/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(googletest EXCLUDE_FROM_ALL) 2 | set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "Suppressing benchmark's tests" FORCE) 3 | add_subdirectory(benchmark EXCLUDE_FROM_ALL) 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "extern/benchmark"] 2 | path = extern/benchmark 3 | url = https://github.com/google/benchmark.git 4 | [submodule "extern/googletest"] 5 | path = extern/googletest 6 | url = https://github.com/google/googletest.git 7 | -------------------------------------------------------------------------------- /src/co_fun/co_fun.t.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | using namespace co_fun; 6 | 7 | TEST(Co_FunTest, TestGTest) { 8 | ASSERT_EQ(1, 1); 9 | } 10 | 11 | TEST(Co_FunTest, Breathing) { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(GNUInstallDirs) 2 | 3 | add_executable(main "") 4 | 5 | target_sources( 6 | main 7 | PRIVATE 8 | main.cpp) 9 | 10 | target_link_libraries(main co_fun) 11 | 12 | install( 13 | TARGETS main 14 | DESTINATION ${CMAKE_INSTALL_BINDIR} 15 | ) 16 | -------------------------------------------------------------------------------- /src/co_fun/co_fun.h: -------------------------------------------------------------------------------- 1 | // co_fun.h -*-C++-*- 2 | #ifndef INCLUDED_CO_FUN 3 | #define INCLUDED_CO_FUN 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | namespace co_fun { 10 | 11 | 12 | 13 | } // namespace co_fun 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /etc/gcc-10-toolchain.cmake: -------------------------------------------------------------------------------- 1 | include_guard(GLOBAL) 2 | 3 | set(CMAKE_C_COMPILER gcc-10) 4 | set(CMAKE_CXX_COMPILER g++-10) 5 | 6 | set(CMAKE_CXX_FLAGS 7 | "-std=c++20 \ 8 | -fcoroutines \ 9 | -Wall -Wextra " 10 | CACHE STRING "CXX_FLAGS" FORCE) 11 | 12 | include("${CMAKE_CURRENT_LIST_DIR}/common-flags.cmake") 13 | -------------------------------------------------------------------------------- /etc/gcc-11-toolchain.cmake: -------------------------------------------------------------------------------- 1 | include_guard(GLOBAL) 2 | 3 | set(CMAKE_C_COMPILER gcc-11) 4 | set(CMAKE_CXX_COMPILER g++-11) 5 | 6 | set(CMAKE_CXX_FLAGS 7 | "-std=c++20 \ 8 | -fcoroutines \ 9 | -fconcepts-diagnostics-depth=3 \ 10 | -Wall -Wextra " 11 | CACHE STRING "CXX_FLAGS" 12 | FORCE) 13 | 14 | include("${CMAKE_CURRENT_LIST_DIR}/common-flags.cmake") 15 | -------------------------------------------------------------------------------- /etc/clang-11-toolchain.cmake: -------------------------------------------------------------------------------- 1 | include_guard(GLOBAL) 2 | 3 | set(CMAKE_C_COMPILER clang-11) 4 | set(CMAKE_CXX_COMPILER clang++-11) 5 | 6 | set(CMAKE_CXX_FLAGS 7 | "-std=c++20 \ 8 | -Wall -Wextra \ 9 | -stdlib=libc++ " 10 | CACHE STRING "CXX_FLAGS" FORCE) 11 | 12 | set(CMAKE_CXX_FLAGS_DEBUG "-O0 -fno-inline -g3" CACHE STRING "C++ DEBUG Flags" FORCE) 13 | set(CMAKE_CXX_FLAGS_RELEASE "-Ofast -g0 -DNDEBUG" CACHE STRING "C++ Release Flags" FORCE) 14 | set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O3 -g -DNDEBUG" CACHE STRING "C++ RelWithDebInfo Flags" FORCE) 15 | -------------------------------------------------------------------------------- /etc/clang-12-toolchain.cmake: -------------------------------------------------------------------------------- 1 | include_guard(GLOBAL) 2 | 3 | set(CMAKE_C_COMPILER clang-12) 4 | set(CMAKE_CXX_COMPILER clang++-12) 5 | 6 | set(CMAKE_CXX_FLAGS 7 | "-std=c++20 \ 8 | -Wall -Wextra \ 9 | -stdlib=libc++ " 10 | CACHE STRING "CXX_FLAGS" FORCE) 11 | 12 | set(CMAKE_CXX_FLAGS_DEBUG "-O0 -fno-inline -g3" CACHE STRING "C++ DEBUG Flags" FORCE) 13 | set(CMAKE_CXX_FLAGS_RELEASE "-Ofast -g0 -DNDEBUG" CACHE STRING "C++ Release Flags" FORCE) 14 | set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O3 -g -DNDEBUG" CACHE STRING "C++ RelWithDebInfo Flags" FORCE) 15 | -------------------------------------------------------------------------------- /src/co_fun/holder.t.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | using namespace co_fun; 6 | 7 | TEST(Co_FunHolderTest, TestGTest) { ASSERT_EQ(1, 1); } 8 | 9 | TEST(Co_FunHolderTest, ValueBreathing) { 10 | Value vi; 11 | int i = vi.get_value(); 12 | (void)(i); // unused warning 13 | 14 | Value vv; 15 | vv.get_value(); 16 | } 17 | 18 | TEST(Co_FunHolderTest, HolderBreathing) { 19 | Holder hi; 20 | hi.set_value(42); 21 | int i = hi.get_value(); 22 | EXPECT_EQ(42, i); 23 | 24 | Value vv; 25 | vv.get_value(); 26 | } 27 | -------------------------------------------------------------------------------- /etc/llvm-master-toolchain.cmake: -------------------------------------------------------------------------------- 1 | set(LLVM_ROOT "$ENV{LLVM_ROOT}" CACHE PATH "Path to LLVM installation") 2 | 3 | set(CMAKE_C_COMPILER ${LLVM_ROOT}/bin/clang) 4 | set(CMAKE_CXX_COMPILER ${LLVM_ROOT}/bin/clang++) 5 | 6 | set(CMAKE_CXX_FLAGS 7 | "-std=c++2a \ 8 | -Wall -Wextra \ 9 | -stdlib=libc++ " 10 | CACHE STRING "CXX_FLAGS" FORCE) 11 | 12 | set(CMAKE_EXE_LINKER_FLAGS 13 | "-Wl,-rpath,${LLVM_ROOT}/lib" 14 | CACHE STRING "CMAKE_EXE_LINKER_FLAGS" FORCE) 15 | 16 | set(CMAKE_CXX_FLAGS_DEBUG "-O0 -fno-inline -g3" CACHE STRING "C++ DEBUG Flags" FORCE) 17 | set(CMAKE_CXX_FLAGS_RELEASE "-Ofast -g0 -DNDEBUG" CACHE STRING "C++ Release Flags" FORCE) 18 | set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O3 -g -DNDEBUG" CACHE STRING "C++ Release Flags" FORCE) 19 | -------------------------------------------------------------------------------- /doc/model.org: -------------------------------------------------------------------------------- 1 | #+OPTIONS: ':nil *:t -:t ::t <:t H:3 \n:nil ^:t arch:headline author:t 2 | #+OPTIONS: broken-links:nil c:nil creator:nil d:(not "LOGBOOK") date:t e:t 3 | #+OPTIONS: email:nil f:t inline:t num:t p:nil pri:nil prop:nil stat:t tags:t 4 | #+OPTIONS: tasks:t tex:t timestamp:t title:t toc:t todo:t |:t 5 | #+TITLE:CO_FUN 6 | #+DATE: <2018-12-03 Mon> 7 | #+AUTHOR: Steve Downey 8 | #+EMAIL: sdowney@sdowney.org 9 | #+LANGUAGE: en 10 | #+SELECT_TAGS: export 11 | #+EXCLUDE_TAGS: noexport 12 | #+CREATOR: Emacs 26.1 (Org mode 9.1.14) 13 | #+OPTIONS: html-link-use-abs-url:nil html-postamble:auto html-preamble:t 14 | #+OPTIONS: html-scripts:t html-style:t html5-fancy:nil tex:t 15 | #+HTML_DOCTYPE: xhtml-strict 16 | #+HTML_CONTAINER: div 17 | #+DESCRIPTION: 18 | #+KEYWORDS: 19 | #+HTML_LINK_HOME: 20 | #+HTML_LINK_UP: 21 | #+HTML_MATHJAX: 22 | #+HTML_HEAD: 23 | #+HTML_HEAD_EXTRA: 24 | #+SUBTITLE: 25 | #+INFOJS_OPT: 26 | #+CREATOR: Emacs 26.1 (Org mode 9.1.14) 27 | #+LATEX_HEADER: 28 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | project(Co_Fun VERSION 0.0.0 LANGUAGES CXX) 4 | 5 | enable_testing() 6 | 7 | set(CMAKE_CXX_STANDARD 20) 8 | 9 | set(TARGETS_EXPORT_NAME ${CMAKE_PROJECT_NAME}Targets) 10 | 11 | add_subdirectory(extern) 12 | add_subdirectory(src) 13 | 14 | include(GNUInstallDirs) 15 | 16 | set(INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/cmake) 17 | 18 | install( 19 | EXPORT ${TARGETS_EXPORT_NAME} 20 | NAMESPACE ${CMAKE_PROJECT_NAME} 21 | DESTINATION ${INSTALL_CONFIGDIR} 22 | ) 23 | 24 | include(CMakePackageConfigHelpers) 25 | 26 | write_basic_package_version_file( 27 | ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_PROJECT_NAME}ConfigVersion.cmake 28 | VERSION ${PROJECT_VERSION} 29 | COMPATIBILITY AnyNewerVersion 30 | ) 31 | 32 | configure_package_config_file( 33 | "cmake/Config.cmake.in" 34 | ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_PROJECT_NAME}Config.cmake 35 | INSTALL_DESTINATION ${INSTALL_CONFIGDIR} 36 | ) 37 | 38 | install(FILES 39 | ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_PROJECT_NAME}Config.cmake 40 | ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_PROJECT_NAME}ConfigVersion.cmake 41 | DESTINATION ${INSTALL_CONFIGDIR} 42 | ) 43 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | * CO_FUN 2 | 3 | ** Fun and Functional programming with C++20 Corotines 4 | 5 | 6 | 7 | ** Building 8 | 9 | There's a decent chance that `make` will work. Otherwise try something like 10 | 11 | ``` shell 12 | make -k TOOLCHAIN=gcc-11 BUILD_TYPE=RelWithDebInfo 13 | ``` 14 | 15 | The makefile is there to drive the cmake build through its states. It's not complicated, nor necessary. 16 | 17 | ** Code organization 18 | - src/delay :: delayed evaluation circa 2015 (Meh) 19 | - src/co_fun :: new fun code using coroutines 20 | 21 | ** Components 22 | *** Holder 23 | Used to hold the pending results for the coroutine 24 | 25 | Based on the holder described in "The Old New Thing" 26 | *** Lazy 27 | A coroutine promise holder that mediates the result of a single function call. 28 | *** Thunk 29 | A result of a function call that when you think you want the result, it may already have been thunk. Shareable. 30 | *** Stream 31 | Fun with suspended function calls. A cons cell is a value and a thunk to the next value. A cons stream is a series of lazy values. From this, the list monad is built, and much of `do` notation desugaring. ConsStream models a range. 32 | -------------------------------------------------------------------------------- /src/delay/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(delay STATIC "") 2 | 3 | target_sources( 4 | delay 5 | PRIVATE 6 | delay.cpp) 7 | 8 | include(GNUInstallDirs) 9 | 10 | target_include_directories(delay PUBLIC 11 | $ 12 | $ # /include/delay 13 | ) 14 | 15 | install( 16 | TARGETS delay 17 | EXPORT ${TARGETS_EXPORT_NAME} 18 | DESTINATION ${CMAKE_INSTALL_LIBDIR} 19 | ) 20 | 21 | string(TOLOWER ${CMAKE_PROJECT_NAME} CMAKE_LOWER_PROJECT_NAME) 22 | 23 | install( 24 | DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/ 25 | DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${CMAKE_LOWER_PROJECT_NAME} 26 | FILES_MATCHING PATTERN "*.h" 27 | ) 28 | 29 | 30 | ## Tests 31 | add_executable(delay_test "") 32 | 33 | target_sources( 34 | delay_test 35 | PRIVATE 36 | delay.t.cpp 37 | delayasync.t.cpp 38 | stream.t.cpp 39 | streamasync.t.cpp) 40 | 41 | target_link_libraries(delay_test delay) 42 | target_link_libraries(delay_test gtest) 43 | target_link_libraries(delay_test gtest_main) 44 | 45 | include(GoogleTest) 46 | gtest_discover_tests(delay_test) 47 | 48 | add_executable( 49 | delay_benchmark 50 | stream.b.cpp 51 | streamasync.b.cpp 52 | ) 53 | 54 | target_link_libraries(delay_benchmark benchmark delay) 55 | -------------------------------------------------------------------------------- /src/co_fun/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(co_fun STATIC "") 2 | 3 | target_sources( 4 | co_fun 5 | PRIVATE 6 | co_fun.cpp 7 | lazy.cpp 8 | thunk.cpp 9 | holder.cpp 10 | stream.cpp) 11 | 12 | include(GNUInstallDirs) 13 | 14 | target_include_directories(co_fun PUBLIC 15 | $ 16 | $ # /include/co_fun 17 | ) 18 | 19 | install( 20 | TARGETS co_fun 21 | EXPORT ${TARGETS_EXPORT_NAME} 22 | DESTINATION ${CMAKE_INSTALL_LIBDIR} 23 | ) 24 | 25 | string(TOLOWER ${CMAKE_PROJECT_NAME} CMAKE_LOWER_PROJECT_NAME) 26 | 27 | install( 28 | DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/ 29 | DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${CMAKE_LOWER_PROJECT_NAME} 30 | FILES_MATCHING PATTERN "*.h" 31 | ) 32 | 33 | 34 | ## Tests 35 | add_executable(co_fun_test "") 36 | 37 | target_sources( 38 | co_fun_test 39 | PRIVATE 40 | co_fun.t.cpp 41 | lazy.t.cpp 42 | thunk.t.cpp 43 | holder.t.cpp 44 | stream.t.cpp) 45 | 46 | target_link_libraries(co_fun_test co_fun) 47 | target_link_libraries(co_fun_test gtest) 48 | target_link_libraries(co_fun_test gtest_main) 49 | 50 | include(GoogleTest) 51 | gtest_discover_tests(co_fun_test) 52 | 53 | add_executable( 54 | co_fun_benchmark 55 | stream.b.cpp 56 | ) 57 | 58 | target_link_libraries(co_fun_benchmark benchmark delay) 59 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | INSTALL_PREFIX?=../install 2 | 3 | ifeq ($(strip $(TOOLCHAIN)),) 4 | BUILD_NAME?=build 5 | BUILD_DIR?=../cmake.bld/$(shell basename $(CURDIR)) 6 | BUILD_PATH?=$(BUILD_DIR)/$(BUILD_NAME) 7 | BUILD_TYPE?=RelWithDebInfo 8 | else 9 | BUILD_NAME?=build-$(TOOLCHAIN) 10 | BUILD_DIR?=../cmake.bld/$(shell basename $(CURDIR)) 11 | BUILD_PATH?=$(BUILD_DIR)/$(BUILD_NAME) 12 | BUILD_TYPE?=RelWithDebInfo 13 | CMAKE_ARGS=-DCMAKE_TOOLCHAIN_FILE=$(CURDIR)/etc/$(TOOLCHAIN)-toolchain.cmake 14 | endif 15 | 16 | define run_cmake = 17 | cmake \ 18 | -G "Ninja" \ 19 | -DCMAKE_BUILD_TYPE=$(BUILD_TYPE) \ 20 | -DCMAKE_INSTALL_PREFIX=$(abspath $(INSTALL_PREFIX)) \ 21 | -DCMAKE_EXPORT_COMPILE_COMMANDS=1 \ 22 | $(CMAKE_ARGS) \ 23 | $(CURDIR) 24 | endef 25 | 26 | default: build 27 | 28 | $(BUILD_PATH): 29 | mkdir -p $(BUILD_PATH) 30 | 31 | $(BUILD_PATH)/CMakeCache.txt: | $(BUILD_PATH) 32 | cd $(BUILD_PATH) && $(run_cmake) 33 | -rm compile_commands.json 34 | ln -s $(BUILD_PATH)/compile_commands.json 35 | 36 | build: $(BUILD_PATH)/CMakeCache.txt 37 | cd $(BUILD_PATH) && ninja -k 0 -v 38 | 39 | install: $(BUILD_PATH)/CMakeCache.txt 40 | cd $(BUILD_PATH) && ninja install 41 | 42 | ctest: $(BUILD_PATH)/CMakeCache.txt 43 | cd $(BUILD_PATH) && ctest 44 | 45 | ctest_ : build 46 | cd $(BUILD_PATH) && ctest 47 | 48 | test: ctest_ 49 | 50 | cmake: | $(BUILD_PATH) 51 | cd $(BUILD_PATH) && $(run-cmake) 52 | 53 | clean: $(BUILD_PATH)/CMakeCache.txt 54 | cd $(BUILD_PATH) && ninja clean 55 | 56 | realclean: 57 | rm -rf $(BUILD_PATH) 58 | 59 | .PHONY: realclean clean cmake test ctest install build default 60 | -------------------------------------------------------------------------------- /src/delay/delayasync.h: -------------------------------------------------------------------------------- 1 | // delay.h -*-C++-*- 2 | #ifndef INCLUDED_DELAYASYNC 3 | #define INCLUDED_DELAYASYNC 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | template 13 | class DelayAsync { 14 | using Func = std::function; 15 | template 16 | using isFuncConv = std::is_convertible; 17 | 18 | std::shared_future future_; 19 | mutable std::atomic_int evaled_; 20 | 21 | public: 22 | DelayAsync() = default; 23 | DelayAsync(const DelayAsync& rhs) : future_(rhs.future_) { 24 | } 25 | DelayAsync(Value const& value) 26 | : future_(std::async(std::launch::deferred, [value]() { return value; })), 27 | evaled_(true){}; 28 | DelayAsync(Value&& value) 29 | : future_(std::async(std::launch::deferred, 30 | [value = std::move(value)]() { return value; })), 31 | evaled_(true){}; 32 | 33 | template ::value>::type> 35 | DelayAsync(Action&& A) 36 | : future_(std::async( 37 | std::launch::deferred, 38 | [action = std::forward(A)]() { return action(); })), 39 | evaled_(false) { 40 | } 41 | 42 | ~DelayAsync() = default; 43 | 44 | Value const& get() const { 45 | evaled_ = true; 46 | return future_.get(); 47 | } 48 | 49 | operator Value const&() const { 50 | evaled_ = true; 51 | return future_.get(); 52 | } 53 | 54 | bool isForced() { 55 | return evaled_; 56 | } 57 | }; 58 | 59 | template 60 | Value const& force(DelayAsync const& delayAsync) { 61 | return delayAsync; 62 | } 63 | 64 | template 65 | Value const& force(DelayAsync&& delayAsync) { 66 | return std::move(delayAsync); 67 | } 68 | 69 | template 70 | auto delayAsync(F&& f, Args&&... args) -> DelayAsync { 71 | using Value = decltype(f(args...)); 72 | return DelayAsync( 73 | [ args..., f_ = std::forward(f) ]() { return f_(args...); }); 74 | } 75 | 76 | #endif 77 | -------------------------------------------------------------------------------- /etc/common-flags.cmake: -------------------------------------------------------------------------------- 1 | include_guard( GLOBAL ) 2 | 3 | # Debug 4 | set(CMAKE_C_FLAGS_DEBUG 5 | "-O0 -fno-inline -g3" 6 | CACHE STRING 7 | "C DEBUG Flags" 8 | FORCE) 9 | 10 | set(CMAKE_CXX_FLAGS_DEBUG 11 | "-O0 -fno-inline -g3" 12 | CACHE STRING 13 | "C++ DEBUG Flags" 14 | FORCE) 15 | 16 | #Release 17 | set(CMAKE_C_FLAGS_RELEASE 18 | "-Ofast -g0 -DNDEBUG" 19 | CACHE STRING "C Release Flags" 20 | FORCE) 21 | 22 | set(CMAKE_CXX_FLAGS_RELEASE 23 | "-Ofast -g0 -DNDEBUG" 24 | CACHE STRING "C++ Release Flags" 25 | FORCE) 26 | 27 | #RelWithdebinfo 28 | set(CMAKE_C_FLAGS_RELWITHDEBINFO 29 | "-O3 -g -DNDEBUG" 30 | CACHE STRING "C++ RelWithDebInfo Flags" 31 | FORCE) 32 | 33 | set(CMAKE_CXX_FLAGS_RELWITHDEBINFO 34 | "-O3 -g -DNDEBUG" 35 | CACHE STRING "C++ RelWithDebInfo Flags" 36 | FORCE) 37 | 38 | # ThreadSanitizer 39 | set(CMAKE_C_FLAGS_TSAN 40 | "-fsanitize=thread -g -O3 -DNDEBUG" 41 | CACHE STRING "Flags used by the C compiler during ThreadSanitizer builds." 42 | FORCE) 43 | 44 | set(CMAKE_CXX_FLAGS_TSAN 45 | "-fsanitize=thread -g -O3 -DNDEBUG" 46 | CACHE STRING "Flags used by the C++ compiler during ThreadSanitizer builds." 47 | FORCE) 48 | 49 | # AddressSanitize 50 | set(CMAKE_C_FLAGS_ASAN 51 | "-fsanitize=address -fno-optimize-sibling-calls -fsanitize-address-use-after-scope -fno-omit-frame-pointer -g -O3 -DNDEBUG" 52 | CACHE STRING "Flags used by the C compiler during AddressSanitizer builds." 53 | FORCE) 54 | 55 | set(CMAKE_CXX_FLAGS_ASAN 56 | "-fsanitize=address -fno-optimize-sibling-calls -fsanitize-address-use-after-scope -fno-omit-frame-pointer -g -O3 -DNDEBUG" 57 | CACHE STRING "Flags used by the C++ compiler during AddressSanitizer builds." 58 | FORCE) 59 | 60 | # LeakSanitizer 61 | set(CMAKE_C_FLAGS_LSAN 62 | "-fsanitize=leak -fno-omit-frame-pointer -g -O3 -DNDEBUG" 63 | CACHE STRING "Flags used by the C compiler during LeakSanitizer builds." 64 | FORCE) 65 | set(CMAKE_CXX_FLAGS_LSAN 66 | "-fsanitize=leak -fno-omit-frame-pointer -g -O3 -DNDEBUG" 67 | CACHE STRING "Flags used by the C++ compiler during LeakSanitizer builds." 68 | FORCE) 69 | 70 | 71 | # UndefinedBehaviour 72 | set(CMAKE_C_FLAGS_UBSAN 73 | "-fsanitize=undefined -g -O3 -DNDEBUG" 74 | CACHE STRING "Flags used by the C compiler during UndefinedBehaviourSanitizer builds." 75 | FORCE) 76 | set(CMAKE_CXX_FLAGS_UBSAN 77 | "-fsanitize=undefined -g -O3 -DNDEBUG" 78 | CACHE STRING "Flags used by the C++ compiler during UndefinedBehaviourSanitizer builds." 79 | FORCE) 80 | -------------------------------------------------------------------------------- /src/delay/delay.h: -------------------------------------------------------------------------------- 1 | // delay.h -*-C++-*- 2 | #ifndef INCLUDED_DELAY 3 | #define INCLUDED_DELAY 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | template 13 | class Delay { 14 | using Func = std::function; 15 | template 16 | using isFuncConv = std::is_convertible; 17 | 18 | mutable Func func_; 19 | 20 | typedef typename std::aligned_storage::value>::type 22 | Storage; 23 | mutable Storage value_; 24 | mutable std::atomic_int evaled_; 25 | mutable std::mutex lock_; 26 | 27 | void setValue() const { 28 | std::unique_lock guard(lock_); 29 | if (!evaled_) { 30 | ::new (&value_) Value(func_()); 31 | func_ = Func(); 32 | evaled_.store(1, std::memory_order_release); 33 | } 34 | } 35 | 36 | public: 37 | Delay() = default; 38 | Delay(const Delay& rhs) { 39 | std::unique_lock guard(rhs.lock_); 40 | int evaled = rhs.evaled_.load(std::memory_order_acquire); 41 | if (!evaled) { 42 | func_ = rhs.func_; 43 | } else { 44 | ::new (&value_) Value(*reinterpret_cast(std::addressof(rhs.value_))); 45 | } 46 | evaled_.store(evaled); 47 | } 48 | 49 | Delay(Value const& value) : evaled_(true) { 50 | ::new (&value_) Value(value); 51 | } 52 | 53 | Delay(Value&& value) : evaled_(true) { 54 | ::new (&value_) Value(std::move(value)); 55 | } 56 | 57 | template ::value>::type> 59 | Delay(Action&& A) : func_(std::forward(A)), evaled_(false) { 60 | } 61 | 62 | ~Delay() { 63 | if (evaled_) { 64 | reinterpret_cast(std::addressof(this->value_))->~Value(); 65 | } 66 | } 67 | 68 | Value const& get() const { 69 | int evaled = evaled_.load(std::memory_order_acquire); 70 | if (!evaled) { 71 | setValue(); 72 | } 73 | return *reinterpret_cast(std::addressof(this->value_)); 74 | } 75 | 76 | operator Value const&() const { 77 | return get(); 78 | } 79 | 80 | bool isForced() { 81 | return evaled_; 82 | } 83 | }; 84 | 85 | template 86 | Value const& force(Delay const& delay) { 87 | return delay; 88 | } 89 | 90 | template 91 | Value const& force(Delay&& delay) { 92 | return std::move(delay); 93 | } 94 | 95 | template 96 | auto delay(F&& f, Args&&... args) -> Delay { 97 | using Value = decltype(f(args...)); 98 | return Delay( 99 | [ args..., f_ = std::forward(f) ]() { return f_(args...); }); 100 | } 101 | 102 | #endif 103 | -------------------------------------------------------------------------------- /src/delay/delay.t.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "gtest/gtest.h" 4 | using ::testing::Test; 5 | 6 | namespace testing { 7 | namespace { 8 | int func_called; 9 | int func2_called; 10 | int func3_called; 11 | } 12 | class DelayTest : public Test { 13 | protected: 14 | DelayTest() { 15 | } 16 | ~DelayTest() { 17 | } 18 | 19 | virtual void SetUp() { 20 | func_called = 0; 21 | func2_called = 0; 22 | func3_called = 0; 23 | } 24 | 25 | virtual void TearDown() { 26 | } 27 | 28 | public: 29 | }; 30 | 31 | namespace { 32 | int func() { 33 | func_called++; 34 | return 5; 35 | } 36 | 37 | int func2(int i) { 38 | func2_called++; 39 | return i; 40 | } 41 | 42 | int func3(int i, int j) { 43 | func3_called++; 44 | return i + j; 45 | } 46 | 47 | std::string stringTest(const char* str) { 48 | return str; 49 | } 50 | } 51 | 52 | 53 | TEST_F(DelayTest, breathingTest) { 54 | Delay D1(1); 55 | 56 | int j{D1}; 57 | 58 | EXPECT_EQ(1, j); 59 | 60 | Delay D2(func); 61 | EXPECT_EQ(0, func_called); 62 | 63 | int k = D2; 64 | EXPECT_EQ(1, func_called); 65 | EXPECT_EQ(5, k); 66 | 67 | Delay D3 = func; 68 | 69 | Delay D4 = func2(3); 70 | EXPECT_EQ(1, func2_called); 71 | 72 | Delay D5([]() { return func2(7); }); 73 | 74 | Delay D6 = delay(func2, 8); 75 | 76 | Delay D7 = delay(func3, 8, 1); 77 | 78 | EXPECT_EQ(1, func_called); 79 | EXPECT_EQ(1, func2_called); 80 | EXPECT_EQ(0, func3_called); 81 | 82 | EXPECT_EQ(5, D3.get()); 83 | EXPECT_EQ(2, func_called); 84 | 85 | EXPECT_EQ(3, D4.get()); 86 | EXPECT_EQ(1, func2_called); 87 | 88 | EXPECT_EQ(7, D5.get()); 89 | EXPECT_EQ(2, func2_called); 90 | 91 | EXPECT_EQ(8, D6.get()); 92 | EXPECT_EQ(3, func2_called); 93 | 94 | EXPECT_EQ(9, D7.get()); 95 | EXPECT_EQ(1, func3_called); 96 | 97 | EXPECT_EQ(2, func_called); 98 | EXPECT_EQ(3, func2_called); 99 | EXPECT_EQ(1, func3_called); 100 | 101 | EXPECT_EQ(1, static_cast(D1)); 102 | EXPECT_EQ(5, static_cast(D2)); 103 | EXPECT_EQ(5, static_cast(D3)); 104 | EXPECT_EQ(3, static_cast(D4)); 105 | EXPECT_EQ(7, static_cast(D5)); 106 | EXPECT_EQ(8, static_cast(D6)); 107 | EXPECT_EQ(9, force(D7)); 108 | 109 | EXPECT_EQ(2, func_called); 110 | EXPECT_EQ(3, func2_called); 111 | EXPECT_EQ(1, func3_called); 112 | 113 | EXPECT_EQ(5, Delay(func).get()); 114 | EXPECT_EQ(5, force(Delay(func))); 115 | } 116 | 117 | TEST_F(DelayTest, moveTest) { 118 | std::string str; 119 | Delay d1(str); 120 | Delay d2("test"); 121 | Delay d3 = delay(stringTest, "this is a test"); 122 | Delay d4([](){return stringTest("another test");}); 123 | 124 | EXPECT_TRUE(d1.isForced()); 125 | EXPECT_TRUE(d2.isForced()); 126 | EXPECT_FALSE(d3.isForced()); 127 | EXPECT_FALSE(d4.isForced()); 128 | 129 | EXPECT_EQ(std::string("this is a test"), force(d3)); 130 | EXPECT_EQ(std::string("another test"), force(d4)); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/delay/delayasync.t.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "gtest/gtest.h" 4 | using ::testing::Test; 5 | 6 | namespace testing { 7 | namespace { 8 | int func_called; 9 | int func2_called; 10 | int func3_called; 11 | } 12 | class DelayAsyncTest : public Test { 13 | protected: 14 | DelayAsyncTest() { 15 | } 16 | ~DelayAsyncTest() { 17 | } 18 | 19 | virtual void SetUp() { 20 | func_called = 0; 21 | func2_called = 0; 22 | func3_called = 0; 23 | } 24 | 25 | virtual void TearDown() { 26 | } 27 | 28 | public: 29 | }; 30 | 31 | namespace { 32 | 33 | int func() { 34 | func_called++; 35 | return 5; 36 | } 37 | 38 | int func2(int i) { 39 | func2_called++; 40 | return i; 41 | } 42 | 43 | int func3(int i, int j) { 44 | func3_called++; 45 | return i + j; 46 | } 47 | 48 | std::string stringTest(const char* str) { 49 | return str; 50 | } 51 | } 52 | 53 | TEST_F(DelayAsyncTest, breathingTest) { 54 | DelayAsync D1(1); 55 | 56 | int j{D1}; 57 | 58 | EXPECT_EQ(1, j); 59 | 60 | DelayAsync D2(func); 61 | EXPECT_EQ(0, func_called); 62 | 63 | int k = D2; 64 | EXPECT_EQ(1, func_called); 65 | EXPECT_EQ(5, k); 66 | 67 | DelayAsync D3 = func; 68 | 69 | DelayAsync D4 = func2(3); 70 | EXPECT_EQ(1, func2_called); 71 | 72 | DelayAsync D5([]() { return func2(7); }); 73 | 74 | DelayAsync D6 = delayAsync(func2, 8); 75 | 76 | DelayAsync D7 = delayAsync(func3, 8, 1); 77 | 78 | EXPECT_EQ(1, func_called); 79 | EXPECT_EQ(1, func2_called); 80 | EXPECT_EQ(0, func3_called); 81 | 82 | EXPECT_EQ(5, D3.get()); 83 | EXPECT_EQ(2, func_called); 84 | 85 | EXPECT_EQ(3, D4.get()); 86 | EXPECT_EQ(1, func2_called); 87 | 88 | EXPECT_EQ(7, D5.get()); 89 | EXPECT_EQ(2, func2_called); 90 | 91 | EXPECT_EQ(8, D6.get()); 92 | EXPECT_EQ(3, func2_called); 93 | 94 | EXPECT_EQ(9, D7.get()); 95 | EXPECT_EQ(1, func3_called); 96 | 97 | EXPECT_EQ(2, func_called); 98 | EXPECT_EQ(3, func2_called); 99 | EXPECT_EQ(1, func3_called); 100 | 101 | EXPECT_EQ(1, static_cast(D1)); 102 | EXPECT_EQ(5, static_cast(D2)); 103 | EXPECT_EQ(5, static_cast(D3)); 104 | EXPECT_EQ(3, static_cast(D4)); 105 | EXPECT_EQ(7, static_cast(D5)); 106 | EXPECT_EQ(8, static_cast(D6)); 107 | EXPECT_EQ(9, force(D7)); 108 | 109 | EXPECT_EQ(2, func_called); 110 | EXPECT_EQ(3, func2_called); 111 | EXPECT_EQ(1, func3_called); 112 | 113 | EXPECT_EQ(5, DelayAsync(func).get()); 114 | EXPECT_EQ(5, force(DelayAsync(func))); 115 | } 116 | 117 | TEST_F(DelayAsyncTest, moveTest) { 118 | std::string str; 119 | DelayAsync d1(str); 120 | DelayAsync d2("test"); 121 | DelayAsync d3 = delayAsync(stringTest, "this is a test"); 122 | DelayAsync d4([](){return stringTest("another test");}); 123 | 124 | EXPECT_TRUE(d1.isForced()); 125 | EXPECT_TRUE(d2.isForced()); 126 | EXPECT_FALSE(d3.isForced()); 127 | EXPECT_FALSE(d4.isForced()); 128 | 129 | EXPECT_EQ(std::string("this is a test"), force(d3)); 130 | EXPECT_EQ(std::string("another test"), force(d4)); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/co_fun/lazy.h: -------------------------------------------------------------------------------- 1 | // lazy.h -*-C++-*- 2 | #ifndef INCLUDED_LAZY 3 | #define INCLUDED_LAZY 4 | 5 | //@PURPOSE: 6 | // 7 | //@CLASSES: 8 | // 9 | //@AUTHOR: Steve Downey (sdowney) 10 | // 11 | //@DESCRIPTION: 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | 20 | namespace co_fun { 21 | 22 | template 23 | class Lazy { 24 | struct Promise : public Holder::Promise { 25 | auto get_return_object() { 26 | auto holder = std::make_unique>(this); 27 | return Lazy(std::move(holder)); 28 | } 29 | }; 30 | 31 | public: 32 | using promise_type = Promise; 33 | 34 | public: 35 | Lazy() : result_() {} 36 | 37 | Lazy(Lazy&& source) : result_(std::move(source.result_)) {} 38 | 39 | explicit Lazy(std::unique_ptr>&& result) 40 | : result_(std::move(result)) {} 41 | 42 | explicit Lazy(Result result) 43 | : result_(std::make_unique>(result)) {} 44 | 45 | ~Lazy() = default; 46 | 47 | bool evaluated() const { return result_ && !result_->unevaluated(); } 48 | 49 | bool isEmpty() const { 50 | bool empty = false; 51 | if (!result_) { 52 | empty = true; 53 | } else if (result_->isNil()) { 54 | empty = true; 55 | } 56 | return empty; 57 | } 58 | 59 | Result&& get() const { 60 | if (!evaluated()) { 61 | result_->resume(); 62 | } 63 | return result_->get_value(); 64 | } 65 | 66 | operator Result&&() const { return get(); } 67 | 68 | private: 69 | std::unique_ptr> result_; 70 | }; 71 | 72 | template 73 | Value const& evaluate(Lazy const& lazy) { 74 | return lazy; 75 | } 76 | 77 | template 78 | Value&& evaluate(Lazy&& lazy) { 79 | return std::move(lazy); 80 | } 81 | 82 | template 83 | auto lazy(F f, Args... args) -> Lazy> { 84 | co_return std::invoke(f, args...); 85 | } 86 | 87 | template 88 | auto transform(Lazy l, F f) -> Lazy> { 89 | co_return f(evaluate(l)); 90 | } 91 | 92 | template 93 | auto join(Lazy>&& l) -> Lazy { 94 | return evaluate(std::move(l)); 95 | } 96 | 97 | template 98 | auto bind(Lazy&& l, Func f) -> decltype(f(evaluate(l))) { 99 | return join(transform(std::move(l), f)); 100 | } 101 | 102 | template 103 | auto bind2(Lazy&& l, Func f) -> decltype(f(evaluate(l))) { 104 | co_return f(evaluate(l)); 105 | } 106 | 107 | // ============================================================================ 108 | // INLINE FUNCTION AND FUNCTION TEMPLATE DEFINITIONS 109 | // ============================================================================ 110 | 111 | } // namespace co_fun 112 | 113 | #endif 114 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | AccessModifierOffset: -2 2 | AlignAfterOpenBracket: Align 3 | AlignConsecutiveAssignments: true 4 | AlignConsecutiveDeclarations: true 5 | AlignEscapedNewlines: Left 6 | AlignOperands: true 7 | AlignTrailingComments: true 8 | AllowAllParametersOfDeclarationOnNextLine: true 9 | AllowShortBlocksOnASingleLine: false 10 | AllowShortCaseLabelsOnASingleLine: false 11 | AllowShortFunctionsOnASingleLine: All 12 | AllowShortIfStatementsOnASingleLine: false 13 | AllowShortLoopsOnASingleLine: false 14 | AlwaysBreakAfterDefinitionReturnType: None 15 | AlwaysBreakAfterReturnType: None 16 | AlwaysBreakBeforeMultilineStrings: false 17 | AlwaysBreakTemplateDeclarations: Yes 18 | BinPackArguments: false 19 | BinPackParameters: false 20 | BraceWrapping: 21 | AfterClass: false 22 | AfterControlStatement: false 23 | AfterEnum: false 24 | AfterFunction: false 25 | AfterNamespace: false 26 | AfterObjCDeclaration: false 27 | AfterStruct: false 28 | AfterUnion: false 29 | AfterExternBlock: false 30 | BeforeCatch: false 31 | BeforeElse: false 32 | IndentBraces: false 33 | SplitEmptyFunction: true 34 | SplitEmptyRecord: true 35 | SplitEmptyNamespace: true 36 | BreakBeforeBinaryOperators: None 37 | BreakBeforeBraces: Custom 38 | BreakBeforeInheritanceComma: false 39 | BreakBeforeTernaryOperators: true 40 | BreakConstructorInitializersBeforeComma: false 41 | BreakConstructorInitializers: BeforeColon 42 | BreakAfterJavaFieldAnnotations: false 43 | BreakStringLiterals: true 44 | ColumnLimit: 79 45 | CommentPragmas: '^ IWYU pragma:' 46 | CompactNamespaces: false 47 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 48 | ConstructorInitializerIndentWidth: 4 49 | ContinuationIndentWidth: 4 50 | Cpp11BracedListStyle: true 51 | DerivePointerAlignment: false 52 | DisableFormat: false 53 | ExperimentalAutoDetectBinPacking: false 54 | FixNamespaceComments: true 55 | ForEachMacros: 56 | - foreach 57 | - Q_FOREACH 58 | - BOOST_FOREACH 59 | IncludeBlocks: Preserve 60 | IncludeCategories: 61 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/' 62 | Priority: 2 63 | - Regex: '^(<|"(gtest|isl|json)/)' 64 | Priority: 3 65 | - Regex: '.*' 66 | Priority: 1 67 | IncludeIsMainRegex: '$' 68 | IndentCaseLabels: false 69 | IndentPPDirectives: None 70 | IndentWidth: 4 71 | IndentWrappedFunctionNames: false 72 | JavaScriptQuotes: Leave 73 | JavaScriptWrapImports: true 74 | KeepEmptyLinesAtTheStartOfBlocks: true 75 | MacroBlockBegin: '' 76 | MacroBlockEnd: '' 77 | MaxEmptyLinesToKeep: 1 78 | NamespaceIndentation: None 79 | ObjCBinPackProtocolList: Auto 80 | ObjCBlockIndentWidth: 4 81 | ObjCSpaceAfterProperty: false 82 | ObjCSpaceBeforeProtocolList: true 83 | PenaltyBreakAssignment: 2 84 | PenaltyBreakBeforeFirstCallParameter: 19 85 | PenaltyBreakComment: 300 86 | PenaltyBreakFirstLessLess: 120 87 | PenaltyBreakString: 1000 88 | PenaltyBreakTemplateDeclaration: 10 89 | PenaltyExcessCharacter: 1000000 90 | PenaltyReturnTypeOnItsOwnLine: 60 91 | PointerAlignment: Left 92 | ReflowComments: true 93 | SortIncludes: false 94 | SortUsingDeclarations: true 95 | SpaceAfterCStyleCast: false 96 | SpaceAfterTemplateKeyword: true 97 | SpaceBeforeAssignmentOperators: true 98 | SpaceBeforeCtorInitializerColon: true 99 | SpaceBeforeInheritanceColon: true 100 | SpaceBeforeParens: ControlStatements 101 | SpaceBeforeRangeBasedForLoopColon: true 102 | SpaceInEmptyParentheses: false 103 | SpacesBeforeTrailingComments: 1 104 | SpacesInAngles: false 105 | SpacesInContainerLiterals: true 106 | SpacesInCStyleCastParentheses: false 107 | SpacesInParentheses: false 108 | SpacesInSquareBrackets: false 109 | Standard: Auto 110 | TabWidth: 8 111 | UseTab: Never 112 | 113 | --- 114 | Language: Cpp 115 | 116 | --- 117 | Language: ObjC 118 | 119 | ... 120 | -------------------------------------------------------------------------------- /src/co_fun/thunk.h: -------------------------------------------------------------------------------- 1 | // thunk.h -*-C++-*- 2 | #ifndef INCLUDED_THUNK 3 | #define INCLUDED_THUNK 4 | 5 | //@PURPOSE: 6 | // 7 | //@CLASSES: 8 | // 9 | //@AUTHOR: Steve Downey (sdowney) 10 | // 11 | //@DESCRIPTION: 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | 21 | namespace co_fun { 22 | 23 | template 24 | class Thunk { 25 | struct Promise : public Holder::Promise { 26 | auto get_return_object() { 27 | auto holder = std::make_shared>(this); 28 | return Thunk(std::move(holder)); 29 | } 30 | }; 31 | 32 | public: 33 | using promise_type = Promise; 34 | 35 | public: 36 | Thunk() : result_() {} 37 | 38 | Thunk(Thunk const& source) : result_(source.result_) {} 39 | 40 | Thunk(Thunk&& source) : result_(std::move(source.result_)) {} 41 | 42 | Thunk(std::shared_ptr>&& r) : result_(r) {} 43 | 44 | explicit Thunk(Result const& r) 45 | : result_(std::make_shared>(r)) {} 46 | 47 | explicit Thunk(Result&& r) 48 | : result_(std::make_shared>(std::move(r))) {} 49 | 50 | ~Thunk() = default; 51 | 52 | Thunk& operator=(const Thunk& rhs) { 53 | result_ = rhs.result_; 54 | return *this; 55 | } 56 | 57 | bool operator==(const Thunk& rhs) const { 58 | if (result_ == rhs.result_) 59 | return true; 60 | return false; 61 | } 62 | 63 | bool operator!=(const Thunk& rhs) const { 64 | if (result_ == rhs.result_) 65 | return false; 66 | return true; 67 | } 68 | 69 | bool evaluated() const { return result_ && !result_->unevaluated(); } 70 | 71 | bool isEmpty() const { 72 | bool empty = false; 73 | if (!result_) { 74 | empty = true; 75 | } else if (result_->isNil()) { 76 | empty = true; 77 | } 78 | return empty; 79 | } 80 | 81 | Result const& get() const& { 82 | if (!evaluated()) { 83 | result_->resume(); 84 | } 85 | return result_->get_value(); 86 | } 87 | 88 | operator Result const &() const { return get(); } 89 | 90 | private: 91 | std::shared_ptr> result_; 92 | }; 93 | 94 | // ============================================================================ 95 | // INLINE FUNCTION AND FUNCTION TEMPLATE DEFINITIONS 96 | // ============================================================================ 97 | 98 | template 99 | Value const& evaluate(Thunk const& thunk) { 100 | return thunk; 101 | } 102 | 103 | template 104 | Value evaluate(Thunk&& thunk) { 105 | return std::move(thunk); 106 | } 107 | 108 | template 109 | auto thunk(F f, Args... args) -> Thunk> { 110 | co_return std::invoke(f, args...); 111 | } 112 | 113 | template 114 | auto transform(Thunk l, F f) 115 | -> Thunk> { 116 | co_return f(evaluate(l)); 117 | } 118 | 119 | template 120 | auto join(Thunk> l) -> Thunk { 121 | return evaluate(l); 122 | } 123 | 124 | template 125 | auto bind(Thunk l, Func f) -> decltype(f(evaluate(l))) { 126 | return join(transform(l, f)); 127 | } 128 | 129 | template 130 | auto bind2(Thunk l, Func f) -> decltype(f(evaluate(l))) { 131 | co_return f(evaluate(l)); 132 | } 133 | 134 | 135 | } // namespace co_fun 136 | 137 | #endif 138 | -------------------------------------------------------------------------------- /src/co_fun/holder.h: -------------------------------------------------------------------------------- 1 | // holder.h -*-C++-*- 2 | #ifndef INCLUDED_CO_FUN_HOLDER 3 | #define INCLUDED_CO_FUN_HOLDER 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | //@PURPOSE: 12 | // 13 | //@CLASSES: 14 | // 15 | //@AUTHOR: Steve Downey (sdowney) 16 | // 17 | //@DESCRIPTION: 18 | 19 | namespace co_fun { 20 | 21 | template 22 | struct Value { 23 | T value; 24 | T&& get_value() { return static_cast(value); } 25 | }; 26 | 27 | template <> 28 | struct Value { 29 | void get_value() {} 30 | }; 31 | 32 | template 33 | struct Holder { 34 | struct Promise { 35 | void return_value(R v) { 36 | holder_->set_value(std::move(v)); 37 | return; 38 | } 39 | 40 | void unhandled_exception() { throw; } 41 | 42 | std::suspend_always initial_suspend() noexcept { return {}; } 43 | 44 | std::suspend_never final_suspend() noexcept { return {}; } 45 | 46 | auto handle() { 47 | return std::coroutine_handle::from_promise(*this); 48 | } 49 | 50 | void setHolder(Holder* holder) { holder_ = holder; } 51 | 52 | Holder* holder_; 53 | }; 54 | 55 | enum class result_status { empty, value, error }; 56 | 57 | std::atomic status{result_status::empty}; 58 | 59 | union result_holder { 60 | result_holder(){}; 61 | ~result_holder(){}; 62 | 63 | Value wrapper; 64 | std::exception_ptr error; 65 | } result_; 66 | 67 | Promise* promise_; 68 | 69 | template 70 | void set_value(Args&&... args) { 71 | new (std::addressof(result_.wrapper)) 72 | Value{std::forward(args)...}; 73 | 74 | status.store(result_status::value, std::memory_order_release); 75 | } 76 | 77 | void unhandled_exception() noexcept { 78 | new (std::addressof(result_.error)) 79 | std::exception_ptr(std::current_exception()); 80 | 81 | status.store(result_status::error, std::memory_order_release); 82 | } 83 | 84 | bool unevaluated() const noexcept { 85 | return status.load(std::memory_order_relaxed) == result_status::empty; 86 | } 87 | 88 | R&& get_value() { 89 | switch (status.load(std::memory_order_acquire)) { 90 | case result_status::empty: { 91 | assert(false); 92 | std::terminate(); 93 | break; 94 | } 95 | case result_status::value: { 96 | return result_.wrapper.get_value(); 97 | } 98 | case result_status::error: { 99 | std::rethrow_exception(std::exchange(result_.error, {})); 100 | break; 101 | } 102 | } 103 | assert(false); 104 | std::terminate(); 105 | } 106 | 107 | void resume() { return promise_->handle().resume(); } 108 | 109 | bool isNil() { return unevaluated() && !promise_; } 110 | 111 | Holder() : promise_(nullptr) {} 112 | 113 | Holder(Promise* p) : promise_(p) { p->setHolder(this); } 114 | 115 | Holder(Holder&& source) 116 | : promise_(std::exchange(source.promise_, nullptr)) {} 117 | 118 | Holder(R t) : promise_(nullptr) { 119 | new (std::addressof(result_.wrapper)) Value{t}; 120 | 121 | status.store(result_status::value, std::memory_order_release); 122 | } 123 | 124 | ~Holder() { 125 | switch (status.load(std::memory_order_relaxed)) { 126 | case result_status::empty: { 127 | if (promise_) 128 | promise_->handle().destroy(); 129 | break; 130 | } 131 | case result_status::value: { 132 | result_.wrapper.~Value(); 133 | break; 134 | } 135 | case result_status::error: { 136 | if (result_.error) 137 | std::rethrow_exception(result_.error); 138 | result_.error.~exception_ptr(); 139 | } break; 140 | } 141 | } 142 | }; 143 | 144 | } // namespace co_fun 145 | 146 | #endif 147 | -------------------------------------------------------------------------------- /src/co_fun/lazy.t.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | using namespace co_fun; 7 | 8 | TEST(Co_FunLazyTest, TestGTest) { ASSERT_EQ(1, 1); } 9 | 10 | using ::testing::Test; 11 | 12 | namespace testing { 13 | namespace { 14 | int func_called; 15 | int func2_called; 16 | int func3_called; 17 | } // namespace 18 | 19 | namespace { 20 | int func() { 21 | func_called++; 22 | return 5; 23 | } 24 | 25 | Lazy co_func() { 26 | func_called++; 27 | co_return 5; 28 | } 29 | 30 | int func2(int i) { 31 | func2_called++; 32 | return i; 33 | } 34 | 35 | int func3(int i, int j) { 36 | func3_called++; 37 | return i + j; 38 | } 39 | std::string stringTest(const char* str) { return str; } 40 | 41 | } // namespace 42 | 43 | TEST(Co_FunLazyTest, Breathing) { 44 | Lazy lazy; 45 | Lazy lazy2(std::move(lazy)); 46 | EXPECT_EQ(false, lazy2.evaluated()); 47 | Lazy lazy3(3); 48 | 49 | int j{lazy3}; 50 | 51 | EXPECT_EQ(3, j); 52 | 53 | Lazy D2 = co_func(); 54 | EXPECT_EQ(0, func_called); 55 | int i = D2; 56 | EXPECT_EQ(i, 5); 57 | EXPECT_EQ(1, func_called); 58 | 59 | int i2 = D2; 60 | EXPECT_EQ(i2, 5); 61 | EXPECT_EQ(1, func_called); 62 | } 63 | 64 | TEST(Co_FunLazyTest, MoveTest) { 65 | std::string str; 66 | Lazy d1(str); 67 | Lazy d2("test"); 68 | Lazy d3 = lazy(stringTest, "this is a test"); 69 | // Lazy d4([]() { return stringTest("another test"); }); 70 | 71 | EXPECT_TRUE(d1.evaluated()); 72 | EXPECT_TRUE(d2.evaluated()); 73 | EXPECT_FALSE(d3.evaluated()); 74 | std::string s = d3; 75 | EXPECT_EQ(std::string("this is a test"), s); 76 | 77 | Lazy d4 = lazy(stringTest, "this is another test"); 78 | EXPECT_EQ(std::string("this is another test"), evaluate(d4)); 79 | 80 | Lazy d5 = lazy(stringTest, "this is another test"); 81 | Lazy d6 = std::move(d5); 82 | EXPECT_EQ(std::string("this is another test"), evaluate(d6)); 83 | } 84 | 85 | Lazy test(int k) { 86 | return lazy([k]() { 87 | func_called++; 88 | return k; 89 | }); 90 | } 91 | 92 | TEST(Co_FunLazyTest, IndirectTest) { 93 | func_called = 0; 94 | auto l = lazy([]() { return func(); }); 95 | EXPECT_FALSE(l.evaluated()); 96 | EXPECT_EQ(0, func_called); 97 | int i = l; 98 | EXPECT_EQ(i, 5); 99 | EXPECT_EQ(1, func_called); 100 | 101 | auto l2 = test(9); 102 | EXPECT_FALSE(l2.evaluated()); 103 | EXPECT_EQ(1, func_called); 104 | int i2 = l2; 105 | EXPECT_EQ(i2, 9); 106 | EXPECT_EQ(2, func_called); 107 | } 108 | 109 | TEST(Co_FunLazyTest, LambdaTest) { 110 | auto l = lazy([]() { return 5; }); 111 | int i = l; 112 | EXPECT_EQ(i, 5); 113 | 114 | auto l2 = lazy([]() { return []() { return 5; }; }); 115 | int i2 = evaluate(l2)(); 116 | EXPECT_EQ(i2, 5); 117 | 118 | auto l3 = lazy([](auto c) { return c; }, []() { return 5; }); 119 | int i3 = evaluate(l3)(); 120 | EXPECT_EQ(i3, 5); 121 | 122 | auto l4 = lazy([](auto c) { return c; }, [](int j) { return j + 5; }); 123 | int i4 = evaluate(l4)(1); 124 | EXPECT_EQ(i4, 6); 125 | 126 | auto lambda1 = [](int k) { return [k](int j) { return k + j; }; }; 127 | auto l5 = lazy(lambda1, 1); 128 | int i5 = evaluate(l5)(6); 129 | EXPECT_EQ(i5, 7); 130 | } 131 | int identity(int i) { return i; } 132 | auto func_return() { return identity; } 133 | 134 | TEST(Co_FunLazyTest, FunctionsTest) { 135 | auto l = lazy(func_return); 136 | int i = l(10); 137 | EXPECT_EQ(i, 10); 138 | } 139 | 140 | TEST(Co_FunLazyTest, TransformTest) { 141 | auto l = lazy([]() { return 5; }); 142 | auto i = transform(std::move(l), [](auto j) { return j * 2; }); 143 | EXPECT_TRUE(l.isEmpty()); 144 | EXPECT_EQ(i, 10); 145 | EXPECT_TRUE(l.isEmpty()); 146 | 147 | auto i2 = transform(Lazy{6}, [](auto j) { return j * 2; }); 148 | EXPECT_EQ(i2, 12); 149 | } 150 | 151 | TEST(Co_FunLazyTest, JoinTest) { 152 | Lazy> l = lazy([]() { return Lazy{5}; }); 153 | Lazy j = join(std::move(l)); 154 | EXPECT_EQ(j, 5); 155 | 156 | Lazy l2 = lazy([](auto i, auto j) { return i + j; }, 3, 5); 157 | Lazy> l3 = [](auto l) -> Lazy> { 158 | co_return l; 159 | }(std::move(l2)); 160 | Lazy l4 = join(std::move(l3)); 161 | EXPECT_EQ(false, l4.evaluated()); 162 | EXPECT_EQ(8, l4); 163 | } 164 | 165 | TEST(Co_FunLazyTest, BindTest) { 166 | Lazy l = lazy([]() { return 5; }); 167 | Lazy b = 168 | bind(std::move(l), [](int i) -> Lazy { co_return 1.0 * i; }); 169 | EXPECT_EQ(false, b.evaluated()); 170 | EXPECT_EQ(5.0, b); 171 | EXPECT_EQ(true, b.evaluated()); 172 | } 173 | 174 | TEST(Co_FunLazyTest, Bind2Test) { 175 | Lazy l = lazy([]() { return 5; }); 176 | Lazy b = 177 | bind2(std::move(l), [](int i) -> Lazy { co_return 1.0 * i; }); 178 | EXPECT_EQ(false, b.evaluated()); 179 | EXPECT_EQ(5.0, b); 180 | EXPECT_EQ(true, b.evaluated()); 181 | } 182 | 183 | } // namespace testing 184 | -------------------------------------------------------------------------------- /src/delay/stream.b.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | int Factorial(uint32_t n) { return (n == 1) ? 1 : n * Factorial(n - 1); } 10 | 11 | static void BM_Concat(benchmark::State& state) { 12 | auto x = state.range(0); 13 | int l = 0; 14 | while (state.KeepRunning()) { 15 | ConsStream inf = iota(0); 16 | ConsStream s1 = take(inf, x); 17 | ConsStream> s2(take(iota(1), x)); 18 | ConsStream> stream = cons(s1, s2); 19 | ConsStream c = concat(stream); 20 | l = last(c); 21 | } 22 | std::stringstream ss; 23 | ss << l; 24 | state.SetLabel(ss.str()); 25 | } 26 | BENCHMARK(BM_Concat)->Arg(8)->Arg(64)->Arg(512); 27 | 28 | static void BM_ConcatMap(benchmark::State& state) { 29 | auto x = state.range(0); 30 | size_t l = 0; 31 | while (state.KeepRunning()) { 32 | l = 0; 33 | auto list = [](int i) { return rangeFrom(0, i); }; 34 | ConsStream mapped = concatMap(list, rangeFrom(1l, x)); 35 | // Eat the stream so not recursively destroying 100K sharedptrs 36 | while (!mapped.tail().isEmpty()) { 37 | mapped = mapped.tail(); 38 | l++; 39 | } 40 | } 41 | std::stringstream ss; 42 | ss << x << ',' << l; 43 | state.SetLabel(ss.str()); 44 | } 45 | BENCHMARK(BM_ConcatMap)->Arg(8)->Arg(64)->Arg(512); 46 | 47 | static void BM_Join(benchmark::State& state) { 48 | auto x = state.range(0); 49 | size_t l = 0; 50 | while (state.KeepRunning()) { 51 | l = 0; 52 | ConsStream inf = iota(0); 53 | ConsStream> s2 = 54 | fmap(inf, [](int i) { return rangeFrom(0, i); }); 55 | ConsStream s3 = join(s2); 56 | ConsStream c = take(s3, x); 57 | // Eat the stream so not recursively destroying 100K sharedptrs 58 | while (!c.tail().isEmpty()) { 59 | c = c.tail(); 60 | l++; 61 | } 62 | } 63 | std::stringstream ss; 64 | ss << x << ',' << l; 65 | state.SetLabel(ss.str()); 66 | } 67 | BENCHMARK(BM_Join)->Arg(8)->Arg(64)->Arg(512); 68 | 69 | static void BM_Join2(benchmark::State& state) { 70 | auto x = state.range(0); 71 | size_t l = 0; 72 | while (state.KeepRunning()) { 73 | l = 0; 74 | ConsStream inf = iota(0); 75 | ConsStream> s2 = 76 | fmap(inf, [](int i) { return rangeFrom(0, i); }); 77 | ConsStream s3 = join2(s2); 78 | ConsStream c = take(s3, x); 79 | // Eat the stream so not recursively destroying 100K sharedptrs 80 | while (!c.tail().isEmpty()) { 81 | c = c.tail(); 82 | l++; 83 | } 84 | } 85 | std::stringstream ss; 86 | ss << x << ',' << l; 87 | state.SetLabel(ss.str()); 88 | } 89 | BENCHMARK(BM_Join2)->Arg(8)->Arg(64)->Arg(512); 90 | 91 | static void BM_Bind(benchmark::State& state) { 92 | auto x = state.range(0); 93 | size_t l = 0; 94 | while (state.KeepRunning()) { 95 | { 96 | state.ResumeTiming(); 97 | l = 0; 98 | ConsStream c = 99 | take(bind2(iota(0), [](int i) { return rangeFrom(0, i); }), x); 100 | l = last(c); 101 | state.PauseTiming(); 102 | } 103 | } 104 | std::stringstream ss; 105 | ss << x << ',' << l; 106 | state.SetLabel(ss.str()); 107 | } 108 | BENCHMARK(BM_Bind)->Arg(8)->Arg(64)->Arg(512)->Arg(1 << 10)->Arg(8 << 10); 109 | 110 | static void BM_Bind2(benchmark::State& state) { 111 | auto x = state.range(0); 112 | size_t l = 0; 113 | while (state.KeepRunning()) { 114 | { 115 | state.ResumeTiming(); 116 | l = 0; 117 | ConsStream c = 118 | take(bind2(iota(0), [](int i) { return rangeFrom(0, i); }), x); 119 | l = last(c); 120 | state.PauseTiming(); 121 | } 122 | } 123 | std::stringstream ss; 124 | ss << x << ',' << l; 125 | state.SetLabel(ss.str()); 126 | } 127 | BENCHMARK(BM_Bind2)->Arg(8)->Arg(64)->Arg(512)->Arg(1 << 10)->Arg(8 << 10); 128 | 129 | ConsStream> triples() { 130 | return bind(iota(1), [](int z) { 131 | return bind(rangeFrom(1, z), [z](int x) { 132 | return bind(rangeFrom(x, z), [x, z](int y) { 133 | return then(guard(x * x + y * y == z * z), [x, y, z]() { 134 | return make(std::make_tuple(x, y, z)); 135 | }); 136 | }); 137 | }); 138 | }); 139 | } 140 | 141 | static void BM_Triple1(benchmark::State& state) { 142 | int x = 0; 143 | int y = 0; 144 | int z = 0; 145 | while (state.KeepRunning()) 146 | std::tie(x, y, z) = last(take(triples(), 10)); 147 | 148 | std::stringstream ss; 149 | ss << x << ',' << y << ',' << z; 150 | state.SetLabel(ss.str()); 151 | } 152 | BENCHMARK(BM_Triple1); 153 | BENCHMARK(BM_Triple1)->UseRealTime(); 154 | 155 | ConsStream> triples2() { 156 | return bind2(iota(1), [](int z) { 157 | return bind2(rangeFrom(1, z), [z](int x) { 158 | return bind2(rangeFrom(x, z), [x, z](int y) { 159 | return then2(guard(x * x + y * y == z * z), [x, y, z]() { 160 | return make(std::make_tuple(x, y, z)); 161 | }); 162 | }); 163 | }); 164 | }); 165 | } 166 | 167 | static void BM_Triple2(benchmark::State& state) { 168 | int x = 0; 169 | int y = 0; 170 | int z = 0; 171 | while (state.KeepRunning()) 172 | std::tie(x, y, z) = last(take(triples2(), 10)); 173 | 174 | std::stringstream ss; 175 | ss << x << ',' << y << ',' << z; 176 | state.SetLabel(ss.str()); 177 | } 178 | BENCHMARK(BM_Triple2); 179 | BENCHMARK(BM_Triple2)->UseRealTime(); 180 | 181 | BENCHMARK_MAIN(); 182 | -------------------------------------------------------------------------------- /src/co_fun/stream.b.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | using namespace co_fun; 10 | 11 | int Factorial(uint32_t n) { return (n == 1) ? 1 : n * Factorial(n - 1); } 12 | 13 | static void BM_Concat(benchmark::State& state) { 14 | auto x = state.range(0); 15 | int l = 0; 16 | while (state.KeepRunning()) { 17 | ConsStream inf = iota(0); 18 | ConsStream s1 = take(inf, x); 19 | ConsStream> s2(take(iota(1), x)); 20 | ConsStream> stream = cons(s1, s2); 21 | ConsStream c = concat(stream); 22 | l = last(c); 23 | } 24 | std::stringstream ss; 25 | ss << l; 26 | state.SetLabel(ss.str()); 27 | } 28 | BENCHMARK(BM_Concat)->Arg(8)->Arg(64)->Arg(512); 29 | 30 | static void BM_ConcatMap(benchmark::State& state) { 31 | auto x = state.range(0); 32 | size_t l = 0; 33 | while (state.KeepRunning()) { 34 | l = 0; 35 | auto list = [](int i) { return rangeFrom(0, i); }; 36 | ConsStream mapped = concatMap(list, rangeFrom(1l, x)); 37 | // Eat the stream so not recursively destroying 100K sharedptrs 38 | while (!mapped.tail().isEmpty()) { 39 | mapped = mapped.tail(); 40 | l++; 41 | } 42 | } 43 | std::stringstream ss; 44 | ss << x << ',' << l; 45 | state.SetLabel(ss.str()); 46 | } 47 | BENCHMARK(BM_ConcatMap)->Arg(8)->Arg(64)->Arg(512); 48 | 49 | static void BM_Join(benchmark::State& state) { 50 | auto x = state.range(0); 51 | size_t l = 0; 52 | while (state.KeepRunning()) { 53 | l = 0; 54 | ConsStream inf = iota(0); 55 | ConsStream> s2 = 56 | fmap(inf, [](int i) { return rangeFrom(0, i); }); 57 | ConsStream s3 = join(s2); 58 | ConsStream c = take(s3, x); 59 | // Eat the stream so not recursively destroying 100K sharedptrs 60 | while (!c.tail().isEmpty()) { 61 | c = c.tail(); 62 | l++; 63 | } 64 | } 65 | std::stringstream ss; 66 | ss << x << ',' << l; 67 | state.SetLabel(ss.str()); 68 | } 69 | BENCHMARK(BM_Join)->Arg(8)->Arg(64)->Arg(512); 70 | 71 | static void BM_Join2(benchmark::State& state) { 72 | auto x = state.range(0); 73 | size_t l = 0; 74 | while (state.KeepRunning()) { 75 | l = 0; 76 | ConsStream inf = iota(0); 77 | ConsStream> s2 = 78 | fmap(inf, [](int i) { return rangeFrom(0, i); }); 79 | ConsStream s3 = join2(s2); 80 | ConsStream c = take(s3, x); 81 | // Eat the stream so not recursively destroying 100K sharedptrs 82 | while (!c.tail().isEmpty()) { 83 | c = c.tail(); 84 | l++; 85 | } 86 | } 87 | std::stringstream ss; 88 | ss << x << ',' << l; 89 | state.SetLabel(ss.str()); 90 | } 91 | BENCHMARK(BM_Join2)->Arg(8)->Arg(64)->Arg(512); 92 | 93 | static void BM_Bind(benchmark::State& state) { 94 | auto x = state.range(0); 95 | size_t l = 0; 96 | while (state.KeepRunning()) { 97 | { 98 | state.ResumeTiming(); 99 | l = 0; 100 | ConsStream c = 101 | take(bind(iota(0), [](int i) { return rangeFrom(0, i); }), x); 102 | l = last(c); 103 | state.PauseTiming(); 104 | } 105 | } 106 | std::stringstream ss; 107 | ss << x << ',' << l; 108 | state.SetLabel(ss.str()); 109 | } 110 | BENCHMARK(BM_Bind)->Arg(8)->Arg(64)->Arg(512)->Arg(1 << 10)->Arg(8 << 10); 111 | 112 | static void BM_Bind2(benchmark::State& state) { 113 | auto x = state.range(0); 114 | size_t l = 0; 115 | while (state.KeepRunning()) { 116 | { 117 | state.ResumeTiming(); 118 | l = 0; 119 | ConsStream c = 120 | take(bind2(iota(0), [](int i) { return rangeFrom(0, i); }), x); 121 | l = last(c); 122 | state.PauseTiming(); 123 | } 124 | } 125 | std::stringstream ss; 126 | ss << x << ',' << l; 127 | state.SetLabel(ss.str()); 128 | } 129 | BENCHMARK(BM_Bind2)->Arg(8)->Arg(64)->Arg(512)->Arg(1 << 10)->Arg(8 << 10); 130 | 131 | ConsStream> triples() { 132 | return bind(iota(1), [](int z) { 133 | return bind(rangeFrom(1, z), [z](int x) { 134 | return bind(rangeFrom(x, z), [x, z](int y) { 135 | return then(guard(x * x + y * y == z * z), [x, y, z]() { 136 | return make(std::make_tuple(x, y, z)); 137 | }); 138 | }); 139 | }); 140 | }); 141 | } 142 | 143 | static void BM_Triple1(benchmark::State& state) { 144 | int x = 0; 145 | int y = 0; 146 | int z = 0; 147 | while (state.KeepRunning()) 148 | std::tie(x, y, z) = last(take(triples(), 10)); 149 | 150 | std::stringstream ss; 151 | ss << x << ',' << y << ',' << z; 152 | state.SetLabel(ss.str()); 153 | } 154 | BENCHMARK(BM_Triple1); 155 | BENCHMARK(BM_Triple1)->UseRealTime(); 156 | 157 | ConsStream> triples2() { 158 | return bind2(iota(1), [](int z) { 159 | return bind2(rangeFrom(1, z), [z](int x) { 160 | return bind2(rangeFrom(x, z), [x, z](int y) { 161 | return then2(guard(x * x + y * y == z * z), [x, y, z]() { 162 | return make(std::make_tuple(x, y, z)); 163 | }); 164 | }); 165 | }); 166 | }); 167 | } 168 | 169 | static void BM_Triple2(benchmark::State& state) { 170 | int x = 0; 171 | int y = 0; 172 | int z = 0; 173 | while (state.KeepRunning()) 174 | std::tie(x, y, z) = last(take(triples2(), 10)); 175 | 176 | std::stringstream ss; 177 | ss << x << ',' << y << ',' << z; 178 | state.SetLabel(ss.str()); 179 | } 180 | BENCHMARK(BM_Triple2); 181 | BENCHMARK(BM_Triple2)->UseRealTime(); 182 | 183 | BENCHMARK_MAIN(); 184 | -------------------------------------------------------------------------------- /src/co_fun/thunk.t.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | using namespace co_fun; 6 | 7 | TEST(Co_FunThunkTest, TestGTest) { ASSERT_EQ(1, 1); } 8 | 9 | using ::testing::Test; 10 | 11 | namespace testing { 12 | namespace { 13 | int func_called; 14 | int func2_called; 15 | int func3_called; 16 | } // namespace 17 | 18 | namespace { 19 | int func() { 20 | func_called++; 21 | return 5; 22 | } 23 | 24 | Thunk co_func() { 25 | func_called++; 26 | co_return 5; 27 | } 28 | 29 | int func2(int i) { 30 | func2_called++; 31 | return i; 32 | } 33 | 34 | int func3(int i, int j) { 35 | func3_called++; 36 | return i + j; 37 | } 38 | 39 | Thunk co_func_str() { co_return std::string("a string"); } 40 | 41 | std::string stringTest(const char* str) { return str; } 42 | } // namespace 43 | 44 | TEST(Co_FunThunkTest, Breathing) { 45 | Thunk thunk; 46 | EXPECT_EQ(false, thunk.evaluated()); 47 | EXPECT_EQ(true, thunk.isEmpty()); 48 | 49 | Thunk thunk2(std::move(thunk)); 50 | EXPECT_EQ(false, thunk2.evaluated()); 51 | EXPECT_EQ(true, thunk2.isEmpty()); 52 | Thunk thunk3(3); 53 | EXPECT_EQ(true, thunk3.evaluated()); 54 | EXPECT_EQ(false, thunk3.isEmpty()); 55 | 56 | int j{thunk3}; 57 | 58 | EXPECT_EQ(3, j); 59 | 60 | Thunk D2 = co_func(); 61 | EXPECT_EQ(0, func_called); 62 | EXPECT_EQ(false, D2.evaluated()); 63 | EXPECT_EQ(false, D2.isEmpty()); 64 | int i = D2; 65 | EXPECT_EQ(i, 5); 66 | EXPECT_EQ(1, func_called); 67 | 68 | int i2 = D2; 69 | EXPECT_EQ(i2, 5); 70 | EXPECT_EQ(1, func_called); 71 | } 72 | 73 | TEST(Co_FunThunkTest, Rvalue) { 74 | Thunk s1 = co_func_str(); 75 | EXPECT_EQ(false, s1.evaluated()); 76 | EXPECT_EQ(false, s1.isEmpty()); 77 | EXPECT_EQ(std::string("a string"), evaluate(s1)); 78 | 79 | EXPECT_EQ(std::string("a string"), evaluate(co_func_str())); 80 | } 81 | 82 | TEST(Co_FunThunkTest, Assignment) { 83 | Thunk t3(3); 84 | Thunk t1; 85 | t1 = t3; 86 | } 87 | 88 | TEST(Co_FunThunkTest, Move) { 89 | std::string str; 90 | Thunk d1(str); 91 | Thunk d2("test"); 92 | Thunk d3 = thunk(stringTest, "this is a test"); 93 | // Thunk d4([]() { return stringTest("another test"); 94 | // }); 95 | 96 | EXPECT_TRUE(d1.evaluated()); 97 | EXPECT_TRUE(d2.evaluated()); 98 | EXPECT_FALSE(d3.evaluated()); 99 | // EXPECT_FALSE(d4.evaluated()); 100 | 101 | EXPECT_EQ(std::string("this is a test"), evaluate(d3)); 102 | // EXPECT_EQ(std::string("another test"), force(d4)); 103 | } 104 | 105 | TEST(Co_FunThunkTest, Sharing) { 106 | Thunk thunk = co_func(); 107 | Thunk t2 = thunk; 108 | Thunk t3 = t2; 109 | 110 | int k = thunk; 111 | EXPECT_EQ(k, 5); 112 | EXPECT_TRUE(t3.evaluated()); 113 | } 114 | 115 | struct watch_destruction { 116 | static int destructor_counter; 117 | 118 | ~watch_destruction() { ++destructor_counter; } 119 | }; 120 | 121 | int watch_destruction::destructor_counter = 0; 122 | 123 | Thunk test_destruction(watch_destruction w) { 124 | co_return w.destructor_counter; 125 | } 126 | 127 | TEST(Co_FunThunkTest, Leak) { 128 | watch_destruction::destructor_counter = 0; 129 | { 130 | auto t = test_destruction(watch_destruction{}); 131 | EXPECT_EQ(watch_destruction::destructor_counter, 1); 132 | int i = t; 133 | EXPECT_EQ(i, 1); 134 | EXPECT_EQ(watch_destruction::destructor_counter, 2); 135 | } 136 | EXPECT_EQ(watch_destruction::destructor_counter, 2); 137 | watch_destruction::destructor_counter = 0; 138 | { 139 | auto t2 = thunk(test_destruction, watch_destruction{}); 140 | EXPECT_EQ(watch_destruction::destructor_counter, 1); 141 | // No evaluation of thunk - potential leak 142 | } 143 | EXPECT_EQ(watch_destruction::destructor_counter, 2); 144 | } 145 | 146 | TEST(Co_FunThunkTest, TransformTest) { 147 | auto t = thunk([]() { return 5; }); 148 | auto i = transform(std::move(t), [](auto j) { return j * 2; }); 149 | EXPECT_TRUE(t.isEmpty()); 150 | EXPECT_EQ(i, 10); 151 | EXPECT_TRUE(t.isEmpty()); 152 | 153 | auto i2 = transform(Thunk{6}, [](auto j) { return j * 2; }); 154 | EXPECT_EQ(i2, 12); 155 | 156 | auto t3 = thunk([]() { return 5; }); 157 | auto i3 = transform(t3, [](auto j) { return j * 2; }); 158 | EXPECT_FALSE(t3.evaluated()); 159 | EXPECT_EQ(i3, 10); 160 | EXPECT_TRUE(t3.evaluated()); 161 | } 162 | 163 | TEST(Co_FunThunkTest, JoinTest) { 164 | Thunk> l = thunk([]() { return Thunk{5}; }); 165 | Thunk j = join(l); 166 | EXPECT_EQ(j, 5); 167 | 168 | Thunk l2 = thunk([](auto i, auto j) { return i + j; }, 3, 5); 169 | Thunk> l3 = [](auto l) -> Thunk> { 170 | co_return l; 171 | }(std::move(l2)); 172 | Thunk l4 = join(l3); 173 | EXPECT_EQ(false, l4.evaluated()); 174 | EXPECT_EQ(8, l4); 175 | } 176 | 177 | TEST(Co_FunThunkTest, BindTest) { 178 | Thunk l = thunk([]() { return 5; }); 179 | Thunk b = 180 | bind(l, [](int i) -> Thunk { co_return 1.0 * i; }); 181 | EXPECT_EQ(false, b.evaluated()); 182 | EXPECT_EQ(5.0, b); 183 | EXPECT_EQ(true, b.evaluated()); 184 | } 185 | 186 | TEST(Co_FunThunkTest, BindTestRvalue) { 187 | Thunk b = bind(thunk([]() { return 5; }), 188 | [](int i) -> Thunk { co_return 1.0 * i; }); 189 | EXPECT_EQ(false, b.evaluated()); 190 | EXPECT_EQ(5.0, b); 191 | EXPECT_EQ(true, b.evaluated()); 192 | } 193 | 194 | TEST(Co_FunThunkTest, Bind2Test) { 195 | Thunk l = thunk([]() { return 5; }); 196 | Thunk b = 197 | bind2(l, [](int i) -> Thunk { co_return 1.0 * i; }); 198 | EXPECT_EQ(false, b.evaluated()); 199 | EXPECT_EQ(5.0, b); 200 | EXPECT_EQ(true, b.evaluated()); 201 | } 202 | 203 | TEST(Co_FunThunkTest, Bind2TestRvalue) { 204 | Thunk b = bind2(thunk([]() { return 5; }), 205 | [](int i) -> Thunk { co_return 1.0 * i; }); 206 | EXPECT_EQ(false, b.evaluated()); 207 | EXPECT_EQ(5.0, b); 208 | EXPECT_EQ(true, b.evaluated()); 209 | } 210 | 211 | 212 | } // namespace testing 213 | -------------------------------------------------------------------------------- /src/delay/streamasync.b.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | static void BM_ConcatAsync(benchmark::State& state) { 10 | auto x = state.range(0); 11 | int l = 0; 12 | while (state.KeepRunning()) { 13 | ConsStreamAsync inf = iota(0); 14 | ConsStreamAsync s1 = take(inf, x); 15 | ConsStreamAsync> s2(take(iota(1), x)); 16 | ConsStreamAsync> streamAsync = cons(s1, s2); 17 | ConsStreamAsync c = concat(streamAsync); 18 | l = last(c); 19 | } 20 | std::stringstream ss; 21 | ss << l; 22 | state.SetLabel(ss.str()); 23 | } 24 | BENCHMARK(BM_ConcatAsync)->Arg(8)->Arg(64)->Arg(512); 25 | 26 | static void BM_ConcatMapAsync(benchmark::State& state) { 27 | auto x = state.range(0); 28 | size_t l = 0; 29 | while (state.KeepRunning()) { 30 | l = 0; 31 | auto list = [](int i) { return rangeFrom(0, i); }; 32 | ConsStreamAsync mapped = concatMap(list, rangeFrom(1l, x)); 33 | // Eat the streamAsync so not recursively destroying 100K sharedptrs 34 | while (!mapped.tail().isEmpty()) { 35 | mapped = mapped.tail(); 36 | l++; 37 | } 38 | } 39 | std::stringstream ss; 40 | ss << x << ',' << l; 41 | state.SetLabel(ss.str()); 42 | } 43 | BENCHMARK(BM_ConcatMapAsync)->Arg(8)->Arg(64)->Arg(512); 44 | 45 | static void BM_JoinAsync(benchmark::State& state) { 46 | auto x = state.range(0); 47 | size_t l = 0; 48 | while (state.KeepRunning()) { 49 | l = 0; 50 | ConsStreamAsync inf = iota(0); 51 | ConsStreamAsync> s2 = 52 | fmap(inf, [](int i) { return rangeFrom(0, i); }); 53 | ConsStreamAsync s3 = join(s2); 54 | ConsStreamAsync c = take(s3, x); 55 | // Eat the streamAsync so not recursively destroying 100K sharedptrs 56 | while (!c.tail().isEmpty()) { 57 | c = c.tail(); 58 | l++; 59 | } 60 | } 61 | std::stringstream ss; 62 | ss << x << ',' << l; 63 | state.SetLabel(ss.str()); 64 | } 65 | BENCHMARK(BM_JoinAsync)->Arg(8)->Arg(64)->Arg(512); 66 | 67 | static void BM_Join2Async(benchmark::State& state) { 68 | auto x = state.range(0); 69 | size_t l = 0; 70 | while (state.KeepRunning()) { 71 | l = 0; 72 | ConsStreamAsync inf = iota(0); 73 | ConsStreamAsync> s2 = 74 | fmap(inf, [](int i) { return rangeFrom(0, i); }); 75 | ConsStreamAsync s3 = join2(s2); 76 | ConsStreamAsync c = take(s3, x); 77 | // Eat the streamAsync so not recursively destroying 100K sharedptrs 78 | while (!c.tail().isEmpty()) { 79 | c = c.tail(); 80 | l++; 81 | } 82 | } 83 | std::stringstream ss; 84 | ss << x << ',' << l; 85 | state.SetLabel(ss.str()); 86 | } 87 | BENCHMARK(BM_Join2Async)->Arg(8)->Arg(64)->Arg(512); 88 | 89 | static void BM_BindAsync(benchmark::State& state) { 90 | auto x = state.range(0); 91 | size_t l = 0; 92 | while (state.KeepRunning()) { 93 | { 94 | state.ResumeTiming(); 95 | l = 0; 96 | ConsStreamAsync c = 97 | take(bind2(iota(0), [](int i) { return rangeFrom(0, i); }), x); 98 | l = last(c); 99 | state.PauseTiming(); 100 | } 101 | } 102 | std::stringstream ss; 103 | ss << x << ',' << l; 104 | state.SetLabel(ss.str()); 105 | } 106 | BENCHMARK(BM_BindAsync)->Arg(8)->Arg(64)->Arg(512)->Arg(1 << 10)->Arg(8 << 10); 107 | 108 | static void BM_Bind2Async(benchmark::State& state) { 109 | auto x = state.range(0); 110 | size_t l = 0; 111 | while (state.KeepRunning()) { 112 | { 113 | state.ResumeTiming(); 114 | l = 0; 115 | ConsStreamAsync c = 116 | take(bind2(iota(0), [](int i) { return rangeFrom(0, i); }), x); 117 | l = last(c); 118 | state.PauseTiming(); 119 | } 120 | } 121 | std::stringstream ss; 122 | ss << x << ',' << l; 123 | state.SetLabel(ss.str()); 124 | } 125 | BENCHMARK(BM_Bind2Async) 126 | ->Arg(8) 127 | ->Arg(64) 128 | ->Arg(512) 129 | ->Arg(1 << 10) 130 | ->Arg(8 << 10); 131 | 132 | namespace { 133 | ConsStreamAsync> triples() { 134 | return bind(iota(1), [](int z) { 135 | return bind(rangeFrom(1, z), [z](int x) { 136 | return bind(rangeFrom(x, z), [x, z](int y) { 137 | return then(guardAsync(x * x + y * y == z * z), [x, y, z]() { 138 | return make(std::make_tuple(x, y, z)); 139 | }); 140 | }); 141 | }); 142 | }); 143 | } 144 | } // namespace 145 | 146 | static void BM_Triple1Async(benchmark::State& state) { 147 | int x = 0; 148 | int y = 0; 149 | int z = 0; 150 | while (state.KeepRunning()) 151 | std::tie(x, y, z) = last(take(triples(), 10)); 152 | 153 | std::stringstream ss; 154 | ss << x << ',' << y << ',' << z; 155 | state.SetLabel(ss.str()); 156 | } 157 | BENCHMARK(BM_Triple1Async); 158 | BENCHMARK(BM_Triple1Async)->UseRealTime(); 159 | 160 | namespace { 161 | ConsStreamAsync> triples2() { 162 | return bind2(iota(1), [](int z) { 163 | return bind2(rangeFrom(1, z), [z](int x) { 164 | return bind2(rangeFrom(x, z), [x, z](int y) { 165 | return then2(guardAsync(x * x + y * y == z * z), [x, y, z]() { 166 | return make(std::make_tuple(x, y, z)); 167 | }); 168 | }); 169 | }); 170 | }); 171 | } 172 | } // namespace 173 | static void BM_Triple2Async(benchmark::State& state) { 174 | int x = 0; 175 | int y = 0; 176 | int z = 0; 177 | while (state.KeepRunning()) 178 | std::tie(x, y, z) = last(take(triples2(), 10)); 179 | 180 | std::stringstream ss; 181 | ss << x << ',' << y << ',' << z; 182 | state.SetLabel(ss.str()); 183 | } 184 | BENCHMARK(BM_Triple2Async); 185 | BENCHMARK(BM_Triple2Async)->UseRealTime(); 186 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /src/delay/stream.h: -------------------------------------------------------------------------------- 1 | // stream.h -*-C++-*- 2 | #ifndef INCLUDED_STREAM 3 | #define INCLUDED_STREAM 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | template 12 | class ConsStream; 13 | 14 | template 15 | class ConsStreamIterator; 16 | 17 | template 18 | class ConsCell { 19 | Value head_; 20 | ConsStream tail_; 21 | 22 | friend class ConsStreamIterator; 23 | 24 | public: 25 | ConsCell(Value const& v, ConsStream const& stream) 26 | : head_(v), tail_(stream) { 27 | } 28 | 29 | explicit ConsCell(Value const& v) : head_(v), tail_() { 30 | } 31 | 32 | Value const& head() const { 33 | return head_; 34 | } 35 | 36 | ConsStream const& tail() const { 37 | return tail_; 38 | } 39 | }; 40 | 41 | template 42 | class ConsStream { 43 | std::shared_ptr>> delayed_cell_; 44 | 45 | friend class ConsStreamIterator; 46 | 47 | public: 48 | typedef Value value; 49 | 50 | ConsStream() = default; 51 | 52 | ConsStream(Value const& value) 53 | : delayed_cell_(std::make_shared>>( 54 | [value]() { return ConsCell(value); })) { 55 | } 56 | 57 | ConsStream(Value && value) 58 | : delayed_cell_(std::make_shared>>( 59 | [v = std::forward(value)]() { return ConsCell(v); })) { 60 | } 61 | 62 | template ::value>::type> 65 | ConsStream(Func&& f) 66 | : delayed_cell_(std::make_shared>>(f)) { 67 | } 68 | 69 | bool isEmpty() const { 70 | return !delayed_cell_; 71 | } 72 | 73 | Value head() const { 74 | return force(*delayed_cell_).head(); 75 | } 76 | 77 | ConsStream tail() const { 78 | return force(*delayed_cell_).tail(); 79 | } 80 | 81 | typedef ConsStreamIterator iterator; 82 | 83 | iterator begin() { 84 | return iterator(delayed_cell_); 85 | }; 86 | 87 | iterator end() { 88 | return iterator(); 89 | } 90 | 91 | int countForced() { 92 | if (!delayed_cell_) { 93 | return 0; 94 | } 95 | 96 | auto cell = delayed_cell_; 97 | int forced = 0; 98 | while (cell && cell->isForced()) { 99 | ++forced; 100 | cell = cell->get().tail().delayed_cell_; 101 | } 102 | return forced; 103 | } 104 | }; 105 | 106 | template 107 | ConsStream make_stream(Value v) { 108 | return ConsStream([v]() { return ConsCell(v); }); 109 | } 110 | 111 | 112 | template typename Applicative, typename Value> 113 | struct Make { 114 | Applicative operator()(Value const& v); 115 | Applicative operator()(Value && v); 116 | }; 117 | 118 | template 119 | struct Make { 120 | typedef typename std::decay::type V; 121 | ConsStream operator()(Value const& v) { 122 | // return ConsStream([v]() { return ConsCell(v); }); 123 | return ConsStream(v); 124 | } 125 | ConsStream operator()(V && v) { 126 | // return ConsStream([v]() { return ConsCell(v); }); 127 | return ConsStream(v); 128 | } 129 | }; 130 | 131 | template typename Applicative, typename Value> 132 | Applicative::type> make(Value const& v){ 133 | Make m; 134 | return m(v); 135 | } 136 | 137 | template typename Applicative, typename Value> 138 | Applicative::type> make(Value && v){ 139 | Make m; 140 | return m(v); 141 | } 142 | 143 | template 144 | class ConsStreamIterator : public std::iterator, 146 | std::ptrdiff_t, 147 | Value*, 148 | Value&> { 149 | std::shared_ptr>> delayed_cell_; 150 | 151 | explicit ConsStreamIterator(std::shared_ptr>> cell) 152 | : delayed_cell_(cell) { 153 | } 154 | 155 | friend class ConsStream; 156 | 157 | public: 158 | ConsStreamIterator() = default; // Default construct gives end. 159 | 160 | void swap(ConsStreamIterator& other) noexcept { 161 | using std::swap; 162 | swap(delayed_cell_, other.delayed_cell_); 163 | } 164 | 165 | ConsStreamIterator& operator++() // Pre-increment 166 | { 167 | delayed_cell_ = force(*delayed_cell_).tail().delayed_cell_; 168 | return *this; 169 | } 170 | 171 | ConsStreamIterator operator++(int) // Post-increment 172 | { 173 | ConsStreamIterator tmp(*this); 174 | delayed_cell_ = force(*delayed_cell_).tail().delayed_cell_; 175 | return tmp; 176 | } 177 | 178 | // two-way comparison: v.begin() == v.cbegin() and vice versa 179 | template 180 | bool operator==(const ConsStreamIterator& rhs) const { 181 | return delayed_cell_ == rhs.delayed_cell_; 182 | } 183 | 184 | template 185 | bool operator!=(const ConsStreamIterator& rhs) const { 186 | return delayed_cell_ != rhs.delayed_cell_; 187 | } 188 | 189 | Value const& operator*() const { 190 | return force(*delayed_cell_).head_; 191 | } 192 | 193 | Value const* operator->() const { 194 | return &force(*delayed_cell_).head_; 195 | } 196 | }; 197 | 198 | template 199 | ConsStream cons(Value n, ConsStream const& stream) { 200 | return ConsStream( 201 | [n, stream]() { return ConsCell(n, stream); }); 202 | } 203 | 204 | template 205 | Value last(ConsStream const& stream) { 206 | ConsStream s = stream; 207 | while (!s.tail().isEmpty()) { 208 | s = s.tail(); 209 | } 210 | return s.head(); 211 | } 212 | 213 | template 214 | ConsStream init(ConsStream const& stream) { 215 | if (stream.tail().isEmpty()) { 216 | return ConsStream(); 217 | } 218 | return cons(stream.head(), init(stream.tail())); 219 | } 220 | 221 | template 222 | size_t lengthAcc(ConsStream const& stream, size_t n) { 223 | if (stream.isEmpty()) { 224 | return n; 225 | } 226 | return lengthAcc(stream.tail(), n+1); 227 | } 228 | 229 | template 230 | size_t length(ConsStream const& stream) { 231 | return lengthAcc(stream, 0); 232 | } 233 | 234 | template 235 | ConsStream filter(Predicate const& p, ConsStream stream) { 236 | while (!stream.isEmpty() && !p(stream.head())) { 237 | stream = stream.tail(); 238 | } 239 | 240 | if (stream.isEmpty()) { 241 | return ConsStream(); 242 | } 243 | 244 | return ConsStream( 245 | [p, stream]() { 246 | return ConsCell(stream.head(), filter(p, stream.tail())); 247 | }); 248 | } 249 | 250 | template 251 | ConsStream rangeFrom(Value n, Value m) { 252 | if (n > m) { 253 | return ConsStream(); 254 | } 255 | return ConsStream( 256 | [n, m]() { return ConsCell(n, rangeFrom(n + 1, m)); }); 257 | } 258 | 259 | template 260 | ConsStream iota(Value n = Value()) { 261 | return ConsStream([n]() { return ConsCell(n, iota(n + 1)); }); 262 | } 263 | 264 | template 265 | ConsStream take(ConsStream const& strm, int n) { 266 | if (n == 0 || strm.isEmpty()) { 267 | return ConsStream(); 268 | } 269 | return ConsStream([strm, n]() { 270 | return ConsCell(strm.head(), take(strm.tail(), n - 1)); 271 | }); 272 | } 273 | 274 | template 275 | ConsStream drop(ConsStream const& strm, int n) { 276 | if (strm.isEmpty()) { 277 | return ConsStream(); 278 | } 279 | 280 | if (n == 0) { 281 | return strm; 282 | } 283 | 284 | return drop(strm.tail(), n-1); 285 | } 286 | 287 | template 288 | ConsStream append(ConsStream const& first, 289 | ConsStream const& second) { 290 | if (first.isEmpty()) { 291 | return second; 292 | } 293 | return ConsStream([first, second]() { 294 | return ConsCell(first.head(), append(first.tail(), second)); 295 | }); 296 | } 297 | 298 | template 299 | ConsStream append(ConsStream const& first, 300 | Delay> const& second) { 301 | if (first.isEmpty()) { 302 | return force(second); 303 | } 304 | return ConsStream([first, second]() { 305 | return ConsCell(first.head(), append(first.tail(), second)); 306 | }); 307 | } 308 | 309 | template 310 | auto fmap(ConsStream const& stream, Func const& f) 311 | -> ConsStream { 312 | using Mapped = decltype(f(stream.head())); 313 | if (stream.isEmpty()) { 314 | return ConsStream(); 315 | } 316 | 317 | return ConsStream([stream, f]() { 318 | return ConsCell(f(stream.head()), fmap(stream.tail(), f)); 319 | }); 320 | } 321 | 322 | /* 323 | foldr :: (a -> b -> b) -> b -> [a] -> b 324 | foldr f z [] = z 325 | foldr f z (x:xs) = f x (foldr f z xs) 326 | */ 327 | template 328 | Result foldr(Op op, Result const& init, ConsStream const& stream) { 329 | if (stream.isEmpty()) { 330 | return init; 331 | } 332 | return op(stream.head(), delay(foldr, 333 | op, 334 | init, 335 | stream.tail())); 336 | } 337 | 338 | /* 339 | concat :: [[a]] -> [a] 340 | concat xss = foldr (++) [] xss 341 | */ 342 | // Note - copy streams, because we're going to reassign to it 343 | template 344 | ConsStream concat(ConsStream> streams) { 345 | while (!streams.isEmpty() && streams.head().isEmpty()) { 346 | streams = streams.tail(); 347 | } 348 | 349 | if (streams.isEmpty()) { 350 | return ConsStream(); 351 | } 352 | 353 | return foldr( 354 | static_cast (&)( 355 | ConsStream const&, Delay> const&)>(append), 356 | ConsStream(), 357 | streams); 358 | } 359 | 360 | // Note - copy streams, because we're going to reassign to it 361 | template 362 | ConsStream join(ConsStream> streams) { 363 | while (!streams.isEmpty() && streams.head().isEmpty()) { 364 | streams = streams.tail(); 365 | } 366 | 367 | if (streams.isEmpty()) { 368 | return ConsStream(); 369 | } 370 | 371 | return ConsStream([streams]() { 372 | return ConsCell(streams.head().head(), 373 | append(streams.head().tail(), join(streams.tail()))); 374 | }); 375 | } 376 | 377 | template 378 | auto bind(ConsStream const& stream, Func const& f) 379 | -> decltype(f(stream.head())) { 380 | return join(fmap(stream, f)); 381 | } 382 | 383 | template 384 | auto then(ConsStream const& stream, Func const& f) -> decltype(f()) { 385 | return join(fmap(stream, [f](Value const&) { return f(); })); 386 | } 387 | 388 | // Note - copy streams, because we're going to reassign to itx 389 | template 390 | auto bind2(ConsStream stream, Func const& f) 391 | -> decltype(f(stream.head())) { 392 | using M = decltype(bind2(stream, f)); 393 | 394 | if (stream.isEmpty()) { 395 | return M(); 396 | } 397 | 398 | auto y = f(stream.head()); 399 | while (!stream.isEmpty() && y.isEmpty()) { 400 | stream = stream.tail(); 401 | if (!stream.isEmpty()) { 402 | y = f(stream.head()); 403 | } 404 | } 405 | 406 | if (stream.isEmpty()) { 407 | return M(); 408 | } 409 | 410 | return M([y, stream, f]() { 411 | using T=decltype(y.head()); 412 | return ConsCell(y.head(), 413 | append(y.tail(), 414 | bind2(stream.tail(), f))); 415 | }); 416 | 417 | } 418 | 419 | template 420 | auto then2(ConsStream const& stream, Func const& f) -> decltype(f()) { 421 | return bind2(stream, [f](Value const&) { return f(); }); 422 | } 423 | 424 | template 425 | ConsStream join2(ConsStream> streams) { 426 | return bind2(streams, [](auto&& v){return std::forward(v);}); 427 | } 428 | 429 | using Unit = std::tuple<>; 430 | 431 | ConsStream guard(bool b) { 432 | if (b) { 433 | return ConsStream(Unit()); 434 | } else { 435 | return ConsStream(); 436 | } 437 | } 438 | 439 | // dot: :: (b -> c) -> (a -> b) -> (a -> c) 440 | template 441 | auto dot(FuncF&& f, FuncG&& g) { 442 | return [f = std::forward(f), 443 | g = std::forward(g)] (auto&&... xs) { 444 | return f(g(std::forward(xs)...)); 445 | }; 446 | } 447 | 448 | //-- | Map a function over a list and concatenate the results. 449 | //concatMap :: (a -> [b]) -> [a] -> [b] 450 | //concatMap f = foldr ((++) . f) [] 451 | 452 | template 453 | auto concatMap(Func&& f, ConsStream const& stream) { 454 | // -> ConsStream { 455 | using ResultOf = std::result_of_t; 456 | 457 | auto appendF = [f_ = std::forward(f)] 458 | (Value v, Delay const& s) { 459 | return append(f_(v), s); 460 | }; 461 | 462 | return foldr( 463 | appendF, 464 | ResultOf(), 465 | stream); 466 | } 467 | 468 | 469 | // template 470 | // ConsStream make_consstream(Value v) { 471 | // return ConsStream(v); 472 | // } 473 | 474 | // Applicative: 475 | // app :: f (a -> b) -> f a -> f b 476 | // fs <*> xs = [f x | f <- fs, x <- xs] 477 | // == concatMap (\f -> concatMap (\x -> [f x]) xs) fs 478 | // == fs >>= (\f -> xs >>= \x -> return (f x)) 479 | 480 | template 481 | auto app2(ConsStream const& funcs, ConsStream const& values) 482 | // -> decltype(funcs.head()(values.head())) { 483 | { 484 | return concatMap( 485 | [values](Func const& f){ 486 | return concatMap( 487 | [f](Value v){ 488 | return make(f(v)); 489 | }, 490 | values); 491 | }, 492 | funcs); 493 | // return funcs.head()(values.head()); 494 | } 495 | template 496 | auto app(ConsStream const& funcs, ConsStream const& values) 497 | // -> decltype(funcs.head()(values.head())) { 498 | { 499 | return bind2(funcs, 500 | [values](Func const& f){ 501 | return bind2(values, 502 | [f](Value const& v){ 503 | return make(f(v)); 504 | }); 505 | }); 506 | } 507 | #endif 508 | -------------------------------------------------------------------------------- /src/co_fun/stream.h: -------------------------------------------------------------------------------- 1 | // stream.h -*-C++-*- 2 | #ifndef INCLUDED_STREAMASYNC 3 | #define INCLUDED_STREAMASYNC 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace co_fun { 14 | template 15 | class ConsStream; 16 | 17 | template 18 | class ConsStreamIterator; 19 | 20 | template 21 | class ConsCell { 22 | Value head_; 23 | ConsStream tail_; 24 | 25 | friend class ConsStreamIterator; 26 | 27 | public: 28 | ConsCell(Value const& v, ConsStream const& stream) 29 | : head_(v), tail_(stream) {} 30 | 31 | explicit ConsCell(Value const& v) : head_(v), tail_() {} 32 | 33 | Value const& head() const { return head_; } 34 | 35 | ConsStream const& tail() const { return tail_; } 36 | }; 37 | 38 | template 39 | class ConsStream { 40 | Thunk> thunked_cell_; 41 | 42 | friend class ConsStreamIterator; 43 | 44 | public: 45 | typedef Value value; 46 | 47 | ConsStream() = default; 48 | 49 | ConsStream(Value const& value) 50 | : thunked_cell_(Thunk>( 51 | thunk([value]() { return ConsCell(value); }))) {} 52 | 53 | ConsStream(Value&& value) 54 | : thunked_cell_( 55 | Thunk>(thunk([v = std::forward(value)]() { 56 | return ConsCell(v); 57 | }))) {} 58 | 59 | template ::value>::type> 62 | ConsStream(Func&& f) : thunked_cell_(Thunk>(thunk(f))) {} 63 | 64 | bool isEmpty() const { return thunked_cell_.isEmpty(); } 65 | 66 | Value head() const { return evaluate(thunked_cell_).head(); } 67 | 68 | ConsStream tail() const { return evaluate(thunked_cell_).tail(); } 69 | 70 | using iterator = ConsStreamIterator; 71 | 72 | iterator begin() { return iterator(thunked_cell_); }; 73 | 74 | iterator end() { return iterator(); } 75 | 76 | int countEvaluated() { 77 | if (thunked_cell_.isEmpty()) { 78 | return 0; 79 | } 80 | 81 | auto cell = thunked_cell_; 82 | int evaluated = 0; 83 | while (!cell.isEmpty() && cell.evaluated()) { 84 | ++evaluated; 85 | cell = cell.get().tail().thunked_cell_; 86 | } 87 | return evaluated; 88 | } 89 | }; 90 | 91 | template 92 | ConsStream make_stream(Value v) { 93 | return ConsStream([v]() { return ConsCell(v); }); 94 | } 95 | 96 | template