From abfbbc8a681f4c71e4a648a0426daf8043b04b50 Mon Sep 17 00:00:00 2001 From: bennyedlund Date: Sat, 27 Apr 2024 11:17:45 +1200 Subject: [PATCH] Updating examples to use std::lanch syntax and fixing some build errors in C++17 --- CMakeLists.txt | 10 +- docs/building.md | 4 +- examples/CMakeLists.txt | 110 +-- examples/README.md | 1 + examples/collage/CMakeLists.txt | 5 +- examples/collage/main.cpp | 839 +++++++++++------------ examples/conanfile.txt | 7 +- examples/image_processing/CMakeLists.txt | 3 +- examples/image_processing/main.cpp | 10 +- examples/webserver/CMakeLists.txt | 1 - examples/webserver/server.cpp | 261 ++++--- lib/CMakeLists.txt | 28 +- lib/config.cmake.in | 3 + lib/public/task_pool/pool.h | 1 - lib/public/task_pool/traits.h | 631 +++++++---------- test/tests.cpp | 6 + 16 files changed, 826 insertions(+), 1094 deletions(-) create mode 100644 lib/config.cmake.in diff --git a/CMakeLists.txt b/CMakeLists.txt index 3eebb4e..495bcc8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ set(OPT_WARNINGS_AS_ERRORS_DEVELOPER_DEFAULT TRUE) # Set the project name and language project( task_pool - VERSION 3.1.0 + VERSION 4.0.0 DESCRIPTION "" HOMEPAGE_URL "%%https://github.com/benny-edlund/task-pool%%" LANGUAGES CXX ) @@ -50,8 +50,8 @@ elseif(NOT PROJECT_IS_TOP_LEVEL) return() endif() -set(CPACK_PACKAGE_FILE_NAME - "${CMAKE_PROJECT_NAME}-${CMAKE_PROJECT_VERSION}-${GIT_SHORT_SHA}-${CMAKE_SYSTEM_NAME}-${CMAKE_BUILD_TYPE}-${CMAKE_CXX_COMPILER_ID}-${CMAKE_CXX_COMPILER_VERSION}" -) - +include(InstallRequiredSystemLibraries) +set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE") +set(CPACK_PACKAGE_VERSION_MAJOR "${task_pool_VERSION_MAJOR}") +set(CPACK_PACKAGE_VERSION_MINOR "${task_pool_VERSION_MINOR}") include(CPack) diff --git a/docs/building.md b/docs/building.md index d15f08f..f35c21e 100644 --- a/docs/building.md +++ b/docs/building.md @@ -7,9 +7,9 @@ A standard release configuration may be produced in the following way conan install . --output-folder=build --build=missing --settings=build_type=Release cd build (linux/macos) -cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake +cmake .. -G "Unix Makefiles" -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_POLICY_DEFAULT_CMP0091=NEW -DCMAKE_BUILD_TYPE=Release (windows) -cmake .. -G "Visual Studio 17 2022" -DCMAKE_BUILD_TYPE=Release -DCMAKE_TOOLCHAIN_FILE="conan_toolchain.cmake" +cmake .. -G "Visual Studio 17 2022" -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_POLICY_DEFAULT_CMP0091=NEW -DCMAKE_BUILD_TYPE=Release cmake --build . ```   diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index af9c786..dd2bae3 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,116 +1,24 @@ cmake_minimum_required(VERSION 3.16...3.23) + set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_EXTENSIONS ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -set(ENABLE_DEVELOPER_MODE ON CACHE BOOL "Enable 'developer mode'") +set(ENABLE_DEVELOPER_MODE OFF CACHE BOOL "Enable 'developer mode'") set(ENABLE_COVERAGE OFF CACHE BOOL "Enable 'coverage mode'") -set(OPT_WARNINGS_AS_ERRORS_DEVELOPER_DEFAULT OFF) -set(WARNINGS_AS_ERRORS_DEVELOPER OFF) +set(OPT_WARNINGS_AS_ERRORS_DEVELOPER_DEFAULT TRUE) -include(FetchContent) -FetchContent_Declare(_project_options URL https://github.com/aminya/project_options/archive/refs/tags/v0.20.0.zip) -FetchContent_MakeAvailable(_project_options) -include(${_project_options_SOURCE_DIR}/Index.cmake) +# Set the project name and language project( task_pool_examples - VERSION 0.0.0 - DESCRIPTION "" + VERSION 0.0 + DESCRIPTION " " HOMEPAGE_URL "%%https://github.com/benny-edlund/task-pool%%" - LANGUAGES CXX C) - -# This variable is set by project() in CMake 3.21+ -string( - COMPARE EQUAL - "${CMAKE_SOURCE_DIR}" - "${PROJECT_SOURCE_DIR}" - PROJECT_IS_TOP_LEVEL) -if(PROJECT_IS_TOP_LEVEL) - include(CTest) - if(CMAKE_COMPILER_IS_GNUCXX) - LIST(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake_modules") - endif() -endif() + LANGUAGES CXX ) set(GIT_SHA "Unknown" CACHE STRING "SHA this build was generated from") string( SUBSTRING "${GIT_SHA}" 0 8 GIT_SHORT_SHA) -get_property(BUILDING_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) -if(BUILDING_MULTI_CONFIG) - if(NOT CMAKE_BUILD_TYPE) - # Make sure that all supported configuration types have their - # associated conan packages available. You can reduce this - # list to only the configuration types you use, but only if one - # is not forced-set on the command line for VS - message(TRACE "Setting up multi-config build types") - set(CMAKE_CONFIGURATION_TYPES - Debug - Release - RelWithDebInfo - MinSizeRel - CACHE STRING "Enabled build types" FORCE) - else() - message(TRACE "User chose a specific build type, so we are using that") - set(CMAKE_CONFIGURATION_TYPES - ${CMAKE_BUILD_TYPE} - CACHE STRING "Enabled build types" FORCE) - endif() -endif() - -include(${_project_options_SOURCE_DIR}/src/DynamicProjectOptions.cmake) - -dynamic_project_options( - # Note: PCH is disabled by default in developer mode because these headers become - # globally included and they can mask other errors - PCH_HEADERS - - - - - ENABLE_CONAN - # CONAN_OPTIONS # Extra options to pass to conan - # MSVC_WARNINGS # Override the defaults for the MSVC warnings - # CLANG_WARNINGS # Override the defaults for the CLANG warnings - # GCC_WARNINGS # Override the defaults for the GCC warnings - CPPCHECK_OPTIONS - --enable=style,performance,warning,portability - --inline-suppr - # We cannot act on a bug/missing feature of cppcheck - --suppress=cppcheckError - --suppress=internalAstError - # if a file does not have an internalAstError, we get an unmatchedSuppression error - --suppress=unmatchedSuppression - --suppress=passedByValue - --suppress=syntaxError - --suppress=constParameter # buggy - --suppress=nullPointer # buggy - --suppress=functionStatic # buggy - --suppress=constStatement # doesnt like pipes - --suppress=throwInNoexceptFunction # does not know about new(std::nothrow) - --inconclusive -) - - -target_compile_features(project_options INTERFACE cxx_std_${CMAKE_CXX_STANDARD}) -# TODO: The INTERFACE library NAMESPACE ALIAS are missing! CK -add_library(task_pool_examples::project_options INTERFACE IMPORTED) -add_library(task_pool_examples::project_warnings INTERFACE IMPORTED) - -# configure files based on CMake configuration options -add_subdirectory(image_processing) -add_subdirectory(webserver) add_subdirectory(collage) - -# If MSVC is being used, and ASAN is enabled, we need to set the debugger environment -# so that it behaves well with MSVC's debugger, and we can run the target from visual studio -if(MSVC) - get_all_installable_targets(all_targets) - message("all_targets=${all_targets}") - set_target_properties(${all_targets} PROPERTIES VS_DEBUGGER_ENVIRONMENT "PATH=$(VC_ExecutablePath_x64);%PATH%") -endif() - -if(CMAKE_SKIP_INSTALL_RULES) - return() -elseif(NOT PROJECT_IS_TOP_LEVEL) - return() -endif() +add_subdirectory(image_processing) +add_subdirectory(webserver) \ No newline at end of file diff --git a/examples/README.md b/examples/README.md index 267b24f..243cb5a 100644 --- a/examples/README.md +++ b/examples/README.md @@ -17,6 +17,7 @@ The examples has some external dependencies, fmt, CLI11, boost (headers) and tur ```bash cd examples +conan install . --output-folder=build cmake -S . -B ./build -DCMAKE_PREFIX_PATH=/path/to/install cmake --build ./build ``` diff --git a/examples/collage/CMakeLists.txt b/examples/collage/CMakeLists.txt index 25f2a29..7ebeee3 100644 --- a/examples/collage/CMakeLists.txt +++ b/examples/collage/CMakeLists.txt @@ -16,6 +16,5 @@ target_link_libraries(example_collage PRIVATE fmt::fmt) target_link_libraries(example_collage PRIVATE libjpeg-turbo::turbojpeg-static) target_link_libraries(example_collage PRIVATE nlohmann_json::nlohmann_json) target_link_libraries(example_collage PRIVATE CLI11::CLI11 ) -target_link_libraries(example_collage PRIVATE CURL::CURL) -target_link_libraries(example_collage PRIVATE ncurses::libcurses) -target_link_libraries(example_collage PRIVATE project_warnings project_options ) \ No newline at end of file +target_link_libraries(example_collage PRIVATE CURL::libcurl) +target_link_libraries(example_collage PRIVATE ncurses::libcurses) \ No newline at end of file diff --git a/examples/collage/main.cpp b/examples/collage/main.cpp index c6795d4..a82f4fd 100644 --- a/examples/collage/main.cpp +++ b/examples/collage/main.cpp @@ -7,24 +7,27 @@ * @copyright Copyright (c) 2023 * * @details - * First off dont write this program, its a bad one. Im intentially going with a poor design to - * show retrying of tasks. + * First off dont write this program, its a bad one. Im intentially going with a + * poor design to show retrying of tasks. * - * The goal of this program is build an image collage of a configurable density from Wikipedia - * random articles. + * The goal of this program is build an image collage of a configurable density + * from Wikipedia random articles. * - * The program solves this by using curl to query the random/summary article. It then locates the - * article image, decompresses it and inserts it into the final image after resizing it. The final - * result is contigously updated. + * The program solves this by using curl to query the random/summary article. It + * then locates the article image, decompresses it and inserts it into the final + * image after resizing it. The final result is contigously updated. * - * The trouble is that the program can only deal with jpeg images and not all article summaries use - * jpegs as their image. Further it seems that Wikipedia routinely renames gifs and pngs to jpg so - * its quite likley that the jpeg decompression fails. Experiments show about a 5% success rate. + * The trouble is that the program can only deal with jpeg images and not all + * article summaries use jpegs as their image. Further it seems that Wikipedia + * routinely renames gifs and pngs to jpg so its quite likley that the jpeg + * decompression fails. Experiments show about a 5% success rate. * - * This deficiancy in the program was not resolved by adding support for other image format. Instead - * we brute foce and just re-runs the task. Poor wikipedia. + * This deficiancy in the program was not resolved by adding support for other + * image format. Instead we brute foce and just re-runs the task. Poor + * wikipedia. * - * Needless to say you should not do this...but it does let us deal with a lot of errors. + * Needless to say you should not do this...but it does let us deal with a lot + * of errors. */ #include #include @@ -38,11 +41,11 @@ #include #include #include -#include -#include +// #include +// #include #include #include -#include +// #include #include #include #include @@ -51,27 +54,23 @@ using json = nlohmann::json; -static std::atomic_size_t s_total_queries{ 0 }; -static std::atomic_size_t s_success_queries{ 0 }; +namespace { +std::atomic_size_t s_total_queries{0}; +std::atomic_size_t s_success_queries{0}; +} // namespace -struct task_failure : public std::runtime_error -{ - explicit task_failure( std::string msg ) - : std::runtime_error( msg ) - { - } - ~task_failure() = default; +struct task_failure : public std::runtime_error { + explicit task_failure(std::string const &msg) : std::runtime_error(msg) {} }; /** * @brief Rectangular dimentions * */ -struct dimentions -{ - std::size_t width; - std::size_t height; - std::size_t size() const noexcept { return width * height; } +struct dimentions { + std::size_t width; + std::size_t height; + std::size_t size() const noexcept { return width * height; } }; /** @@ -79,54 +78,44 @@ struct dimentions * * @tparam T */ -template< typename T > -struct pixel_t -{ - using value_type = T; - value_type red; - value_type green; - value_type blue; +template struct pixel_t { + using value_type = T; + value_type red; + value_type green; + value_type blue; }; -using pixel = pixel_t< std::uint8_t >; +using pixel = pixel_t; /** * @brief Basic image class * */ -class Image -{ - dimentions dims_; - std::vector< pixel > pixels_; +class Image { + dimentions dims_; + std::vector pixels_; public: - Image() - : dims_{ 0, 0 } - { - } - explicit Image( dimentions dims ) // NOLINT - : dims_{ dims } - , pixels_( dims_.size(), pixel{ 0, 0, 0 } ) - { - } - ~Image() = default; - Image( Image const& ) = delete; - Image( Image&& other ) noexcept - : dims_( other.dims_ ) - , pixels_( std::move( other.pixels_ ) ) - { - other.dims_ = {}; - } - Image& operator=( Image const& ) = delete; - Image& operator =( Image&& other ) noexcept - { - dims_ = other.dims_; - other.dims_ = {}; - pixels_.clear(); - std::swap( pixels_, other.pixels_ ); - return *this; - } - dimentions dims() const noexcept { return dims_; } - std::vector< pixel >& pixels() noexcept { return pixels_; } - explicit operator bool() const noexcept { return pixels_.size() == dims_.size(); } + Image() : dims_{0, 0} {} + explicit Image(dimentions dims) // NOLINT + : dims_{dims}, pixels_(dims_.size(), pixel{0, 0, 0}) {} + ~Image() = default; + Image(Image const &) = delete; + Image(Image &&other) noexcept + : dims_(other.dims_), pixels_(std::move(other.pixels_)) { + other.dims_ = {}; + } + Image &operator=(Image const &) = delete; + Image &operator=(Image &&other) noexcept { + dims_ = other.dims_; + other.dims_ = {}; + pixels_.clear(); + std::swap(pixels_, other.pixels_); + return *this; + } + dimentions dims() const noexcept { return dims_; } + std::vector &pixels() noexcept { return pixels_; } + explicit operator bool() const noexcept { + return pixels_.size() == dims_.size(); + } }; /** @@ -135,46 +124,36 @@ class Image * @param img * @return auto */ -auto compress( Image& img ) // NOLINT +auto compress(Image &img) // NOLINT { - if ( img.dims().size() != 0 ) - { - const int JPEG_QUALITY = 75; - int width = static_cast< int >( img.dims().width ); - int height = static_cast< int >( img.dims().height ); - long unsigned int jpegSize = 0; - unsigned char* compressedImage = nullptr; + if (img.dims().size() != 0) { + const int JPEG_QUALITY = 75; + int width = static_cast(img.dims().width); + int height = static_cast(img.dims().height); + long unsigned int jpegSize = 0; + unsigned char *compressedImage = nullptr; - tjhandle jpegCompressor = tjInitCompress(); - std::vector< unsigned char > raw_image( img.dims().width * img.dims().height * 3 ); - std::size_t i = 0; - for ( auto const& pxl : img.pixels() ) - { - raw_image[i++] = pxl.red; - raw_image[i++] = pxl.green; - raw_image[i++] = pxl.blue; - } - auto ec = tjCompress2( jpegCompressor, - raw_image.data(), - width, - 0, - height, - TJPF_RGB, - &compressedImage, - &jpegSize, - TJSAMP_444, - JPEG_QUALITY, - TJFLAG_FASTDCT ); - tjDestroy( jpegCompressor ); - if ( ec != 0 ) - { - fmt::print( "[ {} ]:: Failed to compress image\n", __LINE__ ); - throw task_failure{ tjGetErrorStr() }; - } - return std::make_pair( compressedImage, jpegSize ); + tjhandle jpegCompressor = tjInitCompress(); + std::vector raw_image(img.dims().width * img.dims().height * + 3); + std::size_t i = 0; + for (auto const &pxl : img.pixels()) { + raw_image[i++] = pxl.red; + raw_image[i++] = pxl.green; + raw_image[i++] = pxl.blue; } - unsigned char* none = nullptr; - return std::make_pair( none, 0UL ); + auto ec = tjCompress2(jpegCompressor, raw_image.data(), width, 0, height, + TJPF_RGB, &compressedImage, &jpegSize, TJSAMP_444, + JPEG_QUALITY, TJFLAG_FASTDCT); + tjDestroy(jpegCompressor); + if (ec != 0) { + fmt::print("[ {} ]:: Failed to compress image\n", __LINE__); + throw task_failure{tjGetErrorStr()}; + } + return std::make_pair(compressedImage, jpegSize); + } + unsigned char *none = nullptr; + return std::make_pair(none, 0UL); } /** @@ -183,44 +162,33 @@ auto compress( Image& img ) // NOLINT * @param data * @return Image */ -Image decompress( std::vector< std::uint8_t > data ) -{ - int width = 0; - int height = 0; - int subsample = 0; +Image decompress(std::vector data) { + int width = 0; + int height = 0; + int subsample = 0; - tjhandle jpegDecompressor = tjInitDecompress(); - if ( 0 != tjDecompressHeader2( jpegDecompressor, - data.data(), - data.size() * sizeof( std::uint8_t ), - &width, - &height, - &subsample ) ) - { - tjDestroy( jpegDecompressor ); - throw task_failure( fmt::format( - "[ {} ]: Failed to decompress jpeg header ( {} )", __LINE__, tjGetErrorStr() ) ); - } - Image img( - dimentions{ static_cast< std::size_t >( width ), static_cast< std::size_t >( height ) } ); - if ( 0 != tjDecompress2( jpegDecompressor, - data.data(), - data.size(), - &img.pixels().data()->red, - width, - width * 3, - height, - TJPF_RGB, - 0 ) ) - { - tjDestroy( jpegDecompressor ); - throw task_failure( fmt::format( "[ {} ]: Failed to decompress jpeg body ( {} )", - __LINE__, - tjGetErrorStr2( jpegDecompressor ) ) ); - } - tjDestroy( jpegDecompressor ); - s_success_queries++; - return img; + tjhandle jpegDecompressor = tjInitDecompress(); + if (0 != tjDecompressHeader2(jpegDecompressor, data.data(), + data.size() * sizeof(std::uint8_t), &width, + &height, &subsample)) { + tjDestroy(jpegDecompressor); + throw task_failure( + fmt::format("[ {} ]: Failed to decompress jpeg header ( {} )", + __LINE__, tjGetErrorStr())); + } + Image img(dimentions{static_cast(width), + static_cast(height)}); + if (0 != tjDecompress2(jpegDecompressor, data.data(), data.size(), + &img.pixels().data()->red, width, width * 3, height, + TJPF_RGB, 0)) { + tjDestroy(jpegDecompressor); + throw task_failure( + fmt::format("[ {} ]: Failed to decompress jpeg body ( {} )", __LINE__, + tjGetErrorStr2(jpegDecompressor))); + } + tjDestroy(jpegDecompressor); + s_success_queries++; + return img; } /** @@ -230,17 +198,16 @@ Image decompress( std::vector< std::uint8_t > data ) * @param filename * @return unsigned* */ -unsigned char* write( std::pair< unsigned char*, long unsigned int > data, - std::string const& filename ) -{ - if ( data.first != nullptr ) - { - std::ofstream writer; - writer.open( filename, std::ios::out | std::ios::binary ); - // NOLINTNEXTLINE - writer.write( reinterpret_cast< char* >( data.first ), std::streamsize( data.second ) ); - } - return data.first; +unsigned char *write(std::pair data, + std::string const &filename) { + if (data.first != nullptr) { + std::ofstream writer; + writer.open(filename, std::ios::out | std::ios::binary); + // NOLINTNEXTLINE + writer.write(reinterpret_cast(data.first), + std::streamsize(data.second)); + } + return data.first; } /** @@ -252,10 +219,11 @@ unsigned char* write( std::pair< unsigned char*, long unsigned int > data, * @param userp * @return size_t */ -static size_t curl_write_function_( void* contents, size_t size, size_t nmemb, void* userp ) -{ - static_cast< std::string* >( userp )->append( static_cast< char* >( contents ), size * nmemb ); - return size * nmemb; +static size_t curl_write_function_(void *contents, size_t size, size_t nmemb, + void *userp) { + static_cast(userp)->append(static_cast(contents), + size * nmemb); + return size * nmemb; } /** @@ -264,28 +232,27 @@ static size_t curl_write_function_( void* contents, size_t size, size_t nmemb, v * @param url * @return std::vector< std::uint8_t > */ -std::vector< std::uint8_t > curl( std::string url ) -{ - assert( !url.empty() ); - auto* curl = curl_easy_init(); - if ( curl != nullptr ) - { - s_total_queries++; - curl_easy_setopt( curl, CURLOPT_URL, url.c_str() ); - curl_easy_setopt( curl, CURLOPT_TCP_KEEPALIVE, 1L ); - curl_easy_setopt( curl, CURLOPT_FOLLOWLOCATION, 1L ); +std::vector curl(std::string url) { + assert(!url.empty()); + auto *curl = curl_easy_init(); + if (curl != nullptr) { + s_total_queries++; + curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - std::string response; - std::string header; - curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, curl_write_function_ ); - curl_easy_setopt( curl, CURLOPT_WRITEDATA, &response ); - curl_easy_setopt( curl, CURLOPT_HEADERDATA, &header ); + std::string response; + std::string header; + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_function_); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response); + curl_easy_setopt(curl, CURLOPT_HEADERDATA, &header); - curl_easy_perform( curl ); - curl_easy_cleanup( curl ); - return { response.begin(), response.end() }; - } - throw task_failure( fmt::format( "[ {} ]: Failed to initialize curl", __LINE__ ) ); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + return {response.begin(), response.end()}; + } + throw task_failure( + fmt::format("[ {} ]: Failed to initialize curl", __LINE__)); } /** @@ -294,22 +261,18 @@ std::vector< std::uint8_t > curl( std::string url ) * @param abort * @return std::string */ -std::string find_random_jpeg( be::stop_token abort ) -{ - while ( !abort ) - { - json data = - json::parse( curl( "https://en.wikipedia.org/api/rest_v1/page/random/summary" ) ); - if ( !data["originalimage"]["source"].empty() ) - { - std::string output = data["originalimage"]["source"]; - if ( output.find( ".jpg" ) != output.npos ) - { - return output; - } - } +std::string find_random_jpeg(be::stop_token abort) { + while (!abort) { + json data = json::parse( + curl("https://en.wikipedia.org/api/rest_v1/page/random/summary")); + if (!data["originalimage"]["source"].empty()) { + std::string output = data["originalimage"]["source"]; + if (output.find(".jpg") != output.npos) { + return output; + } } - return {}; + } + return {}; } /** @@ -320,52 +283,44 @@ std::string find_random_jpeg( be::stop_token abort ) * @param abort * @return Image */ -Image resize_image( dimentions size, Image input, be::stop_token abort ) -{ - try - { - Image output( size ); - float scale_width = - static_cast< float >( input.dims().width ) / static_cast< float >( size.width ); - float scale_height = - static_cast< float >( input.dims().height ) / static_cast< float >( size.height ); - std::size_t w = 0; - std::size_t h = 0; - while ( !abort ) - { - auto output_index = w + h * output.dims().width; - std::size_t input_index = - static_cast< std::size_t >( static_cast< float >( w ) * scale_width ) + - ( input.dims().width * - static_cast< std::size_t >( static_cast< float >( h ) * scale_height ) ); - output.pixels().at( output_index ) = input.pixels().at( input_index ); - if ( ++w >= output.dims().width ) - { - ++h; - w = 0; - } - if ( h >= output.dims().height ) - { - break; - } - } - return output; - } - catch ( std::exception const& e ) - { - throw task_failure( - fmt::format( "[ {} ]: Failed to resize image ( {} )\n", __LINE__, e.what() ) ); +Image resize_image(dimentions size, Image input, be::stop_token abort) { + try { + Image output(size); + float scale_width = + static_cast(input.dims().width) / static_cast(size.width); + float scale_height = static_cast(input.dims().height) / + static_cast(size.height); + std::size_t w = 0; + std::size_t h = 0; + while (!abort) { + auto output_index = w + h * output.dims().width; + std::size_t input_index = + static_cast(static_cast(w) * scale_width) + + (input.dims().width * + static_cast(static_cast(h) * scale_height)); + output.pixels().at(output_index) = input.pixels().at(input_index); + if (++w >= output.dims().width) { + ++h; + w = 0; + } + if (h >= output.dims().height) { + break; + } } + return output; + } catch (std::exception const &e) { + throw task_failure(fmt::format("[ {} ]: Failed to resize image ( {} )\n", + __LINE__, e.what())); + } } /** * @brief view-like image wrapper used to represent a unique tile in the output * */ -struct ImageSection -{ - Image& owner; - dimentions start; +struct ImageSection { + Image &owner; + dimentions start; }; /** @@ -375,253 +330,235 @@ struct ImageSection * @param input * @param abort */ -void blit_image( ImageSection output, Image input, be::stop_token abort ) -{ - std::size_t w = 0; - std::size_t h = 0; - try - { - while ( !abort ) - { - std::size_t index = ( output.start.width + w ) + - output.owner.dims().width * ( output.start.height + h ); - output.owner.pixels().at( index ) = input.pixels().at( w + input.dims().width * h ); - if ( ++w >= input.dims().width ) - { - ++h; - w = 0; - } - if ( h >= input.dims().height ) - { - break; - } - } - } - catch ( std::exception const& e ) - { - throw task_failure( - fmt::format( "[ {} ]: Failed to insert image section ( {} )\n", __LINE__, e.what() ) ); +void blit_image(ImageSection output, Image input, be::stop_token abort) { + std::size_t w = 0; + std::size_t h = 0; + try { + while (!abort) { + std::size_t index = (output.start.width + w) + + output.owner.dims().width * (output.start.height + h); + output.owner.pixels().at(index) = + input.pixels().at(w + input.dims().width * h); + if (++w >= input.dims().width) { + ++h; + w = 0; + } + if (h >= input.dims().height) { + break; + } } + } catch (std::exception const &e) { + throw task_failure(fmt::format( + "[ {} ]: Failed to insert image section ( {} )\n", __LINE__, e.what())); + } } /** * @brief Curl helper managing the global state * */ -struct curl_context -{ - curl_context() { curl_global_init( CURL_GLOBAL_ALL ); } - ~curl_context() { curl_global_cleanup(); } +struct curl_context { + curl_context() { curl_global_init(CURL_GLOBAL_ALL); } + ~curl_context() { curl_global_cleanup(); } }; /** * @brief ncurses helper managing the global window state * */ -struct curses_contex -{ - curses_contex() - { - setlocale( LC_ALL, "" ); // NOLINT (not using return value) - initscr(); - nodelay( stdscr, true ); - noecho(); - } - ~curses_contex() { endwin(); } +struct curses_contex { + curses_contex() { + setlocale(LC_ALL, ""); // NOLINT (not using return value) + initscr(); + nodelay(stdscr, true); + noecho(); + } + ~curses_contex() { endwin(); } }; using namespace std::chrono_literals; -int main( int argc, char** argv ) // NOLINT +int main(int argc, char **argv) // NOLINT { - CLI::App app; - std::string filename; - std::size_t tiles_width = 10; - std::size_t tiles_height = 10; - app.description( "Create a collage of random images X tiles wide and Y tiles high." ); - app.add_option( "-x", tiles_width, "Amount of tiles wide (default:10)" ); - app.add_option( "-y", tiles_height, "Amount of tiles high (default:10)" ); - app.add_option( "filename", filename, "Output filename" )->required(); - CLI11_PARSE( app, argc, argv ); + CLI::App app; + std::string filename; + std::size_t tiles_width = 10; + std::size_t tiles_height = 10; + app.description( + "Create a collage of random images X tiles wide and Y tiles high."); + app.add_option("-x", tiles_width, "Amount of tiles wide (default:10)"); + app.add_option("-y", tiles_height, "Amount of tiles high (default:10)"); + app.add_option("filename", filename, "Output filename")->required(); + CLI11_PARSE(app, argc, argv); - if ( tiles_height > 0 && tiles_width > 0 ) - { - curl_context curl_ctx; - curses_contex curses_ctx; - be::task_pool pool; + if (tiles_height > 0 && tiles_width > 0) { + curl_context curl_ctx; + curses_contex curses_ctx; + be::task_pool pool; - static const dimentions s_tile_size{ 128, 128 }; - static const dimentions s_img_size{ s_tile_size.width * tiles_width, - s_tile_size.height * tiles_height }; + static const dimentions s_tile_size{128, 128}; + static const dimentions s_img_size{s_tile_size.width * tiles_width, + s_tile_size.height * tiles_height}; - Image output( s_img_size ); + Image output(s_img_size); - // we wrap the resize method so we can capture the tile size - auto resize = []( Image img, be::stop_token abort ) { - return resize_image( s_tile_size, std::move( img ), std::move( abort ) ); - }; - // main task pipeline, it ends up returning a future so we can monitor them - auto fill_tile = [&]( std::size_t tile ) -> std::future< void > { - auto h = tile / tiles_width; - auto w = tile - h * tiles_width; - dimentions start{ w * s_tile_size.width, h * s_tile_size.height }; - // clang-format off + // we wrap the resize method so we can capture the tile size + auto resize = [](Image img, be::stop_token abort) { + return resize_image(s_tile_size, std::move(img), std::move(abort)); + }; + // main task pipeline, it ends up returning a future so we can monitor them + auto fill_tile = [&](std::size_t tile) -> std::future { + auto h = tile / tiles_width; + auto w = tile - h * tiles_width; + dimentions start{w * s_tile_size.width, h * s_tile_size.height}; + // clang-format off auto pipe = pool | &find_random_jpeg | &curl | &decompress | resize; - return pool.submit( &blit_image, ImageSection{ output, start }, std::move(pipe) ); - // clang-format on - }; - // our main compression and write function. - auto write_file = [&]() -> std::future< void > { - auto jpeg_data = pool.submit( &compress, std::ref( output ) ); - auto jpeg_ptr = pool.submit( &write, std::move( jpeg_data ), std::ref( filename ) ); - return pool.submit( &tjFree, std::move( jpeg_ptr ) ); - }; - // basic console ui showing our progress - auto render_ui = [&]( std::vector< bool > const& status ) { - clear(); - refresh(); - int row = 0; - move( ++row, 0 ); - printw( - "Building image collage from random Wikipedia articles contigously writing to '%s'", - filename.c_str() ); - move( ++row, 0 ); - printw( "(Esc to stop)" ); - ++row; - move( ++row, 0 ); - auto total = s_total_queries.load(); - auto success = s_success_queries.load(); - printw( "total queries: %lu", total ); - move( ++row, 0 ); - printw( - "success rate: %d%%", - static_cast< int >( - ( static_cast< float >( success ) / static_cast< float >( total ) ) * 100.F ) ); - move( ++row, 0 ); - printw( "threads: %d", pool.get_thread_count() ); - move( ++row, 0 ); - printw( "tasks total: %lu", pool.get_tasks_total() ); - move( ++row, 0 ); - printw( "tasks waiting: %lu", pool.get_tasks_waiting() ); - move( ++row, 0 ); - printw( "tasks queued: %lu", pool.get_tasks_queued() ); - move( ++row, 0 ); - printw( "tasks running: %lu", pool.get_tasks_running() ); - ++row; - ++row; - static const int width = static_cast< int >( tiles_width * 2 ); - int w = 0; - int h = 0; - for ( bool state : status ) - { - move( h + row, w ); - printw( state ? "\u2593\u2593" : "\u2591\u2591" ); // NOLINT; - w += 2; - if ( w == width ) - { - ++h; - w = 0; - } - } - move( ++row + h, 0 ); + return pool.submit( std::launch::async, &blit_image, ImageSection{ output, start }, std::move(pipe) ); + // clang-format on + }; + // our main compression and write function. + auto write_file = [&]() -> std::future { + auto jpeg_data = + pool.submit(std::launch::async, &compress, std::ref(output)); + auto jpeg_ptr = pool.submit(std::launch::async, &write, + std::move(jpeg_data), std::ref(filename)); + return pool.submit(std::launch::async, &tjFree, std::move(jpeg_ptr)); + }; + // basic console ui showing our progress + auto render_ui = [&](std::vector const &status) { + clear(); + refresh(); + int row = 0; + move(++row, 0); + printw("Building image collage from random Wikipedia articles " + "contigously writing to '%s'", + filename.c_str()); + move(++row, 0); + printw("(Esc to stop)"); + ++row; + move(++row, 0); + auto total = s_total_queries.load(); + auto success = s_success_queries.load(); + printw("total queries: %lu", total); + move(++row, 0); + printw("success rate: %d%%", + static_cast( + (static_cast(success) / static_cast(total)) * + 100.F)); + move(++row, 0); + printw("threads: %d", pool.get_thread_count()); + move(++row, 0); + printw("tasks total: %lu", pool.get_tasks_total()); + move(++row, 0); + printw("tasks waiting: %lu", pool.get_tasks_waiting()); + move(++row, 0); + printw("tasks queued: %lu", pool.get_tasks_queued()); + move(++row, 0); + printw("tasks running: %lu", pool.get_tasks_running()); + ++row; + ++row; + static const int width = static_cast(tiles_width * 2); + int w = 0; + int h = 0; + for (bool state : status) { + move(h + row, w); + printw(state ? "\u2593\u2593" : "\u2591\u2591"); // NOLINT; + w += 2; + if (w == width) { + ++h; + w = 0; + } + } + move(++row + h, 0); - refresh(); - }; + refresh(); + }; - // First we start writing the initial image...its black - write_file().wait(); + // First we start writing the initial image...its black + write_file().wait(); - // If we know what tile we are working on we can safely generate a view into the output - // image that is unique to each task so we can write concurrently to it without locks. - // As tiles are completed we will reduce this vector. This is done in the main thread only - // so will not need any locks. - using section_vector = std::vector< std::pair< std::size_t, std::future< void > > >; - section_vector sections( tiles_width * tiles_height ); + // If we know what tile we are working on we can safely generate a view into + // the output image that is unique to each task so we can write concurrently + // to it without locks. As tiles are completed we will reduce this vector. + // This is done in the main thread only so will not need any locks. + using section_vector = + std::vector>>; + section_vector sections(tiles_width * tiles_height); - // Next we launch the inital tasks build the output images - std::size_t section = 0; - std::generate( sections.begin(), sections.end(), [&]() { - std::size_t tile_count = section++; - return std::make_pair( tile_count, fill_tile( tile_count ) ); - } ); - // We also generate a parallel status vector for the ui - std::vector< bool > status( tiles_width * tiles_height ); - std::fill( status.begin(), status.end(), false ); + // Next we launch the inital tasks build the output images + std::size_t section = 0; + std::generate(sections.begin(), sections.end(), [&]() { + std::size_t tile_count = section++; + return std::make_pair(tile_count, fill_tile(tile_count)); + }); + // We also generate a parallel status vector for the ui + std::vector status(tiles_width * tiles_height); + std::fill(status.begin(), status.end(), false); - // until we are done - for ( ;; ) - { - if ( sections.empty() ) - { - break; // we are done - } - // This is a efficient way to move all the jobs that are completed to the end of - // the vector. - auto new_end = - std::partition( sections.begin(), sections.end(), []( auto const& tile ) { - return tile.second.wait_for( 0s ) != std::future_status::ready; - } ); - std::vector< std::size_t > relaunch; - bool any_success = false; - // Check the result of all tasks that have completed. Since we use exceptions a failure - // is a thrown exception. If its a 'task_failure' we will requeue the tile and try - // another random article - std::for_each( new_end, sections.end(), [&]( auto& tile ) { - try - { - tile.second.get(); - any_success = true; - status[tile.first] = true; - } - catch ( task_failure const& ) - { - relaunch.push_back( tile.first ); - } - } ); - // remove successful sections - sections.erase( new_end, sections.end() ); - // relaunch failed sections - for ( auto tile : relaunch ) - { - sections.emplace_back( tile, fill_tile( tile ) ); - } - // if any jobs succeeded this iteration then update the image - if ( any_success ) - { - write_file(); - } - // well yeah...render the ui - render_ui( status ); - static const int s_esc = 27; - if ( getch() == s_esc ) - { - printw( "Stopping..." ); - refresh(); - pool.abort(); - printw( "done\n" ); - pool.reset( 1 ); - break; - } - // We are IO bound on the internet queries so no need to busy check this but still we - // want to show we are responsive in the ui - std::this_thread::sleep_for( 120ms ); + // until we are done + for (;;) { + if (sections.empty()) { + break; // we are done + } + // This is a efficient way to move all the jobs that are completed to the + // end of the vector. + auto new_end = std::partition( + sections.begin(), sections.end(), [](auto const &tile) { + return tile.second.wait_for(0s) != std::future_status::ready; + }); + std::vector relaunch; + bool any_success = false; + // Check the result of all tasks that have completed. Since we use + // exceptions a failure is a thrown exception. If its a 'task_failure' we + // will requeue the tile and try another random article + std::for_each(new_end, sections.end(), [&](auto &tile) { + try { + tile.second.get(); + any_success = true; + status[tile.first] = true; + } catch (task_failure const &) { + relaunch.push_back(tile.first); } - printw( "%s", fmt::format( "Saving output to {} ...", filename ).c_str() ); + }); + // remove successful sections + sections.erase(new_end, sections.end()); + // relaunch failed sections + for (auto tile : relaunch) { + sections.emplace_back(tile, fill_tile(tile)); + } + // if any jobs succeeded this iteration then update the image + if (any_success) { + write_file(); + } + // well yeah...render the ui + render_ui(status); + static const int s_esc = 27; + if (getch() == s_esc) { + printw("Stopping..."); refresh(); - auto done = write_file(); - try - { - printw( "done!" ); - refresh(); - done.get(); - } - catch ( std::exception const& e ) - { - printw( "%s", fmt::format( "Failed to write image [{}]\n", e.what() ).c_str() ); - refresh(); - } - std::this_thread::sleep_for( 2s ); + pool.abort(); + printw("done\n"); + pool.reset(1); + break; + } + // We are IO bound on the internet queries so no need to busy check this + // but still we want to show we are responsive in the ui + std::this_thread::sleep_for(120ms); + } + printw("%s", fmt::format("Saving output to {} ...", filename).c_str()); + refresh(); + auto done = write_file(); + try { + printw("done!"); + refresh(); + done.get(); + } catch (std::exception const &e) { + printw("%s", + fmt::format("Failed to write image [{}]\n", e.what()).c_str()); + refresh(); } - return 0; + std::this_thread::sleep_for(2s); + } + return 0; } diff --git a/examples/conanfile.txt b/examples/conanfile.txt index 22d41e5..cce94b7 100644 --- a/examples/conanfile.txt +++ b/examples/conanfile.txt @@ -11,8 +11,9 @@ nlohmann_json/3.11.2 ncurses/6.3 [generators] -cmake_find_package_multi +CMakeDeps +CMakeToolchain [options] -boost:header_only=True -turbojpeg:shared=True +boost/*:header_only=True +turbojpeg/*:shared=True diff --git a/examples/image_processing/CMakeLists.txt b/examples/image_processing/CMakeLists.txt index 40295e2..11d4f41 100644 --- a/examples/image_processing/CMakeLists.txt +++ b/examples/image_processing/CMakeLists.txt @@ -12,5 +12,4 @@ endif() target_link_libraries(example_img PRIVATE task_pool::task_pool ) target_link_libraries(example_img PRIVATE fmt::fmt) target_link_libraries(example_img PRIVATE libjpeg-turbo::turbojpeg-static) -target_link_libraries(example_img PRIVATE CLI11::CLI11 ) -target_link_libraries(example_img PRIVATE project_warnings project_options ) \ No newline at end of file +target_link_libraries(example_img PRIVATE CLI11::CLI11 ) \ No newline at end of file diff --git a/examples/image_processing/main.cpp b/examples/image_processing/main.cpp index d68352e..d4d72eb 100644 --- a/examples/image_processing/main.cpp +++ b/examples/image_processing/main.cpp @@ -206,7 +206,7 @@ int main( int argc, char** argv ) // NOLINT CLI11_PARSE( app, argc, argv ); be::task_pool pool; - std::future< Image > result = pool.submit( []() { + std::future< Image > result = pool.submit( std::launch::async, []() { return Image( dimentions{ s_max, s_max } ); } ); @@ -215,11 +215,11 @@ int main( int argc, char** argv ) // NOLINT scaler< 50 > >(); // NOLINT for ( auto const& work : workload ) { - result = pool.submit( &processor::run, work.get(), std::move( result ) ); + result = pool.submit( std::launch::async, &processor::run, work.get(), std::move( result ) ); } - auto jpeg_data = pool.submit( &compress, std::move( result ) ); - auto jpeg_ptr = pool.submit( &write, std::move( jpeg_data ), std::ref( filename ) ); - auto done = pool.submit( &tjFree, std::move( jpeg_ptr ) ); + auto jpeg_data = pool.submit( std::launch::async, &compress, std::move( result ) ); + auto jpeg_ptr = pool.submit( std::launch::async, &write, std::move( jpeg_data ), std::ref( filename ) ); + auto done = pool.submit( std::launch::async, &tjFree, std::move( jpeg_ptr ) ); try { done.get(); diff --git a/examples/webserver/CMakeLists.txt b/examples/webserver/CMakeLists.txt index 3a501c5..636d8a3 100644 --- a/examples/webserver/CMakeLists.txt +++ b/examples/webserver/CMakeLists.txt @@ -11,5 +11,4 @@ target_link_libraries(example_http PRIVATE task_pool::task_pool ) target_link_libraries(example_http PRIVATE Boost::headers ) target_link_libraries(example_http PRIVATE fmt::fmt) target_link_libraries(example_http PRIVATE CLI11::CLI11 ) -target_link_libraries(example_http PRIVATE project_warnings project_options ) diff --git a/examples/webserver/server.cpp b/examples/webserver/server.cpp index cd138ee..d3fc7d6 100644 --- a/examples/webserver/server.cpp +++ b/examples/webserver/server.cpp @@ -2,8 +2,15 @@ #include "task_pool/traits.h" #include #include -#include +// #include +#include +#if __cplusplus < 201700 #include +using string_view_type = std::experimental::string_view; +#else +#include +using string_view_type = std::string_view; +#endif #include #include #include @@ -16,131 +23,109 @@ #include static constexpr int BUFFER_SIZE = 30720; -using string_view = std::experimental::string_view; -using Data = std::vector< std::uint8_t, boost::pool_allocator< std::uint8_t > >; -using SocketData = std::pair< int, Data >; +using Data = std::vector>; +using SocketData = std::pair; /** - * This example is based on https://github.com/OsasAzamegbe/http-server please visit to view the - * original + * This example is based on https://github.com/OsasAzamegbe/http-server please + * visit to view the original */ -void log_message_( string_view filename, int fileline, std::string const& msg ) -{ - fmt::print( - "[{:%Y-%m-%d %H:%M:%S} {}:{}] {}", - fmt::localtime( std::chrono::system_clock::to_time_t( std::chrono::system_clock::now() ) ), - filename, - fileline, - msg ); +void log_message_(string_view_type filename, int fileline, std::string const &msg) { + fmt::print("[{:%Y-%m-%d %H:%M:%S} {}:{}] {}", + fmt::localtime(std::chrono::system_clock::to_time_t( + std::chrono::system_clock::now())), + filename, fileline, msg); } // NOLINTNEXTLINE -#define LOGGER( i_fmt_file, i_fmt_line, ... ) \ - []( char const* i_fmt_filename, \ - int i_fmt_fileline, \ - auto&& i_fmt_format, \ - auto&&... i_fmt_args ) { \ - constexpr std::size_t log_fmt_args_count = sizeof...( i_fmt_args ); \ - if ( log_fmt_args_count ) \ - { \ - log_message_( \ - i_fmt_filename, i_fmt_fileline, fmt::format( i_fmt_format, i_fmt_args... ) ); \ - } \ - else \ - { \ - log_message_( i_fmt_filename, i_fmt_fileline, std::string( i_fmt_format ) ); \ - } \ - }( i_fmt_file, i_fmt_line, __VA_ARGS__ ) +#define LOGGER(i_fmt_file, i_fmt_line, ...) \ + [](char const *i_fmt_filename, int i_fmt_fileline, auto &&i_fmt_format, \ + auto &&...i_fmt_args) { \ + constexpr std::size_t log_fmt_args_count = sizeof...(i_fmt_args); \ + if (log_fmt_args_count) { \ + log_message_(i_fmt_filename, i_fmt_fileline, \ + fmt::format(i_fmt_format, i_fmt_args...)); \ + } else { \ + log_message_(i_fmt_filename, i_fmt_fileline, std::string(i_fmt_format)); \ + } \ + }(i_fmt_file, i_fmt_line, __VA_ARGS__) // NOLINTNEXTLINE -#define CONSOL_LOG( ... ) LOGGER( __FILE__, __LINE__, __VA_ARGS__ ); +#define CONSOL_LOG(...) LOGGER(__FILE__, __LINE__, __VA_ARGS__); namespace http { -SocketData receive_data( int socket ) -{ - std::array< char, BUFFER_SIZE > buffer{ 0 }; - ssize_t bytesReceived = read( socket, static_cast< void* >( buffer.data() ), BUFFER_SIZE ); - if ( bytesReceived < 0 ) - { - CONSOL_LOG( string_view( "Error read from socket [{}]\n" ), strerror( errno ) ); - return {}; - }; - - return std::make_pair( - socket, - Data( std::make_move_iterator( buffer.begin() ), - std::make_move_iterator( buffer.begin() + sizeof( char ) * static_cast< std::size_t >( - bytesReceived ) ) ) ); +SocketData receive_data(int socket) { + std::array buffer{0}; + ssize_t bytesReceived = + read(socket, static_cast(buffer.data()), BUFFER_SIZE); + if (bytesReceived < 0) { + CONSOL_LOG(string_view_type("Error read from socket [{}]\n"), strerror(errno)); + return {}; + }; + + return std::make_pair( + socket, Data(std::make_move_iterator(buffer.begin()), + std::make_move_iterator( + buffer.begin() + sizeof(char) * static_cast( + bytesReceived)))); } -Data default_response() -{ - static std::string const htmlFile = - "

HOME

Hello from your Server :) " - "

"; - - std::string response = - fmt::format( "HTTP/1.1 200 OK\nContent-Type: text/html\nContent-Length: {}\n\n{}\n", - htmlFile.size(), - htmlFile ); - return { std::make_move_iterator( response.begin() ), - std::make_move_iterator( response.end() ) }; +Data default_response() { + static std::string const htmlFile = + "

HOME

Hello from " + "your Server :) " + "

"; + + std::string response = fmt::format( + "HTTP/1.1 200 OK\nContent-Type: text/html\nContent-Length: {}\n\n{}\n", + htmlFile.size(), htmlFile); + return {std::make_move_iterator(response.begin()), + std::make_move_iterator(response.end())}; } -SocketData parse_request( SocketData data ) -{ - std::string msg( std::make_move_iterator( data.second.begin() ), - std::make_move_iterator( data.second.end() ) ); - CONSOL_LOG( msg ); - return { data.first, default_response() }; +SocketData parse_request(SocketData data) { + std::string msg(std::make_move_iterator(data.second.begin()), + std::make_move_iterator(data.second.end())); + CONSOL_LOG(msg); + return {data.first, default_response()}; } -int send_response( SocketData data, be::stop_token abort ) -{ - long bytesSent = 0; +int send_response(SocketData data, be::stop_token abort) { + long bytesSent = 0; + + auto iter = data.second.begin(); + do { + bytesSent = + write(data.first, &(*iter), + static_cast(std::distance(iter, data.second.end()))); + if (bytesSent < 0) { + CONSOL_LOG( + string_view_type("Error occured sending response to client: [{}]\n"), + strerror(errno)); + } + std::advance(iter, bytesSent); + } while (bytesSent > 0 && !abort); - auto iter = data.second.begin(); - do - { - bytesSent = write( data.first, - &( *iter ), - static_cast< std::size_t >( std::distance( iter, data.second.end() ) ) ); - if ( bytesSent < 0 ) - { - CONSOL_LOG( string_view( "Error occured sending response to client: [{}]\n" ), - strerror( errno ) ); - } - std::advance( iter, bytesSent ); - } while ( bytesSent > 0 && !abort ); - - return data.first; + return data.first; } -void close_connection( int socket ) -{ - close( socket ); - CONSOL_LOG( string_view( "Connection closed\n" ) ); +void close_connection(int socket) { + close(socket); + CONSOL_LOG(string_view_type("Connection closed\n")); } -tcp_server::tcp_server( std::string ip_address, unsigned short port ) - : m_ip_address( std::move( ip_address ) ) - , m_port( port ) - , m_socket() - , m_socketAddress{} - , m_socketAddress_len( sizeof( m_socketAddress ) ) - , m_pool( boost::fast_pool_allocator< char >() ) -{ - m_socketAddress.sin_family = AF_INET; - m_socketAddress.sin_port = htons( m_port ); - m_socketAddress.sin_addr.s_addr = inet_addr( m_ip_address.c_str() ); +tcp_server::tcp_server(std::string ip_address, unsigned short port) + : m_ip_address(std::move(ip_address)), m_port(port), m_socket(), + m_socketAddress{}, m_socketAddress_len(sizeof(m_socketAddress)), + m_pool(boost::fast_pool_allocator()) { + m_socketAddress.sin_family = AF_INET; + m_socketAddress.sin_port = htons(m_port); + m_socketAddress.sin_addr.s_addr = inet_addr(m_ip_address.c_str()); } -tcp_server::~tcp_server() -{ - close( m_socket ); -} +tcp_server::~tcp_server() { close(m_socket); } // clang-format off void tcp_server::serve_forever() @@ -165,49 +150,45 @@ void tcp_server::serve_forever() } // clang-format on -void tcp_server::shutdown() -{ - CONSOL_LOG( string_view( "Shutting down\n" ) ); - m_pool.abort(); +void tcp_server::shutdown() { + CONSOL_LOG(string_view_type("Shutting down\n")); + m_pool.abort(); } -bool tcp_server::start_server() -{ - m_socket = socket( AF_INET, SOCK_STREAM, 0 ); - if ( m_socket < 0 ) - { - CONSOL_LOG( string_view( "Cannot create socket [{}]\n" ), strerror( errno ) ); - return false; - } - // NOLINTNEXTLINE - if ( bind( m_socket, reinterpret_cast< sockaddr* >( &m_socketAddress ), m_socketAddress_len ) < - 0 ) - { - CONSOL_LOG( string_view( "Cannot bind server to port [{}]\n" ), strerror( errno ) ); - return false; - } - CONSOL_LOG( string_view( "Server running on {}:{}\n" ), m_ip_address, m_port ); - return true; +bool tcp_server::start_server() { + m_socket = socket(AF_INET, SOCK_STREAM, 0); + if (m_socket < 0) { + CONSOL_LOG(string_view_type("Cannot create socket [{}]\n"), strerror(errno)); + return false; + } + // NOLINTNEXTLINE + if (bind(m_socket, reinterpret_cast(&m_socketAddress), + m_socketAddress_len) < 0) { + CONSOL_LOG(string_view_type("Cannot bind server to port [{}]\n"), + strerror(errno)); + return false; + } + CONSOL_LOG(string_view_type("Server running on {}:{}\n"), m_ip_address, m_port); + return true; } -int tcp_server::accept_connection() -{ - static constexpr int s_max_connections = 20; - if ( listen( m_socket, s_max_connections ) < 0 ) - { - CONSOL_LOG( string_view( "Cannot listen on port [{}]\n" ), strerror( errno ) ); - return 0; - } - auto socket = // NOLINTNEXTLINE - accept( m_socket, reinterpret_cast< sockaddr* >( &m_socketAddress ), &m_socketAddress_len ); - - if ( socket < 0 ) - { - CONSOL_LOG( string_view( "Server failed to accept incoming connection [{}]\n" ), - strerror( errno ) ); - return 0; - } - return socket; +int tcp_server::accept_connection() { + static constexpr int s_max_connections = 20; + if (listen(m_socket, s_max_connections) < 0) { + CONSOL_LOG(string_view_type("Cannot listen on port [{}]\n"), strerror(errno)); + return 0; + } + auto socket = // NOLINTNEXTLINE + accept(m_socket, reinterpret_cast(&m_socketAddress), + &m_socketAddress_len); + + if (socket < 0) { + CONSOL_LOG( + string_view_type("Server failed to accept incoming connection [{}]\n"), + strerror(errno)); + return 0; + } + return socket; } } // namespace http \ No newline at end of file diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index cbbcbb5..fbfb3c5 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -13,29 +13,25 @@ set(HEADER_LIST # Static library add_library(task_pool_static task_pool.cpp "${HEADER_LIST}") -set_target_properties(task_pool_static PROPERTIES PUBLIC_HEADER "${HEADER_LIST}") target_include_directories( task_pool_static PUBLIC $ $ - $ ) target_include_directories(task_pool_static PRIVATE "${CMAKE_BINARY_DIR}/configured_files/include") -target_compile_definitions( task_pool_static PUBLIC TASKPOOL_STATIC_DEFINE ) +target_compile_definitions( task_pool_static PUBLIC task_pool_STATIC_DEFINE ) if (UNIX) target_link_libraries(task_pool_static PRIVATE pthread) endif() # Shared library add_library(task_pool SHARED task_pool.cpp "${HEADER_LIST}") -set_target_properties(task_pool PROPERTIES PUBLIC_HEADER "${HEADER_LIST}") target_include_directories( task_pool PUBLIC $ $ - $ ) target_include_directories(task_pool PRIVATE "${CMAKE_BINARY_DIR}/configured_files/include") if (UNIX) @@ -50,6 +46,28 @@ generate_export_header( NO_EXPORT_MACRO_NAME TASKPOOL_HIDDEN ) +install(TARGETS task_pool task_pool_static +EXPORT task_pool_targets +LIBRARY DESTINATION lib +ARCHIVE DESTINATION lib +RUNTIME DESTINATION bin +INCLUDES DESTINATION include +) + +install(DIRECTORY public/task_pool DESTINATION include FILES_MATCHING PATTERN "*.h") +install(FILES ${PROJECT_BINARY_DIR}/lib/task_pool/api.h DESTINATION include/task_pool) + +include(CMakePackageConfigHelpers) +write_basic_package_version_file( task_poolConfigVersion.cmake VERSION "${task_pool_VERSION}" COMPATIBILITY AnyNewerVersion ) +export(EXPORT task_pool_targets FILE "${CMAKE_CURRENT_BINARY_DIR}/task_poolTargets.cmake" NAMESPACE task_pool:: ) +configure_package_config_file(${CMAKE_CURRENT_LIST_DIR}/config.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/task_poolConfig.cmake" INSTALL_DESTINATION lib/cmake/task_pool) +install(EXPORT task_pool_targets FILE task_poolTargets.cmake DESTINATION lib/cmake/task_pool NAMESPACE task_pool:: ) +install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/task_poolConfig.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/task_poolConfigVersion.cmake" + DESTINATION lib/cmake/task_pool +) + if ( CMAKE_COMPILER_IS_GNUCXX AND ENABLE_COVERAGE ) target_compile_options(task_pool PRIVATE -g -coverage ) target_compile_options(task_pool_static PRIVATE -g -coverage ) diff --git a/lib/config.cmake.in b/lib/config.cmake.in new file mode 100644 index 0000000..d4dd0f9 --- /dev/null +++ b/lib/config.cmake.in @@ -0,0 +1,3 @@ +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/task_poolTargets.cmake") \ No newline at end of file diff --git a/lib/public/task_pool/pool.h b/lib/public/task_pool/pool.h index f4a50be..e4769fc 100644 --- a/lib/public/task_pool/pool.h +++ b/lib/public/task_pool/pool.h @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include diff --git a/lib/public/task_pool/traits.h b/lib/public/task_pool/traits.h index 9693f67..c4f6d69 100644 --- a/lib/public/task_pool/traits.h +++ b/lib/public/task_pool/traits.h @@ -4,495 +4,376 @@ #include #include #include -#include #include -#include #include +#include #include #include -#if __cplusplus < 201700 -# include -#endif namespace be { -template< class Allocator > -class task_pool_t; +template class task_pool_t; -// template< template< typename, typename... > class Allocator, typename Value, typename... Ts > -// class task_pool_t< Allocator< Value, Ts... > >; +// template< template< typename, typename... > class Allocator, typename Value, +// typename... Ts > class task_pool_t< Allocator< Value, Ts... > >; -template< typename T > -struct is_pool; -template< template< typename > class T, class U > -struct is_pool< T< U > > : public std::is_same< T< U >, be::task_pool_t< U > > -{ -}; +template struct is_pool; +template