├── .clang-format ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE ├── Makefile ├── README.org ├── cmake └── Config.cmake.in ├── doc └── model.org ├── etc ├── clang-11-toolchain.cmake ├── clang-12-toolchain.cmake ├── common-flags.cmake ├── gcc-10-toolchain.cmake ├── gcc-11-toolchain.cmake └── llvm-master-toolchain.cmake ├── extern └── CMakeLists.txt └── src ├── CMakeLists.txt ├── co_fun ├── CMakeLists.txt ├── co_fun.cpp ├── co_fun.h ├── co_fun.t.cpp ├── holder.cpp ├── holder.h ├── holder.t.cpp ├── lazy.cpp ├── lazy.h ├── lazy.t.cpp ├── stream.b.cpp ├── stream.cpp ├── stream.h ├── stream.t.cpp ├── thunk.cpp ├── thunk.h └── thunk.t.cpp ├── delay ├── CMakeLists.txt ├── delay.cpp ├── delay.h ├── delay.t.cpp ├── delayasync.cpp ├── delayasync.h ├── delayasync.t.cpp ├── stream.b.cpp ├── stream.cpp ├── stream.h ├── stream.t.cpp ├── streamasync.b.cpp ├── streamasync.cpp ├── streamasync.h ├── streamasync.t.cpp └── test1.cpp └── examples ├── CMakeLists.txt └── main.cpp /.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 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /compile_commands.json 2 | /.cache/ 3 | /.vscode/ 4 | /build/ 5 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(delay) 2 | add_subdirectory(co_fun) 3 | add_subdirectory(examples) 4 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/co_fun/co_fun.cpp: -------------------------------------------------------------------------------- 1 | // co_fun.cpp -*-C++-*- 2 | #include 3 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/co_fun/holder.cpp: -------------------------------------------------------------------------------- 1 | // holder.cpp -*-C++-*- 2 | #include 3 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /src/co_fun/lazy.cpp: -------------------------------------------------------------------------------- 1 | // lazy.cpp -*-C++-*- 2 | #include 3 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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/stream.cpp: -------------------------------------------------------------------------------- 1 | // stream.cpp -*-C++-*- 2 | #include 3 | -------------------------------------------------------------------------------- /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