├── .clang-format ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── cmake └── DetectCPPZMQVersion.cmake ├── cppzmq.pc.in ├── cppzmqConfig.cmake.in ├── demo ├── CMakeLists.txt └── main.cpp ├── examples ├── CMakeLists.txt ├── hello_world.cpp ├── multipart_messages.cpp └── pubsub_multithread_inproc.cpp ├── libzmq-pkg-config └── FindZeroMQ.cmake ├── tests ├── CMakeLists.txt ├── active_poller.cpp ├── buffer.cpp ├── codec_multipart.cpp ├── context.cpp ├── message.cpp ├── monitor.cpp ├── multipart.cpp ├── poller.cpp ├── recv_multipart.cpp ├── send_multipart.cpp ├── socket.cpp ├── socket_ref.cpp ├── testutil.hpp ├── timers.cpp └── utilities.cpp ├── version.sh ├── zmq.hpp └── zmq_addon.hpp /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | IndentWidth: 4 3 | UseTab: Never 4 | BreakBeforeBraces: Custom 5 | BraceWrapping: 6 | AfterClass: true 7 | AfterControlStatement: false 8 | AfterEnum: true 9 | AfterFunction: true 10 | AfterNamespace: true 11 | AfterObjCDeclaration: true 12 | AfterStruct: true 13 | AfterUnion: true 14 | BeforeCatch: true 15 | BeforeElse: false 16 | IndentBraces: false 17 | 18 | AlignConsecutiveAssignments: false 19 | AlignConsecutiveDeclarations: false 20 | AllowShortIfStatementsOnASingleLine: false 21 | IndentCaseLabels: true 22 | BinPackArguments: true 23 | BinPackParameters: false 24 | AlignTrailingComments: true 25 | AllowShortBlocksOnASingleLine: false 26 | AllowAllParametersOfDeclarationOnNextLine: true 27 | AllowShortFunctionsOnASingleLine: InlineOnly 28 | AlwaysBreakTemplateDeclarations: false 29 | ColumnLimit: 85 30 | MaxEmptyLinesToKeep: 2 31 | KeepEmptyLinesAtTheStartOfBlocks: false 32 | ContinuationIndentWidth: 2 33 | PointerAlignment: Right 34 | ReflowComments: false 35 | SpaceBeforeAssignmentOperators: true 36 | SpaceBeforeParens: ControlStatements 37 | SpaceInEmptyParentheses: false 38 | SpacesInAngles: false 39 | SpacesInParentheses: false 40 | SpacesInSquareBrackets: false 41 | Standard: Cpp11 42 | 43 | SortIncludes: false 44 | 45 | FixNamespaceComments: false 46 | BreakBeforeBinaryOperators: NonAssignment 47 | SpaceAfterTemplateKeyword: false 48 | AlignAfterOpenBracket: Align 49 | AlignOperands: true 50 | BreakConstructorInitializers: AfterColon 51 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 52 | SpaceAfterCStyleCast: true 53 | BreakBeforeTernaryOperators: true 54 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | defaults: 6 | run: 7 | shell: bash 8 | 9 | jobs: 10 | tests: 11 | runs-on: ${{ matrix.os }} 12 | strategy: 13 | matrix: 14 | os: ["ubuntu-22.04"] 15 | cppstd: ["98", "11", "20"] 16 | cc: ["gcc-10"] 17 | cxx: ["g++-10"] 18 | drafts: ["ON"] 19 | libzmq: ["4.3.5"] 20 | libzmqbuild: ["cmake"] 21 | include: 22 | # older libzmq and without draft 23 | - os: "ubuntu-22.04" 24 | cppstd: "11" 25 | cc: "gcc-9" 26 | cxx: "g++-9" 27 | drafts: "OFF" 28 | libzmq: "4.2.0" 29 | libzmqbuild: "pkgconfig" 30 | # without draft 31 | - os: "ubuntu-24.04" 32 | cppstd: "23" 33 | cc: "gcc-13" 34 | cxx: "g++-13" 35 | drafts: "OFF" 36 | libzmq: "4.3.5" 37 | libzmqbuild: "cmake" 38 | # coverage (gcc version should match gcov version) 39 | - os: "ubuntu-22.04" 40 | cppstd: "17" 41 | cc: "gcc-9" 42 | cxx: "g++-9" 43 | drafts: "ON" 44 | libzmq: "4.3.5" 45 | libzmqbuild: "cmake" 46 | coverage: "-DCOVERAGE=ON" 47 | aptinstall: "lcov" 48 | # clang 49 | - os: "ubuntu-22.04" 50 | cppstd: "17" 51 | cc: "clang-14" 52 | cxx: "clang++-14" 53 | drafts: "ON" 54 | libzmq: "4.3.5" 55 | libzmqbuild: "cmake" 56 | # macos 57 | - os: "macos-latest" 58 | cppstd: "17" 59 | cc: "clang" 60 | cxx: "clang++" 61 | drafts: "OFF" 62 | libzmq: "4.3.5" 63 | libzmqbuild: false 64 | brewinstall: "zeromq" 65 | # windows 66 | #- os: "windows-2019" 67 | # cppstd: "14" 68 | # cc: "msbuild" 69 | # cxx: "msbuild" 70 | # drafts: "ON" 71 | # libzmq: "4.3.5" 72 | # libzmqbuild: "cmake" 73 | # platform: "-Ax64" 74 | - os: "windows-2022" 75 | cppstd: "20" 76 | cc: "msbuild" 77 | cxx: "msbuild" 78 | drafts: "ON" 79 | libzmq: "4.3.5" 80 | libzmqbuild: "cmake" 81 | platform: "-Ax64" 82 | 83 | env: 84 | CC: ${{ matrix.cc }} 85 | CXX: ${{ matrix.cxx }} 86 | VERBOSE: 1 87 | THREADS: 2 88 | BUILDTYPE: "Debug" 89 | 90 | steps: 91 | - uses: actions/checkout@v2 92 | 93 | - name: install_deps 94 | run: | 95 | if [ ! -z "${{ matrix.aptinstall }}" ]; then 96 | sudo apt install -y ${{ matrix.aptinstall }} 97 | fi 98 | if [ ! -z "${{ matrix.brewinstall }}" ]; then 99 | brew install ${{ matrix.brewinstall }} 100 | fi 101 | 102 | - name: get_libzmq 103 | run: | 104 | curl -L https://github.com/zeromq/libzmq/archive/v${{ matrix.libzmq }}.tar.gz \ 105 | >zeromq.tar.gz 106 | tar -xvzf zeromq.tar.gz 107 | 108 | - name: build_libzmq_cmake 109 | if: ${{ matrix.libzmqbuild == 'cmake' }} 110 | run: | 111 | cmake -Hlibzmq-${{ matrix.libzmq }} -Blibzmq-build ${{ matrix.platform}} \ 112 | -DWITH_PERF_TOOL=OFF \ 113 | -DZMQ_BUILD_TESTS=OFF \ 114 | -DCMAKE_BUILD_TYPE=Release \ 115 | -DENABLE_DRAFTS=${{ matrix.drafts }} 116 | cmake --build libzmq-build --config ${BUILDTYPE} -j ${THREADS} 117 | echo "LIBZMQ=${PWD}/libzmq-build" >> ${GITHUB_ENV} 118 | 119 | - name: post_build_libzmq_cmake 120 | if: ${{ matrix.libzmqbuild == 'cmake' && startsWith(matrix.os, 'windows') }} 121 | run: | 122 | mkdir -p build/tests/${BUILDTYPE} 123 | cp ${{ env.LIBZMQ }}/bin/${BUILDTYPE}/*.dll build/tests/${BUILDTYPE} 124 | 125 | - name: build_libzmq_pkgconfig 126 | if: ${{ matrix.libzmqbuild == 'pkgconfig' }} 127 | working-directory: libzmq-${{ matrix.libzmq }} 128 | run: | 129 | ./autogen.sh && 130 | ./configure --prefix=${PWD}/libzmq-build && 131 | make -j ${THREADS} 132 | make install 133 | echo "LIBZMQ=${PWD}/libzmq-build" >> ${GITHUB_ENV} 134 | 135 | - name: build 136 | env: 137 | CMAKE_PREFIX_PATH: ${{ env.LIBZMQ }} 138 | run: | 139 | cmake -H. -Bbuild ${{ matrix.platform}} ${{ matrix.coverage }} \ 140 | -DCMAKE_BUILD_TYPE=${BUILDTYPE} \ 141 | -DCMAKE_CXX_STANDARD=${{ matrix.cppstd }} 142 | cmake --build build --config ${BUILDTYPE} -j ${THREADS} 143 | echo "CPPZMQ=${PWD}/build" >> ${GITHUB_ENV} 144 | 145 | - name: test 146 | # for unknown reason no tests are found and run on windows 147 | # could be something to do with catch_discover_tests not working? 148 | run: | 149 | cd ${{ env.CPPZMQ }} 150 | ctest -V -C ${BUILDTYPE} 151 | 152 | - name: demo 153 | # probably need to install libzmq and cppzmq for this to work on windows 154 | if: ${{ matrix.os == 'ubuntu-24.04' }} 155 | env: 156 | CMAKE_PREFIX_PATH: ${{ env.LIBZMQ }}:${{ env.CPPZMQ }} 157 | run: | 158 | cd demo 159 | cmake -H. -Bbuild ${{ matrix.platform}} \ 160 | -DCMAKE_BUILD_TYPE=${BUILDTYPE} \ 161 | -DCMAKE_CXX_STANDARD=${{ matrix.cppstd }} 162 | cmake --build build --config ${BUILDTYPE} 163 | cd build 164 | ctest -V -C ${BUILDTYPE} 165 | 166 | - name: lcov 167 | if: ${{ matrix.coverage && success() }} 168 | run: | 169 | lcov --capture --directory . --output-file coverage.info 170 | lcov --remove coverage.info -o coverage_filtered.info \ 171 | '/usr/include/*' \ 172 | '/usr/local/include/*' \ 173 | ${PWD}'/tests/*' \ 174 | ${PWD}'/build/*' 175 | # to generate local html: genhtml coverage_filtered.info --output-directory . 176 | 177 | - name: coveralls_upload 178 | if: ${{ matrix.coverage && success() }} 179 | uses: coverallsapp/github-action@master 180 | with: 181 | github-token: ${{ secrets.GITHUB_TOKEN }} 182 | path-to-lcov: ./coverage_filtered.info 183 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Vim tmp files 2 | *.swp 3 | 4 | # Build directory 5 | *build/ 6 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.11) 2 | 3 | list (APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") 4 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 5 | 6 | include (DetectCPPZMQVersion) 7 | 8 | project(cppzmq VERSION ${DETECTED_CPPZMQ_VERSION}) 9 | 10 | if (NOT TARGET libzmq AND NOT TARGET libzmq-static) 11 | find_package(ZeroMQ QUIET) 12 | 13 | # libzmq autotools install: fallback to pkg-config 14 | if(NOT ZeroMQ_FOUND) 15 | message(STATUS "CMake libzmq package not found, trying again with pkg-config (normal install of zeromq)") 16 | list (APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/libzmq-pkg-config) 17 | find_package(ZeroMQ REQUIRED) 18 | endif() 19 | 20 | # TODO "REQUIRED" above should already cause a fatal failure if not found, but this doesn't seem to work 21 | if(NOT ZeroMQ_FOUND) 22 | message(FATAL_ERROR "ZeroMQ was not found, neither as a CMake package nor via pkg-config") 23 | endif() 24 | 25 | if (ZeroMQ_FOUND AND NOT (TARGET libzmq OR TARGET libzmq-static)) 26 | message(FATAL_ERROR "ZeroMQ version not supported!") 27 | endif() 28 | endif() 29 | 30 | message(STATUS "cppzmq v${cppzmq_VERSION}") 31 | 32 | set(CPPZMQ_HEADERS 33 | zmq.hpp 34 | zmq_addon.hpp 35 | ) 36 | 37 | foreach (target cppzmq cppzmq-static) 38 | add_library(${target} INTERFACE) 39 | target_include_directories(${target} INTERFACE $ 40 | $) 41 | endforeach() 42 | 43 | target_link_libraries(cppzmq INTERFACE libzmq) 44 | target_link_libraries(cppzmq-static INTERFACE libzmq-static) 45 | 46 | include(GNUInstallDirs) 47 | include(CMakePackageConfigHelpers) 48 | 49 | install(TARGETS cppzmq cppzmq-static 50 | EXPORT ${PROJECT_NAME}-targets) 51 | 52 | install(FILES ${CPPZMQ_HEADERS} 53 | DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) 54 | 55 | # GNUInstallDirs "DATADIR" wrong here; CMake search path wants "share". 56 | set(CPPZMQ_CMAKECONFIG_INSTALL_DIR "share/cmake/${PROJECT_NAME}" CACHE STRING "install path for cppzmqConfig.cmake") 57 | 58 | configure_file(libzmq-pkg-config/FindZeroMQ.cmake 59 | libzmq-pkg-config/FindZeroMQ.cmake 60 | COPYONLY) 61 | 62 | export(EXPORT ${PROJECT_NAME}-targets 63 | FILE "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Targets.cmake") 64 | configure_package_config_file(${PROJECT_NAME}Config.cmake.in 65 | "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" 66 | INSTALL_DESTINATION ${CPPZMQ_CMAKECONFIG_INSTALL_DIR}) 67 | # Workaround until ARCH_INDEPENDENT flag can be used with cmake 3.14. 68 | # The ConigVersion.cmake file contains checks for the architecture is was 69 | # generated on, which can cause problems for header only libraries 70 | # used with e.g. the Conan package manager. Since it is header only we 71 | # can/should omit those checks. 72 | set(CPPZMQ_SIZEOF_VOID_P ${CMAKE_SIZEOF_VOID_P}) 73 | set(CMAKE_SIZEOF_VOID_P "") # a simple unset is not sufficient 74 | write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake 75 | VERSION ${CPPZMQ_VERSION} 76 | COMPATIBILITY AnyNewerVersion) 77 | set(CMAKE_SIZEOF_VOID_P ${CPPZMQ_SIZEOF_VOID_P}) 78 | 79 | configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cppzmq.pc.in 80 | ${CMAKE_CURRENT_BINARY_DIR}/cppzmq.pc @ONLY) 81 | install(FILES ${CMAKE_CURRENT_BINARY_DIR}/cppzmq.pc 82 | DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) 83 | 84 | install(EXPORT ${PROJECT_NAME}-targets 85 | FILE ${PROJECT_NAME}Targets.cmake 86 | DESTINATION ${CPPZMQ_CMAKECONFIG_INSTALL_DIR}) 87 | install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake 88 | ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake 89 | DESTINATION ${CPPZMQ_CMAKECONFIG_INSTALL_DIR}) 90 | install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/libzmq-pkg-config/FindZeroMQ.cmake 91 | DESTINATION ${CPPZMQ_CMAKECONFIG_INSTALL_DIR}/libzmq-pkg-config) 92 | 93 | option(CPPZMQ_BUILD_TESTS "Whether or not to build the tests" ON) 94 | 95 | if (CPPZMQ_BUILD_TESTS) 96 | enable_testing() 97 | add_subdirectory(tests) 98 | if (CMAKE_CXX_STANDARD AND NOT CMAKE_CXX_STANDARD EQUAL 98 AND CMAKE_CXX_STANDARD GREATER_EQUAL 11) 99 | add_subdirectory(examples) 100 | endif() 101 | endif() 102 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any person obtaining a copy 2 | of this software and associated documentation files (the "Software"), to 3 | deal in the Software without restriction, including without limitation the 4 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 5 | sell copies of the Software, and to permit persons to whom the Software is 6 | furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in 9 | all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 14 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 16 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 17 | IN THE SOFTWARE. 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![CI](https://github.com/zeromq/cppzmq/actions/workflows/ci.yml/badge.svg)](https://github.com/zeromq/cppzmq/actions) 2 | [![Coverage Status](https://coveralls.io/repos/github/zeromq/cppzmq/badge.svg?branch=master)](https://coveralls.io/github/zeromq/cppzmq?branch=master) 3 | [![License](https://img.shields.io/github/license/zeromq/cppzmq.svg)](https://github.com/zeromq/cppzmq/blob/master/LICENSE) 4 | 5 | Introduction & Design Goals 6 | =========================== 7 | 8 | cppzmq is a C++ binding for libzmq. It has the following design goals: 9 | - cppzmq maps the libzmq C API to C++ concepts. In particular: 10 | - it is type-safe (the libzmq C API exposes various class-like concepts as void*) 11 | - it provides exception-based error handling (the libzmq C API provides errno-based error handling) 12 | - it provides RAII-style classes that automate resource management (the libzmq C API requires the user to take care to free resources explicitly) 13 | - cppzmq is a light-weight, header-only binding. You only need to include the header file zmq.hpp (and maybe zmq_addon.hpp) to use it. 14 | - zmq.hpp is meant to contain direct mappings of the abstractions provided by the libzmq C API, while zmq_addon.hpp provides additional higher-level abstractions. 15 | 16 | There are other C++ bindings for ZeroMQ with different design goals. In particular, none of the following bindings are header-only: 17 | - [zmqpp](https://github.com/zeromq/zmqpp) is a high-level binding to libzmq. 18 | - [czmqpp](https://github.com/zeromq/czmqpp) is a binding based on the high-level czmq API. 19 | - [fbzmq](https://github.com/facebook/fbzmq) is a binding that integrates with Apache Thrift and provides higher-level abstractions in addition. It requires C++14. 20 | 21 | Supported platforms 22 | =================== 23 | 24 | - Only a subset of the platforms that are supported by libzmq itself are supported. Some features already require a compiler supporting C++11. In the future, probably all features will require C++11. To build and run the tests, CMake and Catch are required. 25 | - Any libzmq 4.x version is expected to work. DRAFT features may only work for the most recent tested version. Currently explicitly tested libzmq versions are 26 | - 4.2.0 (without DRAFT API) 27 | - 4.3.4 (with and without DRAFT API) 28 | - Platforms with full support (i.e. CI executing build and tests) 29 | - Ubuntu 18.04 x64 (with gcc 4.8.5, 5.5.0, 7.5.0) 30 | - Ubuntu 20.04 x64 (with gcc 9.3.0, 10.3.0 and clang 12) 31 | - Visual Studio 2017 x64 32 | - Visual Studio 2019 x64 33 | - macOS 10.15 (with clang 12, without DRAFT API) 34 | - Additional platforms that are known to work: 35 | - We have no current reports on additional platforms that are known to work yet. Please add your platform here. If CI can be provided for them with a cloud-based CI service working with GitHub, you are invited to add CI, and make it possible to be included in the list above. 36 | - Additional platforms that probably work: 37 | - Any platform supported by libzmq that provides a sufficiently recent gcc (4.8.1 or newer) or clang (3.4.1 or newer) 38 | - Visual Studio 2012+ x86/x64 39 | 40 | Examples 41 | ======== 42 | These examples require at least C++11. 43 | ```c++ 44 | #include 45 | 46 | int main() 47 | { 48 | zmq::context_t ctx; 49 | zmq::socket_t sock(ctx, zmq::socket_type::push); 50 | sock.bind("inproc://test"); 51 | sock.send(zmq::str_buffer("Hello, world"), zmq::send_flags::dontwait); 52 | } 53 | ``` 54 | This a more complex example where we send and receive multi-part messages over TCP with a wildcard port. 55 | ```c++ 56 | #include 57 | #include 58 | 59 | int main() 60 | { 61 | zmq::context_t ctx; 62 | zmq::socket_t sock1(ctx, zmq::socket_type::push); 63 | zmq::socket_t sock2(ctx, zmq::socket_type::pull); 64 | sock1.bind("tcp://127.0.0.1:*"); 65 | const std::string last_endpoint = 66 | sock1.get(zmq::sockopt::last_endpoint); 67 | std::cout << "Connecting to " 68 | << last_endpoint << std::endl; 69 | sock2.connect(last_endpoint); 70 | 71 | std::array send_msgs = { 72 | zmq::str_buffer("foo"), 73 | zmq::str_buffer("bar!") 74 | }; 75 | if (!zmq::send_multipart(sock1, send_msgs)) 76 | return 1; 77 | 78 | std::vector recv_msgs; 79 | const auto ret = zmq::recv_multipart( 80 | sock2, std::back_inserter(recv_msgs)); 81 | if (!ret) 82 | return 1; 83 | std::cout << "Got " << *ret 84 | << " messages" << std::endl; 85 | return 0; 86 | } 87 | ``` 88 | 89 | See the `examples` directory for more examples. When the project is compiled with tests enabled, each example gets compiled to an executable. 90 | 91 | 92 | API Overview 93 | ============ 94 | 95 | For an extensive overview of the `zmq.hpp` API in use, see this [Tour of CPPZMQ by @brettviren](https://brettviren.github.io/cppzmq-tour/index.html). 96 | 97 | Bindings for libzmq in `zmq.hpp`: 98 | 99 | Types: 100 | * class `zmq::context_t` 101 | * enum `zmq::ctxopt` 102 | * class `zmq::socket_t` 103 | * class `zmq::socket_ref` 104 | * enum `zmq::socket_type` 105 | * enum `zmq::sockopt` 106 | * enum `zmq::send_flags` 107 | * enum `zmq::recv_flags` 108 | * class `zmq::message_t` 109 | * class `zmq::const_buffer` 110 | * class `zmq::mutable_buffer` 111 | * struct `zmq::recv_buffer_size` 112 | * alias `zmq::send_result_t` 113 | * alias `zmq::recv_result_t` 114 | * alias `zmq::recv_buffer_result_t` 115 | * class `zmq::error_t` 116 | * class `zmq::monitor_t` 117 | * struct `zmq_event_t`, 118 | * alias `zmq::free_fn`, 119 | * alias `zmq::pollitem_t`, 120 | * alias `zmq::fd_t` 121 | * class `zmq::poller_t` DRAFT 122 | * enum `zmq::event_flags` DRAFT 123 | * enum `zmq::poller_event` DRAFT 124 | 125 | Functions: 126 | * `zmq::version` 127 | * `zmq::poll` 128 | * `zmq::proxy` 129 | * `zmq::proxy_steerable` 130 | * `zmq::buffer` 131 | * `zmq::str_buffer` 132 | 133 | Extra high-level types and functions `zmq_addon.hpp`: 134 | 135 | Types: 136 | * class `zmq::multipart_t` 137 | * class `zmq::active_poller_t` DRAFT 138 | 139 | Functions: 140 | * `zmq::recv_multipart` 141 | * `zmq::send_multipart` 142 | * `zmq::send_multipart_n` 143 | * `zmq::encode` 144 | * `zmq::decode` 145 | 146 | Compatibility Guidelines 147 | ======================== 148 | 149 | The users of cppzmq are expected to follow the guidelines below to ensure not to break when upgrading cppzmq to newer versions (non-exhaustive list): 150 | 151 | * Do not depend on any macros defined in cppzmq unless explicitly declared public here. 152 | 153 | The following macros may be used by consumers of cppzmq: `CPPZMQ_VERSION`, `CPPZMQ_VERSION_MAJOR`, `CPPZMQ_VERSION_MINOR`, `CPPZMQ_VERSION_PATCH`. 154 | 155 | Contribution policy 156 | =================== 157 | 158 | The contribution policy is at: http://rfc.zeromq.org/spec:22 159 | 160 | Build instructions 161 | ================== 162 | 163 | Build steps: 164 | 165 | 1. Build [libzmq](https://github.com/zeromq/libzmq) via cmake. This does an out of source build and installs the build files 166 | - `git clone https://github.com/zeromq/libzmq.git` 167 | - `cd libzmq` 168 | - `mkdir build` 169 | - `cd build` 170 | - `cmake ..` 171 | - `sudo make -j4 install` 172 | 173 | 2. Build cppzmq via cmake. This does an out of source build and installs the build files 174 | - `git clone https://github.com/zeromq/cppzmq.git` 175 | - `cd cppzmq` 176 | - `mkdir build` 177 | - `cd build` 178 | - `cmake ..` or `cmake -DCPPZMQ_BUILD_TESTS=OFF ..` to skip building tests 179 | - `sudo make -j4 install` 180 | 181 | 3. Alternatively, build cppzmq via [vcpkg](https://github.com/Microsoft/vcpkg/). This does an out of source build and installs the build files 182 | - `git clone https://github.com/Microsoft/vcpkg.git` 183 | - `cd vcpkg` 184 | - `./bootstrap-vcpkg.sh` (bootstrap-vcpkg.bat for Powershell) 185 | - `./vcpkg integrate install` 186 | - `./vcpkg install cppzmq` 187 | 188 | Using this: 189 | 190 | A cmake find package scripts is provided for you to easily include this library. 191 | Add these lines in your CMakeLists.txt to include the headers and library files of 192 | cpp zmq (which will also include libzmq for you). 193 | 194 | ``` 195 | #find cppzmq wrapper, installed by make of cppzmq 196 | find_package(cppzmq) 197 | target_link_libraries(*Your Project Name* cppzmq) 198 | # Or use static library to link 199 | target_link_libraries(*Your Project Name* cppzmq-static) 200 | ``` 201 | -------------------------------------------------------------------------------- /cmake/DetectCPPZMQVersion.cmake: -------------------------------------------------------------------------------- 1 | 2 | file(READ "${CMAKE_CURRENT_SOURCE_DIR}/zmq.hpp" _CPPZMQ_H_CONTENTS) 3 | string(REGEX REPLACE ".*#define CPPZMQ_VERSION_MAJOR ([0-9]+).*" "\\1" DETECTED_CPPZMQ_VERSION_MAJOR "${_CPPZMQ_H_CONTENTS}") 4 | string(REGEX REPLACE ".*#define CPPZMQ_VERSION_MINOR ([0-9]+).*" "\\1" DETECTED_CPPZMQ_VERSION_MINOR "${_CPPZMQ_H_CONTENTS}") 5 | string(REGEX REPLACE ".*#define CPPZMQ_VERSION_PATCH ([0-9]+).*" "\\1" DETECTED_CPPZMQ_VERSION_PATCH "${_CPPZMQ_H_CONTENTS}") 6 | set(DETECTED_CPPZMQ_VERSION "${DETECTED_CPPZMQ_VERSION_MAJOR}.${DETECTED_CPPZMQ_VERSION_MINOR}.${DETECTED_CPPZMQ_VERSION_PATCH}") 7 | 8 | message(STATUS "Detected CPPZMQ Version - ${DETECTED_CPPZMQ_VERSION}") 9 | -------------------------------------------------------------------------------- /cppzmq.pc.in: -------------------------------------------------------------------------------- 1 | prefix="@CMAKE_INSTALL_PREFIX@" 2 | includedir="@CMAKE_INSTALL_FULL_INCLUDEDIR@" 3 | 4 | Name: @PROJECT_NAME@ 5 | Description: C++ binding for libzmq 6 | URL: https://github.com/zeromq/cppzmq 7 | Version: @PROJECT_VERSION@ 8 | Requires: libzmq 9 | Cflags: -I"${includedir}" @pkg_config_defines@ 10 | -------------------------------------------------------------------------------- /cppzmqConfig.cmake.in: -------------------------------------------------------------------------------- 1 | # cppzmq cmake module 2 | # 3 | # The following import targets are created 4 | # 5 | # :: 6 | # 7 | # cppzmq-static 8 | # cppzmq 9 | # 10 | # This module sets the following variables in your project:: 11 | # 12 | # cppzmq_FOUND - true if cppzmq found on the system 13 | # cppzmq_INCLUDE_DIR - the directory containing cppzmq headers 14 | # cppzmq_LIBRARY - the ZeroMQ library for dynamic linking 15 | # cppzmq_STATIC_LIBRARY - the ZeroMQ library for static linking 16 | 17 | @PACKAGE_INIT@ 18 | 19 | include(CMakeFindDependencyMacro) 20 | find_package(ZeroMQ QUIET) 21 | 22 | # libzmq autotools install: fallback to pkg-config 23 | if(NOT ZeroMQ_FOUND) 24 | list (APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/libzmq-pkg-config) 25 | find_package(ZeroMQ REQUIRED) 26 | endif() 27 | 28 | if(NOT ZeroMQ_FOUND) 29 | message(FATAL_ERROR "ZeroMQ was NOT found!") 30 | endif() 31 | 32 | if(NOT TARGET @PROJECT_NAME@) 33 | include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") 34 | get_target_property(@PROJECT_NAME@_INCLUDE_DIR cppzmq INTERFACE_INCLUDE_DIRECTORIES) 35 | endif() 36 | 37 | -------------------------------------------------------------------------------- /demo/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0 FATAL_ERROR) 2 | 3 | project(cppzmq-demo CXX) 4 | 5 | find_package(cppzmq) 6 | 7 | enable_testing() 8 | add_executable( 9 | demo 10 | main.cpp 11 | ) 12 | 13 | target_link_libraries( 14 | demo 15 | cppzmq 16 | ) 17 | 18 | add_test( 19 | NAME 20 | demo 21 | COMMAND 22 | ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_BINDIR}/demo 23 | ) 24 | -------------------------------------------------------------------------------- /demo/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char **argv) 4 | { 5 | zmq::context_t context; 6 | return 0; 7 | } 8 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.11 FATAL_ERROR) 2 | 3 | project(cppzmq-examples CXX) 4 | 5 | # place binaries and libraries according to GNU standards 6 | 7 | include(GNUInstallDirs) 8 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}) 9 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}) 10 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_BINDIR}) 11 | 12 | find_package(Threads) 13 | find_package(cppzmq) 14 | 15 | add_executable( 16 | pubsub_multithread_inproc 17 | pubsub_multithread_inproc.cpp 18 | ) 19 | target_link_libraries( 20 | pubsub_multithread_inproc 21 | PRIVATE cppzmq ${CMAKE_THREAD_LIBS_INIT} 22 | ) 23 | 24 | add_executable( 25 | hello_world 26 | hello_world.cpp 27 | ) 28 | target_link_libraries( 29 | hello_world 30 | PRIVATE cppzmq ${CMAKE_THREAD_LIBS_INIT} 31 | ) 32 | 33 | add_executable( 34 | multipart_messages 35 | multipart_messages.cpp 36 | ) 37 | target_link_libraries( 38 | multipart_messages 39 | PRIVATE cppzmq ${CMAKE_THREAD_LIBS_INIT} 40 | ) 41 | -------------------------------------------------------------------------------- /examples/hello_world.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() 4 | { 5 | zmq::context_t ctx; 6 | zmq::socket_t sock(ctx, zmq::socket_type::push); 7 | sock.bind("inproc://test"); 8 | sock.send(zmq::str_buffer("Hello, world"), zmq::send_flags::dontwait); 9 | } 10 | -------------------------------------------------------------------------------- /examples/multipart_messages.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() 5 | { 6 | zmq::context_t ctx; 7 | zmq::socket_t sock1(ctx, zmq::socket_type::push); 8 | zmq::socket_t sock2(ctx, zmq::socket_type::pull); 9 | sock1.bind("tcp://127.0.0.1:*"); 10 | const std::string last_endpoint = 11 | sock1.get(zmq::sockopt::last_endpoint); 12 | std::cout << "Connecting to " 13 | << last_endpoint << std::endl; 14 | sock2.connect(last_endpoint); 15 | 16 | std::array send_msgs = { 17 | zmq::str_buffer("foo"), 18 | zmq::str_buffer("bar!") 19 | }; 20 | if (!zmq::send_multipart(sock1, send_msgs)) 21 | return 1; 22 | 23 | std::vector recv_msgs; 24 | const auto ret = zmq::recv_multipart( 25 | sock2, std::back_inserter(recv_msgs)); 26 | if (!ret) 27 | return 1; 28 | std::cout << "Got " << *ret 29 | << " messages" << std::endl; 30 | return 0; 31 | } 32 | -------------------------------------------------------------------------------- /examples/pubsub_multithread_inproc.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "zmq.hpp" 7 | #include "zmq_addon.hpp" 8 | 9 | void PublisherThread(zmq::context_t *ctx) { 10 | // Prepare publisher 11 | zmq::socket_t publisher(*ctx, zmq::socket_type::pub); 12 | publisher.bind("inproc://#1"); 13 | 14 | // Give the subscribers a chance to connect, so they don't lose any messages 15 | std::this_thread::sleep_for(std::chrono::milliseconds(20)); 16 | 17 | while (true) { 18 | // Write three messages, each with an envelope and content 19 | publisher.send(zmq::str_buffer("A"), zmq::send_flags::sndmore); 20 | publisher.send(zmq::str_buffer("Message in A envelope")); 21 | publisher.send(zmq::str_buffer("B"), zmq::send_flags::sndmore); 22 | publisher.send(zmq::str_buffer("Message in B envelope")); 23 | publisher.send(zmq::str_buffer("C"), zmq::send_flags::sndmore); 24 | publisher.send(zmq::str_buffer("Message in C envelope")); 25 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); 26 | } 27 | } 28 | 29 | void SubscriberThread1(zmq::context_t *ctx) { 30 | // Prepare subscriber 31 | zmq::socket_t subscriber(*ctx, zmq::socket_type::sub); 32 | subscriber.connect("inproc://#1"); 33 | 34 | // Thread2 opens "A" and "B" envelopes 35 | subscriber.set(zmq::sockopt::subscribe, "A"); 36 | subscriber.set(zmq::sockopt::subscribe, "B"); 37 | 38 | while (1) { 39 | // Receive all parts of the message 40 | std::vector recv_msgs; 41 | zmq::recv_result_t result = 42 | zmq::recv_multipart(subscriber, std::back_inserter(recv_msgs)); 43 | assert(result && "recv failed"); 44 | assert(*result == 2); 45 | 46 | std::cout << "Thread2: [" << recv_msgs[0].to_string() << "] " 47 | << recv_msgs[1].to_string() << std::endl; 48 | } 49 | } 50 | 51 | void SubscriberThread2(zmq::context_t *ctx) { 52 | // Prepare our context and subscriber 53 | zmq::socket_t subscriber(*ctx, zmq::socket_type::sub); 54 | subscriber.connect("inproc://#1"); 55 | 56 | // Thread3 opens ALL envelopes 57 | subscriber.set(zmq::sockopt::subscribe, ""); 58 | 59 | while (1) { 60 | // Receive all parts of the message 61 | std::vector recv_msgs; 62 | zmq::recv_result_t result = 63 | zmq::recv_multipart(subscriber, std::back_inserter(recv_msgs)); 64 | assert(result && "recv failed"); 65 | assert(*result == 2); 66 | 67 | std::cout << "Thread3: [" << recv_msgs[0].to_string() << "] " 68 | << recv_msgs[1].to_string() << std::endl; 69 | } 70 | } 71 | 72 | int main() { 73 | /* 74 | * No I/O threads are involved in passing messages using the inproc transport. 75 | * Therefore, if you are using a ØMQ context for in-process messaging only you 76 | * can initialise the context with zero I/O threads. 77 | * 78 | * Source: http://api.zeromq.org/4-3:zmq-inproc 79 | */ 80 | zmq::context_t ctx(0); 81 | 82 | auto thread1 = std::async(std::launch::async, PublisherThread, &ctx); 83 | 84 | // Give the publisher a chance to bind, since inproc requires it 85 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 86 | 87 | auto thread2 = std::async(std::launch::async, SubscriberThread1, &ctx); 88 | auto thread3 = std::async(std::launch::async, SubscriberThread2, &ctx); 89 | thread1.wait(); 90 | thread2.wait(); 91 | thread3.wait(); 92 | 93 | /* 94 | * Output: 95 | * An infinite loop of a mix of: 96 | * Thread2: [A] Message in A envelope 97 | * Thread2: [B] Message in B envelope 98 | * Thread3: [A] Message in A envelope 99 | * Thread3: [B] Message in B envelope 100 | * Thread3: [C] Message in C envelope 101 | */ 102 | } 103 | -------------------------------------------------------------------------------- /libzmq-pkg-config/FindZeroMQ.cmake: -------------------------------------------------------------------------------- 1 | set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH ON) 2 | find_package(PkgConfig) 3 | pkg_check_modules(PC_LIBZMQ QUIET libzmq) 4 | 5 | set(ZeroMQ_VERSION ${PC_LIBZMQ_VERSION}) 6 | 7 | find_path(ZeroMQ_INCLUDE_DIR zmq.h 8 | PATHS ${ZeroMQ_DIR}/include 9 | ${PC_LIBZMQ_INCLUDE_DIRS}) 10 | 11 | find_library(ZeroMQ_LIBRARY 12 | NAMES zmq 13 | PATHS ${ZeroMQ_DIR}/lib 14 | ${PC_LIBZMQ_LIBDIR} 15 | ${PC_LIBZMQ_LIBRARY_DIRS}) 16 | 17 | if(ZeroMQ_LIBRARY) 18 | set(ZeroMQ_FOUND ON) 19 | endif() 20 | 21 | set ( ZeroMQ_LIBRARIES ${ZeroMQ_LIBRARY} ) 22 | set ( ZeroMQ_INCLUDE_DIRS ${ZeroMQ_INCLUDE_DIR} ) 23 | 24 | if(NOT TARGET libzmq) 25 | add_library(libzmq UNKNOWN IMPORTED) 26 | set_target_properties(libzmq PROPERTIES 27 | IMPORTED_LOCATION ${ZeroMQ_LIBRARIES} 28 | INTERFACE_INCLUDE_DIRECTORIES ${ZeroMQ_INCLUDE_DIRS}) 29 | endif() 30 | 31 | include ( FindPackageHandleStandardArgs ) 32 | # handle the QUIETLY and REQUIRED arguments and set ZMQ_FOUND to TRUE 33 | # if all listed variables are TRUE 34 | find_package_handle_standard_args ( ZeroMQ DEFAULT_MSG ZeroMQ_LIBRARIES ZeroMQ_INCLUDE_DIRS ) -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(Threads) 2 | 3 | find_package(Catch2 QUIET) 4 | 5 | if (NOT Catch2_FOUND) 6 | include(FetchContent) 7 | 8 | FetchContent_Declare( 9 | Catch2 10 | GIT_REPOSITORY https://github.com/catchorg/Catch2.git 11 | GIT_TAG v3.5.3) 12 | 13 | FetchContent_MakeAvailable(Catch2) 14 | 15 | list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/contrib) 16 | endif() 17 | 18 | add_executable( 19 | unit_tests 20 | buffer.cpp 21 | message.cpp 22 | context.cpp 23 | socket.cpp 24 | socket_ref.cpp 25 | poller.cpp 26 | active_poller.cpp 27 | multipart.cpp 28 | recv_multipart.cpp 29 | send_multipart.cpp 30 | codec_multipart.cpp 31 | monitor.cpp 32 | utilities.cpp 33 | timers.cpp 34 | ) 35 | 36 | target_include_directories(unit_tests PUBLIC ${CATCH_MODULE_PATH}) 37 | target_link_libraries( 38 | unit_tests 39 | PRIVATE Catch2::Catch2WithMain 40 | PRIVATE cppzmq 41 | PRIVATE ${CMAKE_THREAD_LIBS_INIT} 42 | ) 43 | 44 | OPTION (COVERAGE "Enable gcda file generation needed by lcov" OFF) 45 | 46 | if (COVERAGE) 47 | target_compile_options(unit_tests PRIVATE --coverage) 48 | target_link_options(unit_tests PRIVATE --coverage) 49 | message(STATUS "Coverage enabled") 50 | endif() 51 | 52 | include(CTest) 53 | include(Catch) 54 | catch_discover_tests(unit_tests) 55 | -------------------------------------------------------------------------------- /tests/active_poller.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "testutil.hpp" 4 | 5 | #if defined(ZMQ_CPP11) && !defined(ZMQ_CPP11_PARTIAL) && defined(ZMQ_BUILD_DRAFT_API) 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #if !defined(_WIN32) 12 | #include 13 | #endif // !_WIN32 14 | 15 | TEST_CASE("create destroy", "[active_poller]") 16 | { 17 | zmq::active_poller_t active_poller; 18 | CHECK(active_poller.empty()); 19 | } 20 | 21 | static_assert(!std::is_copy_constructible::value, 22 | "active_poller_t should not be copy-constructible"); 23 | static_assert(!std::is_copy_assignable::value, 24 | "active_poller_t should not be copy-assignable"); 25 | 26 | static const zmq::active_poller_t::handler_type no_op_handler = 27 | [](zmq::event_flags) {}; 28 | 29 | TEST_CASE("move construct empty", "[active_poller]") 30 | { 31 | zmq::active_poller_t a; 32 | CHECK(a.empty()); 33 | zmq::active_poller_t b = std::move(a); 34 | CHECK(b.empty()); 35 | CHECK(0u == a.size()); 36 | CHECK(0u == b.size()); 37 | } 38 | 39 | TEST_CASE("move assign empty", "[active_poller]") 40 | { 41 | zmq::active_poller_t a; 42 | CHECK(a.empty()); 43 | zmq::active_poller_t b; 44 | CHECK(b.empty()); 45 | b = std::move(a); 46 | CHECK(0u == a.size()); 47 | CHECK(0u == b.size()); 48 | CHECK(a.empty()); 49 | CHECK(b.empty()); 50 | } 51 | 52 | TEST_CASE("move construct non empty", "[active_poller]") 53 | { 54 | zmq::context_t context; 55 | zmq::socket_t socket{context, zmq::socket_type::router}; 56 | 57 | zmq::active_poller_t a; 58 | a.add(socket, zmq::event_flags::pollin, [](zmq::event_flags) {}); 59 | CHECK_FALSE(a.empty()); 60 | CHECK(1u == a.size()); 61 | zmq::active_poller_t b = std::move(a); 62 | CHECK(a.empty()); 63 | CHECK(0u == a.size()); 64 | CHECK_FALSE(b.empty()); 65 | CHECK(1u == b.size()); 66 | } 67 | 68 | TEST_CASE("move assign non empty", "[active_poller]") 69 | { 70 | zmq::context_t context; 71 | zmq::socket_t socket{context, zmq::socket_type::router}; 72 | 73 | zmq::active_poller_t a; 74 | a.add(socket, zmq::event_flags::pollin, no_op_handler); 75 | CHECK_FALSE(a.empty()); 76 | CHECK(1u == a.size()); 77 | zmq::active_poller_t b; 78 | b = std::move(a); 79 | CHECK(a.empty()); 80 | CHECK(0u == a.size()); 81 | CHECK_FALSE(b.empty()); 82 | CHECK(1u == b.size()); 83 | } 84 | 85 | TEST_CASE("add handler", "[active_poller]") 86 | { 87 | zmq::context_t context; 88 | zmq::socket_t socket{context, zmq::socket_type::router}; 89 | zmq::active_poller_t active_poller; 90 | CHECK_NOTHROW( 91 | active_poller.add(socket, zmq::event_flags::pollin, no_op_handler)); 92 | } 93 | 94 | TEST_CASE("add fd handler", "[active_poller]") 95 | { 96 | int fd = 1; 97 | zmq::active_poller_t active_poller; 98 | CHECK_NOTHROW( 99 | active_poller.add(fd, zmq::event_flags::pollin, no_op_handler)); 100 | } 101 | 102 | TEST_CASE("remove fd handler", "[active_poller]") 103 | { 104 | int fd = 1; 105 | zmq::active_poller_t active_poller; 106 | CHECK_NOTHROW( 107 | active_poller.add(fd, zmq::event_flags::pollin, no_op_handler)); 108 | CHECK_NOTHROW( 109 | active_poller.remove(fd)); 110 | CHECK_THROWS_ZMQ_ERROR(EINVAL, active_poller.remove(100)); 111 | } 112 | 113 | #if !defined(_WIN32) 114 | // On Windows, these functions can only be used with WinSock sockets. 115 | 116 | TEST_CASE("mixed socket and fd handlers", "[active_poller]") 117 | { 118 | int pipefd[2]; 119 | ::pipe(pipefd); 120 | 121 | zmq::context_t context; 122 | constexpr char inprocSocketAddress[] = "inproc://mixed-handlers"; 123 | zmq::socket_t socket_rcv{context, zmq::socket_type::pair}; 124 | zmq::socket_t socket_snd{context, zmq::socket_type::pair}; 125 | socket_rcv.bind(inprocSocketAddress); 126 | socket_snd.connect(inprocSocketAddress); 127 | 128 | unsigned eventsFd = 0; 129 | unsigned eventsSocket = 0; 130 | 131 | constexpr char messageText[] = "message"; 132 | constexpr size_t messageSize = sizeof(messageText); 133 | 134 | zmq::active_poller_t active_poller; 135 | CHECK_NOTHROW( 136 | active_poller.add(pipefd[0], zmq::event_flags::pollin, [&](zmq::event_flags flags) { 137 | if (flags == zmq::event_flags::pollin) 138 | { 139 | char buffer[256]; 140 | CHECK(messageSize == ::read(pipefd[0], buffer, messageSize)); 141 | CHECK(0 == std::strcmp(buffer, messageText)); 142 | ++eventsFd; 143 | } 144 | })); 145 | CHECK_NOTHROW( 146 | active_poller.add(socket_rcv, zmq::event_flags::pollin, [&](zmq::event_flags flags) { 147 | if (flags == zmq::event_flags::pollin) 148 | { 149 | zmq::message_t msg; 150 | CHECK(socket_rcv.recv(msg, zmq::recv_flags::dontwait).has_value()); 151 | CHECK(messageSize == msg.size()); 152 | CHECK(0 == std::strcmp(messageText, msg.data())); 153 | ++eventsSocket; 154 | } 155 | })); 156 | 157 | // send/rcv socket pair 158 | zmq::message_t msg{messageText, messageSize}; 159 | socket_snd.send(msg, zmq::send_flags::dontwait); 160 | CHECK(1 == active_poller.wait(std::chrono::milliseconds{100})); 161 | CHECK(0 == eventsFd); 162 | CHECK(1 == eventsSocket); 163 | 164 | // send/rcv pipe 165 | ::write(pipefd[1], messageText, messageSize); 166 | CHECK(1 == active_poller.wait(std::chrono::milliseconds{100})); 167 | CHECK(1 == eventsFd); 168 | CHECK(1 == eventsSocket); 169 | } 170 | 171 | #endif // !_WIN32 172 | 173 | TEST_CASE("add null handler fails", "[active_poller]") 174 | { 175 | zmq::context_t context; 176 | zmq::socket_t socket{context, zmq::socket_type::router}; 177 | zmq::active_poller_t active_poller; 178 | zmq::active_poller_t::handler_type handler; 179 | CHECK_THROWS_AS(active_poller.add(socket, zmq::event_flags::pollin, handler), 180 | std::invalid_argument); 181 | } 182 | 183 | #if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 3, 0) 184 | // this behaviour was added by https://github.com/zeromq/libzmq/pull/3100 185 | TEST_CASE("add handler invalid events type", "[active_poller]") 186 | { 187 | zmq::context_t context; 188 | zmq::socket_t socket{context, zmq::socket_type::router}; 189 | zmq::active_poller_t active_poller; 190 | short invalid_events_type = 2 << 10; 191 | CHECK_THROWS_AS( 192 | active_poller.add(socket, static_cast(invalid_events_type), 193 | no_op_handler), 194 | zmq::error_t); 195 | CHECK(active_poller.empty()); 196 | CHECK(0u == active_poller.size()); 197 | } 198 | #endif 199 | 200 | TEST_CASE("add handler twice throws", "[active_poller]") 201 | { 202 | common_server_client_setup s; 203 | 204 | CHECK(s.client.send(zmq::message_t{}, zmq::send_flags::none)); 205 | 206 | zmq::active_poller_t active_poller; 207 | bool message_received = false; 208 | active_poller.add( 209 | s.server, zmq::event_flags::pollin, 210 | [&message_received](zmq::event_flags) { message_received = true; }); 211 | CHECK_THROWS_ZMQ_ERROR( 212 | EINVAL, active_poller.add(s.server, zmq::event_flags::pollin, no_op_handler)); 213 | CHECK(1 == active_poller.wait(std::chrono::milliseconds{-1})); 214 | CHECK(message_received); // handler unmodified 215 | } 216 | 217 | TEST_CASE("wait with no handlers throws", "[active_poller]") 218 | { 219 | zmq::active_poller_t active_poller; 220 | CHECK_THROWS_ZMQ_ERROR(EFAULT, 221 | active_poller.wait(std::chrono::milliseconds{10})); 222 | } 223 | 224 | TEST_CASE("remove unregistered throws", "[active_poller]") 225 | { 226 | zmq::context_t context; 227 | zmq::socket_t socket{context, zmq::socket_type::router}; 228 | zmq::active_poller_t active_poller; 229 | CHECK_THROWS_ZMQ_ERROR(EINVAL, active_poller.remove(socket)); 230 | } 231 | 232 | TEST_CASE("remove registered empty", "[active_poller]") 233 | { 234 | zmq::context_t context; 235 | zmq::socket_t socket{context, zmq::socket_type::router}; 236 | zmq::active_poller_t active_poller; 237 | active_poller.add(socket, zmq::event_flags::pollin, no_op_handler); 238 | CHECK_NOTHROW(active_poller.remove(socket)); 239 | } 240 | 241 | TEST_CASE("remove registered non empty", "[active_poller]") 242 | { 243 | zmq::context_t context; 244 | zmq::socket_t socket{context, zmq::socket_type::router}; 245 | zmq::active_poller_t active_poller; 246 | active_poller.add(socket, zmq::event_flags::pollin, no_op_handler); 247 | CHECK_NOTHROW(active_poller.remove(socket)); 248 | } 249 | 250 | namespace 251 | { 252 | struct server_client_setup : common_server_client_setup 253 | { 254 | zmq::active_poller_t::handler_type handler = [&](zmq::event_flags e) { 255 | events = e; 256 | }; 257 | 258 | zmq::event_flags events = zmq::event_flags::none; 259 | }; 260 | 261 | const std::string hi_str = "Hi"; 262 | 263 | } 264 | 265 | TEST_CASE("poll basic", "[active_poller]") 266 | { 267 | server_client_setup s; 268 | 269 | CHECK_NOTHROW(s.client.send(zmq::message_t{hi_str}, zmq::send_flags::none)); 270 | 271 | zmq::active_poller_t active_poller; 272 | bool message_received = false; 273 | zmq::active_poller_t::handler_type handler = 274 | [&message_received](zmq::event_flags events) { 275 | CHECK(zmq::event_flags::none != (events & zmq::event_flags::pollin)); 276 | message_received = true; 277 | }; 278 | CHECK_NOTHROW(active_poller.add(s.server, zmq::event_flags::pollin, handler)); 279 | CHECK(1 == active_poller.wait(std::chrono::milliseconds{-1})); 280 | CHECK(message_received); 281 | } 282 | 283 | /// \todo this contains multiple test cases that should be split up 284 | TEST_CASE("client server", "[active_poller]") 285 | { 286 | const std::string send_msg = hi_str; 287 | 288 | // Setup server and client 289 | server_client_setup s; 290 | 291 | // Setup active_poller 292 | zmq::active_poller_t active_poller; 293 | zmq::event_flags events; 294 | zmq::active_poller_t::handler_type handler = [&](zmq::event_flags e) { 295 | if (zmq::event_flags::none != (e & zmq::event_flags::pollin)) { 296 | zmq::message_t zmq_msg; 297 | CHECK_NOTHROW(s.server.recv(zmq_msg)); // get message 298 | std::string recv_msg(zmq_msg.data(), zmq_msg.size()); 299 | CHECK(send_msg == recv_msg); 300 | } else if (zmq::event_flags::none != (e & ~zmq::event_flags::pollout)) { 301 | INFO("Unexpected event type " << static_cast(events)); 302 | REQUIRE(false); 303 | } 304 | events = e; 305 | }; 306 | 307 | CHECK_NOTHROW(active_poller.add(s.server, zmq::event_flags::pollin, handler)); 308 | 309 | // client sends message 310 | CHECK_NOTHROW(s.client.send(zmq::message_t{send_msg}, zmq::send_flags::none)); 311 | 312 | CHECK(1 == active_poller.wait(std::chrono::milliseconds{-1})); 313 | CHECK(events == zmq::event_flags::pollin); 314 | 315 | // Re-add server socket with pollout flag 316 | CHECK_NOTHROW(active_poller.remove(s.server)); 317 | CHECK_NOTHROW(active_poller.add( 318 | s.server, zmq::event_flags::pollin | zmq::event_flags::pollout, handler)); 319 | CHECK(1 == active_poller.wait(std::chrono::milliseconds{-1})); 320 | CHECK(events == zmq::event_flags::pollout); 321 | } 322 | 323 | TEST_CASE("add invalid socket throws", "[active_poller]") 324 | { 325 | zmq::context_t context; 326 | zmq::active_poller_t active_poller; 327 | zmq::socket_t a{context, zmq::socket_type::router}; 328 | zmq::socket_t b{std::move(a)}; 329 | CHECK_THROWS_AS(active_poller.add(a, zmq::event_flags::pollin, no_op_handler), 330 | zmq::error_t); 331 | } 332 | 333 | TEST_CASE("remove invalid socket throws", "[active_poller]") 334 | { 335 | zmq::context_t context; 336 | zmq::socket_t socket{context, zmq::socket_type::router}; 337 | zmq::active_poller_t active_poller; 338 | CHECK_NOTHROW( 339 | active_poller.add(socket, zmq::event_flags::pollin, no_op_handler)); 340 | CHECK(1u == active_poller.size()); 341 | std::vector sockets; 342 | sockets.emplace_back(std::move(socket)); 343 | CHECK_THROWS_AS(active_poller.remove(socket), zmq::error_t); 344 | CHECK(1u == active_poller.size()); 345 | } 346 | 347 | TEST_CASE("wait on added empty handler", "[active_poller]") 348 | { 349 | server_client_setup s; 350 | CHECK_NOTHROW(s.client.send(zmq::message_t{hi_str}, zmq::send_flags::none)); 351 | zmq::active_poller_t active_poller; 352 | CHECK_NOTHROW( 353 | active_poller.add(s.server, zmq::event_flags::pollin, no_op_handler)); 354 | CHECK_NOTHROW(active_poller.wait(std::chrono::milliseconds{-1})); 355 | } 356 | 357 | TEST_CASE("modify empty throws", "[active_poller]") 358 | { 359 | zmq::context_t context; 360 | zmq::socket_t socket{context, zmq::socket_type::push}; 361 | zmq::active_poller_t active_poller; 362 | CHECK_THROWS_AS(active_poller.modify(socket, zmq::event_flags::pollin), 363 | zmq::error_t); 364 | } 365 | 366 | TEST_CASE("modify invalid socket throws", "[active_poller]") 367 | { 368 | zmq::context_t context; 369 | zmq::socket_t a{context, zmq::socket_type::push}; 370 | zmq::socket_t b{std::move(a)}; 371 | zmq::active_poller_t active_poller; 372 | CHECK_THROWS_AS(active_poller.modify(a, zmq::event_flags::pollin), 373 | zmq::error_t); 374 | } 375 | 376 | TEST_CASE("modify not added throws", "[active_poller]") 377 | { 378 | zmq::context_t context; 379 | zmq::socket_t a{context, zmq::socket_type::push}; 380 | zmq::socket_t b{context, zmq::socket_type::push}; 381 | zmq::active_poller_t active_poller; 382 | CHECK_NOTHROW(active_poller.add(a, zmq::event_flags::pollin, no_op_handler)); 383 | CHECK_THROWS_AS(active_poller.modify(b, zmq::event_flags::pollin), 384 | zmq::error_t); 385 | } 386 | 387 | TEST_CASE("modify simple", "[active_poller]") 388 | { 389 | zmq::context_t context; 390 | zmq::socket_t a{context, zmq::socket_type::push}; 391 | zmq::active_poller_t active_poller; 392 | CHECK_NOTHROW(active_poller.add(a, zmq::event_flags::pollin, no_op_handler)); 393 | CHECK_NOTHROW( 394 | active_poller.modify(a, zmq::event_flags::pollin | zmq::event_flags::pollout)); 395 | } 396 | 397 | TEST_CASE("poll client server", "[active_poller]") 398 | { 399 | // Setup server and client 400 | server_client_setup s; 401 | 402 | // Setup active_poller 403 | zmq::active_poller_t active_poller; 404 | CHECK_NOTHROW(active_poller.add(s.server, zmq::event_flags::pollin, s.handler)); 405 | 406 | // client sends message 407 | CHECK_NOTHROW(s.client.send(zmq::message_t{hi_str}, zmq::send_flags::none)); 408 | 409 | // wait for message and verify events 410 | CHECK_NOTHROW(active_poller.wait(std::chrono::milliseconds{500})); 411 | CHECK(s.events == zmq::event_flags::pollin); 412 | 413 | // Modify server socket with pollout flag 414 | CHECK_NOTHROW(active_poller.modify(s.server, zmq::event_flags::pollin 415 | | zmq::event_flags::pollout)); 416 | CHECK(1 == active_poller.wait(std::chrono::milliseconds{500})); 417 | CHECK(s.events == (zmq::event_flags::pollin | zmq::event_flags::pollout)); 418 | } 419 | 420 | TEST_CASE("wait one return", "[active_poller]") 421 | { 422 | // Setup server and client 423 | server_client_setup s; 424 | 425 | int count = 0; 426 | 427 | // Setup active_poller 428 | zmq::active_poller_t active_poller; 429 | CHECK_NOTHROW(active_poller.add(s.server, zmq::event_flags::pollin, 430 | [&count](zmq::event_flags) { ++count; })); 431 | 432 | // client sends message 433 | CHECK_NOTHROW(s.client.send(zmq::message_t{hi_str}, zmq::send_flags::none)); 434 | 435 | // wait for message and verify events 436 | CHECK(1 == active_poller.wait(std::chrono::milliseconds{500})); 437 | CHECK(1u == count); 438 | } 439 | 440 | TEST_CASE("wait on move constructed active_poller", "[active_poller]") 441 | { 442 | server_client_setup s; 443 | CHECK_NOTHROW(s.client.send(zmq::message_t{hi_str}, zmq::send_flags::none)); 444 | zmq::active_poller_t a; 445 | CHECK_NOTHROW(a.add(s.server, zmq::event_flags::pollin, no_op_handler)); 446 | zmq::active_poller_t b{std::move(a)}; 447 | CHECK(1u == b.size()); 448 | CHECK(0u == a.size()); 449 | CHECK_THROWS_ZMQ_ERROR(EFAULT, a.wait(std::chrono::milliseconds{10})); 450 | CHECK(b.wait(std::chrono::milliseconds{-1})); 451 | } 452 | 453 | TEST_CASE("wait on move assigned active_poller", "[active_poller]") 454 | { 455 | server_client_setup s; 456 | CHECK_NOTHROW(s.client.send(zmq::message_t{hi_str}, zmq::send_flags::none)); 457 | zmq::active_poller_t a; 458 | CHECK_NOTHROW(a.add(s.server, zmq::event_flags::pollin, no_op_handler)); 459 | zmq::active_poller_t b; 460 | b = {std::move(a)}; 461 | CHECK(1u == b.size()); 462 | CHECK(0u == a.size()); 463 | CHECK_THROWS_ZMQ_ERROR(EFAULT, a.wait(std::chrono::milliseconds{10})); 464 | CHECK(b.wait(std::chrono::milliseconds{-1})); 465 | } 466 | 467 | TEST_CASE("received on move constructed active_poller", "[active_poller]") 468 | { 469 | // Setup server and client 470 | server_client_setup s; 471 | int count = 0; 472 | // Setup active_poller a 473 | zmq::active_poller_t a; 474 | CHECK_NOTHROW(a.add(s.server, zmq::event_flags::pollin, 475 | [&count](zmq::event_flags) { ++count; })); 476 | // client sends message 477 | CHECK_NOTHROW(s.client.send(zmq::message_t{hi_str}, zmq::send_flags::none)); 478 | // wait for message and verify it is received 479 | CHECK(1 == a.wait(std::chrono::milliseconds{500})); 480 | CHECK(1u == count); 481 | // Move construct active_poller b 482 | zmq::active_poller_t b{std::move(a)}; 483 | // client sends message again 484 | CHECK_NOTHROW(s.client.send(zmq::message_t{hi_str}, zmq::send_flags::none)); 485 | // wait for message and verify it is received 486 | CHECK(1 == b.wait(std::chrono::milliseconds{500})); 487 | CHECK(2u == count); 488 | } 489 | 490 | 491 | TEST_CASE("remove from handler", "[active_poller]") 492 | { 493 | constexpr size_t ITER_NO = 10; 494 | 495 | // Setup servers and clients 496 | std::vector setup_list; 497 | for (size_t i = 0; i < ITER_NO; ++i) 498 | setup_list.emplace_back(server_client_setup{}); 499 | 500 | // Setup active_poller 501 | zmq::active_poller_t active_poller; 502 | int count = 0; 503 | for (size_t i = 0; i < ITER_NO; ++i) { 504 | CHECK_NOTHROW(active_poller.add( 505 | setup_list[i].server, zmq::event_flags::pollin, 506 | [&, i](zmq::event_flags events) { 507 | CHECK(events == zmq::event_flags::pollin); 508 | active_poller.remove(setup_list[ITER_NO - i - 1].server); 509 | CHECK((ITER_NO - i - 1) == active_poller.size()); 510 | })); 511 | ++count; 512 | } 513 | CHECK(ITER_NO == active_poller.size()); 514 | // Clients send messages 515 | for (auto &s : setup_list) { 516 | CHECK_NOTHROW(s.client.send(zmq::message_t{hi_str}, zmq::send_flags::none)); 517 | } 518 | 519 | // Wait for all servers to receive a message 520 | for (auto &s : setup_list) { 521 | zmq::pollitem_t items[] = {{s.server, 0, ZMQ_POLLIN, 0}}; 522 | zmq::poll(&items[0], 1); 523 | } 524 | 525 | // Fire all handlers in one wait 526 | CHECK(ITER_NO == active_poller.wait(std::chrono::milliseconds{-1})); 527 | CHECK(ITER_NO == count); 528 | } 529 | 530 | #endif 531 | -------------------------------------------------------------------------------- /tests/buffer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #ifdef ZMQ_CPP17 5 | static_assert(std::is_nothrow_swappable_v); 6 | static_assert(std::is_nothrow_swappable_v); 7 | static_assert(std::is_trivially_copyable_v); 8 | static_assert(std::is_trivially_copyable_v); 9 | #endif 10 | 11 | #ifdef ZMQ_CPP11 12 | 13 | using BT = int16_t; 14 | 15 | TEST_CASE("buffer default ctor", "[buffer]") 16 | { 17 | constexpr zmq::mutable_buffer mb; 18 | constexpr zmq::const_buffer cb; 19 | CHECK(mb.size() == 0); 20 | CHECK(mb.data() == nullptr); 21 | CHECK(cb.size() == 0); 22 | CHECK(cb.data() == nullptr); 23 | } 24 | 25 | TEST_CASE("buffer data ctor", "[buffer]") 26 | { 27 | std::vector v(10); 28 | zmq::const_buffer cb(v.data(), v.size() * sizeof(BT)); 29 | CHECK(cb.size() == v.size() * sizeof(BT)); 30 | CHECK(cb.data() == v.data()); 31 | zmq::mutable_buffer mb(v.data(), v.size() * sizeof(BT)); 32 | CHECK(mb.size() == v.size() * sizeof(BT)); 33 | CHECK(mb.data() == v.data()); 34 | zmq::const_buffer from_mut = mb; 35 | CHECK(mb.size() == from_mut.size()); 36 | CHECK(mb.data() == from_mut.data()); 37 | const auto cmb = mb; 38 | static_assert(std::is_same::value, ""); 39 | 40 | constexpr const void *cp = nullptr; 41 | constexpr void *p = nullptr; 42 | constexpr zmq::const_buffer cecb = zmq::buffer(p, 0); 43 | constexpr zmq::mutable_buffer cemb = zmq::buffer(p, 0); 44 | CHECK(cecb.data() == nullptr); 45 | CHECK(cemb.data() == nullptr); 46 | } 47 | 48 | TEST_CASE("const_buffer operator+", "[buffer]") 49 | { 50 | std::vector v(10); 51 | zmq::const_buffer cb(v.data(), v.size() * sizeof(BT)); 52 | const size_t shift = 4; 53 | auto shifted = cb + shift; 54 | CHECK(shifted.size() == v.size() * sizeof(BT) - shift); 55 | CHECK(shifted.data() == v.data() + shift / sizeof(BT)); 56 | auto shifted2 = shift + cb; 57 | CHECK(shifted.size() == shifted2.size()); 58 | CHECK(shifted.data() == shifted2.data()); 59 | auto cbinp = cb; 60 | cbinp += shift; 61 | CHECK(shifted.size() == cbinp.size()); 62 | CHECK(shifted.data() == cbinp.data()); 63 | } 64 | 65 | TEST_CASE("mutable_buffer operator+", "[buffer]") 66 | { 67 | std::vector v(10); 68 | zmq::mutable_buffer mb(v.data(), v.size() * sizeof(BT)); 69 | const size_t shift = 4; 70 | auto shifted = mb + shift; 71 | CHECK(shifted.size() == v.size() * sizeof(BT) - shift); 72 | CHECK(shifted.data() == v.data() + shift / sizeof(BT)); 73 | auto shifted2 = shift + mb; 74 | CHECK(shifted.size() == shifted2.size()); 75 | CHECK(shifted.data() == shifted2.data()); 76 | auto mbinp = mb; 77 | mbinp += shift; 78 | CHECK(shifted.size() == mbinp.size()); 79 | CHECK(shifted.data() == mbinp.data()); 80 | } 81 | 82 | TEST_CASE("mutable_buffer creation basic", "[buffer]") 83 | { 84 | std::vector v(10); 85 | zmq::mutable_buffer mb(v.data(), v.size() * sizeof(BT)); 86 | zmq::mutable_buffer mb2 = zmq::buffer(v.data(), v.size() * sizeof(BT)); 87 | CHECK(mb.data() == mb2.data()); 88 | CHECK(mb.size() == mb2.size()); 89 | zmq::mutable_buffer mb3 = zmq::buffer(mb); 90 | CHECK(mb.data() == mb3.data()); 91 | CHECK(mb.size() == mb3.size()); 92 | zmq::mutable_buffer mb4 = zmq::buffer(mb, 10 * v.size() * sizeof(BT)); 93 | CHECK(mb.data() == mb4.data()); 94 | CHECK(mb.size() == mb4.size()); 95 | zmq::mutable_buffer mb5 = zmq::buffer(mb, 4); 96 | CHECK(mb.data() == mb5.data()); 97 | CHECK(4 == mb5.size()); 98 | } 99 | 100 | TEST_CASE("const_buffer creation basic", "[buffer]") 101 | { 102 | const std::vector v(10); 103 | zmq::const_buffer cb(v.data(), v.size() * sizeof(BT)); 104 | zmq::const_buffer cb2 = zmq::buffer(v.data(), v.size() * sizeof(BT)); 105 | CHECK(cb.data() == cb2.data()); 106 | CHECK(cb.size() == cb2.size()); 107 | zmq::const_buffer cb3 = zmq::buffer(cb); 108 | CHECK(cb.data() == cb3.data()); 109 | CHECK(cb.size() == cb3.size()); 110 | zmq::const_buffer cb4 = zmq::buffer(cb, 10 * v.size() * sizeof(BT)); 111 | CHECK(cb.data() == cb4.data()); 112 | CHECK(cb.size() == cb4.size()); 113 | zmq::const_buffer cb5 = zmq::buffer(cb, 4); 114 | CHECK(cb.data() == cb5.data()); 115 | CHECK(4 == cb5.size()); 116 | } 117 | 118 | TEST_CASE("mutable_buffer creation C array", "[buffer]") 119 | { 120 | BT d[10] = {}; 121 | zmq::mutable_buffer b = zmq::buffer(d); 122 | CHECK(b.size() == 10 * sizeof(BT)); 123 | CHECK(b.data() == static_cast(d)); 124 | zmq::const_buffer b2 = zmq::buffer(d, 4); 125 | CHECK(b2.size() == 4); 126 | CHECK(b2.data() == static_cast(d)); 127 | } 128 | 129 | TEST_CASE("const_buffer creation C array", "[buffer]") 130 | { 131 | const BT d[10] = {}; 132 | zmq::const_buffer b = zmq::buffer(d); 133 | CHECK(b.size() == 10 * sizeof(BT)); 134 | CHECK(b.data() == static_cast(d)); 135 | zmq::const_buffer b2 = zmq::buffer(d, 4); 136 | CHECK(b2.size() == 4); 137 | CHECK(b2.data() == static_cast(d)); 138 | } 139 | 140 | TEST_CASE("mutable_buffer creation array", "[buffer]") 141 | { 142 | std::array d = {}; 143 | zmq::mutable_buffer b = zmq::buffer(d); 144 | CHECK(b.size() == d.size() * sizeof(BT)); 145 | CHECK(b.data() == d.data()); 146 | zmq::mutable_buffer b2 = zmq::buffer(d, 4); 147 | CHECK(b2.size() == 4); 148 | CHECK(b2.data() == d.data()); 149 | } 150 | 151 | TEST_CASE("const_buffer creation array", "[buffer]") 152 | { 153 | const std::array d = {}; 154 | zmq::const_buffer b = zmq::buffer(d); 155 | CHECK(b.size() == d.size() * sizeof(BT)); 156 | CHECK(b.data() == d.data()); 157 | zmq::const_buffer b2 = zmq::buffer(d, 4); 158 | CHECK(b2.size() == 4); 159 | CHECK(b2.data() == d.data()); 160 | } 161 | 162 | TEST_CASE("const_buffer creation array 2", "[buffer]") 163 | { 164 | std::array d = {{}}; 165 | zmq::const_buffer b = zmq::buffer(d); 166 | CHECK(b.size() == d.size() * sizeof(BT)); 167 | CHECK(b.data() == d.data()); 168 | zmq::const_buffer b2 = zmq::buffer(d, 4); 169 | CHECK(b2.size() == 4); 170 | CHECK(b2.data() == d.data()); 171 | } 172 | 173 | TEST_CASE("mutable_buffer creation vector", "[buffer]") 174 | { 175 | std::vector d(10); 176 | zmq::mutable_buffer b = zmq::buffer(d); 177 | CHECK(b.size() == d.size() * sizeof(BT)); 178 | CHECK(b.data() == d.data()); 179 | zmq::mutable_buffer b2 = zmq::buffer(d, 4); 180 | CHECK(b2.size() == 4); 181 | CHECK(b2.data() == d.data()); 182 | d.clear(); 183 | b = zmq::buffer(d); 184 | CHECK(b.size() == 0); 185 | CHECK(b.data() == nullptr); 186 | } 187 | 188 | TEST_CASE("const_buffer creation vector", "[buffer]") 189 | { 190 | std::vector d(10); 191 | zmq::const_buffer b = zmq::buffer(static_cast &>(d)); 192 | CHECK(b.size() == d.size() * sizeof(BT)); 193 | CHECK(b.data() == d.data()); 194 | zmq::const_buffer b2 = zmq::buffer(static_cast &>(d), 4); 195 | CHECK(b2.size() == 4); 196 | CHECK(b2.data() == d.data()); 197 | d.clear(); 198 | b = zmq::buffer(static_cast &>(d)); 199 | CHECK(b.size() == 0); 200 | CHECK(b.data() == nullptr); 201 | } 202 | 203 | TEST_CASE("const_buffer creation string", "[buffer]") 204 | { 205 | const std::wstring d(10, L'a'); 206 | zmq::const_buffer b = zmq::buffer(d); 207 | CHECK(b.size() == d.size() * sizeof(wchar_t)); 208 | CHECK(b.data() == d.data()); 209 | zmq::const_buffer b2 = zmq::buffer(d, 4); 210 | CHECK(b2.size() == 4); 211 | CHECK(b2.data() == d.data()); 212 | } 213 | 214 | TEST_CASE("mutable_buffer creation string", "[buffer]") 215 | { 216 | std::wstring d(10, L'a'); 217 | zmq::mutable_buffer b = zmq::buffer(d); 218 | CHECK(b.size() == d.size() * sizeof(wchar_t)); 219 | CHECK(b.data() == d.data()); 220 | zmq::mutable_buffer b2 = zmq::buffer(d, 4); 221 | CHECK(b2.size() == 4); 222 | CHECK(b2.data() == d.data()); 223 | } 224 | 225 | #if CPPZMQ_HAS_STRING_VIEW 226 | TEST_CASE("const_buffer creation string_view", "[buffer]") 227 | { 228 | std::wstring dstr(10, L'a'); 229 | std::wstring_view d = dstr; 230 | zmq::const_buffer b = zmq::buffer(d); 231 | CHECK(b.size() == d.size() * sizeof(wchar_t)); 232 | CHECK(b.data() == d.data()); 233 | zmq::const_buffer b2 = zmq::buffer(d, 4); 234 | CHECK(b2.size() == 4); 235 | CHECK(b2.data() == d.data()); 236 | } 237 | #endif 238 | 239 | TEST_CASE("const_buffer creation with str_buffer", "[buffer]") 240 | { 241 | const wchar_t wd[10] = {}; 242 | zmq::const_buffer b = zmq::str_buffer(wd); 243 | CHECK(b.size() == 9 * sizeof(wchar_t)); 244 | CHECK(b.data() == static_cast(wd)); 245 | 246 | zmq::const_buffer b2_null = zmq::buffer("hello"); 247 | constexpr zmq::const_buffer b2 = zmq::str_buffer("hello"); 248 | CHECK(b2_null.size() == 6); 249 | CHECK(b2.size() == 5); 250 | CHECK(std::string(static_cast(b2.data()), b2.size()) == "hello"); 251 | } 252 | 253 | TEST_CASE("const_buffer creation with zbuf string literal char", "[buffer]") 254 | { 255 | using namespace zmq::literals; 256 | constexpr zmq::const_buffer b = "hello"_zbuf; 257 | CHECK(b.size() == 5); 258 | CHECK(std::memcmp(b.data(), "hello", b.size()) == 0); 259 | } 260 | 261 | TEST_CASE("const_buffer creation with zbuf string literal wchar_t", "[buffer]") 262 | { 263 | using namespace zmq::literals; 264 | constexpr zmq::const_buffer b = L"hello"_zbuf; 265 | CHECK(b.size() == 5 * sizeof(wchar_t)); 266 | CHECK(std::memcmp(b.data(), L"hello", b.size()) == 0); 267 | } 268 | 269 | TEST_CASE("const_buffer creation with zbuf string literal char16_t", "[buffer]") 270 | { 271 | using namespace zmq::literals; 272 | constexpr zmq::const_buffer b = u"hello"_zbuf; 273 | CHECK(b.size() == 5 * sizeof(char16_t)); 274 | CHECK(std::memcmp(b.data(), u"hello", b.size()) == 0); 275 | } 276 | 277 | TEST_CASE("const_buffer creation with zbuf string literal char32_t", "[buffer]") 278 | { 279 | using namespace zmq::literals; 280 | constexpr zmq::const_buffer b = U"hello"_zbuf; 281 | CHECK(b.size() == 5 * sizeof(char32_t)); 282 | CHECK(std::memcmp(b.data(), U"hello", b.size()) == 0); 283 | } 284 | 285 | TEST_CASE("buffer of structs", "[buffer]") 286 | { 287 | struct some_pod 288 | { 289 | int64_t val; 290 | char arr[8]; 291 | }; 292 | struct some_non_pod 293 | { 294 | int64_t val; 295 | char arr[8]; 296 | std::vector s; // not trivially copyable 297 | }; 298 | static_assert(zmq::detail::is_pod_like::value, ""); 299 | static_assert(!zmq::detail::is_pod_like::value, ""); 300 | std::array d; 301 | zmq::mutable_buffer b = zmq::buffer(d); 302 | CHECK(b.size() == d.size() * sizeof(some_pod)); 303 | CHECK(b.data() == d.data()); 304 | } 305 | 306 | #endif 307 | -------------------------------------------------------------------------------- /tests/codec_multipart.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #ifdef ZMQ_CPP11 5 | 6 | TEST_CASE("multipart codec empty", "[codec_multipart]") 7 | { 8 | using namespace zmq; 9 | 10 | multipart_t mmsg; 11 | message_t msg = mmsg.encode(); 12 | CHECK(msg.size() == 0); 13 | 14 | multipart_t mmsg2; 15 | mmsg2.decode_append(msg); 16 | CHECK(mmsg2.size() == 0); 17 | 18 | } 19 | 20 | TEST_CASE("multipart codec small", "[codec_multipart]") 21 | { 22 | using namespace zmq; 23 | 24 | multipart_t mmsg; 25 | mmsg.addstr("Hello World"); 26 | message_t msg = mmsg.encode(); 27 | CHECK(msg.size() == 1 + 11); // small size packing 28 | 29 | mmsg.addstr("Second frame"); 30 | msg = mmsg.encode(); 31 | CHECK(msg.size() == 1 + 11 + 1 + 12); 32 | 33 | multipart_t mmsg2; 34 | mmsg2.decode_append(msg); 35 | CHECK(mmsg2.size() == 2); 36 | std::string part0 = mmsg2[0].to_string(); 37 | CHECK(part0 == "Hello World"); 38 | CHECK(mmsg2[1].to_string() == "Second frame"); 39 | } 40 | 41 | TEST_CASE("multipart codec big", "[codec_multipart]") 42 | { 43 | using namespace zmq; 44 | 45 | message_t big(495); // large size packing 46 | big.data()[0] = 'X'; 47 | 48 | multipart_t mmsg; 49 | mmsg.pushmem(big.data(), big.size()); 50 | message_t msg = mmsg.encode(); 51 | CHECK(msg.size() == 5 + 495); 52 | CHECK(msg.data()[0] == std::numeric_limits::max()); 53 | CHECK(msg.data()[5] == 'X'); 54 | 55 | CHECK(mmsg.size() == 1); 56 | mmsg.decode_append(msg); 57 | CHECK(mmsg.size() == 2); 58 | CHECK(mmsg[0].data()[0] == 'X'); 59 | } 60 | 61 | TEST_CASE("multipart codec decode bad data overflow", "[codec_multipart]") 62 | { 63 | using namespace zmq; 64 | 65 | char bad_data[3] = {5, 'h', 'i'}; 66 | message_t wrong_size(bad_data, 3); 67 | CHECK(wrong_size.size() == 3); 68 | CHECK(wrong_size.data()[0] == 5); 69 | 70 | CHECK_THROWS_AS( 71 | multipart_t::decode(wrong_size), 72 | std::out_of_range); 73 | } 74 | 75 | TEST_CASE("multipart codec decode bad data extra data", "[codec_multipart]") 76 | { 77 | using namespace zmq; 78 | 79 | char bad_data[3] = {1, 'h', 'i'}; 80 | message_t wrong_size(bad_data, 3); 81 | CHECK(wrong_size.size() == 3); 82 | CHECK(wrong_size.data()[0] == 1); 83 | 84 | CHECK_THROWS_AS( 85 | multipart_t::decode(wrong_size), 86 | std::out_of_range); 87 | } 88 | 89 | 90 | // After exercising it, this test is disabled over concern of running 91 | // on hosts which lack enough free memory to allow the absurdly large 92 | // message part to be allocated. 93 | #if 0 94 | TEST_CASE("multipart codec encode too big", "[codec_multipart]") 95 | { 96 | using namespace zmq; 97 | 98 | const size_t too_big_size = 1L + std::numeric_limits::max(); 99 | CHECK(too_big_size > std::numeric_limits::max()); 100 | char* too_big_data = new char[too_big_size]; 101 | multipart_t mmsg(too_big_data, too_big_size); 102 | delete [] too_big_data; 103 | 104 | CHECK(mmsg.size() == 1); 105 | CHECK(mmsg[0].size() > std::numeric_limits::max()); 106 | 107 | CHECK_THROWS_AS( 108 | mmsg.encode(), 109 | std::range_error); 110 | } 111 | #endif 112 | 113 | TEST_CASE("multipart codec free function with vector of message_t", "[codec_multipart]") 114 | { 115 | using namespace zmq; 116 | std::vector parts; 117 | parts.emplace_back("Hello", 5); 118 | parts.emplace_back("World",5); 119 | auto msg = encode(parts); 120 | CHECK(msg.size() == 1 + 5 + 1 + 5 ); 121 | CHECK(msg.data()[0] == 5); 122 | CHECK(msg.data()[1] == 'H'); 123 | CHECK(msg.data()[6] == 5); 124 | CHECK(msg.data()[7] == 'W'); 125 | 126 | std::vector parts2; 127 | decode(msg, std::back_inserter(parts2)); 128 | CHECK(parts.size() == 2); 129 | CHECK(parts[0].size() == 5); 130 | CHECK(parts[1].size() == 5); 131 | } 132 | 133 | TEST_CASE("multipart codec free function with vector of const_buffer", "[codec_multipart]") 134 | { 135 | using namespace zmq; 136 | std::vector parts; 137 | parts.emplace_back("Hello", 5); 138 | parts.emplace_back("World",5); 139 | auto msg = encode(parts); 140 | CHECK(msg.size() == 1 + 5 + 1 + 5 ); 141 | CHECK(msg.data()[0] == 5); 142 | CHECK(msg.data()[1] == 'H'); 143 | CHECK(msg.data()[6] == 5); 144 | CHECK(msg.data()[7] == 'W'); 145 | 146 | std::vector parts2; 147 | decode(msg, std::back_inserter(parts2)); 148 | CHECK(parts.size() == 2); 149 | CHECK(parts[0].size() == 5); 150 | CHECK(parts[1].size() == 5); 151 | } 152 | 153 | TEST_CASE("multipart codec free function with vector of mutable_buffer", "[codec_multipart]") 154 | { 155 | using namespace zmq; 156 | std::vector parts; 157 | char hello[6] = "Hello"; 158 | parts.emplace_back(hello, 5); 159 | char world[6] = "World"; 160 | parts.emplace_back(world,5); 161 | auto msg = encode(parts); 162 | CHECK(msg.size() == 1 + 5 + 1 + 5 ); 163 | CHECK(msg.data()[0] == 5); 164 | CHECK(msg.data()[1] == 'H'); 165 | CHECK(msg.data()[6] == 5); 166 | CHECK(msg.data()[7] == 'W'); 167 | 168 | std::vector parts2; 169 | decode(msg, std::back_inserter(parts2)); 170 | CHECK(parts.size() == 2); 171 | CHECK(parts[0].size() == 5); 172 | CHECK(parts[1].size() == 5); 173 | } 174 | 175 | TEST_CASE("multipart codec free function with multipart_t", "[codec_multipart]") 176 | { 177 | using namespace zmq; 178 | multipart_t mmsg; 179 | mmsg.addstr("Hello"); 180 | mmsg.addstr("World"); 181 | auto msg = encode(mmsg); 182 | CHECK(msg.size() == 1 + 5 + 1 + 5); 183 | CHECK(msg.data()[0] == 5); 184 | CHECK(msg.data()[1] == 'H'); 185 | CHECK(msg.data()[6] == 5); 186 | CHECK(msg.data()[7] == 'W'); 187 | 188 | multipart_t mmsg2; 189 | decode(msg, std::back_inserter(mmsg2)); 190 | CHECK(mmsg2.size() == 2); 191 | CHECK(mmsg2[0].size() == 5); 192 | CHECK(mmsg2[1].size() == 5); 193 | } 194 | 195 | TEST_CASE("multipart codec static method decode to multipart_t", "[codec_multipart]") 196 | { 197 | using namespace zmq; 198 | multipart_t mmsg; 199 | mmsg.addstr("Hello"); 200 | mmsg.addstr("World"); 201 | auto msg = encode(mmsg); 202 | 203 | auto mmsg2 = multipart_t::decode(msg); 204 | CHECK(mmsg2.size() == 2); 205 | CHECK(mmsg2[0].size() == 5); 206 | CHECK(mmsg2[1].size() == 5); 207 | } 208 | 209 | 210 | #endif 211 | -------------------------------------------------------------------------------- /tests/context.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #if (__cplusplus >= 201703L) 5 | static_assert(std::is_nothrow_swappable::value, 6 | "context_t should be nothrow swappable"); 7 | #endif 8 | 9 | TEST_CASE("context construct default and destroy", "[context]") 10 | { 11 | zmq::context_t context; 12 | } 13 | 14 | TEST_CASE("context create, close and destroy", "[context]") 15 | { 16 | zmq::context_t context; 17 | context.close(); 18 | CHECK(NULL == context.handle()); 19 | } 20 | 21 | TEST_CASE("context shutdown", "[context]") 22 | { 23 | zmq::context_t context; 24 | context.shutdown(); 25 | CHECK(NULL != context.handle()); 26 | context.close(); 27 | CHECK(NULL == context.handle()); 28 | } 29 | 30 | TEST_CASE("context shutdown again", "[context]") 31 | { 32 | zmq::context_t context; 33 | context.shutdown(); 34 | context.shutdown(); 35 | CHECK(NULL != context.handle()); 36 | context.close(); 37 | CHECK(NULL == context.handle()); 38 | } 39 | 40 | #ifdef ZMQ_CPP11 41 | TEST_CASE("context swap", "[context]") 42 | { 43 | zmq::context_t context1; 44 | zmq::context_t context2; 45 | using std::swap; 46 | swap(context1, context2); 47 | } 48 | 49 | TEST_CASE("context - use socket after shutdown", "[context]") 50 | { 51 | zmq::context_t context; 52 | zmq::socket_t sock(context, zmq::socket_type::rep); 53 | context.shutdown(); 54 | try 55 | { 56 | sock.connect("inproc://test"); 57 | zmq::message_t msg; 58 | (void)sock.recv(msg, zmq::recv_flags::dontwait); 59 | REQUIRE(false); 60 | } 61 | catch (const zmq::error_t& e) 62 | { 63 | REQUIRE(e.num() == ETERM); 64 | } 65 | } 66 | 67 | TEST_CASE("context set/get options", "[context]") 68 | { 69 | zmq::context_t context; 70 | #if defined(ZMQ_BLOCKY) && defined(ZMQ_IO_THREADS) 71 | context.set(zmq::ctxopt::blocky, false); 72 | context.set(zmq::ctxopt::io_threads, 5); 73 | CHECK(context.get(zmq::ctxopt::io_threads) == 5); 74 | #endif 75 | 76 | CHECK_THROWS_AS( 77 | context.set(static_cast(-42), 5), 78 | zmq::error_t); 79 | 80 | CHECK_THROWS_AS( 81 | context.get(static_cast(-42)), 82 | zmq::error_t); 83 | } 84 | #endif 85 | -------------------------------------------------------------------------------- /tests/message.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #if defined(ZMQ_CPP11) 5 | static_assert(!std::is_copy_constructible::value, 6 | "message_t should not be copy-constructible"); 7 | static_assert(!std::is_copy_assignable::value, 8 | "message_t should not be copy-assignable"); 9 | #endif 10 | #if (__cplusplus >= 201703L) 11 | static_assert(std::is_nothrow_swappable::value, 12 | "message_t should be nothrow swappable"); 13 | #endif 14 | 15 | TEST_CASE("message default constructed", "[message]") 16 | { 17 | const zmq::message_t message; 18 | CHECK(0u == message.size()); 19 | CHECK(message.empty()); 20 | } 21 | 22 | #ifdef ZMQ_CPP11 23 | TEST_CASE("message swap", "[message]") 24 | { 25 | const std::string data = "foo"; 26 | zmq::message_t message1; 27 | zmq::message_t message2(data.data(), data.size()); 28 | using std::swap; 29 | swap(message1, message2); 30 | CHECK(message1.size() == data.size()); 31 | CHECK(message2.size() == 0); 32 | swap(message1, message2); 33 | CHECK(message1.size() == 0); 34 | CHECK(message2.size() == data.size()); 35 | } 36 | #endif 37 | 38 | namespace 39 | { 40 | const char *const data = "Hi"; 41 | } 42 | 43 | TEST_CASE("message constructor with iterators", "[message]") 44 | { 45 | const std::string hi(data); 46 | const zmq::message_t hi_msg(hi.begin(), hi.end()); 47 | CHECK(2u == hi_msg.size()); 48 | CHECK(0 == memcmp(data, hi_msg.data(), 2)); 49 | } 50 | 51 | TEST_CASE("message constructor with size", "[message]") 52 | { 53 | const zmq::message_t msg(5); 54 | CHECK(msg.size() == 5); 55 | } 56 | 57 | TEST_CASE("message constructor with buffer and size", "[message]") 58 | { 59 | const std::string hi(data); 60 | const zmq::message_t hi_msg(hi.data(), hi.size()); 61 | CHECK(2u == hi_msg.size()); 62 | CHECK(0 == memcmp(data, hi_msg.data(), 2)); 63 | } 64 | 65 | TEST_CASE("message constructor with char array", "[message]") 66 | { 67 | const zmq::message_t hi_msg(data, strlen(data)); 68 | CHECK(2u == hi_msg.size()); 69 | CHECK(0 == memcmp(data, hi_msg.data(), 2)); 70 | } 71 | 72 | #if defined(ZMQ_CPP11) && !defined(ZMQ_CPP11_PARTIAL) 73 | TEST_CASE("message constructor with container - deprecated", "[message]") 74 | { 75 | zmq::message_t hi_msg("Hi"); // deprecated 76 | REQUIRE(3u == hi_msg.size()); 77 | CHECK(0 == memcmp(data, hi_msg.data(), 3)); 78 | } 79 | 80 | TEST_CASE("message constructor with container of trivial data", "[message]") 81 | { 82 | int buf[3] = {1, 2, 3}; 83 | zmq::message_t msg(buf); 84 | REQUIRE(sizeof(buf) == msg.size()); 85 | CHECK(0 == memcmp(buf, msg.data(), msg.size())); 86 | } 87 | 88 | TEST_CASE("message constructor with strings", "[message]") 89 | { 90 | SECTION("string") 91 | { 92 | const std::string hi(data); 93 | zmq::message_t hi_msg(hi); 94 | CHECK(2u == hi_msg.size()); 95 | CHECK(0 == memcmp(data, hi_msg.data(), 2)); 96 | } 97 | #if CPPZMQ_HAS_STRING_VIEW 98 | SECTION("string_view") 99 | { 100 | const std::string_view hi(data); 101 | zmq::message_t hi_msg(hi); 102 | CHECK(2u == hi_msg.size()); 103 | CHECK(0 == memcmp(data, hi_msg.data(), 2)); 104 | } 105 | #endif 106 | } 107 | #endif 108 | 109 | #ifdef ZMQ_HAS_RVALUE_REFS 110 | TEST_CASE("message move constructor", "[message]") 111 | { 112 | zmq::message_t hi_msg(zmq::message_t(data, strlen(data))); 113 | } 114 | 115 | TEST_CASE("message assign move empty before", "[message]") 116 | { 117 | zmq::message_t hi_msg; 118 | hi_msg = zmq::message_t(data, strlen(data)); 119 | CHECK(2u == hi_msg.size()); 120 | CHECK(0 == memcmp(data, hi_msg.data(), 2)); 121 | } 122 | 123 | TEST_CASE("message assign move empty after", "[message]") 124 | { 125 | zmq::message_t hi_msg(data, strlen(data)); 126 | CHECK(!hi_msg.empty()); 127 | hi_msg = zmq::message_t(); 128 | CHECK(0u == hi_msg.size()); 129 | CHECK(hi_msg.empty()); 130 | } 131 | 132 | TEST_CASE("message assign move empty before and after", "[message]") 133 | { 134 | zmq::message_t hi_msg; 135 | hi_msg = zmq::message_t(); 136 | CHECK(0u == hi_msg.size()); 137 | } 138 | #endif 139 | 140 | TEST_CASE("message equality self", "[message]") 141 | { 142 | const zmq::message_t hi_msg(data, strlen(data)); 143 | CHECK(hi_msg == hi_msg); 144 | } 145 | 146 | TEST_CASE("message equality equal", "[message]") 147 | { 148 | const zmq::message_t hi_msg_a(data, strlen(data)); 149 | const zmq::message_t hi_msg_b(data, strlen(data)); 150 | CHECK(hi_msg_a == hi_msg_b); 151 | } 152 | 153 | TEST_CASE("message equality equal empty", "[message]") 154 | { 155 | const zmq::message_t msg_a; 156 | const zmq::message_t msg_b; 157 | CHECK(msg_a == msg_b); 158 | } 159 | 160 | TEST_CASE("message equality non equal", "[message]") 161 | { 162 | const zmq::message_t msg_a("Hi", 2); 163 | const zmq::message_t msg_b("Hello", 5); 164 | CHECK(msg_a != msg_b); 165 | } 166 | 167 | TEST_CASE("message equality non equal rhs empty", "[message]") 168 | { 169 | const zmq::message_t msg_a("Hi", 2); 170 | const zmq::message_t msg_b; 171 | CHECK(msg_a != msg_b); 172 | } 173 | 174 | TEST_CASE("message equality non equal lhs empty", "[message]") 175 | { 176 | const zmq::message_t msg_a; 177 | const zmq::message_t msg_b("Hi", 2); 178 | CHECK(msg_a != msg_b); 179 | } 180 | 181 | TEST_CASE("message to string", "[message]") 182 | { 183 | const zmq::message_t a; 184 | const zmq::message_t b("Foo", 3); 185 | CHECK(a.to_string() == ""); 186 | CHECK(b.to_string() == "Foo"); 187 | #if CPPZMQ_HAS_STRING_VIEW 188 | CHECK(a.to_string_view() == ""); 189 | CHECK(b.to_string_view() == "Foo"); 190 | #endif 191 | 192 | #if defined(ZMQ_CPP11) && !defined(ZMQ_CPP11_PARTIAL) 193 | const zmq::message_t depr("Foo"); // deprecated 194 | CHECK(depr.to_string() != "Foo"); 195 | CHECK(depr.to_string() == std::string("Foo", 4)); 196 | #endif 197 | } 198 | 199 | #if defined(ZMQ_BUILD_DRAFT_API) && ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 0) 200 | TEST_CASE("message routing id persists", "[message]") 201 | { 202 | zmq::message_t msg; 203 | msg.set_routing_id(123); 204 | CHECK(123u == msg.routing_id()); 205 | } 206 | 207 | TEST_CASE("message group persists", "[message]") 208 | { 209 | zmq::message_t msg; 210 | msg.set_group("mygroup"); 211 | CHECK(std::string(msg.group()) == "mygroup"); 212 | } 213 | #endif 214 | 215 | #if ZMQ_VERSION >= ZMQ_MAKE_VERSION(3, 2, 0) 216 | TEST_CASE("message is not shared", "[message]") 217 | { 218 | zmq::message_t msg; 219 | CHECK(msg.get(ZMQ_SHARED) == 0); 220 | } 221 | 222 | TEST_CASE("message is shared", "[message]") 223 | { 224 | size_t msg_sz = 1024; // large enough to be a type_lmsg 225 | zmq::message_t msg1(msg_sz); 226 | zmq::message_t msg2; 227 | msg2.copy(msg1); 228 | CHECK(msg1.get(ZMQ_SHARED) == 1); 229 | CHECK(msg2.get(ZMQ_SHARED) == 1); 230 | CHECK(msg1.size() == msg_sz); 231 | CHECK(msg2.size() == msg_sz); 232 | } 233 | 234 | TEST_CASE("message move is not shared", "[message]") 235 | { 236 | size_t msg_sz = 1024; // large enough to be a type_lmsg 237 | zmq::message_t msg1(msg_sz); 238 | zmq::message_t msg2; 239 | msg2.move(msg1); 240 | CHECK(msg1.get(ZMQ_SHARED) == 0); 241 | CHECK(msg2.get(ZMQ_SHARED) == 0); 242 | CHECK(msg2.size() == msg_sz); 243 | CHECK(msg1.size() == 0); 244 | } 245 | #endif 246 | -------------------------------------------------------------------------------- /tests/monitor.cpp: -------------------------------------------------------------------------------- 1 | #include "testutil.hpp" 2 | 3 | #ifdef ZMQ_CPP11 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | class mock_monitor_t : public zmq::monitor_t 10 | { 11 | public: 12 | 13 | void on_event_connected(const zmq_event_t &, const char *) ZMQ_OVERRIDE 14 | { 15 | ++connected; 16 | ++total; 17 | } 18 | 19 | int total{0}; 20 | int connected{0}; 21 | }; 22 | 23 | #endif 24 | 25 | TEST_CASE("monitor create destroy", "[monitor]") 26 | { 27 | zmq::monitor_t monitor; 28 | } 29 | 30 | #if defined(ZMQ_CPP11) 31 | TEST_CASE("monitor move construct", "[monitor]") 32 | { 33 | zmq::context_t ctx; 34 | zmq::socket_t sock(ctx, ZMQ_DEALER); 35 | SECTION("move ctor empty") { 36 | zmq::monitor_t monitor1; 37 | zmq::monitor_t monitor2 = std::move(monitor1); 38 | } 39 | SECTION("move ctor init") { 40 | zmq::monitor_t monitor1; 41 | monitor1.init(sock, "inproc://monitor-client"); 42 | zmq::monitor_t monitor2 = std::move(monitor1); 43 | } 44 | } 45 | 46 | TEST_CASE("monitor move assign", "[monitor]") 47 | { 48 | zmq::context_t ctx; 49 | zmq::socket_t sock(ctx, ZMQ_DEALER); 50 | SECTION("move assign empty") { 51 | zmq::monitor_t monitor1; 52 | zmq::monitor_t monitor2; 53 | monitor1 = std::move(monitor2); 54 | } 55 | SECTION("move assign init") { 56 | zmq::monitor_t monitor1; 57 | monitor1.init(sock, "inproc://monitor-client"); 58 | zmq::monitor_t monitor2; 59 | monitor2 = std::move(monitor1); 60 | } 61 | SECTION("move assign init both") { 62 | zmq::monitor_t monitor1; 63 | monitor1.init(sock, "inproc://monitor-client"); 64 | zmq::monitor_t monitor2; 65 | zmq::socket_t sock2(ctx, ZMQ_DEALER); 66 | monitor2.init(sock2, "inproc://monitor-client2"); 67 | monitor2 = std::move(monitor1); 68 | } 69 | } 70 | 71 | TEST_CASE("monitor init event count", "[monitor]") 72 | { 73 | common_server_client_setup s{false}; 74 | mock_monitor_t monitor; 75 | 76 | const int expected_event_count = 1; 77 | monitor.init(s.client, "inproc://foo"); 78 | 79 | CHECK_FALSE(monitor.check_event(0)); 80 | s.init(); 81 | 82 | while (monitor.check_event(1000) && monitor.total < expected_event_count) { 83 | } 84 | CHECK(monitor.connected == 1); 85 | CHECK(monitor.total == expected_event_count); 86 | } 87 | 88 | TEST_CASE("monitor init abort", "[monitor]") 89 | { 90 | class mock_monitor : public mock_monitor_t 91 | { 92 | public: 93 | mock_monitor(std::function handle_connected) : 94 | handle_connected{std::move(handle_connected)} 95 | { 96 | } 97 | 98 | void on_event_connected(const zmq_event_t &e, const char *m) ZMQ_OVERRIDE 99 | { 100 | mock_monitor_t::on_event_connected(e, m); 101 | handle_connected(); 102 | } 103 | 104 | std::function handle_connected; 105 | }; 106 | 107 | common_server_client_setup s(false); 108 | 109 | std::mutex mutex; 110 | std::condition_variable cond_var; 111 | bool done{false}; 112 | 113 | mock_monitor monitor([&]() 114 | { 115 | std::lock_guard lock(mutex); 116 | done = true; 117 | cond_var.notify_one(); 118 | }); 119 | monitor.init(s.client, "inproc://foo"); 120 | 121 | auto thread = std::thread([&monitor] 122 | { 123 | while (monitor.check_event(-1)) { 124 | } 125 | }); 126 | 127 | s.init(); 128 | { 129 | std::unique_lock lock(mutex); 130 | CHECK(cond_var.wait_for(lock, std::chrono::seconds(1), 131 | [&done] { return done; })); 132 | } 133 | CHECK(monitor.connected == 1); 134 | monitor.abort(); 135 | thread.join(); 136 | } 137 | 138 | 139 | TEST_CASE("monitor from move assigned socket", "[monitor]") 140 | { 141 | zmq::context_t ctx; 142 | zmq::socket_t sock; 143 | sock = std::move([&ctx] { 144 | zmq::socket_t sock(ctx, ZMQ_DEALER); 145 | return sock; 146 | }()); 147 | zmq::monitor_t monitor1; 148 | monitor1.init(sock, "inproc://monitor-client"); 149 | // On failure, this test might hang indefinitely instead of immediately 150 | // failing 151 | } 152 | #endif 153 | 154 | #if defined(ZMQ_BUILD_DRAFT_API) && defined(ZMQ_CPP11) \ 155 | && !defined(ZMQ_CPP11_PARTIAL) && defined(ZMQ_HAVE_POLLER) 156 | #include "zmq_addon.hpp" 157 | 158 | TEST_CASE("poll monitor events using active poller", "[monitor]") 159 | { 160 | // define necessary class for test 161 | class test_monitor : public zmq::monitor_t 162 | { 163 | public: 164 | void init(zmq::socket_t &socket, 165 | const char *const addr_, 166 | int events = ZMQ_EVENT_ALL) 167 | { 168 | zmq::monitor_t::init(socket, addr_, events); 169 | } 170 | 171 | void addToPoller(zmq::active_poller_t &inActivePoller) 172 | { 173 | inActivePoller.add( 174 | monitor_socket(), zmq::event_flags::pollin, 175 | [&](zmq::event_flags ef) { process_event(static_cast(ef)); }); 176 | } 177 | 178 | void on_event_accepted(const zmq_event_t &event_, const char *addr_) override 179 | { 180 | clientAccepted++; 181 | } 182 | void on_event_disconnected(const zmq_event_t &event, 183 | const char *const addr) override 184 | { 185 | clientDisconnected++; 186 | } 187 | 188 | int clientAccepted = 0; 189 | int clientDisconnected = 0; 190 | }; 191 | 192 | //Arrange 193 | int messageCounter = 0; 194 | const char monitorAddress[] = "inproc://monitor-server"; 195 | 196 | auto addToPoller = [&](zmq::socket_t &socket, zmq::active_poller_t &poller) { 197 | poller.add(socket, zmq::event_flags::pollin, [&](zmq::event_flags ef) { 198 | zmq::message_t msg; 199 | auto result = socket.recv(msg, zmq::recv_flags::dontwait); 200 | messageCounter++; 201 | }); 202 | }; 203 | 204 | common_server_client_setup sockets(false); 205 | 206 | test_monitor monitor; 207 | monitor.init(sockets.server, monitorAddress, 208 | ZMQ_EVENT_ACCEPTED | ZMQ_EVENT_DISCONNECTED); 209 | 210 | zmq::active_poller_t poller; 211 | monitor.addToPoller(poller); 212 | addToPoller(sockets.server, poller); 213 | 214 | sockets.init(); 215 | sockets.client.send(zmq::message_t(0), zmq::send_flags::dontwait); 216 | CHECK(monitor.clientAccepted == 0); 217 | CHECK(monitor.clientDisconnected == 0); 218 | 219 | //Act 220 | for (int i = 0; i < 100; i++) { 221 | poller.wait(std::chrono::milliseconds(50)); 222 | if (monitor.clientAccepted > 0) { 223 | break; 224 | } 225 | } 226 | CHECK(monitor.clientAccepted == 1); 227 | CHECK(monitor.clientDisconnected == 0); 228 | 229 | sockets.client.close(); 230 | 231 | for (int i = 0; i < 100; i++) { 232 | poller.wait(std::chrono::milliseconds(50)); 233 | if (monitor.clientDisconnected > 0) { 234 | break; 235 | } 236 | } 237 | sockets.server.close(); 238 | 239 | // Assert 240 | CHECK(messageCounter == 1); 241 | CHECK(monitor.clientAccepted == 1); 242 | CHECK(monitor.clientDisconnected == 1); 243 | } 244 | #endif 245 | -------------------------------------------------------------------------------- /tests/multipart.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #ifdef ZMQ_HAS_RVALUE_REFS 5 | 6 | #ifdef ZMQ_CPP17 7 | static_assert(std::is_invocable::value, 11 | "Can't multipart_t::send with socket_ref"); 12 | static_assert(std::is_invocable::value, 16 | "Can't multipart_t::recv with socket_ref"); 17 | #endif 18 | static_assert(std::is_constructible::value, 19 | "Can't construct with socket_ref"); 20 | 21 | /// \todo split this up into separate test cases 22 | /// 23 | TEST_CASE("multipart legacy test", "[multipart]") 24 | { 25 | using namespace zmq; 26 | 27 | bool ok = true; 28 | (void) ok; 29 | float num = 0; 30 | (void) num; 31 | std::string str = ""; 32 | message_t msg; 33 | 34 | // Create two PAIR sockets and connect over inproc 35 | context_t context(1); 36 | socket_t output(context, ZMQ_PAIR); 37 | socket_t input(context, ZMQ_PAIR); 38 | output.bind("inproc://multipart.test"); 39 | input.connect("inproc://multipart.test"); 40 | 41 | // Test send and receive of single-frame message 42 | multipart_t multipart; 43 | assert(multipart.empty()); 44 | 45 | multipart.push(message_t("Hello", 5)); 46 | assert(multipart.size() == 1); 47 | 48 | ok = multipart.send(output); 49 | assert(multipart.empty()); 50 | assert(ok); 51 | 52 | ok = multipart.recv(input); 53 | assert(multipart.size() == 1); 54 | assert(ok); 55 | 56 | msg = multipart.pop(); 57 | assert(multipart.empty()); 58 | assert(std::string(msg.data(), msg.size()) == "Hello"); 59 | 60 | // Test send and receive of multi-frame message 61 | multipart.addstr("A"); 62 | multipart.addstr("BB"); 63 | multipart.addstr("CCC"); 64 | assert(multipart.size() == 3); 65 | 66 | multipart_t copy = multipart.clone(); 67 | assert(copy.size() == 3); 68 | 69 | ok = copy.send(output); 70 | assert(copy.empty()); 71 | assert(ok); 72 | 73 | ok = copy.recv(input); 74 | assert(copy.size() == 3); 75 | assert(ok); 76 | assert(copy.equal(&multipart)); 77 | 78 | // Test equality operators 79 | assert(copy == multipart); 80 | assert(multipart == copy); 81 | 82 | multipart.pop(); 83 | 84 | assert(copy != multipart); 85 | assert(multipart != copy); 86 | 87 | multipart_t emptyMessage1 {}; 88 | multipart_t emptyMessage2 {}; 89 | 90 | assert(emptyMessage1 == emptyMessage2); 91 | assert(emptyMessage2 == emptyMessage1); 92 | 93 | multipart.clear(); 94 | assert(multipart.empty()); 95 | 96 | // Test message frame manipulation 97 | multipart.add(message_t("Frame5", 6)); 98 | multipart.addstr("Frame6"); 99 | multipart.addstr("Frame7"); 100 | multipart.addtyp(8.0f); 101 | multipart.addmem("Frame9", 6); 102 | multipart.push(message_t("Frame4", 6)); 103 | multipart.pushstr("Frame3"); 104 | multipart.pushstr("Frame2"); 105 | multipart.pushtyp(1.0f); 106 | multipart.pushmem("Frame0", 6); 107 | assert(multipart.size() == 10); 108 | 109 | const message_t &front_msg = multipart.front(); 110 | assert(multipart.size() == 10); 111 | assert(std::string(front_msg.data(), front_msg.size()) == "Frame0"); 112 | 113 | const message_t &back_msg = multipart.back(); 114 | assert(multipart.size() == 10); 115 | assert(std::string(back_msg.data(), back_msg.size()) == "Frame9"); 116 | 117 | msg = multipart.remove(); 118 | assert(multipart.size() == 9); 119 | assert(std::string(msg.data(), msg.size()) == "Frame9"); 120 | 121 | msg = multipart.pop(); 122 | assert(multipart.size() == 8); 123 | assert(std::string(msg.data(), msg.size()) == "Frame0"); 124 | 125 | num = multipart.poptyp(); 126 | assert(multipart.size() == 7); 127 | assert(num == 1.0f); 128 | 129 | str = multipart.popstr(); 130 | assert(multipart.size() == 6); 131 | assert(str == "Frame2"); 132 | 133 | str = multipart.popstr(); 134 | assert(multipart.size() == 5); 135 | assert(str == "Frame3"); 136 | 137 | str = multipart.popstr(); 138 | assert(multipart.size() == 4); 139 | assert(str == "Frame4"); 140 | 141 | str = multipart.popstr(); 142 | assert(multipart.size() == 3); 143 | assert(str == "Frame5"); 144 | 145 | str = multipart.popstr(); 146 | assert(multipart.size() == 2); 147 | assert(str == "Frame6"); 148 | 149 | str = multipart.popstr(); 150 | assert(multipart.size() == 1); 151 | assert(str == "Frame7"); 152 | 153 | num = multipart.poptyp(); 154 | assert(multipart.empty()); 155 | assert(num == 8.0f); 156 | 157 | // Test message constructors and concatenation 158 | multipart_t head("One", 3); 159 | head.addstr("Two"); 160 | assert(head.size() == 2); 161 | 162 | multipart_t tail(std::string("One-hundred")); 163 | tail.pushstr("Ninety-nine"); 164 | assert(tail.size() == 2); 165 | 166 | multipart_t tmp(message_t("Fifty", 5)); 167 | assert(tmp.size() == 1); 168 | 169 | multipart_t mid = multipart_t::create(49.0f); 170 | mid.append(std::move(tmp)); 171 | assert(mid.size() == 2); 172 | assert(tmp.empty()); 173 | 174 | multipart_t merged(std::move(mid)); 175 | merged.prepend(std::move(head)); 176 | merged.append(std::move(tail)); 177 | assert(merged.size() == 6); 178 | assert(head.empty()); 179 | assert(tail.empty()); 180 | 181 | ok = merged.send(output); 182 | assert(merged.empty()); 183 | assert(ok); 184 | 185 | multipart_t received(input); 186 | assert(received.size() == 6); 187 | 188 | str = received.popstr(); 189 | assert(received.size() == 5); 190 | assert(str == "One"); 191 | 192 | str = received.popstr(); 193 | assert(received.size() == 4); 194 | assert(str == "Two"); 195 | 196 | num = received.poptyp(); 197 | assert(received.size() == 3); 198 | assert(num == 49.0f); 199 | 200 | str = received.popstr(); 201 | assert(received.size() == 2); 202 | assert(str == "Fifty"); 203 | 204 | str = received.popstr(); 205 | assert(received.size() == 1); 206 | assert(str == "Ninety-nine"); 207 | 208 | str = received.popstr(); 209 | assert(received.empty()); 210 | assert(str == "One-hundred"); 211 | } 212 | #endif 213 | -------------------------------------------------------------------------------- /tests/poller.cpp: -------------------------------------------------------------------------------- 1 | #include "testutil.hpp" 2 | 3 | #if defined(ZMQ_BUILD_DRAFT_API) && defined(ZMQ_CPP11) && !defined(ZMQ_CPP11_PARTIAL) && defined(ZMQ_HAVE_POLLER) 4 | 5 | #include 6 | #include 7 | 8 | #ifdef ZMQ_CPP17 9 | static_assert(std::is_nothrow_swappable_v>); 10 | #endif 11 | static_assert(sizeof(zmq_poller_event_t) == sizeof(zmq::poller_event<>), ""); 12 | static_assert(sizeof(zmq_poller_event_t) == sizeof(zmq::poller_event), ""); 14 | static_assert(sizeof(zmq_poller_event_t) == sizeof(zmq::poller_event), ""); 15 | static_assert(alignof(zmq_poller_event_t) == alignof(zmq::poller_event<>), ""); 16 | static_assert(alignof(zmq_poller_event_t) == alignof(zmq::poller_event), ""); 17 | 18 | static_assert(!std::is_copy_constructible>::value, 19 | "poller_t should not be copy-constructible"); 20 | static_assert(!std::is_copy_assignable>::value, 21 | "poller_t should not be copy-assignable"); 22 | 23 | TEST_CASE("event flags", "[poller]") 24 | { 25 | CHECK((zmq::event_flags::pollin | zmq::event_flags::pollout) 26 | == static_cast(ZMQ_POLLIN | ZMQ_POLLOUT)); 27 | CHECK((zmq::event_flags::pollin & zmq::event_flags::pollout) 28 | == static_cast(ZMQ_POLLIN & ZMQ_POLLOUT)); 29 | CHECK((zmq::event_flags::pollin ^ zmq::event_flags::pollout) 30 | == static_cast(ZMQ_POLLIN ^ ZMQ_POLLOUT)); 31 | CHECK(~zmq::event_flags::pollin == static_cast(~ZMQ_POLLIN)); 32 | } 33 | 34 | TEST_CASE("poller create destroy", "[poller]") 35 | { 36 | zmq::poller_t<> a; 37 | #ifdef ZMQ_CPP17 // CTAD 38 | zmq::poller_t b; 39 | zmq::poller_event e; 40 | #endif 41 | } 42 | 43 | TEST_CASE("poller move construct empty", "[poller]") 44 | { 45 | zmq::poller_t<> a; 46 | zmq::poller_t<> b = std::move(a); 47 | } 48 | 49 | TEST_CASE("poller move assign empty", "[poller]") 50 | { 51 | zmq::poller_t<> a; 52 | zmq::poller_t<> b; 53 | b = std::move(a); 54 | } 55 | 56 | TEST_CASE("poller swap", "[poller]") 57 | { 58 | zmq::poller_t<> a; 59 | zmq::poller_t<> b; 60 | using std::swap; 61 | swap(a, b); 62 | } 63 | 64 | TEST_CASE("poller move construct non empty", "[poller]") 65 | { 66 | zmq::context_t context; 67 | zmq::socket_t socket{context, zmq::socket_type::router}; 68 | 69 | zmq::poller_t<> a; 70 | a.add(socket, zmq::event_flags::pollin); 71 | zmq::poller_t<> b = std::move(a); 72 | } 73 | 74 | TEST_CASE("poller move assign non empty", "[poller]") 75 | { 76 | zmq::context_t context; 77 | zmq::socket_t socket{context, zmq::socket_type::router}; 78 | 79 | zmq::poller_t<> a; 80 | a.add(socket, zmq::event_flags::pollin); 81 | zmq::poller_t<> b; 82 | b = std::move(a); 83 | } 84 | 85 | TEST_CASE("poller add nullptr", "[poller]") 86 | { 87 | zmq::context_t context; 88 | zmq::socket_t socket{context, zmq::socket_type::router}; 89 | zmq::poller_t poller; 90 | CHECK_NOTHROW(poller.add(socket, zmq::event_flags::pollin, nullptr)); 91 | } 92 | 93 | TEST_CASE("poller add non nullptr", "[poller]") 94 | { 95 | zmq::context_t context; 96 | zmq::socket_t socket{context, zmq::socket_type::router}; 97 | zmq::poller_t poller; 98 | int i; 99 | CHECK_NOTHROW(poller.add(socket, zmq::event_flags::pollin, &i)); 100 | } 101 | 102 | #if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 3, 0) 103 | // this behaviour was added by https://github.com/zeromq/libzmq/pull/3100 104 | TEST_CASE("poller add handler invalid events type", "[poller]") 105 | { 106 | zmq::context_t context; 107 | zmq::socket_t socket{context, zmq::socket_type::router}; 108 | zmq::poller_t<> poller; 109 | short invalid_events_type = 2 << 10; 110 | CHECK_THROWS_AS( 111 | poller.add(socket, static_cast(invalid_events_type)), 112 | zmq::error_t); 113 | } 114 | #endif 115 | 116 | TEST_CASE("poller add handler twice throws", "[poller]") 117 | { 118 | zmq::context_t context; 119 | zmq::socket_t socket{context, zmq::socket_type::router}; 120 | zmq::poller_t<> poller; 121 | poller.add(socket, zmq::event_flags::pollin); 122 | /// \todo the actual error code should be checked 123 | CHECK_THROWS_AS(poller.add(socket, zmq::event_flags::pollin), 124 | zmq::error_t); 125 | } 126 | 127 | TEST_CASE("poller wait with no handlers throws", "[poller]") 128 | { 129 | zmq::poller_t<> poller; 130 | std::vector> events; 131 | /// \todo the actual error code should be checked 132 | CHECK_THROWS_AS(poller.wait_all(events, std::chrono::milliseconds{10}), 133 | zmq::error_t); 134 | } 135 | 136 | #if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 3, 3) 137 | TEST_CASE("poller add/remove size checks", "[poller]") 138 | { 139 | zmq::context_t context; 140 | zmq::socket_t socket{context, zmq::socket_type::router}; 141 | zmq::poller_t<> poller; 142 | CHECK(poller.size() == 0); 143 | poller.add(socket, zmq::event_flags::pollin); 144 | CHECK(poller.size() == 1); 145 | CHECK_NOTHROW(poller.remove(socket)); 146 | CHECK(poller.size() == 0); 147 | } 148 | #endif 149 | 150 | TEST_CASE("poller remove unregistered throws", "[poller]") 151 | { 152 | zmq::context_t context; 153 | zmq::socket_t socket{context, zmq::socket_type::router}; 154 | zmq::poller_t<> poller; 155 | /// \todo the actual error code should be checked 156 | CHECK_THROWS_AS(poller.remove(socket), zmq::error_t); 157 | } 158 | 159 | TEST_CASE("poller remove registered empty", "[poller]") 160 | { 161 | zmq::context_t context; 162 | zmq::socket_t socket{context, zmq::socket_type::router}; 163 | zmq::poller_t<> poller; 164 | poller.add(socket, zmq::event_flags::pollin); 165 | CHECK_NOTHROW(poller.remove(socket)); 166 | } 167 | 168 | TEST_CASE("poller remove registered non empty", "[poller]") 169 | { 170 | zmq::context_t context; 171 | zmq::socket_t socket{context, zmq::socket_type::router}; 172 | zmq::poller_t poller; 173 | int empty{}; 174 | poller.add(socket, zmq::event_flags::pollin, &empty); 175 | CHECK_NOTHROW(poller.remove(socket)); 176 | } 177 | 178 | const std::string hi_str = "Hi"; 179 | 180 | TEST_CASE("poller poll basic", "[poller]") 181 | { 182 | common_server_client_setup s; 183 | 184 | CHECK_NOTHROW(s.client.send(zmq::message_t{hi_str}, zmq::send_flags::none)); 185 | 186 | zmq::poller_t poller; 187 | std::vector> events{1}; 188 | int i = 0; 189 | CHECK_NOTHROW(poller.add(s.server, zmq::event_flags::pollin, &i)); 190 | CHECK(1 == poller.wait_all(events, std::chrono::milliseconds{-1})); 191 | CHECK(s.server == events[0].socket); 192 | CHECK(&i == events[0].user_data); 193 | } 194 | 195 | TEST_CASE("poller poll basic static array", "[poller]") 196 | { 197 | common_server_client_setup s; 198 | 199 | CHECK_NOTHROW(s.client.send(zmq::message_t{hi_str}, zmq::send_flags::none)); 200 | 201 | zmq::poller_t poller; 202 | std::array, 1> events; 203 | int i = 0; 204 | CHECK_NOTHROW(poller.add(s.server, zmq::event_flags::pollin, &i)); 205 | CHECK(1 == poller.wait_all(events, std::chrono::milliseconds{-1})); 206 | CHECK(s.server == events[0].socket); 207 | CHECK(&i == events[0].user_data); 208 | } 209 | 210 | TEST_CASE("poller add invalid socket throws", "[poller]") 211 | { 212 | zmq::context_t context; 213 | zmq::poller_t<> poller; 214 | zmq::socket_t a{context, zmq::socket_type::router}; 215 | zmq::socket_t b{std::move(a)}; 216 | CHECK_THROWS_AS(poller.add(a, zmq::event_flags::pollin), zmq::error_t); 217 | } 218 | 219 | TEST_CASE("poller remove invalid socket throws", "[poller]") 220 | { 221 | zmq::context_t context; 222 | zmq::socket_t socket{context, zmq::socket_type::router}; 223 | zmq::poller_t<> poller; 224 | CHECK_NOTHROW(poller.add(socket, zmq::event_flags::pollin)); 225 | std::vector sockets; 226 | sockets.emplace_back(std::move(socket)); 227 | CHECK_THROWS_AS(poller.remove(socket), zmq::error_t); 228 | CHECK_NOTHROW(poller.remove(sockets[0])); 229 | } 230 | 231 | TEST_CASE("poller modify empty throws", "[poller]") 232 | { 233 | zmq::context_t context; 234 | zmq::socket_t socket{context, zmq::socket_type::push}; 235 | zmq::poller_t<> poller; 236 | CHECK_THROWS_AS(poller.modify(socket, zmq::event_flags::pollin), 237 | zmq::error_t); 238 | } 239 | 240 | TEST_CASE("poller modify invalid socket throws", "[poller]") 241 | { 242 | zmq::context_t context; 243 | zmq::socket_t a{context, zmq::socket_type::push}; 244 | zmq::socket_t b{std::move(a)}; 245 | zmq::poller_t<> poller; 246 | CHECK_THROWS_AS(poller.modify(a, zmq::event_flags::pollin), zmq::error_t); 247 | } 248 | 249 | TEST_CASE("poller modify not added throws", "[poller]") 250 | { 251 | zmq::context_t context; 252 | zmq::socket_t a{context, zmq::socket_type::push}; 253 | zmq::socket_t b{context, zmq::socket_type::push}; 254 | zmq::poller_t<> poller; 255 | CHECK_NOTHROW(poller.add(a, zmq::event_flags::pollin)); 256 | CHECK_THROWS_AS(poller.modify(b, zmq::event_flags::pollin), zmq::error_t); 257 | } 258 | 259 | TEST_CASE("poller modify simple", "[poller]") 260 | { 261 | zmq::context_t context; 262 | zmq::socket_t a{context, zmq::socket_type::push}; 263 | zmq::poller_t<> poller; 264 | CHECK_NOTHROW(poller.add(a, zmq::event_flags::pollin)); 265 | CHECK_NOTHROW( 266 | poller.modify(a, zmq::event_flags::pollin | zmq::event_flags::pollout)); 267 | } 268 | 269 | TEST_CASE("poller poll client server", "[poller]") 270 | { 271 | // Setup server and client 272 | common_server_client_setup s; 273 | 274 | // Setup poller 275 | zmq::poller_t poller; 276 | CHECK_NOTHROW(poller.add(s.server, zmq::event_flags::pollin, &s.server)); 277 | 278 | // client sends message 279 | CHECK_NOTHROW(s.client.send(zmq::message_t{hi_str}, zmq::send_flags::none)); 280 | 281 | // wait for message and verify events 282 | std::vector> events(1); 283 | CHECK(1 == poller.wait_all(events, std::chrono::milliseconds{500})); 284 | CHECK(zmq::event_flags::pollin == events[0].events); 285 | 286 | // Modify server socket with pollout flag 287 | CHECK_NOTHROW( 288 | poller.modify(s.server, zmq::event_flags::pollin | zmq::event_flags::pollout 289 | )); 290 | CHECK(1 == poller.wait_all(events, std::chrono::milliseconds{500})); 291 | CHECK((zmq::event_flags::pollin | zmq::event_flags::pollout) == events[0].events) 292 | ; 293 | } 294 | 295 | TEST_CASE("poller wait one return", "[poller]") 296 | { 297 | // Setup server and client 298 | common_server_client_setup s; 299 | 300 | // Setup poller 301 | zmq::poller_t<> poller; 302 | CHECK_NOTHROW(poller.add(s.server, zmq::event_flags::pollin)); 303 | 304 | // client sends message 305 | CHECK_NOTHROW(s.client.send(zmq::message_t{hi_str}, zmq::send_flags::none)); 306 | 307 | // wait for message and verify events 308 | std::vector> events(1); 309 | CHECK(1 == poller.wait_all(events, std::chrono::milliseconds{500})); 310 | } 311 | 312 | TEST_CASE("poller wait on move constructed", "[poller]") 313 | { 314 | common_server_client_setup s; 315 | CHECK_NOTHROW(s.client.send(zmq::message_t{hi_str}, zmq::send_flags::none)); 316 | zmq::poller_t<> a; 317 | CHECK_NOTHROW(a.add(s.server, zmq::event_flags::pollin)); 318 | zmq::poller_t<> b{std::move(a)}; 319 | std::vector> events(1); 320 | /// \todo the actual error code should be checked 321 | CHECK_THROWS_AS(a.wait_all(events, std::chrono::milliseconds{10}), 322 | zmq::error_t); 323 | CHECK(1 == b.wait_all(events, std::chrono::milliseconds{-1})); 324 | } 325 | 326 | TEST_CASE("poller wait on move assigned", "[poller]") 327 | { 328 | common_server_client_setup s; 329 | CHECK_NOTHROW(s.client.send(zmq::message_t{hi_str}, zmq::send_flags::none)); 330 | zmq::poller_t<> a; 331 | CHECK_NOTHROW(a.add(s.server, zmq::event_flags::pollin)); 332 | zmq::poller_t<> b; 333 | b = {std::move(a)}; 334 | /// \todo the TEST_CASE error code should be checked 335 | std::vector> events(1); 336 | CHECK_THROWS_AS(a.wait_all(events, std::chrono::milliseconds{10}), 337 | zmq::error_t); 338 | CHECK(1 == b.wait_all(events, std::chrono::milliseconds{-1})); 339 | } 340 | 341 | TEST_CASE("poller remove from handler", "[poller]") 342 | { 343 | constexpr size_t ITER_NO = 10; 344 | 345 | // Setup servers and clients 346 | std::vector setup_list; 347 | for (size_t i = 0; i < ITER_NO; ++i) 348 | setup_list.emplace_back(common_server_client_setup{}); 349 | 350 | // Setup poller 351 | zmq::poller_t<> poller; 352 | for (size_t i = 0; i < ITER_NO; ++i) { 353 | CHECK_NOTHROW(poller.add(setup_list[i].server, zmq::event_flags::pollin)); 354 | } 355 | // Clients send messages 356 | for (auto &s : setup_list) { 357 | CHECK_NOTHROW(s.client.send(zmq::message_t{hi_str}, zmq::send_flags::none)); 358 | } 359 | 360 | // Wait for all servers to receive a message 361 | for (auto &s : setup_list) { 362 | zmq::pollitem_t items[] = {{s.server, 0, ZMQ_POLLIN, 0}}; 363 | zmq::poll(&items[0], 1); 364 | } 365 | 366 | // Fire all handlers in one wait 367 | std::vector> events(ITER_NO); 368 | CHECK(ITER_NO == poller.wait_all(events, std::chrono::milliseconds{-1})); 369 | } 370 | 371 | #endif 372 | -------------------------------------------------------------------------------- /tests/recv_multipart.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #ifdef ZMQ_CPP11 4 | 5 | TEST_CASE("recv_multipart test", "[recv_multipart]") 6 | { 7 | zmq::context_t context(1); 8 | zmq::socket_t output(context, ZMQ_PAIR); 9 | zmq::socket_t input(context, ZMQ_PAIR); 10 | output.bind("inproc://multipart.test"); 11 | input.connect("inproc://multipart.test"); 12 | 13 | SECTION("send 1 message") { 14 | input.send(zmq::str_buffer("hello")); 15 | 16 | std::vector msgs; 17 | auto ret = zmq::recv_multipart(output, std::back_inserter(msgs)); 18 | REQUIRE(ret); 19 | CHECK(*ret == 1); 20 | REQUIRE(msgs.size() == 1); 21 | CHECK(msgs[0].size() == 5); 22 | } 23 | SECTION("send 2 messages") { 24 | input.send(zmq::str_buffer("hello"), zmq::send_flags::sndmore); 25 | input.send(zmq::str_buffer("world!")); 26 | 27 | std::vector msgs; 28 | auto ret = zmq::recv_multipart(output, std::back_inserter(msgs)); 29 | REQUIRE(ret); 30 | CHECK(*ret == 2); 31 | REQUIRE(msgs.size() == 2); 32 | CHECK(msgs[0].size() == 5); 33 | CHECK(msgs[1].size() == 6); 34 | } 35 | SECTION("send no messages, dontwait") { 36 | std::vector msgs; 37 | auto ret = zmq::recv_multipart(output, std::back_inserter(msgs), 38 | zmq::recv_flags::dontwait); 39 | CHECK_FALSE(ret); 40 | REQUIRE(msgs.size() == 0); 41 | } 42 | SECTION("send 1 partial message, dontwait") { 43 | input.send(zmq::str_buffer("hello"), zmq::send_flags::sndmore); 44 | 45 | std::vector msgs; 46 | auto ret = zmq::recv_multipart(output, std::back_inserter(msgs), 47 | zmq::recv_flags::dontwait); 48 | CHECK_FALSE(ret); 49 | REQUIRE(msgs.size() == 0); 50 | } 51 | SECTION("recv with invalid socket") { 52 | std::vector msgs; 53 | CHECK_THROWS_AS( 54 | zmq::recv_multipart(zmq::socket_ref(), std::back_inserter(msgs)), 55 | zmq::error_t); 56 | } 57 | } 58 | 59 | TEST_CASE("recv_multipart_n test", "[recv_multipart]") 60 | { 61 | zmq::context_t context(1); 62 | zmq::socket_t output(context, ZMQ_PAIR); 63 | zmq::socket_t input(context, ZMQ_PAIR); 64 | output.bind("inproc://multipart.test"); 65 | input.connect("inproc://multipart.test"); 66 | 67 | SECTION("send 1 message") { 68 | input.send(zmq::str_buffer("hello")); 69 | 70 | std::array msgs; 71 | auto ret = zmq::recv_multipart_n(output, msgs.data(), msgs.size()); 72 | REQUIRE(ret); 73 | CHECK(*ret == 1); 74 | CHECK(msgs[0].size() == 5); 75 | } 76 | SECTION("send 1 message 2") { 77 | input.send(zmq::str_buffer("hello")); 78 | 79 | std::array msgs; 80 | auto ret = zmq::recv_multipart_n(output, msgs.data(), msgs.size()); 81 | REQUIRE(ret); 82 | CHECK(*ret == 1); 83 | CHECK(msgs[0].size() == 5); 84 | CHECK(msgs[1].size() == 0); 85 | } 86 | SECTION("send 2 messages, recv 1") { 87 | input.send(zmq::str_buffer("hello"), zmq::send_flags::sndmore); 88 | input.send(zmq::str_buffer("world!")); 89 | 90 | std::array msgs; 91 | CHECK_THROWS_AS( 92 | zmq::recv_multipart_n(output, msgs.data(), msgs.size()), 93 | std::runtime_error); 94 | } 95 | SECTION("recv 0") { 96 | input.send(zmq::str_buffer("hello"), zmq::send_flags::sndmore); 97 | input.send(zmq::str_buffer("world!")); 98 | 99 | std::array msgs; 100 | CHECK_THROWS_AS( 101 | zmq::recv_multipart_n(output, msgs.data(), 0), 102 | std::runtime_error); 103 | } 104 | SECTION("send 2 messages") { 105 | input.send(zmq::str_buffer("hello"), zmq::send_flags::sndmore); 106 | input.send(zmq::str_buffer("world!")); 107 | 108 | std::array msgs; 109 | auto ret = zmq::recv_multipart_n(output, msgs.data(), msgs.size()); 110 | REQUIRE(ret); 111 | CHECK(*ret == 2); 112 | CHECK(msgs[0].size() == 5); 113 | CHECK(msgs[1].size() == 6); 114 | } 115 | SECTION("send no messages, dontwait") { 116 | std::array msgs; 117 | auto ret = zmq::recv_multipart_n(output, msgs.data(), msgs.size(), 118 | zmq::recv_flags::dontwait); 119 | CHECK_FALSE(ret); 120 | REQUIRE(msgs[0].size() == 0); 121 | } 122 | SECTION("send 1 partial message, dontwait") { 123 | input.send(zmq::str_buffer("hello"), zmq::send_flags::sndmore); 124 | 125 | std::array msgs; 126 | auto ret = zmq::recv_multipart_n(output, msgs.data(), msgs.size(), 127 | zmq::recv_flags::dontwait); 128 | CHECK_FALSE(ret); 129 | REQUIRE(msgs[0].size() == 0); 130 | } 131 | SECTION("recv with invalid socket") { 132 | std::array msgs; 133 | CHECK_THROWS_AS( 134 | zmq::recv_multipart_n(zmq::socket_ref(), msgs.data(), msgs.size()), 135 | zmq::error_t); 136 | } 137 | } 138 | 139 | #endif 140 | -------------------------------------------------------------------------------- /tests/send_multipart.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #ifdef ZMQ_CPP11 4 | 5 | #include 6 | 7 | TEST_CASE("send_multipart test", "[send_multipart]") 8 | { 9 | zmq::context_t context(1); 10 | zmq::socket_t output(context, ZMQ_PAIR); 11 | zmq::socket_t input(context, ZMQ_PAIR); 12 | output.bind("inproc://multipart.test"); 13 | input.connect("inproc://multipart.test"); 14 | 15 | SECTION("send 0 messages") { 16 | std::vector imsgs; 17 | auto iret = zmq::send_multipart(input, imsgs); 18 | REQUIRE(iret); 19 | CHECK(*iret == 0); 20 | } 21 | SECTION("send 1 message") { 22 | std::array imsgs = {zmq::message_t(3)}; 23 | auto iret = zmq::send_multipart(input, imsgs); 24 | REQUIRE(iret); 25 | CHECK(*iret == 1); 26 | 27 | std::vector omsgs; 28 | auto oret = zmq::recv_multipart(output, std::back_inserter(omsgs)); 29 | REQUIRE(oret); 30 | CHECK(*oret == 1); 31 | REQUIRE(omsgs.size() == 1); 32 | CHECK(omsgs[0].size() == 3); 33 | } 34 | SECTION("send 2 messages") { 35 | std::array imsgs = {zmq::message_t(3), zmq::message_t(4)}; 36 | auto iret = zmq::send_multipart(input, imsgs); 37 | REQUIRE(iret); 38 | CHECK(*iret == 2); 39 | 40 | std::vector omsgs; 41 | auto oret = zmq::recv_multipart(output, std::back_inserter(omsgs)); 42 | REQUIRE(oret); 43 | CHECK(*oret == 2); 44 | REQUIRE(omsgs.size() == 2); 45 | CHECK(omsgs[0].size() == 3); 46 | CHECK(omsgs[1].size() == 4); 47 | } 48 | SECTION("send 2 messages, const_buffer") { 49 | std::array imsgs = {zmq::str_buffer("foo"), 50 | zmq::str_buffer("bar!")}; 51 | auto iret = zmq::send_multipart(input, imsgs); 52 | REQUIRE(iret); 53 | CHECK(*iret == 2); 54 | 55 | std::vector omsgs; 56 | auto oret = zmq::recv_multipart(output, std::back_inserter(omsgs)); 57 | REQUIRE(oret); 58 | CHECK(*oret == 2); 59 | REQUIRE(omsgs.size() == 2); 60 | CHECK(omsgs[0].size() == 3); 61 | CHECK(omsgs[1].size() == 4); 62 | } 63 | SECTION("send 2 messages, mutable_buffer") { 64 | char buf[4] = {}; 65 | std::array imsgs = { 66 | zmq::buffer(buf, 3), zmq::buffer(buf)}; 67 | auto iret = zmq::send_multipart(input, imsgs); 68 | REQUIRE(iret); 69 | CHECK(*iret == 2); 70 | 71 | std::vector omsgs; 72 | auto oret = zmq::recv_multipart(output, std::back_inserter(omsgs)); 73 | REQUIRE(oret); 74 | CHECK(*oret == 2); 75 | REQUIRE(omsgs.size() == 2); 76 | CHECK(omsgs[0].size() == 3); 77 | CHECK(omsgs[1].size() == 4); 78 | } 79 | SECTION("send 2 messages, dontwait") { 80 | zmq::socket_t push(context, ZMQ_PUSH); 81 | push.bind("inproc://multipart.test.push"); 82 | 83 | std::array imsgs = {zmq::message_t(3), zmq::message_t(4)}; 84 | auto iret = zmq::send_multipart(push, imsgs, zmq::send_flags::dontwait); 85 | REQUIRE_FALSE(iret); 86 | } 87 | SECTION("send, misc. containers") { 88 | std::vector msgs_vec; 89 | msgs_vec.emplace_back(3); 90 | msgs_vec.emplace_back(4); 91 | auto iret = zmq::send_multipart(input, msgs_vec); 92 | REQUIRE(iret); 93 | CHECK(*iret == 2); 94 | 95 | std::forward_list msgs_list; 96 | msgs_list.emplace_front(4); 97 | msgs_list.emplace_front(3); 98 | iret = zmq::send_multipart(input, msgs_list); 99 | REQUIRE(iret); 100 | CHECK(*iret == 2); 101 | 102 | // init. list 103 | const auto msgs_il = {zmq::str_buffer("foo"), zmq::str_buffer("bar!")}; 104 | iret = zmq::send_multipart(input, msgs_il); 105 | REQUIRE(iret); 106 | CHECK(*iret == 2); 107 | // rvalue 108 | iret = zmq::send_multipart(input, 109 | std::initializer_list{ 110 | zmq::str_buffer("foo"), 111 | zmq::str_buffer("bar!")}); 112 | REQUIRE(iret); 113 | CHECK(*iret == 2); 114 | } 115 | SECTION("send with invalid socket") { 116 | std::vector msgs(1); 117 | CHECK_THROWS_AS(zmq::send_multipart(zmq::socket_ref(), msgs), 118 | zmq::error_t); 119 | } 120 | } 121 | #endif 122 | -------------------------------------------------------------------------------- /tests/socket.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #ifdef ZMQ_CPP11 4 | #include 5 | #endif 6 | 7 | #if (__cplusplus >= 201703L) 8 | static_assert(std::is_nothrow_swappable::value, 9 | "socket_t should be nothrow swappable"); 10 | #endif 11 | 12 | TEST_CASE("socket default ctor", "[socket]") 13 | { 14 | zmq::socket_t socket; 15 | } 16 | 17 | TEST_CASE("socket create destroy", "[socket]") 18 | { 19 | zmq::context_t context; 20 | zmq::socket_t socket(context, ZMQ_ROUTER); 21 | } 22 | 23 | #ifdef ZMQ_CPP11 24 | TEST_CASE("socket create assign", "[socket]") 25 | { 26 | zmq::context_t context; 27 | zmq::socket_t socket(context, ZMQ_ROUTER); 28 | CHECK(static_cast(socket)); 29 | CHECK(socket.handle() != nullptr); 30 | socket = {}; 31 | CHECK(!static_cast(socket)); 32 | CHECK(socket.handle() == nullptr); 33 | } 34 | 35 | TEST_CASE("socket create by enum and destroy", "[socket]") 36 | { 37 | zmq::context_t context; 38 | zmq::socket_t socket(context, zmq::socket_type::router); 39 | } 40 | 41 | TEST_CASE("socket swap", "[socket]") 42 | { 43 | zmq::context_t context; 44 | zmq::socket_t socket1(context, zmq::socket_type::router); 45 | zmq::socket_t socket2(context, zmq::socket_type::dealer); 46 | using std::swap; 47 | swap(socket1, socket2); 48 | } 49 | 50 | #ifdef ZMQ_CPP11 51 | TEST_CASE("socket options", "[socket]") 52 | { 53 | zmq::context_t context; 54 | zmq::socket_t socket(context, zmq::socket_type::router); 55 | 56 | #ifdef ZMQ_IMMEDIATE 57 | socket.set(zmq::sockopt::immediate, 0); 58 | socket.set(zmq::sockopt::immediate, false); 59 | CHECK(socket.get(zmq::sockopt::immediate) == false); 60 | // unit out of range 61 | CHECK_THROWS_AS(socket.set(zmq::sockopt::immediate, 80), zmq::error_t); 62 | #endif 63 | #ifdef ZMQ_LINGER 64 | socket.set(zmq::sockopt::linger, 55); 65 | CHECK(socket.get(zmq::sockopt::linger) == 55); 66 | #endif 67 | #ifdef ZMQ_ROUTING_ID 68 | const std::string id = "foobar"; 69 | socket.set(zmq::sockopt::routing_id, "foobar"); 70 | socket.set(zmq::sockopt::routing_id, zmq::buffer(id)); 71 | socket.set(zmq::sockopt::routing_id, id); 72 | #if CPPZMQ_HAS_STRING_VIEW 73 | socket.set(zmq::sockopt::routing_id, std::string_view{id}); 74 | #endif 75 | 76 | std::string id_ret(10, ' '); 77 | auto size = socket.get(zmq::sockopt::routing_id, zmq::buffer(id_ret)); 78 | id_ret.resize(size); 79 | CHECK(id == id_ret); 80 | auto stropt = socket.get(zmq::sockopt::routing_id); 81 | CHECK(id == stropt); 82 | 83 | std::string id_ret_small(3, ' '); 84 | // truncated 85 | CHECK_THROWS_AS(socket.get(zmq::sockopt::routing_id, zmq::buffer(id_ret_small)), 86 | zmq::error_t); 87 | #endif 88 | } 89 | 90 | template 91 | void check_array_opt(T opt, 92 | zmq::socket_t &sock, 93 | std::string info, 94 | bool set_only = false) 95 | { 96 | const std::string val = "foobar"; 97 | INFO("setting " + info); 98 | sock.set(opt, val); 99 | if (set_only) 100 | return; 101 | 102 | INFO("getting " + info); 103 | auto s = sock.get(opt); 104 | CHECK(s == val); 105 | } 106 | 107 | template 108 | void check_array_opt_get(T opt, zmq::socket_t &sock, std::string info) 109 | { 110 | INFO("getting " + info); 111 | (void) sock.get(opt); 112 | } 113 | 114 | #if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 0, 0) 115 | template void check_bin_z85(T opt, zmq::socket_t &sock, std::string str_val) 116 | { 117 | std::vector bin_val(32); 118 | const auto dret = zmq_z85_decode(bin_val.data(), str_val.c_str()); 119 | CHECK(dret != nullptr); 120 | 121 | sock.set(opt, str_val); 122 | sock.set(opt, zmq::buffer(bin_val)); 123 | auto sv = sock.get(opt); 124 | CHECK(sv == str_val); 125 | 126 | auto bv = sock.get(opt, 32); 127 | REQUIRE(bv.size() == bin_val.size()); 128 | CHECK(std::memcmp(bv.data(), bin_val.data(), bin_val.size()) == 0); 129 | } 130 | #endif 131 | 132 | TEST_CASE("socket check array options", "[socket]") 133 | { 134 | zmq::context_t context; 135 | zmq::socket_t router(context, zmq::socket_type::router); 136 | zmq::socket_t xpub(context, zmq::socket_type::xpub); 137 | zmq::socket_t sub(context, zmq::socket_type::sub); 138 | 139 | #ifdef ZMQ_BINDTODEVICE 140 | // requires setting CAP_NET_RAW 141 | //check_array_opt(zmq::sockopt::bindtodevice, router, "bindtodevice"); 142 | #endif 143 | #ifdef ZMQ_CONNECT_ROUTING_ID 144 | check_array_opt(zmq::sockopt::connect_routing_id, router, "connect_routing_id", 145 | true); 146 | #endif 147 | #ifdef ZMQ_LAST_ENDPOINT 148 | check_array_opt_get(zmq::sockopt::last_endpoint, router, "last_endpoint"); 149 | #endif 150 | #ifdef ZMQ_METADATA 151 | router.set(zmq::sockopt::metadata, zmq::str_buffer("X-foo:bar")); 152 | #endif 153 | #ifdef ZMQ_PLAIN_PASSWORD 154 | check_array_opt(zmq::sockopt::plain_password, router, "plain_password"); 155 | #endif 156 | #ifdef ZMQ_PLAIN_USERNAME 157 | check_array_opt(zmq::sockopt::plain_username, router, "plain_username"); 158 | #endif 159 | #ifdef ZMQ_ROUTING_ID 160 | check_array_opt(zmq::sockopt::routing_id, router, "routing_id"); 161 | #endif 162 | #ifdef ZMQ_SOCKS_PROXY 163 | check_array_opt(zmq::sockopt::socks_proxy, router, "socks_proxy"); 164 | #endif 165 | #ifdef ZMQ_SUBSCRIBE 166 | check_array_opt(zmq::sockopt::subscribe, sub, "subscribe", true); 167 | #endif 168 | #ifdef ZMQ_UNSUBSCRIBE 169 | check_array_opt(zmq::sockopt::unsubscribe, sub, "unsubscribe", true); 170 | #endif 171 | #ifdef ZMQ_XPUB_WELCOME_MSG 172 | check_array_opt(zmq::sockopt::xpub_welcome_msg, xpub, "xpub_welcome_msg", true); 173 | #endif 174 | #ifdef ZMQ_ZAP_DOMAIN 175 | check_array_opt(zmq::sockopt::zap_domain, router, "zap_domain"); 176 | #endif 177 | 178 | // curve 179 | #if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 0, 0) && defined(ZMQ_HAS_CAPABILITIES) 180 | if (zmq_has("curve") == 1) { 181 | const std::string spk = "rq:rM>}U?@Lns47E1%kR.o@n%FcmmsL/@{H8]yf7"; 182 | const std::string ssk = "JTKVSB%%)wK0E.X)V>+}o?pNmC{O&4W4b!Ni{Lh6"; 183 | const std::string cpk = "Yne@$w-vo= ZMQ_MAKE_VERSION(4, 1, 0) && defined(ZMQ_HAS_CAPABILITIES) 202 | if (zmq_has("gssapi") == 1 && false) // TODO enable 203 | { 204 | zmq::socket_t gss_server(context, zmq::socket_type::router); 205 | gss_server.set(zmq::sockopt::gssapi_server, true); 206 | CHECK(gss_server.get(zmq::sockopt::gssapi_server) == 1); 207 | gss_server.set(zmq::sockopt::gssapi_plaintext, false); 208 | CHECK(gss_server.get(zmq::sockopt::gssapi_plaintext) == 0); 209 | check_array_opt(zmq::sockopt::gssapi_principal, gss_server, 210 | "gssapi_principal"); 211 | 212 | zmq::socket_t gss_client(context, zmq::socket_type::router); 213 | CHECK(gss_client.get(zmq::sockopt::gssapi_server) == 0); 214 | check_array_opt(zmq::sockopt::gssapi_principal, gss_client, 215 | "gssapi_principal"); 216 | check_array_opt(zmq::sockopt::gssapi_service_principal, gss_client, 217 | "gssapi_service_principal"); 218 | } 219 | #endif 220 | } 221 | 222 | template 223 | void check_integral_opt(Opt opt, 224 | zmq::socket_t &sock, 225 | std::string info, 226 | bool set_only = false) 227 | { 228 | const T val = 1; 229 | INFO("setting " + info); 230 | sock.set(opt, val); 231 | if (set_only) 232 | return; 233 | 234 | INFO("getting " + info); 235 | auto s = sock.get(opt); 236 | CHECK(s == val); 237 | } 238 | 239 | template 240 | void check_integral_opt_get(Opt opt, zmq::socket_t &sock, std::string info) 241 | { 242 | INFO("getting " + info); 243 | (void) sock.get(opt); 244 | } 245 | 246 | TEST_CASE("socket check integral options", "[socket]") 247 | { 248 | zmq::context_t context; 249 | zmq::socket_t router(context, zmq::socket_type::router); 250 | zmq::socket_t xpub(context, zmq::socket_type::xpub); 251 | zmq::socket_t req(context, zmq::socket_type::req); 252 | #ifdef ZMQ_STREAM_NOTIFY 253 | zmq::socket_t stream(context, zmq::socket_type::stream); 254 | #endif 255 | 256 | #ifdef ZMQ_AFFINITY 257 | check_integral_opt(zmq::sockopt::affinity, router, "affinity"); 258 | #endif 259 | #ifdef ZMQ_BACKLOG 260 | check_integral_opt(zmq::sockopt::backlog, router, "backlog"); 261 | #endif 262 | #ifdef ZMQ_CONFLATE 263 | check_integral_opt(zmq::sockopt::conflate, router, "conflate"); 264 | #endif 265 | #ifdef ZMQ_CONNECT_TIMEOUT 266 | check_integral_opt(zmq::sockopt::connect_timeout, router, 267 | "connect_timeout"); 268 | #endif 269 | #ifdef ZMQ_EVENTS 270 | check_integral_opt_get(zmq::sockopt::events, router, "events"); 271 | #endif 272 | #ifdef ZMQ_FD 273 | check_integral_opt_get(zmq::sockopt::fd, router, "fd"); 274 | #endif 275 | #ifdef ZMQ_HANDSHAKE_IVL 276 | check_integral_opt(zmq::sockopt::handshake_ivl, router, "handshake_ivl"); 277 | #endif 278 | #ifdef ZMQ_HEARTBEAT_IVL 279 | check_integral_opt(zmq::sockopt::heartbeat_ivl, router, "heartbeat_ivl"); 280 | #endif 281 | #ifdef ZMQ_HEARTBEAT_TIMEOUT 282 | check_integral_opt(zmq::sockopt::heartbeat_timeout, router, 283 | "heartbeat_timeout"); 284 | #endif 285 | #ifdef ZMQ_HEARTBEAT_TTL 286 | router.set(zmq::sockopt::heartbeat_ttl, 100); 287 | CHECK(router.get(zmq::sockopt::heartbeat_ttl) == 100); 288 | #endif 289 | #ifdef ZMQ_IMMEDIATE 290 | check_integral_opt(zmq::sockopt::immediate, router, "immediate"); 291 | #endif 292 | #ifdef ZMQ_INVERT_MATCHING 293 | check_integral_opt(zmq::sockopt::invert_matching, router, 294 | "invert_matching"); 295 | #endif 296 | #ifdef ZMQ_IPV6 297 | check_integral_opt(zmq::sockopt::ipv6, router, "ipv6"); 298 | #endif 299 | #ifdef ZMQ_LINGER 300 | check_integral_opt(zmq::sockopt::linger, router, "linger"); 301 | #endif 302 | #ifdef ZMQ_MAXMSGSIZE 303 | check_integral_opt(zmq::sockopt::maxmsgsize, router, "maxmsgsize"); 304 | #endif 305 | #ifdef ZMQ_MECHANISM 306 | check_integral_opt_get(zmq::sockopt::mechanism, router, "mechanism"); 307 | #endif 308 | #ifdef ZMQ_MULTICAST_HOPS 309 | check_integral_opt(zmq::sockopt::multicast_hops, router, "multicast_hops"); 310 | #endif 311 | #ifdef ZMQ_MULTICAST_LOOP 312 | check_integral_opt(zmq::sockopt::multicast_loop, router, "multicast_loop"); 313 | #endif 314 | #ifdef ZMQ_MULTICAST_MAXTPDU 315 | check_integral_opt(zmq::sockopt::multicast_maxtpdu, router, 316 | "multicast_maxtpdu"); 317 | #endif 318 | #ifdef ZMQ_PLAIN_SERVER 319 | check_integral_opt(zmq::sockopt::plain_server, router, "plain_server"); 320 | #endif 321 | #ifdef ZMQ_USE_FD 322 | check_integral_opt(zmq::sockopt::use_fd, router, "use_fd"); 323 | #endif 324 | #ifdef ZMQ_PROBE_ROUTER 325 | check_integral_opt(zmq::sockopt::probe_router, router, "probe_router", 326 | true); 327 | #endif 328 | #ifdef ZMQ_RATE 329 | check_integral_opt(zmq::sockopt::rate, router, "rate"); 330 | #endif 331 | #ifdef ZMQ_RCVBUF 332 | check_integral_opt(zmq::sockopt::rcvbuf, router, "rcvbuf"); 333 | #endif 334 | #ifdef ZMQ_RCVHWM 335 | check_integral_opt(zmq::sockopt::rcvhwm, router, "rcvhwm"); 336 | #endif 337 | #ifdef ZMQ_RCVMORE 338 | check_integral_opt_get(zmq::sockopt::rcvmore, router, "rcvmore"); 339 | #endif 340 | #ifdef ZMQ_RCVTIMEO 341 | check_integral_opt(zmq::sockopt::rcvtimeo, router, "rcvtimeo"); 342 | #endif 343 | #ifdef ZMQ_RECONNECT_IVL 344 | check_integral_opt(zmq::sockopt::reconnect_ivl, router, "reconnect_ivl"); 345 | #endif 346 | #ifdef ZMQ_RECONNECT_IVL_MAX 347 | check_integral_opt(zmq::sockopt::reconnect_ivl_max, router, 348 | "reconnect_ivl_max"); 349 | #endif 350 | #ifdef ZMQ_RECOVERY_IVL 351 | check_integral_opt(zmq::sockopt::recovery_ivl, router, "recovery_ivl"); 352 | #endif 353 | #ifdef ZMQ_REQ_CORRELATE 354 | check_integral_opt(zmq::sockopt::req_correlate, req, "req_correlate", true); 355 | #endif 356 | #ifdef ZMQ_REQ_RELAXED 357 | check_integral_opt(zmq::sockopt::req_relaxed, req, "req_relaxed", true); 358 | #endif 359 | #ifdef ZMQ_ROUTER_HANDOVER 360 | check_integral_opt(zmq::sockopt::router_handover, router, "router_handover", 361 | true); 362 | #endif 363 | #ifdef ZMQ_ROUTER_MANDATORY 364 | check_integral_opt(zmq::sockopt::router_mandatory, router, 365 | "router_mandatory", true); 366 | #endif 367 | #ifdef ZMQ_ROUTER_RAW 368 | check_integral_opt(zmq::sockopt::router_raw, router, "router_raw", 369 | true); 370 | #endif 371 | #ifdef ZMQ_ROUTER_NOTIFY 372 | check_integral_opt(zmq::sockopt::router_notify, router, "router_notify"); 373 | #endif 374 | #ifdef ZMQ_SNDBUF 375 | check_integral_opt(zmq::sockopt::sndbuf, router, "sndbuf"); 376 | #endif 377 | #ifdef ZMQ_SNDHWM 378 | check_integral_opt(zmq::sockopt::sndhwm, router, "sndhwm"); 379 | #endif 380 | #ifdef ZMQ_SNDTIMEO 381 | check_integral_opt(zmq::sockopt::sndtimeo, router, "sndtimeo"); 382 | #endif 383 | #ifdef ZMQ_STREAM_NOTIFY 384 | check_integral_opt(zmq::sockopt::stream_notify, stream, "stream_notify", 385 | true); 386 | #endif 387 | #ifdef ZMQ_TCP_KEEPALIVE 388 | check_integral_opt(zmq::sockopt::tcp_keepalive, router, "tcp_keepalive"); 389 | #endif 390 | #ifdef ZMQ_TCP_KEEPALIVE_CNT 391 | check_integral_opt(zmq::sockopt::tcp_keepalive_cnt, router, 392 | "tcp_keepalive_cnt"); 393 | #endif 394 | #ifdef ZMQ_TCP_KEEPALIVE_IDLE 395 | check_integral_opt(zmq::sockopt::tcp_keepalive_idle, router, 396 | "tcp_keepalive_idle"); 397 | #endif 398 | #ifdef ZMQ_TCP_KEEPALIVE_INTVL 399 | check_integral_opt(zmq::sockopt::tcp_keepalive_intvl, router, 400 | "tcp_keepalive_intvl"); 401 | #endif 402 | #ifdef ZMQ_TCP_MAXRT 403 | check_integral_opt(zmq::sockopt::tcp_maxrt, router, "tcp_maxrt"); 404 | #endif 405 | #ifdef ZMQ_THREAD_SAFE 406 | check_integral_opt_get(zmq::sockopt::thread_safe, router, "thread_safe"); 407 | #endif 408 | #ifdef ZMQ_TOS 409 | check_integral_opt(zmq::sockopt::tos, router, "tos"); 410 | #endif 411 | #ifdef ZMQ_TYPE 412 | check_integral_opt_get(zmq::sockopt::type, router, "type"); 413 | #ifdef ZMQ_CPP11 414 | check_integral_opt_get(zmq::sockopt::socket_type, router, "socket_type"); 415 | #endif // ZMQ_CPP11 416 | #endif // ZMQ_TYPE 417 | 418 | #ifdef ZMQ_HAVE_VMCI 419 | #ifdef ZMQ_VMCI_BUFFER_SIZE 420 | check_integral_opt(zmq::sockopt::vmci_buffer_size, router, 421 | "vmci_buffer_size"); 422 | #endif 423 | #ifdef ZMQ_VMCI_BUFFER_MIN_SIZE 424 | check_integral_opt(zmq::sockopt::vmci_buffer_min_size, router, 425 | "vmci_buffer_min_size"); 426 | #endif 427 | #ifdef ZMQ_VMCI_BUFFER_MAX_SIZE 428 | check_integral_opt(zmq::sockopt::vmci_buffer_max_size, router, 429 | "vmci_buffer_max_size"); 430 | #endif 431 | #ifdef ZMQ_VMCI_CONNECT_TIMEOUT 432 | check_integral_opt(zmq::sockopt::vmci_connect_timeout, router, 433 | "vmci_connect_timeout"); 434 | #endif 435 | #endif 436 | 437 | #ifdef ZMQ_XPUB_VERBOSE 438 | check_integral_opt(zmq::sockopt::xpub_verbose, xpub, "xpub_verbose", true); 439 | #endif 440 | #ifdef ZMQ_XPUB_VERBOSER 441 | check_integral_opt(zmq::sockopt::xpub_verboser, xpub, "xpub_verboser", 442 | true); 443 | #endif 444 | #ifdef ZMQ_XPUB_MANUAL 445 | check_integral_opt(zmq::sockopt::xpub_manual, xpub, "xpub_manual", true); 446 | #endif 447 | #ifdef ZMQ_XPUB_NODROP 448 | check_integral_opt(zmq::sockopt::xpub_nodrop, xpub, "xpub_nodrop", true); 449 | #endif 450 | #ifdef ZMQ_ZAP_ENFORCE_DOMAIN 451 | check_integral_opt(zmq::sockopt::zap_enforce_domain, router, 452 | "zap_enforce_domain"); 453 | #endif 454 | } 455 | 456 | #endif 457 | 458 | TEST_CASE("socket flags", "[socket]") 459 | { 460 | CHECK((zmq::recv_flags::dontwait | zmq::recv_flags::none) 461 | == static_cast(ZMQ_DONTWAIT | 0)); 462 | CHECK((zmq::recv_flags::dontwait & zmq::recv_flags::none) 463 | == static_cast(ZMQ_DONTWAIT & 0)); 464 | CHECK((zmq::recv_flags::dontwait ^ zmq::recv_flags::none) 465 | == static_cast(ZMQ_DONTWAIT ^ 0)); 466 | CHECK(~zmq::recv_flags::dontwait == static_cast(~ZMQ_DONTWAIT)); 467 | 468 | CHECK((zmq::send_flags::dontwait | zmq::send_flags::sndmore) 469 | == static_cast(ZMQ_DONTWAIT | ZMQ_SNDMORE)); 470 | CHECK((zmq::send_flags::dontwait & zmq::send_flags::sndmore) 471 | == static_cast(ZMQ_DONTWAIT & ZMQ_SNDMORE)); 472 | CHECK((zmq::send_flags::dontwait ^ zmq::send_flags::sndmore) 473 | == static_cast(ZMQ_DONTWAIT ^ ZMQ_SNDMORE)); 474 | CHECK(~zmq::send_flags::dontwait == static_cast(~ZMQ_DONTWAIT)); 475 | } 476 | 477 | TEST_CASE("socket readme example", "[socket]") 478 | { 479 | zmq::context_t ctx; 480 | zmq::socket_t sock(ctx, zmq::socket_type::push); 481 | sock.bind("inproc://test"); 482 | sock.send(zmq::str_buffer("Hello, world"), zmq::send_flags::dontwait); 483 | } 484 | #endif 485 | 486 | TEST_CASE("socket sends and receives const buffer", "[socket]") 487 | { 488 | zmq::context_t context; 489 | zmq::socket_t sender(context, ZMQ_PAIR); 490 | zmq::socket_t receiver(context, ZMQ_PAIR); 491 | receiver.bind("inproc://test"); 492 | sender.connect("inproc://test"); 493 | const char *str = "Hi"; 494 | 495 | #ifdef ZMQ_CPP11 496 | CHECK(2 == *sender.send(zmq::buffer(str, 2))); 497 | char buf[2]; 498 | const auto res = receiver.recv(zmq::buffer(buf)); 499 | CHECK(res); 500 | CHECK(!res->truncated()); 501 | CHECK(2 == res->size); 502 | #else 503 | CHECK(2 == sender.send(str, 2)); 504 | char buf[2]; 505 | CHECK(2 == receiver.recv(buf, 2)); 506 | #endif 507 | CHECK(0 == memcmp(buf, str, 2)); 508 | } 509 | 510 | #ifdef ZMQ_CPP11 511 | 512 | TEST_CASE("socket send none sndmore", "[socket]") 513 | { 514 | zmq::context_t context; 515 | zmq::socket_t s(context, zmq::socket_type::router); 516 | s.bind("inproc://test"); 517 | 518 | std::vector buf(4); 519 | auto res = s.send(zmq::buffer(buf), zmq::send_flags::sndmore); 520 | CHECK(res); 521 | CHECK(*res == buf.size()); 522 | res = s.send(zmq::buffer(buf)); 523 | CHECK(res); 524 | CHECK(*res == buf.size()); 525 | } 526 | 527 | TEST_CASE("socket send dontwait", "[socket]") 528 | { 529 | zmq::context_t context; 530 | zmq::socket_t s(context, zmq::socket_type::push); 531 | s.bind("inproc://test"); 532 | 533 | std::vector buf(4); 534 | auto res = s.send(zmq::buffer(buf), zmq::send_flags::dontwait); 535 | CHECK(!res); 536 | res = 537 | s.send(zmq::buffer(buf), zmq::send_flags::dontwait | zmq::send_flags::sndmore); 538 | CHECK(!res); 539 | 540 | zmq::message_t msg; 541 | auto resm = s.send(msg, zmq::send_flags::dontwait); 542 | CHECK(!resm); 543 | CHECK(msg.size() == 0); 544 | } 545 | 546 | TEST_CASE("socket send exception", "[socket]") 547 | { 548 | zmq::context_t context; 549 | zmq::socket_t s(context, zmq::socket_type::pull); 550 | s.bind("inproc://test"); 551 | 552 | std::vector buf(4); 553 | CHECK_THROWS_AS(s.send(zmq::buffer(buf)), zmq::error_t); 554 | } 555 | 556 | TEST_CASE("socket recv none", "[socket]") 557 | { 558 | zmq::context_t context; 559 | zmq::socket_t s(context, zmq::socket_type::pair); 560 | zmq::socket_t s2(context, zmq::socket_type::pair); 561 | s2.bind("inproc://test"); 562 | s.connect("inproc://test"); 563 | 564 | std::vector sbuf(4); 565 | const auto res_send = s2.send(zmq::buffer(sbuf)); 566 | CHECK(res_send); 567 | CHECK(res_send.has_value()); 568 | 569 | std::vector buf(2); 570 | const auto res = s.recv(zmq::buffer(buf)); 571 | CHECK(res.has_value()); 572 | CHECK(res->truncated()); 573 | CHECK(res->untruncated_size == sbuf.size()); 574 | CHECK(res->size == buf.size()); 575 | 576 | const auto res_send2 = s2.send(zmq::buffer(sbuf)); 577 | CHECK(res_send2.has_value()); 578 | std::vector buf2(10); 579 | const auto res2 = s.recv(zmq::buffer(buf2)); 580 | CHECK(res2.has_value()); 581 | CHECK(!res2->truncated()); 582 | CHECK(res2->untruncated_size == sbuf.size()); 583 | CHECK(res2->size == sbuf.size()); 584 | } 585 | 586 | TEST_CASE("socket send recv message_t", "[socket]") 587 | { 588 | zmq::context_t context; 589 | zmq::socket_t s(context, zmq::socket_type::pair); 590 | zmq::socket_t s2(context, zmq::socket_type::pair); 591 | s2.bind("inproc://test"); 592 | s.connect("inproc://test"); 593 | 594 | zmq::message_t smsg(10); 595 | const auto res_send = s2.send(smsg, zmq::send_flags::none); 596 | CHECK(res_send); 597 | CHECK(*res_send == 10); 598 | CHECK(smsg.size() == 0); 599 | 600 | zmq::message_t rmsg; 601 | const auto res = s.recv(rmsg); 602 | CHECK(res); 603 | CHECK(*res == 10); 604 | CHECK(res.value() == 10); 605 | CHECK(rmsg.size() == *res); 606 | } 607 | 608 | TEST_CASE("socket send recv message_t by pointer", "[socket]") 609 | { 610 | zmq::context_t context; 611 | zmq::socket_t s(context, zmq::socket_type::pair); 612 | zmq::socket_t s2(context, zmq::socket_type::pair); 613 | s2.bind("inproc://test"); 614 | s.connect("inproc://test"); 615 | 616 | zmq::message_t smsg(size_t{10}); 617 | const auto res_send = s2.send(smsg, zmq::send_flags::none); 618 | CHECK(res_send); 619 | CHECK(*res_send == 10); 620 | CHECK(smsg.size() == 0); 621 | 622 | zmq::message_t rmsg; 623 | const bool res = s.recv(&rmsg); 624 | CHECK(res); 625 | } 626 | 627 | TEST_CASE("socket recv dontwait", "[socket]") 628 | { 629 | zmq::context_t context; 630 | zmq::socket_t s(context, zmq::socket_type::pull); 631 | s.bind("inproc://test"); 632 | 633 | std::vector buf(4); 634 | constexpr auto flags = zmq::recv_flags::none | zmq::recv_flags::dontwait; 635 | auto res = s.recv(zmq::buffer(buf), flags); 636 | CHECK(!res); 637 | 638 | zmq::message_t msg; 639 | auto resm = s.recv(msg, flags); 640 | CHECK(!resm); 641 | CHECK_THROWS_AS(resm.value(), std::exception); 642 | CHECK(msg.size() == 0); 643 | } 644 | 645 | TEST_CASE("socket recv exception", "[socket]") 646 | { 647 | zmq::context_t context; 648 | zmq::socket_t s(context, zmq::socket_type::push); 649 | s.bind("inproc://test"); 650 | 651 | std::vector buf(4); 652 | CHECK_THROWS_AS(s.recv(zmq::buffer(buf)), zmq::error_t); 653 | } 654 | 655 | TEST_CASE("socket proxy", "[socket]") 656 | { 657 | zmq::context_t context; 658 | zmq::socket_t front(context, ZMQ_ROUTER); 659 | zmq::socket_t back(context, ZMQ_ROUTER); 660 | zmq::socket_t capture(context, ZMQ_DEALER); 661 | front.bind("inproc://test1"); 662 | back.bind("inproc://test2"); 663 | capture.bind("inproc://test3"); 664 | auto f = std::async(std::launch::async, [&]() { 665 | auto s1 = std::move(front); 666 | auto s2 = std::move(back); 667 | auto s3 = std::move(capture); 668 | try { 669 | zmq::proxy(s1, s2, zmq::socket_ref(s3)); 670 | } 671 | catch (const zmq::error_t &e) { 672 | return e.num() == ETERM; 673 | } 674 | return false; 675 | }); 676 | context.close(); 677 | CHECK(f.get()); 678 | } 679 | 680 | TEST_CASE("socket proxy steerable", "[socket]") 681 | { 682 | zmq::context_t context; 683 | zmq::socket_t front(context, ZMQ_ROUTER); 684 | zmq::socket_t back(context, ZMQ_ROUTER); 685 | zmq::socket_t control(context, ZMQ_SUB); 686 | front.bind("inproc://test1"); 687 | back.bind("inproc://test2"); 688 | control.connect("inproc://test3"); 689 | auto f = std::async(std::launch::async, [&]() { 690 | auto s1 = std::move(front); 691 | auto s2 = std::move(back); 692 | auto s3 = std::move(control); 693 | try { 694 | zmq::proxy_steerable(s1, s2, zmq::socket_ref(), s3); 695 | } 696 | catch (const zmq::error_t &e) { 697 | return e.num() == ETERM; 698 | } 699 | return false; 700 | }); 701 | context.close(); 702 | CHECK(f.get()); 703 | } 704 | #endif 705 | -------------------------------------------------------------------------------- /tests/socket_ref.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #ifdef ZMQ_CPP11 4 | 5 | #ifdef ZMQ_CPP17 6 | static_assert(std::is_nothrow_swappable_v); 7 | #endif 8 | static_assert(sizeof(zmq::socket_ref) == sizeof(void *), "size mismatch"); 9 | static_assert(alignof(zmq::socket_ref) == alignof(void *), "alignment mismatch"); 10 | static_assert(ZMQ_IS_TRIVIALLY_COPYABLE(zmq::socket_ref), 11 | "needs to be trivially copyable"); 12 | 13 | TEST_CASE("socket_ref default init", "[socket_ref]") 14 | { 15 | zmq::socket_ref sr; 16 | CHECK(!sr); 17 | CHECK(sr == nullptr); 18 | CHECK(nullptr == sr); 19 | CHECK(sr.handle() == nullptr); 20 | } 21 | 22 | TEST_CASE("socket_ref create from nullptr", "[socket_ref]") 23 | { 24 | zmq::socket_ref sr = nullptr; 25 | CHECK(sr == nullptr); 26 | CHECK(sr.handle() == nullptr); 27 | } 28 | 29 | TEST_CASE("socket_ref create from handle", "[socket_ref]") 30 | { 31 | void *np = nullptr; 32 | zmq::socket_ref sr{zmq::from_handle, np}; 33 | CHECK(sr == nullptr); 34 | CHECK(sr.handle() == nullptr); 35 | } 36 | 37 | TEST_CASE("socket_ref compare", "[socket_ref]") 38 | { 39 | zmq::socket_ref sr1; 40 | zmq::socket_ref sr2; 41 | CHECK(sr1 == sr2); 42 | CHECK(!(sr1 != sr2)); 43 | } 44 | 45 | TEST_CASE("socket_ref compare from socket_t", "[socket_ref]") 46 | { 47 | zmq::context_t context; 48 | zmq::socket_t s1(context, zmq::socket_type::router); 49 | zmq::socket_t s2(context, zmq::socket_type::dealer); 50 | zmq::socket_ref sr1 = s1; 51 | zmq::socket_ref sr2 = s2; 52 | CHECK(sr1); 53 | CHECK(sr2); 54 | CHECK(sr1 == s1); 55 | CHECK(sr2 == s2); 56 | CHECK(sr1.handle() == s1.handle()); 57 | CHECK(sr1 != sr2); 58 | CHECK(sr1.handle() != sr2.handle()); 59 | CHECK(sr1 != nullptr); 60 | CHECK(nullptr != sr1); 61 | CHECK(sr2 != nullptr); 62 | const bool comp1 = (sr1 < sr2) != (sr1 >= sr2); 63 | CHECK(comp1); 64 | const bool comp2 = (sr1 > sr2) != (sr1 <= sr2); 65 | CHECK(comp2); 66 | std::hash hash; 67 | CHECK(hash(sr1) != hash(sr2)); 68 | CHECK(hash(sr1) == hash(s1)); 69 | } 70 | 71 | TEST_CASE("socket_ref assignment", "[socket_ref]") 72 | { 73 | zmq::context_t context; 74 | zmq::socket_t s1(context, zmq::socket_type::router); 75 | zmq::socket_t s2(context, zmq::socket_type::dealer); 76 | zmq::socket_ref sr1 = s1; 77 | zmq::socket_ref sr2 = s2; 78 | sr1 = s2; 79 | CHECK(sr1 == sr2); 80 | CHECK(sr1.handle() == sr2.handle()); 81 | sr1 = std::move(sr2); 82 | CHECK(sr1 == sr2); 83 | CHECK(sr1.handle() == sr2.handle()); 84 | sr2 = nullptr; 85 | CHECK(sr1 != sr2); 86 | sr1 = nullptr; 87 | CHECK(sr1 == sr2); 88 | } 89 | 90 | TEST_CASE("socket_ref swap", "[socket_ref]") 91 | { 92 | zmq::socket_ref sr1; 93 | zmq::socket_ref sr2; 94 | using std::swap; 95 | swap(sr1, sr2); 96 | } 97 | 98 | TEST_CASE("socket_ref type punning", "[socket_ref]") 99 | { 100 | struct SVP 101 | { 102 | void *p; 103 | } svp; 104 | struct SSR 105 | { 106 | zmq::socket_ref sr; 107 | } ssr; 108 | 109 | zmq::context_t context; 110 | zmq::socket_t socket(context, zmq::socket_type::router); 111 | CHECK(socket.handle() != nullptr); 112 | svp.p = socket.handle(); 113 | // static_cast to silence incorrect warning 114 | std::memcpy(static_cast(&ssr), &svp, sizeof(ssr)); 115 | CHECK(ssr.sr == socket); 116 | } 117 | 118 | #endif 119 | -------------------------------------------------------------------------------- /tests/testutil.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #if defined(ZMQ_CPP11) 7 | 8 | inline std::string bind_ip4_loopback(zmq::socket_t &socket) 9 | { 10 | socket.bind("tcp://127.0.0.1:*"); 11 | std::string endpoint(100, ' '); 12 | endpoint.resize(socket.get(zmq::sockopt::last_endpoint, zmq::buffer(endpoint))); 13 | return endpoint; 14 | } 15 | 16 | struct common_server_client_setup 17 | { 18 | common_server_client_setup(bool initialize = true) 19 | { 20 | if (initialize) 21 | init(); 22 | } 23 | 24 | void init() 25 | { 26 | endpoint = bind_ip4_loopback(server); 27 | REQUIRE_NOTHROW(client.connect(endpoint)); 28 | } 29 | 30 | zmq::context_t context; 31 | zmq::socket_t server{context, zmq::socket_type::pair}; 32 | zmq::socket_t client{context, zmq::socket_type::pair}; 33 | std::string endpoint; 34 | }; 35 | #endif 36 | 37 | #define CHECK_THROWS_ZMQ_ERROR(ecode, expr) \ 38 | do { \ 39 | try { \ 40 | expr; \ 41 | CHECK(false); \ 42 | } \ 43 | catch (const zmq::error_t &ze) { \ 44 | INFO(std::string("Unexpected error code: ") + ze.what()); \ 45 | CHECK(ze.num() == ecode); \ 46 | } \ 47 | catch (const std::exception &ex) { \ 48 | INFO(std::string("Unexpected exception: ") + ex.what()); \ 49 | CHECK(false); \ 50 | } \ 51 | catch (...) { \ 52 | CHECK(false); \ 53 | } \ 54 | } while (false) 55 | -------------------------------------------------------------------------------- /tests/timers.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #if defined(ZMQ_CPP11) && defined(ZMQ_HAVE_TIMERS) 9 | 10 | static_assert(std::is_default_constructible::value); 11 | static_assert(!std::is_copy_constructible::value); 12 | static_assert(!std::is_copy_assignable::value); 13 | 14 | TEST_CASE("timers constructor", "[timers]") 15 | { 16 | zmq::timers timers; 17 | CHECK(!timers.timeout().has_value()); 18 | } 19 | 20 | TEST_CASE("timers add/execute", "[timers]") 21 | { 22 | using namespace std::chrono_literals; 23 | zmq::timers timers; 24 | bool handler_ran = false; 25 | timers.add(4ms, [](auto, void *arg) { *(bool *) arg = true; }, &handler_ran); 26 | CHECK(timers.timeout().has_value()); 27 | CHECK(!handler_ran); 28 | std::this_thread::sleep_for(10ms); 29 | timers.execute(); 30 | CHECK(handler_ran); 31 | } 32 | 33 | TEST_CASE("timers add/cancel", "[timers]") 34 | { 35 | using namespace std::chrono_literals; 36 | zmq::timers timers; 37 | bool handler_ran = false; 38 | auto id = 39 | timers.add(4ms, [](auto, void *arg) { *(bool *) arg = true; }, &handler_ran); 40 | CHECK(timers.timeout().has_value()); 41 | CHECK(!handler_ran); 42 | timers.cancel(id); 43 | CHECK(!timers.timeout().has_value()); 44 | CHECK(!handler_ran); 45 | } 46 | 47 | TEST_CASE("timers set_interval", "[timers]") 48 | { 49 | using namespace std::chrono_literals; 50 | zmq::timers timers; 51 | bool handler_ran = false; 52 | // Interval of 4 hours should never run like this 53 | auto id = 54 | timers.add(4h, [](auto, void *arg) { *(bool *) arg = true; }, &handler_ran); 55 | CHECK(timers.timeout().has_value()); 56 | CHECK(!handler_ran); 57 | // Change the interval to 4ms and wait for it to timeout 58 | timers.set_interval(id, 4ms); 59 | std::this_thread::sleep_for(10ms); 60 | timers.execute(); 61 | CHECK(handler_ran); 62 | } 63 | 64 | TEST_CASE("timers reset", "[timers]") 65 | { 66 | using namespace std::chrono_literals; 67 | zmq::timers timers; 68 | bool handler_ran = false; 69 | auto id = 70 | timers.add(4ms, [](auto, void *arg) { *(bool *) arg = true; }, &handler_ran); 71 | CHECK(timers.timeout().has_value()); 72 | std::this_thread::sleep_for(10ms); 73 | // Available to be executed but we reset it 74 | timers.reset(id); 75 | CHECK(timers.timeout().has_value()); 76 | CHECK(!handler_ran); 77 | 78 | } 79 | 80 | #endif // defined(ZMQ_CPP11) && defined(ZMQ_HAVE_TIMERS) 81 | -------------------------------------------------------------------------------- /tests/utilities.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #if defined(ZMQ_CPP11) && !defined(ZMQ_CPP11_PARTIAL) 5 | 6 | namespace test_ns 7 | { 8 | struct T_nr 9 | { 10 | }; 11 | 12 | struct T_mr 13 | { 14 | void *begin() const noexcept { return nullptr; } 15 | void *end() const noexcept { return nullptr; } 16 | }; 17 | 18 | struct T_fr 19 | { 20 | }; 21 | 22 | inline void *begin(const T_fr &) noexcept 23 | { 24 | return nullptr; 25 | } 26 | 27 | inline void *end(const T_fr &) noexcept 28 | { 29 | return nullptr; 30 | } 31 | 32 | struct T_mfr 33 | { 34 | void *begin() const noexcept { return nullptr; } 35 | void *end() const noexcept { return nullptr; } 36 | }; 37 | 38 | inline void *begin(const T_mfr &) noexcept 39 | { 40 | return nullptr; 41 | } 42 | 43 | inline void *end(const T_mfr &) noexcept 44 | { 45 | return nullptr; 46 | } 47 | 48 | // types with associated namespace std 49 | struct T_assoc_ns_nr : std::exception 50 | { 51 | }; 52 | 53 | struct T_assoc_ns_mr : std::exception 54 | { 55 | void *begin() const noexcept { return nullptr; } 56 | void *end() const noexcept { return nullptr; } 57 | }; 58 | 59 | struct T_assoc_ns_fr : std::exception 60 | { 61 | }; 62 | 63 | inline void *begin(const T_assoc_ns_fr &) noexcept 64 | { 65 | return nullptr; 66 | } 67 | 68 | inline void *end(const T_assoc_ns_fr &) noexcept 69 | { 70 | return nullptr; 71 | } 72 | 73 | struct T_assoc_ns_mfr : std::exception 74 | { 75 | void *begin() const noexcept { return nullptr; } 76 | void *end() const noexcept { return nullptr; } 77 | }; 78 | 79 | inline void *begin(const T_assoc_ns_mfr &) noexcept 80 | { 81 | return nullptr; 82 | } 83 | 84 | inline void *end(const T_assoc_ns_mfr &) noexcept 85 | { 86 | return nullptr; 87 | } 88 | } // namespace test_ns 89 | 90 | TEST_CASE("range SFINAE", "[utilities]") 91 | { 92 | CHECK(!zmq::detail::is_range::value); 93 | CHECK(zmq::detail::is_range::value); 94 | CHECK(zmq::detail::is_range::value); 95 | CHECK(zmq::detail::is_range::value); 96 | CHECK(zmq::detail::is_range::value); 97 | CHECK(zmq::detail::is_range>::value); 98 | 99 | CHECK(!zmq::detail::is_range::value); 100 | CHECK(zmq::detail::is_range::value); 101 | CHECK(zmq::detail::is_range::value); 102 | CHECK(zmq::detail::is_range::value); 103 | 104 | CHECK(!zmq::detail::is_range::value); 105 | CHECK(zmq::detail::is_range::value); 106 | CHECK(zmq::detail::is_range::value); 107 | CHECK(zmq::detail::is_range::value); 108 | } 109 | 110 | #endif 111 | -------------------------------------------------------------------------------- /version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # This script extracts the 0MQ version from zmq.hpp, which is the master 4 | # location for this information. 5 | # 6 | if [ ! -f zmq.hpp ]; then 7 | echo "version.sh: error: zmq.hpp does not exist" 1>&2 8 | exit 1 9 | fi 10 | MAJOR=$(grep '^#define CPPZMQ_VERSION_MAJOR \+[0-9]\+' zmq.hpp) 11 | MINOR=$(grep '^#define CPPZMQ_VERSION_MINOR \+[0-9]\+' zmq.hpp) 12 | PATCH=$(grep '^#define CPPZMQ_VERSION_PATCH \+[0-9]\+' zmq.hpp) 13 | if [ -z "$MAJOR" -o -z "$MINOR" -o -z "$PATCH" ]; then 14 | echo "version.sh: error: could not extract version from zmq.hpp" 1>&2 15 | exit 1 16 | fi 17 | MAJOR=$(echo $MAJOR | awk '{ print $3 }') 18 | MINOR=$(echo $MINOR | awk '{ print $3 }') 19 | PATCH=$(echo $PATCH | awk '{ print $3 }') 20 | echo $MAJOR.$MINOR.$PATCH | tr -d '\n\r' 21 | 22 | -------------------------------------------------------------------------------- /zmq_addon.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2016-2017 ZeroMQ community 3 | Copyright (c) 2016 VOCA AS / Harald Nøkland 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to 7 | deal in the Software without restriction, including without limitation the 8 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9 | sell copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21 | IN THE SOFTWARE. 22 | */ 23 | 24 | #ifndef __ZMQ_ADDON_HPP_INCLUDED__ 25 | #define __ZMQ_ADDON_HPP_INCLUDED__ 26 | 27 | #include "zmq.hpp" 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #ifdef ZMQ_CPP11 34 | #include 35 | #include 36 | #include 37 | 38 | namespace zmq 39 | { 40 | // socket ref or native file descriptor for poller 41 | class poller_ref_t 42 | { 43 | public: 44 | enum RefType 45 | { 46 | RT_SOCKET, 47 | RT_FD 48 | }; 49 | 50 | poller_ref_t() : poller_ref_t(socket_ref{}) 51 | {} 52 | 53 | poller_ref_t(const zmq::socket_ref& socket) : data{RT_SOCKET, socket, {}} 54 | {} 55 | 56 | poller_ref_t(zmq::fd_t fd) : data{RT_FD, {}, fd} 57 | {} 58 | 59 | size_t hash() const ZMQ_NOTHROW 60 | { 61 | std::size_t h = 0; 62 | hash_combine(h, std::get<0>(data)); 63 | hash_combine(h, std::get<1>(data)); 64 | hash_combine(h, std::get<2>(data)); 65 | return h; 66 | } 67 | 68 | bool operator == (const poller_ref_t& o) const ZMQ_NOTHROW 69 | { 70 | return data == o.data; 71 | } 72 | 73 | private: 74 | template 75 | static void hash_combine(std::size_t& seed, const T& v) ZMQ_NOTHROW 76 | { 77 | std::hash hasher; 78 | seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2); 79 | } 80 | 81 | std::tuple data; 82 | 83 | }; // class poller_ref_t 84 | 85 | } // namespace zmq 86 | 87 | // std::hash<> specialization for std::unordered_map 88 | template <> struct std::hash 89 | { 90 | size_t operator()(const zmq::poller_ref_t& ref) const ZMQ_NOTHROW 91 | { 92 | return ref.hash(); 93 | } 94 | }; 95 | #endif // ZMQ_CPP11 96 | 97 | namespace zmq 98 | { 99 | #ifdef ZMQ_CPP11 100 | 101 | namespace detail 102 | { 103 | template 104 | recv_result_t 105 | recv_multipart_n(socket_ref s, OutputIt out, size_t n, recv_flags flags) 106 | { 107 | size_t msg_count = 0; 108 | message_t msg; 109 | while (true) { 110 | if ZMQ_CONSTEXPR_IF (CheckN) { 111 | if (msg_count >= n) 112 | throw std::runtime_error( 113 | "Too many message parts in recv_multipart_n"); 114 | } 115 | if (!s.recv(msg, flags)) { 116 | // zmq ensures atomic delivery of messages 117 | assert(msg_count == 0); 118 | return {}; 119 | } 120 | ++msg_count; 121 | const bool more = msg.more(); 122 | *out++ = std::move(msg); 123 | if (!more) 124 | break; 125 | } 126 | return msg_count; 127 | } 128 | 129 | inline bool is_little_endian() 130 | { 131 | const uint16_t i = 0x01; 132 | return *reinterpret_cast(&i) == 0x01; 133 | } 134 | 135 | inline void write_network_order(unsigned char *buf, const uint32_t value) 136 | { 137 | if (is_little_endian()) { 138 | ZMQ_CONSTEXPR_VAR uint32_t mask = (std::numeric_limits::max)(); 139 | *buf++ = static_cast((value >> 24) & mask); 140 | *buf++ = static_cast((value >> 16) & mask); 141 | *buf++ = static_cast((value >> 8) & mask); 142 | *buf++ = static_cast(value & mask); 143 | } else { 144 | std::memcpy(buf, &value, sizeof(value)); 145 | } 146 | } 147 | 148 | inline uint32_t read_u32_network_order(const unsigned char *buf) 149 | { 150 | if (is_little_endian()) { 151 | return (static_cast(buf[0]) << 24) 152 | + (static_cast(buf[1]) << 16) 153 | + (static_cast(buf[2]) << 8) 154 | + static_cast(buf[3]); 155 | } else { 156 | uint32_t value; 157 | std::memcpy(&value, buf, sizeof(value)); 158 | return value; 159 | } 160 | } 161 | } // namespace detail 162 | 163 | /* Receive a multipart message. 164 | 165 | Writes the zmq::message_t objects to OutputIterator out. 166 | The out iterator must handle an unspecified number of writes, 167 | e.g. by using std::back_inserter. 168 | 169 | Returns: the number of messages received or nullopt (on EAGAIN). 170 | Throws: if recv throws. Any exceptions thrown 171 | by the out iterator will be propagated and the message 172 | may have been only partially received with pending 173 | message parts. It is adviced to close this socket in that event. 174 | */ 175 | template 176 | ZMQ_NODISCARD recv_result_t recv_multipart(socket_ref s, 177 | OutputIt out, 178 | recv_flags flags = recv_flags::none) 179 | { 180 | return detail::recv_multipart_n(s, std::move(out), 0, flags); 181 | } 182 | 183 | /* Receive a multipart message. 184 | 185 | Writes at most n zmq::message_t objects to OutputIterator out. 186 | If the number of message parts of the incoming message exceeds n 187 | then an exception will be thrown. 188 | 189 | Returns: the number of messages received or nullopt (on EAGAIN). 190 | Throws: if recv throws. Throws std::runtime_error if the number 191 | of message parts exceeds n (exactly n messages will have been written 192 | to out). Any exceptions thrown 193 | by the out iterator will be propagated and the message 194 | may have been only partially received with pending 195 | message parts. It is adviced to close this socket in that event. 196 | */ 197 | template 198 | ZMQ_NODISCARD recv_result_t recv_multipart_n(socket_ref s, 199 | OutputIt out, 200 | size_t n, 201 | recv_flags flags = recv_flags::none) 202 | { 203 | return detail::recv_multipart_n(s, std::move(out), n, flags); 204 | } 205 | 206 | /* Send a multipart message. 207 | 208 | The range must be a ForwardRange of zmq::message_t, 209 | zmq::const_buffer or zmq::mutable_buffer. 210 | The flags may be zmq::send_flags::sndmore if there are 211 | more message parts to be sent after the call to this function. 212 | 213 | Returns: the number of messages sent (exactly msgs.size()) or nullopt (on EAGAIN). 214 | Throws: if send throws. Any exceptions thrown 215 | by the msgs range will be propagated and the message 216 | may have been only partially sent. It is adviced to close this socket in that event. 217 | */ 218 | template::value 223 | && (std::is_same, message_t>::value 224 | || detail::is_buffer>::value)>::type 225 | #endif 226 | > 227 | send_result_t 228 | send_multipart(socket_ref s, Range &&msgs, send_flags flags = send_flags::none) 229 | { 230 | using std::begin; 231 | using std::end; 232 | auto it = begin(msgs); 233 | const auto end_it = end(msgs); 234 | size_t msg_count = 0; 235 | while (it != end_it) { 236 | const auto next = std::next(it); 237 | const auto msg_flags = 238 | flags | (next == end_it ? send_flags::none : send_flags::sndmore); 239 | if (!s.send(*it, msg_flags)) { 240 | // zmq ensures atomic delivery of messages 241 | assert(it == begin(msgs)); 242 | return {}; 243 | } 244 | ++msg_count; 245 | it = next; 246 | } 247 | return msg_count; 248 | } 249 | 250 | /* Encode a multipart message. 251 | 252 | The range must be a ForwardRange of zmq::message_t. A 253 | zmq::multipart_t or STL container may be passed for encoding. 254 | 255 | Returns: a zmq::message_t holding the encoded multipart data. 256 | 257 | Throws: std::range_error is thrown if the size of any single part 258 | can not fit in an unsigned 32 bit integer. 259 | 260 | The encoding is compatible with that used by the CZMQ function 261 | zmsg_encode(), see https://rfc.zeromq.org/spec/50/. 262 | Each part consists of a size followed by the data. 263 | These are placed contiguously into the output message. A part of 264 | size less than 255 bytes will have a single byte size value. 265 | Larger parts will have a five byte size value with the first byte 266 | set to 0xFF and the remaining four bytes holding the size of the 267 | part's data. 268 | */ 269 | template::value 274 | && (std::is_same, message_t>::value 275 | || detail::is_buffer>::value)>::type 276 | #endif 277 | > 278 | message_t encode(const Range &parts) 279 | { 280 | size_t mmsg_size = 0; 281 | 282 | // First pass check sizes 283 | for (const auto &part : parts) { 284 | const size_t part_size = part.size(); 285 | if (part_size > (std::numeric_limits::max)()) { 286 | // Size value must fit into uint32_t. 287 | throw std::range_error("Invalid size, message part too large"); 288 | } 289 | const size_t count_size = 290 | part_size < (std::numeric_limits::max)() ? 1 : 5; 291 | mmsg_size += part_size + count_size; 292 | } 293 | 294 | message_t encoded(mmsg_size); 295 | unsigned char *buf = encoded.data(); 296 | for (const auto &part : parts) { 297 | const uint32_t part_size = static_cast(part.size()); 298 | const unsigned char *part_data = 299 | static_cast(part.data()); 300 | 301 | if (part_size < (std::numeric_limits::max)()) { 302 | // small part 303 | *buf++ = static_cast(part_size); 304 | } else { 305 | // big part 306 | *buf++ = (std::numeric_limits::max)(); 307 | detail::write_network_order(buf, part_size); 308 | buf += sizeof(part_size); 309 | } 310 | std::memcpy(buf, part_data, part_size); 311 | buf += part_size; 312 | } 313 | 314 | assert(static_cast(buf - encoded.data()) == mmsg_size); 315 | return encoded; 316 | } 317 | 318 | /* Decode an encoded message to multiple parts. 319 | 320 | The given output iterator must be a ForwardIterator to a container 321 | holding zmq::message_t such as a zmq::multipart_t or various STL 322 | containers. 323 | 324 | Returns the ForwardIterator advanced once past the last decoded 325 | part. 326 | 327 | Throws: a std::out_of_range is thrown if the encoded part sizes 328 | lead to exceeding the message data bounds. 329 | 330 | The decoding assumes the message is encoded in the manner 331 | performed by zmq::encode(), see https://rfc.zeromq.org/spec/50/. 332 | */ 333 | template OutputIt decode(const message_t &encoded, OutputIt out) 334 | { 335 | const unsigned char *source = encoded.data(); 336 | const unsigned char *const limit = source + encoded.size(); 337 | 338 | while (source < limit) { 339 | size_t part_size = *source++; 340 | if (part_size == (std::numeric_limits::max)()) { 341 | if (static_cast(limit - source) < sizeof(uint32_t)) { 342 | throw std::out_of_range( 343 | "Malformed encoding, overflow in reading size"); 344 | } 345 | part_size = detail::read_u32_network_order(source); 346 | // the part size is allowed to be less than 0xFF 347 | source += sizeof(uint32_t); 348 | } 349 | 350 | if (static_cast(limit - source) < part_size) { 351 | throw std::out_of_range("Malformed encoding, overflow in reading part"); 352 | } 353 | *out = message_t(source, part_size); 354 | ++out; 355 | source += part_size; 356 | } 357 | 358 | assert(source == limit); 359 | return out; 360 | } 361 | 362 | #endif 363 | 364 | 365 | #ifdef ZMQ_HAS_RVALUE_REFS 366 | 367 | /* 368 | This class handles multipart messaging. It is the C++ equivalent of zmsg.h, 369 | which is part of CZMQ (the high-level C binding). Furthermore, it is a major 370 | improvement compared to zmsg.hpp, which is part of the examples in the ØMQ 371 | Guide. Unnecessary copying is avoided by using move semantics to efficiently 372 | add/remove parts. 373 | */ 374 | class multipart_t 375 | { 376 | private: 377 | std::deque m_parts; 378 | 379 | public: 380 | typedef std::deque::value_type value_type; 381 | 382 | typedef std::deque::iterator iterator; 383 | typedef std::deque::const_iterator const_iterator; 384 | 385 | typedef std::deque::reverse_iterator reverse_iterator; 386 | typedef std::deque::const_reverse_iterator const_reverse_iterator; 387 | 388 | // Default constructor 389 | multipart_t() {} 390 | 391 | // Construct from socket receive 392 | multipart_t(socket_ref socket) { recv(socket); } 393 | 394 | // Construct from memory block 395 | multipart_t(const void *src, size_t size) { addmem(src, size); } 396 | 397 | // Construct from string 398 | multipart_t(const std::string &string) { addstr(string); } 399 | 400 | // Construct from message part 401 | multipart_t(message_t &&message) { add(std::move(message)); } 402 | 403 | // Move constructor 404 | multipart_t(multipart_t &&other) ZMQ_NOTHROW { m_parts = std::move(other.m_parts); } 405 | 406 | // Move assignment operator 407 | multipart_t &operator=(multipart_t &&other) ZMQ_NOTHROW 408 | { 409 | m_parts = std::move(other.m_parts); 410 | return *this; 411 | } 412 | 413 | // Destructor 414 | virtual ~multipart_t() { clear(); } 415 | 416 | message_t &operator[](size_t n) { return m_parts[n]; } 417 | 418 | const message_t &operator[](size_t n) const { return m_parts[n]; } 419 | 420 | message_t &at(size_t n) { return m_parts.at(n); } 421 | 422 | const message_t &at(size_t n) const { return m_parts.at(n); } 423 | 424 | iterator begin() { return m_parts.begin(); } 425 | 426 | const_iterator begin() const { return m_parts.begin(); } 427 | 428 | const_iterator cbegin() const { return m_parts.cbegin(); } 429 | 430 | reverse_iterator rbegin() { return m_parts.rbegin(); } 431 | 432 | const_reverse_iterator rbegin() const { return m_parts.rbegin(); } 433 | 434 | iterator end() { return m_parts.end(); } 435 | 436 | const_iterator end() const { return m_parts.end(); } 437 | 438 | const_iterator cend() const { return m_parts.cend(); } 439 | 440 | reverse_iterator rend() { return m_parts.rend(); } 441 | 442 | const_reverse_iterator rend() const { return m_parts.rend(); } 443 | 444 | // Delete all parts 445 | void clear() { m_parts.clear(); } 446 | 447 | // Get number of parts 448 | size_t size() const { return m_parts.size(); } 449 | 450 | // Check if number of parts is zero 451 | bool empty() const { return m_parts.empty(); } 452 | 453 | // Receive multipart message from socket 454 | bool recv(socket_ref socket, int flags = 0) 455 | { 456 | clear(); 457 | bool more = true; 458 | while (more) { 459 | message_t message; 460 | #ifdef ZMQ_CPP11 461 | if (!socket.recv(message, static_cast(flags))) 462 | return false; 463 | #else 464 | if (!socket.recv(&message, flags)) 465 | return false; 466 | #endif 467 | more = message.more(); 468 | add(std::move(message)); 469 | } 470 | return true; 471 | } 472 | 473 | // Send multipart message to socket 474 | bool send(socket_ref socket, int flags = 0) 475 | { 476 | flags &= ~(ZMQ_SNDMORE); 477 | bool more = size() > 0; 478 | while (more) { 479 | message_t message = pop(); 480 | more = size() > 0; 481 | #ifdef ZMQ_CPP11 482 | if (!socket.send(message, static_cast( 483 | (more ? ZMQ_SNDMORE : 0) | flags))) 484 | return false; 485 | #else 486 | if (!socket.send(message, (more ? ZMQ_SNDMORE : 0) | flags)) 487 | return false; 488 | #endif 489 | } 490 | clear(); 491 | return true; 492 | } 493 | 494 | // Concatenate other multipart to front 495 | void prepend(multipart_t &&other) 496 | { 497 | while (!other.empty()) 498 | push(other.remove()); 499 | } 500 | 501 | // Concatenate other multipart to back 502 | void append(multipart_t &&other) 503 | { 504 | while (!other.empty()) 505 | add(other.pop()); 506 | } 507 | 508 | // Push memory block to front 509 | void pushmem(const void *src, size_t size) 510 | { 511 | m_parts.push_front(message_t(src, size)); 512 | } 513 | 514 | // Push memory block to back 515 | void addmem(const void *src, size_t size) 516 | { 517 | m_parts.push_back(message_t(src, size)); 518 | } 519 | 520 | // Push string to front 521 | void pushstr(const std::string &string) 522 | { 523 | m_parts.push_front(message_t(string.data(), string.size())); 524 | } 525 | 526 | // Push string to back 527 | void addstr(const std::string &string) 528 | { 529 | m_parts.push_back(message_t(string.data(), string.size())); 530 | } 531 | 532 | // Push type (fixed-size) to front 533 | template void pushtyp(const T &type) 534 | { 535 | static_assert(!std::is_same::value, 536 | "Use pushstr() instead of pushtyp()"); 537 | m_parts.push_front(message_t(&type, sizeof(type))); 538 | } 539 | 540 | // Push type (fixed-size) to back 541 | template void addtyp(const T &type) 542 | { 543 | static_assert(!std::is_same::value, 544 | "Use addstr() instead of addtyp()"); 545 | m_parts.push_back(message_t(&type, sizeof(type))); 546 | } 547 | 548 | // Push message part to front 549 | void push(message_t &&message) { m_parts.push_front(std::move(message)); } 550 | 551 | // Push message part to back 552 | void add(message_t &&message) { m_parts.push_back(std::move(message)); } 553 | 554 | // Alias to allow std::back_inserter() 555 | void push_back(message_t &&message) { m_parts.push_back(std::move(message)); } 556 | 557 | // Pop string from front 558 | std::string popstr() 559 | { 560 | std::string string(m_parts.front().data(), m_parts.front().size()); 561 | m_parts.pop_front(); 562 | return string; 563 | } 564 | 565 | // Pop type (fixed-size) from front 566 | template T poptyp() 567 | { 568 | static_assert(!std::is_same::value, 569 | "Use popstr() instead of poptyp()"); 570 | if (sizeof(T) != m_parts.front().size()) 571 | throw std::runtime_error( 572 | "Invalid type, size does not match the message size"); 573 | T type = *m_parts.front().data(); 574 | m_parts.pop_front(); 575 | return type; 576 | } 577 | 578 | // Pop message part from front 579 | message_t pop() 580 | { 581 | message_t message = std::move(m_parts.front()); 582 | m_parts.pop_front(); 583 | return message; 584 | } 585 | 586 | // Pop message part from back 587 | message_t remove() 588 | { 589 | message_t message = std::move(m_parts.back()); 590 | m_parts.pop_back(); 591 | return message; 592 | } 593 | 594 | // get message part from front 595 | const message_t &front() { return m_parts.front(); } 596 | 597 | // get message part from back 598 | const message_t &back() { return m_parts.back(); } 599 | 600 | // Get pointer to a specific message part 601 | const message_t *peek(size_t index) const { return &m_parts[index]; } 602 | 603 | // Get a string copy of a specific message part 604 | std::string peekstr(size_t index) const 605 | { 606 | std::string string(m_parts[index].data(), m_parts[index].size()); 607 | return string; 608 | } 609 | 610 | // Peek type (fixed-size) from front 611 | template T peektyp(size_t index) const 612 | { 613 | static_assert(!std::is_same::value, 614 | "Use peekstr() instead of peektyp()"); 615 | if (sizeof(T) != m_parts[index].size()) 616 | throw std::runtime_error( 617 | "Invalid type, size does not match the message size"); 618 | T type = *m_parts[index].data(); 619 | return type; 620 | } 621 | 622 | // Create multipart from type (fixed-size) 623 | template static multipart_t create(const T &type) 624 | { 625 | multipart_t multipart; 626 | multipart.addtyp(type); 627 | return multipart; 628 | } 629 | 630 | // Copy multipart 631 | multipart_t clone() const 632 | { 633 | multipart_t multipart; 634 | for (size_t i = 0; i < size(); i++) 635 | multipart.addmem(m_parts[i].data(), m_parts[i].size()); 636 | return multipart; 637 | } 638 | 639 | // Dump content to string 640 | std::string str() const 641 | { 642 | std::stringstream ss; 643 | for (size_t i = 0; i < m_parts.size(); i++) { 644 | const unsigned char *data = m_parts[i].data(); 645 | size_t size = m_parts[i].size(); 646 | 647 | // Dump the message as text or binary 648 | bool isText = true; 649 | for (size_t j = 0; j < size; j++) { 650 | if (data[j] < 32 || data[j] > 127) { 651 | isText = false; 652 | break; 653 | } 654 | } 655 | ss << "\n[" << std::dec << std::setw(3) << std::setfill('0') << size 656 | << "] "; 657 | if (size >= 1000) { 658 | ss << "... (too big to print)"; 659 | continue; 660 | } 661 | for (size_t j = 0; j < size; j++) { 662 | if (isText) 663 | ss << static_cast(data[j]); 664 | else 665 | ss << std::hex << std::setw(2) << std::setfill('0') 666 | << static_cast(data[j]); 667 | } 668 | } 669 | return ss.str(); 670 | } 671 | 672 | // Check if equal to other multipart 673 | bool equal(const multipart_t *other) const ZMQ_NOTHROW 674 | { 675 | return *this == *other; 676 | } 677 | 678 | bool operator==(const multipart_t &other) const ZMQ_NOTHROW 679 | { 680 | if (size() != other.size()) 681 | return false; 682 | for (size_t i = 0; i < size(); i++) 683 | if (at(i) != other.at(i)) 684 | return false; 685 | return true; 686 | } 687 | 688 | bool operator!=(const multipart_t &other) const ZMQ_NOTHROW 689 | { 690 | return !(*this == other); 691 | } 692 | 693 | #ifdef ZMQ_CPP11 694 | 695 | // Return single part message_t encoded from this multipart_t. 696 | message_t encode() const { return zmq::encode(*this); } 697 | 698 | // Decode encoded message into multiple parts and append to self. 699 | void decode_append(const message_t &encoded) 700 | { 701 | zmq::decode(encoded, std::back_inserter(*this)); 702 | } 703 | 704 | // Return a new multipart_t containing the decoded message_t. 705 | static multipart_t decode(const message_t &encoded) 706 | { 707 | multipart_t tmp; 708 | zmq::decode(encoded, std::back_inserter(tmp)); 709 | return tmp; 710 | } 711 | 712 | #endif 713 | 714 | private: 715 | // Disable implicit copying (moving is more efficient) 716 | multipart_t(const multipart_t &other) ZMQ_DELETED_FUNCTION; 717 | void operator=(const multipart_t &other) ZMQ_DELETED_FUNCTION; 718 | }; // class multipart_t 719 | 720 | inline std::ostream &operator<<(std::ostream &os, const multipart_t &msg) 721 | { 722 | return os << msg.str(); 723 | } 724 | 725 | #endif // ZMQ_HAS_RVALUE_REFS 726 | 727 | #if defined(ZMQ_BUILD_DRAFT_API) && defined(ZMQ_CPP11) && defined(ZMQ_HAVE_POLLER) 728 | class active_poller_t 729 | { 730 | public: 731 | active_poller_t() = default; 732 | ~active_poller_t() = default; 733 | 734 | active_poller_t(const active_poller_t &) = delete; 735 | active_poller_t &operator=(const active_poller_t &) = delete; 736 | 737 | active_poller_t(active_poller_t &&src) = default; 738 | active_poller_t &operator=(active_poller_t &&src) = default; 739 | 740 | using handler_type = std::function; 741 | 742 | void add(zmq::socket_ref socket, event_flags events, handler_type handler) 743 | { 744 | const poller_ref_t ref{socket}; 745 | 746 | if (!handler) 747 | throw std::invalid_argument("null handler in active_poller_t::add (socket)"); 748 | auto ret = handlers.emplace( 749 | ref, std::make_shared(std::move(handler))); 750 | if (!ret.second) 751 | throw error_t(EINVAL); // already added 752 | try { 753 | base_poller.add(socket, events, ret.first->second.get()); 754 | need_rebuild = true; 755 | } 756 | catch (...) { 757 | // rollback 758 | handlers.erase(ref); 759 | throw; 760 | } 761 | } 762 | 763 | void add(fd_t fd, event_flags events, handler_type handler) 764 | { 765 | const poller_ref_t ref{fd}; 766 | 767 | if (!handler) 768 | throw std::invalid_argument("null handler in active_poller_t::add (fd)"); 769 | auto ret = handlers.emplace( 770 | ref, std::make_shared(std::move(handler))); 771 | if (!ret.second) 772 | throw error_t(EINVAL); // already added 773 | try { 774 | base_poller.add(fd, events, ret.first->second.get()); 775 | need_rebuild = true; 776 | } 777 | catch (...) { 778 | // rollback 779 | handlers.erase(ref); 780 | throw; 781 | } 782 | } 783 | 784 | void remove(zmq::socket_ref socket) 785 | { 786 | base_poller.remove(socket); 787 | handlers.erase(socket); 788 | need_rebuild = true; 789 | } 790 | 791 | void remove(fd_t fd) 792 | { 793 | base_poller.remove(fd); 794 | handlers.erase(fd); 795 | need_rebuild = true; 796 | } 797 | 798 | void modify(zmq::socket_ref socket, event_flags events) 799 | { 800 | base_poller.modify(socket, events); 801 | } 802 | 803 | void modify(fd_t fd, event_flags events) 804 | { 805 | base_poller.modify(fd, events); 806 | } 807 | 808 | size_t wait(std::chrono::milliseconds timeout) 809 | { 810 | if (need_rebuild) { 811 | poller_events.resize(handlers.size()); 812 | poller_handlers.clear(); 813 | poller_handlers.reserve(handlers.size()); 814 | for (const auto &handler : handlers) { 815 | poller_handlers.push_back(handler.second); 816 | } 817 | need_rebuild = false; 818 | } 819 | const auto count = base_poller.wait_all(poller_events, timeout); 820 | std::for_each(poller_events.begin(), 821 | poller_events.begin() + static_cast(count), 822 | [](decltype(base_poller)::event_type &event) { 823 | assert(event.user_data != nullptr); 824 | (*event.user_data)(event.events); 825 | }); 826 | return count; 827 | } 828 | 829 | ZMQ_NODISCARD bool empty() const noexcept { return handlers.empty(); } 830 | 831 | size_t size() const noexcept { return handlers.size(); } 832 | 833 | private: 834 | bool need_rebuild{false}; 835 | 836 | poller_t base_poller{}; 837 | 838 | std::unordered_map> handlers{}; 839 | 840 | std::vector poller_events{}; 841 | std::vector> poller_handlers{}; 842 | }; // class active_poller_t 843 | #endif // defined(ZMQ_BUILD_DRAFT_API) && defined(ZMQ_CPP11) && defined(ZMQ_HAVE_POLLER) 844 | 845 | 846 | } // namespace zmq 847 | 848 | #endif // __ZMQ_ADDON_HPP_INCLUDED__ 849 | --------------------------------------------------------------------------------