├── .gitignore ├── example.pro ├── cmake_uninstall.cmake.in ├── LICENSE ├── main.cpp ├── example.h ├── CMakeLists.txt ├── README.md ├── example.cpp └── task.hpp /.gitignore: -------------------------------------------------------------------------------- 1 | example.pro.* 2 | CMakeLists.txt.* 3 | -------------------------------------------------------------------------------- /example.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | CONFIG += console -std=c++11 3 | CONFIG -= app_bundle 4 | 5 | QMAKE_CXXFLAGS += -std=c++11 -O2 -I/usr/lib64/llvm/include/c++/v1/ 6 | 7 | SOURCES += main.cpp example.cpp 8 | 9 | HEADERS += example.h task.hpp 10 | 11 | -------------------------------------------------------------------------------- /cmake_uninstall.cmake.in: -------------------------------------------------------------------------------- 1 | 2 | cmake_policy(SET CMP0007 NEW) 3 | 4 | if (NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") 5 | message(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"") 6 | endif(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") 7 | 8 | file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) 9 | string(REGEX REPLACE "\n" ";" files "${files}") 10 | list(REVERSE files) 11 | foreach (file ${files}) 12 | message(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"") 13 | if (EXISTS "$ENV{DESTDIR}${file}") 14 | execute_process( 15 | COMMAND @CMAKE_COMMAND@ -E remove "$ENV{DESTDIR}${file}" 16 | OUTPUT_VARIABLE rm_out 17 | RESULT_VARIABLE rm_retval 18 | ) 19 | if(NOT ${rm_retval} EQUAL 0) 20 | message(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"") 21 | endif (NOT ${rm_retval} EQUAL 0) 22 | else (EXISTS "$ENV{DESTDIR}${file}") 23 | message(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.") 24 | endif (EXISTS "$ENV{DESTDIR}${file}") 25 | endforeach(file) 26 | 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | /* 2 | * copyright: 2014-2018 3 | * name : Francis Banyikwa 4 | * email: mhogomchungu@gmail.com 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in 14 | * the documentation and/or other materials provided with the 15 | * distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 20 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 21 | * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 22 | * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 23 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 25 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 27 | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 | * SUCH DAMAGE. 29 | */ 30 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * copyright: 2014 3 | * name : Francis Banyikwa 4 | * email: mhogomchungu@gmail.com 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in 14 | * the documentation and/or other materials provided with the 15 | * distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 20 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 21 | * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 22 | * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 23 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 25 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 27 | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 | * SUCH DAMAGE. 29 | */ 30 | 31 | #include 32 | #include "example.h" 33 | 34 | int main( int argc,char * argv[] ) 35 | { 36 | QCoreApplication app( argc,argv ) ; 37 | 38 | example e ; 39 | e.start() ; 40 | 41 | return app.exec() ; 42 | } 43 | -------------------------------------------------------------------------------- /example.h: -------------------------------------------------------------------------------- 1 | /* 2 | * copyright: 2014-2015 3 | * name : Francis Banyikwa 4 | * email: mhogomchungu@gmail.com 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in 14 | * the documentation and/or other materials provided with the 15 | * distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 20 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 21 | * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 22 | * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 23 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 25 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 27 | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 | * SUCH DAMAGE. 29 | */ 30 | 31 | #ifndef __EXAMPLE_H_INCLUDED__ 32 | #define __EXAMPLE_H_INCLUDED__ 33 | 34 | #include 35 | 36 | class example : public QObject 37 | { 38 | Q_OBJECT 39 | public: 40 | void start() ; 41 | private slots: 42 | void run() ; 43 | }; 44 | 45 | #endif //__EXAMPLE_H_INCLUDED__ 46 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required( VERSION 3.0 ) 2 | 3 | project( tasks LANGUAGES CXX ) 4 | 5 | set( LIB_VERSION "1.2.6" ) 6 | set( SO_VERSION "2.0.0" ) 7 | 8 | if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") 9 | 10 | else() 11 | add_definitions(-Wall -Wextra -pedantic) 12 | endif() 13 | 14 | if( CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 10.0.0) 15 | set( CMAKE_CXX_STANDARD 20 ) 16 | MESSAGE( STATUS "Setting C++ version to C++20" ) 17 | else() 18 | set( CMAKE_CXX_STANDARD 14 ) 19 | MESSAGE( STATUS "Setting C++ version to C++14" ) 20 | endif() 21 | 22 | set( CMAKE_CXX_STANDARD_REQUIRED ON ) 23 | set( CMAKE_CXX_EXTENSIONS OFF) 24 | 25 | INCLUDE( GNUInstallDirs ) 26 | 27 | INCLUDE( CMakeDependentOption ) 28 | 29 | find_package( Qt5Core REQUIRED ) 30 | 31 | QT5_WRAP_CPP( MOC_LIBRARY task.hpp ) 32 | 33 | include_directories( ${Qt5Core_INCLUDE_DIRS} ) 34 | 35 | if( MCHUNGU_TASK_INSTALL ) 36 | 37 | add_library( mhogomchungu_task SHARED ${MOC_LIBRARY} ) 38 | set_target_properties(mhogomchungu_task PROPERTIES SOVERSION "${SO_VERSION}") 39 | else() 40 | add_library( mhogomchungu_task STATIC ${MOC_LIBRARY} ) 41 | endif() 42 | 43 | target_link_libraries( mhogomchungu_task ${Qt5Core_LIBRARIES} ) 44 | 45 | if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") 46 | 47 | else() 48 | if( WIN32 ) 49 | set_target_properties( mhogomchungu_task PROPERTIES COMPILE_FLAGS "-Wextra -Wall -s -pedantic " ) 50 | else() 51 | set_target_properties( mhogomchungu_task PROPERTIES COMPILE_FLAGS "-Wextra -Wall -s -fPIC -pedantic " ) 52 | endif() 53 | endif() 54 | 55 | if( DEBUG ) 56 | QT5_WRAP_CPP( MOC_EXE example.h ) 57 | add_executable( example example.cpp main.cpp ${MOC_EXE} ) 58 | target_link_libraries( example mhogomchungu_task ) 59 | endif() 60 | 61 | file( WRITE ${PROJECT_BINARY_DIR}/mhogomchungu_task.pc 62 | "prefix=${CMAKE_INSTALL_PREFIX} 63 | libdir=${CMAKE_INSTALL_FULL_LIBDIR} 64 | includedir=${CMAKE_INSTALL_FULL_INCLUDEDIR}/mhogomchungu 65 | 66 | Name: mhogomchungu_task 67 | Description: A Qt5 library that offers task based programming using modern C++ 68 | Version: ${LIB_VERSION} 69 | Libs: -L${CMAKE_INSTALL_FULL_LIBDIR} -l${Qt5Core_LIBRARIES} 70 | Cflags: -I${CMAKE_INSTALL_FULL_INCLUDEDIR}/mhogomchungu 71 | \n") 72 | 73 | if( MCHUNGU_TASK_INSTALL ) 74 | install( FILES ${PROJECT_BINARY_DIR}/mhogomchungu_task.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig/ PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ ) 75 | install( FILES task.hpp DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/mhogomchungu ) 76 | install( TARGETS mhogomchungu_task LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ) 77 | 78 | 79 | # uninstall target 80 | configure_file( 81 | "${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" 82 | "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" 83 | IMMEDIATE @ONLY ) 84 | 85 | add_custom_target( uninstall COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake ) 86 | endif() 87 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | Asynchronous programming in Qt/C++ using tasks and continuations. 4 | ======== 5 | 6 | The project seeks to do async based programming in Qt/C++ using modern C++. 7 | 8 | This library wraps a function into a future where the result of the wrapped function 9 | can be retrieved through the future's below public methods: 10 | 11 | 1. .get(). This method runs the wrapped function on the current thread 12 | and could block the thread and hang GUI. This API is useful when you are already 13 | in a background thread. 14 | 15 | 2. .then(). This method does three things: 16 | 17 | 1. Registers a method to be called when a wrapped function finish running. 18 | 19 | 2. Runs the wrapped function on a background thread. 20 | 21 | 3. Runs the registered method on the current thread when the wrapped 22 | function finish running. 23 | 24 | 3. .await(). This method does three things: 25 | 26 | 1. Suspends the current thread at a point where this method is called. 27 | 28 | 2. Creates a background thread and then runs the wrapped function in the 29 | background thread. 30 | 31 | 3. Unsuspends the current thread when the wrapped function finish and let 32 | the current thread continue normally. 33 | 34 | The suspension at step 1 is done without blocking the thread and hence the 35 | suspension can be done in the GUI thread and the GUI will remain responsive. 36 | 37 | 4. .queue(). This method runs tasks in a future sequentially and a passed in function will be called when all tasks 38 | finish running. This method behaves like ```.then( [](){} )``` if the future is managing only one task. 39 | 40 | 5. .cancel(). This method can be used to cancel a future. It is important to know 41 | that this method does not terminate a running thread that is powering a future, it just 42 | releases memory used by a future and this method should be used if a future is to be discarded 43 | after it it is acquired but never used. To terminate a thread,call .all_threads() method,locate a QThread 44 | instance you want to terminate and call .terminate() method on the instance. 45 | 46 | 6. .all_threads(). This method returns a vector of QThreads that are powering futures. 47 | The vector will contain a single entry if this future powers its own task. If this future 48 | manages other futures,then the returned vector will contain QThread pointers that are in 49 | the same order as tasks/futures passed to Task::run(). 50 | 51 | 7. .start(). This method is to be used if a future is to be run without caring about its result. 52 | Use this API if you want a future to run but dont want to use any of the above mentioned methods. 53 | 54 | 8. .manages_multiple_futures(). This method can be used to check if a future powers 55 | its own task or manages other futures. 56 | 57 | 9. .when_all(). This method is an alias to .then(). The registered continuation is called when all managed tasks are done 58 | and managed tasks run concurrently. 59 | 60 | 10. .when_seq(). This method is an alias to .queue(). The registered continuation is called when all managed task are done 61 | and managed tasks run sequentially. 62 | 63 | 11. .when_any(). This method runs the registered continuation when the first task among managed tasks finish running. 64 | Managed tasks run concurrently. 65 | 66 | Examples of using a future. 67 | ======== 68 | 69 | **1. Example use of .get() method of a future.** 70 | 71 | ```c++ 72 | 73 | Task::future& foo = bar() ; 74 | 75 | int r = foo.get() ; 76 | 77 | ``` 78 | 79 | **2. Example use of .await() method of a future.** 80 | 81 | ```c++ 82 | 83 | Task::future& foo = bar() ; 84 | 85 | int r = foo.await() ; 86 | 87 | ``` 88 | **3. Example use of an alternative .await() method of a future.** 89 | 90 | ```c++ 91 | 92 | int foo() ; //function prototype 93 | 94 | int r = Task::await( foo ) ; 95 | 96 | ``` 97 | 98 | **4. Example use of .then() method of a future.** 99 | 100 | ```c++ 101 | 102 | void meaw( int ) ; //function prototype 103 | 104 | Task::future& foo = bar() ; 105 | 106 | foo.then( meaw ) ; 107 | 108 | ``` 109 | 110 | **5. Example use of .queue() method of a future.** 111 | 112 | ```c++ 113 | 114 | void meaw() ; //function prototype 115 | 116 | Task::future& foo = bar() ; 117 | 118 | foo.queue( meaw ) ; 119 | 120 | ``` 121 | 122 | Examples of creating a future. 123 | ======== 124 | 125 | **1. Creating a future that has no result.** 126 | ```c++ 127 | 128 | void bar() ; //function prototype 129 | 130 | Task::future& foo = Task::run( bar ) ; 131 | 132 | ``` 133 | 134 | **2. Creating a future that has result.** 135 | ```c++ 136 | 137 | int foo() ; //function prototype 138 | 139 | Task::future& foo = Task::run( foo ) ; 140 | 141 | ``` 142 | 143 | **3. Creating a future that combines arbitrary number of functions. .get() and .queue() on the future will cause passed in functions to run sequentially and in the order they are specified. .await() and .then() will cause passed in functions to run concurrently.** 144 | 145 | ```c++ 146 | 147 | void foo() ; //function prototype 148 | void bar() ; //function prototype 149 | void woof() ; //function prototype 150 | 151 | Task::future& foo = Task::run_tasks( foo,bar,woof ) ; 152 | 153 | ``` 154 | 155 | **4. Creating a future that combines arbitrary number of tasks and their continuations that take no argument. .get() and .queue() on the future will cause passed in functions to run sequentially and in the order they are specified. .await() and .then() will cause passed in functions to run concurrently.** 156 | 157 | ```c++ 158 | 159 | void foo() ; //function prototype 160 | void bar() ; //function prototype 161 | 162 | void cfoo() ; //continuation function prototype 163 | void cbar() ; //continuation function prototype 164 | 165 | Task::future& e = Task::run( Task::make_pair( foo,cfoo ),Task::make_pair( bar,cbar ) ) ; 166 | 167 | ``` 168 | 169 | **5. Creating a future that combines arbitrary number of tasks and their continuations that takes an argument. .get() and .queue() on the future will cause passed in pairs to run sequentially and in the order they are specified. .await() and .then() will cause passed in pairs to run concurrently. The result of the future is undefined and a function that takes no argument should be used if .await() method of the future is called.** 170 | 171 | ```c++ 172 | 173 | int foo() ; //function prototype 174 | int bar() ; //function prototype 175 | 176 | void cfoo( int ) ; //continuation function prototype 177 | void cbar( int ) ; //continuation function prototype 178 | 179 | Task::future& e = Task::run( Task::make_pair( foo,cfoo ),Task::make_pair( bar,cbar ) ) ; 180 | ``` 181 | **6. Creating a future that takes two lambdas and run the first one in a background thread and the second one in a user specified thread and manage progress report from the first lambda to the second.** 182 | 183 | ```c++ 184 | 185 | auto run_main = []( QVariant progress ){ 186 | 187 | std::cout << progress.value() ; 188 | } ; 189 | 190 | auto run_bg = []( const Task::progress& progress ){ 191 | 192 | for( int i = 0 ; i < 5 ;i++ ){ 193 | progress.update( i ) ; 194 | QThread::currentThread()->sleep(1); //Simulate long running process 195 | } 196 | } ; 197 | 198 | /* 199 | * This method takes 3 arguments: 200 | * The first argument is a pointer to QObject. 201 | * The second argument is a lambda that will run in a background thread. 202 | * The third argument is a lambda that will run in a thread that owns the object 203 | * pass in as the first argument. 204 | * 205 | * Everytime Task::progress::update() is called by the first lambda, the second 206 | * lambda will be called with the object that was 207 | * passed to the first lambda. 208 | */ 209 | Task::future& e = Task::run( this,run_bg,run_main ) ; 210 | ``` 211 | 212 | Further documentation of how to use the library is here[1]. 213 | 214 | [1] https://github.com/mhogomchungu/tasks/blob/master/example.cpp 215 | -------------------------------------------------------------------------------- /example.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * copyright: 2014-2017 3 | * name : Francis Banyikwa 4 | * email: mhogomchungu@gmail.com 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in 14 | * the documentation and/or other materials provided with the 15 | * distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 20 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 21 | * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 22 | * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 23 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 25 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 27 | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 | * SUCH DAMAGE. 29 | */ 30 | 31 | #include "task.hpp" 32 | #include "example.h" 33 | 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | static void _testing_task_await() ; 40 | static void _testing_task_future_all() ; 41 | static void _testing_multiple_tasks() ; 42 | static void _testing_multiple_tasks_with_start() ; 43 | static void _testing_queue_with_no_results() ; 44 | static void _testing_queue_with_results() ; 45 | static void _testing_checking_multiple_futures() ; 46 | 47 | template< typename T > 48 | static void _print( const T& e ) 49 | { 50 | std::cout << e << std::endl ; 51 | } 52 | 53 | struct wait{ 54 | 55 | void task_finished( const char * s ) 56 | { 57 | QMutexLocker m( &mutex ) ; 58 | 59 | counter++ ; 60 | 61 | std::cout << s << std::endl ; 62 | 63 | if( counter == max ){ 64 | 65 | loop.exit() ; 66 | } 67 | } 68 | int max = 3 ; 69 | int counter = 0 ; 70 | QMutex mutex ; 71 | QEventLoop loop ; 72 | }; 73 | 74 | void example::start() 75 | { 76 | QMetaObject::invokeMethod( this,"run",Qt::QueuedConnection ) ; 77 | } 78 | 79 | QString _longRunningTask() 80 | { 81 | return "abc" ; 82 | } 83 | 84 | static void _printThreadID() 85 | { 86 | std::cout << "Thread id: " << QThread::currentThreadId() << std::endl ; 87 | } 88 | 89 | static void _useResult( const QString& e ) 90 | { 91 | Q_UNUSED( e ) 92 | } 93 | 94 | /* 95 | * A sample use where a task is run on separate thread and return a value and the returned 96 | * value is used on another task run on the original thread 97 | */ 98 | static void _test_run_then() 99 | { 100 | std::cout<< "Testing Task::run().then()" << std::endl ; 101 | 102 | /* 103 | * print the thread id to know we are on what thread. 104 | * We are on the original thread here. 105 | */ 106 | _printThreadID() ; 107 | 108 | Task::run( [](){ 109 | 110 | /* 111 | * print the thread id to know we are on what thread. 112 | * We are on a separate thread here 113 | */ 114 | _printThreadID() ; 115 | 116 | /* 117 | * Do a time consuming process on a separate thread and return its result 118 | */ 119 | return _longRunningTask() ; 120 | 121 | } ).then( []( QString r ){ 122 | 123 | /* 124 | * use the returned value by the previous task 125 | */ 126 | _useResult( r ) ; 127 | /* 128 | * print the thread id to know we are on what thread. 129 | * We are back on the original thread and we will get here as a continuation of the task 130 | * that completed above 131 | */ 132 | _printThreadID() ; 133 | 134 | /* 135 | * moving on to the next test. 136 | */ 137 | _testing_task_await() ; 138 | } ) ; 139 | } 140 | 141 | /* 142 | * Task::await() function below does the following: 143 | * 1. suspends the "_testing_task_await" method at a point where Task::await() method is called. 144 | * 2. creates a new thread. 145 | * 3. runs the _longRunningTask method in the new thread. 146 | * 4. store the result of _longRunningTask function in r. 147 | * 5. resumes "_testing_task_await" method. 148 | */ 149 | static void _testing_task_await() 150 | { 151 | _print( "Testing Task::await()" ) ; 152 | 153 | QString e = Task::await( _longRunningTask ) ; 154 | 155 | _print( e.toLatin1().constData() ) ; 156 | 157 | /* 158 | * moving on to the next test. 159 | */ 160 | _testing_task_future_all() ; 161 | } 162 | 163 | /* 164 | * Task::run() function below does the following: 165 | * 1. Collects a bunch of tasks. 166 | * 2. Runs each task on its own thread. 167 | * 3. Returns a future that holds all above tasks. 168 | * 4. .await() can be called on the future to suspend the current thread at a point 169 | * where this medhod is called to wait for all tasks to finish. 170 | * 5. .then() can be called on the future to register an event to be called when all 171 | * tasks finish running. 172 | */ 173 | static void _testing_task_future_all() 174 | { 175 | auto fn1 = [](){ _printThreadID() ; } ; 176 | auto fn2 = [](){ _printThreadID() ; } ; 177 | auto fn3 = [](){ _printThreadID() ; } ; 178 | 179 | _print( "Testing Task::run().await() multiple tasks" ) ; 180 | 181 | Task::future& e = Task::run_tasks( fn1,fn2,fn3 ) ; 182 | 183 | e.await() ; 184 | 185 | _print( "Testing Task::run().then() multiple tasks" ) ; 186 | 187 | Task::future& f1 = Task::run( fn1 ) ; 188 | Task::future& f2 = Task::run( fn2 ) ; 189 | Task::future& f3 = Task::run( fn3 ) ; 190 | 191 | Task::future& s = Task::run_tasks( f1,f2,f3 ) ; 192 | 193 | s.then( [](){ 194 | 195 | /* 196 | * moving on to the next test. 197 | */ 198 | _testing_multiple_tasks() ; 199 | } ) ; 200 | } 201 | 202 | /* 203 | * Task::run() function below does the following: 204 | * 1. Collects a bunch of tasks and their continuations. 205 | * 2. Runs each task on its own thread. 206 | * 3. On completion of each task,run its continuation on the current thread. 207 | * 4. Returns a future that holds all above tasks. 208 | * 5. .await() can be called on the future to suspend the current thread at a point 209 | * where this medhod is called to wait for all tasks and their continuations to finish. 210 | * 6. .then() can be called on the future to register an event to be called when all 211 | * tasks and their continuations finish. 212 | */ 213 | static void _testing_multiple_tasks_non_movable() 214 | { 215 | _print( "Testing multiple tasks without continuation arguments" ) ; 216 | 217 | auto fna1 = [ a = std::unique_ptr() ](){ _printThreadID(); } ; 218 | auto fna2 = [ a = std::unique_ptr() ](){ _printThreadID(); } ; 219 | auto fna3 = [ a = std::unique_ptr() ](){ _printThreadID(); } ; 220 | 221 | auto ra1 = [ a = std::unique_ptr() ](){ _print( "r1" ) ; } ; 222 | auto ra2 = [ a = std::unique_ptr() ](){ _print( "r2" ) ; } ; 223 | auto ra3 = [ a = std::unique_ptr() ](){ _print( "r3" ) ; } ; 224 | 225 | Task::future& e = Task::run( Task::make_pair( std::move( fna1 ),std::move( ra1 ) ), 226 | Task::make_pair( std::move( fna2 ),std::move( ra2 ) ), 227 | Task::make_pair( std::move( fna3 ),std::move( ra3 ) ) ) ; 228 | 229 | e.await() ; 230 | 231 | _print( "Testing multiple tasks with continuation arguments" ) ; 232 | 233 | auto fn1 = [ a = std::unique_ptr() ](){ _printThreadID() ; return 0 ; } ; 234 | auto fn2 = [ a = std::unique_ptr() ](){ _printThreadID() ; return 0 ; } ; 235 | auto fn3 = [ a = std::unique_ptr() ](){ _printThreadID() ; return 0 ; } ; 236 | 237 | auto r1 = [ a = std::unique_ptr() ]( int ){ _print( "r1" ) ; } ; 238 | auto r2 = [ a = std::unique_ptr() ]( int ){ _print( "r2" ) ; } ; 239 | auto r3 = [ a = std::unique_ptr() ]( int ){ _print( "r3" ) ; } ; 240 | 241 | Task::future& s = Task::run( Task::make_pair( std::move( fn1 ),std::move( r1 ) ), 242 | Task::make_pair( std::move( fn2 ),std::move( r2 ) ), 243 | Task::make_pair( std::move( fn3 ),std::move( r3 ) ) ) ; 244 | 245 | s.then( _testing_multiple_tasks_with_start ) ; 246 | } 247 | 248 | static void _testing_multiple_tasks() 249 | { 250 | _print( "Testing multiple tasks without continuation arguments" ) ; 251 | 252 | auto fna1 = [](){ _printThreadID(); } ; 253 | auto fna2 = [](){ _printThreadID(); } ; 254 | auto fna3 = [](){ _printThreadID(); } ; 255 | 256 | auto ra1 = [](){ _print( "r1" ) ; } ; 257 | auto ra2 = [](){ _print( "r2" ) ; } ; 258 | auto ra3 = [](){ _print( "r3" ) ; } ; 259 | 260 | Task::future& e = Task::run( Task::make_pair( fna1,ra1 ), 261 | Task::make_pair( fna2,ra2 ), 262 | Task::make_pair( fna3,ra3 ) ) ; 263 | 264 | e.await() ; 265 | 266 | _print( "Testing multiple tasks with continuation arguments" ) ; 267 | 268 | auto fn1 = [](){ _printThreadID() ; return 0 ; } ; 269 | auto fn2 = [](){ _printThreadID() ; return 0 ; } ; 270 | auto fn3 = [](){ _printThreadID() ; return 0 ; } ; 271 | 272 | auto r1 = []( int ){ _print( "r1" ) ; } ; 273 | auto r2 = []( int ){ _print( "r2" ) ; } ; 274 | auto r3 = []( int ){ _print( "r3" ) ; } ; 275 | 276 | Task::future& s = Task::run( Task::make_pair( fn1,r1 ), 277 | Task::make_pair( fn2,r2 ), 278 | Task::make_pair( fn3,r3 ) ) ; 279 | 280 | s.then( _testing_multiple_tasks_non_movable ) ; 281 | } 282 | 283 | static void _testing_multiple_tasks_with_start() 284 | { 285 | std::cout<< "Testing multiple tasks with continuation arguments using start" << std::endl ; 286 | 287 | wait w ; 288 | 289 | auto fn1 = [](){ _printThreadID() ; return 0 ; } ; 290 | auto fn2 = [](){ _printThreadID() ; return 0 ; } ; 291 | auto fn3 = [](){ _printThreadID() ; return 0 ; } ; 292 | 293 | auto r1 = [ & ]( int ){ w.task_finished( "r1" ) ; } ; 294 | auto r2 = [ & ]( int ){ w.task_finished( "r2" ) ; } ; 295 | auto r3 = [ & ]( int ){ w.task_finished( "r3" ) ; } ; 296 | 297 | Task::future& s = Task::run( Task::make_pair( fn1,r1 ), 298 | Task::make_pair( fn2,r2 ), 299 | Task::make_pair( fn3,r3 ) ) ; 300 | 301 | s.start() ; 302 | 303 | w.loop.exec() ; 304 | 305 | QCoreApplication::quit() ; 306 | } 307 | 308 | static void _testing_queue_with_no_results() 309 | { 310 | std::cout<< "Testing queue with no result" << std::endl ; 311 | 312 | auto fna1 = [](){ _printThreadID(); } ; 313 | auto fna2 = [](){ _printThreadID(); } ; 314 | auto fna3 = [](){ _printThreadID(); } ; 315 | 316 | auto ra1 = [](){ _print( "r1" ) ; } ; 317 | auto ra2 = [](){ _print( "r2" ) ; } ; 318 | auto ra3 = [](){ _print( "r3" ) ; } ; 319 | 320 | Task::future& e = Task::run( Task::make_pair( fna1,ra1 ), 321 | Task::make_pair( fna2,ra2 ), 322 | Task::make_pair( fna3,ra3 ) ) ; 323 | 324 | e.queue( _testing_queue_with_results ) ; 325 | } 326 | 327 | static void _testing_queue_with_results() 328 | { 329 | std::cout<< "Testing queue with result" << std::endl ; 330 | 331 | auto fn1 = [](){ _printThreadID() ; return 0 ; } ; 332 | auto fn2 = [](){ _printThreadID() ; return 0 ; } ; 333 | auto fn3 = [](){ _printThreadID() ; return 0 ; } ; 334 | 335 | auto r1 = [ = ]( int ){ _print( "r1" ) ; } ; 336 | auto r2 = [ = ]( int ){ _print( "r2" ) ; } ; 337 | auto r3 = [ = ]( int ){ _print( "r3" ) ; } ; 338 | 339 | Task::future& s = Task::run( Task::make_pair( fn1,r1 ), 340 | Task::make_pair( fn2,r2 ), 341 | Task::make_pair( fn3,r3 ) ) ; 342 | s.queue( _test_run_then ) ; 343 | } 344 | 345 | static void _testing_checking_multiple_futures() 346 | { 347 | auto fn1 = [](){} ; 348 | auto fn2 = [](){} ; 349 | auto fn3 = [](){} ; 350 | 351 | _print( "Testing finding out if a future manages multiple futures" ) ; 352 | 353 | Task::future& e = Task::run_tasks( fn1,fn2,fn3 ) ; 354 | 355 | const auto& z = e.all_threads() ; 356 | 357 | std::string s = e.manages_multiple_futures() ? "true" : "false" ; 358 | 359 | _print( "A future managed multiple futures: " + s ) ; 360 | _print( "Number of future managed: " + QString::number( z.size() ).toStdString() ) ; 361 | } 362 | 363 | static void _test_when_any1() 364 | { 365 | _print( "Testing when_any" ) ; 366 | 367 | wait w ; 368 | 369 | auto ll1 = [ & ](){ 370 | 371 | QThread::currentThread()->sleep( 5 ) ; 372 | w.task_finished( "aaa" ) ; 373 | } ; 374 | 375 | auto ll2 = [ & ](){ 376 | 377 | QThread::currentThread()->sleep( 2 ) ; 378 | w.task_finished( "bbb" ) ; 379 | } ; 380 | 381 | auto ll3 = [ & ](){ 382 | 383 | QThread::currentThread()->sleep( 3 ) ; 384 | w.task_finished( "ccc" ) ; 385 | } ; 386 | 387 | Task::run_tasks( ll1,ll2,ll3 ).when_any( [](){ 388 | 389 | _print( "when_any called" ) ; 390 | } ) ; 391 | 392 | w.loop.exec() ; 393 | 394 | _print( "Done testing when_any" ) ; 395 | } 396 | 397 | static void _test_when_any2() 398 | { 399 | _print( "Testing when_any with result" ) ; 400 | 401 | wait w ; 402 | 403 | auto fn1 = [ & ](){ 404 | 405 | QThread::currentThread()->sleep( 5 ) ; 406 | w.task_finished( "aaa" ) ; 407 | return 0 ; 408 | } ; 409 | 410 | auto fn2 = [ & ](){ 411 | 412 | QThread::currentThread()->sleep( 2 ) ; 413 | w.task_finished( "bbb" ) ; 414 | return 0 ; 415 | } ; 416 | 417 | auto fn3 = [ & ](){ 418 | 419 | QThread::currentThread()->sleep( 3 ) ; 420 | w.task_finished( "ccc" ) ; 421 | return 0 ; 422 | } ; 423 | 424 | auto ff = [](int){}; 425 | 426 | Task::future& s = Task::run( Task::make_pair( fn1,ff ), 427 | Task::make_pair( fn2,ff ), 428 | Task::make_pair( fn3,ff ) ) ; 429 | 430 | s.when_any( [](){ 431 | 432 | _print( "when_any called" ) ; 433 | } ) ; 434 | 435 | w.loop.exec() ; 436 | 437 | _print( "Done testing when_any with result" ) ; 438 | } 439 | 440 | static void _test_move_only_callables() 441 | { 442 | Task::run( [ a = std::unique_ptr() ]( int x ){ return x ; },4 ).get() ; 443 | Task::run( [ a = std::unique_ptr() ](int){},4 ).get() ; 444 | 445 | Task::run( [ a = std::unique_ptr() ](){ return 6 ; } ).get() ; 446 | Task::run( [ a = std::unique_ptr() ](){} ).get() ; 447 | 448 | Task::await( [ a = std::unique_ptr() ](){ return 6 ; } ) ; 449 | 450 | auto& tt = Task::run( [ a = std::unique_ptr() ](){ return 6 ; } ) ; 451 | 452 | Task::await( tt ) ; 453 | Task::await( Task::run( [ a = std::unique_ptr() ](){} ) ) ; 454 | 455 | Task::await( [ a = std::unique_ptr() ]( int x ){ return x ; },4 ) ; 456 | 457 | Task::await( [ a = std::unique_ptr() ](int){},4 ) ; 458 | 459 | Task::await( [ a = std::unique_ptr() ](){ return 6 ; } ) ; 460 | 461 | Task::await( [ a = std::unique_ptr() ](){} ) ; 462 | 463 | auto& zz = Task::run( [ a = std::unique_ptr() ](){ return 6 ; } ) ; 464 | 465 | Task::await( zz ) ; 466 | Task::await( Task::run( [ a = std::unique_ptr() ](){} ) ) ; 467 | 468 | Task::exec( [ a = std::unique_ptr() ]( int x ){ return x ; },4 ) ; 469 | Task::exec( [ a = std::unique_ptr() ](int){},4 ) ; 470 | 471 | Task::exec( [ a = std::unique_ptr() ](){ return 6 ; } ) ; 472 | Task::exec( [ a = std::unique_ptr() ](){} ) ; 473 | 474 | Task::run_tasks( [ a = std::unique_ptr() ](){ return 6 ; }, 475 | [ a = std::unique_ptr() ](){ return 6 ; }, 476 | [ a = std::unique_ptr() ](){ return 6 ; }, 477 | [ a = std::unique_ptr() ](){ return 6 ; } ).get() ; 478 | 479 | Task::run_tasks( [](){ return 6 ; }, 480 | [](){ return 6 ; }, 481 | [](){ return 6 ; }, 482 | [](){ return 6 ; } ).get() ; 483 | 484 | Task::run_tasks( Task::run( [ a = std::unique_ptr() ](){} ), 485 | Task::run( [ a = std::unique_ptr() ](){} ), 486 | Task::run( [ a = std::unique_ptr() ](){} ) ).get() ; 487 | } 488 | 489 | static void _test_copyable_callables() 490 | { 491 | auto aa = []( int x ){ return x ; } ; 492 | 493 | auto bb = [](){ return 6 ; } ; 494 | 495 | auto cc = [](){} ; 496 | 497 | auto dd = [](int){} ; 498 | 499 | Task::run( aa,4 ).get() ; 500 | Task::run( dd,4 ).get() ; 501 | 502 | Task::run( bb ).get() ; 503 | Task::run( cc ).get() ; 504 | 505 | Task::await( bb ) ; 506 | 507 | auto& tt = Task::run( bb ) ; 508 | 509 | Task::await( tt ) ; 510 | Task::await( Task::run( cc ) ) ; 511 | 512 | Task::await( aa,4 ) ; 513 | 514 | Task::await( dd,4 ) ; 515 | 516 | Task::await( bb ) ; 517 | 518 | Task::await( cc ) ; 519 | 520 | auto& zz = Task::run( bb ) ; 521 | 522 | Task::await( zz ) ; 523 | Task::await( Task::run( cc ) ) ; 524 | 525 | Task::exec( aa,4 ) ; 526 | Task::exec( dd,4 ) ; 527 | 528 | Task::exec( bb ) ; 529 | Task::exec( cc ) ; 530 | 531 | Task::run_tasks( bb,bb,cc ).get() ; 532 | 533 | Task::run_tasks( Task::run( cc ),Task::run( cc ) ).get() ; 534 | } 535 | 536 | struct foo 537 | { 538 | foo() 539 | { 540 | } 541 | foo(int a) 542 | { 543 | _print(a); 544 | } 545 | std::unique_ptr m ; 546 | }; 547 | 548 | struct www 549 | { 550 | void operator()() 551 | { 552 | 553 | } 554 | www( const www& ) 555 | { 556 | _print( "const www&" ) ; 557 | } 558 | www( www&& ) 559 | { 560 | _print( "www&&" ) ; 561 | } 562 | www() 563 | { 564 | _print( "www()" ) ; 565 | } 566 | www& operator=( const www& ) 567 | { 568 | _print( "www& operator=( const www& )" ) ; 569 | 570 | return *this ; 571 | } 572 | www& operator=( www&& ) 573 | { 574 | _print( "www& operator=( www&& )" ) ; 575 | 576 | return *this ; 577 | } 578 | 579 | std::unique_ptr m ; 580 | }; 581 | 582 | void example::run() 583 | { 584 | #if __cplusplus >= 201703L 585 | 586 | Task::await([](foo,foo,foo){},foo(7),foo(7),foo(7)); 587 | #endif 588 | _test_copyable_callables() ; 589 | 590 | _test_move_only_callables() ; 591 | 592 | _test_when_any1() ; 593 | 594 | _test_when_any2() ; 595 | 596 | auto run_main = []( QVariant x ){ 597 | 598 | std::cout << x.value() << ": mm Thread id: " << QThread::currentThreadId() << std::endl ; 599 | } ; 600 | 601 | auto run_bg = []( const Task::progress& pp ){ 602 | 603 | for( int i = 0 ; i < 2 ;i++ ){ 604 | std::cout << i << ": bg Thread id: " << QThread::currentThreadId() << std::endl ; 605 | pp.update( i ) ; 606 | QThread::currentThread()->sleep(1); 607 | } 608 | } ; 609 | 610 | std::cout << "main Thread: " << QThread::currentThreadId() << std::endl ; 611 | 612 | Task::future& abc = Task::run( this,run_bg,run_main ) ; 613 | 614 | abc.await() ; 615 | 616 | _testing_checking_multiple_futures() ; 617 | 618 | _testing_queue_with_no_results() ; 619 | } 620 | -------------------------------------------------------------------------------- /task.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * copyright: 2014-2020 3 | * name : Francis Banyikwa 4 | * email: mhogomchungu@gmail.com 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in 14 | * the documentation and/or other materials provided with the 15 | * distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 20 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 21 | * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 22 | * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 23 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 25 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 27 | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 | * SUCH DAMAGE. 29 | */ 30 | 31 | #ifndef __TASK_H_INCLUDED__ 32 | #define __TASK_H_INCLUDED__ 33 | 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | /* 45 | * 46 | * Examples on how to use the library are at the end of this file. 47 | * 48 | */ 49 | 50 | /* 51 | * This library wraps a function into a future where the result of the function 52 | * can be retrieved through the future's below public methods: 53 | * 54 | * 1. .get(). This method runs the wrapped function on the current thread 55 | * and could block the thread and hang GUI. 56 | * 57 | * 2. .then(). This method does three things: 58 | * 59 | * 1. Registers a method to be called when a wrapped function finish running. 60 | * 61 | * 2. Runs the wrapped function on a background thread. 62 | * 63 | * 3. Runs the registered method on the current thread when the wrapped function finish 64 | * running. 65 | * 66 | * 3. .await(). This method does three things: 67 | * 68 | * 1. Suspends the current thread at a point where this method is called. 69 | * 70 | * 2. Creates a background thread and then runs the wrapped function in the background 71 | * thread. 72 | * 73 | * 3. Unsuspends the current thread when the wrapped function finish and let the 74 | * current thread continue normally. 75 | * 76 | * The suspension at step 1 is done without blocking the thread and hence the suspension 77 | * can be done in the GUI thread and the GUI will remain responsive. 78 | * 79 | * 4. .queue(). This method runs tasks in a future sequentially and a passed in function will be called 80 | * when all tasks finish running. This method behaves like .then( [](){} ) if the future is 81 | * managing only one task. 82 | * 83 | * 5. .cancel(). This method can be used to cancel a future. It is important to know 84 | * that this method does not terminate a running thread that is powering a future, it just 85 | * releases memory used by a future and this method should be used if a future is to be discarded 86 | * after it it is acquired but never used. 87 | * 88 | * 6. .all_threads(). This method returns a vector of QThreads that are powering futures. 89 | * The vector will contain a single entry if this future powers its own task. If this future 90 | * manages other futures,then the returned vector will contain QThread pointers that are in 91 | * the same order as tasks/futures passed to Task::run(). 92 | * 93 | * 7. .start(). This method is to be used if a future is to be run without caring about 94 | * its result. Use this method if you want a future to run but dont want to use any of the above mentioned 95 | * methods. 96 | * 97 | * 8. .manages_multiple_futures(). This method can be used to check if a future powers 98 | * its own task or manages other futures. 99 | * 100 | * 101 | * The future is of type "Task::future&" and "std::reference_wrapper"[1] 102 | * class can be used if they are to be managed in a container that can not handle references. 103 | * 104 | * [1] http://en.cppreference.com/w/cpp/utility/functional/reference_wrapper 105 | */ 106 | 107 | namespace Task 108 | { 109 | template< typename T > 110 | class future; 111 | 112 | namespace detail 113 | { 114 | template< typename T > 115 | void add_void( Task::future< T >&,Task::future< T >&,std::function< T() >&& ) ; 116 | template< typename T > 117 | void add( Task::future< T >&,Task::future< T >&,std::function< void( T ) >&& ) ; 118 | 119 | template 120 | class functionWrapper 121 | { 122 | public: 123 | template< typename ... Args > 124 | auto operator()( Args&& ... args ) const 125 | { 126 | return (*m_function)( std::forward( args ) ... ) ; 127 | } 128 | functionWrapper( Function function ) : 129 | m_function( std::make_shared( Function( std::move( function ) ) ) ) 130 | { 131 | } 132 | private: 133 | std::shared_ptr m_function ; 134 | }; 135 | 136 | template 137 | functionWrapper function( Function function ) 138 | { 139 | return functionWrapper( std::move( function ) ) ; 140 | } 141 | 142 | #if __cplusplus >= 201703L 143 | template 144 | using result_of = std::invoke_result_t ; 145 | #else 146 | template 147 | using result_of = std::result_of_t ; 148 | #endif 149 | template 150 | using copyable = std::enable_if_t::value,int> ; 151 | 152 | template 153 | using not_copyable = std::enable_if_t::value,int> ; 154 | 155 | template 156 | using has_same_return_type = std::enable_if_t,ReturnType>::value,int> ; 157 | 158 | template 159 | using has_void_return_type = has_same_return_type ; 160 | 161 | template 162 | using has_bool_return_type = has_same_return_type ; 163 | 164 | template 165 | using has_non_void_return_type = std::enable_if_t>::value,int> ; 166 | 167 | template 168 | using has_argument = has_same_return_type,Function,Args...> ; 169 | 170 | template 171 | using has_no_argument = has_same_return_type,Function> ; 172 | 173 | template 174 | using returns_void = has_void_return_type ; 175 | 176 | template 177 | using returns_value = has_non_void_return_type ; 178 | } 179 | 180 | template< typename T > 181 | struct pair{ 182 | pair( std::function< T() > first,std::function< void( T ) > second ) : 183 | value( std::make_pair( std::move( first ),std::move( second ) ) ) 184 | { 185 | } 186 | std::pair< std::function< T() >,std::function< void( T ) > > value ; 187 | }; 188 | 189 | template<> 190 | struct pair{ 191 | pair( std::function< void() > first,std::function< void() > second ) : 192 | value( std::make_pair( std::move( first ),std::move( second ) ) ) 193 | { 194 | } 195 | std::pair< std::function< void() >,std::function< void() > > value ; 196 | }; 197 | template< typename E, 198 | typename F, 199 | Task::detail::not_copyable = 0, 200 | Task::detail::not_copyable = 0 > 201 | pair> make_pair( E e,F f ) 202 | { 203 | using type = Task::detail::result_of ; 204 | return pair( Task::detail::function( std::move( e ) ),Task::detail::function( std::move( f ) ) ) ; 205 | } 206 | template< typename E, 207 | typename F, 208 | Task::detail::copyable = 0, 209 | Task::detail::copyable = 0 > 210 | pair> make_pair( E e,F f ) 211 | { 212 | return pair>( std::move( e ),std::move( f ) ) ; 213 | } 214 | template< typename E, 215 | typename F, 216 | Task::detail::not_copyable = 0, 217 | Task::detail::copyable = 0 > 218 | pair> make_pair( E e,F f ) 219 | { 220 | using type = Task::detail::result_of ; 221 | return pair( Task::detail::function( std::move( e ) ),std::move( f ) ) ; 222 | } 223 | template< typename E, 224 | typename F, 225 | Task::detail::copyable = 0, 226 | Task::detail::not_copyable = 0 > 227 | pair> make_pair( E e,F f ) 228 | { 229 | using type = Task::detail::result_of ; 230 | return pair( std::move( e ),Task::detail::function( std::move( f ) ) ) ; 231 | } 232 | template< typename T > 233 | class future : private QObject 234 | { 235 | public: 236 | /* 237 | * Use this API if you care about the result 238 | */ 239 | //std::function< void( T ) > 240 | template = 0, 242 | Task::detail::copyable = 0> 243 | void then( Function function ) 244 | { 245 | m_function = std::move( function ) ; 246 | this->start() ; 247 | } 248 | template = 0, 250 | Task::detail::not_copyable = 0> 251 | void then( Function function ) 252 | { 253 | m_function = Task::detail::function( std::move( function ) ) ; 254 | this->start() ; 255 | } 256 | /* 257 | * Use this API if you DO NOT care about the result 258 | */ 259 | //std::function< void( void ) > 260 | template = 0, 262 | Task::detail::copyable = 0> 263 | void then( Function function ) 264 | { 265 | m_function_1 = std::move( function ) ; 266 | this->start() ; 267 | } 268 | template = 0, 270 | Task::detail::not_copyable = 0> 271 | void then( Function function ) 272 | { 273 | m_function_1 = Task::detail::function( std::move( function ) ) ; 274 | this->start() ; 275 | } 276 | template = 0, 278 | Task::detail::not_copyable = 0> 279 | void queue( Function function ) 280 | { 281 | if( this->manages_multiple_futures() ){ 282 | 283 | m_function_1 = Task::detail::function( std::move( function ) ) ; 284 | 285 | this->_queue() ; 286 | }else{ 287 | this->then( Task::detail::function( std::move( function ) ) ) ; 288 | } 289 | } 290 | template = 0, 292 | Task::detail::copyable = 0> 293 | void queue( Function function ) 294 | { 295 | if( this->manages_multiple_futures() ){ 296 | 297 | m_function_1 = std::move( function ) ; 298 | 299 | this->_queue() ; 300 | }else{ 301 | this->then( std::move( function ) ) ; 302 | } 303 | } 304 | 305 | void queue() 306 | { 307 | if( this->manages_multiple_futures() ){ 308 | 309 | m_function_1 = [](){} ; 310 | 311 | this->_queue() ; 312 | }else{ 313 | this->then( [](){} ) ; 314 | } 315 | } 316 | /* 317 | * Below two API just exposes existing functionality using more standard names 318 | */ 319 | template 320 | void when_all( Function function ) 321 | { 322 | this->then( std::move( function ) ) ; 323 | } 324 | template 325 | void when_seq( Function function ) 326 | { 327 | this->queue( std::move( function ) ) ; 328 | } 329 | template = 0, 331 | Task::detail::copyable = 0> 332 | void when_any( Function function ) 333 | { 334 | if( this->manages_multiple_futures() ){ 335 | 336 | this->_when_any( std::move( function ) ) ; 337 | }else{ 338 | this->then( std::move( function ) ) ; 339 | } 340 | } 341 | template = 0, 343 | Task::detail::not_copyable = 0> 344 | void when_any( Function function ) 345 | { 346 | if( this->manages_multiple_futures() ){ 347 | 348 | this->_when_any( Task::detail::function( std::move( function ) ) ) ; 349 | }else{ 350 | this->then( Task::detail::function( std::move( function ) ) ) ; 351 | } 352 | } 353 | void when_all() 354 | { 355 | this->then( [](){} ) ; 356 | } 357 | void when_seq() 358 | { 359 | this->queue( [](){} ) ; 360 | } 361 | void when_any() 362 | { 363 | if( this->manages_multiple_futures() ){ 364 | 365 | this->_when_any( [](){} ) ; 366 | }else{ 367 | this->then( [](){} ) ; 368 | } 369 | } 370 | T get() 371 | { 372 | if( this->manages_multiple_futures() ){ 373 | 374 | for( auto& it : m_tasks ){ 375 | 376 | it.second( it.first->get() ) ; 377 | } 378 | 379 | this->deleteLater() ; 380 | 381 | return T() ; 382 | }else{ 383 | return m_get() ; 384 | } 385 | } 386 | T await() 387 | { 388 | QEventLoop p ; 389 | 390 | T q ; 391 | 392 | m_function = [ & ]( T&& r ){ q = std::move( r ) ; p.exit() ; } ; 393 | 394 | this->start() ; 395 | 396 | p.exec() ; 397 | 398 | return q ; 399 | } 400 | bool manages_multiple_futures() 401 | { 402 | return m_tasks.size() > 0 ; 403 | } 404 | const std::vector< QThread * >& all_threads() 405 | { 406 | return m_threads ; 407 | } 408 | QThread * first_thread() 409 | { 410 | return m_threads[ 0 ] ; 411 | } 412 | QThread * thread_at( std::vector< QThread * >::size_type s ) 413 | { 414 | return m_threads[ s ] ; 415 | } 416 | void start() 417 | { 418 | if( this->manages_multiple_futures() ){ 419 | 420 | this->_start() ; 421 | }else{ 422 | m_start() ; 423 | } 424 | } 425 | void cancel() 426 | { 427 | if( this->manages_multiple_futures() ){ 428 | 429 | for( auto& it : m_tasks ){ 430 | 431 | it.first->cancel() ; 432 | } 433 | 434 | this->deleteLater() ; 435 | }else{ 436 | m_cancel() ; 437 | } 438 | } 439 | future() = default ; 440 | future( const future& ) = delete ; 441 | future( future&& ) = delete ; 442 | future& operator=( const future& ) = delete ; 443 | future& operator=( future&& ) = delete ; 444 | 445 | future( QThread * e, 446 | std::function< void() >&& start, 447 | std::function< void() >&& cancel, 448 | std::function< T() >&& get ) : 449 | m_thread( e ), 450 | m_start ( std::move( start ) ), 451 | m_cancel( std::move( cancel ) ), 452 | m_get ( std::move( get ) ) 453 | { 454 | if( m_thread ){ 455 | 456 | m_threads.push_back( m_thread ) ; 457 | }else{ 458 | /* 459 | * This object was created by "_private_future< T >()" class. 460 | * It has no QThread of its own because it only manages other futures. 461 | * 462 | */ 463 | } 464 | } 465 | void run( T&& r ) 466 | { 467 | if( m_function_1 != nullptr ){ 468 | 469 | m_function_1() ; 470 | 471 | }else if( m_function != nullptr ){ 472 | 473 | m_function( std::move( r ) ) ; 474 | } 475 | } 476 | 477 | template< typename E > 478 | friend void Task::detail::add( Task::future< E >&, 479 | Task::future< E >&, 480 | std::function< void( E ) >&& ) ; 481 | private: 482 | void _when_any( std::function< void() > function ) 483 | { 484 | m_when_any_function = std::move( function ) ; 485 | 486 | for( auto& it : m_tasks ){ 487 | 488 | it.first->then( [ & ]( T&& e ){ 489 | 490 | QMutexLocker m( &m_mutex ) ; 491 | 492 | m_counter++ ; 493 | 494 | if( m_task_not_run ){ 495 | 496 | m_task_not_run = false ; 497 | 498 | m.unlock() ; 499 | 500 | it.second( std::forward( e ) ) ; 501 | 502 | m_when_any_function() ; 503 | }else{ 504 | m.unlock() ; 505 | it.second( std::forward( e ) ) ; 506 | } 507 | 508 | if( m_counter == m_tasks.size() ){ 509 | 510 | this->deleteLater() ; 511 | } 512 | } ) ; 513 | } 514 | } 515 | void _queue() 516 | { 517 | m_tasks[ m_counter ].first->then( [ this ]( T&& e ){ 518 | 519 | m_tasks[ m_counter ].second( std::forward( e ) ) ; 520 | 521 | m_counter++ ; 522 | 523 | if( m_counter == m_tasks.size() ){ 524 | 525 | m_function_1() ; 526 | 527 | this->deleteLater() ; 528 | }else{ 529 | this->_queue() ; 530 | } 531 | } ) ; 532 | } 533 | void _start() 534 | { 535 | for( auto& it : m_tasks ){ 536 | 537 | it.first->then( [ & ]( T&& e ){ 538 | 539 | QMutexLocker m( &m_mutex ) ; 540 | 541 | Q_UNUSED( m ) ; 542 | 543 | m_counter++ ; 544 | 545 | it.second( std::forward( e ) ) ; 546 | 547 | if( m_counter == m_tasks.size() ){ 548 | 549 | if( m_function_1 != nullptr ){ 550 | 551 | m_function_1() ; 552 | 553 | }else if( m_function != nullptr ){ 554 | 555 | m_function( T() ) ; 556 | } 557 | 558 | this->deleteLater() ; 559 | } 560 | } ) ; 561 | } 562 | } 563 | 564 | QThread * m_thread = nullptr ; 565 | std::function< void( T ) > m_function = nullptr ; 566 | std::function< void() > m_function_1 = nullptr ; 567 | std::function< void() > m_start = [](){} ; 568 | std::function< void() > m_cancel = [](){} ; 569 | std::function< T() > m_get = [](){ return T() ; } ; 570 | std::function< void() > m_when_any_function ; 571 | 572 | QMutex m_mutex ; 573 | std::vector< std::pair< Task::future< T > *,std::function< void( T ) > > > m_tasks ; 574 | std::vector< QThread * > m_threads ; 575 | decltype( m_tasks.size() ) m_counter = 0 ; 576 | bool m_task_not_run = true ; 577 | }; 578 | 579 | template<> 580 | class future< void > : private QObject 581 | { 582 | public: 583 | template = 0> 584 | void then( Function function ) 585 | { 586 | m_function = std::move( function ) ; 587 | this->start() ; 588 | } 589 | template = 0> 590 | void then( Function function ) 591 | { 592 | m_function = Task::detail::function( std::move( function ) ) ; 593 | this->start() ; 594 | } 595 | template = 0> 596 | void queue( Function function ) 597 | { 598 | if( this->manages_multiple_futures() ){ 599 | 600 | m_function = std::move( function ) ; 601 | 602 | this->_queue() ; 603 | }else{ 604 | this->then( std::move( function ) ) ; 605 | } 606 | } 607 | template = 0> 608 | void queue( Function function ) 609 | { 610 | if( this->manages_multiple_futures() ){ 611 | 612 | m_function = Task::detail::function( std::move( function ) ) ; 613 | 614 | this->_queue() ; 615 | }else{ 616 | this->then( Task::detail::function( std::move( function ) ) ) ; 617 | } 618 | } 619 | void queue() 620 | { 621 | if( this->manages_multiple_futures() ){ 622 | 623 | m_function = [](){} ; 624 | 625 | this->_queue() ; 626 | }else{ 627 | this->then( [](){} ) ; 628 | } 629 | } 630 | void when_any() 631 | { 632 | if( this->manages_multiple_futures() ){ 633 | 634 | this->_when_any( [](){} ) ; 635 | }else{ 636 | this->then( [](){} ) ; 637 | } 638 | } 639 | template = 0> 640 | void when_any( Function function ) 641 | { 642 | if( this->manages_multiple_futures() ){ 643 | 644 | this->_when_any( std::move( function ) ) ; 645 | }else{ 646 | this->then( std::move( function ) ) ; 647 | } 648 | } 649 | template = 0> 650 | void when_any( Function function ) 651 | { 652 | if( this->manages_multiple_futures() ){ 653 | 654 | this->_when_any( Task::detail::function( std::move( function ) ) ) ; 655 | }else{ 656 | this->then( Task::detail::function( std::move( function ) ) ) ; 657 | } 658 | } 659 | /* 660 | * Below two API just exposes existing functionality using more standard names 661 | */ 662 | template 663 | void when_all( Function function ) 664 | { 665 | this->then( std::move( function ) ) ; 666 | } 667 | template 668 | void when_seq( Function function ) 669 | { 670 | this->queue( std::move( function ) ) ; 671 | } 672 | void when_all() 673 | { 674 | this->then( [](){} ) ; 675 | } 676 | void when_seq() 677 | { 678 | this->queue() ; 679 | } 680 | void get() 681 | { 682 | if( this->manages_multiple_futures() ){ 683 | 684 | for( auto& it : m_tasks ){ 685 | 686 | it.first->get() ; 687 | it.second() ; 688 | } 689 | 690 | this->deleteLater() ; 691 | }else{ 692 | m_get() ; 693 | } 694 | } 695 | void await() 696 | { 697 | QEventLoop p ; 698 | 699 | m_function = [ & ](){ p.exit() ; } ; 700 | 701 | this->start() ; 702 | 703 | p.exec() ; 704 | } 705 | bool manages_multiple_futures() 706 | { 707 | return m_tasks.size() > 0 ; 708 | } 709 | const std::vector< QThread * >& all_threads() 710 | { 711 | return m_threads ; 712 | } 713 | QThread * first_thread() 714 | { 715 | return m_threads[ 0 ] ; 716 | } 717 | QThread * thread_at( std::vector< QThread * >::size_type s ) 718 | { 719 | return m_threads[ s ] ; 720 | } 721 | void start() 722 | { 723 | if( this->manages_multiple_futures() ){ 724 | 725 | this->_start() ; 726 | }else{ 727 | m_start() ; 728 | } 729 | } 730 | void cancel() 731 | { 732 | if( this->manages_multiple_futures() ){ 733 | 734 | for( auto& it : m_tasks ){ 735 | 736 | it.first->cancel() ; 737 | } 738 | 739 | this->deleteLater() ; 740 | }else{ 741 | m_cancel() ; 742 | } 743 | } 744 | future() = default ; 745 | future( const future& ) = delete ; 746 | future( future&& ) = delete ; 747 | future& operator=( const future& ) = delete ; 748 | future& operator=( future&& ) = delete ; 749 | 750 | future( QThread * e , 751 | std::function< void() >&& start, 752 | std::function< void() >&& cancel, 753 | std::function< void() >&& get ) : 754 | m_thread( e ), 755 | m_start ( std::move( start ) ), 756 | m_cancel( std::move( cancel ) ), 757 | m_get ( std::move( get ) ) 758 | { 759 | if( m_thread ){ 760 | 761 | m_threads.push_back( m_thread ) ; 762 | }else{ 763 | /* 764 | * This object was created by "_private_future< T >()" class. 765 | * It has no QThread of its own because it only manages other futures. 766 | * 767 | */ 768 | } 769 | } 770 | 771 | template< typename T > 772 | friend void Task::detail::add_void( Task::future< T >&, 773 | Task::future< T >&, 774 | std::function< T() >&& ) ; 775 | void run() 776 | { 777 | m_function() ; 778 | } 779 | private: 780 | void _when_any( std::function< void() > function ) 781 | { 782 | m_when_any_function = std::move( function ) ; 783 | 784 | for( auto& it : m_tasks ){ 785 | 786 | it.first->then( [ & ](){ 787 | 788 | QMutexLocker m( &m_mutex ) ; 789 | 790 | m_counter++ ; 791 | 792 | if( m_task_not_run ){ 793 | 794 | m_task_not_run = false ; 795 | 796 | m.unlock() ; 797 | 798 | it.second() ; 799 | 800 | m_when_any_function() ; 801 | }else{ 802 | m.unlock() ; 803 | it.second() ; 804 | } 805 | 806 | if( m_counter == m_tasks.size() ){ 807 | 808 | this->deleteLater() ; 809 | } 810 | } ) ; 811 | } 812 | } 813 | void _queue() 814 | { 815 | m_tasks[ m_counter ].first->then( [ this ](){ 816 | 817 | m_tasks[ m_counter ].second() ; 818 | 819 | m_counter++ ; 820 | 821 | if( m_counter == m_tasks.size() ){ 822 | 823 | m_function() ; 824 | 825 | this->deleteLater() ; 826 | }else{ 827 | this->_queue() ; 828 | } 829 | } ) ; 830 | } 831 | 832 | void _start() 833 | { 834 | for( auto& it : m_tasks ){ 835 | 836 | it.first->then( [ & ](){ 837 | 838 | QMutexLocker m( &m_mutex ) ; 839 | 840 | Q_UNUSED( m ) ; 841 | 842 | m_counter++ ; 843 | 844 | it.second() ; 845 | 846 | if( m_counter == m_tasks.size() ){ 847 | 848 | m_function() ; 849 | 850 | this->deleteLater() ; 851 | } 852 | } ) ; 853 | } 854 | } 855 | 856 | QThread * m_thread = nullptr ; 857 | 858 | std::function< void() > m_function = [](){} ; 859 | std::function< void() > m_start = [](){} ; 860 | std::function< void() > m_cancel = [](){} ; 861 | std::function< void() > m_get = [](){} ; 862 | std::function< void() > m_when_any_function ; 863 | QMutex m_mutex ; 864 | std::vector< std::pair< Task::future< void > *,std::function< void() > > > m_tasks ; 865 | std::vector< QThread * > m_threads ; 866 | decltype( m_tasks.size() ) m_counter = 0 ; 867 | bool m_task_not_run = true ; 868 | }; 869 | 870 | namespace detail 871 | { 872 | /* 873 | * -------------------------Start of internal helper functions------------------------- 874 | */ 875 | template< typename Type,typename Function > 876 | class ThreadHelper : public QThread 877 | { 878 | public: 879 | ThreadHelper( Function function ) : 880 | m_function( std::move( function ) ), 881 | m_future( this, 882 | [ this ](){ this->start() ; }, 883 | [ this ](){ this->deleteLater() ; }, 884 | [ this ](){ this->deleteLater() ; return m_function() ; } ) 885 | { 886 | connect( this,&QThread::finished,this,&QThread::deleteLater ) ; 887 | } 888 | Task::future& Future() 889 | { 890 | return m_future ; 891 | } 892 | private: 893 | ~ThreadHelper() 894 | { 895 | m_future.run( std::move( m_result ) ) ; 896 | } 897 | void run() 898 | { 899 | m_result = m_function() ; 900 | } 901 | Function m_function ; 902 | Task::future m_future ; 903 | Type m_result ; 904 | }; 905 | 906 | template< typename Function> 907 | class ThreadHelperVoid : public QThread 908 | { 909 | public: 910 | ThreadHelperVoid( Function function ) : 911 | m_function( std::move( function ) ), 912 | m_future( this, 913 | [ this ](){ this->start() ; }, 914 | [ this ](){ this->deleteLater() ; }, 915 | [ this ](){ m_function() ; this->deleteLater() ; } ) 916 | { 917 | connect( this,&QThread::finished,this,&QThread::deleteLater ) ; 918 | } 919 | Task::future< void >& Future() 920 | { 921 | return m_future ; 922 | } 923 | private: 924 | ~ThreadHelperVoid() 925 | { 926 | m_future.run() ; 927 | } 928 | void run() 929 | { 930 | m_function() ; 931 | } 932 | Function m_function ; 933 | Task::future< void > m_future ; 934 | }; 935 | template = 0> 936 | Task::future>& run( Fn function ) 937 | { 938 | using t = Task::detail::result_of ; 939 | 940 | return ( new ThreadHelper( std::move( function ) ) )->Future() ; 941 | } 942 | template = 0> 943 | Task::future>& run( Fn function ) 944 | { 945 | return ( new ThreadHelperVoid( std::move( function ) ) )->Future() ; 946 | } 947 | template< typename T > 948 | void add( Task::future< T >& a,Task::future< T >& b,std::function< void( T ) >&& c ) 949 | { 950 | a.m_tasks.emplace_back( std::addressof( b ),std::move( c ) ) ; 951 | a.m_threads.push_back( b.m_thread ) ; 952 | } 953 | 954 | template< typename T > 955 | void add_void( Task::future< T >& a,Task::future< T >& b,std::function< T() >&& c ) 956 | { 957 | a.m_tasks.emplace_back( std::addressof( b ),std::move( c ) ) ; 958 | a.m_threads.push_back( b.m_thread ) ; 959 | } 960 | 961 | template< typename T > 962 | void add_task( Task::future< T >& f ) 963 | { 964 | Q_UNUSED( f ) 965 | } 966 | 967 | template< typename T > 968 | void add_future( Task::future< T >& f ) 969 | { 970 | Q_UNUSED( f ) 971 | } 972 | 973 | template< typename T > 974 | void add_pair( Task::future< T >& f ) 975 | { 976 | Q_UNUSED( f ) 977 | } 978 | 979 | template< typename T > 980 | void add_pair_void( Task::future< T >& f ) 981 | { 982 | Q_UNUSED( f ) 983 | } 984 | 985 | template< typename ... T, 986 | typename Function, 987 | Task::detail::not_copyable = 0 > 988 | void add_task( Task::future< void >& f,Function e,T&& ... t ) ; 989 | 990 | template< typename ... T, 991 | typename Function, 992 | Task::detail::copyable = 0 > 993 | void add_task( Task::future< void >& f,Function e,T&& ... t ) 994 | { 995 | add_void( f,Task::detail::run( std::function< void() >( std::move( e ) ) ), 996 | std::function< void() >( [](){} ) ) ; 997 | add_task( f,std::forward( t ) ... ) ; 998 | } 999 | 1000 | template< typename ... T, 1001 | typename Function, 1002 | Task::detail::not_copyable > 1003 | void add_task( Task::future< void >& f,Function e,T&& ... t ) 1004 | { 1005 | auto a = std::function< void() >( Task::detail::function( std::move( e ) ) ) ; 1006 | 1007 | add_void( f,Task::detail::run( std::move( a ) ), 1008 | std::function< void() >( [](){} ) ) ; 1009 | 1010 | add_task( f,std::forward( t ) ... ) ; 1011 | } 1012 | 1013 | template< typename ... T > 1014 | void add_future( Task::future< void >& f,Task::future< void >& e,T&& ... t ) 1015 | { 1016 | add_void( f,e,std::function< void() >( [](){} ) ) ; 1017 | add_future( f,std::forward( t ) ... ) ; 1018 | } 1019 | 1020 | template< typename E,typename F,typename ... T > 1021 | void add_pair( Task::future< E >& f,F&& s,T&& ... t ) 1022 | { 1023 | add( f,Task::detail::run( std::move( s.value.first ) ),std::move( s.value.second ) ) ; 1024 | add_pair( f,std::forward( t ) ... ) ; 1025 | } 1026 | 1027 | template< typename F,typename ... T > 1028 | void add_pair_void( Task::future< void >& f,F&& s,T&& ... t ) 1029 | { 1030 | add_void( f,Task::detail::run( std::move( s.value.first ) ),std::move( s.value.second ) ) ; 1031 | add_pair_void( f,std::forward( t ) ... ) ; 1032 | } 1033 | 1034 | template< typename T > 1035 | Task::future< T >& future() 1036 | { 1037 | return *( new Task::future< T >() ) ; 1038 | } 1039 | 1040 | } //end of detail namespace 1041 | 1042 | 1043 | /* 1044 | * -------------------------End of internal helper functions------------------------- 1045 | */ 1046 | template< typename Fn,Task::detail::copyable = 0 > 1047 | auto& run( Fn function ) 1048 | { 1049 | return Task::detail::run( std::move( function ) ) ; 1050 | } 1051 | template< typename Fn,Task::detail::not_copyable = 0 > 1052 | auto& run( Fn function ) 1053 | { 1054 | return Task::detail::run( Task::detail::function( std::move( function ) ) ) ; 1055 | } 1056 | #if __cplusplus > 201703L 1057 | template< typename Fn,typename ... Args > 1058 | future>& run( Fn function,Args ... args ) 1059 | { 1060 | return Task::run( [ function = std::move( function ),... args = std::move( args ) ]()mutable{ 1061 | 1062 | return function( std::move( args ) ... ) ; 1063 | } ) ; 1064 | } 1065 | #elif __cplusplus == 201703L 1066 | template< typename Fn,typename ... Args > 1067 | future>& run( Fn function,Args ... args ) 1068 | { 1069 | return Task::run( [ args = std::make_tuple( std::move( args ) ... ),function = std::move( function ) ]()mutable{ 1070 | 1071 | return std::apply( [ function = std::move( function ) ]( Args ... args ){ 1072 | 1073 | return function( std::move( args ) ... ) ; 1074 | 1075 | },std::move( args ) ) ; 1076 | } ) ; 1077 | } 1078 | #else 1079 | template< typename Fn,typename ... Args > 1080 | future>& run( Fn function,Args ... args ) 1081 | { 1082 | return Task::run( [ =,function = std::move( function ) ](){ 1083 | 1084 | return function( std::move( args ) ... ) ; 1085 | } ) ; 1086 | } 1087 | #endif 1088 | class progress : public QObject{ 1089 | Q_OBJECT 1090 | public: 1091 | template< typename function > 1092 | progress( QObject * obj,function fn ) 1093 | { 1094 | connect( this,&progress::update,obj,std::move( fn ) ) ; 1095 | } 1096 | signals: 1097 | void update( QVariant x ) const ; 1098 | private: 1099 | }; 1100 | 1101 | template< typename Fn,typename cb > 1102 | future>& run( QObject * obj,Fn function,cb rp ) 1103 | { 1104 | return Task::run( [ obj,rp = std::move( rp ),function = std::move( function ) ](){ 1105 | 1106 | return function( progress( obj,std::move( rp ) ) ) ; 1107 | } ) ; 1108 | } 1109 | 1110 | template< typename Function, 1111 | Task::detail::copyable = 0, 1112 | typename ... T > 1113 | Task::future< void >& run_tasks( Function f,T ... t ) 1114 | { 1115 | auto& e = Task::detail::future< void >() ; 1116 | Task::detail::add_task( e,std::move( f ),std::move( t ) ... ) ; 1117 | return e ; 1118 | } 1119 | template< typename Function, 1120 | Task::detail::not_copyable = 0, 1121 | typename ... T > 1122 | Task::future< void >& run_tasks( Function f,T ... t ) 1123 | { 1124 | auto& e = Task::detail::future< void >() ; 1125 | Task::detail::add_task( e,Task::detail::function( std::move( f ) ),std::move( t ) ... ) ; 1126 | return e ; 1127 | } 1128 | 1129 | template< typename ... T > 1130 | Task::future< void >& run_tasks( Task::future< void >& s,T&& ... t ) 1131 | { 1132 | auto& e = Task::detail::future< void >() ; 1133 | Task::detail::add_future( e,s,std::forward( t ) ... ) ; 1134 | return e ; 1135 | } 1136 | 1137 | template< typename ... T > 1138 | Task::future< void >& run( pair< void > s,T ... t ) 1139 | { 1140 | auto& e = Task::detail::future< void >() ; 1141 | Task::detail::add_pair_void( e,std::move( s ),std::move( t ) ... ) ; 1142 | return e ; 1143 | } 1144 | 1145 | template< typename E,typename ... T > 1146 | Task::future< E >& run( pair< E > s,T ... t ) 1147 | { 1148 | auto& e = Task::detail::future< E >() ; 1149 | Task::detail::add_pair( e,std::move( s ),std::move( t ) ... ) ; 1150 | return e ; 1151 | } 1152 | 1153 | /* 1154 | * 1155 | * A few useful helper functions 1156 | * 1157 | */ 1158 | template< typename Fn > 1159 | Task::detail::result_of await( Fn function ) 1160 | { 1161 | return Task::run( std::move( function ) ).await() ; 1162 | } 1163 | 1164 | template< typename Fn,typename ... Args > 1165 | Task::detail::result_of await( Fn function,Args ... args ) 1166 | { 1167 | return Task::run( std::move( function ),std::move( args ) ... ).await() ; 1168 | } 1169 | template< typename T > 1170 | T await( Task::future& e ) 1171 | { 1172 | return e.await() ; 1173 | } 1174 | 1175 | template< typename T > 1176 | T await( std::future t ) 1177 | { 1178 | return Task::await( [ & ](){ return t.get() ; } ) ; 1179 | } 1180 | 1181 | /* 1182 | * These methods run their arguments in a separate thread and does not offer 1183 | * continuation feature.Useful when wanting to just run a function in a 1184 | * different thread. 1185 | */ 1186 | template< typename Fn > 1187 | void exec( Fn function ) 1188 | { 1189 | Task::run( std::move( function ) ).start() ; 1190 | } 1191 | template< typename Fn,typename ... Args > 1192 | void exec( Fn function,Args ... args ) 1193 | { 1194 | Task::run( std::move( function ),std::move( args ) ... ).start() ; 1195 | } 1196 | template< typename T > 1197 | void exec( Task::future& e ) 1198 | { 1199 | e.start() ; 1200 | } 1201 | 1202 | namespace process { 1203 | 1204 | class result{ 1205 | public: 1206 | result() = default ; 1207 | result( int exit_code ) : 1208 | m_finished( true ), 1209 | m_exitCode( exit_code ), 1210 | m_exitStatus( 0 ) 1211 | { 1212 | } 1213 | template< typename E,typename F > 1214 | result( E&& std_out, 1215 | F&& std_error, 1216 | int exit_code, 1217 | int exit_status, 1218 | bool finished ) : 1219 | m_stdOut( std::forward( std_out ) ), 1220 | m_stdError( std::forward( std_error ) ), 1221 | m_finished( finished ), 1222 | m_exitCode( exit_code ), 1223 | m_exitStatus( exit_status ) 1224 | { 1225 | } 1226 | result( QProcess& e,int s ) 1227 | { 1228 | m_finished = e.waitForFinished( s ) ; 1229 | m_stdOut = e.readAllStandardOutput() ; 1230 | m_stdError = e.readAllStandardError() ; 1231 | m_exitCode = e.exitCode() ; 1232 | m_exitStatus = e.exitStatus() ; 1233 | } 1234 | const QByteArray& std_out() const 1235 | { 1236 | return m_stdOut ; 1237 | } 1238 | const QByteArray& std_error() const 1239 | { 1240 | return m_stdError ; 1241 | } 1242 | bool finished() const 1243 | { 1244 | return m_finished ; 1245 | } 1246 | bool success() const 1247 | { 1248 | return m_exitCode == 0 && 1249 | m_exitStatus == QProcess::NormalExit && 1250 | m_finished == true ; 1251 | } 1252 | bool failed() const 1253 | { 1254 | return !this->success() ; 1255 | } 1256 | int exit_code() const 1257 | { 1258 | return m_exitCode ; 1259 | } 1260 | int exit_status() const 1261 | { 1262 | return m_exitStatus ; 1263 | } 1264 | private: 1265 | QByteArray m_stdOut ; 1266 | QByteArray m_stdError ; 1267 | bool m_finished = false ; 1268 | int m_exitCode = 255 ; 1269 | int m_exitStatus = 255 ; 1270 | }; 1271 | 1272 | static inline Task::future< result >& run( const QString& cmd, 1273 | const QStringList& args, 1274 | int waitTime = -1, 1275 | const QByteArray& password = QByteArray(), 1276 | const QProcessEnvironment& env = QProcessEnvironment(), 1277 | std::function< void() > setUp_child_process = [](){} ) 1278 | { 1279 | return Task::run( [ = ](){ 1280 | 1281 | class Process : public QProcess{ 1282 | public: 1283 | Process( std::function< void() > function, 1284 | const QProcessEnvironment& env ) : 1285 | m_function( std::move( function ) ) 1286 | { 1287 | this->setProcessEnvironment( env ) ; 1288 | } 1289 | protected: 1290 | void setupChildProcess() 1291 | { 1292 | m_function() ; 1293 | } 1294 | private: 1295 | std::function< void() > m_function ; 1296 | 1297 | } exe( std::move( setUp_child_process ),env ) ; 1298 | 1299 | if( args.isEmpty() ){ 1300 | 1301 | #if QT_VERSION < QT_VERSION_CHECK( 5,15,0 ) 1302 | exe.start( cmd ) ; 1303 | #else 1304 | exe.start( cmd,args ) ; 1305 | #endif 1306 | }else{ 1307 | exe.start( cmd,args ) ; 1308 | } 1309 | 1310 | if( !password.isEmpty() ){ 1311 | 1312 | exe.waitForStarted( waitTime ) ; 1313 | exe.write( password ) ; 1314 | exe.closeWriteChannel() ; 1315 | } 1316 | 1317 | return result( exe,waitTime ) ; 1318 | } ) ; 1319 | } 1320 | 1321 | static inline Task::future< result >& run( const QString& cmd,const QByteArray& password ) 1322 | { 1323 | return Task::process::run( cmd,{},-1,password ) ; 1324 | } 1325 | 1326 | static inline Task::future< result >& run( const QString& cmd, 1327 | const QStringList& args, 1328 | const QByteArray& password ) 1329 | { 1330 | return Task::process::run( cmd,args,-1,password ) ; 1331 | } 1332 | } 1333 | } 1334 | 1335 | #if 0 // start example block 1336 | 1337 | Examples on how to use the library 1338 | 1339 | ********************************************************** 1340 | * Example use cases on how to use Task::run().then() API 1341 | ********************************************************** 1342 | 1343 | templated version that passes a return value of one function to another function 1344 | --------------------------------------------------------------------------------- 1345 | 1346 | int foo 1347 | { 1348 | /* 1349 | * This task will run on a different thread 1350 | * This tasks returns a result 1351 | */ 1352 | return 0 ; 1353 | } 1354 | 1355 | void bar( int r ) 1356 | { 1357 | /* 1358 | * This task will run on the original thread. 1359 | * This tasks takes an argument returned by task _a 1360 | */ 1361 | } 1362 | 1363 | Task::run( foo ).then( bar ) ; 1364 | 1365 | alternatively, 1366 | 1367 | Task::future& e = Task::run( foo ) ; 1368 | 1369 | e.then( bar ) ; 1370 | 1371 | 1372 | Non templated version that does not pass around return value 1373 | ---------------------------------------------------------------- 1374 | void foo_1() 1375 | { 1376 | /* 1377 | * This task will run on a different thread 1378 | * This tasks returns with no result 1379 | */ 1380 | } 1381 | 1382 | void bar_1() 1383 | { 1384 | /* 1385 | * This task will run on the original thread. 1386 | * This tasks takes no argument 1387 | */ 1388 | } 1389 | 1390 | Task::run( foo_1 ).then( bar_1 ) ; 1391 | 1392 | alternatively, 1393 | 1394 | Task::future& e = Task::run( foo_1 ) ; 1395 | 1396 | e.then( bar_1 ) ; 1397 | 1398 | ********************************************************** 1399 | * Example use cases on how to use Task::run().await() API 1400 | ********************************************************** 1401 | 1402 | int r = Task::await( foo ) ; 1403 | 1404 | alternatively, 1405 | 1406 | Task::future& e = Task::run( foo ) ; 1407 | 1408 | int r = e.await() ; 1409 | 1410 | alternatively, 1411 | 1412 | int r = Task::run( foo ).await() ; 1413 | 1414 | ******************************************************************* 1415 | * Example use cases on how to use lambda that requires an argument 1416 | ******************************************************************* 1417 | 1418 | auto foo_2 = []( int x ){ 1419 | 1420 | return x + 1 ; 1421 | } ; 1422 | 1423 | Task::run( foo_2,6 ).then( []( int r ){ 1424 | 1425 | qDebug() << r ; 1426 | } ) ; 1427 | 1428 | alternatively, 1429 | 1430 | r = Task::await( foo_2,6 ) ; 1431 | 1432 | ******************************************************************* 1433 | * Example use cases on how to run multiple tasks and wait for all to 1434 | * to finish before continuing 1435 | ******************************************************************* 1436 | 1437 | Task::future& e = Task::run( fn1,fn2,fn3 ) ; 1438 | 1439 | or alternatively, 1440 | 1441 | Task::future& f1 = Task::run( fn1 ) ; 1442 | Task::future& f2 = Task::run( fn2 ) ; 1443 | Task::future& e = Task::run( f1,f2,f3 ) ; 1446 | 1447 | 1.0 .await() can then be called on the future to wait for all tasks to finish before continuing. 1448 | 2.0 .then() can then be called on the future to invoke a callback on the current thread when all tasks finish. 1449 | 1450 | ******************************************************************* 1451 | * Example use cases on how to run multiple tasks and their continuations 1452 | * and wait for all to finish before continuing 1453 | ******************************************************************* 1454 | 1455 | std::cout<< "Testing multiple tasks without continuation arguments" << std::endl ; 1456 | 1457 | auto fna1 = [](){ _printThreadID(); } ; 1458 | auto fna2 = [](){ _printThreadID(); } ; 1459 | auto fna3 = [](){ _printThreadID(); } ; 1460 | 1461 | auto ra1 = [](){ std::cout << "r1" << std::endl ; } ; 1462 | auto ra2 = [](){ std::cout << "r2" << std::endl ; } ; 1463 | auto ra3 = [](){ std::cout << "r3" << std::endl ; } ; 1464 | 1465 | Task::future& e = Task::run( Task::make_pair( fna1,ra1 ), 1466 | Task::make_pair( fna2,ra2 ), 1467 | Task::make_pair( fna3,ra3 ) ) ; 1468 | 1469 | e.await() ; 1470 | 1471 | std::cout<< "Testing multiple tasks with continuation arguments" << std::endl ; 1472 | 1473 | auto fn1 = [](){ _printThreadID(); return 0 ; } ; 1474 | auto fn2 = [](){ _printThreadID(); return 0 ; } ; 1475 | auto fn3 = [](){ _printThreadID(); return 0 ; } ; 1476 | 1477 | auto r1 = []( int ){ std::cout << "r1" << std::endl ; } ; 1478 | auto r2 = []( int ){ std::cout << "r2" << std::endl ; } ; 1479 | auto r3 = []( int ){ std::cout << "r3" << std::endl ; } ; 1480 | 1481 | Task::future& s = Task::run( Task::make_pair( fn1,r1 ), 1482 | Task::make_pair( fn2,r2 ), 1483 | Task::make_pair( fn3,r3 ) ) ; 1484 | 1485 | s.then( [](){ QCoreApplication::quit() ; } ) ; 1486 | 1487 | #endif //end example block 1488 | 1489 | #endif //__TASK_H_INCLUDED__ 1490 | --------------------------------------------------------------------------------