diff --git a/.gitignore b/.gitignore index 36b5bc4..7f3f27c 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,3 @@ cmake-build-* _private doxygen Doxyfile -tool/cpplipa_*deps.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index 4a09834..368c78e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,7 +16,7 @@ cmake_minimum_required(VERSION 3.16) cmake_policy(VERSION 3.16) -project(dmitigr_cpplipa) +project(dmitigr_libs) if (NOT (UNIX OR WIN32)) message(FATAL_ERROR "Unsupported platform") @@ -24,25 +24,25 @@ endif() list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) include(dmitigr) -include(dmitigr_cpplipa) +include(dmitigr_libs) # ------------------------------------------------------------------------------ # Build options # ------------------------------------------------------------------------------ -set(DMITIGR_CPPLIPA_CLANG_USE_LIBCPP On CACHE BOOL +set(DMITIGR_LIBS_CLANG_USE_LIBCPP On CACHE BOOL "Use libc++ with Clang?") -set(DMITIGR_CPPLIPA_HEADER_ONLY Off CACHE BOOL +set(DMITIGR_LIBS_HEADER_ONLY Off CACHE BOOL "Whole header-only?") -set(DMITIGR_CPPLIPA_DOXYGEN Off CACHE BOOL +set(DMITIGR_LIBS_DOXYGEN Off CACHE BOOL "Build configurations for Doxygen?") -set(DMITIGR_CPPLIPA_TESTS Off CACHE BOOL +set(DMITIGR_LIBS_TESTS Off CACHE BOOL "Build tests?") -set(DMITIGR_CPPLIPA_OPENSSL Off CACHE BOOL +set(DMITIGR_LIBS_OPENSSL Off CACHE BOOL "Link to OpenSSL where possible?") -set(DMITIGR_CPPLIPA_ZLIB Off CACHE BOOL +set(DMITIGR_LIBS_ZLIB Off CACHE BOOL "Link to Zlib where possible?") -set(DMITIGR_CPPLIPA_AIO "uv" CACHE STRING +set(DMITIGR_LIBS_AIO "uv" CACHE STRING "What AIO to use? (\"uv\" - is the only option now.)") set(BUILD_SHARED_LIBS Off CACHE BOOL "Build shared libraries?") @@ -51,7 +51,7 @@ set(CMAKE_VERBOSE_MAKEFILE On CACHE BOOL # Enable linking with libc++ if requested. if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - if (DMITIGR_CPPLIPA_CLANG_USE_LIBCPP) + if (DMITIGR_LIBS_CLANG_USE_LIBCPP) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") endif() endif() @@ -62,57 +62,57 @@ if(NOT DEFINED CMAKE_BUILD_TYPE OR NOT CMAKE_BUILD_TYPE) "Build type: Debug Release RelWithDebInfo MinSizeRel" FORCE) endif() -if (NOT DMITIGR_CPPLIPA_HEADER_ONLY) +if (NOT DMITIGR_LIBS_HEADER_ONLY) if(BUILD_SHARED_LIBS) message("Shared libraries are requested (${CMAKE_BUILD_TYPE})") else() message("Static libraries are requested (${CMAKE_BUILD_TYPE})") endif() elseif(BUILD_SHARED_LIBS) - message(FATAL_ERROR "DMITIGR_CPPLIPA_HEADER_ONLY and BUILD_SHARED_LIBS are mutually exclusive") + message(FATAL_ERROR "DMITIGR_LIBS_HEADER_ONLY and BUILD_SHARED_LIBS are mutually exclusive") else() message("Only 3rdparties without header-only option will be built") endif() -if(DMITIGR_CPPLIPA_DOXYGEN) +if(DMITIGR_LIBS_DOXYGEN) message("Doxygen configurations are requested") endif() -if(DMITIGR_CPPLIPA_TESTS) +if(DMITIGR_LIBS_TESTS) message("Tests are requested (${CMAKE_BUILD_TYPE})") endif() -string(REGEX MATCH "(uv)$" aio "${DMITIGR_CPPLIPA_AIO}") +string(REGEX MATCH "(uv)$" aio "${DMITIGR_LIBS_AIO}") if(NOT aio) - message(FATAL_ERROR "Invalid AIO -- \"${DMITIGR_CPPLIPA_AIO}\"") + message(FATAL_ERROR "Invalid AIO -- \"${DMITIGR_LIBS_AIO}\"") endif() # ------------------------------------------------------------------------------ -set(DMITIGR_CPPLIPA_INSTALL On CACHE BOOL +set(DMITIGR_LIBS_INSTALL On CACHE BOOL "Install the stuff?") if(UNIX) - set(DMITIGR_CPPLIPA_SHARE_INSTALL_DIR "share/dmitigr_cpplipa" CACHE + set(DMITIGR_LIBS_SHARE_INSTALL_DIR "share/dmitigr_libs" CACHE STRING "Name of the installation directory for the shared stuff relative to ${CMAKE_INSTALL_PREFIX}") - set(DMITIGR_CPPLIPA_CMAKE_INSTALL_DIR "${DMITIGR_CPPLIPA_SHARE_INSTALL_DIR}/cmake" CACHE + set(DMITIGR_LIBS_CMAKE_INSTALL_DIR "${DMITIGR_LIBS_SHARE_INSTALL_DIR}/cmake" CACHE STRING "Name of the installation directory for the CMake stuff relative to ${CMAKE_INSTALL_PREFIX}") - set(DMITIGR_CPPLIPA_LIB_INSTALL_DIR "lib" CACHE + set(DMITIGR_LIBS_LIB_INSTALL_DIR "lib" CACHE STRING "Name of the installation directory for the libraries relative to ${CMAKE_INSTALL_PREFIX}") - set(DMITIGR_CPPLIPA_INCLUDE_INSTALL_DIR "include" CACHE + set(DMITIGR_LIBS_INCLUDE_INSTALL_DIR "include" CACHE STRING "Name of the installation directory for the includes relative to ${CMAKE_INSTALL_PREFIX}") elseif(WIN32) # On Windows, CMAKE_INSTALL_PREFIX is $ENV{ProgramFiles}\\${CMAKE_PROJECT_NAME} by default. In turn: # - on AMD64: ProgramFiles=%ProgramFiles% # - on x86: ProgramFiles=%ProgramFiles(x86)% # See: https://msdn.microsoft.com/en-us/library/aa384274.aspx - set(DMITIGR_CPPLIPA_SHARE_INSTALL_DIR "." CACHE + set(DMITIGR_LIBS_SHARE_INSTALL_DIR "." CACHE STRING "Name of the installation directory for the shared stuff relative to ${CMAKE_INSTALL_PREFIX}") - set(DMITIGR_CPPLIPA_CMAKE_INSTALL_DIR "cmake" CACHE + set(DMITIGR_LIBS_CMAKE_INSTALL_DIR "cmake" CACHE STRING "Name of the installation directory for the CMake stuff relative to ${CMAKE_INSTALL_PREFIX}") - set(DMITIGR_CPPLIPA_LIB_INSTALL_DIR "lib" CACHE + set(DMITIGR_LIBS_LIB_INSTALL_DIR "lib" CACHE STRING "Name of the installation directory for the libraries relative to ${CMAKE_INSTALL_PREFIX}") - set(DMITIGR_CPPLIPA_INCLUDE_INSTALL_DIR "include" CACHE + set(DMITIGR_LIBS_INCLUDE_INSTALL_DIR "include" CACHE STRING "Name of the installation directory for the includes relative to ${CMAKE_INSTALL_PREFIX}") endif() @@ -131,18 +131,18 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) # The libraries sources root directory. set(lib_root "src") -# Ensure that dmitigr_cpplipa_libraries is empty! -set(dmitigr_cpplipa_libraries) -foreach(lib ${dmitigr_cpplipa_libraries_all}) +# Ensure that dmitigr_libs_package is empty! +set(dmitigr_libs_package) +foreach(lib ${dmitigr_libs}) set(pref "${CMAKE_CURRENT_SOURCE_DIR}/${lib_root}/${lib}") if(EXISTS "${pref}" OR EXISTS "${pref}.hpp") - list(APPEND dmitigr_cpplipa_libraries ${lib}) + list(APPEND dmitigr_libs_package ${lib}) endif() endforeach() unset(pref) -configure_file(cmake/dmitigr_cpplipa_libraries.cmake.in - ${CMAKE_CURRENT_SOURCE_DIR}/cmake/dmitigr_cpplipa_libraries.cmake @ONLY +configure_file(cmake/dmitigr_libs_package.cmake.in + ${CMAKE_CURRENT_SOURCE_DIR}/cmake/dmitigr_libs_package.cmake @ONLY NEWLINE_STYLE UNIX) # ------------------------------------------------------------------------------ @@ -153,17 +153,17 @@ set(content "#!/bin/bash\n# Copyright (C) Dmitry Igrishin\n\n") set(content "${content}# This file is automatically generated by CMake!\n\n") # Dump library list -set(content "${content}cpplipa_libraries=\"") -foreach(lib ${dmitigr_cpplipa_libraries}) +set(content "${content}igrilibs=\"") +foreach(lib ${dmitigr_libs_package}) set(content "${content}${lib} ") endforeach() string(STRIP "${content}" content) set(content "${content}\"\n") # Dump dependency lists -foreach(lib ${dmitigr_cpplipa_libraries}) +foreach(lib ${dmitigr_libs_package}) set(content "${content}${lib}_deps=\"") - dmitigr_cpplipa_get_deps(lib_deps "${lib}") + dmitigr_libs_get_deps(lib_deps "${lib}") foreach(dep ${lib_deps}) # Check that the dependency is present string(FIND "${dep}" "3rdparty_" pos) @@ -177,32 +177,32 @@ foreach(lib ${dmitigr_cpplipa_libraries}) message(FATAL_ERROR "${lib} is depends on ${dep} which is not present") endif() - # Append the content for cpplipa_deps.sh + # Append the content for igrilibs_deps.sh set(content "${content}${dep} ") endforeach() string(STRIP "${content}" content) set(content "${content}\"\n") endforeach() if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/tool) - file(WRITE tool/cpplipa_deps.sh "${content}") + file(WRITE tool/igrilibs_deps.sh "${content}") endif() unset(content) -message("This library package contains: ${dmitigr_cpplipa_libraries}") +message("This package contains: ${dmitigr_libs_package}") # ------------------------------------------------------------------------------ # Third-party dependencies # ------------------------------------------------------------------------------ -if ("rajson" IN_LIST dmitigr_cpplipa_libraries) +if ("rajson" IN_LIST dmitigr_libs_package) include(dmitigr_3rdparty_rapidjson) endif() -if ("ws" IN_LIST dmitigr_cpplipa_libraries) +if ("ws" IN_LIST dmitigr_libs_package) include(dmitigr_3rdparty_usockets) include(dmitigr_3rdparty_uwebsockets) endif() -if ("wscl" IN_LIST dmitigr_cpplipa_libraries) +if ("wscl" IN_LIST dmitigr_libs_package) include(dmitigr_3rdparty_uwsc) endif() @@ -216,33 +216,33 @@ endif() set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") if (WIN32) - set(dmitigr_cpplipa_resource_destination_dir "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$") + set(dmitigr_libs_resource_destination_dir "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$") elseif (UNIX) - set(dmitigr_cpplipa_resource_destination_dir "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}") + set(dmitigr_libs_resource_destination_dir "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}") endif() # ------------------------------------------------------------------------------ # Custom targets # ------------------------------------------------------------------------------ -add_custom_target(dmitigr_cpplipa_uninstall) +add_custom_target(dmitigr_libs_uninstall) -add_custom_target(dmitigr_cpplipa_create_resource_destination_dir ALL - COMMAND cmake -E make_directory "${dmitigr_cpplipa_resource_destination_dir}" +add_custom_target(dmitigr_libs_create_resource_destination_dir ALL + COMMAND cmake -E make_directory "${dmitigr_libs_resource_destination_dir}" ) # -------------------------------------- # Installing # -------------------------------------- -if(DMITIGR_CPPLIPA_INSTALL) +if(DMITIGR_LIBS_INSTALL) install(FILES LICENSE.txt - DESTINATION ${DMITIGR_CPPLIPA_SHARE_INSTALL_DIR}) + DESTINATION ${DMITIGR_LIBS_SHARE_INSTALL_DIR}) install(FILES - cmake/dmitigr_cpplipa_libraries.cmake - cmake/dmitigr_cpplipa_libraries_all.cmake - cmake/dmitigr_cpplipa-config.cmake - DESTINATION ${DMITIGR_CPPLIPA_CMAKE_INSTALL_DIR}) + cmake/dmitigr_libs.cmake + cmake/dmitigr_libs_package.cmake + cmake/dmitigr_libs-config.cmake + DESTINATION ${DMITIGR_LIBS_CMAKE_INSTALL_DIR}) endif() # ------------------------------------------------------------------------------ @@ -255,11 +255,11 @@ function(dmitigr_install_lib_includes file_list) string(REGEX REPLACE "^${lib_root}/" "" file "${file}") get_filename_component(dir ${file} DIRECTORY) install(FILES ${lib_root}/${file} - DESTINATION ${DMITIGR_CPPLIPA_INCLUDE_INSTALL_DIR}/dmitigr/${dir}) + DESTINATION ${DMITIGR_LIBS_INCLUDE_INSTALL_DIR}/dmitigr/${dir}) endforeach() endfunction() -foreach(lib ${dmitigr_cpplipa_libraries}) +foreach(lib ${dmitigr_libs_package}) string(TOUPPER "${lib}" LIB) set(dmlib dmitigr_${lib}) @@ -267,13 +267,13 @@ foreach(lib ${dmitigr_cpplipa_libraries}) include(${dmlib}) - dmitigr_cpplipa_set_library_info_lib_variables(${lib}) + dmitigr_libs_set_library_info_lib_variables(${lib}) # ------------- # Documentation # ------------- - if(DMITIGR_CPPLIPA_DOXYGEN) + if(DMITIGR_LIBS_DOXYGEN) configure_file(Doxyfile.in ${CMAKE_CURRENT_SOURCE_DIR}/doxygen/${lib}/Doxyfile @ONLY NEWLINE_STYLE UNIX) @@ -302,7 +302,7 @@ foreach(lib ${dmitigr_cpplipa_libraries}) list(APPEND ${dmlib}_headers ${file}) endforeach() - set(${dmlib}_header_only ${DMITIGR_CPPLIPA_HEADER_ONLY}) + set(${dmlib}_header_only ${DMITIGR_LIBS_HEADER_ONLY}) else() set(${dmlib}_header_only TRUE) endif() @@ -362,7 +362,7 @@ foreach(lib ${dmitigr_cpplipa_libraries}) message(FATAL_ERROR "Variable ${dmlib}_version_major is not defined") endif() - foreach(st ${dmitigr_cpplipa_source_types}) + foreach(st ${dmitigr_libs_source_types}) list(TRANSFORM ${dmlib}_${st} PREPEND "${lib_root}/${lib}/") list(APPEND ${dmlib}_sources ${${dmlib}_${st}}) endforeach() @@ -406,7 +406,7 @@ foreach(lib ${dmitigr_cpplipa_libraries}) # DEBUG_POSTFIX "d" ) - dmitigr_cpplipa_target_compile_options(${dmlib}) + dmitigr_libs_target_compile_options(${dmlib}) endif() if(NOT ${${dmlib}_header_only}) @@ -430,7 +430,7 @@ foreach(lib ${dmitigr_cpplipa_libraries}) # ------------------------------------ if(NOT ${${dmlib}_header_only}) - foreach(dep ${dmitigr_cpplipa_${lib}_deps}) + foreach(dep ${dmitigr_libs_${lib}_deps}) target_link_libraries(${dmlib} PUBLIC dmitigr_${dep}) endforeach() @@ -439,7 +439,7 @@ foreach(lib ${dmitigr_cpplipa_libraries}) PRIVATE ${${dmlib}_target_link_libraries_private} PUBLIC ${${dmlib}_target_link_libraries_public}) else() # header-only - foreach(dep ${dmitigr_cpplipa_${lib}_deps}) + foreach(dep ${dmitigr_libs_${lib}_deps}) target_link_libraries(${dmlib} INTERFACE dmitigr_${dep}) endforeach() @@ -452,7 +452,7 @@ foreach(lib ${dmitigr_cpplipa_libraries}) # Installing # ------------------------------------ - if(DMITIGR_CPPLIPA_INSTALL) + if(DMITIGR_LIBS_INSTALL) dmitigr_install_lib_includes(${dmlib}_headers) if(${${dmlib}_header_only}) dmitigr_install_lib_includes(${dmlib}_implementations) @@ -461,20 +461,20 @@ foreach(lib ${dmitigr_cpplipa_libraries}) if(NOT ${${dmlib}_header_only}) install(TARGETS ${dmlib} EXPORT ${dmlib}_export - ARCHIVE DESTINATION ${DMITIGR_CPPLIPA_LIB_INSTALL_DIR} - LIBRARY DESTINATION ${DMITIGR_CPPLIPA_LIB_INSTALL_DIR} - RUNTIME DESTINATION ${DMITIGR_CPPLIPA_LIB_INSTALL_DIR}) + ARCHIVE DESTINATION ${DMITIGR_LIBS_LIB_INSTALL_DIR} + LIBRARY DESTINATION ${DMITIGR_LIBS_LIB_INSTALL_DIR} + RUNTIME DESTINATION ${DMITIGR_LIBS_LIB_INSTALL_DIR}) else() install(TARGETS ${dmlib} EXPORT ${dmlib}_export - INCLUDES DESTINATION ${DMITIGR_CPPLIPA_INCLUDE_INSTALL_DIR}) + INCLUDES DESTINATION ${DMITIGR_LIBS_INCLUDE_INSTALL_DIR}) endif() # Don't use NAMESPACE, since ${dmlib} contains "dmitigr_" prefix already # and it seems there is no way to omit it in order to use NAMESPACE... install(EXPORT ${dmlib}_export # NAMESPACE dmitigr:: - DESTINATION ${DMITIGR_CPPLIPA_CMAKE_INSTALL_DIR} + DESTINATION ${DMITIGR_LIBS_CMAKE_INSTALL_DIR} FILE ${dmlib}_${export_file_suffix}-config.cmake) endif() @@ -482,20 +482,20 @@ foreach(lib ${dmitigr_cpplipa_libraries}) # Uninstalling # ------------------------------------ - add_custom_command(TARGET dmitigr_cpplipa_uninstall PRE_BUILD - COMMAND cmake -E rm -f ${CMAKE_INSTALL_PREFIX}/${DMITIGR_CPPLIPA_CMAKE_INSTALL_DIR}/${dmlib}* - COMMAND cmake -E rm -f ${CMAKE_INSTALL_PREFIX}/${DMITIGR_CPPLIPA_LIB_INSTALL_DIR}/${dmlib}* - COMMAND cmake -E rm -f ${CMAKE_INSTALL_PREFIX}/${DMITIGR_CPPLIPA_LIB_INSTALL_DIR}/lib${dmlib}* - COMMAND cmake -E rm -rf ${CMAKE_INSTALL_PREFIX}/${DMITIGR_CPPLIPA_INCLUDE_INSTALL_DIR}/dmitigr/${lib}* - COMMAND cmake -E rm -rf ${CMAKE_INSTALL_PREFIX}/${DMITIGR_CPPLIPA_INCLUDE_INSTALL_DIR}/dmitigr/${lib}) + add_custom_command(TARGET dmitigr_libs_uninstall PRE_BUILD + COMMAND cmake -E rm -f ${CMAKE_INSTALL_PREFIX}/${DMITIGR_LIBS_CMAKE_INSTALL_DIR}/${dmlib}* + COMMAND cmake -E rm -f ${CMAKE_INSTALL_PREFIX}/${DMITIGR_LIBS_LIB_INSTALL_DIR}/${dmlib}* + COMMAND cmake -E rm -f ${CMAKE_INSTALL_PREFIX}/${DMITIGR_LIBS_LIB_INSTALL_DIR}/lib${dmlib}* + COMMAND cmake -E rm -rf ${CMAKE_INSTALL_PREFIX}/${DMITIGR_LIBS_INCLUDE_INSTALL_DIR}/dmitigr/${lib}* + COMMAND cmake -E rm -rf ${CMAKE_INSTALL_PREFIX}/${DMITIGR_LIBS_INCLUDE_INSTALL_DIR}/dmitigr/${lib}) - dmitigr_cpplipa_get_deps(res ${lib}) + dmitigr_libs_get_deps(res ${lib}) foreach(dep ${res}) string(FIND "${dep}" "3rdparty_" pos) if (pos EQUAL 0) string(SUBSTRING "${dep}" 11 -1 dep) - add_custom_command(TARGET dmitigr_cpplipa_uninstall PRE_BUILD - COMMAND cmake -E rm -rf ${CMAKE_INSTALL_PREFIX}/${DMITIGR_CPPLIPA_INCLUDE_INSTALL_DIR}/dmitigr/3rdparty/*${dep}*) + add_custom_command(TARGET dmitigr_libs_uninstall PRE_BUILD + COMMAND cmake -E rm -rf ${CMAKE_INSTALL_PREFIX}/${DMITIGR_LIBS_INCLUDE_INSTALL_DIR}/dmitigr/3rdparty/*${dep}*) endif() endforeach() endforeach() @@ -504,7 +504,7 @@ endforeach() # Tests # ------------------------------------------------------------------------------ -if(DMITIGR_CPPLIPA_TESTS) +if(DMITIGR_LIBS_TESTS) enable_testing() function(dmitigr_configure_test lib test) @@ -523,17 +523,19 @@ if(DMITIGR_CPPLIPA_TESTS) ${${dmlib}_tests_target_link_libraries} ${${dmlib}_test_${test}_target_link_libraries}) target_compile_definitions(${exe} PRIVATE - ${${dmlib}_tests_target_compile_definitions}) + ${${dmlib}_tests_target_compile_definitions} + ${${dmlib}_test_${test}_target_compile_definitions}) target_compile_options(${exe} PRIVATE - "${${dmlib}_tests_target_compile_options}") - dmitigr_cpplipa_target_compile_options(${exe}) + ${${dmlib}_tests_target_compile_options} + ${${dmlib}_test_${test}_target_compile_options}) + dmitigr_libs_target_compile_options(${exe}) if(is_unit_test) add_test(NAME ${exe} COMMAND ${exe}) endif() endfunction() # Get the actual list of libraries with tests and configure these tests - foreach(lib ${dmitigr_cpplipa_libraries}) + foreach(lib ${dmitigr_libs_package}) set(dmlib dmitigr_${lib}) if(${dmlib}_tests) list(APPEND libraries_with_tests ${lib}) @@ -566,7 +568,7 @@ if(DMITIGR_CPPLIPA_TESTS) # Dump list of libraries with tests set(content "#!/bin/bash\n# Copyright (C) Dmitry Igrishin\n\n") set(content "${content}# This file is automatically generated by CMake!\n\n") - set(content "${content}cpplipa_libraries_with_tests=\"") + set(content "${content}igrilibs_with_tests=\"") foreach(lib ${libraries_with_tests}) set(content "${content}${lib} ") endforeach() @@ -585,7 +587,7 @@ if(DMITIGR_CPPLIPA_TESTS) # Write content if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/tool) - file(WRITE tool/cpplipa_test_deps.sh "${content}") + file(WRITE tool/igrilibs_test_deps.sh "${content}") endif() unset(content) endif() diff --git a/README.md b/README.md index 3f81c0a..8a9ecaf 100644 --- a/README.md +++ b/README.md @@ -142,7 +142,7 @@ Create `hello/CMakeLists.txt`: ```cmake cmake_minimum_required(VERSION 3.16) project(foo) -find_package(dmitigr_cpplipa REQUIRED COMPONENTS pgfe) +find_package(dmitigr_libs REQUIRED COMPONENTS pgfe) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) add_executable(hello hello.cpp) @@ -159,7 +159,7 @@ cmake --build . ### Advanced usage -For more details please, see [usage section][dmitigr_cpplipa_usage] for hints +For more details please, see [usage section][dmitigr_libs_usage] for hints how to link Pgfe. ## Quick tutorial @@ -185,7 +185,7 @@ auto make_options() { return Connection_options{} .set(Communication_mode::net) - .set_net_hostname("localhost") + .set_hostname("localhost") .set_database("db") .set_username("user") .set_password("password"); @@ -622,8 +622,8 @@ Pgfe is depends on the [libpq] library. ## CMake options -Since Pgfe is a C++ library subpackage of [Cpplipa][dmitigr_cpplipa], almost -all the [CMake options of Cpplipa][dmitigr_cpplipa_cmake_options] are applicable +Since Pgfe is a C++ library subpackage of [Igrilibs][dmitigr_libs], almost +all the [CMake options of Igrilibs][dmitigr_libs_cmake_options] are applicable to Pgfe. Please, pay attention to the following: @@ -636,9 +636,9 @@ Please, pay attention to the following: doesn't selects the build configuration within the generated build environment. The [CMake] command line option `--config` should be used for that purpose. -[dmitigr_cpplipa]: https://github.com/dmitigr/cpplipa -[dmitigr_cpplipa_cmake_options]: https://github.com/dmitigr/cpplipa#cmake-options -[dmitigr_cpplipa_usage]: https://github.com/dmitigr/cpplipa.git#usage +[dmitigr_libs]: https://github.com/dmitigr/igrilibs +[dmitigr_libs_cmake_options]: https://github.com/dmitigr/igrilibs#cmake-options +[dmitigr_libs_usage]: https://github.com/dmitigr/igrilibs.git#usage [dmitigr_pgfe]: https://github.com/dmitigr/pgfe.git [PostgreSQL]: https://www.postgresql.org/ diff --git a/cmake/dmitigr.cmake b/cmake/dmitigr.cmake index 7848575..30b8c6e 100644 --- a/cmake/dmitigr.cmake +++ b/cmake/dmitigr.cmake @@ -20,7 +20,7 @@ function(dmitigr_append_cppfs libraries) list(APPEND ${libraries} stdc++fs) endif() elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - if (DMITIGR_CPPLIPA_CLANG_USE_LIBCPP) + if (DMITIGR_LIBS_CLANG_USE_LIBCPP) if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "7") list(APPEND ${libraries} c++experimental) elseif(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "9") diff --git a/cmake/dmitigr_base.cmake b/cmake/dmitigr_base.cmake index fb2e5f4..0e4275a 100644 --- a/cmake/dmitigr_base.cmake +++ b/cmake/dmitigr_base.cmake @@ -18,7 +18,7 @@ # Info # ------------------------------------------------------------------------------ -dmitigr_cpplipa_set_library_info(base 0 0 0 "Base stuff") +dmitigr_libs_set_library_info(base 0 0 0 "Base stuff") # ------------------------------------------------------------------------------ # Sources @@ -26,7 +26,11 @@ dmitigr_cpplipa_set_library_info(base 0 0 0 "Base stuff") set(dmitigr_base_headers assert.hpp + endianness.hpp + enum_bitmask.hpp + err.hpp errc.hpp errctg.hpp exceptions.hpp + ret.hpp ) diff --git a/cmake/dmitigr_cpplipa_libraries_all.cmake b/cmake/dmitigr_cpplipa_libraries_all.cmake deleted file mode 100644 index 86f4b8e..0000000 --- a/cmake/dmitigr_cpplipa_libraries_all.cmake +++ /dev/null @@ -1,73 +0,0 @@ -# -*- cmake -*- -# -# Copyright 2022 Dmitry Igrishin -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# ------------------------------------------------------------------------------ -# Library list -# ------------------------------------------------------------------------------ - -set(dmitigr_cpplipa_libraries_all - # Level 0 (base level) - base - # Level 1 - algo concur dt fs hsh math os que rajson rnd str ttpl util uv - # Level 2 - mulf net prg sqlixx url - # Level 3 - jrpc ws wscl - # Level 4 - fcgi http pgfe - # Level 5 - web - ) - -# ------------------------------------------------------------------------------ -# Dependency lists -# ------------------------------------------------------------------------------ - -# Third-parties -set(dmitigr_cpplipa_3rdparty_uwebsockets_deps 3rdparty_usockets) - -# Level 1 -set(dmitigr_cpplipa_algo_deps base) -set(dmitigr_cpplipa_concur_deps base) -set(dmitigr_cpplipa_dt_deps base) -set(dmitigr_cpplipa_fs_deps) -set(dmitigr_cpplipa_hsh_deps) -set(dmitigr_cpplipa_math_deps base) -set(dmitigr_cpplipa_os_deps base) -set(dmitigr_cpplipa_que_deps) -set(dmitigr_cpplipa_rajson_deps base fs 3rdparty_rapidjson) -set(dmitigr_cpplipa_rnd_deps base) -set(dmitigr_cpplipa_str_deps base) -set(dmitigr_cpplipa_ttpl_deps base) -set(dmitigr_cpplipa_util_deps base) -set(dmitigr_cpplipa_uv_deps base) -# Level 2 -set(dmitigr_cpplipa_mulf_deps base str) -set(dmitigr_cpplipa_net_deps base fs os util) -set(dmitigr_cpplipa_prg_deps base fs os rajson str) -set(dmitigr_cpplipa_sqlixx_deps base fs) -set(dmitigr_cpplipa_url_deps base str) -# Level 3 -set(dmitigr_cpplipa_jrpc_deps base algo math rajson str) -set(dmitigr_cpplipa_ws_deps base fs net 3rdparty_uwebsockets) -set(dmitigr_cpplipa_wscl_deps base net 3rdparty_uwsc) -# Level 4 -set(dmitigr_cpplipa_fcgi_deps base fs math net) -set(dmitigr_cpplipa_http_deps base dt net str) -set(dmitigr_cpplipa_pgfe_deps base fs net str util) -# Level 5 -set(dmitigr_cpplipa_web_deps base fcgi fs http jrpc mulf str ttpl) diff --git a/cmake/dmitigr_fs.cmake b/cmake/dmitigr_fs.cmake index ef4caa0..40e9fee 100644 --- a/cmake/dmitigr_fs.cmake +++ b/cmake/dmitigr_fs.cmake @@ -18,7 +18,7 @@ # Info # ------------------------------------------------------------------------------ -dmitigr_cpplipa_set_library_info(fs 0 0 0 "Standard filesystem extensions") +dmitigr_libs_set_library_info(fs 0 0 0 "Standard filesystem extensions") # ------------------------------------------------------------------------------ # Sources diff --git a/cmake/dmitigr_cpplipa-config.cmake b/cmake/dmitigr_libs-config.cmake similarity index 75% rename from cmake/dmitigr_cpplipa-config.cmake rename to cmake/dmitigr_libs-config.cmake index 2c836fb..4172f20 100644 --- a/cmake/dmitigr_cpplipa-config.cmake +++ b/cmake/dmitigr_libs-config.cmake @@ -14,10 +14,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -function(dmitigr_cpplipa_load_with_deps component) +function(dmitigr_libs_load_with_deps component) # Loading the component's dependencies - foreach(dep ${dmitigr_cpplipa_${component}_deps}) - dmitigr_cpplipa_load_with_deps(${dep}) + foreach(dep ${dmitigr_libs_${component}_deps}) + dmitigr_libs_load_with_deps(${dep}) endforeach() # Loading the component @@ -42,13 +42,13 @@ endfunction() # ------------------------------------------------------------------------------ -include(${CMAKE_CURRENT_LIST_DIR}/dmitigr_cpplipa_libraries.cmake) -include(${CMAKE_CURRENT_LIST_DIR}/dmitigr_cpplipa_libraries_all.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/dmitigr_libs.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/dmitigr_libs_package.cmake) -if(NOT dmitigr_cpplipa_FIND_COMPONENTS) - set(dmitigr_cpplipa_FIND_COMPONENTS ${dmitigr_cpplipa_libraries}) +if(NOT dmitigr_libs_FIND_COMPONENTS) + set(dmitigr_libs_FIND_COMPONENTS ${dmitigr_libs_package}) endif() -foreach(component ${dmitigr_cpplipa_FIND_COMPONENTS}) - dmitigr_cpplipa_load_with_deps(${component}) +foreach(component ${dmitigr_libs_FIND_COMPONENTS}) + dmitigr_libs_load_with_deps(${component}) endforeach() diff --git a/cmake/dmitigr_cpplipa.cmake b/cmake/dmitigr_libs.cmake similarity index 70% rename from cmake/dmitigr_cpplipa.cmake rename to cmake/dmitigr_libs.cmake index 5c762f8..c748b08 100644 --- a/cmake/dmitigr_cpplipa.cmake +++ b/cmake/dmitigr_libs.cmake @@ -24,13 +24,73 @@ # dmitigr_${lib}_test_${test}_target_link_libraries # dmitigr_${lib}_test_${test}_target_compile_definitions -include(${CMAKE_CURRENT_LIST_DIR}/dmitigr_cpplipa_libraries_all.cmake) +# ------------------------------------------------------------------------------ +# Library list +# ------------------------------------------------------------------------------ + +set(dmitigr_libs + # Level 0 (base level) + base + # Level 1 + algo dt fs hsh ipc math os que rnd str tpl util uv + # Level 2 + log mulf net rajson sqlixx url + # Level 3 + concur fcgi http jrpc msg pgfe prg ws wscl + # Level 4 + lisp + # Level 5 + web + ) + +# ------------------------------------------------------------------------------ +# Dependency lists +# ------------------------------------------------------------------------------ + +# Third-parties +set(dmitigr_libs_3rdparty_uwebsockets_deps 3rdparty_usockets) + +# Level 1 +set(dmitigr_libs_algo_deps base) +set(dmitigr_libs_dt_deps base) +set(dmitigr_libs_fs_deps) +set(dmitigr_libs_hsh_deps) +set(dmitigr_libs_ipc_deps base) +set(dmitigr_libs_math_deps base) +set(dmitigr_libs_os_deps base) +set(dmitigr_libs_que_deps) +set(dmitigr_libs_rnd_deps base) +set(dmitigr_libs_str_deps base) +set(dmitigr_libs_tpl_deps base) +set(dmitigr_libs_util_deps base) +set(dmitigr_libs_uv_deps base) +# Level 2 +set(dmitigr_libs_log_deps base fs os str) +set(dmitigr_libs_mulf_deps base str) +set(dmitigr_libs_net_deps base fs os) +set(dmitigr_libs_rajson_deps base fs 3rdparty_rapidjson) +set(dmitigr_libs_sqlixx_deps base fs) +set(dmitigr_libs_url_deps base str) +# Level 3 +set(dmitigr_libs_concur_deps base log) +set(dmitigr_libs_fcgi_deps base fs math net) +set(dmitigr_libs_http_deps base dt net str) +set(dmitigr_libs_jrpc_deps base algo math rajson str) +set(dmitigr_libs_msg_deps base fs ipc os rajson sqlixx) +set(dmitigr_libs_pgfe_deps base fs net str util) +set(dmitigr_libs_prg_deps base fs log os rajson str) +set(dmitigr_libs_ws_deps base fs http net 3rdparty_uwebsockets) +set(dmitigr_libs_wscl_deps base net 3rdparty_uwsc) +# Level 4 +set(dmitigr_libs_lisp_deps base) +# Level 5 +set(dmitigr_libs_web_deps base concur fs http jrpc lisp log prg str tpl url ws) # ------------------------------------------------------------------------------ # Source type list # ------------------------------------------------------------------------------ -set(dmitigr_cpplipa_source_types +set(dmitigr_libs_source_types headers build_only_sources implementations @@ -42,10 +102,10 @@ set(dmitigr_cpplipa_source_types # Dependency related stuff # ------------------------------------------------------------------------------ -function(dmitigr_cpplipa_get_deps res_var lib) - foreach(dep ${dmitigr_cpplipa_${lib}_deps}) +function(dmitigr_libs_get_deps res_var lib) + foreach(dep ${dmitigr_libs_${lib}_deps}) # Getting dependencies of dep - dmitigr_cpplipa_get_deps(dep_deps ${dep}) + dmitigr_libs_get_deps(dep_deps ${dep}) # Adding dependencies of dep to the result foreach(d ${dep_deps}) @@ -67,7 +127,7 @@ endfunction() # Target compile options # ------------------------------------------------------------------------------ -function(dmitigr_cpplipa_target_compile_options t) +function(dmitigr_libs_target_compile_options t) if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") target_compile_options(${t} PRIVATE -pedantic @@ -111,7 +171,7 @@ endfunction() # ------------------------------------------------------------------------------ -macro(dmitigr_cpplipa_set_library_info lib +macro(dmitigr_libs_set_library_info lib version_major version_minor version_patch description) if((${version_major} LESS 0) OR (${version_minor} LESS 0) OR (${version_patch} LESS 0)) @@ -141,7 +201,7 @@ endmacro() # ------------------------------------------------------------------------------ -macro(dmitigr_cpplipa_set_library_info_lib_variables lib) +macro(dmitigr_libs_set_library_info_lib_variables lib) set(dmitigr_lib_name ${dmitigr_${lib}_name}) set(dmitigr_lib_NAME ${dmitigr_${lib}_NAME}) set(dmitigr_lib_Name ${dmitigr_${lib}_Name}) diff --git a/cmake/dmitigr_cpplipa_libraries.cmake.in b/cmake/dmitigr_libs_package.cmake similarity index 90% rename from cmake/dmitigr_cpplipa_libraries.cmake.in rename to cmake/dmitigr_libs_package.cmake index 4d8b770..c225216 100644 --- a/cmake/dmitigr_cpplipa_libraries.cmake.in +++ b/cmake/dmitigr_libs_package.cmake @@ -14,4 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -set(dmitigr_cpplipa_libraries "@dmitigr_cpplipa_libraries@") +set(dmitigr_libs_package "base;fs;os;str;util;net;pgfe") diff --git a/cmake/dmitigr_libs_package.cmake.in b/cmake/dmitigr_libs_package.cmake.in new file mode 100644 index 0000000..450487f --- /dev/null +++ b/cmake/dmitigr_libs_package.cmake.in @@ -0,0 +1,17 @@ +# -*- cmake -*- +# +# Copyright 2022 Dmitry Igrishin +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set(dmitigr_libs_package "@dmitigr_libs_package@") diff --git a/cmake/dmitigr_net.cmake b/cmake/dmitigr_net.cmake index aaf11f6..3503737 100644 --- a/cmake/dmitigr_net.cmake +++ b/cmake/dmitigr_net.cmake @@ -18,7 +18,7 @@ # Info # ------------------------------------------------------------------------------ -dmitigr_cpplipa_set_library_info(net 0 0 0 "Networking") +dmitigr_libs_set_library_info(net 0 0 0 "Networking") # ------------------------------------------------------------------------------ # Sources @@ -59,7 +59,7 @@ endif() # Tests # ------------------------------------------------------------------------------ -if(DMITIGR_CPPLIPA_TESTS) +if(DMITIGR_LIBS_TESTS) if(UNIX AND NOT CMAKE_SYSTEM_NAME MATCHES MSYS|MinGW|Cygwin) set(dmitigr_net_tests net) set(dmitigr_net_tests_target_link_libraries dmitigr_base) diff --git a/cmake/dmitigr_os.cmake b/cmake/dmitigr_os.cmake index 43632fb..e4e4fb2 100644 --- a/cmake/dmitigr_os.cmake +++ b/cmake/dmitigr_os.cmake @@ -18,7 +18,7 @@ # Info # ------------------------------------------------------------------------------ -dmitigr_cpplipa_set_library_info(os 0 0 0 "OS basics") +dmitigr_libs_set_library_info(os 0 0 0 "OS basics") # ------------------------------------------------------------------------------ # Sources diff --git a/cmake/dmitigr_pgfe.cmake b/cmake/dmitigr_pgfe.cmake index 1453637..8dab0d6 100644 --- a/cmake/dmitigr_pgfe.cmake +++ b/cmake/dmitigr_pgfe.cmake @@ -18,7 +18,7 @@ # Info # ------------------------------------------------------------------------------ -dmitigr_cpplipa_set_library_info(pgfe 2 0 1 "PostgreSQL client API") +dmitigr_libs_set_library_info(pgfe 2 0 1 "PostgreSQL client API") # ------------------------------------------------------------------------------ # Sources @@ -117,7 +117,7 @@ endif() # Tests # ------------------------------------------------------------------------------ -if(DMITIGR_CPPLIPA_TESTS) +if(DMITIGR_LIBS_TESTS) set(dmitigr_pgfe_tests array_dimension benchmark_array_client @@ -149,12 +149,12 @@ if(DMITIGR_CPPLIPA_TESTS) set(dmitigr_pgfe_tests_target_link_libraries dmitigr_base dmitigr_os dmitigr_str dmitigr_util) - set(prefix ${dmitigr_cpplipa_SOURCE_DIR}/test/pgfe) + set(prefix ${dmitigr_libs_SOURCE_DIR}/test/pgfe) add_custom_target(dmitigr_pgfe_copy_test_resources ALL COMMAND cmake -E copy_if_different "${prefix}/pgfe-unit-statement_vector.sql" - "${dmitigr_cpplipa_resource_destination_dir}" + "${dmitigr_libs_resource_destination_dir}" ) add_dependencies(dmitigr_pgfe_copy_test_resources - dmitigr_cpplipa_create_resource_destination_dir) + dmitigr_libs_create_resource_destination_dir) endif() diff --git a/cmake/dmitigr_str.cmake b/cmake/dmitigr_str.cmake index 247b5c5..dd1d773 100644 --- a/cmake/dmitigr_str.cmake +++ b/cmake/dmitigr_str.cmake @@ -18,20 +18,21 @@ # Info # ------------------------------------------------------------------------------ -dmitigr_cpplipa_set_library_info(str 0 0 0 "Standard string extensions") +dmitigr_libs_set_library_info(str 0 0 0 "Standard string extensions") # ------------------------------------------------------------------------------ # Sources # ------------------------------------------------------------------------------ set(dmitigr_str_headers + basics.hpp + c_str.h c_str.hpp exceptions.hpp line.hpp numeric.hpp predicate.hpp sequence.hpp - simple_phrase.hpp stream.hpp substr.hpp time.hpp @@ -42,7 +43,7 @@ set(dmitigr_str_headers # Tests # ------------------------------------------------------------------------------ -if(DMITIGR_CPPLIPA_TESTS) - set(dmitigr_str_tests test) +if(DMITIGR_LIBS_TESTS) + set(dmitigr_str_tests test time) set(dmitigr_str_tests_target_link_libraries dmitigr_base) endif() diff --git a/cmake/dmitigr_util.cmake b/cmake/dmitigr_util.cmake index 96a8f09..5c5f51f 100644 --- a/cmake/dmitigr_util.cmake +++ b/cmake/dmitigr_util.cmake @@ -18,7 +18,7 @@ # Info # ------------------------------------------------------------------------------ -dmitigr_cpplipa_set_library_info(util 0 0 0 "Utilities") +dmitigr_libs_set_library_info(util 0 0 0 "Utilities") # ------------------------------------------------------------------------------ # Sources @@ -35,6 +35,6 @@ set(dmitigr_util_headers # Tests # ------------------------------------------------------------------------------ -if(DMITIGR_CPPLIPA_TESTS) +if(DMITIGR_LIBS_TESTS) set(dmitigr_util_tests diag) endif() diff --git a/doc/pgfe/README.md b/doc/pgfe/README.md index 3f81c0a..8a9ecaf 100644 --- a/doc/pgfe/README.md +++ b/doc/pgfe/README.md @@ -142,7 +142,7 @@ Create `hello/CMakeLists.txt`: ```cmake cmake_minimum_required(VERSION 3.16) project(foo) -find_package(dmitigr_cpplipa REQUIRED COMPONENTS pgfe) +find_package(dmitigr_libs REQUIRED COMPONENTS pgfe) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) add_executable(hello hello.cpp) @@ -159,7 +159,7 @@ cmake --build . ### Advanced usage -For more details please, see [usage section][dmitigr_cpplipa_usage] for hints +For more details please, see [usage section][dmitigr_libs_usage] for hints how to link Pgfe. ## Quick tutorial @@ -185,7 +185,7 @@ auto make_options() { return Connection_options{} .set(Communication_mode::net) - .set_net_hostname("localhost") + .set_hostname("localhost") .set_database("db") .set_username("user") .set_password("password"); @@ -622,8 +622,8 @@ Pgfe is depends on the [libpq] library. ## CMake options -Since Pgfe is a C++ library subpackage of [Cpplipa][dmitigr_cpplipa], almost -all the [CMake options of Cpplipa][dmitigr_cpplipa_cmake_options] are applicable +Since Pgfe is a C++ library subpackage of [Igrilibs][dmitigr_libs], almost +all the [CMake options of Igrilibs][dmitigr_libs_cmake_options] are applicable to Pgfe. Please, pay attention to the following: @@ -636,9 +636,9 @@ Please, pay attention to the following: doesn't selects the build configuration within the generated build environment. The [CMake] command line option `--config` should be used for that purpose. -[dmitigr_cpplipa]: https://github.com/dmitigr/cpplipa -[dmitigr_cpplipa_cmake_options]: https://github.com/dmitigr/cpplipa#cmake-options -[dmitigr_cpplipa_usage]: https://github.com/dmitigr/cpplipa.git#usage +[dmitigr_libs]: https://github.com/dmitigr/igrilibs +[dmitigr_libs_cmake_options]: https://github.com/dmitigr/igrilibs#cmake-options +[dmitigr_libs_usage]: https://github.com/dmitigr/igrilibs.git#usage [dmitigr_pgfe]: https://github.com/dmitigr/pgfe.git [PostgreSQL]: https://www.postgresql.org/ diff --git a/src/base/base.hpp b/src/base/base.hpp index 76d0c1d..dccb862 100644 --- a/src/base/base.hpp +++ b/src/base/base.hpp @@ -18,8 +18,11 @@ #define DMITIGR_BASE_BASE_HPP #include "assert.hpp" +#include "enum_bitmask.hpp" +#include "err.hpp" #include "errc.hpp" #include "errctg.hpp" #include "exceptions.hpp" +#include "ret.hpp" #endif // DMITIGR_BASE_BASE_HPP diff --git a/src/util/endianness.hpp b/src/base/endianness.hpp similarity index 86% rename from src/util/endianness.hpp rename to src/base/endianness.hpp index ca858e0..61190e7 100644 --- a/src/util/endianness.hpp +++ b/src/base/endianness.hpp @@ -14,10 +14,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef DMITIGR_UTIL_ENDIANNESS_HPP -#define DMITIGR_UTIL_ENDIANNESS_HPP +#ifndef DMITIGR_BASE_ENDIANNESS_HPP +#define DMITIGR_BASE_ENDIANNESS_HPP -namespace dmitigr::util { +namespace dmitigr { /// An endianness. enum class Endianness { @@ -39,6 +39,6 @@ inline Endianness endianness() noexcept return result; } -} // namespace dmitigr::util +} // namespace dmitigr -#endif // DMITIGR_UTIL_ENDIANNESS_HPP +#endif // DMITIGR_BASE_ENDIANNESS_HPP diff --git a/src/util/enum_bitmask.hpp b/src/base/enum_bitmask.hpp similarity index 86% rename from src/util/enum_bitmask.hpp rename to src/base/enum_bitmask.hpp index b10d12d..3ad6296 100644 --- a/src/util/enum_bitmask.hpp +++ b/src/base/enum_bitmask.hpp @@ -14,12 +14,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef DMITIGR_UTIL_ENUM_BITMASK_HPP -#define DMITIGR_UTIL_ENUM_BITMASK_HPP +#ifndef DMITIGR_BASE_ENUM_BITMASK_HPP +#define DMITIGR_BASE_ENUM_BITMASK_HPP #include -namespace dmitigr::util { +namespace dmitigr { /// Structure inspired by the exposition from 14822:2014 17.5.2.1.3. template struct Is_bitmask_enum : std::false_type {}; @@ -84,11 +84,11 @@ operator^=(T& lhs, const T rhs) noexcept return lhs = (lhs ^ rhs); } -} // namespace dmitigr::util +} // namespace dmitigr /** - * @brief The helper macro for defining enum bitmask operators above in any - * namespace. + * @brief The helper macro for defining enum bitmask operators + * above in any namespace. * * @param T The type name. */ @@ -96,43 +96,43 @@ operator^=(T& lhs, const T rhs) noexcept /** Bitwise AND for `T`. */ \ constexpr T operator&(const T lhs, const T rhs) noexcept \ { \ - return dmitigr::util::operator&(lhs, rhs); \ + return dmitigr::operator&(lhs, rhs); \ } \ \ /** Bitwise OR for `T`. */ \ constexpr T operator|(const T lhs, const T rhs) noexcept \ { \ - return dmitigr::util::operator|(lhs, rhs); \ + return dmitigr::operator|(lhs, rhs); \ } \ \ /** Bitwise XOR for `T`. */ \ constexpr T operator^(const T lhs, const T rhs) noexcept \ { \ - return dmitigr::util::operator^(lhs, rhs); \ + return dmitigr::operator^(lhs, rhs); \ } \ \ /** Bitwise NOT for `T`. */ \ constexpr T operator~(const T rhs) noexcept \ { \ - return dmitigr::util::operator~(rhs); \ + return dmitigr::operator~(rhs); \ } \ \ /** Bitwise AND for `T` with assignment to lvalue. */ \ constexpr T& operator&=(T& lhs, T rhs) noexcept \ { \ - return dmitigr::util::operator&=(lhs, rhs); \ + return dmitigr::operator&=(lhs, rhs); \ } \ \ /** Bitwise OR for `T` with assignment to lvalue. */ \ constexpr T& operator|=(T& lhs, T rhs) noexcept \ { \ - return dmitigr::util::operator|=(lhs, rhs); \ + return dmitigr::operator|=(lhs, rhs); \ } \ \ /** Bitwise XOR for `T` with assignment to lvalue. */ \ constexpr T& operator^=(T& lhs, T rhs) noexcept \ { \ - return dmitigr::util::operator^=(lhs, rhs); \ + return dmitigr::operator^=(lhs, rhs); \ } -#endif // DMITIGR_UTIL_ENUM_BITMASK_HPP +#endif // DMITIGR_BASE_ENUM_BITMASK_HPP diff --git a/src/base/err.hpp b/src/base/err.hpp new file mode 100644 index 0000000..269de17 --- /dev/null +++ b/src/base/err.hpp @@ -0,0 +1,108 @@ +// -*- C++ -*- +// +// Copyright 2022 Dmitry Igrishin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DMITIGR_BASE_ERR_HPP +#define DMITIGR_BASE_ERR_HPP + +#include +#include +#include + +namespace dmitigr { + +/// An error. +class Err final { +public: + /// Constructs not an error. + Err() noexcept = default; + + /// The constructor. + explicit Err(std::error_condition cond, std::string what = {}) + : condition_{std::move(cond)} + , what_{std::move(what)} + {} + + /// @returns `true` if the instance represents an error. + explicit operator bool() const noexcept + { + return static_cast(condition_); + } + + /// @returns The error condition. + std::error_condition condition() const noexcept + { + return condition_; + } + + /// @returns The what-string. + const std::string& what() const noexcept + { + return what_; + } + + /// @returns The error message combined from `condition().message()` and `what()`. + std::string message() const + { + std::string result{condition_.message()}; + if (!what_.empty()) + result.append(": ").append(what_); + return result; + } + +private: + std::error_condition condition_; + std::string what_; +}; + +/// @returns `true` if `lhs` is equals to `rhs`. +inline bool operator==(const Err& lhs, const std::error_condition& rhs) noexcept +{ + return lhs.condition() == rhs; +} + +/// @overload +inline bool operator==(const std::error_condition& lhs, const Err& rhs) noexcept +{ + return lhs == rhs.condition(); +} + +/// @overload +inline bool operator==(const Err& lhs, const Err& rhs) noexcept +{ + return lhs.condition() == rhs.condition(); +} + +/// @returns `true` if `lhs` is not equals to `rhs`. +inline bool operator!=(const Err& lhs, const std::error_condition& rhs) noexcept +{ + return !(lhs.condition() == rhs); +} + +/// @overload +inline bool operator!=(const std::error_condition& lhs, const Err& rhs) noexcept +{ + return !(lhs == rhs.condition()); +} + +/// @overload +inline bool operator!=(const Err& lhs, const Err& rhs) noexcept +{ + return !(lhs == rhs); +} + +} // namespace dmitigr + +#endif // DMITIGR_BASE_ERR_HPP diff --git a/src/base/errc.hpp b/src/base/errc.hpp index 042f785..23f8bc7 100644 --- a/src/base/errc.hpp +++ b/src/base/errc.hpp @@ -49,7 +49,8 @@ constexpr const char* to_literal(const Errc errc) noexcept * @returns The literal returned by `to_literal(errc)`, or literal * "unknown error" if `to_literal(errc)` returned `nullptr`. */ -constexpr const char* to_literal_anyway(const Errc errc) noexcept +template +constexpr const char* to_literal_anyway(const E errc) noexcept { constexpr const char* unknown{"unknown error"}; const char* const literal{to_literal(errc)}; diff --git a/src/base/errctg.hpp b/src/base/errctg.hpp index e90156b..f0b35c0 100644 --- a/src/base/errctg.hpp +++ b/src/base/errctg.hpp @@ -39,7 +39,7 @@ namespace dmitigr { /** * @ingroup errors * - * @brief A Generic category of errors. + * @brief A generic category of errors. * * @see Exception. */ diff --git a/src/base/exceptions.hpp b/src/base/exceptions.hpp index 227d4a2..75631b7 100644 --- a/src/base/exceptions.hpp +++ b/src/base/exceptions.hpp @@ -17,6 +17,7 @@ #ifndef DMITIGR_BASE_EXCEPTIONS_HPP #define DMITIGR_BASE_EXCEPTIONS_HPP +#include "err.hpp" #include "errctg.hpp" #include @@ -39,8 +40,7 @@ class Exception : public std::exception { * @param what The what-string. */ Exception(const std::error_condition& errc, const std::string& what) - : what_{what} - , condition_{errc} + : err_{errc, what} {} /** @@ -52,21 +52,31 @@ class Exception : public std::exception { : Exception{Errc::generic, what} {} + /// Constructs an instance from the object of type Err. + explicit Exception(Err err) + : err_{std::move(err)} + {} + /// @returns The what-string. const char* what() const noexcept override { - return what_.what(); + return err_.what().c_str(); } /// @returns The error condition. std::error_condition condition() const noexcept { - return condition_; + return err_.condition(); + } + + /// @returns The underlying Err instance. + const Err& err() const noexcept + { + return err_; } private: - std::runtime_error what_; - std::error_condition condition_; + Err err_; }; } // namespace dmitigr diff --git a/src/base/ret.hpp b/src/base/ret.hpp new file mode 100644 index 0000000..8fe5590 --- /dev/null +++ b/src/base/ret.hpp @@ -0,0 +1,99 @@ +// -*- C++ -*- +// +// Copyright 2022 Dmitry Igrishin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DMITIGR_BASE_RET_HPP +#define DMITIGR_BASE_RET_HPP + +#include "err.hpp" + +#include + +namespace dmitigr { + +/** + * @brief A function return value. + * + * @details This template struct is useful as the return type of functions which + * must not throw exceptions. + */ +template +struct Ret final { + static_assert(!std::is_same_v); + + /// The alias of the error type. + using Error = Err; + + /// The alias of the result type. + using Result = T; + + /// Holds not an error and a default-constructed value of type T. + constexpr Ret() noexcept = default; + + /// Holds an error and a default-constructed value of type T. + constexpr Ret(Err err) noexcept + : err{std::move(err)} + {} + + /// @overload + template>> + constexpr Ret(const ErrCondEnum ec) noexcept + : err{ec} + {} + + /// Holds not an error and a given value of type T. + constexpr Ret(T res) noexcept + : res{std::move(res)} + {} + + /** + * @brief Holds the both `err` and `res`. + * + * @details This constructor is useful to return an error with an information + * provided by `res`. + */ + constexpr Ret(Err err, T res) noexcept + : err{err} + , res{std::move(res)} + {} + + /// @returns The error. + template + static auto make_error(E e, Types&& ... res_args) noexcept + { + return Ret{Err{std::move(e)}, T{std::forward(res_args)...}}; + } + + /// @returns The result. + template + static auto make_result(Types&& ... res_args) noexcept + { + return Ret{T{std::forward(res_args)...}}; + } + + /// @returns `true` if this instance is not an error. + explicit operator bool() const noexcept + { + return !err; + } + + Err err; + T res{}; +}; + +} // namespace dmitigr + +#endif // DMITIGR_BASE_RET_HPP diff --git a/src/net/address.hpp b/src/net/address.hpp index 44a3482..5507f70 100644 --- a/src/net/address.hpp +++ b/src/net/address.hpp @@ -115,7 +115,7 @@ class Ip_address final { } /// @returns `true` if `text` is either valid IPv4 or IPv6 address. - static bool is_valid(const std::string& str) + static bool is_valid(const std::string& str) noexcept { char buf[sizeof(::in6_addr)]; return inet_pton__(AF_INET, str.c_str(), buf) > 0 || @@ -182,6 +182,8 @@ class Ip_address final { bool is_valid_{}; std::variant<::in_addr, ::in6_addr> binary_; + friend int cmp(const Ip_address&, const Ip_address&) noexcept; + void* binary__() { return const_cast(static_cast(this)->binary()); @@ -228,6 +230,94 @@ class Ip_address final { } }; +/** + * @returns + * - `0` if: + * - `lhs` and `rhs` are invalid; + * - `lhs` and `rhs` are valid, represents IP addresses + * of the same family (IPv4 or IPv6) and equals. + * - `-1` if: + * - `lhs` is not valid and `rhs` is valid; + * - `lhs` represents IPv4 and `rhs` represents IPv6; + * - `lhs` and `rhs` represents IP addresses of the same family + * (IPv4 or IPv6) and `lhs` is less then `rhs`; + * - `1` if: + * - `lhs` is valid and `rhs` is not valid; + * - `lhs` represents IPv6 and `rhs` represents IPv4; + * - `lhs` and `rhs` represents IP addresses of the same family + * (IPv4 or IPv6) and `lhs` is greater then `rhs`; + */ +inline int cmp(const Ip_address& lhs, const Ip_address& rhs) noexcept +{ + if (!lhs.is_valid() && !rhs.is_valid()) + // Both invalid addresses are always equals. + return 0; + else if (!rhs.is_valid()) + // Any address is always greater than invalid address. + return 1; + else if (!lhs.is_valid()) + // Any invalid address is always less than any address. + return -1; + + const auto* const lhs_addr = std::get_if<::in_addr>(&lhs.binary_); + const auto* const rhs_addr = std::get_if<::in_addr>(&rhs.binary_); + if (!lhs_addr && rhs_addr) + // IPv6 is always greater than IPv4. + return 1; + else if (lhs_addr && !rhs_addr) + // IPv4 is always less than IPv6. + return -1; + else if (lhs_addr && rhs_addr) + // Compare IPv4. + return lhs_addr->s_addr < rhs_addr->s_addr ? -1 : + lhs_addr->s_addr > rhs_addr->s_addr ? 1 : 0; + else { + // Compare IPv6 + const auto* const lhs_addr6 = std::get_if<::in6_addr>(&lhs.binary_); + const auto* const rhs_addr6 = std::get_if<::in6_addr>(&rhs.binary_); + DMITIGR_ASSERT(lhs_addr6); + DMITIGR_ASSERT(rhs_addr6); + return std::memcmp(lhs_addr6->s6_addr, rhs_addr6->s6_addr, + sizeof(::in6_addr::s6_addr)); + } +} + +/// @returns `true` if `lhs` is less than `rhs`. +inline bool operator<(const Ip_address& lhs, const Ip_address& rhs) noexcept +{ + return cmp(lhs, rhs) < 0; +} + +/// @returns `true` if `lhs` is less than or equals to `rhs`. +inline bool operator<=(const Ip_address& lhs, const Ip_address& rhs) noexcept +{ + return cmp(lhs, rhs) <= 0; +} + +/// @returns `true` if `lhs` is equals to `rhs`. +inline bool operator==(const Ip_address& lhs, const Ip_address& rhs) noexcept +{ + return cmp(lhs, rhs) == 0; +} + +/// @returns `true` if `lhs` is not equals to `rhs`. +inline bool operator!=(const Ip_address& lhs, const Ip_address& rhs) noexcept +{ + return cmp(lhs, rhs) != 0; +} + +/// @returns `true` if `lhs` is greater than or equals to `rhs`. +inline bool operator>=(const Ip_address& lhs, const Ip_address& rhs) noexcept +{ + return cmp(lhs, rhs) >= 0; +} + +/// @returns `true` if `lhs` is greater than `rhs`. +inline bool operator>(const Ip_address& lhs, const Ip_address& rhs) noexcept +{ + return cmp(lhs, rhs) > 0; +} + /// An socket address. class Socket_address final { public: diff --git a/src/net/conversions.hpp b/src/net/conversions.hpp index d71d5fe..2fdb9eb 100644 --- a/src/net/conversions.hpp +++ b/src/net/conversions.hpp @@ -17,7 +17,7 @@ #ifndef DMITIGR_NET_CONVERSIONS_HPP #define DMITIGR_NET_CONVERSIONS_HPP -#include "../util/endianness.hpp" +#include "../base/endianness.hpp" #include "exceptions.hpp" #include @@ -47,12 +47,12 @@ inline void copy(void* const dest, const std::size_t dest_size, const auto src_ubytes = static_cast(src); const auto dest_ubytes = static_cast(dest); - switch (util::endianness()) { - case util::Endianness::big: + switch (endianness()) { + case Endianness::big: for (std::size_t i = 0; i < src_size; ++i) dest_ubytes[dest_size - src_size + i] = src_ubytes[i]; break; - case util::Endianness::little: + case Endianness::little: for (std::size_t i = 0; i < src_size; ++i) dest_ubytes[dest_size - 1 - i] = src_ubytes[i]; break; diff --git a/src/net/net.hpp b/src/net/net.hpp index 492df07..66ae87d 100644 --- a/src/net/net.hpp +++ b/src/net/net.hpp @@ -17,14 +17,19 @@ #ifndef DMITIGR_NET_NET_HPP #define DMITIGR_NET_NET_HPP +#include "types_fwd.hpp" #include "address.hpp" #include "basics.hpp" #include "client.hpp" #include "conversions.hpp" #include "descriptor.hpp" #include "endpoint.hpp" +#include "errctg.hpp" +#include "exceptions.hpp" +#include "last_error.hpp" #include "listener.hpp" #include "socket.hpp" #include "util.hpp" +#include "version.hpp" #endif // DMITIGR_NET_NET_HPP diff --git a/src/net/socket.hpp b/src/net/socket.hpp index 9fbaba8..f825505 100644 --- a/src/net/socket.hpp +++ b/src/net/socket.hpp @@ -17,8 +17,8 @@ #ifndef DMITIGR_NET_SOCKET_HPP #define DMITIGR_NET_SOCKET_HPP +#include "../base/enum_bitmask.hpp" #include "../os/last_error.hpp" -#include "../util/enum_bitmask.hpp" #include "address.hpp" #include "exceptions.hpp" @@ -63,12 +63,12 @@ enum class Socket_readiness { } // namespace dmitigr::net -namespace dmitigr::util { +namespace dmitigr { /// Full specialization for net::Socket_readiness. template<> struct Is_bitmask_enum : std::true_type {}; -} // namespace dmitigr::util +} // namespace dmitigr namespace dmitigr::net { diff --git a/src/net/util.hpp b/src/net/util.hpp index 248289e..2e76c65 100644 --- a/src/net/util.hpp +++ b/src/net/util.hpp @@ -17,19 +17,18 @@ #ifndef DMITIGR_NET_UTIL_HPP #define DMITIGR_NET_UTIL_HPP -#include +#include #include namespace dmitigr::net { /// @returns `true` if the `hostname` denotes a valid hostname. -inline bool is_hostname_valid(const std::string& hostname) +inline bool is_hostname_valid(const std::string& hostname) noexcept { // Returns `true` if `ch` is a valid hostname character. - static const auto is_hostname_char = [](const char ch) + static const auto is_hostname_char = [](const unsigned char ch) noexcept { - static const std::locale l; - return std::isalnum(ch, l) || (ch == '_') || (ch == '-'); + return std::isalnum(ch) || ch == '_' || ch == '-'; }; constexpr std::string::size_type max_length{253}; diff --git a/src/os/os.hpp b/src/os/os.hpp index 08172ea..a650960 100644 --- a/src/os/os.hpp +++ b/src/os/os.hpp @@ -17,12 +17,12 @@ #ifndef DMITIGR_OS_OS_HPP #define DMITIGR_OS_OS_HPP +#include "types_fwd.hpp" #include "environment.hpp" #include "error.hpp" #include "exceptions.hpp" #include "last_error.hpp" #include "pid.hpp" -#include "types_fwd.hpp" #include "version.hpp" #ifdef _WIN32 #include "windows.hpp" diff --git a/src/pgfe/array_conversions.hpp b/src/pgfe/array_conversions.hpp index 72c687e..e173f3c 100644 --- a/src/pgfe/array_conversions.hpp +++ b/src/pgfe/array_conversions.hpp @@ -19,6 +19,7 @@ #include "../base/assert.hpp" #include "../str/c_str.hpp" +#include "../str/predicate.hpp" #include "basic_conversions.hpp" #include "conversions_api.hpp" #include "data.hpp" @@ -26,7 +27,6 @@ #include #include -#include #include #include #include @@ -516,7 +516,6 @@ const char* parse_array_literal(const char* literal, const char delimiter, char previous_char{}; char previous_nonspace_char{}; std::string element; - const std::locale loc; while (const char c = *literal) { switch (state) { case in_beginning: { @@ -524,7 +523,7 @@ const char* parse_array_literal(const char* literal, const char delimiter, handler(dimension); dimension = 1; state = in_dimension; - } else if (std::isspace(c, loc)) { + } else if (str::is_space(c)) { // Skip space. } else throw Client_exception{Client_errc::malformed_literal}; @@ -535,7 +534,7 @@ const char* parse_array_literal(const char* literal, const char delimiter, case in_dimension: { DMITIGR_ASSERT(dimension > 0); - if (std::isspace(c, loc)) { + if (str::is_space(c)) { // Skip space. } else if (c == delimiter) { if (previous_nonspace_char == delimiter || previous_nonspace_char == '{') @@ -615,7 +614,7 @@ const char* parse_array_literal(const char* literal, const char delimiter, } // extracted element processing preparing_to_the_next_iteration: - if (!std::isspace(c, loc)) + if (str::is_non_space(c)) previous_nonspace_char = c; previous_char = c; ++literal; diff --git a/src/pgfe/basics.hpp b/src/pgfe/basics.hpp index d5daa81..183b709 100644 --- a/src/pgfe/basics.hpp +++ b/src/pgfe/basics.hpp @@ -17,7 +17,7 @@ #ifndef DMITIGR_PGFE_BASICS_HPP #define DMITIGR_PGFE_BASICS_HPP -#include "../util/enum_bitmask.hpp" +#include "../base/enum_bitmask.hpp" #include @@ -78,12 +78,10 @@ enum class External_library { // ============================================================================= -namespace util { template<> struct Is_bitmask_enum final : std::true_type {}; template<> struct Is_bitmask_enum final : std::true_type {}; -} // namespace util namespace pgfe { diff --git a/src/pgfe/connection.cpp b/src/pgfe/connection.cpp index f908e42..04f25f8 100644 --- a/src/pgfe/connection.cpp +++ b/src/pgfe/connection.cpp @@ -58,10 +58,10 @@ DMITIGR_PGFE_INLINE Server_status ping(const Connection_options& options) case PQPING_NO_ATTEMPT: throw Client_exception{"due to client-side problem no attempt was made to" " contact the PostgreSQL server"}; - default: - break; + default:; } DMITIGR_ASSERT(false); + throw false; // disable -Wreturn-type } // ============================================================================= diff --git a/src/pgfe/errc.cpp b/src/pgfe/errc.cpp index 2f670a2..08c0d5d 100644 --- a/src/pgfe/errc.cpp +++ b/src/pgfe/errc.cpp @@ -148,8 +148,8 @@ DMITIGR_PGFE_INLINE const char* to_literal(const Server_errc errc) noexcept return "c22_invalid_indicator_parameter_value"; case Server_errc::c22_invalid_parameter_value: return "c22_invalid_parameter_value"; - case Server_errc::c22_invalid_preceding_following_size: - return "c22_invalid_preceding_following_size"; + case Server_errc::c22_invalid_preceding_or_following_size: + return "c22_invalid_preceding_or_following_size"; case Server_errc::c22_invalid_regular_expression: return "c22_invalid_regular_expression"; case Server_errc::c22_invalid_row_count_in_limit_clause: @@ -224,20 +224,22 @@ DMITIGR_PGFE_INLINE const char* to_literal(const Server_errc errc) noexcept return "c22_non_unique_keys_in_json_object"; case Server_errc::c22_singleton_json_item_required: return "c22_singleton_json_item_required"; - case Server_errc::c22_json_array_not_found: - return "c22_json_array_not_found"; - case Server_errc::c22_json_member_not_found: - return "c22_json_member_not_found"; - case Server_errc::c22_json_number_not_found: - return "c22_json_number_not_found"; - case Server_errc::c22_object_not_found: - return "c22_object_not_found"; + case Server_errc::c22_sql_json_array_not_found: + return "c22_sql_json_array_not_found"; + case Server_errc::c22_sql_json_member_not_found: + return "c22_sql_json_member_not_found"; + case Server_errc::c22_sql_json_number_not_found: + return "c22_sql_json_number_not_found"; + case Server_errc::c22_sql_json_object_not_found: + return "c22_sql_json_object_not_found"; case Server_errc::c22_too_many_json_array_elements: return "c22_too_many_json_array_elements"; case Server_errc::c22_too_many_json_object_members: return "c22_too_many_json_object_members"; - case Server_errc::c22_json_scalar_required: - return "c22_json_scalar_required"; + case Server_errc::c22_sql_json_scalar_required: + return "c22_sql_json_scalar_required"; + case Server_errc::c22_sql_json_item_cannot_be_cast_to_target_type: + return "c22_sql_json_item_cannot_be_cast_to_target_type"; case Server_errc::c23_integrity_constraint_violation: return "c23_integrity_constraint_violation"; case Server_errc::c23_restrict_violation: diff --git a/src/pgfe/errc.hpp b/src/pgfe/errc.hpp index 62260ca..6f834ed 100644 --- a/src/pgfe/errc.hpp +++ b/src/pgfe/errc.hpp @@ -283,7 +283,7 @@ enum class Server_errc { c22_invalid_parameter_value = 3452619, /// 22013 - c22_invalid_preceding_following_size = 3452583, + c22_invalid_preceding_or_following_size = 3452583, /// 2201B c22_invalid_regular_expression = 3452591, @@ -397,16 +397,16 @@ enum class Server_errc { c22_singleton_json_item_required = 3452660, /// 22039 - c22_json_array_not_found = 3452661, + c22_sql_json_array_not_found = 3452661, /// 2203A - c22_json_member_not_found = 3452662, + c22_sql_json_member_not_found = 3452662, /// 2203B - c22_json_number_not_found = 3452663, + c22_sql_json_number_not_found = 3452663, /// 2203C - c22_object_not_found = 3452664, + c22_sql_json_object_not_found = 3452664, /// 2203D c22_too_many_json_array_elements = 3452665, @@ -415,7 +415,10 @@ enum class Server_errc { c22_too_many_json_object_members = 3452666, /// 2203F - c22_json_scalar_required = 3452667, + c22_sql_json_scalar_required = 3452667, + + /// 2203G + c22_sql_json_item_cannot_be_cast_to_target_type = 3452668, /* * Class 23 - Integrity Constraint Violation diff --git a/src/pgfe/large_object.hpp b/src/pgfe/large_object.hpp index 17a6103..22d001b 100644 --- a/src/pgfe/large_object.hpp +++ b/src/pgfe/large_object.hpp @@ -71,10 +71,8 @@ enum class Large_object_seek_whence { } // namespace pgfe -namespace util { template<> struct Is_bitmask_enum final : std::true_type {}; -} // namespace util namespace pgfe { diff --git a/src/pgfe/misc.cpp b/src/pgfe/misc.cpp index 482f090..f737481 100644 --- a/src/pgfe/misc.cpp +++ b/src/pgfe/misc.cpp @@ -19,7 +19,7 @@ #include "misc.hpp" #include "pq.hpp" -#include +#include namespace dmitigr::pgfe { @@ -36,12 +36,11 @@ DMITIGR_PGFE_INLINE std::string unquote_identifier(const std::string_view identi std::string result; const std::string_view::size_type sz = identifier.size(); - const std::locale loc; for (std::string_view::size_type i = 0; i < sz; ++i) { const char c = identifier[i]; if (state == top) { if (c != '"') - result += std::tolower(c, loc); + result += std::tolower(static_cast(c)); else state = double_quote; } else if (state == double_quote) { @@ -64,11 +63,10 @@ DMITIGR_PGFE_INLINE int array_dimension(const std::string_view literal, return 0; int dimension{}; - const std::locale loc; for (const auto c : literal) { if (c == '{') ++dimension; - else if (std::isspace(c, loc)) + else if (std::isspace(static_cast(c))) ; // Skip space. else if (!dimension || c == delimiter) throw Client_exception{Client_errc::malformed_literal}; diff --git a/src/pgfe/problem.cpp b/src/pgfe/problem.cpp index 322ca74..8abb35a 100644 --- a/src/pgfe/problem.cpp +++ b/src/pgfe/problem.cpp @@ -21,9 +21,9 @@ #include "problem.hpp" #include +#include #include #include -#include namespace dmitigr::pgfe { @@ -150,14 +150,13 @@ DMITIGR_PGFE_INLINE std::error_condition Problem::min_error_condition() noexcept DMITIGR_PGFE_INLINE int Problem::sqlstate_string_to_int(const char* const sqlstate) { - const std::locale loc; const std::string_view state{sqlstate}; if (!((state.size() == 5) && - std::isalnum(state[0], loc) && - std::isalnum(state[1], loc) && - std::isalnum(state[2], loc) && - std::isalnum(state[3], loc) && - std::isalnum(state[4], loc))) + std::isalnum(static_cast(state[0])) && + std::isalnum(static_cast(state[1])) && + std::isalnum(static_cast(state[2])) && + std::isalnum(static_cast(state[3])) && + std::isalnum(static_cast(state[4])))) throw Client_exception{"cannot convert SQLSTATE to int"}; errno = 0; diff --git a/src/pgfe/statement.cpp b/src/pgfe/statement.cpp index ab6422a..969d00d 100644 --- a/src/pgfe/statement.cpp +++ b/src/pgfe/statement.cpp @@ -15,6 +15,7 @@ // limitations under the License. #include "../base/assert.hpp" +#include "../str/predicate.hpp" #include "connection.hpp" #include "data.hpp" #include "exceptions.hpp" @@ -53,7 +54,7 @@ Statement::Fragment::is_named_parameter(const std::string_view name) const noexc DMITIGR_PGFE_INLINE Statement::Statement(const std::string_view text) { - auto s = parse_sql_input(text, loc_).first; + auto s = parse_sql_input(text).first; swap(s); assert(is_invariant_ok()); } @@ -107,7 +108,6 @@ DMITIGR_PGFE_INLINE Statement& Statement::operator=(Statement&& rhs) noexcept DMITIGR_PGFE_INLINE void Statement::swap(Statement& rhs) noexcept { using std::swap; - swap(loc_, rhs.loc_); swap(fragments_, rhs.fragments_); swap(positional_parameters_, rhs.positional_parameters_); swap(named_parameters_, rhs.named_parameters_); @@ -171,7 +171,7 @@ DMITIGR_PGFE_INLINE bool Statement::is_query_empty() const noexcept return all_of(cbegin(fragments_), cend(fragments_), [this](const Fragment& f) { - return is_comment(f) || (is_text(f) && is_blank_string(f.str, loc_)); + return is_comment(f) || (is_text(f) && str::is_blank(f.str)); }); } @@ -219,21 +219,15 @@ DMITIGR_PGFE_INLINE bool Statement::has_missing_parameters() const noexcept DMITIGR_PGFE_INLINE void Statement::append(const Statement& appendix) { - const bool was_query_empty = is_query_empty(); - - // Updating fragments - auto old_fragments = fragments_; - try { - fragments_.insert(cend(fragments_), cbegin(appendix.fragments_), - cend(appendix.fragments_)); - update_cache(appendix); // can throw (strong exception safety guarantee) - - if (was_query_empty) - is_extra_data_should_be_extracted_from_comments_ = true; - } catch (...) { - fragments_.swap(old_fragments); // rollback - throw; - } + const bool was_query_empty{is_query_empty()}; + + // Update fragments. + fragments_.insert(cend(fragments_), cbegin(appendix.fragments_), + cend(appendix.fragments_)); + update_cache(appendix); // can throw + + if (was_query_empty) + is_extra_data_should_be_extracted_from_comments_ = true; assert(is_invariant_ok()); } @@ -270,26 +264,20 @@ Statement::replace_parameter(const std::string_view name, if (!(has_parameter(name) && (this != &replacement))) throw Client_exception{"cannot replace Statement parameter"}; - // Updating fragments - auto old_fragments = fragments_; - try { - for (auto fi = begin(fragments_); fi != end(fragments_);) { - if (fi->is_named_parameter(name)) { - // Insert the `replacement` just before `fi`. - fragments_.insert(fi, cbegin(replacement.fragments_), - cend(replacement.fragments_)); - // Erase named parameter pointed by `fi` and got the next iterator. - fi = fragments_.erase(fi); - } else - ++fi; - } - - update_cache(replacement); // can throw (strong exception safety guarantee) - } catch (...) { - fragments_.swap(old_fragments); - throw; + // Update fragments. + for (auto fi = begin(fragments_); fi != end(fragments_);) { + if (fi->is_named_parameter(name)) { + // Insert the `replacement` just before `fi`. + fragments_.insert(fi, cbegin(replacement.fragments_), + cend(replacement.fragments_)); + // Erase named parameter pointed by `fi` and got the next iterator. + fi = fragments_.erase(fi); + } else + ++fi; } + update_cache(replacement); // can throw + assert(is_invariant_ok()); } @@ -423,14 +411,14 @@ struct Statement::Extra final { /// @returns The vector of associated extra data. static std::vector> - extract(const Fragment_list& fragments, const std::locale& loc) + extract(const Fragment_list& fragments) { std::vector> result; - const auto iters = first_related_comments(fragments, loc); + const auto iters = first_related_comments(fragments); if (iters.first != cend(fragments)) { const auto comments = joined_comments(iters.first, iters.second); for (const auto& comment : comments) { - auto associations = extract(comment.first, comment.second, loc); + auto associations = extract(comment.first, comment.second); result.reserve(result.capacity() + associations.size()); for (auto& a : associations) result.push_back(std::move(a)); @@ -457,10 +445,9 @@ struct Statement::Extra final { * * @param input An input string with comments. * @param comment_type A type of comments in the `input`. - * @param loc The locale. */ static std::vector> extract(const std::string_view input, - const Comment_type comment_type, const std::locale& loc) + const Comment_type comment_type) { enum { top, dollar, dollar_quote_leading_tag, dollar_quote, dollar_quote_dollar } state = top; @@ -470,9 +457,9 @@ struct Statement::Extra final { std::string dollar_quote_leading_tag_name; std::string dollar_quote_trailing_tag_name; - const auto is_valid_tag_char = [&loc](const char c) noexcept + const auto is_valid_tag_char = [](const unsigned char c) noexcept { - return isalnum(c, loc) || c == '_' || c == '-'; + return std::isalnum(c) || c == '_' || c == '-'; }; for (const auto current_char : input) { @@ -510,7 +497,7 @@ struct Statement::Extra final { */ state = top; result.emplace_back(std::move(dollar_quote_leading_tag_name), - Data::make(cleaned_content(std::move(content), comment_type, loc), + Data::make(cleaned_content(std::move(content), comment_type), Data_format::text)); content = {}; dollar_quote_leading_tag_name = {}; @@ -536,7 +523,7 @@ struct Statement::Extra final { * @returns The number of characters to remove after each '\n'. */ static std::size_t indent_size(const std::string_view content, - const Comment_type comment_type, const std::locale& loc) + const Comment_type comment_type) { const auto set_if_less = [](auto& variable, const auto count) { @@ -557,7 +544,7 @@ struct Statement::Extra final { count = 0; else if (current_char == '*') state = after_asterisk; - else if (isspace(current_char, loc)) + else if (str::is_space(current_char)) ++count; else state = after_non_asterisk; @@ -618,12 +605,12 @@ struct Statement::Extra final { * multiline comments only). */ static std::string cleaned_content(std::string&& content, - const Comment_type comment_type, const std::locale& loc) + const Comment_type comment_type) { std::string result; // Removing the indentation characters (if any). - if (const std::size_t isize = indent_size(content, comment_type, loc); isize > 0) { + if (const std::size_t isize = indent_size(content, comment_type); isize > 0) { std::size_t count{}; enum { eating, skiping } state = eating; for (const auto current_char : content) { @@ -681,15 +668,14 @@ struct Statement::Extra final { * @returns The pair of iterators that specifies the range of relevant comments. */ std::pair - static first_related_comments(const Fragment_list& fragments, const std::locale& loc) + static first_related_comments(const Fragment_list& fragments) { using Ft = Fragment::Type; const auto b = cbegin(fragments); const auto e = cend(fragments); auto result = std::make_pair(e, e); - const auto is_nearby_string = [](const std::string_view str, - const std::locale& strloc) + const auto is_nearby_string = [](const std::string_view str) { std::string::size_type count{}; for (const auto c : str) { @@ -697,7 +683,7 @@ struct Statement::Extra final { ++count; if (count > 1) return false; - } else if (!is_space(c, strloc)) + } else if (!str::is_space(c)) break; } return true; @@ -707,10 +693,10 @@ struct Statement::Extra final { * Stops lookup when either named parameter or positional parameter are found. * (Only fragments of type `text` can have related comments.) */ - auto i = find_if(b, e, [&loc, &is_nearby_string](const Fragment& f) + auto i = find_if(b, e, [&is_nearby_string](const Fragment& f) { return (f.type == Ft::text && - is_nearby_string(f.str, loc) && !is_blank_string(f.str, loc)) || + is_nearby_string(f.str) && !str::is_blank(f.str)) || f.type == Ft::named_parameter || f.type == Ft::positional_parameter; }); @@ -719,9 +705,9 @@ struct Statement::Extra final { do { --i; DMITIGR_ASSERT(is_comment(*i) || - (is_text(*i) && is_blank_string(i->str, loc))); + (is_text(*i) && str::is_blank(i->str))); if (i->type == Ft::text) { - if (!is_nearby_string(i->str, loc)) + if (!is_nearby_string(i->str)) break; } result.first = i; @@ -794,9 +780,9 @@ struct Statement::Extra final { DMITIGR_PGFE_INLINE const Tuple& Statement::extra() const noexcept { if (!extra_) - extra_.emplace(Extra::extract(fragments_, loc_)); + extra_.emplace(Extra::extract(fragments_)); else if (is_extra_data_should_be_extracted_from_comments_) - extra_->append(Tuple{Extra::extract(fragments_, loc_)}); + extra_->append(Tuple{Extra::extract(fragments_)}); is_extra_data_should_be_extracted_from_comments_ = false; assert(is_invariant_ok()); return *extra_; @@ -905,39 +891,29 @@ Statement::push_named_parameter(const std::string& str, const char quote_char) // Updaters // --------------------------------------------------------------------------- -// Exception safety guarantee: strong. +// Exception safety guarantee: basic. DMITIGR_PGFE_INLINE void Statement::update_cache(const Statement& rhs) { - // Preparing for merge positional parameters. + // Prepare positional parameters for merge. const auto old_pos_params_size = positional_parameters_.size(); const auto rhs_pos_params_size = rhs.positional_parameters_.size(); - if (old_pos_params_size < rhs_pos_params_size) - positional_parameters_.resize(rhs_pos_params_size); // can throw - - try { - const auto new_pos_params_size = positional_parameters_.size(); - DMITIGR_ASSERT(new_pos_params_size >= rhs_pos_params_size); - - // Creating the cache for named parameters. (Can throw.) - decltype (named_parameters_) new_named_parameters = named_parameters(); - - // Check the new parameter count. - const auto new_parameter_count = new_pos_params_size + new_named_parameters.size(); - if (new_parameter_count > max_parameter_count()) - throw Client_exception{"parameter count (" + - std::to_string(new_parameter_count) + ") " - "exceeds the maximum (" + std::to_string(max_parameter_count()) + ")"}; - - // Merging positional parameters (cannot throw). - for (std::size_t i{}; i < rhs_pos_params_size; ++i) { - if (!positional_parameters_[i] && rhs.positional_parameters_[i]) - positional_parameters_[i] = true; - } + const auto new_pos_params_size = std::max(old_pos_params_size, rhs_pos_params_size); + positional_parameters_.resize(new_pos_params_size); // can throw + + // Recreate the cache for named parameters. (Can throw.) + named_parameters_ = named_parameters(); - named_parameters_.swap(new_named_parameters); // commit (cannot throw) - } catch (...) { - positional_parameters_.resize(old_pos_params_size); // rollback - throw; + // Check the new parameter count. + const auto new_parameter_count = new_pos_params_size + named_parameters_.size(); + if (new_parameter_count > max_parameter_count()) + throw Client_exception{"parameter count (" + + std::to_string(new_parameter_count) + ") " + "exceeds the maximum (" + std::to_string(max_parameter_count()) + ")"}; + + // Merge positional parameters (cannot throw). + for (std::size_t i{}; i < rhs_pos_params_size; ++i) { + if (!positional_parameters_[i] && rhs.positional_parameters_[i]) + positional_parameters_[i] = true; } assert(is_invariant_ok()); @@ -989,21 +965,6 @@ DMITIGR_PGFE_INLINE auto Statement::named_parameters() const // Predicates // --------------------------------------------------------------------------- -DMITIGR_PGFE_INLINE bool -Statement::is_space(const char c, const std::locale& loc) noexcept -{ - return isspace(c, loc); -} - -DMITIGR_PGFE_INLINE bool -Statement::is_blank_string(const std::string& str, const std::locale& loc) noexcept -{ - return all_of(cbegin(str), cend(str), [&loc](const auto& c) - { - return is_space(c, loc); - }); -} - DMITIGR_PGFE_INLINE bool Statement::is_comment(const Fragment& f) noexcept { @@ -1018,13 +979,13 @@ Statement::is_text(const Fragment& f) noexcept } DMITIGR_PGFE_INLINE bool -Statement::is_ident_char(const char c, const std::locale& loc) noexcept +Statement::is_ident_char(const unsigned char c) noexcept { - return isalnum(c, loc) || c == '_' || c == '$'; + return std::isalnum(c) || c == '_' || c == '$'; } DMITIGR_PGFE_INLINE bool -Statement::is_quote_char(const char c) noexcept +Statement::is_quote_char(const unsigned char c) noexcept { return c == '\'' || c == '\"'; } @@ -1101,7 +1062,7 @@ Statement::is_quote_char(const char c) noexcept * that follows returned SQL string. */ DMITIGR_PGFE_INLINE std::pair -Statement::parse_sql_input(const std::string_view text, const std::locale& loc) +Statement::parse_sql_input(const std::string_view text) { enum { top, @@ -1162,7 +1123,7 @@ Statement::parse_sql_input(const std::string_view text, const std::locale& loc) continue; case '$': - if (!is_ident_char(previous_char, loc)) + if (!is_ident_char(previous_char)) state = dollar; else fragment += current_char; @@ -1209,12 +1170,12 @@ Statement::parse_sql_input(const std::string_view text, const std::locale& loc) case dollar: DMITIGR_ASSERT(previous_char == '$'); - if (isdigit(current_char, loc)) { + if (isdigit(static_cast(current_char))) { state = positional_parameter; result.push_text(fragment); fragment.clear(); // The 1st digit of positional parameter (current_char) will be stored below. - } else if (is_ident_char(current_char, loc)) { + } else if (is_ident_char(current_char)) { if (current_char == '$') { state = dollar_quote; } else { @@ -1231,8 +1192,8 @@ Statement::parse_sql_input(const std::string_view text, const std::locale& loc) continue; case positional_parameter: - DMITIGR_ASSERT(isdigit(previous_char, loc)); - if (!isdigit(current_char, loc)) { + DMITIGR_ASSERT(isdigit(static_cast(previous_char))); + if (!isdigit(static_cast(current_char))) { state = top; result.push_positional_parameter(fragment); fragment.clear(); @@ -1245,11 +1206,11 @@ Statement::parse_sql_input(const std::string_view text, const std::locale& loc) goto finish; case dollar_quote_leading_tag: - DMITIGR_ASSERT(previous_char != '$' && is_ident_char(previous_char, loc)); + DMITIGR_ASSERT(previous_char != '$' && is_ident_char(previous_char)); if (current_char == '$') { fragment += current_char; state = dollar_quote; - } else if (is_ident_char(current_char, loc)) { + } else if (is_ident_char(current_char)) { dollar_quote_leading_tag_name += current_char; fragment += current_char; } else @@ -1281,7 +1242,7 @@ Statement::parse_sql_input(const std::string_view text, const std::locale& loc) case colon: DMITIGR_ASSERT(previous_char == ':'); - if (is_ident_char(current_char, loc) || is_quote_char(current_char)) { + if (is_ident_char(current_char) || is_quote_char(current_char)) { state = named_parameter; result.push_text(fragment); fragment.clear(); @@ -1301,10 +1262,10 @@ Statement::parse_sql_input(const std::string_view text, const std::locale& loc) goto finish; case named_parameter: - DMITIGR_ASSERT(is_ident_char(previous_char, loc) || + DMITIGR_ASSERT(is_ident_char(previous_char) || (is_quote_char(previous_char) && quote_char)); - if (!is_ident_char(current_char, loc)) { + if (!is_ident_char(current_char)) { state = top; result.push_named_parameter(fragment, quote_char); fragment.clear(); diff --git a/src/pgfe/statement.hpp b/src/pgfe/statement.hpp index 7005d44..a94dfcf 100644 --- a/src/pgfe/statement.hpp +++ b/src/pgfe/statement.hpp @@ -23,9 +23,9 @@ #include "tuple.hpp" #include "types_fwd.hpp" +#include #include #include -#include #include #include #include @@ -211,7 +211,7 @@ class Statement final : public Parameterizable { * data of this instance. * * @par Exception safety guarantee - * Strong. + * Basic. */ DMITIGR_PGFE_API void append(const Statement& appendix); @@ -254,7 +254,7 @@ class Statement final : public Parameterizable { * named by the `name`. The extra data will *not* be affected. * * @par Exception safety guarantee - * Strong. + * Basic. * * @see has_parameter(), bind(). */ @@ -397,7 +397,6 @@ class Statement final : public Parameterizable { }; using Fragment_list = std::list; - std::locale loc_; Fragment_list fragments_; std::vector positional_parameters_; // cache std::vector named_parameters_; // cache @@ -405,7 +404,7 @@ class Statement final : public Parameterizable { mutable std::optional extra_; // cache static std::pair - parse_sql_input(std::string_view, const std::locale& loc); + parse_sql_input(std::string_view); bool is_invariant_ok() const noexcept override; @@ -439,12 +438,10 @@ class Statement final : public Parameterizable { // Predicates // --------------------------------------------------------------------------- - static bool is_space(const char c, const std::locale& loc) noexcept; - static bool is_blank_string(const std::string& str, const std::locale& loc) noexcept; static bool is_comment(const Fragment& f) noexcept; static bool is_text(const Fragment& f) noexcept; - static bool is_ident_char(const char c, const std::locale& loc) noexcept; - static bool is_quote_char(const char c) noexcept; + static bool is_ident_char(const unsigned char c) noexcept; + static bool is_quote_char(const unsigned char c) noexcept; // --------------------------------------------------------------------------- // Extra data diff --git a/src/pgfe/statement_vector.cpp b/src/pgfe/statement_vector.cpp index 0ab71bc..c2d39a0 100644 --- a/src/pgfe/statement_vector.cpp +++ b/src/pgfe/statement_vector.cpp @@ -25,9 +25,8 @@ namespace dmitigr::pgfe { DMITIGR_PGFE_INLINE Statement_vector::Statement_vector(std::string_view input) { - const std::locale loc; while (!input.empty()) { - auto [st, pos] = Statement::parse_sql_input(input, loc); + auto [st, pos] = Statement::parse_sql_input(input); statements_.emplace_back(std::move(st)); DMITIGR_ASSERT(pos <= input.size()); input = input.substr(pos); diff --git a/src/str/basics.hpp b/src/str/basics.hpp new file mode 100644 index 0000000..98586e2 --- /dev/null +++ b/src/str/basics.hpp @@ -0,0 +1,37 @@ +// -*- C++ -*- +// +// Copyright 2022 Dmitry Igrishin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DMITIGR_STR_BASICS_HPP +#define DMITIGR_STR_BASICS_HPP + +#include "../base/enum_bitmask.hpp" + +namespace dmitigr { + +namespace str { +/// Denotes trimming mode bitmask. +enum class Trim { + lhs = 1, + rhs, + all +}; +} // namespace str + +template<> struct Is_bitmask_enum : std::true_type {}; + +} // namespace dmitigr + +#endif // DMITIGR_STR_BASICS_HPP diff --git a/src/str/c_str.hpp b/src/str/c_str.hpp index 41f7ad5..eb1928a 100644 --- a/src/str/c_str.hpp +++ b/src/str/c_str.hpp @@ -17,10 +17,9 @@ #ifndef DMITIGR_STR_C_STR_HPP #define DMITIGR_STR_C_STR_HPP -#include "version.hpp" +#include "predicate.hpp" #include -#include namespace dmitigr::str { @@ -32,11 +31,10 @@ namespace dmitigr::str { * @returns The pointer to a next non-space character, or pointer to the * terminating zero character. */ -inline const char* next_non_space_pointer(const char* p, - const std::locale& loc = {}) noexcept +inline const char* next_non_space_pointer(const char* p) noexcept { if (p) { - while (*p != '\0' && std::isspace(*p, loc)) + while (*p != '\0' && is_space(*p)) ++p; } return p; diff --git a/src/str/line.hpp b/src/str/line.hpp index ac53376..6709c77 100644 --- a/src/str/line.hpp +++ b/src/str/line.hpp @@ -18,7 +18,6 @@ #define DMITIGR_STR_LINE_HPP #include "exceptions.hpp" -#include "version.hpp" #include #include diff --git a/src/str/numeric.hpp b/src/str/numeric.hpp index b33f7ce..2687423 100644 --- a/src/str/numeric.hpp +++ b/src/str/numeric.hpp @@ -18,7 +18,6 @@ #define DMITIGR_STR_NUMERIC_HPP #include "exceptions.hpp" -#include "version.hpp" #include #include diff --git a/src/str/predicate.hpp b/src/str/predicate.hpp index a56430a..3ae7c8b 100644 --- a/src/str/predicate.hpp +++ b/src/str/predicate.hpp @@ -17,11 +17,8 @@ #ifndef DMITIGR_STR_PREDICATE_HPP #define DMITIGR_STR_PREDICATE_HPP -#include "version.hpp" - #include -#include -#include +#include #include namespace dmitigr::str { @@ -31,41 +28,50 @@ namespace dmitigr::str { // ----------------------------------------------------------------------------- /// @returns `true` if `c` is a valid space character. -inline bool is_space_character(const char c, - const std::locale& loc = {}) noexcept +template +bool is_space(const Ch ch) noexcept +{ + return std::isspace(static_cast(ch)); +} + +/// @returns `!is_space(ch)`. +template +bool is_non_space(const Ch ch) noexcept +{ + return !is_space(ch); +} + +/// @returns `true` if `c` is printable character. +template +bool is_printable(const Ch ch) noexcept { - return std::isspace(c, loc); + return std::isprint(static_cast(ch)); } -/// @returns `(is_space_character(c) == false)`. -inline bool is_non_space_character(const char c, - const std::locale& loc = {}) noexcept +/// @returns `true` if `c` is a zero character. +template +bool is_zero(const Ch ch) noexcept { - return !is_space_character(c, loc); + return ch == '\0'; } -/// @returns `true` if `c` is a valid simple identifier character. -inline bool is_simple_identifier_character(const char c, - const std::locale& loc = {}) noexcept +/// @returns `true` if `c` is a non zero character. +template +bool is_non_zero(const Ch ch) noexcept { - return std::isalnum(c, loc) || c == '_'; + return !is_zero(ch); } -/// @returns `(is_simple_identifier_character(c) == false)`. -inline bool is_non_simple_identifier_character(const char c, - const std::locale& loc = {}) noexcept +/// @returns `true` if `str` is a blank string. +inline bool is_blank(const std::string_view str) noexcept { - return !is_simple_identifier_character(c, loc); + return std::all_of(cbegin(str), cend(str), is_space); } /// @returns `true` if `str` has at least one space character. -inline bool has_space(const std::string& str, - const std::locale& loc = {}) noexcept +inline bool has_space(const std::string_view str) noexcept { - return std::any_of(cbegin(str), cend(str), [&loc](const auto ch) - { - return is_space_character(ch, loc); - }); + return std::any_of(cbegin(str), cend(str), is_space); } /// @returns `true` if `input` is starting with `pattern`. diff --git a/src/str/sequence.hpp b/src/str/sequence.hpp index 16522c4..8bfa15b 100644 --- a/src/str/sequence.hpp +++ b/src/str/sequence.hpp @@ -17,10 +17,10 @@ #ifndef DMITIGR_STR_SEQUENCE_HPP #define DMITIGR_STR_SEQUENCE_HPP -#include "version.hpp" - #include +#include #include +#include namespace dmitigr::str { @@ -31,7 +31,7 @@ namespace dmitigr::str { /// @returns The string with stringified elements of the sequence in range `[b, e)`. template std::string to_string(const InputIterator b, const InputIterator e, - const std::string& sep, const Function& to_str) + const std::string_view sep, const Function& to_str) { std::string result; if (b != e) { @@ -41,7 +41,7 @@ std::string to_string(const InputIterator b, const InputIterator e, result.append(sep); } const auto sep_size = sep.size(); - for (std::string::size_type j = 0; j < sep_size; ++j) + for (std::string::size_type j{}; j < sep_size; ++j) result.pop_back(); } return result; @@ -49,7 +49,7 @@ std::string to_string(const InputIterator b, const InputIterator e, /// @returns The string with stringified elements of the `Container`. template -std::string to_string(const Container& cont, const std::string& sep, +std::string to_string(const Container& cont, const std::string_view sep, const Function& to_str) { return to_string(cbegin(cont), cend(cont), sep, to_str); @@ -57,12 +57,39 @@ std::string to_string(const Container& cont, const std::string& sep, /// @returns The string with stringified elements of the `Container`. template -std::string to_string(const Container& cont, const std::string& sep) +std::string to_string(const Container& cont, const std::string_view sep) { return to_string(cont, sep, [](const std::string& e) -> const auto& - { - return e; - }); + { + return e; + }); +} + +/** + * @brief Splits the `input` string into the parts separated by the + * specified `separators`. + * + * @returns The vector of splitted parts. + */ +template +inline std::vector to_vector(const std::string_view input, + const std::string_view separators) +{ + std::vector result; + result.reserve(4); + std::string_view::size_type pos{std::string_view::npos}; + std::string_view::size_type offset{}; + while (offset < input.size()) { + pos = input.find_first_of(separators, offset); + DMITIGR_ASSERT(offset <= pos); + const auto part_size = + std::min(pos, input.size()) - offset; + result.push_back(S{input.substr(offset, part_size)}); + offset += part_size + 1; + } + if (pos != std::string_view::npos) // input ends with a separator + result.emplace_back(); + return result; } } // namespace dmitigr::str diff --git a/src/str/simple_phrase.hpp b/src/str/simple_phrase.hpp deleted file mode 100644 index e38b2eb..0000000 --- a/src/str/simple_phrase.hpp +++ /dev/null @@ -1,148 +0,0 @@ -// -*- C++ -*- -// -// Copyright 2022 Dmitry Igrishin -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef DMITIGR_STR_SIMPLE_PHRASE_HPP -#define DMITIGR_STR_SIMPLE_PHRASE_HPP - -#include "version.hpp" -#include "../base/assert.hpp" - -#include -#include -#include - -namespace dmitigr::str { - -/** - * @brief A "simple phrase" - an unquoted expression without spaces, or - * quoted expression (which can include any characters). - */ -class Simple_phrase final { -public: - /// @brief A phrase status. - enum class Status { - ok = 0, - stream_error = 1, - invalid_input = 2 - }; - - /// @brief Default-constructible. - Simple_phrase() = default; - - /** - * @brief Reads a next "simple phrase" from the `input`. - * - * Whitespaces (i.e. space, tab or newline) or the quote (i.e. '"') - * that follows after the phrase are preserved in the `input`. - * - * @returns The string with the "simple phrase". - * - * @throws Exception with the appropriate code and incomplete result - * of parsing. - */ - explicit Simple_phrase(std::istream& input, std::locale loc = {}) - { - const auto is_input_ok = [&input] - { - return !input.fail() || input.eof(); - }; - - // Skip whitespaces (i.e. ' ', '\t' and '\n'). - char ch; - while (input.get(ch) && std::isspace(ch, loc)); - if (!is_input_ok()) { - status_ = Status::stream_error; - return; - } - - if (input) { - if (ch == '"') { - // Try to reach the trailing quote character. - const char quote_char = ch; - constexpr char escape_char = '\\'; - while (input.get(ch) && ch != quote_char) { - if (ch == escape_char) { - // Escape character reached. Read the next character to analyze. - if (input.get(ch)) { - if (ch != quote_char) - /* - * The "escape" character does not really escape anything, - * thus must be preserved into the result string. - */ - data_ += escape_char; - data_ += ch; - } - } else - data_ += ch; - } - if (!is_input_ok()) { - status_ = Status::stream_error; - return; - } - - if (ch != quote_char) { - // The trailing quote character was NOT reached. - DMITIGR_ASSERT(input.eof()); - status_ = Status::invalid_input; - return; - } - } else { - /* - * There is no leading quote detected. - * So read characters until EOF, space, newline or the quote. - */ - data_ += ch; - while (input.get(ch) && !std::isspace(ch, loc) && ch != '"') - data_ += ch; - if (!is_input_ok()) { - status_ = Status::stream_error; - return; - } - if (std::isspace(ch, loc) || ch == '"') - input.putback(ch); - } - } - } - - /// @returns A phrase status after construction. - Status status() const noexcept - { - return status_; - } - - /// @returns An (unquotted) phrase data. - const std::string& data() const noexcept - { - return data_; - } - - /// @returns Instance of type string move-constructed from this instance. - std::string move_to_string() - { - std::string result; - result.swap(data_); - status_ = Status::ok; - return result; - } - -private: - Status status_{Status::ok}; - std::string data_; -}; - -} // namespace dmitigr::str - -#endif // DMITIGR_STR_SIMPLE_PHRASE_HPP diff --git a/src/str/str.hpp b/src/str/str.hpp index 4ae0766..2ac693d 100644 --- a/src/str/str.hpp +++ b/src/str/str.hpp @@ -17,16 +17,17 @@ #ifndef DMITIGR_STR_STR_HPP #define DMITIGR_STR_STR_HPP +#include "basics.hpp" +#include "c_str.h" #include "c_str.hpp" +#include "exceptions.hpp" #include "line.hpp" #include "numeric.hpp" #include "predicate.hpp" #include "sequence.hpp" -#include "simple_phrase.hpp" #include "stream.hpp" #include "substr.hpp" #include "time.hpp" #include "transform.hpp" -#include "version.hpp" #endif // DMITIGR_STR_STR_HPP diff --git a/src/str/stream.hpp b/src/str/stream.hpp index 18f1ac5..943d878 100644 --- a/src/str/stream.hpp +++ b/src/str/stream.hpp @@ -17,14 +17,19 @@ #ifndef DMITIGR_STR_STREAM_HPP #define DMITIGR_STR_STREAM_HPP +#include "../base/ret.hpp" #include "../fs/filesystem.hpp" +#include "basics.hpp" #include "exceptions.hpp" -#include "version.hpp" +#include "predicate.hpp" +#include #include +#include #include #include #include +#include #include #include @@ -40,8 +45,8 @@ namespace dmitigr::str { * @param is_binary The indicator of binary read mode. */ template -std::vector read_to_strings_if(std::istream& input, - const Pred& pred, const char delimiter = '\n') +std::vector +read_to_strings_if(std::istream& input, const Pred& pred, const char delimiter = '\n') { std::vector result; std::string line; @@ -59,7 +64,8 @@ std::vector read_to_strings_if(std::istream& input, * @param is_binary The indicator of binary read mode. */ template -std::vector read_to_strings_if(const std::filesystem::path& path, +std::vector +read_to_strings_if(const std::filesystem::path& path, const Pred& pred, const char delimiter = '\n', const bool is_binary = true) { constexpr std::ios_base::openmode in{std::ios_base::in}; @@ -72,8 +78,8 @@ std::vector read_to_strings_if(const std::filesystem::path& path, * * @see read_to_strings_if(). */ -inline std::vector read_to_strings(std::istream& input, - const char delimiter = '\n') +inline std::vector +read_to_strings(std::istream& input, const char delimiter = '\n') { return read_to_strings_if(input, [](const auto&){return true;}, delimiter); } @@ -94,16 +100,44 @@ read_to_strings(const std::filesystem::path& path, /** * @brief Reads a whole `input` stream to a string. * + * @par Requires + * `!(BufSize % 8)`. + * * @returns The string with the content read from the `input`. */ -inline std::string read_to_string(std::istream& input) +template +std::string read_to_string(std::istream& input, const std::optional trim = {}) { std::string result; - std::array buffer; + std::array buffer; static_assert(!(buffer.size() % 8)); + bool lhs_trimmed{}; + const auto append_buffer = [&] + { + std::size_t space_count{}; + if (trim && !lhs_trimmed && static_cast(*trim & Trim::lhs)) { + for (const auto ch : buffer) { + if (!is_space(ch)) { + lhs_trimmed = true; + break; + } else + ++space_count; + } + } + result.append(buffer.data() + space_count, + static_cast(input.gcount()) - space_count); + }; while (input.read(buffer.data(), buffer.size())) - result.append(buffer.data(), buffer.size()); - result.append(buffer.data(), static_cast(input.gcount())); + append_buffer(); + append_buffer(); + + if (trim && static_cast(*trim & Trim::rhs)) { + const auto rb = crbegin(result); + const auto re = crend(result); + const auto te = find_if(rb, re, is_non_space).base(); + result.resize(te - cbegin(result)); + } + return result; } @@ -115,15 +149,39 @@ inline std::string read_to_string(std::istream& input) * * @returns The string with the file data. */ -inline std::string read_to_string(const std::filesystem::path& path, - const bool is_binary = true) +template +Ret read_to_string_nothrow(const std::filesystem::path& path, + const bool is_binary = true, + const std::optional trim = {}) { + using Ret = Ret; constexpr std::ios_base::openmode in{std::ios_base::in}; std::ifstream input{path, is_binary ? in | std::ios_base::binary : in}; if (input) - return read_to_string(input); + return Ret::make_result(read_to_string(input, trim)); + else + return Ret::make_error(Err{Errc::generic, + "unable to open \"" + path.generic_string() + "\""}); +} + +/** + * @brief Reads the file into an instance of `std::string`. + * + * @param path The path to the file to read the data from. + * @param is_binary The indicator of binary read mode. + * + * @returns The string with the file data. + */ +template +std::string read_to_string(const std::filesystem::path& path, + const bool is_binary = true, + const std::optional trim = {}) +{ + auto [err, res] = read_to_string_nothrow(path, is_binary, trim); + if (!err) + return res; else - throw Exception{"unable to open \"" + path.generic_string() + "\""}; + throw Exception{err}; } } // namespace dmitigr::str diff --git a/src/str/substr.hpp b/src/str/substr.hpp index ac07404..a935cb4 100644 --- a/src/str/substr.hpp +++ b/src/str/substr.hpp @@ -17,14 +17,12 @@ #ifndef DMITIGR_STR_SUBSTR_HPP #define DMITIGR_STR_SUBSTR_HPP +#include "predicate.hpp" #include "exceptions.hpp" -#include "version.hpp" #include -#include -#include +#include #include -#include namespace dmitigr::str { @@ -37,142 +35,19 @@ namespace dmitigr::str { * [pos, str.size()), or `std::string_view::npos` if there is no such a position. * * @par Requires - * `(pos <= str.size())`. + * `pos <= str.size()`. */ inline std::string_view::size_type -position_of_non_space(const std::string_view str, - const std::string_view::size_type pos, const std::locale& loc = {}) +first_non_space_pos(const std::string_view str, const std::string_view::size_type pos) { if (!(pos <= str.size())) throw Exception{"cannot get position of non space by using invalid offset"}; const auto b = cbegin(str); const auto e = cend(str); - const auto i = std::find_if(b + pos, e, [&loc](const auto ch) - { - return is_non_space_character(ch, loc); - }); - return (i != e) ? static_cast(i - b) : - std::string_view::npos; -} - -/** - * @returns The substring of `str` from position of `pos` until the position - * of the character `c` that `(pred(c) == false)` as the first element, and - * the position of the character followed `c` as the second element. - * - * @par Requires - * `(pos <= str.size())`. - */ -template -std::pair -substring_if(const std::string& str, const Pred& pred, - std::string::size_type pos, const std::locale& loc = {}) -{ - if (!(pos <= str.size())) - throw Exception{"cannot get substring by using invalid offset"}; - - std::pair result; - const auto input_size = str.size(); - for (; pos < input_size; ++pos) { - if (pred(str[pos], loc)) - result.first += str[pos]; - else - break; - } - result.second = pos; - return result; -} - -/** - * @returns The substring of `str` with the "simple identifier" that starts - * from the position of `pos` in pair with the position of a character following - * that substring. - * - * @par Requires - * `(pos <= str.size())`. - */ -inline std::pair -substring_if_simple_identifier(const std::string& str, - const std::string::size_type pos, const std::locale& loc = {}) -{ - if (!(pos <= str.size())) - throw Exception{"cannot get substring by using invalid offset"}; - - return std::isalpha(str[pos], loc) ? - substring_if(str, is_simple_identifier_character, pos) : - std::make_pair(std::string{}, pos); -} - -/** - * @returns The substring of `str` without spaces that starts from the position - * of `pos` in pair with the position of a character following that substring. - * - * @par Requires - * `(pos <= str.size())`. - */ -inline std::pair -substring_if_no_spaces(const std::string& str, const std::string::size_type pos, - const std::locale& loc = {}) -{ - return substring_if(str, is_non_space_character, pos, loc); -} - -/** - * @returns The unquoted substring of `str` if `(str[pos] == '\'')` or the - * substring without spaces from the position of `pos` in pair with the position - * of a character following that substring. - * - * @par Requires - * `(pos <= str.size())`. - */ -inline std::pair -unquoted_substring(const std::string& str, std::string::size_type pos, - const std::locale& loc = {}) -{ - if (!(pos <= str.size())) - throw Exception{"cannot get unquoted substring by using invalid offset"}; - - if (pos == str.size()) - return {std::string{}, pos}; - - std::pair result; - constexpr char quote_char = '\''; - constexpr char escape_char = '\\'; - if (str[pos] == quote_char) { - // Trying to reach the trailing quote character. - const auto input_size = str.size(); - enum { normal, escape } state = normal; - for (++pos; pos < input_size; ++pos) { - const auto ch = str[pos]; - switch (state) { - case normal: - if (ch == quote_char) - goto finish; - else if (ch == escape_char) - state = escape; - else - result.first += ch; - break; - case escape: - if (ch != quote_char) - result.first += escape_char; // it's not escape, so preserve - result.first += ch; - state = normal; - break; - } - } - - finish: - if ((pos == input_size && str.back() != quote_char) || - (pos < input_size && str[pos] != quote_char)) - throw Exception{"cannot get unquoted substring because no trailing quote " - "found"}; - else - result.second = pos + 1; // discarding the trailing quote - } else - result = substring_if_no_spaces(str, pos, loc); - return result; + const auto i = std::find_if(b + pos, e, is_non_space); + return i != e ? static_cast(i - b) + : std::string_view::npos; } } // namespace dmitigr::str diff --git a/src/str/time.hpp b/src/str/time.hpp index 0b01672..bd37e5a 100644 --- a/src/str/time.hpp +++ b/src/str/time.hpp @@ -17,48 +17,49 @@ #ifndef DMITIGR_STR_TIME_HPP #define DMITIGR_STR_TIME_HPP +#include "../base/assert.hpp" #include "exceptions.hpp" -#include "version.hpp" #include +#include +#include #include -#include +#include namespace dmitigr::str { -// ----------------------------------------------------------------------------- -// Time -// ----------------------------------------------------------------------------- - /** * @returns The human-readable string representation of the given timepoint - * with microseconds or empty string on error. + * with microseconds or empty string_view on error. */ template -std::string to_string(const std::chrono::time_point tp) +std::string_view +to_string_view(const std::chrono::time_point tp) noexcept { namespace chrono = std::chrono; const auto tp_time_t = Clock::to_time_t(tp); const auto tse = tp.time_since_epoch(); const auto sec = chrono::duration_cast(tse); - const auto rest_us = chrono::duration_cast(tse - sec); - char buf[32]; - std::string result; - if (const auto count = std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", - std::localtime(&tp_time_t))) { - const auto us = std::to_string(rest_us.count()); - result.reserve(count + 1 + us.size()); - result.assign(buf, count); - result.append(".").append(us); + const auto us = chrono::duration_cast(tse - sec); + static thread_local char buf[64]; + if (const auto dt_length = std::strftime(buf, + sizeof(buf), "%Y-%m-%d %H:%M:%S", std::localtime(&tp_time_t))) { + const auto max_rest_length = sizeof(buf) - dt_length; + const auto rest_length = std::snprintf(buf + dt_length, + max_rest_length, "%c%ld", '.', us.count()); + DMITIGR_ASSERT(rest_length > 0 && + static_cast(rest_length) < max_rest_length); + return {buf, dt_length + rest_length}; } - return result; + buf[0] = '\0'; + return {buf, 0}; } -/// @retruns `to_string(Clock::now())`. +/// @retruns `to_string_view(Clock::now())`. template -inline std::string now_string() +inline std::string_view now() noexcept { - return to_string(Clock::now()); + return to_string_view(Clock::now()); } } // namespace dmitigr::str diff --git a/src/str/transform.hpp b/src/str/transform.hpp index 191021d..9d33df5 100644 --- a/src/str/transform.hpp +++ b/src/str/transform.hpp @@ -17,14 +17,14 @@ #ifndef DMITIGR_STR_TRANSFORM_HPP #define DMITIGR_STR_TRANSFORM_HPP -#include "version.hpp" #include "../base/assert.hpp" +#include "basics.hpp" +#include "predicate.hpp" #include -#include +#include #include #include -#include namespace dmitigr::str { @@ -34,7 +34,7 @@ namespace dmitigr::str { /// @returns The string with the specified `delimiter` between the characters. inline std::string -sparsed_string(const std::string_view input, const std::string& delimiter) +sparsed_string(const std::string_view input, const std::string_view delimiter) { std::string result; if (!input.empty()) { @@ -52,7 +52,7 @@ sparsed_string(const std::string_view input, const std::string& delimiter) /** * @par Effects - * `(str.back() == c)`. + * `str.back() == c`. */ inline void terminate(std::string& str, const char c) { @@ -60,53 +60,31 @@ inline void terminate(std::string& str, const char c) str += c; } -// Trims `str` by dropping whitespaces at both sides of it. -inline void trim(std::string& str, const std::locale& loc = {}) +/// Trims `str` by dropping whitespaces at both sides of it. +inline std::string trimmed(std::string str, const Trim trim = Trim::all) { if (str.empty()) - return; + return str; - const auto is_not_space = [&loc](const auto c){return !isspace(c, loc);}; const auto b = begin(str); const auto e = end(str); - const auto tb = find_if(b, e, is_not_space); + const auto tb = static_cast(trim & Trim::lhs) ? + find_if(b, e, is_non_space) : b; if (tb == e) { str.clear(); // the string consists of spaces, so just clear it out - return; + return str; } - - const auto rb = rbegin(str); - const auto re = rend(str); - const auto te = find_if(rb, re, is_not_space).base(); - move(tb, te, b); - str.resize(te - tb); -} - -/** - * @brief Splits the `input` string into the parts separated by the - * specified `separators`. - * - * @returns The vector of splitted parts. - */ -template -inline std::vector split(const std::string_view input, - const std::string_view separators) -{ - std::vector result; - result.reserve(4); - std::string_view::size_type pos{std::string_view::npos}; - std::string_view::size_type offset{}; - while (offset < input.size()) { - pos = input.find_first_of(separators, offset); - DMITIGR_ASSERT(offset <= pos); - const auto part_size = - std::min(pos, input.size()) - offset; - result.push_back(S{input.substr(offset, part_size)}); - offset += part_size + 1; + const auto te = static_cast(trim & Trim::rhs) ? + find_if(rbegin(str), rend(str), is_non_space).base() : e; + + const std::string::size_type new_size = te - tb; + if (new_size != str.size()) { + if (tb != b) + move(tb, te, b); + str.resize(new_size); } - if (pos != std::string_view::npos) // input ends with a separator - result.emplace_back(); - return result; + + return str; } // ----------------------------------------------------------------------------- @@ -116,30 +94,29 @@ inline std::vector split(const std::string_view input, * @brief Replaces all of uppercase characters in `str` by the corresponding * lowercase characters. */ -inline void lowercase(std::string& str, const std::locale& loc = {}) +inline void lowercase(std::string& str) { auto b = begin(str); auto e = end(str); - transform(b, e, b, [&loc](const char c){return tolower(c, loc);}); + transform(b, e, b, [](const unsigned char c){return tolower(c);}); } /** * @returns The modified copy of the `str` with all of uppercase characters * replaced by the corresponding lowercase characters. */ -inline std::string to_lowercase(std::string str, const std::locale& loc = {}) +inline std::string to_lowercase(std::string result) { - lowercase(str, loc); - return str; + lowercase(result); + return result; } /// @returns `true` if all of characters of `str` are in uppercase. -inline bool is_lowercased(const std::string_view str, - const std::locale& loc = {}) noexcept +inline bool is_lowercased(const std::string_view str) noexcept { - return std::all_of(cbegin(str), cend(str), [&loc](const char c) + return std::all_of(cbegin(str), cend(str), [](const unsigned char c) { - return islower(c, loc); + return islower(c); }); } @@ -150,29 +127,29 @@ inline bool is_lowercased(const std::string_view str, * @brief Replaces all of lowercase characters in `str` by the corresponding * uppercase characters. */ -inline void uppercase(std::string& str, const std::locale& loc = {}) +inline void uppercase(std::string& str) { auto b = begin(str); auto e = end(str); - transform(b, e, b, [&loc](const char c){return toupper(c, loc);}); + transform(b, e, b, [](const unsigned char c){return toupper(c);}); } /** * @returns The modified copy of the `str` with all of lowercase characters * replaced by the corresponding uppercase characters. */ -inline std::string to_uppercase(std::string str, const std::locale& loc = {}) +inline std::string to_uppercase(std::string result) { - uppercase(str, loc); - return str; + uppercase(result); + return result; } /// @returns `true` if all of character of `str` are in lowercase. -inline bool is_uppercased(const std::string_view str, const std::locale& loc = {}) noexcept +inline bool is_uppercased(const std::string_view str) noexcept { - return std::all_of(cbegin(str), cend(str), [&loc](const char c) + return std::all_of(cbegin(str), cend(str), [](const unsigned char c) { - return isupper(c, loc); + return isupper(c); }); } diff --git a/src/str/version.hpp b/src/str/version.hpp deleted file mode 100644 index c4a2314..0000000 --- a/src/str/version.hpp +++ /dev/null @@ -1,41 +0,0 @@ -// -*- C++ -*- -// -// Copyright 2022 Dmitry Igrishin -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -// This file is generated automatically. Edit version.hpp.in instead!!!!!!!!!!!! -// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - -#ifndef DMITIGR_STR_VERSION_HPP -#define DMITIGR_STR_VERSION_HPP - -#include - -namespace dmitigr::str { - -/// @returns The library version. -constexpr std::int_fast32_t version() noexcept -{ - // Actual values are set in CMakeLists.txt. - constexpr std::int_least32_t major = 0; - constexpr std::int_least32_t minor = 1; - - // 11.234 -> 11 * 1000 + 234 = 11234 - return major*1000 + minor; -} - -} // namespace dmitigr::str - -#endif // DMITIGR_STR_VERSION_HPP diff --git a/src/util/util.hpp b/src/util/util.hpp index 3f072e4..bfdacae 100644 --- a/src/util/util.hpp +++ b/src/util/util.hpp @@ -17,9 +17,8 @@ #ifndef DMITIGR_UTIL_UTIL_HPP #define DMITIGR_UTIL_UTIL_HPP +#include "contract.hpp" #include "diagnostic.hpp" -#include "endianness.hpp" -#include "enum_bitmask.hpp" #include "memory.hpp" #endif // DMITIGR_UTIL_UTIL_HPP diff --git a/test/net/net-unit-net.cpp b/test/net/net-unit-net.cpp index 2a52ba6..817da1a 100644 --- a/test/net/net-unit-net.cpp +++ b/test/net/net-unit-net.cpp @@ -41,6 +41,46 @@ int main() DMITIGR_ASSERT(ip.binary()); DMITIGR_ASSERT(ip.to_string() == v6_addr_str); + // Comparison. + { + net::Ip_address ip1; + net::Ip_address ip2; + DMITIGR_ASSERT(!ip1.is_valid()); + DMITIGR_ASSERT(!ip2.is_valid()); + DMITIGR_ASSERT(ip1 == ip2); + } + { + net::Ip_address ip1; + const auto ip2 = net::Ip_address::from_text("192.168.1.1"); + DMITIGR_ASSERT(!ip1.is_valid()); + DMITIGR_ASSERT(ip2.is_valid()); + DMITIGR_ASSERT(ip1 < ip2); + } + { + const auto ip1 = net::Ip_address::from_text("192.168.1.1"); + const auto ip2 = net::Ip_address::from_text("192.168.1.1"); + DMITIGR_ASSERT(ip1 == ip2); + const auto ip1_copy = ip1; + const auto ip2_copy = ip2; + DMITIGR_ASSERT(ip1 == ip1_copy); + DMITIGR_ASSERT(ip2 == ip2_copy); + } + { + const auto ip1 = net::Ip_address::from_text("192.168.1.1"); + const auto ip2 = net::Ip_address::from_text("192.168.1.2"); + DMITIGR_ASSERT(ip1 < ip2); + } + { + const auto ip1 = net::Ip_address::from_text("192.168.1.1"); + const auto ip2 = net::Ip_address::from_text("fe80::1:2:3:4"); + DMITIGR_ASSERT(ip1 < ip2); + } + { + const auto ip1 = net::Ip_address::from_text("fe80::1:2:3:4"); + const auto ip2 = net::Ip_address::from_text("fe80::1:2:3:4"); + DMITIGR_ASSERT(ip1 == ip2); + } + const int n = 10; auto n1 = net::conv(n); DMITIGR_ASSERT(n != n1); diff --git a/test/pgfe/pgfe-unit-benchmark_array.hpp b/test/pgfe/pgfe-unit-benchmark_array.hpp index c1c51a6..81e2b8b 100644 --- a/test/pgfe/pgfe-unit-benchmark_array.hpp +++ b/test/pgfe/pgfe-unit-benchmark_array.hpp @@ -14,8 +14,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef DMITIGR_CPPLIPA_TESTS_PGFE_UNIT_BENCHMARK_ARRAY_HPP -#define DMITIGR_CPPLIPA_TESTS_PGFE_UNIT_BENCHMARK_ARRAY_HPP +#ifndef DMITIGR_LIBS_TESTS_PGFE_UNIT_BENCHMARK_ARRAY_HPP +#define DMITIGR_LIBS_TESTS_PGFE_UNIT_BENCHMARK_ARRAY_HPP #include "pgfe-unit.hpp" @@ -56,4 +56,4 @@ inline std::tuple> pre } // namespace dmitigr::pgfe::test::arraybench -#endif // DMITIGR_CPPLIPA_TESTS_PGFE_UNIT_BENCHMARK_ARRAY_HPP +#endif // DMITIGR_LIBS_TESTS_PGFE_UNIT_BENCHMARK_ARRAY_HPP diff --git a/test/pgfe/pgfe-unit.hpp b/test/pgfe/pgfe-unit.hpp index c338d74..0e6a29b 100644 --- a/test/pgfe/pgfe-unit.hpp +++ b/test/pgfe/pgfe-unit.hpp @@ -14,8 +14,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef DMITIGR_CPPLIPA_TEST_PGFE_UNIT_HPP -#define DMITIGR_CPPLIPA_TEST_PGFE_UNIT_HPP +#ifndef DMITIGR_LIBS_TEST_PGFE_UNIT_HPP +#define DMITIGR_LIBS_TEST_PGFE_UNIT_HPP #include "../../src/base/assert.hpp" #include "../../src/os/environment.hpp" @@ -86,4 +86,4 @@ inline auto make_ssl_connection() } // namespace dmitigr::pgfe::test -#endif // DMITIGR_CPPLIPA_TEST_PGFE_UNIT_HPP +#endif // DMITIGR_LIBS_TEST_PGFE_UNIT_HPP diff --git a/test/str/str-unit-test.cpp b/test/str/str-unit-test.cpp index 7f0c24c..a24a3a5 100644 --- a/test/str/str-unit-test.cpp +++ b/test/str/str-unit-test.cpp @@ -15,6 +15,7 @@ // limitations under the License. #include "../../src/base/assert.hpp" +#include "../../src/str/sequence.hpp" #include "../../src/str/transform.hpp" int main() @@ -28,50 +29,43 @@ int main() // Empty string { - std::string s; - str::trim(s); + auto s = str::trimmed(""); DMITIGR_ASSERT(s.empty()); } // String with only spaces { - std::string s{" \f\n\r\t\v"}; - str::trim(s); + auto s = str::trimmed(" \f\n\r\t\v"); DMITIGR_ASSERT(s.empty()); } // String without spaces { - std::string s{"content"}; - str::trim(s); + auto s = str::trimmed("content"); DMITIGR_ASSERT(s == "content"); } // String with spaces from left { - std::string s{" \f\n\r\t\vcontent"}; - str::trim(s); + auto s = str::trimmed(" \f\n\r\t\vcontent"); DMITIGR_ASSERT(s == "content"); } // String with spaces from right { - std::string s{"content \f\n\r\t\v"}; - str::trim(s); + auto s = str::trimmed("content \f\n\r\t\v"); DMITIGR_ASSERT(s == "content"); } // String with spaces from both sides { - std::string s{" \f\n\r\t\vcontent \f\n\r\t\v"}; - str::trim(s); + auto s = str::trimmed(" \f\n\r\t\vcontent \f\n\r\t\v"); DMITIGR_ASSERT(s == "content"); } // String with spaces from both sides with spaces in the content { - std::string s{" \f\n\r\t\vcon ten t \f\n\r\t\v"}; - str::trim(s); + auto s = str::trimmed(" \f\n\r\t\vcon ten t \f\n\r\t\v"); DMITIGR_ASSERT(s == "con ten t"); } @@ -82,42 +76,42 @@ int main() // Emptry string, no separators { std::string s; - const auto v = str::split(s, ""); + const auto v = str::to_vector(s, ""); DMITIGR_ASSERT(s.empty()); } // Emptry string and separator { std::string s; - const auto v = str::split(s, ","); + const auto v = str::to_vector(s, ","); DMITIGR_ASSERT(s.empty()); } // String with only separator { std::string s{","}; - const auto v = str::split(s, s); + const auto v = str::to_vector(s, s); DMITIGR_ASSERT(v.size() == 2); } // String with only separators { std::string s{",,..!!"}; - const auto v = str::split(s, s); + const auto v = str::to_vector(s, s); DMITIGR_ASSERT(v.size() == 7); } // String without separator { std::string s{"content"}; - const auto v = str::split(s, ","); + const auto v = str::to_vector(s, ","); DMITIGR_ASSERT(v.size() == 1); } // String with separator { std::string s{"1 2 3"}; - const auto v = str::split(s, " "); + const auto v = str::to_vector(s, " "); DMITIGR_ASSERT(v.size() == 3); DMITIGR_ASSERT(v[0] == "1"); DMITIGR_ASSERT(v[1] == "2"); @@ -127,7 +121,7 @@ int main() // String with separators { std::string s{"1 2,3"}; - const auto v = str::split(s, " ,"); + const auto v = str::to_vector(s, " ,"); DMITIGR_ASSERT(v.size() == 3); DMITIGR_ASSERT(v[0] == "1"); DMITIGR_ASSERT(v[1] == "2"); @@ -137,7 +131,7 @@ int main() // String with separators to vector of string_view { std::string s{"1 2,3"}; - const auto v = str::split(s, " ,"); + const auto v = str::to_vector(s, " ,"); DMITIGR_ASSERT(v.size() == 3); DMITIGR_ASSERT(v[0] == "1"); DMITIGR_ASSERT(v[1] == "2"); diff --git a/test/str/str-unit-time.cpp b/test/str/str-unit-time.cpp new file mode 100644 index 0000000..0328773 --- /dev/null +++ b/test/str/str-unit-time.cpp @@ -0,0 +1,38 @@ +// -*- C++ -*- +// +// Copyright 2022 Dmitry Igrishin +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "../../src/base/assert.hpp" +#include "../../src/str/time.hpp" + +#include +#include + +int main() +{ + try { + namespace str = dmitigr::str; + + std::cout.precision(std::numeric_limits::max_digits10); + std::cout << str::now() << std::endl; + std::cout << str::now() << std::endl; + } catch (const std::exception& e) { + std::cerr << e.what() << std::endl; + return 1; + } catch (...) { + std::cerr << "unknown error" << std::endl; + return 2; + } +}