Merge commit '31cbcd39be6aef3ed43121da5b797d8ec9b0fd31' as 'libs/cppzmq'
This commit is contained in:
53
libs/cppzmq/.clang-format
Normal file
53
libs/cppzmq/.clang-format
Normal file
@@ -0,0 +1,53 @@
|
||||
BasedOnStyle: LLVM
|
||||
IndentWidth: 4
|
||||
UseTab: Never
|
||||
BreakBeforeBraces: Custom
|
||||
BraceWrapping:
|
||||
AfterClass: true
|
||||
AfterControlStatement: false
|
||||
AfterEnum: true
|
||||
AfterFunction: true
|
||||
AfterNamespace: true
|
||||
AfterObjCDeclaration: true
|
||||
AfterStruct: true
|
||||
AfterUnion: true
|
||||
BeforeCatch: true
|
||||
BeforeElse: false
|
||||
IndentBraces: false
|
||||
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignConsecutiveDeclarations: false
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
IndentCaseLabels: true
|
||||
BinPackArguments: true
|
||||
BinPackParameters: false
|
||||
AlignTrailingComments: true
|
||||
AllowShortBlocksOnASingleLine: false
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowShortFunctionsOnASingleLine: InlineOnly
|
||||
AlwaysBreakTemplateDeclarations: false
|
||||
ColumnLimit: 85
|
||||
MaxEmptyLinesToKeep: 2
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
ContinuationIndentWidth: 2
|
||||
PointerAlignment: Right
|
||||
ReflowComments: false
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesInAngles: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
Standard: Cpp11
|
||||
|
||||
SortIncludes: false
|
||||
|
||||
FixNamespaceComments: false
|
||||
BreakBeforeBinaryOperators: NonAssignment
|
||||
SpaceAfterTemplateKeyword: false
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignOperands: true
|
||||
BreakConstructorInitializers: AfterColon
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||
SpaceAfterCStyleCast: true
|
||||
BreakBeforeTernaryOperators: true
|
||||
195
libs/cppzmq/.github/workflows/ci.yml
vendored
Normal file
195
libs/cppzmq/.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,195 @@
|
||||
name: CI
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: ["ubuntu-latest"]
|
||||
cppstd: ["98", "11", "20"]
|
||||
cc: ["gcc-10"]
|
||||
cxx: ["g++-10"]
|
||||
drafts: ["ON"]
|
||||
libzmq: ["4.3.4"]
|
||||
libzmqbuild: ["cmake"]
|
||||
include:
|
||||
# older libzmq and gcc without draft
|
||||
- os: "ubuntu-18.04"
|
||||
cppstd: "11"
|
||||
cc: "gcc-7"
|
||||
cxx: "g++-7"
|
||||
drafts: "OFF"
|
||||
libzmq: "4.2.0"
|
||||
libzmqbuild: "pkgconfig"
|
||||
# gcc 4.8
|
||||
- os: "ubuntu-18.04"
|
||||
cppstd: "11"
|
||||
cc: "gcc-4.8"
|
||||
cxx: "g++-4.8"
|
||||
drafts: "ON"
|
||||
libzmq: "4.3.4"
|
||||
libzmqbuild: "cmake"
|
||||
aptinstall: "gcc-4.8 g++-4.8"
|
||||
# gcc 5
|
||||
- os: "ubuntu-18.04"
|
||||
cppstd: "11"
|
||||
cc: "gcc-5"
|
||||
cxx: "g++-5"
|
||||
drafts: "ON"
|
||||
libzmq: "4.3.4"
|
||||
libzmqbuild: "cmake"
|
||||
aptinstall: "gcc-5 g++-5"
|
||||
# without draft
|
||||
- os: "ubuntu-latest"
|
||||
cppstd: "20"
|
||||
cc: "gcc-10"
|
||||
cxx: "g++-10"
|
||||
drafts: "OFF"
|
||||
libzmq: "4.3.4"
|
||||
libzmqbuild: "cmake"
|
||||
# coverage (gcc version should match gcov version)
|
||||
- os: "ubuntu-latest"
|
||||
cppstd: "17"
|
||||
cc: "gcc-9"
|
||||
cxx: "g++-9"
|
||||
drafts: "ON"
|
||||
libzmq: "4.3.4"
|
||||
libzmqbuild: "cmake"
|
||||
coverage: "-DCOVERAGE=ON"
|
||||
aptinstall: "lcov"
|
||||
# clang
|
||||
- os: "ubuntu-latest"
|
||||
cppstd: "17"
|
||||
cc: "clang-12"
|
||||
cxx: "clang++-12"
|
||||
drafts: "ON"
|
||||
libzmq: "4.3.4"
|
||||
libzmqbuild: "cmake"
|
||||
# macos
|
||||
- os: "macos-latest"
|
||||
cppstd: "17"
|
||||
cc: "clang"
|
||||
cxx: "clang++"
|
||||
drafts: "OFF"
|
||||
libzmq: "4.3.4"
|
||||
libzmqbuild: false
|
||||
brewinstall: "zeromq"
|
||||
# windows
|
||||
- os: "windows-2019"
|
||||
cppstd: "14"
|
||||
cc: "msbuild"
|
||||
cxx: "msbuild"
|
||||
drafts: "ON"
|
||||
libzmq: "4.3.4"
|
||||
libzmqbuild: "cmake"
|
||||
platform: "-Ax64"
|
||||
- os: "windows-latest"
|
||||
cppstd: "20"
|
||||
cc: "msbuild"
|
||||
cxx: "msbuild"
|
||||
drafts: "ON"
|
||||
libzmq: "4.3.4"
|
||||
libzmqbuild: "cmake"
|
||||
platform: "-Ax64"
|
||||
|
||||
env:
|
||||
CC: ${{ matrix.cc }}
|
||||
CXX: ${{ matrix.cxx }}
|
||||
VERBOSE: 1
|
||||
THREADS: 2
|
||||
BUILDTYPE: "Debug"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: install_deps
|
||||
run: |
|
||||
if [ ! -z "${{ matrix.aptinstall }}" ]; then
|
||||
sudo apt install -y ${{ matrix.aptinstall }}
|
||||
fi
|
||||
if [ ! -z "${{ matrix.brewinstall }}" ]; then
|
||||
brew install ${{ matrix.brewinstall }}
|
||||
fi
|
||||
|
||||
- name: get_libzmq
|
||||
run: |
|
||||
curl -L https://github.com/zeromq/libzmq/archive/v${{ matrix.libzmq }}.tar.gz \
|
||||
>zeromq.tar.gz
|
||||
tar -xvzf zeromq.tar.gz
|
||||
|
||||
- name: build_libzmq_cmake
|
||||
if: ${{ matrix.libzmqbuild == 'cmake' }}
|
||||
run: |
|
||||
cmake -Hlibzmq-${{ matrix.libzmq }} -Blibzmq-build ${{ matrix.platform}} \
|
||||
-DWITH_PERF_TOOL=OFF \
|
||||
-DZMQ_BUILD_TESTS=OFF \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DENABLE_DRAFTS=${{ matrix.drafts }}
|
||||
cmake --build libzmq-build --config ${BUILDTYPE} -j ${THREADS}
|
||||
echo "LIBZMQ=${PWD}/libzmq-build" >> ${GITHUB_ENV}
|
||||
|
||||
- name: build_libzmq_pkgconfig
|
||||
if: ${{ matrix.libzmqbuild == 'pkgconfig' }}
|
||||
working-directory: libzmq-${{ matrix.libzmq }}
|
||||
run: |
|
||||
./autogen.sh &&
|
||||
./configure --prefix=${PWD}/libzmq-build &&
|
||||
make -j ${THREADS}
|
||||
make install
|
||||
echo "LIBZMQ=${PWD}/libzmq-build" >> ${GITHUB_ENV}
|
||||
|
||||
- name: build
|
||||
env:
|
||||
CMAKE_PREFIX_PATH: ${{ env.LIBZMQ }}
|
||||
run: |
|
||||
cmake -H. -Bbuild ${{ matrix.platform}} ${{ matrix.coverage }} \
|
||||
-DCMAKE_BUILD_TYPE=${BUILDTYPE} \
|
||||
-DENABLE_DRAFTS=${{ matrix.drafts }} \
|
||||
-DCMAKE_CXX_STANDARD=${{ matrix.cppstd }}
|
||||
cmake --build build --config ${BUILDTYPE} -j ${THREADS}
|
||||
echo "CPPZMQ=${PWD}/build" >> ${GITHUB_ENV}
|
||||
|
||||
- name: test
|
||||
# for unknown reason no tests are found and run on windows
|
||||
# could be something to do with catch_discover_tests not working?
|
||||
run: |
|
||||
cd ${{ env.CPPZMQ }}
|
||||
ctest -V -C ${BUILDTYPE}
|
||||
|
||||
- name: demo
|
||||
# probably need to install libzmq and cppzmq for this to work on windows
|
||||
if: ${{ matrix.os == 'ubuntu*' }}
|
||||
env:
|
||||
CMAKE_PREFIX_PATH: ${{ env.LIBZMQ }}:${{ env.CPPZMQ }}
|
||||
run: |
|
||||
cd demo
|
||||
cmake -H. -Bbuild ${{ matrix.platform}} \
|
||||
-DCMAKE_BUILD_TYPE=${BUILDTYPE} \
|
||||
-DCMAKE_CXX_STANDARD=${{ matrix.cppstd }}
|
||||
cmake --build build --config ${BUILDTYPE}
|
||||
cd build
|
||||
ctest -V -C ${BUILDTYPE}
|
||||
|
||||
- name: lcov
|
||||
if: ${{ matrix.coverage && success() }}
|
||||
run: |
|
||||
lcov --capture --directory . --output-file coverage.info
|
||||
lcov --remove coverage.info -o coverage_filtered.info \
|
||||
'/usr/include/*' \
|
||||
'/usr/local/include/*' \
|
||||
${PWD}'/tests/*' \
|
||||
${PWD}'/build/*'
|
||||
# to generate local html: genhtml coverage_filtered.info --output-directory .
|
||||
|
||||
- name: coveralls_upload
|
||||
if: ${{ matrix.coverage && success() }}
|
||||
uses: coverallsapp/github-action@master
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
path-to-lcov: ./coverage_filtered.info
|
||||
5
libs/cppzmq/.gitignore
vendored
Normal file
5
libs/cppzmq/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
# Vim tmp files
|
||||
*.swp
|
||||
|
||||
# Build directory
|
||||
*build/
|
||||
112
libs/cppzmq/CMakeLists.txt
Normal file
112
libs/cppzmq/CMakeLists.txt
Normal file
@@ -0,0 +1,112 @@
|
||||
cmake_minimum_required(VERSION 3.11)
|
||||
|
||||
list (APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
|
||||
include (DetectCPPZMQVersion)
|
||||
|
||||
project(cppzmq VERSION ${DETECTED_CPPZMQ_VERSION})
|
||||
|
||||
if (NOT TARGET libzmq AND NOT TARGET libzmq-static)
|
||||
find_package(ZeroMQ QUIET)
|
||||
|
||||
# libzmq autotools install: fallback to pkg-config
|
||||
if(NOT ZeroMQ_FOUND)
|
||||
message(STATUS "CMake libzmq package not found, trying again with pkg-config (normal install of zeromq)")
|
||||
list (APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/libzmq-pkg-config)
|
||||
find_package(ZeroMQ REQUIRED)
|
||||
endif()
|
||||
|
||||
# TODO "REQUIRED" above should already cause a fatal failure if not found, but this doesn't seem to work
|
||||
if(NOT ZeroMQ_FOUND)
|
||||
message(FATAL_ERROR "ZeroMQ was not found, neither as a CMake package nor via pkg-config")
|
||||
endif()
|
||||
|
||||
if (ZeroMQ_FOUND AND NOT (TARGET libzmq OR TARGET libzmq-static))
|
||||
message(FATAL_ERROR "ZeroMQ version not supported!")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (EXISTS "${CMAKE_SOURCE_DIR}/.git")
|
||||
OPTION (ENABLE_DRAFTS "Build and install draft classes and methods" ON)
|
||||
else ()
|
||||
OPTION (ENABLE_DRAFTS "Build and install draft classes and methods" OFF)
|
||||
endif ()
|
||||
if (ENABLE_DRAFTS)
|
||||
ADD_DEFINITIONS (-DZMQ_BUILD_DRAFT_API)
|
||||
set (pkg_config_defines "-DZMQ_BUILD_DRAFT_API=1")
|
||||
else (ENABLE_DRAFTS)
|
||||
set (pkg_config_defines "")
|
||||
endif (ENABLE_DRAFTS)
|
||||
|
||||
message(STATUS "cppzmq v${cppzmq_VERSION}")
|
||||
|
||||
set(CPPZMQ_HEADERS
|
||||
zmq.hpp
|
||||
zmq_addon.hpp
|
||||
)
|
||||
|
||||
foreach (target cppzmq cppzmq-static)
|
||||
add_library(${target} INTERFACE)
|
||||
target_include_directories(${target} INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||
$<INSTALL_INTERFACE:include>)
|
||||
endforeach()
|
||||
|
||||
target_link_libraries(cppzmq INTERFACE libzmq)
|
||||
target_link_libraries(cppzmq-static INTERFACE libzmq-static)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
include(CMakePackageConfigHelpers)
|
||||
|
||||
install(TARGETS cppzmq cppzmq-static
|
||||
EXPORT ${PROJECT_NAME}-targets)
|
||||
|
||||
install(FILES ${CPPZMQ_HEADERS}
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
|
||||
# GNUInstallDirs "DATADIR" wrong here; CMake search path wants "share".
|
||||
set(CPPZMQ_CMAKECONFIG_INSTALL_DIR "share/cmake/${PROJECT_NAME}" CACHE STRING "install path for cppzmqConfig.cmake")
|
||||
|
||||
configure_file(libzmq-pkg-config/FindZeroMQ.cmake
|
||||
libzmq-pkg-config/FindZeroMQ.cmake
|
||||
COPYONLY)
|
||||
|
||||
export(EXPORT ${PROJECT_NAME}-targets
|
||||
FILE "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Targets.cmake")
|
||||
configure_package_config_file(${PROJECT_NAME}Config.cmake.in
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
|
||||
INSTALL_DESTINATION ${CPPZMQ_CMAKECONFIG_INSTALL_DIR})
|
||||
# Workaround until ARCH_INDEPENDENT flag can be used with cmake 3.14.
|
||||
# The ConigVersion.cmake file contains checks for the architecture is was
|
||||
# generated on, which can cause problems for header only libraries
|
||||
# used with e.g. the Conan package manager. Since it is header only we
|
||||
# can/should omit those checks.
|
||||
set(CPPZMQ_SIZEOF_VOID_P ${CMAKE_SIZEOF_VOID_P})
|
||||
set(CMAKE_SIZEOF_VOID_P "") # a simple unset is not sufficient
|
||||
write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake
|
||||
VERSION ${CPPZMQ_VERSION}
|
||||
COMPATIBILITY AnyNewerVersion)
|
||||
set(CMAKE_SIZEOF_VOID_P ${CPPZMQ_SIZEOF_VOID_P})
|
||||
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cppzmq.pc.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/cppzmq.pc @ONLY)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/cppzmq.pc
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
|
||||
|
||||
install(EXPORT ${PROJECT_NAME}-targets
|
||||
FILE ${PROJECT_NAME}Targets.cmake
|
||||
DESTINATION ${CPPZMQ_CMAKECONFIG_INSTALL_DIR})
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake
|
||||
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake
|
||||
DESTINATION ${CPPZMQ_CMAKECONFIG_INSTALL_DIR})
|
||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/libzmq-pkg-config/FindZeroMQ.cmake
|
||||
DESTINATION ${CPPZMQ_CMAKECONFIG_INSTALL_DIR}/libzmq-pkg-config)
|
||||
|
||||
option(CPPZMQ_BUILD_TESTS "Whether or not to build the tests" ON)
|
||||
|
||||
if (CPPZMQ_BUILD_TESTS)
|
||||
enable_testing()
|
||||
add_subdirectory(tests)
|
||||
if (CMAKE_CXX_STANDARD AND NOT CMAKE_CXX_STANDARD EQUAL 98 AND CMAKE_CXX_STANDARD GREATER_EQUAL 11)
|
||||
add_subdirectory(examples)
|
||||
endif()
|
||||
endif()
|
||||
17
libs/cppzmq/LICENSE
Normal file
17
libs/cppzmq/LICENSE
Normal file
@@ -0,0 +1,17 @@
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
deal in the Software without restriction, including without limitation the
|
||||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
sell copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
IN THE SOFTWARE.
|
||||
196
libs/cppzmq/README.md
Normal file
196
libs/cppzmq/README.md
Normal file
@@ -0,0 +1,196 @@
|
||||
[](https://github.com/zeromq/cppzmq/actions)
|
||||
[](https://coveralls.io/github/zeromq/cppzmq?branch=master)
|
||||
[](https://github.com/zeromq/cppzmq/blob/master/LICENSE)
|
||||
|
||||
Introduction & Design Goals
|
||||
===========================
|
||||
|
||||
cppzmq is a C++ binding for libzmq. It has the following design goals:
|
||||
- cppzmq maps the libzmq C API to C++ concepts. In particular:
|
||||
- it is type-safe (the libzmq C API exposes various class-like concepts as void*)
|
||||
- it provides exception-based error handling (the libzmq C API provides errno-based error handling)
|
||||
- it provides RAII-style classes that automate resource management (the libzmq C API requires the user to take care to free resources explicitly)
|
||||
- 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.
|
||||
- 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.
|
||||
|
||||
There are other C++ bindings for ZeroMQ with different design goals. In particular, none of the following bindings are header-only:
|
||||
- [zmqpp](https://github.com/zeromq/zmqpp) is a high-level binding to libzmq.
|
||||
- [czmqpp](https://github.com/zeromq/czmqpp) is a binding based on the high-level czmq API.
|
||||
- [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.
|
||||
|
||||
Supported platforms
|
||||
===================
|
||||
|
||||
- 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.
|
||||
- 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
|
||||
- 4.2.0 (without DRAFT API)
|
||||
- 4.3.4 (with and without DRAFT API)
|
||||
- Platforms with full support (i.e. CI executing build and tests)
|
||||
- Ubuntu 18.04 x64 (with gcc 4.8.5, 5.5.0, 7.5.0)
|
||||
- Ubuntu 20.04 x64 (with gcc 9.3.0, 10.3.0 and clang 12)
|
||||
- Visual Studio 2017 x64
|
||||
- Visual Studio 2019 x64
|
||||
- macOS 10.15 (with clang 12, without DRAFT API)
|
||||
- Additional platforms that are known to work:
|
||||
- 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.
|
||||
- Additional platforms that probably work:
|
||||
- Any platform supported by libzmq that provides a sufficiently recent gcc (4.8.1 or newer) or clang (3.4.1 or newer)
|
||||
- Visual Studio 2012+ x86/x64
|
||||
|
||||
Examples
|
||||
========
|
||||
These examples require at least C++11.
|
||||
```c++
|
||||
#include <zmq.hpp>
|
||||
|
||||
int main()
|
||||
{
|
||||
zmq::context_t ctx;
|
||||
zmq::socket_t sock(ctx, zmq::socket_type::push);
|
||||
sock.bind("inproc://test");
|
||||
sock.send(zmq::str_buffer("Hello, world"), zmq::send_flags::dontwait);
|
||||
}
|
||||
```
|
||||
This a more complex example where we send and receive multi-part messages over TCP with a wildcard port.
|
||||
```c++
|
||||
#include <iostream>
|
||||
#include <zmq_addon.hpp>
|
||||
|
||||
int main()
|
||||
{
|
||||
zmq::context_t ctx;
|
||||
zmq::socket_t sock1(ctx, zmq::socket_type::push);
|
||||
zmq::socket_t sock2(ctx, zmq::socket_type::pull);
|
||||
sock1.bind("tcp://127.0.0.1:*");
|
||||
const std::string last_endpoint =
|
||||
sock1.get(zmq::sockopt::last_endpoint);
|
||||
std::cout << "Connecting to "
|
||||
<< last_endpoint << std::endl;
|
||||
sock2.connect(last_endpoint);
|
||||
|
||||
std::array<zmq::const_buffer, 2> send_msgs = {
|
||||
zmq::str_buffer("foo"),
|
||||
zmq::str_buffer("bar!")
|
||||
};
|
||||
if (!zmq::send_multipart(sock1, send_msgs))
|
||||
return 1;
|
||||
|
||||
std::vector<zmq::message_t> recv_msgs;
|
||||
const auto ret = zmq::recv_multipart(
|
||||
sock2, std::back_inserter(recv_msgs));
|
||||
if (!ret)
|
||||
return 1;
|
||||
std::cout << "Got " << *ret
|
||||
<< " messages" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
See the `examples` directory for more examples. When the project is compiled with tests enabled, each example gets compiled to an executable.
|
||||
|
||||
|
||||
API Overview
|
||||
============
|
||||
|
||||
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).
|
||||
|
||||
Bindings for libzmq in `zmq.hpp`:
|
||||
|
||||
Types:
|
||||
* class `zmq::context_t`
|
||||
* enum `zmq::ctxopt`
|
||||
* class `zmq::socket_t`
|
||||
* class `zmq::socket_ref`
|
||||
* enum `zmq::socket_type`
|
||||
* enum `zmq::sockopt`
|
||||
* enum `zmq::send_flags`
|
||||
* enum `zmq::recv_flags`
|
||||
* class `zmq::message_t`
|
||||
* class `zmq::const_buffer`
|
||||
* class `zmq::mutable_buffer`
|
||||
* struct `zmq::recv_buffer_size`
|
||||
* alias `zmq::send_result_t`
|
||||
* alias `zmq::recv_result_t`
|
||||
* alias `zmq::recv_buffer_result_t`
|
||||
* class `zmq::error_t`
|
||||
* class `zmq::monitor_t`
|
||||
* struct `zmq_event_t`,
|
||||
* alias `zmq::free_fn`,
|
||||
* alias `zmq::pollitem_t`,
|
||||
* alias `zmq::fd_t`
|
||||
* class `zmq::poller_t` DRAFT
|
||||
* enum `zmq::event_flags` DRAFT
|
||||
* enum `zmq::poller_event` DRAFT
|
||||
|
||||
Functions:
|
||||
* `zmq::version`
|
||||
* `zmq::poll`
|
||||
* `zmq::proxy`
|
||||
* `zmq::proxy_steerable`
|
||||
* `zmq::buffer`
|
||||
* `zmq::str_buffer`
|
||||
|
||||
Extra high-level types and functions `zmq_addon.hpp`:
|
||||
|
||||
Types:
|
||||
* class `zmq::multipart_t`
|
||||
* class `zmq::active_poller_t` DRAFT
|
||||
|
||||
Functions:
|
||||
* `zmq::recv_multipart`
|
||||
* `zmq::send_multipart`
|
||||
* `zmq::send_multipart_n`
|
||||
* `zmq::encode`
|
||||
* `zmq::decode`
|
||||
|
||||
Compatibility Guidelines
|
||||
========================
|
||||
|
||||
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):
|
||||
|
||||
* Do not depend on any macros defined in cppzmq unless explicitly declared public here.
|
||||
|
||||
The following macros may be used by consumers of cppzmq: `CPPZMQ_VERSION`, `CPPZMQ_VERSION_MAJOR`, `CPPZMQ_VERSION_MINOR`, `CPPZMQ_VERSION_PATCH`.
|
||||
|
||||
Contribution policy
|
||||
===================
|
||||
|
||||
The contribution policy is at: http://rfc.zeromq.org/spec:22
|
||||
|
||||
Build instructions
|
||||
==================
|
||||
|
||||
Build steps:
|
||||
|
||||
1. Build [libzmq](https://github.com/zeromq/libzmq) via cmake. This does an out of source build and installs the build files
|
||||
- download and unzip the lib, cd to directory
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake ..
|
||||
- sudo make -j4 install
|
||||
|
||||
2. Build cppzmq via cmake. This does an out of source build and installs the build files
|
||||
- download and unzip the lib, cd to directory
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake ..
|
||||
- sudo make -j4 install
|
||||
|
||||
3. Build cppzmq via [vcpkg](https://github.com/Microsoft/vcpkg/). This does an out of source build and installs the build files
|
||||
- git clone https://github.com/Microsoft/vcpkg.git
|
||||
- cd vcpkg
|
||||
- ./bootstrap-vcpkg.sh # bootstrap-vcpkg.bat for Powershell
|
||||
- ./vcpkg integrate install
|
||||
- ./vcpkg install cppzmq
|
||||
|
||||
Using this:
|
||||
|
||||
A cmake find package scripts is provided for you to easily include this library.
|
||||
Add these lines in your CMakeLists.txt to include the headers and library files of
|
||||
cpp zmq (which will also include libzmq for you).
|
||||
|
||||
```
|
||||
#find cppzmq wrapper, installed by make of cppzmq
|
||||
find_package(cppzmq)
|
||||
target_link_libraries(*Your Project Name* cppzmq)
|
||||
```
|
||||
8
libs/cppzmq/cmake/DetectCPPZMQVersion.cmake
Normal file
8
libs/cppzmq/cmake/DetectCPPZMQVersion.cmake
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
file(READ "${CMAKE_CURRENT_SOURCE_DIR}/zmq.hpp" _CPPZMQ_H_CONTENTS)
|
||||
string(REGEX REPLACE ".*#define CPPZMQ_VERSION_MAJOR ([0-9]+).*" "\\1" DETECTED_CPPZMQ_VERSION_MAJOR "${_CPPZMQ_H_CONTENTS}")
|
||||
string(REGEX REPLACE ".*#define CPPZMQ_VERSION_MINOR ([0-9]+).*" "\\1" DETECTED_CPPZMQ_VERSION_MINOR "${_CPPZMQ_H_CONTENTS}")
|
||||
string(REGEX REPLACE ".*#define CPPZMQ_VERSION_PATCH ([0-9]+).*" "\\1" DETECTED_CPPZMQ_VERSION_PATCH "${_CPPZMQ_H_CONTENTS}")
|
||||
set(DETECTED_CPPZMQ_VERSION "${DETECTED_CPPZMQ_VERSION_MAJOR}.${DETECTED_CPPZMQ_VERSION_MINOR}.${DETECTED_CPPZMQ_VERSION_PATCH}")
|
||||
|
||||
message(STATUS "Detected CPPZMQ Version - ${DETECTED_CPPZMQ_VERSION}")
|
||||
9
libs/cppzmq/cppzmq.pc.in
Normal file
9
libs/cppzmq/cppzmq.pc.in
Normal file
@@ -0,0 +1,9 @@
|
||||
prefix="@CMAKE_INSTALL_PREFIX@"
|
||||
includedir="@CMAKE_INSTALL_FULL_INCLUDEDIR@"
|
||||
|
||||
Name: @PROJECT_NAME@
|
||||
Description: C++ binding for libzmq
|
||||
URL: https://github.com/zeromq/cppzmq
|
||||
Version: @PROJECT_VERSION@
|
||||
Requires: libzmq
|
||||
Cflags: -I"${includedir}" @pkg_config_defines@
|
||||
36
libs/cppzmq/cppzmqConfig.cmake.in
Normal file
36
libs/cppzmq/cppzmqConfig.cmake.in
Normal file
@@ -0,0 +1,36 @@
|
||||
# cppzmq cmake module
|
||||
#
|
||||
# The following import targets are created
|
||||
#
|
||||
# ::
|
||||
#
|
||||
# cppzmq-static
|
||||
# cppzmq
|
||||
#
|
||||
# This module sets the following variables in your project::
|
||||
#
|
||||
# cppzmq_FOUND - true if cppzmq found on the system
|
||||
# cppzmq_INCLUDE_DIR - the directory containing cppzmq headers
|
||||
# cppzmq_LIBRARY - the ZeroMQ library for dynamic linking
|
||||
# cppzmq_STATIC_LIBRARY - the ZeroMQ library for static linking
|
||||
|
||||
@PACKAGE_INIT@
|
||||
|
||||
include(CMakeFindDependencyMacro)
|
||||
find_package(ZeroMQ QUIET)
|
||||
|
||||
# libzmq autotools install: fallback to pkg-config
|
||||
if(NOT ZeroMQ_FOUND)
|
||||
list (APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/libzmq-pkg-config)
|
||||
find_package(ZeroMQ REQUIRED)
|
||||
endif()
|
||||
|
||||
if(NOT ZeroMQ_FOUND)
|
||||
message(FATAL_ERROR "ZeroMQ was NOT found!")
|
||||
endif()
|
||||
|
||||
if(NOT TARGET @PROJECT_NAME@)
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
|
||||
get_target_property(@PROJECT_NAME@_INCLUDE_DIR cppzmq INTERFACE_INCLUDE_DIRECTORIES)
|
||||
endif()
|
||||
|
||||
23
libs/cppzmq/demo/CMakeLists.txt
Normal file
23
libs/cppzmq/demo/CMakeLists.txt
Normal file
@@ -0,0 +1,23 @@
|
||||
cmake_minimum_required(VERSION 3.0 FATAL_ERROR)
|
||||
|
||||
project(cppzmq-demo CXX)
|
||||
|
||||
find_package(cppzmq)
|
||||
|
||||
enable_testing()
|
||||
add_executable(
|
||||
demo
|
||||
main.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
demo
|
||||
cppzmq
|
||||
)
|
||||
|
||||
add_test(
|
||||
NAME
|
||||
demo
|
||||
COMMAND
|
||||
${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_BINDIR}/demo
|
||||
)
|
||||
7
libs/cppzmq/demo/main.cpp
Normal file
7
libs/cppzmq/demo/main.cpp
Normal file
@@ -0,0 +1,7 @@
|
||||
#include <zmq.hpp>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
zmq::context_t context;
|
||||
return 0;
|
||||
}
|
||||
40
libs/cppzmq/examples/CMakeLists.txt
Normal file
40
libs/cppzmq/examples/CMakeLists.txt
Normal file
@@ -0,0 +1,40 @@
|
||||
cmake_minimum_required(VERSION 3.0 FATAL_ERROR)
|
||||
|
||||
project(cppzmq-examples CXX)
|
||||
|
||||
# place binaries and libraries according to GNU standards
|
||||
|
||||
include(GNUInstallDirs)
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR})
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR})
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_BINDIR})
|
||||
|
||||
find_package(Threads)
|
||||
find_package(cppzmq)
|
||||
|
||||
add_executable(
|
||||
pubsub_multithread_inproc
|
||||
pubsub_multithread_inproc.cpp
|
||||
)
|
||||
target_link_libraries(
|
||||
pubsub_multithread_inproc
|
||||
PRIVATE cppzmq ${CMAKE_THREAD_LIBS_INIT}
|
||||
)
|
||||
|
||||
add_executable(
|
||||
hello_world
|
||||
hello_world.cpp
|
||||
)
|
||||
target_link_libraries(
|
||||
hello_world
|
||||
PRIVATE cppzmq ${CMAKE_THREAD_LIBS_INIT}
|
||||
)
|
||||
|
||||
add_executable(
|
||||
multipart_messages
|
||||
multipart_messages.cpp
|
||||
)
|
||||
target_link_libraries(
|
||||
multipart_messages
|
||||
PRIVATE cppzmq ${CMAKE_THREAD_LIBS_INIT}
|
||||
)
|
||||
9
libs/cppzmq/examples/hello_world.cpp
Normal file
9
libs/cppzmq/examples/hello_world.cpp
Normal file
@@ -0,0 +1,9 @@
|
||||
#include <zmq.hpp>
|
||||
|
||||
int main()
|
||||
{
|
||||
zmq::context_t ctx;
|
||||
zmq::socket_t sock(ctx, zmq::socket_type::push);
|
||||
sock.bind("inproc://test");
|
||||
sock.send(zmq::str_buffer("Hello, world"), zmq::send_flags::dontwait);
|
||||
}
|
||||
31
libs/cppzmq/examples/multipart_messages.cpp
Normal file
31
libs/cppzmq/examples/multipart_messages.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#include <iostream>
|
||||
#include <zmq_addon.hpp>
|
||||
|
||||
int main()
|
||||
{
|
||||
zmq::context_t ctx;
|
||||
zmq::socket_t sock1(ctx, zmq::socket_type::push);
|
||||
zmq::socket_t sock2(ctx, zmq::socket_type::pull);
|
||||
sock1.bind("tcp://127.0.0.1:*");
|
||||
const std::string last_endpoint =
|
||||
sock1.get(zmq::sockopt::last_endpoint);
|
||||
std::cout << "Connecting to "
|
||||
<< last_endpoint << std::endl;
|
||||
sock2.connect(last_endpoint);
|
||||
|
||||
std::array<zmq::const_buffer, 2> send_msgs = {
|
||||
zmq::str_buffer("foo"),
|
||||
zmq::str_buffer("bar!")
|
||||
};
|
||||
if (!zmq::send_multipart(sock1, send_msgs))
|
||||
return 1;
|
||||
|
||||
std::vector<zmq::message_t> recv_msgs;
|
||||
const auto ret = zmq::recv_multipart(
|
||||
sock2, std::back_inserter(recv_msgs));
|
||||
if (!ret)
|
||||
return 1;
|
||||
std::cout << "Got " << *ret
|
||||
<< " messages" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
102
libs/cppzmq/examples/pubsub_multithread_inproc.cpp
Normal file
102
libs/cppzmq/examples/pubsub_multithread_inproc.cpp
Normal file
@@ -0,0 +1,102 @@
|
||||
#include <future>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include "zmq.hpp"
|
||||
#include "zmq_addon.hpp"
|
||||
|
||||
void PublisherThread(zmq::context_t *ctx) {
|
||||
// Prepare publisher
|
||||
zmq::socket_t publisher(*ctx, zmq::socket_type::pub);
|
||||
publisher.bind("inproc://#1");
|
||||
|
||||
// Give the subscribers a chance to connect, so they don't lose any messages
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
|
||||
while (true) {
|
||||
// Write three messages, each with an envelope and content
|
||||
publisher.send(zmq::str_buffer("A"), zmq::send_flags::sndmore);
|
||||
publisher.send(zmq::str_buffer("Message in A envelope"));
|
||||
publisher.send(zmq::str_buffer("B"), zmq::send_flags::sndmore);
|
||||
publisher.send(zmq::str_buffer("Message in B envelope"));
|
||||
publisher.send(zmq::str_buffer("C"), zmq::send_flags::sndmore);
|
||||
publisher.send(zmq::str_buffer("Message in C envelope"));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
}
|
||||
|
||||
void SubscriberThread1(zmq::context_t *ctx) {
|
||||
// Prepare subscriber
|
||||
zmq::socket_t subscriber(*ctx, zmq::socket_type::sub);
|
||||
subscriber.connect("inproc://#1");
|
||||
|
||||
// Thread2 opens "A" and "B" envelopes
|
||||
subscriber.set(zmq::sockopt::subscribe, "A");
|
||||
subscriber.set(zmq::sockopt::subscribe, "B");
|
||||
|
||||
while (1) {
|
||||
// Receive all parts of the message
|
||||
std::vector<zmq::message_t> recv_msgs;
|
||||
zmq::recv_result_t result =
|
||||
zmq::recv_multipart(subscriber, std::back_inserter(recv_msgs));
|
||||
assert(result && "recv failed");
|
||||
assert(*result == 2);
|
||||
|
||||
std::cout << "Thread2: [" << recv_msgs[0].to_string() << "] "
|
||||
<< recv_msgs[1].to_string() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void SubscriberThread2(zmq::context_t *ctx) {
|
||||
// Prepare our context and subscriber
|
||||
zmq::socket_t subscriber(*ctx, zmq::socket_type::sub);
|
||||
subscriber.connect("inproc://#1");
|
||||
|
||||
// Thread3 opens ALL envelopes
|
||||
subscriber.set(zmq::sockopt::subscribe, "");
|
||||
|
||||
while (1) {
|
||||
// Receive all parts of the message
|
||||
std::vector<zmq::message_t> recv_msgs;
|
||||
zmq::recv_result_t result =
|
||||
zmq::recv_multipart(subscriber, std::back_inserter(recv_msgs));
|
||||
assert(result && "recv failed");
|
||||
assert(*result == 2);
|
||||
|
||||
std::cout << "Thread3: [" << recv_msgs[0].to_string() << "] "
|
||||
<< recv_msgs[1].to_string() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
/*
|
||||
* No I/O threads are involved in passing messages using the inproc transport.
|
||||
* Therefore, if you are using a ØMQ context for in-process messaging only you
|
||||
* can initialise the context with zero I/O threads.
|
||||
*
|
||||
* Source: http://api.zeromq.org/4-3:zmq-inproc
|
||||
*/
|
||||
zmq::context_t ctx(0);
|
||||
|
||||
auto thread1 = std::async(std::launch::async, PublisherThread, &ctx);
|
||||
|
||||
// Give the publisher a chance to bind, since inproc requires it
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
|
||||
auto thread2 = std::async(std::launch::async, SubscriberThread1, &ctx);
|
||||
auto thread3 = std::async(std::launch::async, SubscriberThread2, &ctx);
|
||||
thread1.wait();
|
||||
thread2.wait();
|
||||
thread3.wait();
|
||||
|
||||
/*
|
||||
* Output:
|
||||
* An infinite loop of a mix of:
|
||||
* Thread2: [A] Message in A envelope
|
||||
* Thread2: [B] Message in B envelope
|
||||
* Thread3: [A] Message in A envelope
|
||||
* Thread3: [B] Message in B envelope
|
||||
* Thread3: [C] Message in C envelope
|
||||
*/
|
||||
}
|
||||
34
libs/cppzmq/libzmq-pkg-config/FindZeroMQ.cmake
Normal file
34
libs/cppzmq/libzmq-pkg-config/FindZeroMQ.cmake
Normal file
@@ -0,0 +1,34 @@
|
||||
set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH ON)
|
||||
find_package(PkgConfig)
|
||||
pkg_check_modules(PC_LIBZMQ QUIET libzmq)
|
||||
|
||||
set(ZeroMQ_VERSION ${PC_LIBZMQ_VERSION})
|
||||
|
||||
find_path(ZeroMQ_INCLUDE_DIR zmq.h
|
||||
PATHS ${ZeroMQ_DIR}/include
|
||||
${PC_LIBZMQ_INCLUDE_DIRS})
|
||||
|
||||
find_library(ZeroMQ_LIBRARY
|
||||
NAMES zmq
|
||||
PATHS ${ZeroMQ_DIR}/lib
|
||||
${PC_LIBZMQ_LIBDIR}
|
||||
${PC_LIBZMQ_LIBRARY_DIRS})
|
||||
|
||||
if(ZeroMQ_LIBRARY)
|
||||
set(ZeroMQ_FOUND ON)
|
||||
endif()
|
||||
|
||||
set ( ZeroMQ_LIBRARIES ${ZeroMQ_LIBRARY} )
|
||||
set ( ZeroMQ_INCLUDE_DIRS ${ZeroMQ_INCLUDE_DIR} )
|
||||
|
||||
if(NOT TARGET libzmq)
|
||||
add_library(libzmq UNKNOWN IMPORTED)
|
||||
set_target_properties(libzmq PROPERTIES
|
||||
IMPORTED_LOCATION ${ZeroMQ_LIBRARIES}
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${ZeroMQ_INCLUDE_DIRS})
|
||||
endif()
|
||||
|
||||
include ( FindPackageHandleStandardArgs )
|
||||
# handle the QUIETLY and REQUIRED arguments and set ZMQ_FOUND to TRUE
|
||||
# if all listed variables are TRUE
|
||||
find_package_handle_standard_args ( ZeroMQ DEFAULT_MSG ZeroMQ_LIBRARIES ZeroMQ_INCLUDE_DIRS )
|
||||
53
libs/cppzmq/tests/CMakeLists.txt
Normal file
53
libs/cppzmq/tests/CMakeLists.txt
Normal file
@@ -0,0 +1,53 @@
|
||||
find_package(Threads)
|
||||
|
||||
find_package(Catch2 QUIET)
|
||||
|
||||
if (NOT Catch2_FOUND)
|
||||
include(FetchContent)
|
||||
|
||||
FetchContent_Declare(
|
||||
Catch2
|
||||
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
|
||||
GIT_TAG v2.13.9)
|
||||
|
||||
FetchContent_MakeAvailable(Catch2)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/contrib)
|
||||
endif()
|
||||
|
||||
add_executable(
|
||||
unit_tests
|
||||
buffer.cpp
|
||||
message.cpp
|
||||
context.cpp
|
||||
socket.cpp
|
||||
socket_ref.cpp
|
||||
poller.cpp
|
||||
active_poller.cpp
|
||||
multipart.cpp
|
||||
recv_multipart.cpp
|
||||
send_multipart.cpp
|
||||
codec_multipart.cpp
|
||||
monitor.cpp
|
||||
utilities.cpp
|
||||
)
|
||||
|
||||
target_include_directories(unit_tests PUBLIC ${CATCH_MODULE_PATH})
|
||||
target_link_libraries(
|
||||
unit_tests
|
||||
PRIVATE Catch2::Catch2
|
||||
PRIVATE cppzmq
|
||||
PRIVATE ${CMAKE_THREAD_LIBS_INIT}
|
||||
)
|
||||
|
||||
OPTION (COVERAGE "Enable gcda file generation needed by lcov" OFF)
|
||||
|
||||
if (COVERAGE)
|
||||
target_compile_options(unit_tests PRIVATE --coverage)
|
||||
target_link_options(unit_tests PRIVATE --coverage)
|
||||
message(STATUS "Coverage enabled")
|
||||
endif()
|
||||
|
||||
include(CTest)
|
||||
include(Catch)
|
||||
catch_discover_tests(unit_tests)
|
||||
446
libs/cppzmq/tests/active_poller.cpp
Normal file
446
libs/cppzmq/tests/active_poller.cpp
Normal file
@@ -0,0 +1,446 @@
|
||||
#include <zmq_addon.hpp>
|
||||
|
||||
#include "testutil.hpp"
|
||||
|
||||
#if defined(ZMQ_CPP11) && !defined(ZMQ_CPP11_PARTIAL) && defined(ZMQ_BUILD_DRAFT_API)
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
TEST_CASE("create destroy", "[active_poller]")
|
||||
{
|
||||
zmq::active_poller_t active_poller;
|
||||
CHECK(active_poller.empty());
|
||||
}
|
||||
|
||||
static_assert(!std::is_copy_constructible<zmq::active_poller_t>::value,
|
||||
"active_poller_t should not be copy-constructible");
|
||||
static_assert(!std::is_copy_assignable<zmq::active_poller_t>::value,
|
||||
"active_poller_t should not be copy-assignable");
|
||||
|
||||
static const zmq::active_poller_t::handler_type no_op_handler =
|
||||
[](zmq::event_flags) {};
|
||||
|
||||
TEST_CASE("move construct empty", "[active_poller]")
|
||||
{
|
||||
zmq::active_poller_t a;
|
||||
CHECK(a.empty());
|
||||
zmq::active_poller_t b = std::move(a);
|
||||
CHECK(b.empty());
|
||||
CHECK(0u == a.size());
|
||||
CHECK(0u == b.size());
|
||||
}
|
||||
|
||||
TEST_CASE("move assign empty", "[active_poller]")
|
||||
{
|
||||
zmq::active_poller_t a;
|
||||
CHECK(a.empty());
|
||||
zmq::active_poller_t b;
|
||||
CHECK(b.empty());
|
||||
b = std::move(a);
|
||||
CHECK(0u == a.size());
|
||||
CHECK(0u == b.size());
|
||||
CHECK(a.empty());
|
||||
CHECK(b.empty());
|
||||
}
|
||||
|
||||
TEST_CASE("move construct non empty", "[active_poller]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::socket_t socket{context, zmq::socket_type::router};
|
||||
|
||||
zmq::active_poller_t a;
|
||||
a.add(socket, zmq::event_flags::pollin, [](zmq::event_flags) {});
|
||||
CHECK_FALSE(a.empty());
|
||||
CHECK(1u == a.size());
|
||||
zmq::active_poller_t b = std::move(a);
|
||||
CHECK(a.empty());
|
||||
CHECK(0u == a.size());
|
||||
CHECK_FALSE(b.empty());
|
||||
CHECK(1u == b.size());
|
||||
}
|
||||
|
||||
TEST_CASE("move assign non empty", "[active_poller]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::socket_t socket{context, zmq::socket_type::router};
|
||||
|
||||
zmq::active_poller_t a;
|
||||
a.add(socket, zmq::event_flags::pollin, no_op_handler);
|
||||
CHECK_FALSE(a.empty());
|
||||
CHECK(1u == a.size());
|
||||
zmq::active_poller_t b;
|
||||
b = std::move(a);
|
||||
CHECK(a.empty());
|
||||
CHECK(0u == a.size());
|
||||
CHECK_FALSE(b.empty());
|
||||
CHECK(1u == b.size());
|
||||
}
|
||||
|
||||
TEST_CASE("add handler", "[active_poller]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::socket_t socket{context, zmq::socket_type::router};
|
||||
zmq::active_poller_t active_poller;
|
||||
CHECK_NOTHROW(
|
||||
active_poller.add(socket, zmq::event_flags::pollin, no_op_handler));
|
||||
}
|
||||
|
||||
TEST_CASE("add null handler fails", "[active_poller]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::socket_t socket{context, zmq::socket_type::router};
|
||||
zmq::active_poller_t active_poller;
|
||||
zmq::active_poller_t::handler_type handler;
|
||||
CHECK_THROWS_AS(active_poller.add(socket, zmq::event_flags::pollin, handler),
|
||||
std::invalid_argument);
|
||||
}
|
||||
|
||||
#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 3, 0)
|
||||
// this behaviour was added by https://github.com/zeromq/libzmq/pull/3100
|
||||
TEST_CASE("add handler invalid events type", "[active_poller]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::socket_t socket{context, zmq::socket_type::router};
|
||||
zmq::active_poller_t active_poller;
|
||||
short invalid_events_type = 2 << 10;
|
||||
CHECK_THROWS_AS(
|
||||
active_poller.add(socket, static_cast<zmq::event_flags>(invalid_events_type),
|
||||
no_op_handler),
|
||||
zmq::error_t);
|
||||
CHECK(active_poller.empty());
|
||||
CHECK(0u == active_poller.size());
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_CASE("add handler twice throws", "[active_poller]")
|
||||
{
|
||||
common_server_client_setup s;
|
||||
|
||||
CHECK(s.client.send(zmq::message_t{}, zmq::send_flags::none));
|
||||
|
||||
zmq::active_poller_t active_poller;
|
||||
bool message_received = false;
|
||||
active_poller.add(
|
||||
s.server, zmq::event_flags::pollin,
|
||||
[&message_received](zmq::event_flags) { message_received = true; });
|
||||
CHECK_THROWS_ZMQ_ERROR(
|
||||
EINVAL, active_poller.add(s.server, zmq::event_flags::pollin, no_op_handler));
|
||||
CHECK(1 == active_poller.wait(std::chrono::milliseconds{-1}));
|
||||
CHECK(message_received); // handler unmodified
|
||||
}
|
||||
|
||||
TEST_CASE("wait with no handlers throws", "[active_poller]")
|
||||
{
|
||||
zmq::active_poller_t active_poller;
|
||||
CHECK_THROWS_ZMQ_ERROR(EFAULT,
|
||||
active_poller.wait(std::chrono::milliseconds{10}));
|
||||
}
|
||||
|
||||
TEST_CASE("remove unregistered throws", "[active_poller]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::socket_t socket{context, zmq::socket_type::router};
|
||||
zmq::active_poller_t active_poller;
|
||||
CHECK_THROWS_ZMQ_ERROR(EINVAL, active_poller.remove(socket));
|
||||
}
|
||||
|
||||
TEST_CASE("remove registered empty", "[active_poller]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::socket_t socket{context, zmq::socket_type::router};
|
||||
zmq::active_poller_t active_poller;
|
||||
active_poller.add(socket, zmq::event_flags::pollin, no_op_handler);
|
||||
CHECK_NOTHROW(active_poller.remove(socket));
|
||||
}
|
||||
|
||||
TEST_CASE("remove registered non empty", "[active_poller]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::socket_t socket{context, zmq::socket_type::router};
|
||||
zmq::active_poller_t active_poller;
|
||||
active_poller.add(socket, zmq::event_flags::pollin, no_op_handler);
|
||||
CHECK_NOTHROW(active_poller.remove(socket));
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
struct server_client_setup : common_server_client_setup
|
||||
{
|
||||
zmq::active_poller_t::handler_type handler = [&](zmq::event_flags e) {
|
||||
events = e;
|
||||
};
|
||||
|
||||
zmq::event_flags events = zmq::event_flags::none;
|
||||
};
|
||||
|
||||
const std::string hi_str = "Hi";
|
||||
|
||||
}
|
||||
|
||||
TEST_CASE("poll basic", "[active_poller]")
|
||||
{
|
||||
server_client_setup s;
|
||||
|
||||
CHECK_NOTHROW(s.client.send(zmq::message_t{hi_str}, zmq::send_flags::none));
|
||||
|
||||
zmq::active_poller_t active_poller;
|
||||
bool message_received = false;
|
||||
zmq::active_poller_t::handler_type handler =
|
||||
[&message_received](zmq::event_flags events) {
|
||||
CHECK(zmq::event_flags::none != (events & zmq::event_flags::pollin));
|
||||
message_received = true;
|
||||
};
|
||||
CHECK_NOTHROW(active_poller.add(s.server, zmq::event_flags::pollin, handler));
|
||||
CHECK(1 == active_poller.wait(std::chrono::milliseconds{-1}));
|
||||
CHECK(message_received);
|
||||
}
|
||||
|
||||
/// \todo this contains multiple test cases that should be split up
|
||||
TEST_CASE("client server", "[active_poller]")
|
||||
{
|
||||
const std::string send_msg = hi_str;
|
||||
|
||||
// Setup server and client
|
||||
server_client_setup s;
|
||||
|
||||
// Setup active_poller
|
||||
zmq::active_poller_t active_poller;
|
||||
zmq::event_flags events;
|
||||
zmq::active_poller_t::handler_type handler = [&](zmq::event_flags e) {
|
||||
if (zmq::event_flags::none != (e & zmq::event_flags::pollin)) {
|
||||
zmq::message_t zmq_msg;
|
||||
CHECK_NOTHROW(s.server.recv(zmq_msg)); // get message
|
||||
std::string recv_msg(zmq_msg.data<char>(), zmq_msg.size());
|
||||
CHECK(send_msg == recv_msg);
|
||||
} else if (zmq::event_flags::none != (e & ~zmq::event_flags::pollout)) {
|
||||
INFO("Unexpected event type " << static_cast<short>(events));
|
||||
REQUIRE(false);
|
||||
}
|
||||
events = e;
|
||||
};
|
||||
|
||||
CHECK_NOTHROW(active_poller.add(s.server, zmq::event_flags::pollin, handler));
|
||||
|
||||
// client sends message
|
||||
CHECK_NOTHROW(s.client.send(zmq::message_t{send_msg}, zmq::send_flags::none));
|
||||
|
||||
CHECK(1 == active_poller.wait(std::chrono::milliseconds{-1}));
|
||||
CHECK(events == zmq::event_flags::pollin);
|
||||
|
||||
// Re-add server socket with pollout flag
|
||||
CHECK_NOTHROW(active_poller.remove(s.server));
|
||||
CHECK_NOTHROW(active_poller.add(
|
||||
s.server, zmq::event_flags::pollin | zmq::event_flags::pollout, handler));
|
||||
CHECK(1 == active_poller.wait(std::chrono::milliseconds{-1}));
|
||||
CHECK(events == zmq::event_flags::pollout);
|
||||
}
|
||||
|
||||
TEST_CASE("add invalid socket throws", "[active_poller]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::active_poller_t active_poller;
|
||||
zmq::socket_t a{context, zmq::socket_type::router};
|
||||
zmq::socket_t b{std::move(a)};
|
||||
CHECK_THROWS_AS(active_poller.add(a, zmq::event_flags::pollin, no_op_handler),
|
||||
zmq::error_t);
|
||||
}
|
||||
|
||||
TEST_CASE("remove invalid socket throws", "[active_poller]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::socket_t socket{context, zmq::socket_type::router};
|
||||
zmq::active_poller_t active_poller;
|
||||
CHECK_NOTHROW(
|
||||
active_poller.add(socket, zmq::event_flags::pollin, no_op_handler));
|
||||
CHECK(1u == active_poller.size());
|
||||
std::vector<zmq::socket_t> sockets;
|
||||
sockets.emplace_back(std::move(socket));
|
||||
CHECK_THROWS_AS(active_poller.remove(socket), zmq::error_t);
|
||||
CHECK(1u == active_poller.size());
|
||||
}
|
||||
|
||||
TEST_CASE("wait on added empty handler", "[active_poller]")
|
||||
{
|
||||
server_client_setup s;
|
||||
CHECK_NOTHROW(s.client.send(zmq::message_t{hi_str}, zmq::send_flags::none));
|
||||
zmq::active_poller_t active_poller;
|
||||
CHECK_NOTHROW(
|
||||
active_poller.add(s.server, zmq::event_flags::pollin, no_op_handler));
|
||||
CHECK_NOTHROW(active_poller.wait(std::chrono::milliseconds{-1}));
|
||||
}
|
||||
|
||||
TEST_CASE("modify empty throws", "[active_poller]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::socket_t socket{context, zmq::socket_type::push};
|
||||
zmq::active_poller_t active_poller;
|
||||
CHECK_THROWS_AS(active_poller.modify(socket, zmq::event_flags::pollin),
|
||||
zmq::error_t);
|
||||
}
|
||||
|
||||
TEST_CASE("modify invalid socket throws", "[active_poller]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::socket_t a{context, zmq::socket_type::push};
|
||||
zmq::socket_t b{std::move(a)};
|
||||
zmq::active_poller_t active_poller;
|
||||
CHECK_THROWS_AS(active_poller.modify(a, zmq::event_flags::pollin),
|
||||
zmq::error_t);
|
||||
}
|
||||
|
||||
TEST_CASE("modify not added throws", "[active_poller]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::socket_t a{context, zmq::socket_type::push};
|
||||
zmq::socket_t b{context, zmq::socket_type::push};
|
||||
zmq::active_poller_t active_poller;
|
||||
CHECK_NOTHROW(active_poller.add(a, zmq::event_flags::pollin, no_op_handler));
|
||||
CHECK_THROWS_AS(active_poller.modify(b, zmq::event_flags::pollin),
|
||||
zmq::error_t);
|
||||
}
|
||||
|
||||
TEST_CASE("modify simple", "[active_poller]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::socket_t a{context, zmq::socket_type::push};
|
||||
zmq::active_poller_t active_poller;
|
||||
CHECK_NOTHROW(active_poller.add(a, zmq::event_flags::pollin, no_op_handler));
|
||||
CHECK_NOTHROW(
|
||||
active_poller.modify(a, zmq::event_flags::pollin | zmq::event_flags::pollout));
|
||||
}
|
||||
|
||||
TEST_CASE("poll client server", "[active_poller]")
|
||||
{
|
||||
// Setup server and client
|
||||
server_client_setup s;
|
||||
|
||||
// Setup active_poller
|
||||
zmq::active_poller_t active_poller;
|
||||
CHECK_NOTHROW(active_poller.add(s.server, zmq::event_flags::pollin, s.handler));
|
||||
|
||||
// client sends message
|
||||
CHECK_NOTHROW(s.client.send(zmq::message_t{hi_str}, zmq::send_flags::none));
|
||||
|
||||
// wait for message and verify events
|
||||
CHECK_NOTHROW(active_poller.wait(std::chrono::milliseconds{500}));
|
||||
CHECK(s.events == zmq::event_flags::pollin);
|
||||
|
||||
// Modify server socket with pollout flag
|
||||
CHECK_NOTHROW(active_poller.modify(s.server, zmq::event_flags::pollin
|
||||
| zmq::event_flags::pollout));
|
||||
CHECK(1 == active_poller.wait(std::chrono::milliseconds{500}));
|
||||
CHECK(s.events == (zmq::event_flags::pollin | zmq::event_flags::pollout));
|
||||
}
|
||||
|
||||
TEST_CASE("wait one return", "[active_poller]")
|
||||
{
|
||||
// Setup server and client
|
||||
server_client_setup s;
|
||||
|
||||
int count = 0;
|
||||
|
||||
// Setup active_poller
|
||||
zmq::active_poller_t active_poller;
|
||||
CHECK_NOTHROW(active_poller.add(s.server, zmq::event_flags::pollin,
|
||||
[&count](zmq::event_flags) { ++count; }));
|
||||
|
||||
// client sends message
|
||||
CHECK_NOTHROW(s.client.send(zmq::message_t{hi_str}, zmq::send_flags::none));
|
||||
|
||||
// wait for message and verify events
|
||||
CHECK(1 == active_poller.wait(std::chrono::milliseconds{500}));
|
||||
CHECK(1u == count);
|
||||
}
|
||||
|
||||
TEST_CASE("wait on move constructed active_poller", "[active_poller]")
|
||||
{
|
||||
server_client_setup s;
|
||||
CHECK_NOTHROW(s.client.send(zmq::message_t{hi_str}, zmq::send_flags::none));
|
||||
zmq::active_poller_t a;
|
||||
CHECK_NOTHROW(a.add(s.server, zmq::event_flags::pollin, no_op_handler));
|
||||
zmq::active_poller_t b{std::move(a)};
|
||||
CHECK(1u == b.size());
|
||||
CHECK(0u == a.size());
|
||||
CHECK_THROWS_ZMQ_ERROR(EFAULT, a.wait(std::chrono::milliseconds{10}));
|
||||
CHECK(b.wait(std::chrono::milliseconds{-1}));
|
||||
}
|
||||
|
||||
TEST_CASE("wait on move assigned active_poller", "[active_poller]")
|
||||
{
|
||||
server_client_setup s;
|
||||
CHECK_NOTHROW(s.client.send(zmq::message_t{hi_str}, zmq::send_flags::none));
|
||||
zmq::active_poller_t a;
|
||||
CHECK_NOTHROW(a.add(s.server, zmq::event_flags::pollin, no_op_handler));
|
||||
zmq::active_poller_t b;
|
||||
b = {std::move(a)};
|
||||
CHECK(1u == b.size());
|
||||
CHECK(0u == a.size());
|
||||
CHECK_THROWS_ZMQ_ERROR(EFAULT, a.wait(std::chrono::milliseconds{10}));
|
||||
CHECK(b.wait(std::chrono::milliseconds{-1}));
|
||||
}
|
||||
|
||||
TEST_CASE("received on move constructed active_poller", "[active_poller]")
|
||||
{
|
||||
// Setup server and client
|
||||
server_client_setup s;
|
||||
int count = 0;
|
||||
// Setup active_poller a
|
||||
zmq::active_poller_t a;
|
||||
CHECK_NOTHROW(a.add(s.server, zmq::event_flags::pollin,
|
||||
[&count](zmq::event_flags) { ++count; }));
|
||||
// client sends message
|
||||
CHECK_NOTHROW(s.client.send(zmq::message_t{hi_str}, zmq::send_flags::none));
|
||||
// wait for message and verify it is received
|
||||
CHECK(1 == a.wait(std::chrono::milliseconds{500}));
|
||||
CHECK(1u == count);
|
||||
// Move construct active_poller b
|
||||
zmq::active_poller_t b{std::move(a)};
|
||||
// client sends message again
|
||||
CHECK_NOTHROW(s.client.send(zmq::message_t{hi_str}, zmq::send_flags::none));
|
||||
// wait for message and verify it is received
|
||||
CHECK(1 == b.wait(std::chrono::milliseconds{500}));
|
||||
CHECK(2u == count);
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("remove from handler", "[active_poller]")
|
||||
{
|
||||
constexpr size_t ITER_NO = 10;
|
||||
|
||||
// Setup servers and clients
|
||||
std::vector<server_client_setup> setup_list;
|
||||
for (size_t i = 0; i < ITER_NO; ++i)
|
||||
setup_list.emplace_back(server_client_setup{});
|
||||
|
||||
// Setup active_poller
|
||||
zmq::active_poller_t active_poller;
|
||||
int count = 0;
|
||||
for (size_t i = 0; i < ITER_NO; ++i) {
|
||||
CHECK_NOTHROW(active_poller.add(
|
||||
setup_list[i].server, zmq::event_flags::pollin,
|
||||
[&, i](zmq::event_flags events) {
|
||||
CHECK(events == zmq::event_flags::pollin);
|
||||
active_poller.remove(setup_list[ITER_NO - i - 1].server);
|
||||
CHECK((ITER_NO - i - 1) == active_poller.size());
|
||||
}));
|
||||
++count;
|
||||
}
|
||||
CHECK(ITER_NO == active_poller.size());
|
||||
// Clients send messages
|
||||
for (auto &s : setup_list) {
|
||||
CHECK_NOTHROW(s.client.send(zmq::message_t{hi_str}, zmq::send_flags::none));
|
||||
}
|
||||
|
||||
// Wait for all servers to receive a message
|
||||
for (auto &s : setup_list) {
|
||||
zmq::pollitem_t items[] = {{s.server, 0, ZMQ_POLLIN, 0}};
|
||||
zmq::poll(&items[0], 1);
|
||||
}
|
||||
|
||||
// Fire all handlers in one wait
|
||||
CHECK(ITER_NO == active_poller.wait(std::chrono::milliseconds{-1}));
|
||||
CHECK(ITER_NO == count);
|
||||
}
|
||||
|
||||
#endif
|
||||
306
libs/cppzmq/tests/buffer.cpp
Normal file
306
libs/cppzmq/tests/buffer.cpp
Normal file
@@ -0,0 +1,306 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <zmq.hpp>
|
||||
|
||||
#ifdef ZMQ_CPP17
|
||||
static_assert(std::is_nothrow_swappable_v<zmq::const_buffer>);
|
||||
static_assert(std::is_nothrow_swappable_v<zmq::mutable_buffer>);
|
||||
static_assert(std::is_trivially_copyable_v<zmq::const_buffer>);
|
||||
static_assert(std::is_trivially_copyable_v<zmq::mutable_buffer>);
|
||||
#endif
|
||||
|
||||
#ifdef ZMQ_CPP11
|
||||
|
||||
using BT = int16_t;
|
||||
|
||||
TEST_CASE("buffer default ctor", "[buffer]")
|
||||
{
|
||||
constexpr zmq::mutable_buffer mb;
|
||||
constexpr zmq::const_buffer cb;
|
||||
CHECK(mb.size() == 0);
|
||||
CHECK(mb.data() == nullptr);
|
||||
CHECK(cb.size() == 0);
|
||||
CHECK(cb.data() == nullptr);
|
||||
}
|
||||
|
||||
TEST_CASE("buffer data ctor", "[buffer]")
|
||||
{
|
||||
std::vector<BT> v(10);
|
||||
zmq::const_buffer cb(v.data(), v.size() * sizeof(BT));
|
||||
CHECK(cb.size() == v.size() * sizeof(BT));
|
||||
CHECK(cb.data() == v.data());
|
||||
zmq::mutable_buffer mb(v.data(), v.size() * sizeof(BT));
|
||||
CHECK(mb.size() == v.size() * sizeof(BT));
|
||||
CHECK(mb.data() == v.data());
|
||||
zmq::const_buffer from_mut = mb;
|
||||
CHECK(mb.size() == from_mut.size());
|
||||
CHECK(mb.data() == from_mut.data());
|
||||
const auto cmb = mb;
|
||||
static_assert(std::is_same<decltype(cmb.data()), void *>::value, "");
|
||||
|
||||
constexpr const void *cp = nullptr;
|
||||
constexpr void *p = nullptr;
|
||||
constexpr zmq::const_buffer cecb = zmq::buffer(p, 0);
|
||||
constexpr zmq::mutable_buffer cemb = zmq::buffer(p, 0);
|
||||
CHECK(cecb.data() == nullptr);
|
||||
CHECK(cemb.data() == nullptr);
|
||||
}
|
||||
|
||||
TEST_CASE("const_buffer operator+", "[buffer]")
|
||||
{
|
||||
std::vector<BT> v(10);
|
||||
zmq::const_buffer cb(v.data(), v.size() * sizeof(BT));
|
||||
const size_t shift = 4;
|
||||
auto shifted = cb + shift;
|
||||
CHECK(shifted.size() == v.size() * sizeof(BT) - shift);
|
||||
CHECK(shifted.data() == v.data() + shift / sizeof(BT));
|
||||
auto shifted2 = shift + cb;
|
||||
CHECK(shifted.size() == shifted2.size());
|
||||
CHECK(shifted.data() == shifted2.data());
|
||||
auto cbinp = cb;
|
||||
cbinp += shift;
|
||||
CHECK(shifted.size() == cbinp.size());
|
||||
CHECK(shifted.data() == cbinp.data());
|
||||
}
|
||||
|
||||
TEST_CASE("mutable_buffer operator+", "[buffer]")
|
||||
{
|
||||
std::vector<BT> v(10);
|
||||
zmq::mutable_buffer mb(v.data(), v.size() * sizeof(BT));
|
||||
const size_t shift = 4;
|
||||
auto shifted = mb + shift;
|
||||
CHECK(shifted.size() == v.size() * sizeof(BT) - shift);
|
||||
CHECK(shifted.data() == v.data() + shift / sizeof(BT));
|
||||
auto shifted2 = shift + mb;
|
||||
CHECK(shifted.size() == shifted2.size());
|
||||
CHECK(shifted.data() == shifted2.data());
|
||||
auto mbinp = mb;
|
||||
mbinp += shift;
|
||||
CHECK(shifted.size() == mbinp.size());
|
||||
CHECK(shifted.data() == mbinp.data());
|
||||
}
|
||||
|
||||
TEST_CASE("mutable_buffer creation basic", "[buffer]")
|
||||
{
|
||||
std::vector<BT> v(10);
|
||||
zmq::mutable_buffer mb(v.data(), v.size() * sizeof(BT));
|
||||
zmq::mutable_buffer mb2 = zmq::buffer(v.data(), v.size() * sizeof(BT));
|
||||
CHECK(mb.data() == mb2.data());
|
||||
CHECK(mb.size() == mb2.size());
|
||||
zmq::mutable_buffer mb3 = zmq::buffer(mb);
|
||||
CHECK(mb.data() == mb3.data());
|
||||
CHECK(mb.size() == mb3.size());
|
||||
zmq::mutable_buffer mb4 = zmq::buffer(mb, 10 * v.size() * sizeof(BT));
|
||||
CHECK(mb.data() == mb4.data());
|
||||
CHECK(mb.size() == mb4.size());
|
||||
zmq::mutable_buffer mb5 = zmq::buffer(mb, 4);
|
||||
CHECK(mb.data() == mb5.data());
|
||||
CHECK(4 == mb5.size());
|
||||
}
|
||||
|
||||
TEST_CASE("const_buffer creation basic", "[buffer]")
|
||||
{
|
||||
const std::vector<BT> v(10);
|
||||
zmq::const_buffer cb(v.data(), v.size() * sizeof(BT));
|
||||
zmq::const_buffer cb2 = zmq::buffer(v.data(), v.size() * sizeof(BT));
|
||||
CHECK(cb.data() == cb2.data());
|
||||
CHECK(cb.size() == cb2.size());
|
||||
zmq::const_buffer cb3 = zmq::buffer(cb);
|
||||
CHECK(cb.data() == cb3.data());
|
||||
CHECK(cb.size() == cb3.size());
|
||||
zmq::const_buffer cb4 = zmq::buffer(cb, 10 * v.size() * sizeof(BT));
|
||||
CHECK(cb.data() == cb4.data());
|
||||
CHECK(cb.size() == cb4.size());
|
||||
zmq::const_buffer cb5 = zmq::buffer(cb, 4);
|
||||
CHECK(cb.data() == cb5.data());
|
||||
CHECK(4 == cb5.size());
|
||||
}
|
||||
|
||||
TEST_CASE("mutable_buffer creation C array", "[buffer]")
|
||||
{
|
||||
BT d[10] = {};
|
||||
zmq::mutable_buffer b = zmq::buffer(d);
|
||||
CHECK(b.size() == 10 * sizeof(BT));
|
||||
CHECK(b.data() == static_cast<BT*>(d));
|
||||
zmq::const_buffer b2 = zmq::buffer(d, 4);
|
||||
CHECK(b2.size() == 4);
|
||||
CHECK(b2.data() == static_cast<BT*>(d));
|
||||
}
|
||||
|
||||
TEST_CASE("const_buffer creation C array", "[buffer]")
|
||||
{
|
||||
const BT d[10] = {};
|
||||
zmq::const_buffer b = zmq::buffer(d);
|
||||
CHECK(b.size() == 10 * sizeof(BT));
|
||||
CHECK(b.data() == static_cast<const BT*>(d));
|
||||
zmq::const_buffer b2 = zmq::buffer(d, 4);
|
||||
CHECK(b2.size() == 4);
|
||||
CHECK(b2.data() == static_cast<const BT*>(d));
|
||||
}
|
||||
|
||||
TEST_CASE("mutable_buffer creation array", "[buffer]")
|
||||
{
|
||||
std::array<BT, 10> d = {};
|
||||
zmq::mutable_buffer b = zmq::buffer(d);
|
||||
CHECK(b.size() == d.size() * sizeof(BT));
|
||||
CHECK(b.data() == d.data());
|
||||
zmq::mutable_buffer b2 = zmq::buffer(d, 4);
|
||||
CHECK(b2.size() == 4);
|
||||
CHECK(b2.data() == d.data());
|
||||
}
|
||||
|
||||
TEST_CASE("const_buffer creation array", "[buffer]")
|
||||
{
|
||||
const std::array<BT, 10> d = {};
|
||||
zmq::const_buffer b = zmq::buffer(d);
|
||||
CHECK(b.size() == d.size() * sizeof(BT));
|
||||
CHECK(b.data() == d.data());
|
||||
zmq::const_buffer b2 = zmq::buffer(d, 4);
|
||||
CHECK(b2.size() == 4);
|
||||
CHECK(b2.data() == d.data());
|
||||
}
|
||||
|
||||
TEST_CASE("const_buffer creation array 2", "[buffer]")
|
||||
{
|
||||
std::array<const BT, 10> d = {{}};
|
||||
zmq::const_buffer b = zmq::buffer(d);
|
||||
CHECK(b.size() == d.size() * sizeof(BT));
|
||||
CHECK(b.data() == d.data());
|
||||
zmq::const_buffer b2 = zmq::buffer(d, 4);
|
||||
CHECK(b2.size() == 4);
|
||||
CHECK(b2.data() == d.data());
|
||||
}
|
||||
|
||||
TEST_CASE("mutable_buffer creation vector", "[buffer]")
|
||||
{
|
||||
std::vector<BT> d(10);
|
||||
zmq::mutable_buffer b = zmq::buffer(d);
|
||||
CHECK(b.size() == d.size() * sizeof(BT));
|
||||
CHECK(b.data() == d.data());
|
||||
zmq::mutable_buffer b2 = zmq::buffer(d, 4);
|
||||
CHECK(b2.size() == 4);
|
||||
CHECK(b2.data() == d.data());
|
||||
d.clear();
|
||||
b = zmq::buffer(d);
|
||||
CHECK(b.size() == 0);
|
||||
CHECK(b.data() == nullptr);
|
||||
}
|
||||
|
||||
TEST_CASE("const_buffer creation vector", "[buffer]")
|
||||
{
|
||||
std::vector<BT> d(10);
|
||||
zmq::const_buffer b = zmq::buffer(static_cast<const std::vector<BT> &>(d));
|
||||
CHECK(b.size() == d.size() * sizeof(BT));
|
||||
CHECK(b.data() == d.data());
|
||||
zmq::const_buffer b2 = zmq::buffer(static_cast<const std::vector<BT> &>(d), 4);
|
||||
CHECK(b2.size() == 4);
|
||||
CHECK(b2.data() == d.data());
|
||||
d.clear();
|
||||
b = zmq::buffer(static_cast<const std::vector<BT> &>(d));
|
||||
CHECK(b.size() == 0);
|
||||
CHECK(b.data() == nullptr);
|
||||
}
|
||||
|
||||
TEST_CASE("const_buffer creation string", "[buffer]")
|
||||
{
|
||||
const std::wstring d(10, L'a');
|
||||
zmq::const_buffer b = zmq::buffer(d);
|
||||
CHECK(b.size() == d.size() * sizeof(wchar_t));
|
||||
CHECK(b.data() == d.data());
|
||||
zmq::const_buffer b2 = zmq::buffer(d, 4);
|
||||
CHECK(b2.size() == 4);
|
||||
CHECK(b2.data() == d.data());
|
||||
}
|
||||
|
||||
TEST_CASE("mutable_buffer creation string", "[buffer]")
|
||||
{
|
||||
std::wstring d(10, L'a');
|
||||
zmq::mutable_buffer b = zmq::buffer(d);
|
||||
CHECK(b.size() == d.size() * sizeof(wchar_t));
|
||||
CHECK(b.data() == d.data());
|
||||
zmq::mutable_buffer b2 = zmq::buffer(d, 4);
|
||||
CHECK(b2.size() == 4);
|
||||
CHECK(b2.data() == d.data());
|
||||
}
|
||||
|
||||
#if CPPZMQ_HAS_STRING_VIEW
|
||||
TEST_CASE("const_buffer creation string_view", "[buffer]")
|
||||
{
|
||||
std::wstring dstr(10, L'a');
|
||||
std::wstring_view d = dstr;
|
||||
zmq::const_buffer b = zmq::buffer(d);
|
||||
CHECK(b.size() == d.size() * sizeof(wchar_t));
|
||||
CHECK(b.data() == d.data());
|
||||
zmq::const_buffer b2 = zmq::buffer(d, 4);
|
||||
CHECK(b2.size() == 4);
|
||||
CHECK(b2.data() == d.data());
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_CASE("const_buffer creation with str_buffer", "[buffer]")
|
||||
{
|
||||
const wchar_t wd[10] = {};
|
||||
zmq::const_buffer b = zmq::str_buffer(wd);
|
||||
CHECK(b.size() == 9 * sizeof(wchar_t));
|
||||
CHECK(b.data() == static_cast<const wchar_t*>(wd));
|
||||
|
||||
zmq::const_buffer b2_null = zmq::buffer("hello");
|
||||
constexpr zmq::const_buffer b2 = zmq::str_buffer("hello");
|
||||
CHECK(b2_null.size() == 6);
|
||||
CHECK(b2.size() == 5);
|
||||
CHECK(std::string(static_cast<const char*>(b2.data()), b2.size()) == "hello");
|
||||
}
|
||||
|
||||
TEST_CASE("const_buffer creation with zbuf string literal char", "[buffer]")
|
||||
{
|
||||
using namespace zmq::literals;
|
||||
constexpr zmq::const_buffer b = "hello"_zbuf;
|
||||
CHECK(b.size() == 5);
|
||||
CHECK(std::memcmp(b.data(), "hello", b.size()) == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("const_buffer creation with zbuf string literal wchar_t", "[buffer]")
|
||||
{
|
||||
using namespace zmq::literals;
|
||||
constexpr zmq::const_buffer b = L"hello"_zbuf;
|
||||
CHECK(b.size() == 5 * sizeof(wchar_t));
|
||||
CHECK(std::memcmp(b.data(), L"hello", b.size()) == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("const_buffer creation with zbuf string literal char16_t", "[buffer]")
|
||||
{
|
||||
using namespace zmq::literals;
|
||||
constexpr zmq::const_buffer b = u"hello"_zbuf;
|
||||
CHECK(b.size() == 5 * sizeof(char16_t));
|
||||
CHECK(std::memcmp(b.data(), u"hello", b.size()) == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("const_buffer creation with zbuf string literal char32_t", "[buffer]")
|
||||
{
|
||||
using namespace zmq::literals;
|
||||
constexpr zmq::const_buffer b = U"hello"_zbuf;
|
||||
CHECK(b.size() == 5 * sizeof(char32_t));
|
||||
CHECK(std::memcmp(b.data(), U"hello", b.size()) == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("buffer of structs", "[buffer]")
|
||||
{
|
||||
struct some_pod
|
||||
{
|
||||
int64_t val;
|
||||
char arr[8];
|
||||
};
|
||||
struct some_non_pod
|
||||
{
|
||||
int64_t val;
|
||||
char arr[8];
|
||||
std::vector<int> s; // not trivially copyable
|
||||
};
|
||||
static_assert(zmq::detail::is_pod_like<some_pod>::value, "");
|
||||
static_assert(!zmq::detail::is_pod_like<some_non_pod>::value, "");
|
||||
std::array<some_pod, 1> d;
|
||||
zmq::mutable_buffer b = zmq::buffer(d);
|
||||
CHECK(b.size() == d.size() * sizeof(some_pod));
|
||||
CHECK(b.data() == d.data());
|
||||
}
|
||||
|
||||
#endif
|
||||
210
libs/cppzmq/tests/codec_multipart.cpp
Normal file
210
libs/cppzmq/tests/codec_multipart.cpp
Normal file
@@ -0,0 +1,210 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <zmq_addon.hpp>
|
||||
|
||||
#ifdef ZMQ_CPP11
|
||||
|
||||
TEST_CASE("multipart codec empty", "[codec_multipart]")
|
||||
{
|
||||
using namespace zmq;
|
||||
|
||||
multipart_t mmsg;
|
||||
message_t msg = mmsg.encode();
|
||||
CHECK(msg.size() == 0);
|
||||
|
||||
multipart_t mmsg2;
|
||||
mmsg2.decode_append(msg);
|
||||
CHECK(mmsg2.size() == 0);
|
||||
|
||||
}
|
||||
|
||||
TEST_CASE("multipart codec small", "[codec_multipart]")
|
||||
{
|
||||
using namespace zmq;
|
||||
|
||||
multipart_t mmsg;
|
||||
mmsg.addstr("Hello World");
|
||||
message_t msg = mmsg.encode();
|
||||
CHECK(msg.size() == 1 + 11); // small size packing
|
||||
|
||||
mmsg.addstr("Second frame");
|
||||
msg = mmsg.encode();
|
||||
CHECK(msg.size() == 1 + 11 + 1 + 12);
|
||||
|
||||
multipart_t mmsg2;
|
||||
mmsg2.decode_append(msg);
|
||||
CHECK(mmsg2.size() == 2);
|
||||
std::string part0 = mmsg2[0].to_string();
|
||||
CHECK(part0 == "Hello World");
|
||||
CHECK(mmsg2[1].to_string() == "Second frame");
|
||||
}
|
||||
|
||||
TEST_CASE("multipart codec big", "[codec_multipart]")
|
||||
{
|
||||
using namespace zmq;
|
||||
|
||||
message_t big(495); // large size packing
|
||||
big.data<char>()[0] = 'X';
|
||||
|
||||
multipart_t mmsg;
|
||||
mmsg.pushmem(big.data(), big.size());
|
||||
message_t msg = mmsg.encode();
|
||||
CHECK(msg.size() == 5 + 495);
|
||||
CHECK(msg.data<unsigned char>()[0] == std::numeric_limits<uint8_t>::max());
|
||||
CHECK(msg.data<unsigned char>()[5] == 'X');
|
||||
|
||||
CHECK(mmsg.size() == 1);
|
||||
mmsg.decode_append(msg);
|
||||
CHECK(mmsg.size() == 2);
|
||||
CHECK(mmsg[0].data<char>()[0] == 'X');
|
||||
}
|
||||
|
||||
TEST_CASE("multipart codec decode bad data overflow", "[codec_multipart]")
|
||||
{
|
||||
using namespace zmq;
|
||||
|
||||
char bad_data[3] = {5, 'h', 'i'};
|
||||
message_t wrong_size(bad_data, 3);
|
||||
CHECK(wrong_size.size() == 3);
|
||||
CHECK(wrong_size.data<char>()[0] == 5);
|
||||
|
||||
CHECK_THROWS_AS(
|
||||
multipart_t::decode(wrong_size),
|
||||
std::out_of_range);
|
||||
}
|
||||
|
||||
TEST_CASE("multipart codec decode bad data extra data", "[codec_multipart]")
|
||||
{
|
||||
using namespace zmq;
|
||||
|
||||
char bad_data[3] = {1, 'h', 'i'};
|
||||
message_t wrong_size(bad_data, 3);
|
||||
CHECK(wrong_size.size() == 3);
|
||||
CHECK(wrong_size.data<char>()[0] == 1);
|
||||
|
||||
CHECK_THROWS_AS(
|
||||
multipart_t::decode(wrong_size),
|
||||
std::out_of_range);
|
||||
}
|
||||
|
||||
|
||||
// After exercising it, this test is disabled over concern of running
|
||||
// on hosts which lack enough free memory to allow the absurdly large
|
||||
// message part to be allocated.
|
||||
#if 0
|
||||
TEST_CASE("multipart codec encode too big", "[codec_multipart]")
|
||||
{
|
||||
using namespace zmq;
|
||||
|
||||
const size_t too_big_size = 1L + std::numeric_limits<uint32_t>::max();
|
||||
CHECK(too_big_size > std::numeric_limits<uint32_t>::max());
|
||||
char* too_big_data = new char[too_big_size];
|
||||
multipart_t mmsg(too_big_data, too_big_size);
|
||||
delete [] too_big_data;
|
||||
|
||||
CHECK(mmsg.size() == 1);
|
||||
CHECK(mmsg[0].size() > std::numeric_limits<uint32_t>::max());
|
||||
|
||||
CHECK_THROWS_AS(
|
||||
mmsg.encode(),
|
||||
std::range_error);
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_CASE("multipart codec free function with vector of message_t", "[codec_multipart]")
|
||||
{
|
||||
using namespace zmq;
|
||||
std::vector<message_t> parts;
|
||||
parts.emplace_back("Hello", 5);
|
||||
parts.emplace_back("World",5);
|
||||
auto msg = encode(parts);
|
||||
CHECK(msg.size() == 1 + 5 + 1 + 5 );
|
||||
CHECK(msg.data<unsigned char>()[0] == 5);
|
||||
CHECK(msg.data<unsigned char>()[1] == 'H');
|
||||
CHECK(msg.data<unsigned char>()[6] == 5);
|
||||
CHECK(msg.data<unsigned char>()[7] == 'W');
|
||||
|
||||
std::vector<message_t> parts2;
|
||||
decode(msg, std::back_inserter(parts2));
|
||||
CHECK(parts.size() == 2);
|
||||
CHECK(parts[0].size() == 5);
|
||||
CHECK(parts[1].size() == 5);
|
||||
}
|
||||
|
||||
TEST_CASE("multipart codec free function with vector of const_buffer", "[codec_multipart]")
|
||||
{
|
||||
using namespace zmq;
|
||||
std::vector<const_buffer> parts;
|
||||
parts.emplace_back("Hello", 5);
|
||||
parts.emplace_back("World",5);
|
||||
auto msg = encode(parts);
|
||||
CHECK(msg.size() == 1 + 5 + 1 + 5 );
|
||||
CHECK(msg.data<unsigned char>()[0] == 5);
|
||||
CHECK(msg.data<unsigned char>()[1] == 'H');
|
||||
CHECK(msg.data<unsigned char>()[6] == 5);
|
||||
CHECK(msg.data<unsigned char>()[7] == 'W');
|
||||
|
||||
std::vector<message_t> parts2;
|
||||
decode(msg, std::back_inserter(parts2));
|
||||
CHECK(parts.size() == 2);
|
||||
CHECK(parts[0].size() == 5);
|
||||
CHECK(parts[1].size() == 5);
|
||||
}
|
||||
|
||||
TEST_CASE("multipart codec free function with vector of mutable_buffer", "[codec_multipart]")
|
||||
{
|
||||
using namespace zmq;
|
||||
std::vector<mutable_buffer> parts;
|
||||
char hello[6] = "Hello";
|
||||
parts.emplace_back(hello, 5);
|
||||
char world[6] = "World";
|
||||
parts.emplace_back(world,5);
|
||||
auto msg = encode(parts);
|
||||
CHECK(msg.size() == 1 + 5 + 1 + 5 );
|
||||
CHECK(msg.data<unsigned char>()[0] == 5);
|
||||
CHECK(msg.data<unsigned char>()[1] == 'H');
|
||||
CHECK(msg.data<unsigned char>()[6] == 5);
|
||||
CHECK(msg.data<unsigned char>()[7] == 'W');
|
||||
|
||||
std::vector<message_t> parts2;
|
||||
decode(msg, std::back_inserter(parts2));
|
||||
CHECK(parts.size() == 2);
|
||||
CHECK(parts[0].size() == 5);
|
||||
CHECK(parts[1].size() == 5);
|
||||
}
|
||||
|
||||
TEST_CASE("multipart codec free function with multipart_t", "[codec_multipart]")
|
||||
{
|
||||
using namespace zmq;
|
||||
multipart_t mmsg;
|
||||
mmsg.addstr("Hello");
|
||||
mmsg.addstr("World");
|
||||
auto msg = encode(mmsg);
|
||||
CHECK(msg.size() == 1 + 5 + 1 + 5);
|
||||
CHECK(msg.data<unsigned char>()[0] == 5);
|
||||
CHECK(msg.data<unsigned char>()[1] == 'H');
|
||||
CHECK(msg.data<unsigned char>()[6] == 5);
|
||||
CHECK(msg.data<unsigned char>()[7] == 'W');
|
||||
|
||||
multipart_t mmsg2;
|
||||
decode(msg, std::back_inserter(mmsg2));
|
||||
CHECK(mmsg2.size() == 2);
|
||||
CHECK(mmsg2[0].size() == 5);
|
||||
CHECK(mmsg2[1].size() == 5);
|
||||
}
|
||||
|
||||
TEST_CASE("multipart codec static method decode to multipart_t", "[codec_multipart]")
|
||||
{
|
||||
using namespace zmq;
|
||||
multipart_t mmsg;
|
||||
mmsg.addstr("Hello");
|
||||
mmsg.addstr("World");
|
||||
auto msg = encode(mmsg);
|
||||
|
||||
auto mmsg2 = multipart_t::decode(msg);
|
||||
CHECK(mmsg2.size() == 2);
|
||||
CHECK(mmsg2[0].size() == 5);
|
||||
CHECK(mmsg2[1].size() == 5);
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
84
libs/cppzmq/tests/context.cpp
Normal file
84
libs/cppzmq/tests/context.cpp
Normal file
@@ -0,0 +1,84 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <zmq.hpp>
|
||||
|
||||
#if (__cplusplus >= 201703L)
|
||||
static_assert(std::is_nothrow_swappable<zmq::context_t>::value,
|
||||
"context_t should be nothrow swappable");
|
||||
#endif
|
||||
|
||||
TEST_CASE("context construct default and destroy", "[context]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
}
|
||||
|
||||
TEST_CASE("context create, close and destroy", "[context]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
context.close();
|
||||
CHECK(NULL == context.handle());
|
||||
}
|
||||
|
||||
TEST_CASE("context shutdown", "[context]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
context.shutdown();
|
||||
CHECK(NULL != context.handle());
|
||||
context.close();
|
||||
CHECK(NULL == context.handle());
|
||||
}
|
||||
|
||||
TEST_CASE("context shutdown again", "[context]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
context.shutdown();
|
||||
context.shutdown();
|
||||
CHECK(NULL != context.handle());
|
||||
context.close();
|
||||
CHECK(NULL == context.handle());
|
||||
}
|
||||
|
||||
#ifdef ZMQ_CPP11
|
||||
TEST_CASE("context swap", "[context]")
|
||||
{
|
||||
zmq::context_t context1;
|
||||
zmq::context_t context2;
|
||||
using std::swap;
|
||||
swap(context1, context2);
|
||||
}
|
||||
|
||||
TEST_CASE("context - use socket after shutdown", "[context]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::socket_t sock(context, zmq::socket_type::rep);
|
||||
context.shutdown();
|
||||
try
|
||||
{
|
||||
sock.connect("inproc://test");
|
||||
zmq::message_t msg;
|
||||
(void)sock.recv(msg, zmq::recv_flags::dontwait);
|
||||
REQUIRE(false);
|
||||
}
|
||||
catch (const zmq::error_t& e)
|
||||
{
|
||||
REQUIRE(e.num() == ETERM);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("context set/get options", "[context]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
#if defined(ZMQ_BLOCKY) && defined(ZMQ_IO_THREADS)
|
||||
context.set(zmq::ctxopt::blocky, false);
|
||||
context.set(zmq::ctxopt::io_threads, 5);
|
||||
CHECK(context.get(zmq::ctxopt::io_threads) == 5);
|
||||
#endif
|
||||
|
||||
CHECK_THROWS_AS(
|
||||
context.set(static_cast<zmq::ctxopt>(-42), 5),
|
||||
zmq::error_t);
|
||||
|
||||
CHECK_THROWS_AS(
|
||||
context.get(static_cast<zmq::ctxopt>(-42)),
|
||||
zmq::error_t);
|
||||
}
|
||||
#endif
|
||||
246
libs/cppzmq/tests/message.cpp
Normal file
246
libs/cppzmq/tests/message.cpp
Normal file
@@ -0,0 +1,246 @@
|
||||
#define CATCH_CONFIG_MAIN
|
||||
#include <catch2/catch.hpp>
|
||||
#include <zmq.hpp>
|
||||
|
||||
#if defined(ZMQ_CPP11)
|
||||
static_assert(!std::is_copy_constructible<zmq::message_t>::value,
|
||||
"message_t should not be copy-constructible");
|
||||
static_assert(!std::is_copy_assignable<zmq::message_t>::value,
|
||||
"message_t should not be copy-assignable");
|
||||
#endif
|
||||
#if (__cplusplus >= 201703L)
|
||||
static_assert(std::is_nothrow_swappable<zmq::message_t>::value,
|
||||
"message_t should be nothrow swappable");
|
||||
#endif
|
||||
|
||||
TEST_CASE("message default constructed", "[message]")
|
||||
{
|
||||
const zmq::message_t message;
|
||||
CHECK(0u == message.size());
|
||||
CHECK(message.empty());
|
||||
}
|
||||
|
||||
#ifdef ZMQ_CPP11
|
||||
TEST_CASE("message swap", "[message]")
|
||||
{
|
||||
const std::string data = "foo";
|
||||
zmq::message_t message1;
|
||||
zmq::message_t message2(data.data(), data.size());
|
||||
using std::swap;
|
||||
swap(message1, message2);
|
||||
CHECK(message1.size() == data.size());
|
||||
CHECK(message2.size() == 0);
|
||||
swap(message1, message2);
|
||||
CHECK(message1.size() == 0);
|
||||
CHECK(message2.size() == data.size());
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
const char *const data = "Hi";
|
||||
}
|
||||
|
||||
TEST_CASE("message constructor with iterators", "[message]")
|
||||
{
|
||||
const std::string hi(data);
|
||||
const zmq::message_t hi_msg(hi.begin(), hi.end());
|
||||
CHECK(2u == hi_msg.size());
|
||||
CHECK(0 == memcmp(data, hi_msg.data(), 2));
|
||||
}
|
||||
|
||||
TEST_CASE("message constructor with size", "[message]")
|
||||
{
|
||||
const zmq::message_t msg(5);
|
||||
CHECK(msg.size() == 5);
|
||||
}
|
||||
|
||||
TEST_CASE("message constructor with buffer and size", "[message]")
|
||||
{
|
||||
const std::string hi(data);
|
||||
const zmq::message_t hi_msg(hi.data(), hi.size());
|
||||
CHECK(2u == hi_msg.size());
|
||||
CHECK(0 == memcmp(data, hi_msg.data(), 2));
|
||||
}
|
||||
|
||||
TEST_CASE("message constructor with char array", "[message]")
|
||||
{
|
||||
const zmq::message_t hi_msg(data, strlen(data));
|
||||
CHECK(2u == hi_msg.size());
|
||||
CHECK(0 == memcmp(data, hi_msg.data(), 2));
|
||||
}
|
||||
|
||||
#if defined(ZMQ_CPP11) && !defined(ZMQ_CPP11_PARTIAL)
|
||||
TEST_CASE("message constructor with container - deprecated", "[message]")
|
||||
{
|
||||
zmq::message_t hi_msg("Hi"); // deprecated
|
||||
REQUIRE(3u == hi_msg.size());
|
||||
CHECK(0 == memcmp(data, hi_msg.data(), 3));
|
||||
}
|
||||
|
||||
TEST_CASE("message constructor with container of trivial data", "[message]")
|
||||
{
|
||||
int buf[3] = {1, 2, 3};
|
||||
zmq::message_t msg(buf);
|
||||
REQUIRE(sizeof(buf) == msg.size());
|
||||
CHECK(0 == memcmp(buf, msg.data(), msg.size()));
|
||||
}
|
||||
|
||||
TEST_CASE("message constructor with strings", "[message]")
|
||||
{
|
||||
SECTION("string")
|
||||
{
|
||||
const std::string hi(data);
|
||||
zmq::message_t hi_msg(hi);
|
||||
CHECK(2u == hi_msg.size());
|
||||
CHECK(0 == memcmp(data, hi_msg.data(), 2));
|
||||
}
|
||||
#if CPPZMQ_HAS_STRING_VIEW
|
||||
SECTION("string_view")
|
||||
{
|
||||
const std::string_view hi(data);
|
||||
zmq::message_t hi_msg(hi);
|
||||
CHECK(2u == hi_msg.size());
|
||||
CHECK(0 == memcmp(data, hi_msg.data(), 2));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ZMQ_HAS_RVALUE_REFS
|
||||
TEST_CASE("message move constructor", "[message]")
|
||||
{
|
||||
zmq::message_t hi_msg(zmq::message_t(data, strlen(data)));
|
||||
}
|
||||
|
||||
TEST_CASE("message assign move empty before", "[message]")
|
||||
{
|
||||
zmq::message_t hi_msg;
|
||||
hi_msg = zmq::message_t(data, strlen(data));
|
||||
CHECK(2u == hi_msg.size());
|
||||
CHECK(0 == memcmp(data, hi_msg.data(), 2));
|
||||
}
|
||||
|
||||
TEST_CASE("message assign move empty after", "[message]")
|
||||
{
|
||||
zmq::message_t hi_msg(data, strlen(data));
|
||||
CHECK(!hi_msg.empty());
|
||||
hi_msg = zmq::message_t();
|
||||
CHECK(0u == hi_msg.size());
|
||||
CHECK(hi_msg.empty());
|
||||
}
|
||||
|
||||
TEST_CASE("message assign move empty before and after", "[message]")
|
||||
{
|
||||
zmq::message_t hi_msg;
|
||||
hi_msg = zmq::message_t();
|
||||
CHECK(0u == hi_msg.size());
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_CASE("message equality self", "[message]")
|
||||
{
|
||||
const zmq::message_t hi_msg(data, strlen(data));
|
||||
CHECK(hi_msg == hi_msg);
|
||||
}
|
||||
|
||||
TEST_CASE("message equality equal", "[message]")
|
||||
{
|
||||
const zmq::message_t hi_msg_a(data, strlen(data));
|
||||
const zmq::message_t hi_msg_b(data, strlen(data));
|
||||
CHECK(hi_msg_a == hi_msg_b);
|
||||
}
|
||||
|
||||
TEST_CASE("message equality equal empty", "[message]")
|
||||
{
|
||||
const zmq::message_t msg_a;
|
||||
const zmq::message_t msg_b;
|
||||
CHECK(msg_a == msg_b);
|
||||
}
|
||||
|
||||
TEST_CASE("message equality non equal", "[message]")
|
||||
{
|
||||
const zmq::message_t msg_a("Hi", 2);
|
||||
const zmq::message_t msg_b("Hello", 5);
|
||||
CHECK(msg_a != msg_b);
|
||||
}
|
||||
|
||||
TEST_CASE("message equality non equal rhs empty", "[message]")
|
||||
{
|
||||
const zmq::message_t msg_a("Hi", 2);
|
||||
const zmq::message_t msg_b;
|
||||
CHECK(msg_a != msg_b);
|
||||
}
|
||||
|
||||
TEST_CASE("message equality non equal lhs empty", "[message]")
|
||||
{
|
||||
const zmq::message_t msg_a;
|
||||
const zmq::message_t msg_b("Hi", 2);
|
||||
CHECK(msg_a != msg_b);
|
||||
}
|
||||
|
||||
TEST_CASE("message to string", "[message]")
|
||||
{
|
||||
const zmq::message_t a;
|
||||
const zmq::message_t b("Foo", 3);
|
||||
CHECK(a.to_string() == "");
|
||||
CHECK(b.to_string() == "Foo");
|
||||
#if CPPZMQ_HAS_STRING_VIEW
|
||||
CHECK(a.to_string_view() == "");
|
||||
CHECK(b.to_string_view() == "Foo");
|
||||
#endif
|
||||
|
||||
#if defined(ZMQ_CPP11) && !defined(ZMQ_CPP11_PARTIAL)
|
||||
const zmq::message_t depr("Foo"); // deprecated
|
||||
CHECK(depr.to_string() != "Foo");
|
||||
CHECK(depr.to_string() == std::string("Foo", 4));
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(ZMQ_BUILD_DRAFT_API) && ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 0)
|
||||
TEST_CASE("message routing id persists", "[message]")
|
||||
{
|
||||
zmq::message_t msg;
|
||||
msg.set_routing_id(123);
|
||||
CHECK(123u == msg.routing_id());
|
||||
}
|
||||
|
||||
TEST_CASE("message group persists", "[message]")
|
||||
{
|
||||
zmq::message_t msg;
|
||||
msg.set_group("mygroup");
|
||||
CHECK(std::string(msg.group()) == "mygroup");
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(3, 2, 0)
|
||||
TEST_CASE("message is not shared", "[message]")
|
||||
{
|
||||
zmq::message_t msg;
|
||||
CHECK(msg.get(ZMQ_SHARED) == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("message is shared", "[message]")
|
||||
{
|
||||
size_t msg_sz = 1024; // large enough to be a type_lmsg
|
||||
zmq::message_t msg1(msg_sz);
|
||||
zmq::message_t msg2;
|
||||
msg2.copy(msg1);
|
||||
CHECK(msg1.get(ZMQ_SHARED) == 1);
|
||||
CHECK(msg2.get(ZMQ_SHARED) == 1);
|
||||
CHECK(msg1.size() == msg_sz);
|
||||
CHECK(msg2.size() == msg_sz);
|
||||
}
|
||||
|
||||
TEST_CASE("message move is not shared", "[message]")
|
||||
{
|
||||
size_t msg_sz = 1024; // large enough to be a type_lmsg
|
||||
zmq::message_t msg1(msg_sz);
|
||||
zmq::message_t msg2;
|
||||
msg2.move(msg1);
|
||||
CHECK(msg1.get(ZMQ_SHARED) == 0);
|
||||
CHECK(msg2.get(ZMQ_SHARED) == 0);
|
||||
CHECK(msg2.size() == msg_sz);
|
||||
CHECK(msg1.size() == 0);
|
||||
}
|
||||
#endif
|
||||
152
libs/cppzmq/tests/monitor.cpp
Normal file
152
libs/cppzmq/tests/monitor.cpp
Normal file
@@ -0,0 +1,152 @@
|
||||
#include "testutil.hpp"
|
||||
|
||||
#ifdef ZMQ_CPP11
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
|
||||
class mock_monitor_t : public zmq::monitor_t
|
||||
{
|
||||
public:
|
||||
|
||||
void on_event_connected(const zmq_event_t &, const char *) ZMQ_OVERRIDE
|
||||
{
|
||||
++connected;
|
||||
++total;
|
||||
}
|
||||
|
||||
int total{0};
|
||||
int connected{0};
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
TEST_CASE("monitor create destroy", "[monitor]")
|
||||
{
|
||||
zmq::monitor_t monitor;
|
||||
}
|
||||
|
||||
#if defined(ZMQ_CPP11)
|
||||
TEST_CASE("monitor move construct", "[monitor]")
|
||||
{
|
||||
zmq::context_t ctx;
|
||||
zmq::socket_t sock(ctx, ZMQ_DEALER);
|
||||
SECTION("move ctor empty") {
|
||||
zmq::monitor_t monitor1;
|
||||
zmq::monitor_t monitor2 = std::move(monitor1);
|
||||
}
|
||||
SECTION("move ctor init") {
|
||||
zmq::monitor_t monitor1;
|
||||
monitor1.init(sock, "inproc://monitor-client");
|
||||
zmq::monitor_t monitor2 = std::move(monitor1);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("monitor move assign", "[monitor]")
|
||||
{
|
||||
zmq::context_t ctx;
|
||||
zmq::socket_t sock(ctx, ZMQ_DEALER);
|
||||
SECTION("move assign empty") {
|
||||
zmq::monitor_t monitor1;
|
||||
zmq::monitor_t monitor2;
|
||||
monitor1 = std::move(monitor2);
|
||||
}
|
||||
SECTION("move assign init") {
|
||||
zmq::monitor_t monitor1;
|
||||
monitor1.init(sock, "inproc://monitor-client");
|
||||
zmq::monitor_t monitor2;
|
||||
monitor2 = std::move(monitor1);
|
||||
}
|
||||
SECTION("move assign init both") {
|
||||
zmq::monitor_t monitor1;
|
||||
monitor1.init(sock, "inproc://monitor-client");
|
||||
zmq::monitor_t monitor2;
|
||||
zmq::socket_t sock2(ctx, ZMQ_DEALER);
|
||||
monitor2.init(sock2, "inproc://monitor-client2");
|
||||
monitor2 = std::move(monitor1);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("monitor init event count", "[monitor]")
|
||||
{
|
||||
common_server_client_setup s{false};
|
||||
mock_monitor_t monitor;
|
||||
|
||||
const int expected_event_count = 1;
|
||||
monitor.init(s.client, "inproc://foo");
|
||||
|
||||
CHECK_FALSE(monitor.check_event(0));
|
||||
s.init();
|
||||
|
||||
while (monitor.check_event(1000) && monitor.total < expected_event_count) {
|
||||
}
|
||||
CHECK(monitor.connected == 1);
|
||||
CHECK(monitor.total == expected_event_count);
|
||||
}
|
||||
|
||||
TEST_CASE("monitor init abort", "[monitor]")
|
||||
{
|
||||
class mock_monitor : public mock_monitor_t
|
||||
{
|
||||
public:
|
||||
mock_monitor(std::function<void(void)> handle_connected) :
|
||||
handle_connected{std::move(handle_connected)}
|
||||
{
|
||||
}
|
||||
|
||||
void on_event_connected(const zmq_event_t &e, const char *m) ZMQ_OVERRIDE
|
||||
{
|
||||
mock_monitor_t::on_event_connected(e, m);
|
||||
handle_connected();
|
||||
}
|
||||
|
||||
std::function<void(void)> handle_connected;
|
||||
};
|
||||
|
||||
common_server_client_setup s(false);
|
||||
|
||||
std::mutex mutex;
|
||||
std::condition_variable cond_var;
|
||||
bool done{false};
|
||||
|
||||
mock_monitor monitor([&]()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
done = true;
|
||||
cond_var.notify_one();
|
||||
});
|
||||
monitor.init(s.client, "inproc://foo");
|
||||
|
||||
auto thread = std::thread([&monitor]
|
||||
{
|
||||
while (monitor.check_event(-1)) {
|
||||
}
|
||||
});
|
||||
|
||||
s.init();
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
CHECK(cond_var.wait_for(lock, std::chrono::seconds(1),
|
||||
[&done] { return done; }));
|
||||
}
|
||||
CHECK(monitor.connected == 1);
|
||||
monitor.abort();
|
||||
thread.join();
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("monitor from move assigned socket", "[monitor]")
|
||||
{
|
||||
zmq::context_t ctx;
|
||||
zmq::socket_t sock;
|
||||
sock = std::move([&ctx] {
|
||||
zmq::socket_t sock(ctx, ZMQ_DEALER);
|
||||
return sock;
|
||||
}());
|
||||
zmq::monitor_t monitor1;
|
||||
monitor1.init(sock, "inproc://monitor-client");
|
||||
// On failure, this test might hang indefinitely instead of immediately
|
||||
// failing
|
||||
}
|
||||
#endif
|
||||
212
libs/cppzmq/tests/multipart.cpp
Normal file
212
libs/cppzmq/tests/multipart.cpp
Normal file
@@ -0,0 +1,212 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <zmq_addon.hpp>
|
||||
|
||||
#ifdef ZMQ_HAS_RVALUE_REFS
|
||||
|
||||
#ifdef ZMQ_CPP17
|
||||
static_assert(std::is_invocable<decltype(&zmq::multipart_t::send),
|
||||
zmq::multipart_t *,
|
||||
zmq::socket_ref,
|
||||
int>::value,
|
||||
"Can't multipart_t::send with socket_ref");
|
||||
static_assert(std::is_invocable<decltype(&zmq::multipart_t::recv),
|
||||
zmq::multipart_t *,
|
||||
zmq::socket_ref,
|
||||
int>::value,
|
||||
"Can't multipart_t::recv with socket_ref");
|
||||
#endif
|
||||
static_assert(std::is_constructible<zmq::multipart_t, zmq::socket_ref>::value,
|
||||
"Can't construct with socket_ref");
|
||||
|
||||
/// \todo split this up into separate test cases
|
||||
///
|
||||
TEST_CASE("multipart legacy test", "[multipart]")
|
||||
{
|
||||
using namespace zmq;
|
||||
|
||||
bool ok = true;
|
||||
(void) ok;
|
||||
float num = 0;
|
||||
(void) num;
|
||||
std::string str = "";
|
||||
message_t msg;
|
||||
|
||||
// Create two PAIR sockets and connect over inproc
|
||||
context_t context(1);
|
||||
socket_t output(context, ZMQ_PAIR);
|
||||
socket_t input(context, ZMQ_PAIR);
|
||||
output.bind("inproc://multipart.test");
|
||||
input.connect("inproc://multipart.test");
|
||||
|
||||
// Test send and receive of single-frame message
|
||||
multipart_t multipart;
|
||||
assert(multipart.empty());
|
||||
|
||||
multipart.push(message_t("Hello", 5));
|
||||
assert(multipart.size() == 1);
|
||||
|
||||
ok = multipart.send(output);
|
||||
assert(multipart.empty());
|
||||
assert(ok);
|
||||
|
||||
ok = multipart.recv(input);
|
||||
assert(multipart.size() == 1);
|
||||
assert(ok);
|
||||
|
||||
msg = multipart.pop();
|
||||
assert(multipart.empty());
|
||||
assert(std::string(msg.data<char>(), msg.size()) == "Hello");
|
||||
|
||||
// Test send and receive of multi-frame message
|
||||
multipart.addstr("A");
|
||||
multipart.addstr("BB");
|
||||
multipart.addstr("CCC");
|
||||
assert(multipart.size() == 3);
|
||||
|
||||
multipart_t copy = multipart.clone();
|
||||
assert(copy.size() == 3);
|
||||
|
||||
ok = copy.send(output);
|
||||
assert(copy.empty());
|
||||
assert(ok);
|
||||
|
||||
ok = copy.recv(input);
|
||||
assert(copy.size() == 3);
|
||||
assert(ok);
|
||||
assert(copy.equal(&multipart));
|
||||
|
||||
// Test equality operators
|
||||
assert(copy == multipart);
|
||||
assert(multipart == copy);
|
||||
|
||||
multipart.pop();
|
||||
|
||||
assert(copy != multipart);
|
||||
assert(multipart != copy);
|
||||
|
||||
multipart_t emptyMessage1 {};
|
||||
multipart_t emptyMessage2 {};
|
||||
|
||||
assert(emptyMessage1 == emptyMessage2);
|
||||
assert(emptyMessage2 == emptyMessage1);
|
||||
|
||||
multipart.clear();
|
||||
assert(multipart.empty());
|
||||
|
||||
// Test message frame manipulation
|
||||
multipart.add(message_t("Frame5", 6));
|
||||
multipart.addstr("Frame6");
|
||||
multipart.addstr("Frame7");
|
||||
multipart.addtyp(8.0f);
|
||||
multipart.addmem("Frame9", 6);
|
||||
multipart.push(message_t("Frame4", 6));
|
||||
multipart.pushstr("Frame3");
|
||||
multipart.pushstr("Frame2");
|
||||
multipart.pushtyp(1.0f);
|
||||
multipart.pushmem("Frame0", 6);
|
||||
assert(multipart.size() == 10);
|
||||
|
||||
const message_t &front_msg = multipart.front();
|
||||
assert(multipart.size() == 10);
|
||||
assert(std::string(front_msg.data<char>(), front_msg.size()) == "Frame0");
|
||||
|
||||
const message_t &back_msg = multipart.back();
|
||||
assert(multipart.size() == 10);
|
||||
assert(std::string(back_msg.data<char>(), back_msg.size()) == "Frame9");
|
||||
|
||||
msg = multipart.remove();
|
||||
assert(multipart.size() == 9);
|
||||
assert(std::string(msg.data<char>(), msg.size()) == "Frame9");
|
||||
|
||||
msg = multipart.pop();
|
||||
assert(multipart.size() == 8);
|
||||
assert(std::string(msg.data<char>(), msg.size()) == "Frame0");
|
||||
|
||||
num = multipart.poptyp<float>();
|
||||
assert(multipart.size() == 7);
|
||||
assert(num == 1.0f);
|
||||
|
||||
str = multipart.popstr();
|
||||
assert(multipart.size() == 6);
|
||||
assert(str == "Frame2");
|
||||
|
||||
str = multipart.popstr();
|
||||
assert(multipart.size() == 5);
|
||||
assert(str == "Frame3");
|
||||
|
||||
str = multipart.popstr();
|
||||
assert(multipart.size() == 4);
|
||||
assert(str == "Frame4");
|
||||
|
||||
str = multipart.popstr();
|
||||
assert(multipart.size() == 3);
|
||||
assert(str == "Frame5");
|
||||
|
||||
str = multipart.popstr();
|
||||
assert(multipart.size() == 2);
|
||||
assert(str == "Frame6");
|
||||
|
||||
str = multipart.popstr();
|
||||
assert(multipart.size() == 1);
|
||||
assert(str == "Frame7");
|
||||
|
||||
num = multipart.poptyp<float>();
|
||||
assert(multipart.empty());
|
||||
assert(num == 8.0f);
|
||||
|
||||
// Test message constructors and concatenation
|
||||
multipart_t head("One", 3);
|
||||
head.addstr("Two");
|
||||
assert(head.size() == 2);
|
||||
|
||||
multipart_t tail(std::string("One-hundred"));
|
||||
tail.pushstr("Ninety-nine");
|
||||
assert(tail.size() == 2);
|
||||
|
||||
multipart_t tmp(message_t("Fifty", 5));
|
||||
assert(tmp.size() == 1);
|
||||
|
||||
multipart_t mid = multipart_t::create(49.0f);
|
||||
mid.append(std::move(tmp));
|
||||
assert(mid.size() == 2);
|
||||
assert(tmp.empty());
|
||||
|
||||
multipart_t merged(std::move(mid));
|
||||
merged.prepend(std::move(head));
|
||||
merged.append(std::move(tail));
|
||||
assert(merged.size() == 6);
|
||||
assert(head.empty());
|
||||
assert(tail.empty());
|
||||
|
||||
ok = merged.send(output);
|
||||
assert(merged.empty());
|
||||
assert(ok);
|
||||
|
||||
multipart_t received(input);
|
||||
assert(received.size() == 6);
|
||||
|
||||
str = received.popstr();
|
||||
assert(received.size() == 5);
|
||||
assert(str == "One");
|
||||
|
||||
str = received.popstr();
|
||||
assert(received.size() == 4);
|
||||
assert(str == "Two");
|
||||
|
||||
num = received.poptyp<float>();
|
||||
assert(received.size() == 3);
|
||||
assert(num == 49.0f);
|
||||
|
||||
str = received.popstr();
|
||||
assert(received.size() == 2);
|
||||
assert(str == "Fifty");
|
||||
|
||||
str = received.popstr();
|
||||
assert(received.size() == 1);
|
||||
assert(str == "Ninety-nine");
|
||||
|
||||
str = received.popstr();
|
||||
assert(received.empty());
|
||||
assert(str == "One-hundred");
|
||||
}
|
||||
#endif
|
||||
356
libs/cppzmq/tests/poller.cpp
Normal file
356
libs/cppzmq/tests/poller.cpp
Normal file
@@ -0,0 +1,356 @@
|
||||
#include "testutil.hpp"
|
||||
|
||||
#if defined(ZMQ_BUILD_DRAFT_API) && defined(ZMQ_CPP11) && !defined(ZMQ_CPP11_PARTIAL) && defined(ZMQ_HAVE_POLLER)
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
#ifdef ZMQ_CPP17
|
||||
static_assert(std::is_nothrow_swappable_v<zmq::poller_t<>>);
|
||||
#endif
|
||||
static_assert(sizeof(zmq_poller_event_t) == sizeof(zmq::poller_event<>), "");
|
||||
static_assert(sizeof(zmq_poller_event_t) == sizeof(zmq::poller_event<zmq::
|
||||
no_user_data>), "");
|
||||
static_assert(sizeof(zmq_poller_event_t) == sizeof(zmq::poller_event<int>), "");
|
||||
static_assert(alignof(zmq_poller_event_t) == alignof(zmq::poller_event<>), "");
|
||||
static_assert(alignof(zmq_poller_event_t) == alignof(zmq::poller_event<int>), "");
|
||||
|
||||
static_assert(!std::is_copy_constructible<zmq::poller_t<>>::value,
|
||||
"poller_t should not be copy-constructible");
|
||||
static_assert(!std::is_copy_assignable<zmq::poller_t<>>::value,
|
||||
"poller_t should not be copy-assignable");
|
||||
|
||||
TEST_CASE("event flags", "[poller]")
|
||||
{
|
||||
CHECK((zmq::event_flags::pollin | zmq::event_flags::pollout)
|
||||
== static_cast<zmq::event_flags>(ZMQ_POLLIN | ZMQ_POLLOUT));
|
||||
CHECK((zmq::event_flags::pollin & zmq::event_flags::pollout)
|
||||
== static_cast<zmq::event_flags>(ZMQ_POLLIN & ZMQ_POLLOUT));
|
||||
CHECK((zmq::event_flags::pollin ^ zmq::event_flags::pollout)
|
||||
== static_cast<zmq::event_flags>(ZMQ_POLLIN ^ ZMQ_POLLOUT));
|
||||
CHECK(~zmq::event_flags::pollin == static_cast<zmq::event_flags>(~ZMQ_POLLIN));
|
||||
}
|
||||
|
||||
TEST_CASE("poller create destroy", "[poller]")
|
||||
{
|
||||
zmq::poller_t<> a;
|
||||
#ifdef ZMQ_CPP17 // CTAD
|
||||
zmq::poller_t b;
|
||||
zmq::poller_event e;
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_CASE("poller move construct empty", "[poller]")
|
||||
{
|
||||
zmq::poller_t<> a;
|
||||
zmq::poller_t<> b = std::move(a);
|
||||
}
|
||||
|
||||
TEST_CASE("poller move assign empty", "[poller]")
|
||||
{
|
||||
zmq::poller_t<> a;
|
||||
zmq::poller_t<> b;
|
||||
b = std::move(a);
|
||||
}
|
||||
|
||||
TEST_CASE("poller swap", "[poller]")
|
||||
{
|
||||
zmq::poller_t<> a;
|
||||
zmq::poller_t<> b;
|
||||
using std::swap;
|
||||
swap(a, b);
|
||||
}
|
||||
|
||||
TEST_CASE("poller move construct non empty", "[poller]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::socket_t socket{context, zmq::socket_type::router};
|
||||
|
||||
zmq::poller_t<> a;
|
||||
a.add(socket, zmq::event_flags::pollin);
|
||||
zmq::poller_t<> b = std::move(a);
|
||||
}
|
||||
|
||||
TEST_CASE("poller move assign non empty", "[poller]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::socket_t socket{context, zmq::socket_type::router};
|
||||
|
||||
zmq::poller_t<> a;
|
||||
a.add(socket, zmq::event_flags::pollin);
|
||||
zmq::poller_t<> b;
|
||||
b = std::move(a);
|
||||
}
|
||||
|
||||
TEST_CASE("poller add nullptr", "[poller]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::socket_t socket{context, zmq::socket_type::router};
|
||||
zmq::poller_t<void> poller;
|
||||
CHECK_NOTHROW(poller.add(socket, zmq::event_flags::pollin, nullptr));
|
||||
}
|
||||
|
||||
TEST_CASE("poller add non nullptr", "[poller]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::socket_t socket{context, zmq::socket_type::router};
|
||||
zmq::poller_t<int> poller;
|
||||
int i;
|
||||
CHECK_NOTHROW(poller.add(socket, zmq::event_flags::pollin, &i));
|
||||
}
|
||||
|
||||
#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 3, 0)
|
||||
// this behaviour was added by https://github.com/zeromq/libzmq/pull/3100
|
||||
TEST_CASE("poller add handler invalid events type", "[poller]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::socket_t socket{context, zmq::socket_type::router};
|
||||
zmq::poller_t<> poller;
|
||||
short invalid_events_type = 2 << 10;
|
||||
CHECK_THROWS_AS(
|
||||
poller.add(socket, static_cast<zmq::event_flags>(invalid_events_type)),
|
||||
zmq::error_t);
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_CASE("poller add handler twice throws", "[poller]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::socket_t socket{context, zmq::socket_type::router};
|
||||
zmq::poller_t<> poller;
|
||||
poller.add(socket, zmq::event_flags::pollin);
|
||||
/// \todo the actual error code should be checked
|
||||
CHECK_THROWS_AS(poller.add(socket, zmq::event_flags::pollin),
|
||||
zmq::error_t);
|
||||
}
|
||||
|
||||
TEST_CASE("poller wait with no handlers throws", "[poller]")
|
||||
{
|
||||
zmq::poller_t<> poller;
|
||||
std::vector<zmq::poller_event<>> events;
|
||||
/// \todo the actual error code should be checked
|
||||
CHECK_THROWS_AS(poller.wait_all(events, std::chrono::milliseconds{10}),
|
||||
zmq::error_t);
|
||||
}
|
||||
|
||||
#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 3, 3)
|
||||
TEST_CASE("poller add/remove size checks", "[poller]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::socket_t socket{context, zmq::socket_type::router};
|
||||
zmq::poller_t<> poller;
|
||||
CHECK(poller.size() == 0);
|
||||
poller.add(socket, zmq::event_flags::pollin);
|
||||
CHECK(poller.size() == 1);
|
||||
CHECK_NOTHROW(poller.remove(socket));
|
||||
CHECK(poller.size() == 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_CASE("poller remove unregistered throws", "[poller]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::socket_t socket{context, zmq::socket_type::router};
|
||||
zmq::poller_t<> poller;
|
||||
/// \todo the actual error code should be checked
|
||||
CHECK_THROWS_AS(poller.remove(socket), zmq::error_t);
|
||||
}
|
||||
|
||||
TEST_CASE("poller remove registered empty", "[poller]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::socket_t socket{context, zmq::socket_type::router};
|
||||
zmq::poller_t<> poller;
|
||||
poller.add(socket, zmq::event_flags::pollin);
|
||||
CHECK_NOTHROW(poller.remove(socket));
|
||||
}
|
||||
|
||||
TEST_CASE("poller remove registered non empty", "[poller]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::socket_t socket{context, zmq::socket_type::router};
|
||||
zmq::poller_t<int> poller;
|
||||
int empty{};
|
||||
poller.add(socket, zmq::event_flags::pollin, &empty);
|
||||
CHECK_NOTHROW(poller.remove(socket));
|
||||
}
|
||||
|
||||
const std::string hi_str = "Hi";
|
||||
|
||||
TEST_CASE("poller poll basic", "[poller]")
|
||||
{
|
||||
common_server_client_setup s;
|
||||
|
||||
CHECK_NOTHROW(s.client.send(zmq::message_t{hi_str}, zmq::send_flags::none));
|
||||
|
||||
zmq::poller_t<int> poller;
|
||||
std::vector<zmq::poller_event<int>> events{1};
|
||||
int i = 0;
|
||||
CHECK_NOTHROW(poller.add(s.server, zmq::event_flags::pollin, &i));
|
||||
CHECK(1 == poller.wait_all(events, std::chrono::milliseconds{-1}));
|
||||
CHECK(s.server == events[0].socket);
|
||||
CHECK(&i == events[0].user_data);
|
||||
}
|
||||
|
||||
TEST_CASE("poller add invalid socket throws", "[poller]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::poller_t<> poller;
|
||||
zmq::socket_t a{context, zmq::socket_type::router};
|
||||
zmq::socket_t b{std::move(a)};
|
||||
CHECK_THROWS_AS(poller.add(a, zmq::event_flags::pollin), zmq::error_t);
|
||||
}
|
||||
|
||||
TEST_CASE("poller remove invalid socket throws", "[poller]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::socket_t socket{context, zmq::socket_type::router};
|
||||
zmq::poller_t<> poller;
|
||||
CHECK_NOTHROW(poller.add(socket, zmq::event_flags::pollin));
|
||||
std::vector<zmq::socket_t> sockets;
|
||||
sockets.emplace_back(std::move(socket));
|
||||
CHECK_THROWS_AS(poller.remove(socket), zmq::error_t);
|
||||
CHECK_NOTHROW(poller.remove(sockets[0]));
|
||||
}
|
||||
|
||||
TEST_CASE("poller modify empty throws", "[poller]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::socket_t socket{context, zmq::socket_type::push};
|
||||
zmq::poller_t<> poller;
|
||||
CHECK_THROWS_AS(poller.modify(socket, zmq::event_flags::pollin),
|
||||
zmq::error_t);
|
||||
}
|
||||
|
||||
TEST_CASE("poller modify invalid socket throws", "[poller]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::socket_t a{context, zmq::socket_type::push};
|
||||
zmq::socket_t b{std::move(a)};
|
||||
zmq::poller_t<> poller;
|
||||
CHECK_THROWS_AS(poller.modify(a, zmq::event_flags::pollin), zmq::error_t);
|
||||
}
|
||||
|
||||
TEST_CASE("poller modify not added throws", "[poller]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::socket_t a{context, zmq::socket_type::push};
|
||||
zmq::socket_t b{context, zmq::socket_type::push};
|
||||
zmq::poller_t<> poller;
|
||||
CHECK_NOTHROW(poller.add(a, zmq::event_flags::pollin));
|
||||
CHECK_THROWS_AS(poller.modify(b, zmq::event_flags::pollin), zmq::error_t);
|
||||
}
|
||||
|
||||
TEST_CASE("poller modify simple", "[poller]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::socket_t a{context, zmq::socket_type::push};
|
||||
zmq::poller_t<> poller;
|
||||
CHECK_NOTHROW(poller.add(a, zmq::event_flags::pollin));
|
||||
CHECK_NOTHROW(
|
||||
poller.modify(a, zmq::event_flags::pollin | zmq::event_flags::pollout));
|
||||
}
|
||||
|
||||
TEST_CASE("poller poll client server", "[poller]")
|
||||
{
|
||||
// Setup server and client
|
||||
common_server_client_setup s;
|
||||
|
||||
// Setup poller
|
||||
zmq::poller_t<zmq::socket_t> poller;
|
||||
CHECK_NOTHROW(poller.add(s.server, zmq::event_flags::pollin, &s.server));
|
||||
|
||||
// client sends message
|
||||
CHECK_NOTHROW(s.client.send(zmq::message_t{hi_str}, zmq::send_flags::none));
|
||||
|
||||
// wait for message and verify events
|
||||
std::vector<zmq::poller_event<zmq::socket_t>> events(1);
|
||||
CHECK(1 == poller.wait_all(events, std::chrono::milliseconds{500}));
|
||||
CHECK(zmq::event_flags::pollin == events[0].events);
|
||||
|
||||
// Modify server socket with pollout flag
|
||||
CHECK_NOTHROW(
|
||||
poller.modify(s.server, zmq::event_flags::pollin | zmq::event_flags::pollout
|
||||
));
|
||||
CHECK(1 == poller.wait_all(events, std::chrono::milliseconds{500}));
|
||||
CHECK((zmq::event_flags::pollin | zmq::event_flags::pollout) == events[0].events)
|
||||
;
|
||||
}
|
||||
|
||||
TEST_CASE("poller wait one return", "[poller]")
|
||||
{
|
||||
// Setup server and client
|
||||
common_server_client_setup s;
|
||||
|
||||
// Setup poller
|
||||
zmq::poller_t<> poller;
|
||||
CHECK_NOTHROW(poller.add(s.server, zmq::event_flags::pollin));
|
||||
|
||||
// client sends message
|
||||
CHECK_NOTHROW(s.client.send(zmq::message_t{hi_str}, zmq::send_flags::none));
|
||||
|
||||
// wait for message and verify events
|
||||
std::vector<zmq::poller_event<>> events(1);
|
||||
CHECK(1 == poller.wait_all(events, std::chrono::milliseconds{500}));
|
||||
}
|
||||
|
||||
TEST_CASE("poller wait on move constructed", "[poller]")
|
||||
{
|
||||
common_server_client_setup s;
|
||||
CHECK_NOTHROW(s.client.send(zmq::message_t{hi_str}, zmq::send_flags::none));
|
||||
zmq::poller_t<> a;
|
||||
CHECK_NOTHROW(a.add(s.server, zmq::event_flags::pollin));
|
||||
zmq::poller_t<> b{std::move(a)};
|
||||
std::vector<zmq::poller_event<>> events(1);
|
||||
/// \todo the actual error code should be checked
|
||||
CHECK_THROWS_AS(a.wait_all(events, std::chrono::milliseconds{10}),
|
||||
zmq::error_t);
|
||||
CHECK(1 == b.wait_all(events, std::chrono::milliseconds{-1}));
|
||||
}
|
||||
|
||||
TEST_CASE("poller wait on move assigned", "[poller]")
|
||||
{
|
||||
common_server_client_setup s;
|
||||
CHECK_NOTHROW(s.client.send(zmq::message_t{hi_str}, zmq::send_flags::none));
|
||||
zmq::poller_t<> a;
|
||||
CHECK_NOTHROW(a.add(s.server, zmq::event_flags::pollin));
|
||||
zmq::poller_t<> b;
|
||||
b = {std::move(a)};
|
||||
/// \todo the TEST_CASE error code should be checked
|
||||
std::vector<zmq::poller_event<>> events(1);
|
||||
CHECK_THROWS_AS(a.wait_all(events, std::chrono::milliseconds{10}),
|
||||
zmq::error_t);
|
||||
CHECK(1 == b.wait_all(events, std::chrono::milliseconds{-1}));
|
||||
}
|
||||
|
||||
TEST_CASE("poller remove from handler", "[poller]")
|
||||
{
|
||||
constexpr size_t ITER_NO = 10;
|
||||
|
||||
// Setup servers and clients
|
||||
std::vector<common_server_client_setup> setup_list;
|
||||
for (size_t i = 0; i < ITER_NO; ++i)
|
||||
setup_list.emplace_back(common_server_client_setup{});
|
||||
|
||||
// Setup poller
|
||||
zmq::poller_t<> poller;
|
||||
for (size_t i = 0; i < ITER_NO; ++i) {
|
||||
CHECK_NOTHROW(poller.add(setup_list[i].server, zmq::event_flags::pollin));
|
||||
}
|
||||
// Clients send messages
|
||||
for (auto &s : setup_list) {
|
||||
CHECK_NOTHROW(s.client.send(zmq::message_t{hi_str}, zmq::send_flags::none));
|
||||
}
|
||||
|
||||
// Wait for all servers to receive a message
|
||||
for (auto &s : setup_list) {
|
||||
zmq::pollitem_t items[] = {{s.server, 0, ZMQ_POLLIN, 0}};
|
||||
zmq::poll(&items[0], 1);
|
||||
}
|
||||
|
||||
// Fire all handlers in one wait
|
||||
std::vector<zmq::poller_event<>> events(ITER_NO);
|
||||
CHECK(ITER_NO == poller.wait_all(events, std::chrono::milliseconds{-1}));
|
||||
}
|
||||
|
||||
#endif
|
||||
139
libs/cppzmq/tests/recv_multipart.cpp
Normal file
139
libs/cppzmq/tests/recv_multipart.cpp
Normal file
@@ -0,0 +1,139 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <zmq_addon.hpp>
|
||||
#ifdef ZMQ_CPP11
|
||||
|
||||
TEST_CASE("recv_multipart test", "[recv_multipart]")
|
||||
{
|
||||
zmq::context_t context(1);
|
||||
zmq::socket_t output(context, ZMQ_PAIR);
|
||||
zmq::socket_t input(context, ZMQ_PAIR);
|
||||
output.bind("inproc://multipart.test");
|
||||
input.connect("inproc://multipart.test");
|
||||
|
||||
SECTION("send 1 message") {
|
||||
input.send(zmq::str_buffer("hello"));
|
||||
|
||||
std::vector<zmq::message_t> msgs;
|
||||
auto ret = zmq::recv_multipart(output, std::back_inserter(msgs));
|
||||
REQUIRE(ret);
|
||||
CHECK(*ret == 1);
|
||||
REQUIRE(msgs.size() == 1);
|
||||
CHECK(msgs[0].size() == 5);
|
||||
}
|
||||
SECTION("send 2 messages") {
|
||||
input.send(zmq::str_buffer("hello"), zmq::send_flags::sndmore);
|
||||
input.send(zmq::str_buffer("world!"));
|
||||
|
||||
std::vector<zmq::message_t> msgs;
|
||||
auto ret = zmq::recv_multipart(output, std::back_inserter(msgs));
|
||||
REQUIRE(ret);
|
||||
CHECK(*ret == 2);
|
||||
REQUIRE(msgs.size() == 2);
|
||||
CHECK(msgs[0].size() == 5);
|
||||
CHECK(msgs[1].size() == 6);
|
||||
}
|
||||
SECTION("send no messages, dontwait") {
|
||||
std::vector<zmq::message_t> msgs;
|
||||
auto ret = zmq::recv_multipart(output, std::back_inserter(msgs),
|
||||
zmq::recv_flags::dontwait);
|
||||
CHECK_FALSE(ret);
|
||||
REQUIRE(msgs.size() == 0);
|
||||
}
|
||||
SECTION("send 1 partial message, dontwait") {
|
||||
input.send(zmq::str_buffer("hello"), zmq::send_flags::sndmore);
|
||||
|
||||
std::vector<zmq::message_t> msgs;
|
||||
auto ret = zmq::recv_multipart(output, std::back_inserter(msgs),
|
||||
zmq::recv_flags::dontwait);
|
||||
CHECK_FALSE(ret);
|
||||
REQUIRE(msgs.size() == 0);
|
||||
}
|
||||
SECTION("recv with invalid socket") {
|
||||
std::vector<zmq::message_t> msgs;
|
||||
CHECK_THROWS_AS(
|
||||
zmq::recv_multipart(zmq::socket_ref(), std::back_inserter(msgs)),
|
||||
zmq::error_t);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("recv_multipart_n test", "[recv_multipart]")
|
||||
{
|
||||
zmq::context_t context(1);
|
||||
zmq::socket_t output(context, ZMQ_PAIR);
|
||||
zmq::socket_t input(context, ZMQ_PAIR);
|
||||
output.bind("inproc://multipart.test");
|
||||
input.connect("inproc://multipart.test");
|
||||
|
||||
SECTION("send 1 message") {
|
||||
input.send(zmq::str_buffer("hello"));
|
||||
|
||||
std::array<zmq::message_t, 1> msgs;
|
||||
auto ret = zmq::recv_multipart_n(output, msgs.data(), msgs.size());
|
||||
REQUIRE(ret);
|
||||
CHECK(*ret == 1);
|
||||
CHECK(msgs[0].size() == 5);
|
||||
}
|
||||
SECTION("send 1 message 2") {
|
||||
input.send(zmq::str_buffer("hello"));
|
||||
|
||||
std::array<zmq::message_t, 2> msgs;
|
||||
auto ret = zmq::recv_multipart_n(output, msgs.data(), msgs.size());
|
||||
REQUIRE(ret);
|
||||
CHECK(*ret == 1);
|
||||
CHECK(msgs[0].size() == 5);
|
||||
CHECK(msgs[1].size() == 0);
|
||||
}
|
||||
SECTION("send 2 messages, recv 1") {
|
||||
input.send(zmq::str_buffer("hello"), zmq::send_flags::sndmore);
|
||||
input.send(zmq::str_buffer("world!"));
|
||||
|
||||
std::array<zmq::message_t, 1> msgs;
|
||||
CHECK_THROWS_AS(
|
||||
zmq::recv_multipart_n(output, msgs.data(), msgs.size()),
|
||||
std::runtime_error);
|
||||
}
|
||||
SECTION("recv 0") {
|
||||
input.send(zmq::str_buffer("hello"), zmq::send_flags::sndmore);
|
||||
input.send(zmq::str_buffer("world!"));
|
||||
|
||||
std::array<zmq::message_t, 1> msgs;
|
||||
CHECK_THROWS_AS(
|
||||
zmq::recv_multipart_n(output, msgs.data(), 0),
|
||||
std::runtime_error);
|
||||
}
|
||||
SECTION("send 2 messages") {
|
||||
input.send(zmq::str_buffer("hello"), zmq::send_flags::sndmore);
|
||||
input.send(zmq::str_buffer("world!"));
|
||||
|
||||
std::array<zmq::message_t, 2> msgs;
|
||||
auto ret = zmq::recv_multipart_n(output, msgs.data(), msgs.size());
|
||||
REQUIRE(ret);
|
||||
CHECK(*ret == 2);
|
||||
CHECK(msgs[0].size() == 5);
|
||||
CHECK(msgs[1].size() == 6);
|
||||
}
|
||||
SECTION("send no messages, dontwait") {
|
||||
std::array<zmq::message_t, 1> msgs;
|
||||
auto ret = zmq::recv_multipart_n(output, msgs.data(), msgs.size(),
|
||||
zmq::recv_flags::dontwait);
|
||||
CHECK_FALSE(ret);
|
||||
REQUIRE(msgs[0].size() == 0);
|
||||
}
|
||||
SECTION("send 1 partial message, dontwait") {
|
||||
input.send(zmq::str_buffer("hello"), zmq::send_flags::sndmore);
|
||||
|
||||
std::array<zmq::message_t, 1> msgs;
|
||||
auto ret = zmq::recv_multipart_n(output, msgs.data(), msgs.size(),
|
||||
zmq::recv_flags::dontwait);
|
||||
CHECK_FALSE(ret);
|
||||
REQUIRE(msgs[0].size() == 0);
|
||||
}
|
||||
SECTION("recv with invalid socket") {
|
||||
std::array<zmq::message_t, 1> msgs;
|
||||
CHECK_THROWS_AS(
|
||||
zmq::recv_multipart_n(zmq::socket_ref(), msgs.data(), msgs.size()),
|
||||
zmq::error_t);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
121
libs/cppzmq/tests/send_multipart.cpp
Normal file
121
libs/cppzmq/tests/send_multipart.cpp
Normal file
@@ -0,0 +1,121 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <zmq_addon.hpp>
|
||||
#ifdef ZMQ_CPP11
|
||||
|
||||
#include <forward_list>
|
||||
|
||||
TEST_CASE("send_multipart test", "[send_multipart]")
|
||||
{
|
||||
zmq::context_t context(1);
|
||||
zmq::socket_t output(context, ZMQ_PAIR);
|
||||
zmq::socket_t input(context, ZMQ_PAIR);
|
||||
output.bind("inproc://multipart.test");
|
||||
input.connect("inproc://multipart.test");
|
||||
|
||||
SECTION("send 0 messages") {
|
||||
std::vector<zmq::message_t> imsgs;
|
||||
auto iret = zmq::send_multipart(input, imsgs);
|
||||
REQUIRE(iret);
|
||||
CHECK(*iret == 0);
|
||||
}
|
||||
SECTION("send 1 message") {
|
||||
std::array<zmq::message_t, 1> imsgs = {zmq::message_t(3)};
|
||||
auto iret = zmq::send_multipart(input, imsgs);
|
||||
REQUIRE(iret);
|
||||
CHECK(*iret == 1);
|
||||
|
||||
std::vector<zmq::message_t> omsgs;
|
||||
auto oret = zmq::recv_multipart(output, std::back_inserter(omsgs));
|
||||
REQUIRE(oret);
|
||||
CHECK(*oret == 1);
|
||||
REQUIRE(omsgs.size() == 1);
|
||||
CHECK(omsgs[0].size() == 3);
|
||||
}
|
||||
SECTION("send 2 messages") {
|
||||
std::array<zmq::message_t, 2> imsgs = {zmq::message_t(3), zmq::message_t(4)};
|
||||
auto iret = zmq::send_multipart(input, imsgs);
|
||||
REQUIRE(iret);
|
||||
CHECK(*iret == 2);
|
||||
|
||||
std::vector<zmq::message_t> omsgs;
|
||||
auto oret = zmq::recv_multipart(output, std::back_inserter(omsgs));
|
||||
REQUIRE(oret);
|
||||
CHECK(*oret == 2);
|
||||
REQUIRE(omsgs.size() == 2);
|
||||
CHECK(omsgs[0].size() == 3);
|
||||
CHECK(omsgs[1].size() == 4);
|
||||
}
|
||||
SECTION("send 2 messages, const_buffer") {
|
||||
std::array<zmq::const_buffer, 2> imsgs = {zmq::str_buffer("foo"),
|
||||
zmq::str_buffer("bar!")};
|
||||
auto iret = zmq::send_multipart(input, imsgs);
|
||||
REQUIRE(iret);
|
||||
CHECK(*iret == 2);
|
||||
|
||||
std::vector<zmq::message_t> omsgs;
|
||||
auto oret = zmq::recv_multipart(output, std::back_inserter(omsgs));
|
||||
REQUIRE(oret);
|
||||
CHECK(*oret == 2);
|
||||
REQUIRE(omsgs.size() == 2);
|
||||
CHECK(omsgs[0].size() == 3);
|
||||
CHECK(omsgs[1].size() == 4);
|
||||
}
|
||||
SECTION("send 2 messages, mutable_buffer") {
|
||||
char buf[4] = {};
|
||||
std::array<zmq::mutable_buffer, 2> imsgs = {
|
||||
zmq::buffer(buf, 3), zmq::buffer(buf)};
|
||||
auto iret = zmq::send_multipart(input, imsgs);
|
||||
REQUIRE(iret);
|
||||
CHECK(*iret == 2);
|
||||
|
||||
std::vector<zmq::message_t> omsgs;
|
||||
auto oret = zmq::recv_multipart(output, std::back_inserter(omsgs));
|
||||
REQUIRE(oret);
|
||||
CHECK(*oret == 2);
|
||||
REQUIRE(omsgs.size() == 2);
|
||||
CHECK(omsgs[0].size() == 3);
|
||||
CHECK(omsgs[1].size() == 4);
|
||||
}
|
||||
SECTION("send 2 messages, dontwait") {
|
||||
zmq::socket_t push(context, ZMQ_PUSH);
|
||||
push.bind("inproc://multipart.test.push");
|
||||
|
||||
std::array<zmq::message_t, 2> imsgs = {zmq::message_t(3), zmq::message_t(4)};
|
||||
auto iret = zmq::send_multipart(push, imsgs, zmq::send_flags::dontwait);
|
||||
REQUIRE_FALSE(iret);
|
||||
}
|
||||
SECTION("send, misc. containers") {
|
||||
std::vector<zmq::message_t> msgs_vec;
|
||||
msgs_vec.emplace_back(3);
|
||||
msgs_vec.emplace_back(4);
|
||||
auto iret = zmq::send_multipart(input, msgs_vec);
|
||||
REQUIRE(iret);
|
||||
CHECK(*iret == 2);
|
||||
|
||||
std::forward_list<zmq::message_t> msgs_list;
|
||||
msgs_list.emplace_front(4);
|
||||
msgs_list.emplace_front(3);
|
||||
iret = zmq::send_multipart(input, msgs_list);
|
||||
REQUIRE(iret);
|
||||
CHECK(*iret == 2);
|
||||
|
||||
// init. list
|
||||
const auto msgs_il = {zmq::str_buffer("foo"), zmq::str_buffer("bar!")};
|
||||
iret = zmq::send_multipart(input, msgs_il);
|
||||
REQUIRE(iret);
|
||||
CHECK(*iret == 2);
|
||||
// rvalue
|
||||
iret = zmq::send_multipart(input,
|
||||
std::initializer_list<zmq::const_buffer>{
|
||||
zmq::str_buffer("foo"),
|
||||
zmq::str_buffer("bar!")});
|
||||
REQUIRE(iret);
|
||||
CHECK(*iret == 2);
|
||||
}
|
||||
SECTION("send with invalid socket") {
|
||||
std::vector<zmq::message_t> msgs(1);
|
||||
CHECK_THROWS_AS(zmq::send_multipart(zmq::socket_ref(), msgs),
|
||||
zmq::error_t);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
700
libs/cppzmq/tests/socket.cpp
Normal file
700
libs/cppzmq/tests/socket.cpp
Normal file
@@ -0,0 +1,700 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <zmq.hpp>
|
||||
#ifdef ZMQ_CPP11
|
||||
#include <future>
|
||||
#endif
|
||||
|
||||
#if (__cplusplus >= 201703L)
|
||||
static_assert(std::is_nothrow_swappable<zmq::socket_t>::value,
|
||||
"socket_t should be nothrow swappable");
|
||||
#endif
|
||||
|
||||
TEST_CASE("socket default ctor", "[socket]")
|
||||
{
|
||||
zmq::socket_t socket;
|
||||
}
|
||||
|
||||
TEST_CASE("socket create destroy", "[socket]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::socket_t socket(context, ZMQ_ROUTER);
|
||||
}
|
||||
|
||||
#ifdef ZMQ_CPP11
|
||||
TEST_CASE("socket create assign", "[socket]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::socket_t socket(context, ZMQ_ROUTER);
|
||||
CHECK(static_cast<bool>(socket));
|
||||
CHECK(socket.handle() != nullptr);
|
||||
socket = {};
|
||||
CHECK(!static_cast<bool>(socket));
|
||||
CHECK(socket.handle() == nullptr);
|
||||
}
|
||||
|
||||
TEST_CASE("socket create by enum and destroy", "[socket]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::socket_t socket(context, zmq::socket_type::router);
|
||||
}
|
||||
|
||||
TEST_CASE("socket swap", "[socket]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::socket_t socket1(context, zmq::socket_type::router);
|
||||
zmq::socket_t socket2(context, zmq::socket_type::dealer);
|
||||
using std::swap;
|
||||
swap(socket1, socket2);
|
||||
}
|
||||
|
||||
#ifdef ZMQ_CPP11
|
||||
TEST_CASE("socket options", "[socket]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::socket_t socket(context, zmq::socket_type::router);
|
||||
|
||||
#ifdef ZMQ_IMMEDIATE
|
||||
socket.set(zmq::sockopt::immediate, 0);
|
||||
socket.set(zmq::sockopt::immediate, false);
|
||||
CHECK(socket.get(zmq::sockopt::immediate) == false);
|
||||
// unit out of range
|
||||
CHECK_THROWS_AS(socket.set(zmq::sockopt::immediate, 80), zmq::error_t);
|
||||
#endif
|
||||
#ifdef ZMQ_LINGER
|
||||
socket.set(zmq::sockopt::linger, 55);
|
||||
CHECK(socket.get(zmq::sockopt::linger) == 55);
|
||||
#endif
|
||||
#ifdef ZMQ_ROUTING_ID
|
||||
const std::string id = "foobar";
|
||||
socket.set(zmq::sockopt::routing_id, "foobar");
|
||||
socket.set(zmq::sockopt::routing_id, zmq::buffer(id));
|
||||
socket.set(zmq::sockopt::routing_id, id);
|
||||
#if CPPZMQ_HAS_STRING_VIEW
|
||||
socket.set(zmq::sockopt::routing_id, std::string_view{id});
|
||||
#endif
|
||||
|
||||
std::string id_ret(10, ' ');
|
||||
auto size = socket.get(zmq::sockopt::routing_id, zmq::buffer(id_ret));
|
||||
id_ret.resize(size);
|
||||
CHECK(id == id_ret);
|
||||
auto stropt = socket.get(zmq::sockopt::routing_id);
|
||||
CHECK(id == stropt);
|
||||
|
||||
std::string id_ret_small(3, ' ');
|
||||
// truncated
|
||||
CHECK_THROWS_AS(socket.get(zmq::sockopt::routing_id, zmq::buffer(id_ret_small)),
|
||||
zmq::error_t);
|
||||
#endif
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void check_array_opt(T opt,
|
||||
zmq::socket_t &sock,
|
||||
std::string info,
|
||||
bool set_only = false)
|
||||
{
|
||||
const std::string val = "foobar";
|
||||
INFO("setting " + info);
|
||||
sock.set(opt, val);
|
||||
if (set_only)
|
||||
return;
|
||||
|
||||
INFO("getting " + info);
|
||||
auto s = sock.get(opt);
|
||||
CHECK(s == val);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void check_array_opt_get(T opt, zmq::socket_t &sock, std::string info)
|
||||
{
|
||||
INFO("getting " + info);
|
||||
(void) sock.get(opt);
|
||||
}
|
||||
|
||||
#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 0, 0)
|
||||
template<class T> void check_bin_z85(T opt, zmq::socket_t &sock, std::string str_val)
|
||||
{
|
||||
std::vector<uint8_t> bin_val(32);
|
||||
const auto dret = zmq_z85_decode(bin_val.data(), str_val.c_str());
|
||||
CHECK(dret != nullptr);
|
||||
|
||||
sock.set(opt, str_val);
|
||||
sock.set(opt, zmq::buffer(bin_val));
|
||||
auto sv = sock.get(opt);
|
||||
CHECK(sv == str_val);
|
||||
|
||||
auto bv = sock.get(opt, 32);
|
||||
REQUIRE(bv.size() == bin_val.size());
|
||||
CHECK(std::memcmp(bv.data(), bin_val.data(), bin_val.size()) == 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_CASE("socket check array options", "[socket]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::socket_t router(context, zmq::socket_type::router);
|
||||
zmq::socket_t xpub(context, zmq::socket_type::xpub);
|
||||
zmq::socket_t sub(context, zmq::socket_type::sub);
|
||||
|
||||
#ifdef ZMQ_BINDTODEVICE
|
||||
// requires setting CAP_NET_RAW
|
||||
//check_array_opt(zmq::sockopt::bindtodevice, router, "bindtodevice");
|
||||
#endif
|
||||
#ifdef ZMQ_CONNECT_ROUTING_ID
|
||||
check_array_opt(zmq::sockopt::connect_routing_id, router, "connect_routing_id",
|
||||
true);
|
||||
#endif
|
||||
#ifdef ZMQ_LAST_ENDPOINT
|
||||
check_array_opt_get(zmq::sockopt::last_endpoint, router, "last_endpoint");
|
||||
#endif
|
||||
#ifdef ZMQ_METADATA
|
||||
router.set(zmq::sockopt::metadata, zmq::str_buffer("X-foo:bar"));
|
||||
#endif
|
||||
#ifdef ZMQ_PLAIN_PASSWORD
|
||||
check_array_opt(zmq::sockopt::plain_password, router, "plain_password");
|
||||
#endif
|
||||
#ifdef ZMQ_PLAIN_USERNAME
|
||||
check_array_opt(zmq::sockopt::plain_username, router, "plain_username");
|
||||
#endif
|
||||
#ifdef ZMQ_ROUTING_ID
|
||||
check_array_opt(zmq::sockopt::routing_id, router, "routing_id");
|
||||
#endif
|
||||
#ifdef ZMQ_SOCKS_PROXY
|
||||
check_array_opt(zmq::sockopt::socks_proxy, router, "socks_proxy");
|
||||
#endif
|
||||
#ifdef ZMQ_SUBSCRIBE
|
||||
check_array_opt(zmq::sockopt::subscribe, sub, "subscribe", true);
|
||||
#endif
|
||||
#ifdef ZMQ_UNSUBSCRIBE
|
||||
check_array_opt(zmq::sockopt::unsubscribe, sub, "unsubscribe", true);
|
||||
#endif
|
||||
#ifdef ZMQ_XPUB_WELCOME_MSG
|
||||
check_array_opt(zmq::sockopt::xpub_welcome_msg, xpub, "xpub_welcome_msg", true);
|
||||
#endif
|
||||
#ifdef ZMQ_ZAP_DOMAIN
|
||||
check_array_opt(zmq::sockopt::zap_domain, router, "zap_domain");
|
||||
#endif
|
||||
|
||||
// curve
|
||||
#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 0, 0) && defined(ZMQ_HAS_CAPABILITIES)
|
||||
if (zmq_has("curve") == 1) {
|
||||
const std::string spk = "rq:rM>}U?@Lns47E1%kR.o@n%FcmmsL/@{H8]yf7";
|
||||
const std::string ssk = "JTKVSB%%)wK0E.X)V>+}o?pNmC{O&4W4b!Ni{Lh6";
|
||||
const std::string cpk = "Yne@$w-vo<fVvi]a<NY6T1ed:M$fCG*[IaLV{hID";
|
||||
const std::string csk = "D:)Q[IlAW!ahhC2ac:9*A}h:p?([4%wOTJ%JR%cs";
|
||||
|
||||
zmq::socket_t curve_server(context, zmq::socket_type::router);
|
||||
curve_server.set(zmq::sockopt::curve_server, true);
|
||||
CHECK(curve_server.get(zmq::sockopt::curve_server));
|
||||
check_bin_z85(zmq::sockopt::curve_secretkey, curve_server, ssk);
|
||||
|
||||
zmq::socket_t curve_client(context, zmq::socket_type::router);
|
||||
curve_client.set(zmq::sockopt::curve_server, false);
|
||||
CHECK_FALSE(curve_client.get(zmq::sockopt::curve_server));
|
||||
check_bin_z85(zmq::sockopt::curve_serverkey, curve_client, spk);
|
||||
check_bin_z85(zmq::sockopt::curve_publickey, curve_client, cpk);
|
||||
check_bin_z85(zmq::sockopt::curve_secretkey, curve_client, csk);
|
||||
}
|
||||
#endif
|
||||
|
||||
// gssapi
|
||||
#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 1, 0) && defined(ZMQ_HAS_CAPABILITIES)
|
||||
if (zmq_has("gssapi") == 1 && false) // TODO enable
|
||||
{
|
||||
zmq::socket_t gss_server(context, zmq::socket_type::router);
|
||||
gss_server.set(zmq::sockopt::gssapi_server, true);
|
||||
CHECK(gss_server.get(zmq::sockopt::gssapi_server) == 1);
|
||||
gss_server.set(zmq::sockopt::gssapi_plaintext, false);
|
||||
CHECK(gss_server.get(zmq::sockopt::gssapi_plaintext) == 0);
|
||||
check_array_opt(zmq::sockopt::gssapi_principal, gss_server,
|
||||
"gssapi_principal");
|
||||
|
||||
zmq::socket_t gss_client(context, zmq::socket_type::router);
|
||||
CHECK(gss_client.get(zmq::sockopt::gssapi_server) == 0);
|
||||
check_array_opt(zmq::sockopt::gssapi_principal, gss_client,
|
||||
"gssapi_principal");
|
||||
check_array_opt(zmq::sockopt::gssapi_service_principal, gss_client,
|
||||
"gssapi_service_principal");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
template<class T, class Opt>
|
||||
void check_integral_opt(Opt opt,
|
||||
zmq::socket_t &sock,
|
||||
std::string info,
|
||||
bool set_only = false)
|
||||
{
|
||||
const T val = 1;
|
||||
INFO("setting " + info);
|
||||
sock.set(opt, val);
|
||||
if (set_only)
|
||||
return;
|
||||
|
||||
INFO("getting " + info);
|
||||
auto s = sock.get(opt);
|
||||
CHECK(s == val);
|
||||
}
|
||||
|
||||
template<class T, class Opt>
|
||||
void check_integral_opt_get(Opt opt, zmq::socket_t &sock, std::string info)
|
||||
{
|
||||
INFO("getting " + info);
|
||||
(void) sock.get(opt);
|
||||
}
|
||||
|
||||
TEST_CASE("socket check integral options", "[socket]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::socket_t router(context, zmq::socket_type::router);
|
||||
zmq::socket_t xpub(context, zmq::socket_type::xpub);
|
||||
zmq::socket_t req(context, zmq::socket_type::req);
|
||||
#ifdef ZMQ_STREAM_NOTIFY
|
||||
zmq::socket_t stream(context, zmq::socket_type::stream);
|
||||
#endif
|
||||
|
||||
#ifdef ZMQ_AFFINITY
|
||||
check_integral_opt<uint64_t>(zmq::sockopt::affinity, router, "affinity");
|
||||
#endif
|
||||
#ifdef ZMQ_BACKLOG
|
||||
check_integral_opt<int>(zmq::sockopt::backlog, router, "backlog");
|
||||
#endif
|
||||
#ifdef ZMQ_CONFLATE
|
||||
check_integral_opt<int>(zmq::sockopt::conflate, router, "conflate");
|
||||
#endif
|
||||
#ifdef ZMQ_CONNECT_TIMEOUT
|
||||
check_integral_opt<int>(zmq::sockopt::connect_timeout, router,
|
||||
"connect_timeout");
|
||||
#endif
|
||||
#ifdef ZMQ_EVENTS
|
||||
check_integral_opt_get<int>(zmq::sockopt::events, router, "events");
|
||||
#endif
|
||||
#ifdef ZMQ_FD
|
||||
check_integral_opt_get<zmq::fd_t>(zmq::sockopt::fd, router, "fd");
|
||||
#endif
|
||||
#ifdef ZMQ_HANDSHAKE_IVL
|
||||
check_integral_opt<int>(zmq::sockopt::handshake_ivl, router, "handshake_ivl");
|
||||
#endif
|
||||
#ifdef ZMQ_HEARTBEAT_IVL
|
||||
check_integral_opt<int>(zmq::sockopt::heartbeat_ivl, router, "heartbeat_ivl");
|
||||
#endif
|
||||
#ifdef ZMQ_HEARTBEAT_TIMEOUT
|
||||
check_integral_opt<int>(zmq::sockopt::heartbeat_timeout, router,
|
||||
"heartbeat_timeout");
|
||||
#endif
|
||||
#ifdef ZMQ_HEARTBEAT_TTL
|
||||
router.set(zmq::sockopt::heartbeat_ttl, 100);
|
||||
CHECK(router.get(zmq::sockopt::heartbeat_ttl) == 100);
|
||||
#endif
|
||||
#ifdef ZMQ_IMMEDIATE
|
||||
check_integral_opt<int>(zmq::sockopt::immediate, router, "immediate");
|
||||
#endif
|
||||
#ifdef ZMQ_INVERT_MATCHING
|
||||
check_integral_opt<int>(zmq::sockopt::invert_matching, router,
|
||||
"invert_matching");
|
||||
#endif
|
||||
#ifdef ZMQ_IPV6
|
||||
check_integral_opt<int>(zmq::sockopt::ipv6, router, "ipv6");
|
||||
#endif
|
||||
#ifdef ZMQ_LINGER
|
||||
check_integral_opt<int>(zmq::sockopt::linger, router, "linger");
|
||||
#endif
|
||||
#ifdef ZMQ_MAXMSGSIZE
|
||||
check_integral_opt<int64_t>(zmq::sockopt::maxmsgsize, router, "maxmsgsize");
|
||||
#endif
|
||||
#ifdef ZMQ_MECHANISM
|
||||
check_integral_opt_get<int>(zmq::sockopt::mechanism, router, "mechanism");
|
||||
#endif
|
||||
#ifdef ZMQ_MULTICAST_HOPS
|
||||
check_integral_opt<int>(zmq::sockopt::multicast_hops, router, "multicast_hops");
|
||||
#endif
|
||||
#ifdef ZMQ_MULTICAST_LOOP
|
||||
check_integral_opt<int>(zmq::sockopt::multicast_loop, router, "multicast_loop");
|
||||
#endif
|
||||
#ifdef ZMQ_MULTICAST_MAXTPDU
|
||||
check_integral_opt<int>(zmq::sockopt::multicast_maxtpdu, router,
|
||||
"multicast_maxtpdu");
|
||||
#endif
|
||||
#ifdef ZMQ_PLAIN_SERVER
|
||||
check_integral_opt<int>(zmq::sockopt::plain_server, router, "plain_server");
|
||||
#endif
|
||||
#ifdef ZMQ_USE_FD
|
||||
check_integral_opt<int>(zmq::sockopt::use_fd, router, "use_fd");
|
||||
#endif
|
||||
#ifdef ZMQ_PROBE_ROUTER
|
||||
check_integral_opt<int>(zmq::sockopt::probe_router, router, "probe_router",
|
||||
true);
|
||||
#endif
|
||||
#ifdef ZMQ_RATE
|
||||
check_integral_opt<int>(zmq::sockopt::rate, router, "rate");
|
||||
#endif
|
||||
#ifdef ZMQ_RCVBUF
|
||||
check_integral_opt<int>(zmq::sockopt::rcvbuf, router, "rcvbuf");
|
||||
#endif
|
||||
#ifdef ZMQ_RCVHWM
|
||||
check_integral_opt<int>(zmq::sockopt::rcvhwm, router, "rcvhwm");
|
||||
#endif
|
||||
#ifdef ZMQ_RCVMORE
|
||||
check_integral_opt_get<int>(zmq::sockopt::rcvmore, router, "rcvmore");
|
||||
#endif
|
||||
#ifdef ZMQ_RCVTIMEO
|
||||
check_integral_opt<int>(zmq::sockopt::rcvtimeo, router, "rcvtimeo");
|
||||
#endif
|
||||
#ifdef ZMQ_RECONNECT_IVL
|
||||
check_integral_opt<int>(zmq::sockopt::reconnect_ivl, router, "reconnect_ivl");
|
||||
#endif
|
||||
#ifdef ZMQ_RECONNECT_IVL_MAX
|
||||
check_integral_opt<int>(zmq::sockopt::reconnect_ivl_max, router,
|
||||
"reconnect_ivl_max");
|
||||
#endif
|
||||
#ifdef ZMQ_RECOVERY_IVL
|
||||
check_integral_opt<int>(zmq::sockopt::recovery_ivl, router, "recovery_ivl");
|
||||
#endif
|
||||
#ifdef ZMQ_REQ_CORRELATE
|
||||
check_integral_opt<int>(zmq::sockopt::req_correlate, req, "req_correlate", true);
|
||||
#endif
|
||||
#ifdef ZMQ_REQ_RELAXED
|
||||
check_integral_opt<int>(zmq::sockopt::req_relaxed, req, "req_relaxed", true);
|
||||
#endif
|
||||
#ifdef ZMQ_ROUTER_HANDOVER
|
||||
check_integral_opt<int>(zmq::sockopt::router_handover, router, "router_handover",
|
||||
true);
|
||||
#endif
|
||||
#ifdef ZMQ_ROUTER_MANDATORY
|
||||
check_integral_opt<int>(zmq::sockopt::router_mandatory, router,
|
||||
"router_mandatory", true);
|
||||
#endif
|
||||
#ifdef ZMQ_ROUTER_NOTIFY
|
||||
check_integral_opt<int>(zmq::sockopt::router_notify, router, "router_notify");
|
||||
#endif
|
||||
#ifdef ZMQ_SNDBUF
|
||||
check_integral_opt<int>(zmq::sockopt::sndbuf, router, "sndbuf");
|
||||
#endif
|
||||
#ifdef ZMQ_SNDHWM
|
||||
check_integral_opt<int>(zmq::sockopt::sndhwm, router, "sndhwm");
|
||||
#endif
|
||||
#ifdef ZMQ_SNDTIMEO
|
||||
check_integral_opt<int>(zmq::sockopt::sndtimeo, router, "sndtimeo");
|
||||
#endif
|
||||
#ifdef ZMQ_STREAM_NOTIFY
|
||||
check_integral_opt<int>(zmq::sockopt::stream_notify, stream, "stream_notify",
|
||||
true);
|
||||
#endif
|
||||
#ifdef ZMQ_TCP_KEEPALIVE
|
||||
check_integral_opt<int>(zmq::sockopt::tcp_keepalive, router, "tcp_keepalive");
|
||||
#endif
|
||||
#ifdef ZMQ_TCP_KEEPALIVE_CNT
|
||||
check_integral_opt<int>(zmq::sockopt::tcp_keepalive_cnt, router,
|
||||
"tcp_keepalive_cnt");
|
||||
#endif
|
||||
#ifdef ZMQ_TCP_KEEPALIVE_IDLE
|
||||
check_integral_opt<int>(zmq::sockopt::tcp_keepalive_idle, router,
|
||||
"tcp_keepalive_idle");
|
||||
#endif
|
||||
#ifdef ZMQ_TCP_KEEPALIVE_INTVL
|
||||
check_integral_opt<int>(zmq::sockopt::tcp_keepalive_intvl, router,
|
||||
"tcp_keepalive_intvl");
|
||||
#endif
|
||||
#ifdef ZMQ_TCP_MAXRT
|
||||
check_integral_opt<int>(zmq::sockopt::tcp_maxrt, router, "tcp_maxrt");
|
||||
#endif
|
||||
#ifdef ZMQ_THREAD_SAFE
|
||||
check_integral_opt_get<bool>(zmq::sockopt::thread_safe, router, "thread_safe");
|
||||
#endif
|
||||
#ifdef ZMQ_TOS
|
||||
check_integral_opt<int>(zmq::sockopt::tos, router, "tos");
|
||||
#endif
|
||||
#ifdef ZMQ_TYPE
|
||||
check_integral_opt_get<int>(zmq::sockopt::type, router, "type");
|
||||
#ifdef ZMQ_CPP11
|
||||
check_integral_opt_get<zmq::socket_type>(zmq::sockopt::socket_type, router, "socket_type");
|
||||
#endif // ZMQ_CPP11
|
||||
#endif // ZMQ_TYPE
|
||||
|
||||
#ifdef ZMQ_HAVE_VMCI
|
||||
#ifdef ZMQ_VMCI_BUFFER_SIZE
|
||||
check_integral_opt<uint64_t>(zmq::sockopt::vmci_buffer_size, router,
|
||||
"vmci_buffer_size");
|
||||
#endif
|
||||
#ifdef ZMQ_VMCI_BUFFER_MIN_SIZE
|
||||
check_integral_opt<uint64_t>(zmq::sockopt::vmci_buffer_min_size, router,
|
||||
"vmci_buffer_min_size");
|
||||
#endif
|
||||
#ifdef ZMQ_VMCI_BUFFER_MAX_SIZE
|
||||
check_integral_opt<uint64_t>(zmq::sockopt::vmci_buffer_max_size, router,
|
||||
"vmci_buffer_max_size");
|
||||
#endif
|
||||
#ifdef ZMQ_VMCI_CONNECT_TIMEOUT
|
||||
check_integral_opt<int>(zmq::sockopt::vmci_connect_timeout, router,
|
||||
"vmci_connect_timeout");
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef ZMQ_XPUB_VERBOSE
|
||||
check_integral_opt<int>(zmq::sockopt::xpub_verbose, xpub, "xpub_verbose", true);
|
||||
#endif
|
||||
#ifdef ZMQ_XPUB_VERBOSER
|
||||
check_integral_opt<int>(zmq::sockopt::xpub_verboser, xpub, "xpub_verboser",
|
||||
true);
|
||||
#endif
|
||||
#ifdef ZMQ_XPUB_MANUAL
|
||||
check_integral_opt<int>(zmq::sockopt::xpub_manual, xpub, "xpub_manual", true);
|
||||
#endif
|
||||
#ifdef ZMQ_XPUB_NODROP
|
||||
check_integral_opt<int>(zmq::sockopt::xpub_nodrop, xpub, "xpub_nodrop", true);
|
||||
#endif
|
||||
#ifdef ZMQ_ZAP_ENFORCE_DOMAIN
|
||||
check_integral_opt<int>(zmq::sockopt::zap_enforce_domain, router,
|
||||
"zap_enforce_domain");
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
TEST_CASE("socket flags", "[socket]")
|
||||
{
|
||||
CHECK((zmq::recv_flags::dontwait | zmq::recv_flags::none)
|
||||
== static_cast<zmq::recv_flags>(ZMQ_DONTWAIT | 0));
|
||||
CHECK((zmq::recv_flags::dontwait & zmq::recv_flags::none)
|
||||
== static_cast<zmq::recv_flags>(ZMQ_DONTWAIT & 0));
|
||||
CHECK((zmq::recv_flags::dontwait ^ zmq::recv_flags::none)
|
||||
== static_cast<zmq::recv_flags>(ZMQ_DONTWAIT ^ 0));
|
||||
CHECK(~zmq::recv_flags::dontwait == static_cast<zmq::recv_flags>(~ZMQ_DONTWAIT));
|
||||
|
||||
CHECK((zmq::send_flags::dontwait | zmq::send_flags::sndmore)
|
||||
== static_cast<zmq::send_flags>(ZMQ_DONTWAIT | ZMQ_SNDMORE));
|
||||
CHECK((zmq::send_flags::dontwait & zmq::send_flags::sndmore)
|
||||
== static_cast<zmq::send_flags>(ZMQ_DONTWAIT & ZMQ_SNDMORE));
|
||||
CHECK((zmq::send_flags::dontwait ^ zmq::send_flags::sndmore)
|
||||
== static_cast<zmq::send_flags>(ZMQ_DONTWAIT ^ ZMQ_SNDMORE));
|
||||
CHECK(~zmq::send_flags::dontwait == static_cast<zmq::send_flags>(~ZMQ_DONTWAIT));
|
||||
}
|
||||
|
||||
TEST_CASE("socket readme example", "[socket]")
|
||||
{
|
||||
zmq::context_t ctx;
|
||||
zmq::socket_t sock(ctx, zmq::socket_type::push);
|
||||
sock.bind("inproc://test");
|
||||
sock.send(zmq::str_buffer("Hello, world"), zmq::send_flags::dontwait);
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_CASE("socket sends and receives const buffer", "[socket]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::socket_t sender(context, ZMQ_PAIR);
|
||||
zmq::socket_t receiver(context, ZMQ_PAIR);
|
||||
receiver.bind("inproc://test");
|
||||
sender.connect("inproc://test");
|
||||
const char *str = "Hi";
|
||||
|
||||
#ifdef ZMQ_CPP11
|
||||
CHECK(2 == *sender.send(zmq::buffer(str, 2)));
|
||||
char buf[2];
|
||||
const auto res = receiver.recv(zmq::buffer(buf));
|
||||
CHECK(res);
|
||||
CHECK(!res->truncated());
|
||||
CHECK(2 == res->size);
|
||||
#else
|
||||
CHECK(2 == sender.send(str, 2));
|
||||
char buf[2];
|
||||
CHECK(2 == receiver.recv(buf, 2));
|
||||
#endif
|
||||
CHECK(0 == memcmp(buf, str, 2));
|
||||
}
|
||||
|
||||
#ifdef ZMQ_CPP11
|
||||
|
||||
TEST_CASE("socket send none sndmore", "[socket]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::socket_t s(context, zmq::socket_type::router);
|
||||
s.bind("inproc://test");
|
||||
|
||||
std::vector<char> buf(4);
|
||||
auto res = s.send(zmq::buffer(buf), zmq::send_flags::sndmore);
|
||||
CHECK(res);
|
||||
CHECK(*res == buf.size());
|
||||
res = s.send(zmq::buffer(buf));
|
||||
CHECK(res);
|
||||
CHECK(*res == buf.size());
|
||||
}
|
||||
|
||||
TEST_CASE("socket send dontwait", "[socket]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::socket_t s(context, zmq::socket_type::push);
|
||||
s.bind("inproc://test");
|
||||
|
||||
std::vector<char> buf(4);
|
||||
auto res = s.send(zmq::buffer(buf), zmq::send_flags::dontwait);
|
||||
CHECK(!res);
|
||||
res =
|
||||
s.send(zmq::buffer(buf), zmq::send_flags::dontwait | zmq::send_flags::sndmore);
|
||||
CHECK(!res);
|
||||
|
||||
zmq::message_t msg;
|
||||
auto resm = s.send(msg, zmq::send_flags::dontwait);
|
||||
CHECK(!resm);
|
||||
CHECK(msg.size() == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("socket send exception", "[socket]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::socket_t s(context, zmq::socket_type::pull);
|
||||
s.bind("inproc://test");
|
||||
|
||||
std::vector<char> buf(4);
|
||||
CHECK_THROWS_AS(s.send(zmq::buffer(buf)), zmq::error_t);
|
||||
}
|
||||
|
||||
TEST_CASE("socket recv none", "[socket]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::socket_t s(context, zmq::socket_type::pair);
|
||||
zmq::socket_t s2(context, zmq::socket_type::pair);
|
||||
s2.bind("inproc://test");
|
||||
s.connect("inproc://test");
|
||||
|
||||
std::vector<char> sbuf(4);
|
||||
const auto res_send = s2.send(zmq::buffer(sbuf));
|
||||
CHECK(res_send);
|
||||
CHECK(res_send.has_value());
|
||||
|
||||
std::vector<char> buf(2);
|
||||
const auto res = s.recv(zmq::buffer(buf));
|
||||
CHECK(res.has_value());
|
||||
CHECK(res->truncated());
|
||||
CHECK(res->untruncated_size == sbuf.size());
|
||||
CHECK(res->size == buf.size());
|
||||
|
||||
const auto res_send2 = s2.send(zmq::buffer(sbuf));
|
||||
CHECK(res_send2.has_value());
|
||||
std::vector<char> buf2(10);
|
||||
const auto res2 = s.recv(zmq::buffer(buf2));
|
||||
CHECK(res2.has_value());
|
||||
CHECK(!res2->truncated());
|
||||
CHECK(res2->untruncated_size == sbuf.size());
|
||||
CHECK(res2->size == sbuf.size());
|
||||
}
|
||||
|
||||
TEST_CASE("socket send recv message_t", "[socket]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::socket_t s(context, zmq::socket_type::pair);
|
||||
zmq::socket_t s2(context, zmq::socket_type::pair);
|
||||
s2.bind("inproc://test");
|
||||
s.connect("inproc://test");
|
||||
|
||||
zmq::message_t smsg(10);
|
||||
const auto res_send = s2.send(smsg, zmq::send_flags::none);
|
||||
CHECK(res_send);
|
||||
CHECK(*res_send == 10);
|
||||
CHECK(smsg.size() == 0);
|
||||
|
||||
zmq::message_t rmsg;
|
||||
const auto res = s.recv(rmsg);
|
||||
CHECK(res);
|
||||
CHECK(*res == 10);
|
||||
CHECK(res.value() == 10);
|
||||
CHECK(rmsg.size() == *res);
|
||||
}
|
||||
|
||||
TEST_CASE("socket send recv message_t by pointer", "[socket]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::socket_t s(context, zmq::socket_type::pair);
|
||||
zmq::socket_t s2(context, zmq::socket_type::pair);
|
||||
s2.bind("inproc://test");
|
||||
s.connect("inproc://test");
|
||||
|
||||
zmq::message_t smsg(size_t{10});
|
||||
const auto res_send = s2.send(smsg, zmq::send_flags::none);
|
||||
CHECK(res_send);
|
||||
CHECK(*res_send == 10);
|
||||
CHECK(smsg.size() == 0);
|
||||
|
||||
zmq::message_t rmsg;
|
||||
const bool res = s.recv(&rmsg);
|
||||
CHECK(res);
|
||||
}
|
||||
|
||||
TEST_CASE("socket recv dontwait", "[socket]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::socket_t s(context, zmq::socket_type::pull);
|
||||
s.bind("inproc://test");
|
||||
|
||||
std::vector<char> buf(4);
|
||||
constexpr auto flags = zmq::recv_flags::none | zmq::recv_flags::dontwait;
|
||||
auto res = s.recv(zmq::buffer(buf), flags);
|
||||
CHECK(!res);
|
||||
|
||||
zmq::message_t msg;
|
||||
auto resm = s.recv(msg, flags);
|
||||
CHECK(!resm);
|
||||
CHECK_THROWS_AS(resm.value(), std::exception);
|
||||
CHECK(msg.size() == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("socket recv exception", "[socket]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::socket_t s(context, zmq::socket_type::push);
|
||||
s.bind("inproc://test");
|
||||
|
||||
std::vector<char> buf(4);
|
||||
CHECK_THROWS_AS(s.recv(zmq::buffer(buf)), zmq::error_t);
|
||||
}
|
||||
|
||||
TEST_CASE("socket proxy", "[socket]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::socket_t front(context, ZMQ_ROUTER);
|
||||
zmq::socket_t back(context, ZMQ_ROUTER);
|
||||
zmq::socket_t capture(context, ZMQ_DEALER);
|
||||
front.bind("inproc://test1");
|
||||
back.bind("inproc://test2");
|
||||
capture.bind("inproc://test3");
|
||||
auto f = std::async(std::launch::async, [&]() {
|
||||
auto s1 = std::move(front);
|
||||
auto s2 = std::move(back);
|
||||
auto s3 = std::move(capture);
|
||||
try {
|
||||
zmq::proxy(s1, s2, zmq::socket_ref(s3));
|
||||
}
|
||||
catch (const zmq::error_t &e) {
|
||||
return e.num() == ETERM;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
context.close();
|
||||
CHECK(f.get());
|
||||
}
|
||||
|
||||
TEST_CASE("socket proxy steerable", "[socket]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::socket_t front(context, ZMQ_ROUTER);
|
||||
zmq::socket_t back(context, ZMQ_ROUTER);
|
||||
zmq::socket_t control(context, ZMQ_SUB);
|
||||
front.bind("inproc://test1");
|
||||
back.bind("inproc://test2");
|
||||
control.connect("inproc://test3");
|
||||
auto f = std::async(std::launch::async, [&]() {
|
||||
auto s1 = std::move(front);
|
||||
auto s2 = std::move(back);
|
||||
auto s3 = std::move(control);
|
||||
try {
|
||||
zmq::proxy_steerable(s1, s2, zmq::socket_ref(), s3);
|
||||
}
|
||||
catch (const zmq::error_t &e) {
|
||||
return e.num() == ETERM;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
context.close();
|
||||
CHECK(f.get());
|
||||
}
|
||||
#endif
|
||||
118
libs/cppzmq/tests/socket_ref.cpp
Normal file
118
libs/cppzmq/tests/socket_ref.cpp
Normal file
@@ -0,0 +1,118 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <zmq.hpp>
|
||||
#ifdef ZMQ_CPP11
|
||||
|
||||
#ifdef ZMQ_CPP17
|
||||
static_assert(std::is_nothrow_swappable_v<zmq::socket_ref>);
|
||||
#endif
|
||||
static_assert(sizeof(zmq::socket_ref) == sizeof(void *), "size mismatch");
|
||||
static_assert(alignof(zmq::socket_ref) == alignof(void *), "alignment mismatch");
|
||||
static_assert(ZMQ_IS_TRIVIALLY_COPYABLE(zmq::socket_ref),
|
||||
"needs to be trivially copyable");
|
||||
|
||||
TEST_CASE("socket_ref default init", "[socket_ref]")
|
||||
{
|
||||
zmq::socket_ref sr;
|
||||
CHECK(!sr);
|
||||
CHECK(sr == nullptr);
|
||||
CHECK(nullptr == sr);
|
||||
CHECK(sr.handle() == nullptr);
|
||||
}
|
||||
|
||||
TEST_CASE("socket_ref create from nullptr", "[socket_ref]")
|
||||
{
|
||||
zmq::socket_ref sr = nullptr;
|
||||
CHECK(sr == nullptr);
|
||||
CHECK(sr.handle() == nullptr);
|
||||
}
|
||||
|
||||
TEST_CASE("socket_ref create from handle", "[socket_ref]")
|
||||
{
|
||||
void *np = nullptr;
|
||||
zmq::socket_ref sr{zmq::from_handle, np};
|
||||
CHECK(sr == nullptr);
|
||||
CHECK(sr.handle() == nullptr);
|
||||
}
|
||||
|
||||
TEST_CASE("socket_ref compare", "[socket_ref]")
|
||||
{
|
||||
zmq::socket_ref sr1;
|
||||
zmq::socket_ref sr2;
|
||||
CHECK(sr1 == sr2);
|
||||
CHECK(!(sr1 != sr2));
|
||||
}
|
||||
|
||||
TEST_CASE("socket_ref compare from socket_t", "[socket_ref]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::socket_t s1(context, zmq::socket_type::router);
|
||||
zmq::socket_t s2(context, zmq::socket_type::dealer);
|
||||
zmq::socket_ref sr1 = s1;
|
||||
zmq::socket_ref sr2 = s2;
|
||||
CHECK(sr1);
|
||||
CHECK(sr2);
|
||||
CHECK(sr1 == s1);
|
||||
CHECK(sr2 == s2);
|
||||
CHECK(sr1.handle() == s1.handle());
|
||||
CHECK(sr1 != sr2);
|
||||
CHECK(sr1.handle() != sr2.handle());
|
||||
CHECK(sr1 != nullptr);
|
||||
CHECK(nullptr != sr1);
|
||||
CHECK(sr2 != nullptr);
|
||||
const bool comp1 = (sr1 < sr2) != (sr1 >= sr2);
|
||||
CHECK(comp1);
|
||||
const bool comp2 = (sr1 > sr2) != (sr1 <= sr2);
|
||||
CHECK(comp2);
|
||||
std::hash<zmq::socket_ref> hash;
|
||||
CHECK(hash(sr1) != hash(sr2));
|
||||
CHECK(hash(sr1) == hash(s1));
|
||||
}
|
||||
|
||||
TEST_CASE("socket_ref assignment", "[socket_ref]")
|
||||
{
|
||||
zmq::context_t context;
|
||||
zmq::socket_t s1(context, zmq::socket_type::router);
|
||||
zmq::socket_t s2(context, zmq::socket_type::dealer);
|
||||
zmq::socket_ref sr1 = s1;
|
||||
zmq::socket_ref sr2 = s2;
|
||||
sr1 = s2;
|
||||
CHECK(sr1 == sr2);
|
||||
CHECK(sr1.handle() == sr2.handle());
|
||||
sr1 = std::move(sr2);
|
||||
CHECK(sr1 == sr2);
|
||||
CHECK(sr1.handle() == sr2.handle());
|
||||
sr2 = nullptr;
|
||||
CHECK(sr1 != sr2);
|
||||
sr1 = nullptr;
|
||||
CHECK(sr1 == sr2);
|
||||
}
|
||||
|
||||
TEST_CASE("socket_ref swap", "[socket_ref]")
|
||||
{
|
||||
zmq::socket_ref sr1;
|
||||
zmq::socket_ref sr2;
|
||||
using std::swap;
|
||||
swap(sr1, sr2);
|
||||
}
|
||||
|
||||
TEST_CASE("socket_ref type punning", "[socket_ref]")
|
||||
{
|
||||
struct SVP
|
||||
{
|
||||
void *p;
|
||||
} svp;
|
||||
struct SSR
|
||||
{
|
||||
zmq::socket_ref sr;
|
||||
} ssr;
|
||||
|
||||
zmq::context_t context;
|
||||
zmq::socket_t socket(context, zmq::socket_type::router);
|
||||
CHECK(socket.handle() != nullptr);
|
||||
svp.p = socket.handle();
|
||||
// static_cast to silence incorrect warning
|
||||
std::memcpy(static_cast<void *>(&ssr), &svp, sizeof(ssr));
|
||||
CHECK(ssr.sr == socket);
|
||||
}
|
||||
|
||||
#endif
|
||||
54
libs/cppzmq/tests/testutil.hpp
Normal file
54
libs/cppzmq/tests/testutil.hpp
Normal file
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include <catch2/catch.hpp>
|
||||
#include <zmq.hpp>
|
||||
|
||||
#if defined(ZMQ_CPP11)
|
||||
|
||||
inline std::string bind_ip4_loopback(zmq::socket_t &socket)
|
||||
{
|
||||
socket.bind("tcp://127.0.0.1:*");
|
||||
std::string endpoint(100, ' ');
|
||||
endpoint.resize(socket.get(zmq::sockopt::last_endpoint, zmq::buffer(endpoint)));
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
struct common_server_client_setup
|
||||
{
|
||||
common_server_client_setup(bool initialize = true)
|
||||
{
|
||||
if (initialize)
|
||||
init();
|
||||
}
|
||||
|
||||
void init()
|
||||
{
|
||||
endpoint = bind_ip4_loopback(server);
|
||||
REQUIRE_NOTHROW(client.connect(endpoint));
|
||||
}
|
||||
|
||||
zmq::context_t context;
|
||||
zmq::socket_t server{context, zmq::socket_type::pair};
|
||||
zmq::socket_t client{context, zmq::socket_type::pair};
|
||||
std::string endpoint;
|
||||
};
|
||||
#endif
|
||||
|
||||
#define CHECK_THROWS_ZMQ_ERROR(ecode, expr) \
|
||||
do { \
|
||||
try { \
|
||||
expr; \
|
||||
CHECK(false); \
|
||||
} \
|
||||
catch (const zmq::error_t &ze) { \
|
||||
INFO(std::string("Unexpected error code: ") + ze.what()); \
|
||||
CHECK(ze.num() == ecode); \
|
||||
} \
|
||||
catch (const std::exception &ex) { \
|
||||
INFO(std::string("Unexpected exception: ") + ex.what()); \
|
||||
CHECK(false); \
|
||||
} \
|
||||
catch (...) { \
|
||||
CHECK(false); \
|
||||
} \
|
||||
} while (false)
|
||||
110
libs/cppzmq/tests/utilities.cpp
Normal file
110
libs/cppzmq/tests/utilities.cpp
Normal file
@@ -0,0 +1,110 @@
|
||||
#include <catch2/catch.hpp>
|
||||
#include <zmq.hpp>
|
||||
|
||||
#if defined(ZMQ_CPP11) && !defined(ZMQ_CPP11_PARTIAL)
|
||||
|
||||
namespace test_ns
|
||||
{
|
||||
struct T_nr
|
||||
{
|
||||
};
|
||||
|
||||
struct T_mr
|
||||
{
|
||||
void *begin() const noexcept { return nullptr; }
|
||||
void *end() const noexcept { return nullptr; }
|
||||
};
|
||||
|
||||
struct T_fr
|
||||
{
|
||||
};
|
||||
|
||||
inline void *begin(const T_fr &) noexcept
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline void *end(const T_fr &) noexcept
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
struct T_mfr
|
||||
{
|
||||
void *begin() const noexcept { return nullptr; }
|
||||
void *end() const noexcept { return nullptr; }
|
||||
};
|
||||
|
||||
inline void *begin(const T_mfr &) noexcept
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline void *end(const T_mfr &) noexcept
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// types with associated namespace std
|
||||
struct T_assoc_ns_nr : std::exception
|
||||
{
|
||||
};
|
||||
|
||||
struct T_assoc_ns_mr : std::exception
|
||||
{
|
||||
void *begin() const noexcept { return nullptr; }
|
||||
void *end() const noexcept { return nullptr; }
|
||||
};
|
||||
|
||||
struct T_assoc_ns_fr : std::exception
|
||||
{
|
||||
};
|
||||
|
||||
inline void *begin(const T_assoc_ns_fr &) noexcept
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline void *end(const T_assoc_ns_fr &) noexcept
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
struct T_assoc_ns_mfr : std::exception
|
||||
{
|
||||
void *begin() const noexcept { return nullptr; }
|
||||
void *end() const noexcept { return nullptr; }
|
||||
};
|
||||
|
||||
inline void *begin(const T_assoc_ns_mfr &) noexcept
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline void *end(const T_assoc_ns_mfr &) noexcept
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
} // namespace test_ns
|
||||
|
||||
TEST_CASE("range SFINAE", "[utilities]")
|
||||
{
|
||||
CHECK(!zmq::detail::is_range<int>::value);
|
||||
CHECK(zmq::detail::is_range<std::string>::value);
|
||||
CHECK(zmq::detail::is_range<std::string &>::value);
|
||||
CHECK(zmq::detail::is_range<const std::string &>::value);
|
||||
CHECK(zmq::detail::is_range<decltype("hello")>::value);
|
||||
CHECK(zmq::detail::is_range<std::initializer_list<int>>::value);
|
||||
|
||||
CHECK(!zmq::detail::is_range<test_ns::T_nr>::value);
|
||||
CHECK(zmq::detail::is_range<test_ns::T_mr>::value);
|
||||
CHECK(zmq::detail::is_range<test_ns::T_fr>::value);
|
||||
CHECK(zmq::detail::is_range<test_ns::T_mfr>::value);
|
||||
|
||||
CHECK(!zmq::detail::is_range<test_ns::T_assoc_ns_nr>::value);
|
||||
CHECK(zmq::detail::is_range<test_ns::T_assoc_ns_mr>::value);
|
||||
CHECK(zmq::detail::is_range<test_ns::T_assoc_ns_fr>::value);
|
||||
CHECK(zmq::detail::is_range<test_ns::T_assoc_ns_mfr>::value);
|
||||
}
|
||||
|
||||
#endif
|
||||
21
libs/cppzmq/version.sh
Executable file
21
libs/cppzmq/version.sh
Executable file
@@ -0,0 +1,21 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# This script extracts the 0MQ version from zmq.hpp, which is the master
|
||||
# location for this information.
|
||||
#
|
||||
if [ ! -f zmq.hpp ]; then
|
||||
echo "version.sh: error: zmq.hpp does not exist" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
MAJOR=$(grep '^#define CPPZMQ_VERSION_MAJOR \+[0-9]\+' zmq.hpp)
|
||||
MINOR=$(grep '^#define CPPZMQ_VERSION_MINOR \+[0-9]\+' zmq.hpp)
|
||||
PATCH=$(grep '^#define CPPZMQ_VERSION_PATCH \+[0-9]\+' zmq.hpp)
|
||||
if [ -z "$MAJOR" -o -z "$MINOR" -o -z "$PATCH" ]; then
|
||||
echo "version.sh: error: could not extract version from zmq.hpp" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
MAJOR=$(echo $MAJOR | awk '{ print $3 }')
|
||||
MINOR=$(echo $MINOR | awk '{ print $3 }')
|
||||
PATCH=$(echo $PATCH | awk '{ print $3 }')
|
||||
echo $MAJOR.$MINOR.$PATCH | tr -d '\n\r'
|
||||
|
||||
2721
libs/cppzmq/zmq.hpp
Normal file
2721
libs/cppzmq/zmq.hpp
Normal file
File diff suppressed because it is too large
Load Diff
753
libs/cppzmq/zmq_addon.hpp
Normal file
753
libs/cppzmq/zmq_addon.hpp
Normal file
@@ -0,0 +1,753 @@
|
||||
/*
|
||||
Copyright (c) 2016-2017 ZeroMQ community
|
||||
Copyright (c) 2016 VOCA AS / Harald Nøkland
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
deal in the Software without restriction, including without limitation the
|
||||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
sell copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef __ZMQ_ADDON_HPP_INCLUDED__
|
||||
#define __ZMQ_ADDON_HPP_INCLUDED__
|
||||
|
||||
#include "zmq.hpp"
|
||||
|
||||
#include <deque>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#ifdef ZMQ_CPP11
|
||||
#include <limits>
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
#endif
|
||||
|
||||
namespace zmq
|
||||
{
|
||||
#ifdef ZMQ_CPP11
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template<bool CheckN, class OutputIt>
|
||||
recv_result_t
|
||||
recv_multipart_n(socket_ref s, OutputIt out, size_t n, recv_flags flags)
|
||||
{
|
||||
size_t msg_count = 0;
|
||||
message_t msg;
|
||||
while (true) {
|
||||
if ZMQ_CONSTEXPR_IF (CheckN) {
|
||||
if (msg_count >= n)
|
||||
throw std::runtime_error(
|
||||
"Too many message parts in recv_multipart_n");
|
||||
}
|
||||
if (!s.recv(msg, flags)) {
|
||||
// zmq ensures atomic delivery of messages
|
||||
assert(msg_count == 0);
|
||||
return {};
|
||||
}
|
||||
++msg_count;
|
||||
const bool more = msg.more();
|
||||
*out++ = std::move(msg);
|
||||
if (!more)
|
||||
break;
|
||||
}
|
||||
return msg_count;
|
||||
}
|
||||
|
||||
inline bool is_little_endian()
|
||||
{
|
||||
const uint16_t i = 0x01;
|
||||
return *reinterpret_cast<const uint8_t *>(&i) == 0x01;
|
||||
}
|
||||
|
||||
inline void write_network_order(unsigned char *buf, const uint32_t value)
|
||||
{
|
||||
if (is_little_endian()) {
|
||||
ZMQ_CONSTEXPR_VAR uint32_t mask = (std::numeric_limits<std::uint8_t>::max)();
|
||||
*buf++ = static_cast<unsigned char>((value >> 24) & mask);
|
||||
*buf++ = static_cast<unsigned char>((value >> 16) & mask);
|
||||
*buf++ = static_cast<unsigned char>((value >> 8) & mask);
|
||||
*buf++ = static_cast<unsigned char>(value & mask);
|
||||
} else {
|
||||
std::memcpy(buf, &value, sizeof(value));
|
||||
}
|
||||
}
|
||||
|
||||
inline uint32_t read_u32_network_order(const unsigned char *buf)
|
||||
{
|
||||
if (is_little_endian()) {
|
||||
return (static_cast<uint32_t>(buf[0]) << 24)
|
||||
+ (static_cast<uint32_t>(buf[1]) << 16)
|
||||
+ (static_cast<uint32_t>(buf[2]) << 8)
|
||||
+ static_cast<uint32_t>(buf[3]);
|
||||
} else {
|
||||
uint32_t value;
|
||||
std::memcpy(&value, buf, sizeof(value));
|
||||
return value;
|
||||
}
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
/* Receive a multipart message.
|
||||
|
||||
Writes the zmq::message_t objects to OutputIterator out.
|
||||
The out iterator must handle an unspecified number of writes,
|
||||
e.g. by using std::back_inserter.
|
||||
|
||||
Returns: the number of messages received or nullopt (on EAGAIN).
|
||||
Throws: if recv throws. Any exceptions thrown
|
||||
by the out iterator will be propagated and the message
|
||||
may have been only partially received with pending
|
||||
message parts. It is adviced to close this socket in that event.
|
||||
*/
|
||||
template<class OutputIt>
|
||||
ZMQ_NODISCARD recv_result_t recv_multipart(socket_ref s,
|
||||
OutputIt out,
|
||||
recv_flags flags = recv_flags::none)
|
||||
{
|
||||
return detail::recv_multipart_n<false>(s, std::move(out), 0, flags);
|
||||
}
|
||||
|
||||
/* Receive a multipart message.
|
||||
|
||||
Writes at most n zmq::message_t objects to OutputIterator out.
|
||||
If the number of message parts of the incoming message exceeds n
|
||||
then an exception will be thrown.
|
||||
|
||||
Returns: the number of messages received or nullopt (on EAGAIN).
|
||||
Throws: if recv throws. Throws std::runtime_error if the number
|
||||
of message parts exceeds n (exactly n messages will have been written
|
||||
to out). Any exceptions thrown
|
||||
by the out iterator will be propagated and the message
|
||||
may have been only partially received with pending
|
||||
message parts. It is adviced to close this socket in that event.
|
||||
*/
|
||||
template<class OutputIt>
|
||||
ZMQ_NODISCARD recv_result_t recv_multipart_n(socket_ref s,
|
||||
OutputIt out,
|
||||
size_t n,
|
||||
recv_flags flags = recv_flags::none)
|
||||
{
|
||||
return detail::recv_multipart_n<true>(s, std::move(out), n, flags);
|
||||
}
|
||||
|
||||
/* Send a multipart message.
|
||||
|
||||
The range must be a ForwardRange of zmq::message_t,
|
||||
zmq::const_buffer or zmq::mutable_buffer.
|
||||
The flags may be zmq::send_flags::sndmore if there are
|
||||
more message parts to be sent after the call to this function.
|
||||
|
||||
Returns: the number of messages sent (exactly msgs.size()) or nullopt (on EAGAIN).
|
||||
Throws: if send throws. Any exceptions thrown
|
||||
by the msgs range will be propagated and the message
|
||||
may have been only partially sent. It is adviced to close this socket in that event.
|
||||
*/
|
||||
template<class Range
|
||||
#ifndef ZMQ_CPP11_PARTIAL
|
||||
,
|
||||
typename = typename std::enable_if<
|
||||
detail::is_range<Range>::value
|
||||
&& (std::is_same<detail::range_value_t<Range>, message_t>::value
|
||||
|| detail::is_buffer<detail::range_value_t<Range>>::value)>::type
|
||||
#endif
|
||||
>
|
||||
send_result_t
|
||||
send_multipart(socket_ref s, Range &&msgs, send_flags flags = send_flags::none)
|
||||
{
|
||||
using std::begin;
|
||||
using std::end;
|
||||
auto it = begin(msgs);
|
||||
const auto end_it = end(msgs);
|
||||
size_t msg_count = 0;
|
||||
while (it != end_it) {
|
||||
const auto next = std::next(it);
|
||||
const auto msg_flags =
|
||||
flags | (next == end_it ? send_flags::none : send_flags::sndmore);
|
||||
if (!s.send(*it, msg_flags)) {
|
||||
// zmq ensures atomic delivery of messages
|
||||
assert(it == begin(msgs));
|
||||
return {};
|
||||
}
|
||||
++msg_count;
|
||||
it = next;
|
||||
}
|
||||
return msg_count;
|
||||
}
|
||||
|
||||
/* Encode a multipart message.
|
||||
|
||||
The range must be a ForwardRange of zmq::message_t. A
|
||||
zmq::multipart_t or STL container may be passed for encoding.
|
||||
|
||||
Returns: a zmq::message_t holding the encoded multipart data.
|
||||
|
||||
Throws: std::range_error is thrown if the size of any single part
|
||||
can not fit in an unsigned 32 bit integer.
|
||||
|
||||
The encoding is compatible with that used by the CZMQ function
|
||||
zmsg_encode(), see https://rfc.zeromq.org/spec/50/.
|
||||
Each part consists of a size followed by the data.
|
||||
These are placed contiguously into the output message. A part of
|
||||
size less than 255 bytes will have a single byte size value.
|
||||
Larger parts will have a five byte size value with the first byte
|
||||
set to 0xFF and the remaining four bytes holding the size of the
|
||||
part's data.
|
||||
*/
|
||||
template<class Range
|
||||
#ifndef ZMQ_CPP11_PARTIAL
|
||||
,
|
||||
typename = typename std::enable_if<
|
||||
detail::is_range<Range>::value
|
||||
&& (std::is_same<detail::range_value_t<Range>, message_t>::value
|
||||
|| detail::is_buffer<detail::range_value_t<Range>>::value)>::type
|
||||
#endif
|
||||
>
|
||||
message_t encode(const Range &parts)
|
||||
{
|
||||
size_t mmsg_size = 0;
|
||||
|
||||
// First pass check sizes
|
||||
for (const auto &part : parts) {
|
||||
const size_t part_size = part.size();
|
||||
if (part_size > (std::numeric_limits<std::uint32_t>::max)()) {
|
||||
// Size value must fit into uint32_t.
|
||||
throw std::range_error("Invalid size, message part too large");
|
||||
}
|
||||
const size_t count_size =
|
||||
part_size < (std::numeric_limits<std::uint8_t>::max)() ? 1 : 5;
|
||||
mmsg_size += part_size + count_size;
|
||||
}
|
||||
|
||||
message_t encoded(mmsg_size);
|
||||
unsigned char *buf = encoded.data<unsigned char>();
|
||||
for (const auto &part : parts) {
|
||||
const uint32_t part_size = static_cast<uint32_t>(part.size());
|
||||
const unsigned char *part_data =
|
||||
static_cast<const unsigned char *>(part.data());
|
||||
|
||||
if (part_size < (std::numeric_limits<std::uint8_t>::max)()) {
|
||||
// small part
|
||||
*buf++ = (unsigned char) part_size;
|
||||
} else {
|
||||
// big part
|
||||
*buf++ = (std::numeric_limits<uint8_t>::max)();
|
||||
detail::write_network_order(buf, part_size);
|
||||
buf += sizeof(part_size);
|
||||
}
|
||||
std::memcpy(buf, part_data, part_size);
|
||||
buf += part_size;
|
||||
}
|
||||
|
||||
assert(static_cast<size_t>(buf - encoded.data<unsigned char>()) == mmsg_size);
|
||||
return encoded;
|
||||
}
|
||||
|
||||
/* Decode an encoded message to multiple parts.
|
||||
|
||||
The given output iterator must be a ForwardIterator to a container
|
||||
holding zmq::message_t such as a zmq::multipart_t or various STL
|
||||
containers.
|
||||
|
||||
Returns the ForwardIterator advanced once past the last decoded
|
||||
part.
|
||||
|
||||
Throws: a std::out_of_range is thrown if the encoded part sizes
|
||||
lead to exceeding the message data bounds.
|
||||
|
||||
The decoding assumes the message is encoded in the manner
|
||||
performed by zmq::encode(), see https://rfc.zeromq.org/spec/50/.
|
||||
*/
|
||||
template<class OutputIt> OutputIt decode(const message_t &encoded, OutputIt out)
|
||||
{
|
||||
const unsigned char *source = encoded.data<unsigned char>();
|
||||
const unsigned char *const limit = source + encoded.size();
|
||||
|
||||
while (source < limit) {
|
||||
size_t part_size = *source++;
|
||||
if (part_size == (std::numeric_limits<std::uint8_t>::max)()) {
|
||||
if (static_cast<size_t>(limit - source) < sizeof(uint32_t)) {
|
||||
throw std::out_of_range(
|
||||
"Malformed encoding, overflow in reading size");
|
||||
}
|
||||
part_size = detail::read_u32_network_order(source);
|
||||
// the part size is allowed to be less than 0xFF
|
||||
source += sizeof(uint32_t);
|
||||
}
|
||||
|
||||
if (static_cast<size_t>(limit - source) < part_size) {
|
||||
throw std::out_of_range("Malformed encoding, overflow in reading part");
|
||||
}
|
||||
*out = message_t(source, part_size);
|
||||
++out;
|
||||
source += part_size;
|
||||
}
|
||||
|
||||
assert(source == limit);
|
||||
return out;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef ZMQ_HAS_RVALUE_REFS
|
||||
|
||||
/*
|
||||
This class handles multipart messaging. It is the C++ equivalent of zmsg.h,
|
||||
which is part of CZMQ (the high-level C binding). Furthermore, it is a major
|
||||
improvement compared to zmsg.hpp, which is part of the examples in the ØMQ
|
||||
Guide. Unnecessary copying is avoided by using move semantics to efficiently
|
||||
add/remove parts.
|
||||
*/
|
||||
class multipart_t
|
||||
{
|
||||
private:
|
||||
std::deque<message_t> m_parts;
|
||||
|
||||
public:
|
||||
typedef std::deque<message_t>::value_type value_type;
|
||||
|
||||
typedef std::deque<message_t>::iterator iterator;
|
||||
typedef std::deque<message_t>::const_iterator const_iterator;
|
||||
|
||||
typedef std::deque<message_t>::reverse_iterator reverse_iterator;
|
||||
typedef std::deque<message_t>::const_reverse_iterator const_reverse_iterator;
|
||||
|
||||
// Default constructor
|
||||
multipart_t() {}
|
||||
|
||||
// Construct from socket receive
|
||||
multipart_t(socket_ref socket) { recv(socket); }
|
||||
|
||||
// Construct from memory block
|
||||
multipart_t(const void *src, size_t size) { addmem(src, size); }
|
||||
|
||||
// Construct from string
|
||||
multipart_t(const std::string &string) { addstr(string); }
|
||||
|
||||
// Construct from message part
|
||||
multipart_t(message_t &&message) { add(std::move(message)); }
|
||||
|
||||
// Move constructor
|
||||
multipart_t(multipart_t &&other) ZMQ_NOTHROW { m_parts = std::move(other.m_parts); }
|
||||
|
||||
// Move assignment operator
|
||||
multipart_t &operator=(multipart_t &&other) ZMQ_NOTHROW
|
||||
{
|
||||
m_parts = std::move(other.m_parts);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Destructor
|
||||
virtual ~multipart_t() { clear(); }
|
||||
|
||||
message_t &operator[](size_t n) { return m_parts[n]; }
|
||||
|
||||
const message_t &operator[](size_t n) const { return m_parts[n]; }
|
||||
|
||||
message_t &at(size_t n) { return m_parts.at(n); }
|
||||
|
||||
const message_t &at(size_t n) const { return m_parts.at(n); }
|
||||
|
||||
iterator begin() { return m_parts.begin(); }
|
||||
|
||||
const_iterator begin() const { return m_parts.begin(); }
|
||||
|
||||
const_iterator cbegin() const { return m_parts.cbegin(); }
|
||||
|
||||
reverse_iterator rbegin() { return m_parts.rbegin(); }
|
||||
|
||||
const_reverse_iterator rbegin() const { return m_parts.rbegin(); }
|
||||
|
||||
iterator end() { return m_parts.end(); }
|
||||
|
||||
const_iterator end() const { return m_parts.end(); }
|
||||
|
||||
const_iterator cend() const { return m_parts.cend(); }
|
||||
|
||||
reverse_iterator rend() { return m_parts.rend(); }
|
||||
|
||||
const_reverse_iterator rend() const { return m_parts.rend(); }
|
||||
|
||||
// Delete all parts
|
||||
void clear() { m_parts.clear(); }
|
||||
|
||||
// Get number of parts
|
||||
size_t size() const { return m_parts.size(); }
|
||||
|
||||
// Check if number of parts is zero
|
||||
bool empty() const { return m_parts.empty(); }
|
||||
|
||||
// Receive multipart message from socket
|
||||
bool recv(socket_ref socket, int flags = 0)
|
||||
{
|
||||
clear();
|
||||
bool more = true;
|
||||
while (more) {
|
||||
message_t message;
|
||||
#ifdef ZMQ_CPP11
|
||||
if (!socket.recv(message, static_cast<recv_flags>(flags)))
|
||||
return false;
|
||||
#else
|
||||
if (!socket.recv(&message, flags))
|
||||
return false;
|
||||
#endif
|
||||
more = message.more();
|
||||
add(std::move(message));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Send multipart message to socket
|
||||
bool send(socket_ref socket, int flags = 0)
|
||||
{
|
||||
flags &= ~(ZMQ_SNDMORE);
|
||||
bool more = size() > 0;
|
||||
while (more) {
|
||||
message_t message = pop();
|
||||
more = size() > 0;
|
||||
#ifdef ZMQ_CPP11
|
||||
if (!socket.send(message, static_cast<send_flags>(
|
||||
(more ? ZMQ_SNDMORE : 0) | flags)))
|
||||
return false;
|
||||
#else
|
||||
if (!socket.send(message, (more ? ZMQ_SNDMORE : 0) | flags))
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Concatenate other multipart to front
|
||||
void prepend(multipart_t &&other)
|
||||
{
|
||||
while (!other.empty())
|
||||
push(other.remove());
|
||||
}
|
||||
|
||||
// Concatenate other multipart to back
|
||||
void append(multipart_t &&other)
|
||||
{
|
||||
while (!other.empty())
|
||||
add(other.pop());
|
||||
}
|
||||
|
||||
// Push memory block to front
|
||||
void pushmem(const void *src, size_t size)
|
||||
{
|
||||
m_parts.push_front(message_t(src, size));
|
||||
}
|
||||
|
||||
// Push memory block to back
|
||||
void addmem(const void *src, size_t size)
|
||||
{
|
||||
m_parts.push_back(message_t(src, size));
|
||||
}
|
||||
|
||||
// Push string to front
|
||||
void pushstr(const std::string &string)
|
||||
{
|
||||
m_parts.push_front(message_t(string.data(), string.size()));
|
||||
}
|
||||
|
||||
// Push string to back
|
||||
void addstr(const std::string &string)
|
||||
{
|
||||
m_parts.push_back(message_t(string.data(), string.size()));
|
||||
}
|
||||
|
||||
// Push type (fixed-size) to front
|
||||
template<typename T> void pushtyp(const T &type)
|
||||
{
|
||||
static_assert(!std::is_same<T, std::string>::value,
|
||||
"Use pushstr() instead of pushtyp<std::string>()");
|
||||
m_parts.push_front(message_t(&type, sizeof(type)));
|
||||
}
|
||||
|
||||
// Push type (fixed-size) to back
|
||||
template<typename T> void addtyp(const T &type)
|
||||
{
|
||||
static_assert(!std::is_same<T, std::string>::value,
|
||||
"Use addstr() instead of addtyp<std::string>()");
|
||||
m_parts.push_back(message_t(&type, sizeof(type)));
|
||||
}
|
||||
|
||||
// Push message part to front
|
||||
void push(message_t &&message) { m_parts.push_front(std::move(message)); }
|
||||
|
||||
// Push message part to back
|
||||
void add(message_t &&message) { m_parts.push_back(std::move(message)); }
|
||||
|
||||
// Alias to allow std::back_inserter()
|
||||
void push_back(message_t &&message) { m_parts.push_back(std::move(message)); }
|
||||
|
||||
// Pop string from front
|
||||
std::string popstr()
|
||||
{
|
||||
std::string string(m_parts.front().data<char>(), m_parts.front().size());
|
||||
m_parts.pop_front();
|
||||
return string;
|
||||
}
|
||||
|
||||
// Pop type (fixed-size) from front
|
||||
template<typename T> T poptyp()
|
||||
{
|
||||
static_assert(!std::is_same<T, std::string>::value,
|
||||
"Use popstr() instead of poptyp<std::string>()");
|
||||
if (sizeof(T) != m_parts.front().size())
|
||||
throw std::runtime_error(
|
||||
"Invalid type, size does not match the message size");
|
||||
T type = *m_parts.front().data<T>();
|
||||
m_parts.pop_front();
|
||||
return type;
|
||||
}
|
||||
|
||||
// Pop message part from front
|
||||
message_t pop()
|
||||
{
|
||||
message_t message = std::move(m_parts.front());
|
||||
m_parts.pop_front();
|
||||
return message;
|
||||
}
|
||||
|
||||
// Pop message part from back
|
||||
message_t remove()
|
||||
{
|
||||
message_t message = std::move(m_parts.back());
|
||||
m_parts.pop_back();
|
||||
return message;
|
||||
}
|
||||
|
||||
// get message part from front
|
||||
const message_t &front() { return m_parts.front(); }
|
||||
|
||||
// get message part from back
|
||||
const message_t &back() { return m_parts.back(); }
|
||||
|
||||
// Get pointer to a specific message part
|
||||
const message_t *peek(size_t index) const { return &m_parts[index]; }
|
||||
|
||||
// Get a string copy of a specific message part
|
||||
std::string peekstr(size_t index) const
|
||||
{
|
||||
std::string string(m_parts[index].data<char>(), m_parts[index].size());
|
||||
return string;
|
||||
}
|
||||
|
||||
// Peek type (fixed-size) from front
|
||||
template<typename T> T peektyp(size_t index) const
|
||||
{
|
||||
static_assert(!std::is_same<T, std::string>::value,
|
||||
"Use peekstr() instead of peektyp<std::string>()");
|
||||
if (sizeof(T) != m_parts[index].size())
|
||||
throw std::runtime_error(
|
||||
"Invalid type, size does not match the message size");
|
||||
T type = *m_parts[index].data<T>();
|
||||
return type;
|
||||
}
|
||||
|
||||
// Create multipart from type (fixed-size)
|
||||
template<typename T> static multipart_t create(const T &type)
|
||||
{
|
||||
multipart_t multipart;
|
||||
multipart.addtyp(type);
|
||||
return multipart;
|
||||
}
|
||||
|
||||
// Copy multipart
|
||||
multipart_t clone() const
|
||||
{
|
||||
multipart_t multipart;
|
||||
for (size_t i = 0; i < size(); i++)
|
||||
multipart.addmem(m_parts[i].data(), m_parts[i].size());
|
||||
return multipart;
|
||||
}
|
||||
|
||||
// Dump content to string
|
||||
std::string str() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
for (size_t i = 0; i < m_parts.size(); i++) {
|
||||
const unsigned char *data = m_parts[i].data<unsigned char>();
|
||||
size_t size = m_parts[i].size();
|
||||
|
||||
// Dump the message as text or binary
|
||||
bool isText = true;
|
||||
for (size_t j = 0; j < size; j++) {
|
||||
if (data[j] < 32 || data[j] > 127) {
|
||||
isText = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ss << "\n[" << std::dec << std::setw(3) << std::setfill('0') << size
|
||||
<< "] ";
|
||||
if (size >= 1000) {
|
||||
ss << "... (too big to print)";
|
||||
continue;
|
||||
}
|
||||
for (size_t j = 0; j < size; j++) {
|
||||
if (isText)
|
||||
ss << static_cast<char>(data[j]);
|
||||
else
|
||||
ss << std::hex << std::setw(2) << std::setfill('0')
|
||||
<< static_cast<short>(data[j]);
|
||||
}
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
// Check if equal to other multipart
|
||||
bool equal(const multipart_t *other) const ZMQ_NOTHROW
|
||||
{
|
||||
return *this == *other;
|
||||
}
|
||||
|
||||
bool operator==(const multipart_t &other) const ZMQ_NOTHROW
|
||||
{
|
||||
if (size() != other.size())
|
||||
return false;
|
||||
for (size_t i = 0; i < size(); i++)
|
||||
if (at(i) != other.at(i))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator!=(const multipart_t &other) const ZMQ_NOTHROW
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
#ifdef ZMQ_CPP11
|
||||
|
||||
// Return single part message_t encoded from this multipart_t.
|
||||
message_t encode() const { return zmq::encode(*this); }
|
||||
|
||||
// Decode encoded message into multiple parts and append to self.
|
||||
void decode_append(const message_t &encoded)
|
||||
{
|
||||
zmq::decode(encoded, std::back_inserter(*this));
|
||||
}
|
||||
|
||||
// Return a new multipart_t containing the decoded message_t.
|
||||
static multipart_t decode(const message_t &encoded)
|
||||
{
|
||||
multipart_t tmp;
|
||||
zmq::decode(encoded, std::back_inserter(tmp));
|
||||
return tmp;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
private:
|
||||
// Disable implicit copying (moving is more efficient)
|
||||
multipart_t(const multipart_t &other) ZMQ_DELETED_FUNCTION;
|
||||
void operator=(const multipart_t &other) ZMQ_DELETED_FUNCTION;
|
||||
}; // class multipart_t
|
||||
|
||||
inline std::ostream &operator<<(std::ostream &os, const multipart_t &msg)
|
||||
{
|
||||
return os << msg.str();
|
||||
}
|
||||
|
||||
#endif // ZMQ_HAS_RVALUE_REFS
|
||||
|
||||
#if defined(ZMQ_BUILD_DRAFT_API) && defined(ZMQ_CPP11) && defined(ZMQ_HAVE_POLLER)
|
||||
class active_poller_t
|
||||
{
|
||||
public:
|
||||
active_poller_t() = default;
|
||||
~active_poller_t() = default;
|
||||
|
||||
active_poller_t(const active_poller_t &) = delete;
|
||||
active_poller_t &operator=(const active_poller_t &) = delete;
|
||||
|
||||
active_poller_t(active_poller_t &&src) = default;
|
||||
active_poller_t &operator=(active_poller_t &&src) = default;
|
||||
|
||||
using handler_type = std::function<void(event_flags)>;
|
||||
|
||||
void add(zmq::socket_ref socket, event_flags events, handler_type handler)
|
||||
{
|
||||
if (!handler)
|
||||
throw std::invalid_argument("null handler in active_poller_t::add");
|
||||
auto ret = handlers.emplace(
|
||||
socket, std::make_shared<handler_type>(std::move(handler)));
|
||||
if (!ret.second)
|
||||
throw error_t(EINVAL); // already added
|
||||
try {
|
||||
base_poller.add(socket, events, ret.first->second.get());
|
||||
need_rebuild = true;
|
||||
}
|
||||
catch (...) {
|
||||
// rollback
|
||||
handlers.erase(socket);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void remove(zmq::socket_ref socket)
|
||||
{
|
||||
base_poller.remove(socket);
|
||||
handlers.erase(socket);
|
||||
need_rebuild = true;
|
||||
}
|
||||
|
||||
void modify(zmq::socket_ref socket, event_flags events)
|
||||
{
|
||||
base_poller.modify(socket, events);
|
||||
}
|
||||
|
||||
size_t wait(std::chrono::milliseconds timeout)
|
||||
{
|
||||
if (need_rebuild) {
|
||||
poller_events.resize(handlers.size());
|
||||
poller_handlers.clear();
|
||||
poller_handlers.reserve(handlers.size());
|
||||
for (const auto &handler : handlers) {
|
||||
poller_handlers.push_back(handler.second);
|
||||
}
|
||||
need_rebuild = false;
|
||||
}
|
||||
const auto count = base_poller.wait_all(poller_events, timeout);
|
||||
std::for_each(poller_events.begin(),
|
||||
poller_events.begin() + static_cast<ptrdiff_t>(count),
|
||||
[](decltype(base_poller)::event_type &event) {
|
||||
assert(event.user_data != nullptr);
|
||||
(*event.user_data)(event.events);
|
||||
});
|
||||
return count;
|
||||
}
|
||||
|
||||
ZMQ_NODISCARD bool empty() const noexcept { return handlers.empty(); }
|
||||
|
||||
size_t size() const noexcept { return handlers.size(); }
|
||||
|
||||
private:
|
||||
bool need_rebuild{false};
|
||||
|
||||
poller_t<handler_type> base_poller{};
|
||||
std::unordered_map<socket_ref, std::shared_ptr<handler_type>> handlers{};
|
||||
std::vector<decltype(base_poller)::event_type> poller_events{};
|
||||
std::vector<std::shared_ptr<handler_type>> poller_handlers{};
|
||||
}; // class active_poller_t
|
||||
#endif // defined(ZMQ_BUILD_DRAFT_API) && defined(ZMQ_CPP11) && defined(ZMQ_HAVE_POLLER)
|
||||
|
||||
|
||||
} // namespace zmq
|
||||
|
||||
#endif // __ZMQ_ADDON_HPP_INCLUDED__
|
||||
Reference in New Issue
Block a user