Initial commit
This commit is contained in:
Коммит
222e20fd00
|
@ -0,0 +1,3 @@
|
||||||
|
# Vim
|
||||||
|
*.swp
|
||||||
|
*.swo
|
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "thirdparty/rapidjson"]
|
||||||
|
path = thirdparty/rapidjson
|
||||||
|
url = https://github.com/miloyip/rapidjson.git
|
|
@ -0,0 +1,40 @@
|
||||||
|
cmake_minimum_required (VERSION 2.8.12)
|
||||||
|
project (bond)
|
||||||
|
|
||||||
|
cmake_policy (SET CMP0022 NEW)
|
||||||
|
|
||||||
|
set (CMAKE_MODULE_PATH
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/cmake
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/cmake-modules)
|
||||||
|
|
||||||
|
enable_testing()
|
||||||
|
|
||||||
|
set (BOND_INCLUDE ${CMAKE_CURRENT_SOURCE_DIR}/cpp/inc)
|
||||||
|
set (BOND_PYTHON_INCLUDE ${CMAKE_CURRENT_SOURCE_DIR}/python/inc)
|
||||||
|
set (BOND_GENERATED ${CMAKE_CURRENT_SOURCE_DIR}/cpp/generated)
|
||||||
|
set (BOND_COMPAT_TEST_DIR ${CMAKE_CURRENT_SOURCE_DIR}/test/compat)
|
||||||
|
|
||||||
|
include (Config)
|
||||||
|
include (Bond)
|
||||||
|
include (NoDebug)
|
||||||
|
include (Compiler)
|
||||||
|
include (PythonTest)
|
||||||
|
|
||||||
|
add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} -C ${CMAKE_CFG_INTDIR} --output-on-failure)
|
||||||
|
|
||||||
|
add_subdirectory (compiler)
|
||||||
|
add_subdirectory (cpp)
|
||||||
|
add_python_subdirectory (python)
|
||||||
|
add_subdirectory (examples)
|
||||||
|
|
||||||
|
install (DIRECTORY
|
||||||
|
cpp/inc/bond
|
||||||
|
cpp/generated/bond
|
||||||
|
python/inc/bond
|
||||||
|
DESTINATION include
|
||||||
|
PATTERN *.cpp EXCLUDE)
|
||||||
|
|
||||||
|
install (EXPORT bond
|
||||||
|
DESTINATION lib/bond
|
||||||
|
EXPORT_LINK_INTERFACE_LIBRARIES)
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2014 Microsoft
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
|
@ -0,0 +1,161 @@
|
||||||
|
Bond
|
||||||
|
====
|
||||||
|
|
||||||
|
Bond is an open source, cross-platform framework for working with schematized
|
||||||
|
data. It supports cross-language serialization/deserialization and powerful
|
||||||
|
generic mechanisms for efficiently manipulating data. Bond is broadly used at
|
||||||
|
Microsoft in high scale services.
|
||||||
|
|
||||||
|
Bond is published on GitHub at [https://github.com/Microsoft/bond/](https://github.com/Microsoft/bond/).
|
||||||
|
|
||||||
|
For details, see the User's Manuals for [C++](http://Microsoft.github.io/bond/manual/bond_cpp.html), [C#](http://Microsoft.github.io/bond/manual/bond_cs.html) and [Python](http://Microsoft.github.io/bond/manual/bond_py.html).
|
||||||
|
|
||||||
|
Dependencies
|
||||||
|
------------
|
||||||
|
|
||||||
|
The Bond repository uses Git submodules and should be cloned with the
|
||||||
|
`--recursive` flag:
|
||||||
|
|
||||||
|
git clone --recursive https://github.com/Microsoft/bond.git
|
||||||
|
|
||||||
|
In order to build Bond you will need CMake (2.8.12+), Haskell (ghc 7.4+ and
|
||||||
|
cabal-install 1.20+) and Boost (1.54+). The core Bond C++ library can be used
|
||||||
|
with C++03 compilers, although Python support, unit tests and various examples
|
||||||
|
require some C++11 features.
|
||||||
|
|
||||||
|
Following are specific instructions for building on various platforms.
|
||||||
|
|
||||||
|
### Linux
|
||||||
|
|
||||||
|
Bond can be built with Clang (3.4+) or GNU C++ (4.7+). We recommend the latest
|
||||||
|
version of Clang as it's much faster with template-heavy code like Bond.
|
||||||
|
|
||||||
|
Run the following commands to install the minimal set of packages needed to
|
||||||
|
build the core Bond library on Ubuntu 14.04:
|
||||||
|
|
||||||
|
sudo apt-get install \
|
||||||
|
clang \
|
||||||
|
cmake \
|
||||||
|
zlib1g-dev \
|
||||||
|
ghc \
|
||||||
|
cabal-install \
|
||||||
|
libboost-dev \
|
||||||
|
libboost-thread-dev
|
||||||
|
|
||||||
|
cabal update
|
||||||
|
cabal install cabal-install
|
||||||
|
|
||||||
|
In the root `bond` directory run:
|
||||||
|
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make
|
||||||
|
sudo make install
|
||||||
|
|
||||||
|
The `build` directory is just an example. Any directory can be used for build
|
||||||
|
destination.
|
||||||
|
|
||||||
|
In order to build all the C++ and Python tests and examples, a few more
|
||||||
|
packages are needed:
|
||||||
|
|
||||||
|
sudo apt-get install \
|
||||||
|
python2.7-dev \
|
||||||
|
libboost-date-time-dev \
|
||||||
|
libboost-test-dev \
|
||||||
|
libboost-python-dev
|
||||||
|
|
||||||
|
Running the following command in the build directory will build and execute all
|
||||||
|
the tests and examples:
|
||||||
|
|
||||||
|
make --jobs 8 check
|
||||||
|
|
||||||
|
(unit tests are large so you may want to run 4-8 build jobs in parallel,
|
||||||
|
assuming you have enough memory)
|
||||||
|
|
||||||
|
### OS X
|
||||||
|
|
||||||
|
- Install XCode 6.1
|
||||||
|
- Install CMake ([http://www.cmake.org/download/](http://www.cmake.org/download/))
|
||||||
|
- Install Haskell Platform ([http://haskell.org/platform/](http://haskell.org/platform/))
|
||||||
|
|
||||||
|
Update cabal to the latest version:
|
||||||
|
|
||||||
|
cabal update
|
||||||
|
cabal install cabal-install
|
||||||
|
|
||||||
|
Install Boost using Homebrew ([http://brew.sh/](http://brew.sh/)):
|
||||||
|
|
||||||
|
brew install boost boost-python
|
||||||
|
|
||||||
|
(boost-python is optional and only needed for Python support)
|
||||||
|
|
||||||
|
Bond can be built on OS X using either standard *nix makefiles or XCode. In
|
||||||
|
order to generate and build makefiles, in the root `bond` directory run:
|
||||||
|
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake ..
|
||||||
|
make
|
||||||
|
sudo make install
|
||||||
|
|
||||||
|
Alternatively you can use the CMake application to generate either *nix
|
||||||
|
makefiles or XCode project into a directory of your choice (it doesn't have to
|
||||||
|
be called `build`).
|
||||||
|
|
||||||
|
You can build and run unit tests by building the `check` target in XCode or
|
||||||
|
by running make in the build directory:
|
||||||
|
|
||||||
|
make --jobs 8 check
|
||||||
|
|
||||||
|
### Windows
|
||||||
|
|
||||||
|
- Install Visual Studio 2013
|
||||||
|
- Install CMake ([http://www.cmake.org/download/](http://www.cmake.org/download/))
|
||||||
|
- Install Haskell Platform ([http://haskell.org/platform/](http://haskell.org/platform/))
|
||||||
|
|
||||||
|
|
||||||
|
Update cabal to the latest version (if behind a proxy, set environment variable
|
||||||
|
`HTTP_PROXY=http://proxy:port` before running cabal):
|
||||||
|
|
||||||
|
cabal update
|
||||||
|
cabal install cabal-install
|
||||||
|
|
||||||
|
Now you are ready to build the C# version of Bond. Open the solution file
|
||||||
|
`cs\cs.sln` in Visual Studio 2013 and build as usual. The C# unit tests can
|
||||||
|
also be run from with the solution.
|
||||||
|
|
||||||
|
The C++ and Python versions of Bond additionally require:
|
||||||
|
|
||||||
|
- Boost 1.54+ ([http://www.boost.org/users/download/](http://www.boost.org/users/download/))
|
||||||
|
- Python 2.7 ([https://www.python.org/downloads/](https://www.python.org/downloads/))
|
||||||
|
|
||||||
|
You may need to set the environment variables `BOOST_ROOT` and `BOOST_LIBRARYDIR`
|
||||||
|
to specify where Boost and its pre-built libraries for your environment can be
|
||||||
|
found. The core Bond library and most examples only require Boost headers. The
|
||||||
|
pre-built libraries are only needed for unit tests and Python support. If Boost
|
||||||
|
or Python libraries are not found on the system then some tests and examples will
|
||||||
|
not be built.
|
||||||
|
|
||||||
|
In order to configure C++/Python project run `cmake-gui`, select the root of
|
||||||
|
this repository as the source code directory and some other directory as the
|
||||||
|
target for generated project files and build binaries. Now press Generate and
|
||||||
|
select desired target build environment. This configuration step has to be
|
||||||
|
performed only once. From now on you can use the generated solution `bond.sln`.
|
||||||
|
|
||||||
|
IMPORTANT: Bond unit tests are very large. If you are building using the Visual
|
||||||
|
Studio toolchain you have to select 64-bit tools by setting the following
|
||||||
|
environment variable:
|
||||||
|
|
||||||
|
For Visual Studio 2012:
|
||||||
|
|
||||||
|
set _IsNativeEnvironment=true
|
||||||
|
|
||||||
|
For Visual Studio 2013:
|
||||||
|
|
||||||
|
set PreferredToolArchitecture=x64
|
||||||
|
|
||||||
|
In order to run unit tests on the Debug build, execute the following command in
|
||||||
|
the build directory:
|
||||||
|
|
||||||
|
ctest -C Debug
|
|
@ -0,0 +1,140 @@
|
||||||
|
include (CMakeParseArguments)
|
||||||
|
|
||||||
|
#
|
||||||
|
# add_bond_codegen (file.bond [file2.bond ...]
|
||||||
|
# [ENUM_HEADER]
|
||||||
|
# [OUTPUT_DIR dir]
|
||||||
|
# [IMPORT_DIR dir [dir2, ...]]
|
||||||
|
# [OPTIONS opt [opt2 ...]])
|
||||||
|
# [TARGET name]
|
||||||
|
#
|
||||||
|
function (add_bond_codegen)
|
||||||
|
set (flagArgs ENUM_HEADER)
|
||||||
|
set (oneValueArgs OUTPUT_DIR TARGET)
|
||||||
|
set (multiValueArgs IMPORT_DIR OPTIONS)
|
||||||
|
cmake_parse_arguments (arg "${flagArgs}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||||
|
set (options)
|
||||||
|
set (outputDir ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR})
|
||||||
|
if (arg_OUTPUT_DIR)
|
||||||
|
set (outputDir ${arg_OUTPUT_DIR})
|
||||||
|
else()
|
||||||
|
endif()
|
||||||
|
list (APPEND options --output-dir="${outputDir}")
|
||||||
|
list (APPEND options --import-dir="${BOND_INCLUDE}")
|
||||||
|
foreach (dir ${arg_IMPORT_DIR})
|
||||||
|
list(APPEND options --import-dir="${dir}")
|
||||||
|
endforeach()
|
||||||
|
foreach (opt ${arg_OPTIONS})
|
||||||
|
list (APPEND options "${opt}")
|
||||||
|
endforeach()
|
||||||
|
if (arg_ENUM_HEADER)
|
||||||
|
list(APPEND options --enum-header)
|
||||||
|
endif()
|
||||||
|
set (inputs "${arg_UNPARSED_ARGUMENTS}")
|
||||||
|
set (outputs)
|
||||||
|
foreach (file ${inputs})
|
||||||
|
get_filename_component (name ${file} NAME_WE)
|
||||||
|
list (APPEND outputs
|
||||||
|
"${outputDir}/${name}_reflection.h"
|
||||||
|
"${outputDir}/${name}_types.h"
|
||||||
|
"${outputDir}/${name}_types.cpp"
|
||||||
|
"${outputDir}/${name}_apply.h"
|
||||||
|
"${outputDir}/${name}_apply.cpp"
|
||||||
|
)
|
||||||
|
if (arg_ENUM_HEADER)
|
||||||
|
list(APPEND outputs "${outputDir}/${name}_enum.h")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT ${outputs}
|
||||||
|
COMMAND ${GBC_EXECUTABLE} c++ ${options} ${inputs}
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
DEPENDS ${inputs} compiler ${GBC_EXECUTABLE})
|
||||||
|
if (arg_TARGET)
|
||||||
|
add_custom_target (${arg_TARGET}
|
||||||
|
DEPENDS ${outputs}
|
||||||
|
SOURCES ${inputs})
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
#
|
||||||
|
# add_bond_executable (name
|
||||||
|
# [schem.bond [schema2.bond]]
|
||||||
|
# source.cpp [source2.cpp])
|
||||||
|
#
|
||||||
|
function (add_bond_executable target)
|
||||||
|
set (schemas)
|
||||||
|
set (sources)
|
||||||
|
foreach (file ${ARGV})
|
||||||
|
get_filename_component (ext ${file} EXT)
|
||||||
|
if (ext STREQUAL ".bond")
|
||||||
|
get_filename_component (name ${file} NAME_WE)
|
||||||
|
list (APPEND schemas "${file}")
|
||||||
|
list (APPEND sources "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${name}_types.cpp")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
if (schemas)
|
||||||
|
add_bond_codegen (${schemas})
|
||||||
|
endif()
|
||||||
|
add_executable (${ARGV} ${sources})
|
||||||
|
target_link_libraries (${target} PRIVATE
|
||||||
|
bond
|
||||||
|
bond_apply)
|
||||||
|
target_include_directories (${target} PRIVATE
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
#
|
||||||
|
# add_bond_test (name
|
||||||
|
# [schem.bond [schema2.bond]]
|
||||||
|
# source.cpp [source2.cpp])
|
||||||
|
#
|
||||||
|
function (add_bond_test test)
|
||||||
|
list (INSERT ARGV 1 EXCLUDE_FROM_ALL)
|
||||||
|
add_bond_executable (${ARGV})
|
||||||
|
add_dependencies (check ${test})
|
||||||
|
add_test (
|
||||||
|
NAME ${test}
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||||
|
COMMAND ${test})
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
#
|
||||||
|
# add_bond_python_module (name
|
||||||
|
# [schem.bond [schema2.bond]]
|
||||||
|
# source.cpp [source2.cpp])
|
||||||
|
#
|
||||||
|
function (add_bond_python_module target)
|
||||||
|
set (schemas)
|
||||||
|
set (sources)
|
||||||
|
foreach (file ${ARGV})
|
||||||
|
get_filename_component (ext ${file} EXT)
|
||||||
|
if (ext STREQUAL ".bond")
|
||||||
|
get_filename_component (name ${file} NAME_WE)
|
||||||
|
list (APPEND schemas "${file}")
|
||||||
|
list (APPEND sources "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${name}_types.cpp")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
if (schemas)
|
||||||
|
add_bond_codegen (${schemas})
|
||||||
|
endif()
|
||||||
|
list (INSERT ARGV 1 EXCLUDE_FROM_ALL)
|
||||||
|
python_add_module (${ARGV} ${sources})
|
||||||
|
add_dependencies (check ${target})
|
||||||
|
target_link_libraries (${target} PRIVATE
|
||||||
|
bond
|
||||||
|
bond_apply
|
||||||
|
${PYTHON_LIBRARIES}
|
||||||
|
${Boost_PYTHON_LIBRARY})
|
||||||
|
target_include_directories (${target} PRIVATE
|
||||||
|
${BOND_PYTHON_INCLUDE}
|
||||||
|
${WINDOWSSDK_PREFERRED_DIR}/Include
|
||||||
|
${WINDOWSSDK_PREFERRED_DIR}/Include/shared
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
${PYTHON_INCLUDE_DIR})
|
||||||
|
target_compile_definitions (${target} PRIVATE
|
||||||
|
-DBOOST_PYTHON_STATIC_LIB)
|
||||||
|
endfunction()
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
function (cxx_target_compile_options compiler)
|
||||||
|
list (REMOVE_AT ARGV 0)
|
||||||
|
if (${CMAKE_CXX_COMPILER_ID} STREQUAL ${compiler})
|
||||||
|
target_compile_options (${ARGV})
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function (cxx_add_compile_options compiler)
|
||||||
|
list (REMOVE_AT ARGV 0)
|
||||||
|
if (${CMAKE_CXX_COMPILER_ID} STREQUAL ${compiler})
|
||||||
|
add_compile_options (${ARGV})
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function (use_cxx11)
|
||||||
|
cxx_add_compile_options (Clang -std=c++11)
|
||||||
|
cxx_add_compile_options (GNU -std=c++11)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function (target_use_cxx11 name)
|
||||||
|
cxx_target_compile_options (Clang ${name} PRIVATE -std=c++11)
|
||||||
|
cxx_target_compile_options (GNU ${name} PRIVATE -std=c++11)
|
||||||
|
endfunction()
|
|
@ -0,0 +1,44 @@
|
||||||
|
include (Compiler)
|
||||||
|
|
||||||
|
if (MSVC)
|
||||||
|
# disable MSVC warnings
|
||||||
|
add_compile_options (/bigobj /FIbond/core/warning.h)
|
||||||
|
add_definitions (-D_CRT_SECURE_NO_WARNINGS)
|
||||||
|
set (Boost_USE_STATIC_LIBS ON)
|
||||||
|
endif (MSVC)
|
||||||
|
|
||||||
|
if (WIN32)
|
||||||
|
find_package (WindowsSDK)
|
||||||
|
|
||||||
|
# If C# has been built we will also run C# compatibility tests
|
||||||
|
find_program (BOND_CSHARP_COMPAT_TEST Bond.CompatibilityTest.exe
|
||||||
|
PATH_SUFFIXES net40 net45
|
||||||
|
NO_DEFAULT_PATH
|
||||||
|
PATHS
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/cs/test/compat/bin/debug"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/cs/test/compat/bin/retail")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_package (PythonLibs 2.7)
|
||||||
|
find_package (PythonInterp 2.7)
|
||||||
|
|
||||||
|
find_package (Boost 1.53.0
|
||||||
|
OPTIONAL_COMPONENTS
|
||||||
|
date_time
|
||||||
|
thread
|
||||||
|
system
|
||||||
|
unit_test_framework
|
||||||
|
python)
|
||||||
|
|
||||||
|
# disable Boost auto-linking
|
||||||
|
add_definitions (-DBOOST_ALL_NO_LIB)
|
||||||
|
|
||||||
|
cxx_add_compile_options(Clang -fPIC)
|
||||||
|
cxx_add_compile_options(GNU -fPIC)
|
||||||
|
|
||||||
|
include_directories (
|
||||||
|
${BOND_INCLUDE}
|
||||||
|
${BOND_GENERATED}
|
||||||
|
${Boost_INCLUDE_DIRS}
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/rapidjson/include)
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
include (FindPackageHandleStandardArgs)
|
||||||
|
include (HaskellUtil)
|
||||||
|
|
||||||
|
find_haskell_program (cabal)
|
||||||
|
|
||||||
|
find_package_handle_standard_args (Cabal
|
||||||
|
FOUND_VAR CABAL_FOUND
|
||||||
|
REQUIRED_VARS Haskell_CABAL_EXECUTABLE
|
||||||
|
VERSION_VAR CABAL_VERSION
|
||||||
|
FAIL_MESSAGE
|
||||||
|
"Couldn't find the required version of cabal. You can update cabal by running 'cabal update' and then 'cabal install cabal-install'. If the right version of cabal is already installed but wasn't found, you can specify the directory where it is located via the environment variable CABAL_PATH")
|
||||||
|
|
||||||
|
mark_as_advanced (Haskell_CABAL_EXECUTABLE)
|
|
@ -0,0 +1,13 @@
|
||||||
|
include (FindPackageHandleStandardArgs)
|
||||||
|
include (HaskellUtil)
|
||||||
|
|
||||||
|
find_haskell_program (ghc)
|
||||||
|
|
||||||
|
find_package_handle_standard_args (GHC
|
||||||
|
FOUND_VAR GHC_FOUND
|
||||||
|
REQUIRED_VARS Haskell_GHC_EXECUTABLE
|
||||||
|
VERSION_VAR GHC_VERSION
|
||||||
|
FAIL_MESSAGE
|
||||||
|
"Couldn't find the required version of ghc. You can install ghc with Haskell Platform (https://www.haskell.org/platform/). If the right version of ghc is already installed but wasn't found, you can specify the directory where it is located via the environment variable GHC_PATH")
|
||||||
|
|
||||||
|
mark_as_advanced (Haskell_GHC_EXECUTABLE)
|
|
@ -0,0 +1,38 @@
|
||||||
|
macro (find_haskell_program program)
|
||||||
|
string (TOUPPER ${program} PROGRAM)
|
||||||
|
if (NOT ${PROGRAM}_FOUND)
|
||||||
|
set (_HASKELL_PLATFORM_VERSIONS
|
||||||
|
2014.2.0.0
|
||||||
|
2013.2.0.0
|
||||||
|
2012.4.0.0
|
||||||
|
2012.2.0.0)
|
||||||
|
|
||||||
|
foreach (_VER ${_HASKELL_PLATFORM_VERSIONS})
|
||||||
|
list (APPEND _HASKELL_PLATFORM_PATHS
|
||||||
|
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\Haskell\\Haskell Platform\\${_VER};InstallDir]")
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
unset (Haskell_${PROGRAM}_EXECUTABLE CACHE)
|
||||||
|
find_program (Haskell_${PROGRAM}_EXECUTABLE ${program}
|
||||||
|
HINTS $ENV{${PROGRAM}_PATH}
|
||||||
|
PATH_SUFFIXES bin
|
||||||
|
NO_DEFAULT_PATH
|
||||||
|
PATHS
|
||||||
|
"$ENV{HOME}/.cabal"
|
||||||
|
"$ENV{HOME}/Library/Haskell"
|
||||||
|
"$ENV{APPDATA}/cabal"
|
||||||
|
${_HASKELL_PLATFORM_PATHS}
|
||||||
|
ENV PATH
|
||||||
|
${CMAKE_SYSTEM_PROGRAM_PATH})
|
||||||
|
|
||||||
|
if (Haskell_${PROGRAM}_EXECUTABLE)
|
||||||
|
execute_process (
|
||||||
|
COMMAND "${Haskell_${PROGRAM}_EXECUTABLE}" --numeric-version
|
||||||
|
OUTPUT_VARIABLE ${PROGRAM}_VERSION
|
||||||
|
ERROR_QUIET)
|
||||||
|
string (REGEX REPLACE "\n" "" ${PROGRAM}_VERSION "${${PROGRAM}_VERSION}")
|
||||||
|
endif()
|
||||||
|
unset (_HASKELL_PLATFORM_VERSIONS)
|
||||||
|
unset (_HASKELL_PLATFORM_PATHS)
|
||||||
|
endif()
|
||||||
|
endmacro()
|
|
@ -0,0 +1,12 @@
|
||||||
|
macro (_debug_none var)
|
||||||
|
string (REPLACE /debug /debug:None ${var} ${${var}})
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
macro (no_pdb)
|
||||||
|
if (MSVC)
|
||||||
|
_debug_none (CMAKE_EXE_LINKER_FLAGS_DEBUG)
|
||||||
|
_debug_none (CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO)
|
||||||
|
_debug_none (CMAKE_MODULE_LINKER_FLAGS_DEBUG)
|
||||||
|
_debug_none (CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO)
|
||||||
|
endif()
|
||||||
|
endmacro()
|
|
@ -0,0 +1,29 @@
|
||||||
|
set (BOND_CMAKE_DIR ${CMAKE_CURRENT_LIST_DIR})
|
||||||
|
|
||||||
|
macro (add_python_subdirectory dir)
|
||||||
|
if (PYTHONINTERP_FOUND AND
|
||||||
|
PYTHONLIBS_FOUND AND
|
||||||
|
Boost_PYTHON_FOUND AND
|
||||||
|
(WINDOWSSDK_FOUND OR NOT WIN32))
|
||||||
|
add_subdirectory (${dir})
|
||||||
|
endif()
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
function (add_python_test)
|
||||||
|
set (flagArgs)
|
||||||
|
set (oneValueArgs NAME MODULE SCRIPT)
|
||||||
|
set (multiValueArgs)
|
||||||
|
cmake_parse_arguments (arg "${flagArgs}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||||
|
|
||||||
|
if (PYTHONINTERP_FOUND)
|
||||||
|
add_test (
|
||||||
|
NAME ${arg_NAME}
|
||||||
|
COMMAND
|
||||||
|
${CMAKE_COMMAND}
|
||||||
|
-DPYTHON_EXECUTABLE=${PYTHON_EXECUTABLE}
|
||||||
|
-DPYTHONPATH=$<TARGET_FILE_DIR:${arg_MODULE}>
|
||||||
|
-DSCRIPT=${arg_SCRIPT}
|
||||||
|
-P ${BOND_CMAKE_DIR}/RunPythonTest.cmake
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
endif()
|
||||||
|
endfunction()
|
|
@ -0,0 +1,9 @@
|
||||||
|
set (ENV{PYTHONPATH} ${PYTHONPATH})
|
||||||
|
|
||||||
|
execute_process (
|
||||||
|
COMMAND ${PYTHON_EXECUTABLE} ${SCRIPT}
|
||||||
|
RESULT_VARIABLE error)
|
||||||
|
|
||||||
|
if (error)
|
||||||
|
message (FATAL_ERROR)
|
||||||
|
endif()
|
|
@ -0,0 +1,4 @@
|
||||||
|
# Cabal build artifacts
|
||||||
|
dist
|
||||||
|
.cabal-sandbox
|
||||||
|
cabal.sandbox.config
|
|
@ -0,0 +1,146 @@
|
||||||
|
-- Copyright (c) Microsoft. All rights reserved.
|
||||||
|
-- Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
module Bond.Lexer
|
||||||
|
( angles
|
||||||
|
, braces
|
||||||
|
, brackets
|
||||||
|
, colon
|
||||||
|
, comma
|
||||||
|
, commaEnd
|
||||||
|
, commaEnd1
|
||||||
|
, commaSep1
|
||||||
|
, decimal
|
||||||
|
, equal
|
||||||
|
, float
|
||||||
|
, identifier
|
||||||
|
, integer
|
||||||
|
, keyword
|
||||||
|
, lexeme
|
||||||
|
, natural
|
||||||
|
, parens
|
||||||
|
, unescapedStringLiteral
|
||||||
|
, semi
|
||||||
|
, semiEnd
|
||||||
|
, semiOrCommaEnd
|
||||||
|
, semiOrCommaSep
|
||||||
|
, semiOrCommaSep1
|
||||||
|
, semiOrCommaSepEnd
|
||||||
|
, semiOrCommaSepEnd1
|
||||||
|
, semiSep
|
||||||
|
, stringLiteral
|
||||||
|
, symbol
|
||||||
|
, whiteSpace
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Control.Monad.Reader
|
||||||
|
import Text.ParserCombinators.Parsec
|
||||||
|
import qualified Text.Parsec.Token as P
|
||||||
|
|
||||||
|
type LanguageDef st env = P.GenLanguageDef String st (ReaderT env IO)
|
||||||
|
|
||||||
|
bondIdl :: LanguageDef st env
|
||||||
|
bondIdl = P.LanguageDef
|
||||||
|
{ P.commentStart = "/*"
|
||||||
|
, P.commentEnd = "*/"
|
||||||
|
, P.commentLine = "//"
|
||||||
|
, P.nestedComments = True
|
||||||
|
, P.identStart = letter <|> char '_'
|
||||||
|
, P.identLetter = alphaNum <|> char '_'
|
||||||
|
, P.opStart = mzero
|
||||||
|
, P.opLetter = mzero
|
||||||
|
, P.reservedNames =
|
||||||
|
[ "blob"
|
||||||
|
, "bond_meta"
|
||||||
|
, "bonded"
|
||||||
|
, "bool"
|
||||||
|
, "class"
|
||||||
|
, "double"
|
||||||
|
, "enum"
|
||||||
|
, "false"
|
||||||
|
, "float"
|
||||||
|
, "import"
|
||||||
|
, "int16"
|
||||||
|
, "int32"
|
||||||
|
, "int64"
|
||||||
|
, "int8"
|
||||||
|
, "list"
|
||||||
|
, "map"
|
||||||
|
, "namespace"
|
||||||
|
, "nullable"
|
||||||
|
, "optional"
|
||||||
|
, "required"
|
||||||
|
, "required_optional"
|
||||||
|
, "Schema"
|
||||||
|
, "sealed"
|
||||||
|
, "service"
|
||||||
|
, "set"
|
||||||
|
, "string"
|
||||||
|
, "struct"
|
||||||
|
, "true"
|
||||||
|
, "uint16"
|
||||||
|
, "uint32"
|
||||||
|
, "uint64"
|
||||||
|
, "uint8"
|
||||||
|
, "using"
|
||||||
|
, "var"
|
||||||
|
, "vector"
|
||||||
|
, "view_of"
|
||||||
|
, "void"
|
||||||
|
, "wstring"
|
||||||
|
]
|
||||||
|
, P.reservedOpNames = []
|
||||||
|
, P.caseSensitive = True
|
||||||
|
}
|
||||||
|
|
||||||
|
lexer = P.makeTokenParser bondIdl
|
||||||
|
|
||||||
|
angles = P.angles lexer
|
||||||
|
braces = P.braces lexer
|
||||||
|
brackets = P.brackets lexer
|
||||||
|
colon = P.colon lexer
|
||||||
|
comma = P.comma lexer
|
||||||
|
commaSep1 = P.commaSep1 lexer
|
||||||
|
decimal = P.decimal lexer
|
||||||
|
identifier = P.identifier lexer
|
||||||
|
integer = P.integer lexer
|
||||||
|
keyword = P.reserved lexer
|
||||||
|
lexeme = P.lexeme lexer
|
||||||
|
natural = P.natural lexer
|
||||||
|
parens = P.parens lexer
|
||||||
|
semi = P.semi lexer
|
||||||
|
semiSep = P.semiSep lexer
|
||||||
|
symbol = P.symbol lexer
|
||||||
|
whiteSpace = P.whiteSpace lexer
|
||||||
|
|
||||||
|
equal = symbol "="
|
||||||
|
semiEnd p = endBy p semi
|
||||||
|
commaEnd p = endBy p comma
|
||||||
|
commaEnd1 p = endBy1 p comma
|
||||||
|
|
||||||
|
semiOrComma = semi <|> comma
|
||||||
|
|
||||||
|
semiOrCommaSep p = sepBy p semiOrComma
|
||||||
|
semiOrCommaSep1 p = sepBy1 p semiOrComma
|
||||||
|
semiOrCommaEnd p = endBy p semiOrComma
|
||||||
|
semiOrCommaSepEnd p = sepEndBy p semiOrComma
|
||||||
|
semiOrCommaSepEnd1 p = sepEndBy1 p semiOrComma
|
||||||
|
|
||||||
|
quote = symbol "\""
|
||||||
|
quotes = between quote quote
|
||||||
|
|
||||||
|
stringLiteral = P.stringLiteral lexer
|
||||||
|
|
||||||
|
unescapedStringLiteral = quotes $ many $ satisfy (/= '"')
|
||||||
|
|
||||||
|
-- Can't use float from Text.Parsec.Token because it doesn't handle numbers
|
||||||
|
-- starting with +/- sign.
|
||||||
|
float = do
|
||||||
|
s <- sign
|
||||||
|
f <- P.float lexer
|
||||||
|
return $ s f
|
||||||
|
where
|
||||||
|
sign = (char '-' >> return negate)
|
||||||
|
<|> (char '+' >> return id)
|
||||||
|
<|> return id
|
||||||
|
|
|
@ -0,0 +1,312 @@
|
||||||
|
-- Copyright (c) Microsoft. All rights reserved.
|
||||||
|
-- Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
{-# LANGUAGE RecordWildCards #-}
|
||||||
|
|
||||||
|
module Bond.Parser
|
||||||
|
( Bond(..)
|
||||||
|
, parseBond
|
||||||
|
, newEnvironment
|
||||||
|
)
|
||||||
|
where
|
||||||
|
|
||||||
|
import Data.Ord
|
||||||
|
import Data.List
|
||||||
|
import Data.Function
|
||||||
|
import Control.Applicative
|
||||||
|
import Control.Monad.Reader
|
||||||
|
import Control.Monad.Trans (liftIO)
|
||||||
|
import Text.Parsec.Pos (initialPos)
|
||||||
|
import Text.Parsec hiding (many, optional, (<|>))
|
||||||
|
import Bond.Lexer
|
||||||
|
import Bond.Schema
|
||||||
|
|
||||||
|
-- parser state, mutable and global
|
||||||
|
-- list of structs, enums and aliases declared in the current and all imported files
|
||||||
|
type Symbols = [Declaration]
|
||||||
|
|
||||||
|
-- parser environment, immutable but contextual
|
||||||
|
data Environment =
|
||||||
|
Environment
|
||||||
|
{ currentNamespaces :: [Namespace] -- namespace(s) in current context
|
||||||
|
, currentParams :: [TypeParam] -- type parameter(s) for current type (struct or alias)
|
||||||
|
, currentFile :: FilePath -- path of the current file
|
||||||
|
, resolveImport :: FilePath -> FilePath -> IO (FilePath, String) -- imports resolver
|
||||||
|
}
|
||||||
|
|
||||||
|
newEnvironment :: FilePath -> (FilePath -> FilePath -> IO (FilePath, String)) -> Environment
|
||||||
|
newEnvironment = Environment [] []
|
||||||
|
|
||||||
|
type Parser a = ParsecT String Symbols (ReaderT Environment IO) a
|
||||||
|
|
||||||
|
parseBond = runParserT bond []
|
||||||
|
|
||||||
|
data Bond = Bond [Import] [Namespace] [Declaration]
|
||||||
|
|
||||||
|
-- parser for .bond files
|
||||||
|
bond :: Parser Bond
|
||||||
|
bond = do
|
||||||
|
whiteSpace
|
||||||
|
imports <- many import_
|
||||||
|
namespaces <- many1 namespace
|
||||||
|
local (with namespaces) $ Bond imports namespaces <$> many declaration <* eof
|
||||||
|
where
|
||||||
|
with namespaces e = e { currentNamespaces = namespaces }
|
||||||
|
|
||||||
|
import_ :: Parser Import
|
||||||
|
import_ = do
|
||||||
|
i <- Import <$ keyword "import" <*> unescapedStringLiteral <?> "import statement"
|
||||||
|
input <- getInput
|
||||||
|
pos <- getPosition
|
||||||
|
processImport i
|
||||||
|
setInput input
|
||||||
|
setPosition pos
|
||||||
|
return i
|
||||||
|
|
||||||
|
processImport :: Import -> Parser Bond
|
||||||
|
processImport (Import file) = do
|
||||||
|
Environment { currentFile = currentFile, resolveImport = resolveImport } <- ask
|
||||||
|
(path, content) <- liftIO $ resolveImport currentFile file
|
||||||
|
setInput content
|
||||||
|
setPosition $ initialPos path
|
||||||
|
local (\e -> e { currentFile = path }) bond
|
||||||
|
|
||||||
|
-- parser for struct, enum or type alias declaration/definition
|
||||||
|
declaration :: Parser Declaration
|
||||||
|
declaration = do
|
||||||
|
decl <- try forward
|
||||||
|
<|> try struct
|
||||||
|
<|> try view
|
||||||
|
<|> try enum
|
||||||
|
<|> try alias
|
||||||
|
updateSymbols decl <?> "declaration"
|
||||||
|
return decl
|
||||||
|
|
||||||
|
updateSymbols decl = do
|
||||||
|
(previous, symbols) <- partition (duplicateDeclaration decl) <$> getState
|
||||||
|
case reconcile previous decl of
|
||||||
|
(False, _) -> fail $ "The " ++ show decl ++ " has been previously defined as " ++ show (head previous)
|
||||||
|
(True, f) -> modifyState (f symbols)
|
||||||
|
where
|
||||||
|
reconcile [x@Forward {}] y@Struct {} = (paramsMatch x y, add y)
|
||||||
|
reconcile [x@Forward {}] y@Forward {} = (paramsMatch x y, const id)
|
||||||
|
reconcile [x@Struct {}] y@Forward {} = (paramsMatch x y, add y)
|
||||||
|
reconcile [] x = (True, add x)
|
||||||
|
-- This allows identical duplicate definitions, which is how parsing the
|
||||||
|
-- same import multiple times is handled. Ideally we would avoid parsing
|
||||||
|
-- imports multiple times but that would have to depend on canonical file
|
||||||
|
-- paths which are unreliable.
|
||||||
|
reconcile [x] y = (x == y, const id)
|
||||||
|
paramsMatch = (==) `on` (map paramConstraint . declParams)
|
||||||
|
add x xs _ = x:xs
|
||||||
|
|
||||||
|
findSymbol :: QualifiedName -> Parser Declaration
|
||||||
|
findSymbol name = doFind <?> "qualified name"
|
||||||
|
where
|
||||||
|
doFind = do
|
||||||
|
namespaces <- asks currentNamespaces
|
||||||
|
symbols <- getState
|
||||||
|
case find (isDecl namespaces name) symbols of
|
||||||
|
Just decl -> return decl
|
||||||
|
Nothing -> fail $ "Unknown symbol: " ++ showQualifiedName name
|
||||||
|
isDecl namespaces [name] decl =
|
||||||
|
name == declName decl
|
||||||
|
&& any (`elem` namespaces) (declNamespaces decl)
|
||||||
|
isDecl _ qualifiedName decl =
|
||||||
|
takeName qualifiedName == declName decl
|
||||||
|
&& any ((takeNamespace qualifiedName ==) . nsName) (declNamespaces decl)
|
||||||
|
|
||||||
|
findStruct :: QualifiedName -> Parser Declaration
|
||||||
|
findStruct name = doFind <?> "qualified struct name"
|
||||||
|
where
|
||||||
|
doFind = do
|
||||||
|
symbol <- findSymbol name
|
||||||
|
case symbol of
|
||||||
|
Struct {..} -> return symbol
|
||||||
|
_ -> fail $ "The " ++ show symbol ++ " is invalid in this context. Expected a struct."
|
||||||
|
|
||||||
|
-- namespace
|
||||||
|
namespace :: Parser Namespace
|
||||||
|
namespace = Namespace <$ keyword "namespace" <*> language <*> qualifiedName <* optional semi <?> "namespace declaration"
|
||||||
|
where
|
||||||
|
language = optional (keyword "cpp" *> pure Cpp
|
||||||
|
<|> keyword "cs" *> pure Cs
|
||||||
|
<|> keyword "java" *> pure Java
|
||||||
|
<|> keyword "csharp" *> pure Cs)
|
||||||
|
|
||||||
|
-- identifier optionally qualified with namespace
|
||||||
|
qualifiedName :: Parser QualifiedName
|
||||||
|
qualifiedName = sepBy1 identifier (char '.') <?> "qualified name"
|
||||||
|
|
||||||
|
-- type parameters
|
||||||
|
parameters :: Parser [TypeParam]
|
||||||
|
parameters = option [] (angles $ commaSep1 param) <?> "type parameters"
|
||||||
|
where
|
||||||
|
param = TypeParam <$> identifier <*> constraint
|
||||||
|
constraint = optional (colon *> keyword "value" *> pure Value)
|
||||||
|
|
||||||
|
-- type alias
|
||||||
|
alias :: Parser Declaration
|
||||||
|
alias = do
|
||||||
|
name <- keyword "using" *> identifier <?> "alias definition"
|
||||||
|
params <- parameters
|
||||||
|
namespaces <- asks currentNamespaces
|
||||||
|
local (with params) $ Alias namespaces name params <$ equal <*> type_ <* semi
|
||||||
|
where
|
||||||
|
with params e = e { currentParams = params }
|
||||||
|
|
||||||
|
-- forward declaration
|
||||||
|
forward :: Parser Declaration
|
||||||
|
forward = Forward <$> asks currentNamespaces <*> name <*> parameters <* semi <?> "forward declaration"
|
||||||
|
where
|
||||||
|
name = keyword "struct" *> identifier
|
||||||
|
|
||||||
|
-- attributes parser
|
||||||
|
attributes :: Parser [Attribute]
|
||||||
|
attributes = many attribute <?> "attributes"
|
||||||
|
where
|
||||||
|
attribute = brackets (Attribute <$> qualifiedName <*> parens stringLiteral <?> "attribute")
|
||||||
|
|
||||||
|
-- struct view parser
|
||||||
|
view :: Parser Declaration
|
||||||
|
view = do
|
||||||
|
attr <- attributes
|
||||||
|
name <- keyword "struct" *> identifier
|
||||||
|
decl <- keyword "view_of" *> qualifiedName >>= findStruct
|
||||||
|
fields <- braces $ semiOrCommaSepEnd1 identifier
|
||||||
|
namespaces <- asks currentNamespaces
|
||||||
|
Struct namespaces attr name (declParams decl) (structBase decl) (viewFields decl fields) <$ optional semi
|
||||||
|
where
|
||||||
|
viewFields Struct {..} fields = filter ((`elem` fields) . fieldName) structFields
|
||||||
|
|
||||||
|
-- struct definition parser
|
||||||
|
struct :: Parser Declaration
|
||||||
|
struct = do
|
||||||
|
attr <- attributes
|
||||||
|
name <- keyword "struct" *> identifier <?> "struct definition"
|
||||||
|
params <- parameters
|
||||||
|
namespaces <- asks currentNamespaces
|
||||||
|
updateSymbols $ Forward namespaces name params
|
||||||
|
local (with params) $ Struct namespaces attr name params <$> base <*> fields <* optional semi
|
||||||
|
where
|
||||||
|
base = optional (colon *> userType <?> "base struct")
|
||||||
|
fields = unique $ braces $ manySortedBy (comparing fieldOrdinal) (field <* semi)
|
||||||
|
with params e = e { currentParams = params }
|
||||||
|
unique p = do
|
||||||
|
fields <- p
|
||||||
|
case findDuplicates fields of
|
||||||
|
[] -> return fields
|
||||||
|
Field {..}:_ -> fail $ "Duplicate definition of the field with ordinal " ++ show fieldOrdinal
|
||||||
|
where
|
||||||
|
findDuplicates xs = deleteFirstsBy ordinal xs (nubBy ordinal xs)
|
||||||
|
ordinal = (==) `on` fieldOrdinal
|
||||||
|
|
||||||
|
manySortedBy = manyAccum . insertBy
|
||||||
|
|
||||||
|
-- field definition parser
|
||||||
|
field :: Parser Field
|
||||||
|
field = makeField <$> attributes <*> ordinal <*> modifier <*> ftype <*> identifier <*> optional default_
|
||||||
|
where
|
||||||
|
ordinal = (fromIntegral <$> integer) <* colon <?> "field ordinal"
|
||||||
|
modifier = option Optional
|
||||||
|
(keyword "optional" *> pure Optional
|
||||||
|
<|> keyword "required" *> pure Required
|
||||||
|
<|> keyword "required_optional" *> pure RequiredOptional)
|
||||||
|
default_ = equal *>
|
||||||
|
(keyword "true" *> pure (DefaultBool True)
|
||||||
|
<|> keyword "false" *> pure (DefaultBool False)
|
||||||
|
<|> keyword "nothing" *> pure DefaultNothing
|
||||||
|
<|> DefaultString <$ optional (char 'L') <*> stringLiteral
|
||||||
|
<|> DefaultEnum <$> identifier
|
||||||
|
<|> DefaultFloat <$> try float
|
||||||
|
<|> DefaultInteger <$> fromIntegral <$> integer)
|
||||||
|
|
||||||
|
-- enum definition parser
|
||||||
|
enum :: Parser Declaration
|
||||||
|
enum = Enum <$> asks currentNamespaces <*> attributes <*> name <*> consts <* optional semi <?> "enum definition"
|
||||||
|
where
|
||||||
|
name = keyword "enum" *> (identifier <?> "enum identifier")
|
||||||
|
consts = braces (semiOrCommaSepEnd1 const <?> "enum constant")
|
||||||
|
const = Constant <$> identifier <*> optional value
|
||||||
|
value = equal *> (fromIntegral <$> integer)
|
||||||
|
|
||||||
|
-- basic types parser
|
||||||
|
basicType :: Parser Type
|
||||||
|
basicType =
|
||||||
|
keyword "int8" *> pure BT_Int8
|
||||||
|
<|> keyword "int16" *> pure BT_Int16
|
||||||
|
<|> keyword "int32" *> pure BT_Int32
|
||||||
|
<|> keyword "int64" *> pure BT_Int64
|
||||||
|
<|> keyword "uint8" *> pure BT_UInt8
|
||||||
|
<|> keyword "uint16" *> pure BT_UInt16
|
||||||
|
<|> keyword "uint32" *> pure BT_UInt32
|
||||||
|
<|> keyword "uint64" *> pure BT_UInt64
|
||||||
|
<|> keyword "float" *> pure BT_Float
|
||||||
|
<|> keyword "double" *> pure BT_Double
|
||||||
|
<|> keyword "wstring" *> pure BT_WString
|
||||||
|
<|> keyword "string" *> pure BT_String
|
||||||
|
<|> keyword "bool" *> pure BT_Bool
|
||||||
|
|
||||||
|
keyType :: Parser Type
|
||||||
|
keyType = try (basicType <|> basicUserType) <?> "scalar, string or enum"
|
||||||
|
|
||||||
|
basicUserType :: Parser Type
|
||||||
|
basicUserType = do
|
||||||
|
t <- userType
|
||||||
|
if isBasic t then
|
||||||
|
return t
|
||||||
|
else
|
||||||
|
fail $ "Disallowed key type: " ++ typeName t
|
||||||
|
where
|
||||||
|
isBasic t = case t of
|
||||||
|
BT_TypeParam _ -> True
|
||||||
|
BT_UserDefined a@Alias {..} params -> isBasic $ resolveAlias a params
|
||||||
|
BT_String -> True
|
||||||
|
BT_WString -> True
|
||||||
|
_ -> scalarType t
|
||||||
|
typeName (BT_UserDefined decl _) = declName decl
|
||||||
|
|
||||||
|
-- containers parser
|
||||||
|
complexType :: Parser Type
|
||||||
|
complexType =
|
||||||
|
keyword "list" *> angles (BT_List <$> type_)
|
||||||
|
<|> keyword "blob" *> pure BT_Blob
|
||||||
|
<|> keyword "vector" *> angles (BT_Vector <$> type_)
|
||||||
|
<|> keyword "nullable" *> angles (BT_Nullable <$> type_)
|
||||||
|
<|> keyword "set" *> angles (BT_Set <$> keyType)
|
||||||
|
<|> keyword "map" *> angles (BT_Map <$> keyType <* comma <*> type_)
|
||||||
|
<|> keyword "bonded" *> angles (BT_Bonded <$> userType)
|
||||||
|
|
||||||
|
-- parser for user defined type (struct, enum, alias or type parameter)
|
||||||
|
userType :: Parser Type
|
||||||
|
userType = do
|
||||||
|
name <- qualifiedName
|
||||||
|
params <- asks currentParams
|
||||||
|
case find (isParam name) params of
|
||||||
|
Just param -> return $ BT_TypeParam param
|
||||||
|
Nothing -> do
|
||||||
|
decl <- findSymbol name
|
||||||
|
args <- option [] (angles $ commaSep1 arg)
|
||||||
|
if length args /= length (params decl)
|
||||||
|
then
|
||||||
|
fail $ declName decl ++ " requires " ++ (show.length $ declParams decl) ++ " type argument(s)"
|
||||||
|
else
|
||||||
|
return $ BT_UserDefined decl args
|
||||||
|
where
|
||||||
|
params Enum{..} = []
|
||||||
|
params d = declParams d
|
||||||
|
arg = type_ <|> BT_IntTypeArg <$> (fromIntegral <$> integer)
|
||||||
|
where
|
||||||
|
isParam [name] TypeParam {..} = name == paramName
|
||||||
|
isParam _ _ = False
|
||||||
|
|
||||||
|
-- type parser
|
||||||
|
type_ :: Parser Type
|
||||||
|
type_ = try (basicType <|> complexType <|> userType) <?> "type"
|
||||||
|
|
||||||
|
-- field type parser
|
||||||
|
ftype :: Parser Type
|
||||||
|
ftype = keyword "bond_meta::name" *> pure BT_MetaName
|
||||||
|
<|> keyword "bond_meta::full_name" *> pure BT_MetaFullName
|
||||||
|
<|> type_
|
||||||
|
|
|
@ -0,0 +1,272 @@
|
||||||
|
-- Copyright (c) Microsoft. All rights reserved.
|
||||||
|
-- Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
{-# LANGUAGE OverloadedStrings, RecordWildCards #-}
|
||||||
|
|
||||||
|
module Bond.Schema
|
||||||
|
( Declaration(..)
|
||||||
|
, Field(..)
|
||||||
|
, makeField
|
||||||
|
, Constant(..)
|
||||||
|
, Modifier(..)
|
||||||
|
, Type(..)
|
||||||
|
, TypeParam(..)
|
||||||
|
, Constraint(..)
|
||||||
|
, Default(..)
|
||||||
|
, Import(..)
|
||||||
|
, Language(..)
|
||||||
|
, Namespace(..)
|
||||||
|
, Attribute(..)
|
||||||
|
, QualifiedName(..)
|
||||||
|
, takeName
|
||||||
|
, takeNamespace
|
||||||
|
, showQualifiedName
|
||||||
|
, scalarType
|
||||||
|
, listType
|
||||||
|
, associativeType
|
||||||
|
, containerType
|
||||||
|
, stringType
|
||||||
|
, metaType
|
||||||
|
, structType
|
||||||
|
, nullableType
|
||||||
|
, duplicateDeclaration
|
||||||
|
, isBaseField
|
||||||
|
, foldMapFields
|
||||||
|
, foldMapStructFields
|
||||||
|
, foldMapType
|
||||||
|
, metaField
|
||||||
|
, resolveAlias
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Data.Maybe
|
||||||
|
import Data.Word
|
||||||
|
import Data.List
|
||||||
|
import Data.Foldable (foldMap)
|
||||||
|
import Data.Monoid
|
||||||
|
import System.FilePath
|
||||||
|
import Data.Text.Lazy.Builder
|
||||||
|
import Text.Shakespeare.Text
|
||||||
|
import Bond.Util
|
||||||
|
|
||||||
|
type QualifiedName = [String]
|
||||||
|
|
||||||
|
takeName :: QualifiedName -> String
|
||||||
|
takeName = last
|
||||||
|
|
||||||
|
takeNamespace :: QualifiedName -> QualifiedName
|
||||||
|
takeNamespace = subtract 1 . length >>= take
|
||||||
|
|
||||||
|
showQualifiedName :: QualifiedName -> String
|
||||||
|
showQualifiedName = sepBy "." id
|
||||||
|
|
||||||
|
data Modifier = Optional | Required | RequiredOptional deriving Eq
|
||||||
|
|
||||||
|
data Type =
|
||||||
|
BT_Int8 | BT_Int16 | BT_Int32 | BT_Int64 |
|
||||||
|
BT_UInt8 | BT_UInt16 | BT_UInt32 | BT_UInt64 |
|
||||||
|
BT_Float | BT_Double |
|
||||||
|
BT_Bool |
|
||||||
|
BT_String | BT_WString |
|
||||||
|
BT_MetaName | BT_MetaFullName |
|
||||||
|
BT_Blob |
|
||||||
|
BT_Maybe Type |
|
||||||
|
BT_List Type |
|
||||||
|
BT_Vector Type |
|
||||||
|
BT_Nullable Type |
|
||||||
|
BT_Set Type |
|
||||||
|
BT_Map Type Type |
|
||||||
|
BT_Bonded Type |
|
||||||
|
BT_IntTypeArg Int |
|
||||||
|
BT_TypeParam TypeParam |
|
||||||
|
BT_UserDefined Declaration [Type]
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
scalarType BT_Int8 = True
|
||||||
|
scalarType BT_Int16 = True
|
||||||
|
scalarType BT_Int32 = True
|
||||||
|
scalarType BT_Int64 = True
|
||||||
|
scalarType BT_UInt8 = True
|
||||||
|
scalarType BT_UInt16 = True
|
||||||
|
scalarType BT_UInt32 = True
|
||||||
|
scalarType BT_UInt64 = True
|
||||||
|
scalarType BT_Float = True
|
||||||
|
scalarType BT_Double = True
|
||||||
|
scalarType BT_Bool = True
|
||||||
|
scalarType (BT_TypeParam (TypeParam _ (Just Value))) = True
|
||||||
|
scalarType (BT_UserDefined Enum {..} _) = True
|
||||||
|
scalarType _ = False
|
||||||
|
|
||||||
|
metaType BT_MetaName = True
|
||||||
|
metaType BT_MetaFullName = True
|
||||||
|
metaType _ = False
|
||||||
|
|
||||||
|
stringType BT_String = True
|
||||||
|
stringType BT_WString = True
|
||||||
|
stringType _ = False
|
||||||
|
|
||||||
|
listType (BT_List _) = True
|
||||||
|
listType (BT_Vector _) = True
|
||||||
|
listType _ = False
|
||||||
|
|
||||||
|
associativeType (BT_Set _) = True
|
||||||
|
associativeType (BT_Map _ _) = True
|
||||||
|
associativeType _ = False
|
||||||
|
|
||||||
|
containerType f = listType f || associativeType f
|
||||||
|
|
||||||
|
structType (BT_UserDefined Struct {} _) = True
|
||||||
|
structType (BT_UserDefined a@Alias {} args) = structType $ resolveAlias a args
|
||||||
|
structType _ = False
|
||||||
|
|
||||||
|
nullableType (BT_Nullable _) = True
|
||||||
|
nullableType _ = False
|
||||||
|
|
||||||
|
metaField Field {..} = Any $ metaType fieldType
|
||||||
|
|
||||||
|
data Default =
|
||||||
|
DefaultBool Bool |
|
||||||
|
DefaultInteger Integer |
|
||||||
|
DefaultFloat Double |
|
||||||
|
DefaultString String |
|
||||||
|
DefaultEnum String|
|
||||||
|
DefaultNothing
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
data Attribute =
|
||||||
|
Attribute
|
||||||
|
{ attrName :: QualifiedName -- attribute name
|
||||||
|
, attrValue :: String -- value
|
||||||
|
}
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
data Field =
|
||||||
|
Field
|
||||||
|
{ fieldAttributes :: [Attribute] -- zero or more attributes
|
||||||
|
, fieldOrdinal :: Word16 -- ordinal
|
||||||
|
, fieldModifier :: Modifier -- field modifier
|
||||||
|
, fieldType :: Type -- type
|
||||||
|
, fieldName :: String -- field name
|
||||||
|
, fieldDefault :: Maybe Default -- optional default value
|
||||||
|
}
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
makeField a o m t n d@(Just DefaultNothing) = Field a o m (BT_Maybe t) n d
|
||||||
|
makeField a o m t n d = Field a o m t n d
|
||||||
|
|
||||||
|
data Constant =
|
||||||
|
Constant
|
||||||
|
{ constantName :: String -- enum constant name
|
||||||
|
, constantValue :: Maybe Int -- optional value
|
||||||
|
}
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
data Constraint = Value deriving Eq
|
||||||
|
|
||||||
|
instance Show Constraint where
|
||||||
|
show Value = ": value"
|
||||||
|
|
||||||
|
data TypeParam =
|
||||||
|
TypeParam
|
||||||
|
{ paramName :: String
|
||||||
|
, paramConstraint :: Maybe Constraint
|
||||||
|
}
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
instance Show TypeParam where
|
||||||
|
show TypeParam {..} = paramName ++ optional show paramConstraint
|
||||||
|
|
||||||
|
data Declaration =
|
||||||
|
Struct
|
||||||
|
{ declNamespaces :: [Namespace] -- namespace(s) in which the struct is declared
|
||||||
|
, declAttributes :: [Attribute] -- zero or more attributes
|
||||||
|
, declName :: String -- struct identifier
|
||||||
|
, declParams :: [TypeParam] -- type parameters for generics
|
||||||
|
, structBase :: Maybe Type -- optional base struct
|
||||||
|
, structFields :: [Field] -- zero or more fields
|
||||||
|
}
|
||||||
|
|
|
||||||
|
Enum
|
||||||
|
{ declNamespaces :: [Namespace] -- namespace(s) in which the enum is declared
|
||||||
|
, declAttributes :: [Attribute] -- zero or more attributes
|
||||||
|
, declName :: String -- enum identifier
|
||||||
|
, enumConstants :: [Constant] -- one or more constant values
|
||||||
|
}
|
||||||
|
|
|
||||||
|
Forward
|
||||||
|
{ declNamespaces :: [Namespace] -- namespace(s) in which the struct is declared
|
||||||
|
, declName :: String -- struct identifier
|
||||||
|
, declParams :: [TypeParam] -- type parameters for generics
|
||||||
|
}
|
||||||
|
|
|
||||||
|
Alias
|
||||||
|
{ declNamespaces :: [Namespace] -- namespace(s) in which the alias is declared
|
||||||
|
, declName :: String -- alias identifier
|
||||||
|
, declParams :: [TypeParam] -- type parameters for generics
|
||||||
|
, aliasType :: Type -- aliased type
|
||||||
|
}
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
showTypeParams = angles . sepBy ", " show
|
||||||
|
|
||||||
|
instance Show Declaration where
|
||||||
|
show Struct {..} = "struct " ++ declName ++ showTypeParams declParams
|
||||||
|
show Enum {..} = "enum " ++ declName
|
||||||
|
show Forward {..} = "struct declaration " ++ declName ++ showTypeParams declParams
|
||||||
|
show Alias {..} = "alias " ++ declName ++ showTypeParams declParams
|
||||||
|
|
||||||
|
mapType :: (Type -> Type) -> Type -> Type
|
||||||
|
mapType f (BT_UserDefined decl args) = BT_UserDefined decl $ map f args
|
||||||
|
mapType f (BT_Map key value) = BT_Map (f key) (f value)
|
||||||
|
mapType f (BT_List element) = BT_List $ f element
|
||||||
|
mapType f (BT_Vector element) = BT_Vector $ f element
|
||||||
|
mapType f (BT_Set element) = BT_Set $ f element
|
||||||
|
mapType f (BT_Nullable element) = BT_Nullable $ f element
|
||||||
|
mapType f (BT_Bonded struct) = BT_Bonded $ f struct
|
||||||
|
mapType f x = f x
|
||||||
|
|
||||||
|
foldMapFields :: (Monoid m) => (Field -> m) -> Type -> m
|
||||||
|
foldMapFields f t = case t of
|
||||||
|
(BT_UserDefined s@Struct {..} _) -> optional (foldMapFields f) structBase <> foldMap f structFields
|
||||||
|
(BT_UserDefined a@Alias {..} args) -> foldMapFields f $ resolveAlias a args
|
||||||
|
_ -> mempty
|
||||||
|
|
||||||
|
foldMapStructFields f s = foldMapFields f $ BT_UserDefined s []
|
||||||
|
|
||||||
|
foldMapType :: (Monoid m) => (Type -> m) -> Type -> m
|
||||||
|
foldMapType f t@(BT_UserDefined decl args) = f t <> foldMap (foldMapType f) args
|
||||||
|
foldMapType f t@(BT_Map key value) = f t <> foldMapType f key <> foldMapType f value
|
||||||
|
foldMapType f t@(BT_List element) = f t <> foldMapType f element
|
||||||
|
foldMapType f t@(BT_Vector element) = f t <> foldMapType f element
|
||||||
|
foldMapType f t@(BT_Set element) = f t <> foldMapType f element
|
||||||
|
foldMapType f t@(BT_Nullable element) = f t <> foldMapType f element
|
||||||
|
foldMapType f t@(BT_Bonded struct) = f t <> foldMapType f struct
|
||||||
|
foldMapType f x = f x
|
||||||
|
|
||||||
|
|
||||||
|
resolveAlias :: Declaration -> [Type] -> Type
|
||||||
|
resolveAlias Alias {..} args = mapType resolveParam $ resolveParam aliasType
|
||||||
|
where
|
||||||
|
resolveParam (BT_TypeParam param) = snd.fromJust $ find ((param ==).fst) paramsArgs
|
||||||
|
resolveParam x = x
|
||||||
|
paramsArgs = zip declParams args
|
||||||
|
|
||||||
|
duplicateDeclaration :: Declaration -> Declaration -> Bool
|
||||||
|
duplicateDeclaration left right =
|
||||||
|
(declName left == declName right)
|
||||||
|
&& not (null $ intersect (declNamespaces left) (declNamespaces right))
|
||||||
|
|
||||||
|
isBaseField :: String -> Maybe Type -> Bool
|
||||||
|
isBaseField name = getAny . optional (foldMapFields (Any.(name==).fieldName))
|
||||||
|
|
||||||
|
data Import = Import FilePath
|
||||||
|
|
||||||
|
data Language = Cpp | Cs | CSharp | Java deriving (Eq)
|
||||||
|
|
||||||
|
data Namespace =
|
||||||
|
Namespace
|
||||||
|
{ nsLanguage :: Maybe Language
|
||||||
|
, nsName :: QualifiedName
|
||||||
|
}
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
-- Copyright (c) Microsoft. All rights reserved.
|
||||||
|
-- Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
{-# LANGUAGE QuasiQuotes, OverloadedStrings #-}
|
||||||
|
|
||||||
|
module Bond.Template.Cpp.Apply_cpp (apply_cpp) where
|
||||||
|
|
||||||
|
import System.FilePath
|
||||||
|
import Data.Monoid
|
||||||
|
import Text.Shakespeare.Text
|
||||||
|
import Bond.Schema
|
||||||
|
import Bond.Template.Util
|
||||||
|
import Bond.Template.Cpp.Apply_h
|
||||||
|
import qualified Bond.Template.Cpp.Util as CPP
|
||||||
|
|
||||||
|
-- generate the *_apply.cpp file from parsed .bond file
|
||||||
|
apply_cpp protocols cpp file imports declarations = ("_apply.cpp", [lt|
|
||||||
|
#include "#{file}_apply.h"
|
||||||
|
#include "#{file}_reflection.h"
|
||||||
|
|
||||||
|
#{CPP.openNamespace cpp}
|
||||||
|
#{newlineSepEnd 1 (apply protocols attr body) declarations}
|
||||||
|
#{CPP.closeNamespace cpp}
|
||||||
|
|])
|
||||||
|
where
|
||||||
|
body = [lt|
|
||||||
|
{
|
||||||
|
return bond::Apply<>(transform, value);
|
||||||
|
}|]
|
||||||
|
|
||||||
|
attr = [lt||]
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
-- Copyright (c) Microsoft. All rights reserved.
|
||||||
|
-- Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
{-# LANGUAGE QuasiQuotes, OverloadedStrings, RecordWildCards #-}
|
||||||
|
|
||||||
|
module Bond.Template.Cpp.Apply_h (apply_h, Protocol(..), apply) where
|
||||||
|
|
||||||
|
import System.FilePath
|
||||||
|
import Data.Monoid
|
||||||
|
import Text.Shakespeare.Text
|
||||||
|
import Bond.Schema
|
||||||
|
import Bond.Util
|
||||||
|
import Bond.Template.Util
|
||||||
|
import qualified Bond.Template.Cpp.Util as CPP
|
||||||
|
|
||||||
|
data Protocol =
|
||||||
|
Protocol
|
||||||
|
{ protocolReader :: String
|
||||||
|
, protocolWriter :: String
|
||||||
|
}
|
||||||
|
|
||||||
|
-- generate the *_apply.h file from parsed .bond file
|
||||||
|
apply_h protocols attribute cpp file imports declarations = ("_apply.h", [lt|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "#{file}_types.h"
|
||||||
|
#include <bond/core/bond.h>
|
||||||
|
#include <bond/stream/output_buffer.h>
|
||||||
|
#{newlineSep 0 includeImport imports}
|
||||||
|
|
||||||
|
#{CPP.openNamespace cpp}
|
||||||
|
#{newlineSepEnd 1 (apply protocols attr semi) declarations}
|
||||||
|
#{CPP.closeNamespace cpp}
|
||||||
|
|])
|
||||||
|
where
|
||||||
|
includeImport (Import path) = [lt|#include "#{dropExtension path}_apply.h"|]
|
||||||
|
|
||||||
|
attr = optional (\a -> [lt|#{a}
|
||||||
|
|]) attribute
|
||||||
|
|
||||||
|
semi = [lt|;|]
|
||||||
|
|
||||||
|
-- Apply overloads
|
||||||
|
apply protocols attr body Struct {..} | null declParams = [lt|
|
||||||
|
//
|
||||||
|
// Overloads of Apply function with common transforms for #{declName}.
|
||||||
|
// These overloads will be selected using argument dependent lookup
|
||||||
|
// before bond::Apply function templates.
|
||||||
|
//
|
||||||
|
#{attr}bool Apply(const bond::To<#{declName}>& transform,
|
||||||
|
const bond::bonded<#{declName}>& value)#{body}
|
||||||
|
|
||||||
|
#{attr}bool Apply(const bond::InitSchemaDef& transform,
|
||||||
|
const #{declName}& value)#{body}
|
||||||
|
#{newlineSep 1 applyOverloads protocols}|]
|
||||||
|
where
|
||||||
|
applyOverloads p = [lt|#{deserialization p}
|
||||||
|
#{serialization serializer p}
|
||||||
|
#{serialization marshaler p}|]
|
||||||
|
|
||||||
|
serializer = "Serializer" :: String
|
||||||
|
marshaler = "Marshaler" :: String
|
||||||
|
|
||||||
|
deserialization Protocol {..} = [lt|
|
||||||
|
#{attr}bool Apply(const bond::To<#{declName}>& transform,
|
||||||
|
const bond::bonded<#{declName}, bond::#{protocolReader}<bond::InputBuffer>&>& value)#{body}
|
||||||
|
|
||||||
|
#{attr}bool Apply(const bond::To<#{declName}>& transform,
|
||||||
|
const bond::bonded<void, bond::#{protocolReader}<bond::InputBuffer>&>& value)#{body}|]
|
||||||
|
|
||||||
|
serialization transform Protocol {..} = [lt|
|
||||||
|
#{attr}bool Apply(const bond::#{transform}<bond::#{protocolWriter}<bond::OutputBuffer> >& transform,
|
||||||
|
const #{declName}& value)#{body}
|
||||||
|
|
||||||
|
#{attr}bool Apply(const bond::#{transform}<bond::#{protocolWriter}<bond::OutputBuffer> >& transform,
|
||||||
|
const bond::bonded<#{declName}>& value)#{body}
|
||||||
|
#{newlineSep 1 (transcoding transform) protocols}|]
|
||||||
|
where
|
||||||
|
transcoding transform Protocol {protocolReader = fromReader} = [lt|
|
||||||
|
#{attr}bool Apply(const bond::#{transform}<bond::#{protocolWriter}<bond::OutputBuffer> >& transform,
|
||||||
|
const bond::bonded<#{declName}, bond::#{fromReader}<bond::InputBuffer>&>& value)#{body}|]
|
||||||
|
|
||||||
|
apply _ _ _ _ = mempty
|
|
@ -0,0 +1,40 @@
|
||||||
|
-- Copyright (c) Microsoft. All rights reserved.
|
||||||
|
-- Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
{-# LANGUAGE QuasiQuotes, OverloadedStrings, RecordWildCards #-}
|
||||||
|
|
||||||
|
module Bond.Template.Cpp.Enum_h (enum_h) where
|
||||||
|
|
||||||
|
import System.FilePath
|
||||||
|
import Data.Monoid
|
||||||
|
import Text.Shakespeare.Text
|
||||||
|
import Bond.Schema
|
||||||
|
import Bond.Template.TypeMapping
|
||||||
|
import Bond.Template.Util
|
||||||
|
import qualified Bond.Template.Cpp.Util as CPP
|
||||||
|
|
||||||
|
-- generate the *_types.h file from parsed .bond file
|
||||||
|
enum_h cpp file imports declarations = ("_enum.h", [lt|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#{CPP.openNamespace cpp}
|
||||||
|
namespace _bond_enumerators
|
||||||
|
{
|
||||||
|
#{newlineSep 1 typeDeclaration declarations}
|
||||||
|
} // namespace _bond_enumerators
|
||||||
|
|
||||||
|
#{newlineSep 0 usingDeclaration declarations}
|
||||||
|
#{CPP.closeNamespace cpp}
|
||||||
|
|])
|
||||||
|
where
|
||||||
|
-- enum definition
|
||||||
|
typeDeclaration e@Enum {..} = [lt|
|
||||||
|
namespace #{declName}
|
||||||
|
{
|
||||||
|
#{CPP.enumDefinition e}
|
||||||
|
} // namespace #{declName}
|
||||||
|
|]
|
||||||
|
typeDeclaration _ = mempty
|
||||||
|
|
||||||
|
usingDeclaration Enum {..} = [lt|using namespace _bond_enumerators::#{declName};|]
|
||||||
|
usingDeclaration _ = mempty
|
|
@ -0,0 +1,113 @@
|
||||||
|
-- Copyright (c) Microsoft. All rights reserved.
|
||||||
|
-- Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
{-# LANGUAGE QuasiQuotes, OverloadedStrings, RecordWildCards #-}
|
||||||
|
|
||||||
|
module Bond.Template.Cpp.Reflection_h (reflection_h) where
|
||||||
|
|
||||||
|
import System.FilePath
|
||||||
|
import Data.Monoid
|
||||||
|
import Text.Shakespeare.Text
|
||||||
|
import Bond.Schema
|
||||||
|
import Bond.Template.TypeMapping
|
||||||
|
import Bond.Template.Util
|
||||||
|
import qualified Bond.Template.Cpp.Util as CPP
|
||||||
|
|
||||||
|
-- generate the *_refection.h file from parsed .bond file
|
||||||
|
reflection_h cpp file imports declarations = ("_reflection.h", [lt|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "#{file}_types.h"
|
||||||
|
#include <bond/core/reflection.h>
|
||||||
|
#{newlineSepEnd 0 include imports}
|
||||||
|
#{CPP.openNamespace cpp}
|
||||||
|
#{doubleLineSepEnd 1 schema declarations}
|
||||||
|
#{CPP.closeNamespace cpp}
|
||||||
|
|])
|
||||||
|
where
|
||||||
|
idlNamespace = getIdlQualifiedName $ getIdlNamespace cpp
|
||||||
|
|
||||||
|
-- C++ type
|
||||||
|
cppType = getTypeName cpp
|
||||||
|
|
||||||
|
-- template for generating #include statement from import
|
||||||
|
include (Import path) = [lt|#include "#{dropExtension path}_reflection.h"|]
|
||||||
|
|
||||||
|
-- template for generating struct schema
|
||||||
|
schema s@Struct {..} = [lt|//
|
||||||
|
// #{declName}
|
||||||
|
//
|
||||||
|
#{CPP.template s}struct #{structName}::Schema
|
||||||
|
{
|
||||||
|
typedef #{baseType structBase} base;
|
||||||
|
|
||||||
|
static const bond::Metadata metadata;
|
||||||
|
#{newlineBeginSep 2 fieldMetadata structFields}
|
||||||
|
|
||||||
|
public: struct var
|
||||||
|
{#{fieldTemplates}};
|
||||||
|
|
||||||
|
private: typedef boost::mpl::list<> fields0;
|
||||||
|
#{newlineSep 2 pushField indexedFields}
|
||||||
|
|
||||||
|
public: typedef #{typename}fields#{length structFields}::type fields;
|
||||||
|
#{constructor}
|
||||||
|
|
||||||
|
static bond::Metadata GetMetadata()
|
||||||
|
{
|
||||||
|
return bond::reflection::MetadataInit#{metadataInitArgs}("#{declName}", "#{idlNamespace}.#{declName}",
|
||||||
|
#{CPP.attributeInit declAttributes}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#{onlyTemplate $ CPP.schemaMetadata cpp s}|]
|
||||||
|
where
|
||||||
|
structParams = CPP.structParams s
|
||||||
|
|
||||||
|
structName = CPP.structName s
|
||||||
|
|
||||||
|
onlyTemplate x = if null declParams then mempty else x
|
||||||
|
|
||||||
|
metadataInitArgs = onlyTemplate [lt|<boost::mpl::list#{structParams} >|]
|
||||||
|
|
||||||
|
typename = onlyTemplate [lt|typename |]
|
||||||
|
|
||||||
|
-- constructor, generated only for struct templates
|
||||||
|
constructor = onlyTemplate [lt|
|
||||||
|
Schema()
|
||||||
|
{
|
||||||
|
// Force instantiation of template statics
|
||||||
|
(void)metadata;
|
||||||
|
#{newlineSep 3 static structFields}
|
||||||
|
}|]
|
||||||
|
where
|
||||||
|
static Field {..} = [lt|(void)s_#{fieldName}_metadata;|]
|
||||||
|
|
||||||
|
-- reversed list of field names zipped with indexes
|
||||||
|
indexedFields :: [(String, Int)]
|
||||||
|
indexedFields = zipWith ((,) . fieldName) (reverse structFields) [0..]
|
||||||
|
|
||||||
|
baseType (Just base) = cppType base
|
||||||
|
baseType Nothing = "bond::no_base"
|
||||||
|
|
||||||
|
pushField (field, i) =
|
||||||
|
[lt|private: typedef #{typename}boost::mpl::push_front<fields#{i}, #{typename}var::#{field}>::type fields#{i + 1};|]
|
||||||
|
|
||||||
|
fieldMetadata Field {..} =
|
||||||
|
[lt|private: static const bond::Metadata s_#{fieldName}_metadata;|]
|
||||||
|
|
||||||
|
fieldTemplates = mconcatFor structFields $ \ f@Field {..} -> [lt|
|
||||||
|
// #{fieldName}
|
||||||
|
typedef bond::reflection::FieldTemplate<
|
||||||
|
#{fieldOrdinal},
|
||||||
|
#{CPP.modifierTag f},
|
||||||
|
#{structName},
|
||||||
|
#{cppType fieldType},
|
||||||
|
&#{structName}::#{fieldName},
|
||||||
|
&s_#{fieldName}_metadata
|
||||||
|
> #{fieldName};
|
||||||
|
|]
|
||||||
|
|
||||||
|
|
||||||
|
-- nothing to generate for enums
|
||||||
|
schema _ = mempty
|
|
@ -0,0 +1,71 @@
|
||||||
|
-- Copyright (c) Microsoft. All rights reserved.
|
||||||
|
-- Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
{-# LANGUAGE QuasiQuotes, OverloadedStrings, RecordWildCards #-}
|
||||||
|
|
||||||
|
module Bond.Template.Cpp.Types_cpp (types_cpp) where
|
||||||
|
|
||||||
|
import Data.Monoid
|
||||||
|
import Text.Shakespeare.Text
|
||||||
|
import Bond.Schema
|
||||||
|
import Bond.Template.TypeMapping
|
||||||
|
import Bond.Template.Util
|
||||||
|
import qualified Bond.Template.Cpp.Util as CPP
|
||||||
|
|
||||||
|
-- generate the *_types_cpp file from parsed .bond file
|
||||||
|
types_cpp cpp file imports declarations = ("_types.cpp", [lt|
|
||||||
|
#include "#{file}_reflection.h"
|
||||||
|
#include <bond/core/exception.h>
|
||||||
|
|
||||||
|
#{CPP.openNamespace cpp}
|
||||||
|
#{doubleLineSepEnd 1 statics declarations}
|
||||||
|
#{CPP.closeNamespace cpp}
|
||||||
|
|])
|
||||||
|
where
|
||||||
|
-- definitions of Schema statics for non-generic structs
|
||||||
|
statics s@Struct {..} =
|
||||||
|
if null declParams then CPP.schemaMetadata cpp s else mempty
|
||||||
|
|
||||||
|
-- global variables for enum name/value conversions
|
||||||
|
statics e@Enum {..} = [lt|
|
||||||
|
namespace _bond_enumerators
|
||||||
|
{
|
||||||
|
namespace #{declName}
|
||||||
|
{
|
||||||
|
const
|
||||||
|
std::map<std::string, enum #{declName}> _name_to_value_#{declName} =
|
||||||
|
boost::assign::map_list_of<std::string, enum #{declName}>
|
||||||
|
#{newlineSep 4 constant enumConstants};
|
||||||
|
|
||||||
|
const
|
||||||
|
std::map<enum #{declName}, std::string> _value_to_name_#{declName} =
|
||||||
|
bond::reverse_map(_name_to_value_#{declName});
|
||||||
|
|
||||||
|
const std::string& ToString(enum #{declName} value)
|
||||||
|
{
|
||||||
|
std::map<enum #{declName}, std::string>::const_iterator it =
|
||||||
|
GetValueToNameMap(value).find(value);
|
||||||
|
|
||||||
|
if (GetValueToNameMap(value).end() == it)
|
||||||
|
bond::InvalidEnumValueException(value, "#{declName}");
|
||||||
|
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FromString(const std::string& name, enum #{declName}& value)
|
||||||
|
{
|
||||||
|
std::map<std::string, enum #{declName}>::const_iterator it =
|
||||||
|
_name_to_value_#{declName}.find(name);
|
||||||
|
|
||||||
|
if (_name_to_value_#{declName}.end() == it)
|
||||||
|
bond::InvalidEnumValueException(name.c_str(), "#{declName}");
|
||||||
|
|
||||||
|
value = it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace #{declName}
|
||||||
|
} // namespace _bond_enumerators|]
|
||||||
|
where
|
||||||
|
constant Constant {..} = [lt|("#{constantName}", #{constantName})|]
|
||||||
|
|
||||||
|
statics _ = mempty
|
|
@ -0,0 +1,365 @@
|
||||||
|
-- Copyright (c) Microsoft. All rights reserved.
|
||||||
|
-- Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
{-# LANGUAGE QuasiQuotes, OverloadedStrings, RecordWildCards #-}
|
||||||
|
|
||||||
|
module Bond.Template.Cpp.Types_h (types_h) where
|
||||||
|
|
||||||
|
import System.FilePath
|
||||||
|
import Data.Maybe
|
||||||
|
import Data.List
|
||||||
|
import Data.Monoid
|
||||||
|
import Data.Text.Lazy.Builder
|
||||||
|
import qualified Data.Text.Lazy as L
|
||||||
|
import Data.Foldable (foldMap)
|
||||||
|
import Text.Shakespeare.Text
|
||||||
|
import Bond.Version
|
||||||
|
import Bond.Schema
|
||||||
|
import Bond.Util
|
||||||
|
import Bond.Template.TypeMapping
|
||||||
|
import Bond.Template.Util
|
||||||
|
import qualified Bond.Template.Cpp.Util as CPP
|
||||||
|
|
||||||
|
-- generate the *_types.h file from parsed .bond file
|
||||||
|
types_h userHeaders enumHeader allocator cpp file imports declarations = ("_types.h", [lt|
|
||||||
|
#pragma once
|
||||||
|
#{newlineBeginSep 0 includeHeader userHeaders}
|
||||||
|
#include <bond/core/bond_version.h>
|
||||||
|
|
||||||
|
#if BOND_VERSION < 0x302
|
||||||
|
#error This file was generated by a newer version of Bond compiler
|
||||||
|
#error and is incompatible with your version Bond library.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if BOND_MIN_CODEGEN_VERSION > 0x#{majorVersion}#{minorVersion}
|
||||||
|
#error This file was generated by an older version of Bond compiler
|
||||||
|
#error and is incompatible with your version Bond library.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <bond/core/config.h>
|
||||||
|
#include <bond/core/containers.h>
|
||||||
|
#{newlineSep 0 optionalHeader bondHeaders}
|
||||||
|
#{includeEnum}
|
||||||
|
#{newlineSepEnd 0 includeImport imports}
|
||||||
|
#{CPP.openNamespace cpp}
|
||||||
|
#{doubleLineSep 1 typeDeclaration declarations}
|
||||||
|
#{CPP.closeNamespace cpp}
|
||||||
|
#{optional usesAllocatorSpecialization allocator}
|
||||||
|
|])
|
||||||
|
where
|
||||||
|
cppType = getTypeName cpp
|
||||||
|
idlNamespace = getIdlQualifiedName $ getIdlNamespace cpp
|
||||||
|
|
||||||
|
cppDefaultValue = CPP.defaultValue cpp
|
||||||
|
|
||||||
|
includeImport (Import path) = [lt|#include "#{dropExtension path}_types.h"|]
|
||||||
|
|
||||||
|
optionalHeader (False, _) = mempty
|
||||||
|
optionalHeader (True, header) = includeHeader header
|
||||||
|
|
||||||
|
includeHeader header = [lt|#include #{header}|]
|
||||||
|
|
||||||
|
includeEnum = if enumHeader then [lt|#include "#{file}_enum.h"|] else mempty
|
||||||
|
|
||||||
|
-- True if declarations have any type satisfying f
|
||||||
|
have f = getAny $ foldMap g declarations
|
||||||
|
where
|
||||||
|
g s@Struct{..} = foldMapStructFields (foldMapType f . fieldType) s
|
||||||
|
<> optional (foldMapType f) structBase
|
||||||
|
g Alias{..} = foldMapType f aliasType
|
||||||
|
g _ = Any False
|
||||||
|
|
||||||
|
anyBonded (BT_Bonded _) = Any True
|
||||||
|
anyBonded _ = Any False
|
||||||
|
|
||||||
|
anyBlob BT_Blob = Any True
|
||||||
|
anyBlob _ = Any False
|
||||||
|
|
||||||
|
anyNullable = Any . nullableType
|
||||||
|
|
||||||
|
bondHeaders :: [(Bool, String)]
|
||||||
|
bondHeaders = [
|
||||||
|
(have anyNullable, "<bond/core/nullable.h>"),
|
||||||
|
(have anyBonded, "<bond/core/bonded.h>"),
|
||||||
|
(have anyBlob, "<bond/core/blob.h>")]
|
||||||
|
|
||||||
|
usesAllocatorSpecialization alloc = [lt|
|
||||||
|
#if !defined(BOND_NO_CXX11_ALLOCATOR)
|
||||||
|
namespace std
|
||||||
|
{
|
||||||
|
#{doubleLineSep 1 usesAllocator declarations}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|]
|
||||||
|
where
|
||||||
|
usesAllocator s@Struct {..} = [lt|template <typename _Alloc#{sepBeginBy ", typename " paramName declParams}>
|
||||||
|
struct uses_allocator<#{typename} #{getDeclQualifiedTypeName cpp s}#{CPP.structParams s}, _Alloc>
|
||||||
|
: is_convertible<_Alloc, #{allocParam}>
|
||||||
|
{};|]
|
||||||
|
where
|
||||||
|
typename = if null declParams then mempty else [lt|typename|]
|
||||||
|
allocParam = if last alloc == '>' then alloc ++ " " else alloc
|
||||||
|
usesAllocator _ = mempty
|
||||||
|
|
||||||
|
-- forward declaration
|
||||||
|
typeDeclaration f@Forward {..} = [lt|#{CPP.template f}struct #{declName};|]
|
||||||
|
|
||||||
|
-- struct definition
|
||||||
|
typeDeclaration s@Struct {..} = [lt|
|
||||||
|
#{template}struct #{declName}#{optional base structBase}
|
||||||
|
{
|
||||||
|
#{newlineSepEnd 2 field structFields}#{defaultCtor}
|
||||||
|
|
||||||
|
#{copyCtor}
|
||||||
|
#{moveCtor}
|
||||||
|
#{optional allocatorCtor allocator}
|
||||||
|
#{assignmentOp}
|
||||||
|
|
||||||
|
bool operator==(const #{declName}&#{otherParam}) const
|
||||||
|
{
|
||||||
|
return true#{optional baseEqual structBase}#{newlineBeginSep 4 fieldEqual structFields};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const #{declName}& other) const
|
||||||
|
{
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
|
||||||
|
void swap(#{declName}&#{otherParam})
|
||||||
|
{
|
||||||
|
using std::swap;#{optional swapBase structBase}#{newlineBeginSep 3 swapField structFields}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Schema;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
#{initMetadata}
|
||||||
|
};
|
||||||
|
|
||||||
|
#{template}inline void swap(#{structName}& left, #{structName}& right)
|
||||||
|
{
|
||||||
|
left.swap(right);
|
||||||
|
}|]
|
||||||
|
where
|
||||||
|
template = CPP.template s
|
||||||
|
structName = CPP.structName s
|
||||||
|
|
||||||
|
otherParam = if hasOnlyMetaFields then mempty else [lt| other|]
|
||||||
|
hasOnlyMetaFields = not (any (not . getAny . metaField) structFields) && isNothing structBase
|
||||||
|
hasMetaFields = getAny $ foldMapStructFields metaField s
|
||||||
|
|
||||||
|
base x = [lt|
|
||||||
|
: #{cppType x}|]
|
||||||
|
|
||||||
|
field Field {..} = [lt|#{cppType fieldType} #{fieldName};|]
|
||||||
|
|
||||||
|
notMeta Field {fieldType = BT_MetaName, ..} _ = [lt|/* skip bond_meta::name field '#{fieldName}' */|]
|
||||||
|
notMeta Field {fieldType = BT_MetaFullName, ..} _ = [lt|/* skip bond_meta::full_name field '#{fieldName}' */|]
|
||||||
|
notMeta _ f = f
|
||||||
|
|
||||||
|
fieldEqual f@Field {..} = notMeta f [lt|&& (#{fieldName} ==#{otherParam}.#{fieldName})|]
|
||||||
|
|
||||||
|
baseEqual b = [lt|
|
||||||
|
&& (static_cast<const #{cppType b}&>(*this) == static_cast<const #{cppType b}&>(#{otherParam}))|]
|
||||||
|
|
||||||
|
swapField f@Field {..} = notMeta f [lt|swap(#{fieldName},#{otherParam}.#{fieldName});|]
|
||||||
|
|
||||||
|
swapBase b = [lt|
|
||||||
|
#{cppType b}::swap(#{otherParam});|]
|
||||||
|
|
||||||
|
-- value to pass to field initializer in ctor initialize list
|
||||||
|
-- or Nothing if field doesn't need explicit initialization
|
||||||
|
initValue (BT_Maybe _) _ = Nothing
|
||||||
|
initValue (BT_TypeParam _) _ = Just mempty
|
||||||
|
initValue (BT_UserDefined a@Alias {} args) d =
|
||||||
|
case findAliasMapping cpp a of
|
||||||
|
Nothing -> initValue (resolveAlias a args) d
|
||||||
|
Just _ -> Just mempty
|
||||||
|
initValue t (Just d) = Just $ cppDefaultValue t d
|
||||||
|
initValue t _
|
||||||
|
| scalarType t = Just mempty
|
||||||
|
| otherwise = Nothing
|
||||||
|
|
||||||
|
-- constructor initializer list from 'base' and 'fields' initializers
|
||||||
|
initializeList base fields = between colon mempty $ commaLineSep 3 id [base, fields]
|
||||||
|
where
|
||||||
|
colon = [lt|
|
||||||
|
: |]
|
||||||
|
|
||||||
|
-- constructor body
|
||||||
|
ctorBody = if hasMetaFields then [lt|
|
||||||
|
{
|
||||||
|
InitMetadata("#{declName}", "#{idlNamespace}.#{declName}");
|
||||||
|
}|]
|
||||||
|
else [lt|
|
||||||
|
{
|
||||||
|
}|]
|
||||||
|
|
||||||
|
-- default constructor
|
||||||
|
defaultCtor = [lt|
|
||||||
|
#{declName}()#{initList}#{ctorBody}|]
|
||||||
|
where
|
||||||
|
initList = initializeList mempty
|
||||||
|
$ commaLineSep 3 fieldInit structFields
|
||||||
|
fieldInit Field {..} = optional (\x -> [lt|#{fieldName}(#{x})|])
|
||||||
|
$ initValue fieldType fieldDefault
|
||||||
|
|
||||||
|
allocatorCtor alloc = [lt|
|
||||||
|
explicit
|
||||||
|
#{declName}(const #{alloc}&#{allocParam})#{initList}#{ctorBody}
|
||||||
|
|]
|
||||||
|
where
|
||||||
|
allocParam = if needAlloc then [lt| allocator|] else mempty
|
||||||
|
where
|
||||||
|
needAlloc = isJust structBase || isJust (find needsAlloc structFields)
|
||||||
|
needsAlloc Field {..} = isJust $ allocInitValue (const $ const Nothing) fieldType fieldDefault
|
||||||
|
initList = initializeList
|
||||||
|
(optional baseInit structBase)
|
||||||
|
(commaLineSep 3 fieldInit structFields)
|
||||||
|
baseInit b = [lt|#{cppType b}(allocator)|]
|
||||||
|
fieldInit Field {..} = optional (\x -> [lt|#{fieldName}(#{x})|])
|
||||||
|
$ allocInitValue initValue fieldType fieldDefault
|
||||||
|
allocInitValue _ t (Just d)
|
||||||
|
| stringType t = Just [lt|#{cppDefaultValue t d}, allocator|]
|
||||||
|
allocInitValue _ t _
|
||||||
|
| listType t || metaType t || stringType t || structType t = Just "allocator"
|
||||||
|
| associativeType t = Just [lt|std::less<#{keyType t}>(), allocator|]
|
||||||
|
allocInitValue i (BT_Nullable t) _
|
||||||
|
| scalarType t = Nothing
|
||||||
|
| nullableType t = allocInitValue i t Nothing
|
||||||
|
| otherwise = Just "allocator"
|
||||||
|
allocInitValue i (BT_Maybe t) _
|
||||||
|
| scalarType t = Nothing
|
||||||
|
| otherwise = allocInitValue i t Nothing
|
||||||
|
allocInitValue i t@(BT_UserDefined a@Alias {..} args) d = if allocParameterized t
|
||||||
|
then allocInitValue i (resolveAlias a args) d
|
||||||
|
else Just mempty
|
||||||
|
allocInitValue i f d = i f d
|
||||||
|
keyType (BT_Set key) = cppType key
|
||||||
|
keyType (BT_Map key _) = cppType key
|
||||||
|
allocParameterized = L.isInfixOf (L.pack alloc) . toLazyText . cppType
|
||||||
|
|
||||||
|
-- copy constructor
|
||||||
|
copyCtor = if hasMetaFields then define else implicitlyDeclared
|
||||||
|
where
|
||||||
|
-- default OK when there are no meta fields
|
||||||
|
implicitlyDeclared = CPP.ifndef CPP.defaultedFunctions [lt|
|
||||||
|
// Compiler generated copy ctor OK
|
||||||
|
#{declName}(const #{declName}& other) = default;|]
|
||||||
|
|
||||||
|
-- define ctor to initialize meta fields
|
||||||
|
define = [lt|#{declName}(const #{declName}& other)#{initList}#{ctorBody}|]
|
||||||
|
where
|
||||||
|
initList = initializeList
|
||||||
|
(optional baseCopy structBase)
|
||||||
|
(commaLineSep 3 fieldCopy structFields)
|
||||||
|
baseCopy b = [lt|#{cppType b}(other)|]
|
||||||
|
fieldCopy Field {..} = [lt|#{fieldName}(other.#{fieldName}#{getAllocator fieldType})|]
|
||||||
|
getAllocator BT_MetaName = [lt|.get_allocator()|]
|
||||||
|
getAllocator BT_MetaFullName = [lt|.get_allocator()|]
|
||||||
|
getAllocator _ = mempty
|
||||||
|
|
||||||
|
-- move constructor
|
||||||
|
moveCtor = CPP.ifndef CPP.rvalueReferences [lt|
|
||||||
|
#{declName}(#{declName}&&#{param})#{initList}#{ctorBody}|]
|
||||||
|
where
|
||||||
|
initList = initializeList
|
||||||
|
(optional baseMove structBase)
|
||||||
|
(commaLineSep 3 fieldMove structFields)
|
||||||
|
baseMove b = [lt|#{cppType b}(std::move(other))|]
|
||||||
|
fieldMove Field {..} = [lt|#{fieldName}(std::move(other.#{fieldName}))|]
|
||||||
|
param = if initList == mempty then mempty else [lt| other|]
|
||||||
|
|
||||||
|
-- operator=
|
||||||
|
assignmentOp = if hasMetaFields then define else implicitlyDeclared
|
||||||
|
where
|
||||||
|
-- default OK when there are no meta fields
|
||||||
|
implicitlyDeclared = CPP.ifndef CPP.defaultedFunctions [lt|
|
||||||
|
// Compiler generated operator= OK
|
||||||
|
#{declName}& operator=(const #{declName}& other) = default;|]
|
||||||
|
|
||||||
|
-- define operator= using swap
|
||||||
|
define = [lt|#{declName}& operator=(const #{declName}& other)
|
||||||
|
{
|
||||||
|
#{declName}(other).swap(*this);
|
||||||
|
return *this;
|
||||||
|
}|]
|
||||||
|
|
||||||
|
initMetadata = [lt|void InitMetadata(const char*#{nameParam}, const char*#{qualifiedNameParam})
|
||||||
|
{#{newlineBeginSep 3 id [baseInit, nameInit, qualifiedInit]}
|
||||||
|
}|]
|
||||||
|
where
|
||||||
|
nameParam = if baseInit == mempty && nameInit == mempty then mempty else [lt| name|]
|
||||||
|
qualifiedNameParam = if baseInit == mempty && qualifiedInit == mempty then mempty else [lt| qualified_name|]
|
||||||
|
baseInit = optional (\b -> [lt|#{cppType b}::InitMetadata(name, qualified_name);|]) structBase
|
||||||
|
nameInit = newlineSep 3 init structFields
|
||||||
|
where
|
||||||
|
init Field {fieldType = BT_MetaName, ..} = [lt|this->#{fieldName} = name;|]
|
||||||
|
init _ = mempty
|
||||||
|
qualifiedInit = newlineSep 3 init structFields
|
||||||
|
where
|
||||||
|
init Field {fieldType = BT_MetaFullName, ..} = [lt|this->#{fieldName} = qualified_name;|]
|
||||||
|
init _ = mempty
|
||||||
|
|
||||||
|
-- enum definition and helpers
|
||||||
|
typeDeclaration e@Enum {..} = [lt|
|
||||||
|
namespace _bond_enumerators
|
||||||
|
{
|
||||||
|
namespace #{declName}
|
||||||
|
{
|
||||||
|
#{enumDefinition}
|
||||||
|
extern const std::map<enum #{declName}, std::string> _value_to_name_#{declName};
|
||||||
|
extern const std::map<std::string, enum #{declName}> _name_to_value_#{declName};
|
||||||
|
|
||||||
|
inline
|
||||||
|
const char* GetTypeName(enum #{declName})
|
||||||
|
{
|
||||||
|
return "#{declName}";
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
const char* GetTypeName(enum #{declName}, const bond::qualified_name_tag&)
|
||||||
|
{
|
||||||
|
return "#{idlNamespace}.#{declName}";
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
const std::map<enum #{declName}, std::string>& GetValueToNameMap(enum #{declName})
|
||||||
|
{
|
||||||
|
return _value_to_name_#{declName};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
const std::map<std::string, enum #{declName}>& GetNameToValueMap(enum #{declName})
|
||||||
|
{
|
||||||
|
return _name_to_value_#{declName};
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& ToString(enum #{declName} value);
|
||||||
|
|
||||||
|
void FromString(const std::string& name, enum #{declName}& value);
|
||||||
|
|
||||||
|
inline
|
||||||
|
bool ToEnum(enum #{declName}& value, const std::string& name)
|
||||||
|
{
|
||||||
|
std::map<std::string, enum #{declName}>::const_iterator it =
|
||||||
|
_name_to_value_#{declName}.find(name);
|
||||||
|
|
||||||
|
if (_name_to_value_#{declName}.end() == it)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
value = it->second;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} // namespace #{declName}
|
||||||
|
} // namespace _bond_enumerators
|
||||||
|
|
||||||
|
#{enumUsing}|]
|
||||||
|
where
|
||||||
|
enumDefinition = if enumHeader then mempty else [lt|#{CPP.enumDefinition e}
|
||||||
|
|]
|
||||||
|
enumUsing = if enumHeader then mempty else [lt|using namespace _bond_enumerators::#{declName};
|
||||||
|
|]
|
||||||
|
|
||||||
|
typeDeclaration _ = mempty
|
|
@ -0,0 +1,119 @@
|
||||||
|
-- Copyright (c) Microsoft. All rights reserved.
|
||||||
|
-- Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
{-# LANGUAGE QuasiQuotes, OverloadedStrings, RecordWildCards #-}
|
||||||
|
|
||||||
|
module Bond.Template.Cpp.Util
|
||||||
|
( openNamespace
|
||||||
|
, closeNamespace
|
||||||
|
, structName
|
||||||
|
, structParams
|
||||||
|
, template
|
||||||
|
, modifierTag
|
||||||
|
, defaultValue
|
||||||
|
, attributeInit
|
||||||
|
, schemaMetadata
|
||||||
|
, ifndef
|
||||||
|
, defaultedFunctions
|
||||||
|
, rvalueReferences
|
||||||
|
, enumDefinition
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Text.Shakespeare.Text
|
||||||
|
import Data.Monoid
|
||||||
|
import Bond.Schema
|
||||||
|
import Bond.Util
|
||||||
|
import Bond.Template.Util
|
||||||
|
import Bond.Template.TypeMapping
|
||||||
|
|
||||||
|
-- open namespaces
|
||||||
|
openNamespace cpp = newlineSep 0 open $ getNamespace cpp
|
||||||
|
where
|
||||||
|
open n = [lt|namespace #{n}
|
||||||
|
{|]
|
||||||
|
|
||||||
|
-- close namespaces in reverse order
|
||||||
|
closeNamespace cpp = newlineSep 0 close (reverse $ getNamespace cpp)
|
||||||
|
where
|
||||||
|
close n = [lt|} // namespace #{n}|]
|
||||||
|
|
||||||
|
structName s@Struct {..} = declName <> structParams s
|
||||||
|
|
||||||
|
structParams Struct {..} = angles $ sepBy ", " paramName declParams
|
||||||
|
|
||||||
|
template d = if null $ declParams d then mempty else [lt|template <typename #{params}>
|
||||||
|
|]
|
||||||
|
where
|
||||||
|
params = sepBy ", typename " paramName $ declParams d
|
||||||
|
|
||||||
|
-- attribute initializer
|
||||||
|
attributeInit [] = "bond::reflection::Attributes()"
|
||||||
|
attributeInit xs = [lt|boost::assign::map_list_of<std::string, std::string>#{newlineBeginSep 5 attrNameValue xs}|]
|
||||||
|
where
|
||||||
|
attrNameValue Attribute {..} = [lt|("#{getIdlQualifiedName attrName}", "#{attrValue}")|]
|
||||||
|
|
||||||
|
|
||||||
|
-- modifier tag type for a field
|
||||||
|
modifierTag Field {..} = [lt|bond::reflection::#{modifier fieldType fieldModifier}_field_modifier|]
|
||||||
|
where
|
||||||
|
modifier BT_MetaName _ = [lt|required_optional|]
|
||||||
|
modifier BT_MetaFullName _ = [lt|required_optional|]
|
||||||
|
modifier _ RequiredOptional = [lt|required_optional|]
|
||||||
|
modifier _ Required = [lt|required|]
|
||||||
|
modifier _ _ = [lt|optional|]
|
||||||
|
|
||||||
|
defaultValue _ BT_WString (DefaultString x) = [lt|L"#{x}"|]
|
||||||
|
defaultValue _ BT_String (DefaultString x) = [lt|"#{x}"|]
|
||||||
|
defaultValue _ BT_Float (DefaultFloat x) = [lt|#{x}f|]
|
||||||
|
defaultValue _ BT_Int64 (DefaultInteger (-9223372036854775808)) = [lt|-9223372036854775807LL-1|]
|
||||||
|
defaultValue _ BT_Int64 (DefaultInteger x) = [lt|#{x}LL|]
|
||||||
|
defaultValue _ BT_UInt64 (DefaultInteger x) = [lt|#{x}ULL|]
|
||||||
|
defaultValue _ BT_Int32 (DefaultInteger (-2147483648)) = [lt|-2147483647-1|]
|
||||||
|
defaultValue m t (DefaultEnum x) = enumValue m t x
|
||||||
|
defaultValue _ _ (DefaultBool True) = "true"
|
||||||
|
defaultValue _ _ (DefaultBool False) = "false"
|
||||||
|
defaultValue _ _ (DefaultInteger x) = [lt|#{x}|]
|
||||||
|
defaultValue _ _ (DefaultFloat x) = [lt|#{x}|]
|
||||||
|
defaultValue _ _ (DefaultNothing) = mempty
|
||||||
|
|
||||||
|
enumValue cpp (BT_UserDefined e@Enum {..} _) x =
|
||||||
|
[lt|#{getGlobalQualifiedName cppTypeMapping $ getDeclNamespace cpp e}::_bond_enumerators::#{declName}::#{x}|]
|
||||||
|
|
||||||
|
-- schema metadata static member definitions
|
||||||
|
schemaMetadata cpp s@Struct {..} = [lt|
|
||||||
|
#{template s}const bond::Metadata #{structName s}::Schema::metadata
|
||||||
|
= #{structName s}::Schema::GetMetadata();#{newlineBeginSep 1 staticDef structFields}|]
|
||||||
|
where
|
||||||
|
-- static member definition for field metadata
|
||||||
|
staticDef f@Field {..}
|
||||||
|
| fieldModifier == Optional && null fieldAttributes = [lt|
|
||||||
|
#{template s}const bond::Metadata #{structName s}::Schema::s_#{fieldName}_metadata
|
||||||
|
= bond::reflection::MetadataInit(#{defaultInit f}"#{fieldName}");|]
|
||||||
|
| otherwise = [lt|
|
||||||
|
#{template s}const bond::Metadata #{structName s}::Schema::s_#{fieldName}_metadata
|
||||||
|
= bond::reflection::MetadataInit(#{defaultInit f}"#{fieldName}", #{modifierTag f}::value,
|
||||||
|
#{attributeInit fieldAttributes});|]
|
||||||
|
where
|
||||||
|
defaultInit Field {fieldDefault = (Just def), ..} = [lt|#{explicitDefault def}, |]
|
||||||
|
defaultInit _ = mempty
|
||||||
|
explicitDefault (DefaultNothing) = "bond::nothing"
|
||||||
|
explicitDefault d@(DefaultInteger _) = staticCast d
|
||||||
|
explicitDefault d@(DefaultFloat _) = staticCast d
|
||||||
|
explicitDefault d = defaultValue cpp fieldType d
|
||||||
|
staticCast d = [lt|static_cast<#{getTypeName cpp fieldType}>(#{defaultValue cpp fieldType d})|]
|
||||||
|
|
||||||
|
defaultedFunctions = [lt|BOND_NO_CXX11_DEFAULTED_FUNCTIONS|]
|
||||||
|
rvalueReferences = [lt|BOND_NO_CXX11_RVALUE_REFERENCES|]
|
||||||
|
|
||||||
|
ifndef m = between [lt|
|
||||||
|
#ifndef #{m}|] [lt|
|
||||||
|
#endif|]
|
||||||
|
|
||||||
|
enumDefinition Enum {..} = [lt|enum #{declName}
|
||||||
|
{
|
||||||
|
#{commaLineSep 3 constant enumConstants}
|
||||||
|
};|]
|
||||||
|
where
|
||||||
|
constant Constant {..} = [lt|#{constantName}#{optional value constantValue}|]
|
||||||
|
value x = [lt| = #{x}|]
|
||||||
|
|
|
@ -0,0 +1,109 @@
|
||||||
|
-- Copyright (c) Microsoft. All rights reserved.
|
||||||
|
-- Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
{-# LANGUAGE QuasiQuotes, OverloadedStrings, RecordWildCards #-}
|
||||||
|
|
||||||
|
module Bond.Template.Cs.Types_cs (types_cs) where
|
||||||
|
|
||||||
|
import System.FilePath
|
||||||
|
import Data.Monoid
|
||||||
|
import Data.Foldable (foldMap)
|
||||||
|
import Text.Shakespeare.Text
|
||||||
|
import Bond.Schema
|
||||||
|
import Bond.Util
|
||||||
|
import Bond.Template.TypeMapping
|
||||||
|
import Bond.Template.Util
|
||||||
|
import qualified Bond.Template.Cs.Util as CS
|
||||||
|
|
||||||
|
-- generate the *_types.cs file from parsed .bond file
|
||||||
|
types_cs readOnly useFields cs file imports declarations = ("_types.cs", [lt|
|
||||||
|
#{CS.disableReSharperWarnings}
|
||||||
|
namespace #{csNamespace}
|
||||||
|
{
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
#{doubleLineSep 1 typeDefinition declarations}
|
||||||
|
} // #{csNamespace}
|
||||||
|
|])
|
||||||
|
where
|
||||||
|
idlNamespace = getIdlQualifiedName $ getIdlNamespace cs
|
||||||
|
|
||||||
|
-- C# type
|
||||||
|
csType = getTypeName cs
|
||||||
|
csNamespace = getQualifiedName csTypeMapping $ getNamespace cs
|
||||||
|
|
||||||
|
-- C# class definition for schema struct
|
||||||
|
typeDefinition s@Struct {..} = [lt|#{CS.typeAttributes cs s}
|
||||||
|
public partial class #{declName}#{params}#{optional baseClass structBase}#{constraints}
|
||||||
|
{
|
||||||
|
#{doubleLineSep 2 property structFields}
|
||||||
|
#{constructors}
|
||||||
|
}|]
|
||||||
|
where
|
||||||
|
-- type parameters
|
||||||
|
params = angles $ sepBy ", " paramName declParams
|
||||||
|
|
||||||
|
-- constraints
|
||||||
|
constraints = CS.paramConstraints declParams
|
||||||
|
|
||||||
|
-- base
|
||||||
|
callBaseCtor = getAny $ optional (foldMapFields metaField) structBase
|
||||||
|
|
||||||
|
baseClass x = [lt|
|
||||||
|
: #{csType x}|]
|
||||||
|
|
||||||
|
baseCtor = if not callBaseCtor then mempty else [lt|
|
||||||
|
: base(fullName, name)|]
|
||||||
|
|
||||||
|
-- default value
|
||||||
|
csDefault = CS.defaultValue cs
|
||||||
|
|
||||||
|
-- constructors
|
||||||
|
constructors = if emptyCtor then mempty else [lt|
|
||||||
|
public #{declName}()
|
||||||
|
: this("#{idlNamespace}.#{declName}", "#{declName}")
|
||||||
|
{}
|
||||||
|
|
||||||
|
protected #{declName}(string fullName, string name)#{baseCtor}
|
||||||
|
{
|
||||||
|
#{newlineSep 3 initializer structFields}
|
||||||
|
}|]
|
||||||
|
where
|
||||||
|
emptyCtor = not callBaseCtor && (useFields && noMetaFields || null structFields)
|
||||||
|
noMetaFields = not $ getAny $ foldMap metaField structFields
|
||||||
|
|
||||||
|
-- property or field
|
||||||
|
property f@Field {..} = [lt|#{CS.propertyAttributes cs f}
|
||||||
|
#{new}public #{csType fieldType} #{fieldName}#{autoPropertyOrField}|]
|
||||||
|
where
|
||||||
|
autoPropertyOrField =
|
||||||
|
if useFields then
|
||||||
|
[lt|#{optional fieldInitializer $ csDefault f};|]
|
||||||
|
else
|
||||||
|
[lt| { get; #{set}; }|]
|
||||||
|
fieldInitializer x = [lt| = #{x}|]
|
||||||
|
set = if readOnly then "private set" else "set" :: String
|
||||||
|
new = if isBaseField fieldName structBase then "new " else "" :: String
|
||||||
|
|
||||||
|
-- initializers in constructor
|
||||||
|
initializer f@Field {..} = optional init $ def f
|
||||||
|
where
|
||||||
|
init x = [lt|#{this fieldName} = #{x};|]
|
||||||
|
this = if fieldName == "name" || fieldName == "fullName" then ("this." ++) else id
|
||||||
|
def Field {fieldType = BT_MetaName, ..} = Just "name"
|
||||||
|
def Field {fieldType = BT_MetaFullName, ..} = Just "fullName"
|
||||||
|
def x = if useFields then Nothing else csDefault x
|
||||||
|
|
||||||
|
-- C# enum definition for schema enum
|
||||||
|
typeDefinition e@Enum {..} = [lt|
|
||||||
|
#{CS.typeAttributes cs e}
|
||||||
|
public enum #{declName}
|
||||||
|
{
|
||||||
|
#{newlineSep 2 constant enumConstants}
|
||||||
|
}|]
|
||||||
|
where
|
||||||
|
-- constant
|
||||||
|
constant Constant {..} = let value x = [lt| = #{x}|] in
|
||||||
|
[lt|#{constantName}#{optional value constantValue},|]
|
||||||
|
|
||||||
|
typeDefinition _ = mempty
|
|
@ -0,0 +1,116 @@
|
||||||
|
-- Copyright (c) Microsoft. All rights reserved.
|
||||||
|
-- Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
{-# LANGUAGE QuasiQuotes, OverloadedStrings, RecordWildCards #-}
|
||||||
|
|
||||||
|
module Bond.Template.Cs.Util
|
||||||
|
( typeAttributes
|
||||||
|
, propertyAttributes
|
||||||
|
, schemaAttributes
|
||||||
|
, paramConstraints
|
||||||
|
, defaultValue
|
||||||
|
, disableReSharperWarnings
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Data.Monoid
|
||||||
|
import Text.Shakespeare.Text
|
||||||
|
import Bond.Version
|
||||||
|
import Bond.Schema
|
||||||
|
import Bond.Template.TypeMapping
|
||||||
|
import Bond.Template.Util
|
||||||
|
|
||||||
|
disableReSharperWarnings = [lt|
|
||||||
|
#region ReSharper warnings
|
||||||
|
// ReSharper disable PartialTypeWithSinglePart
|
||||||
|
// ReSharper disable RedundantNameQualifier
|
||||||
|
// ReSharper disable InconsistentNaming
|
||||||
|
// ReSharper disable CheckNamespace
|
||||||
|
// ReSharper disable UnusedParameter.Local
|
||||||
|
// ReSharper disable RedundantUsingDirective
|
||||||
|
#endregion
|
||||||
|
|]
|
||||||
|
|
||||||
|
-- C# field/property attributes
|
||||||
|
propertyAttributes cs Field {..} =
|
||||||
|
schemaAttributes 2 fieldAttributes
|
||||||
|
<> [lt|[global::Bond.Id(#{fieldOrdinal})#{typeAttribute}#{modifierAttribute fieldType fieldModifier}]|]
|
||||||
|
where
|
||||||
|
csAnnotated = setTypeMapping cs csAnnotatedTypeMapping
|
||||||
|
annotatedType = getTypeName csAnnotated fieldType
|
||||||
|
propertyType = getTypeName cs fieldType
|
||||||
|
typeAttribute = if annotatedType /= propertyType
|
||||||
|
then [lt|, global::Bond.Type(typeof(#{annotatedType}))|]
|
||||||
|
else mempty
|
||||||
|
modifierAttribute BT_MetaName _ = [lt|, global::Bond.RequiredOptional|]
|
||||||
|
modifierAttribute BT_MetaFullName _ = [lt|, global::Bond.RequiredOptional|]
|
||||||
|
modifierAttribute _ Required = [lt|, global::Bond.Required|]
|
||||||
|
modifierAttribute _ RequiredOptional = [lt|, global::Bond.RequiredOptional|]
|
||||||
|
modifierAttribute _ _ = mempty
|
||||||
|
|
||||||
|
-- C# class/struct/interface attributes
|
||||||
|
typeAttributes cs s@Struct {..} =
|
||||||
|
optionalTypeAttributes cs s
|
||||||
|
<> [lt|[global::Bond.Schema]
|
||||||
|
|]
|
||||||
|
<> generatedCodeAttr
|
||||||
|
|
||||||
|
-- C# enum attributes
|
||||||
|
typeAttributes cs e@Enum {..} =
|
||||||
|
optionalTypeAttributes cs e
|
||||||
|
<> generatedCodeAttr
|
||||||
|
|
||||||
|
generatedCodeAttr = [lt|[System.CodeDom.Compiler.GeneratedCode("gbc", "#{majorVersion}.#{minorVersion}")]|]
|
||||||
|
|
||||||
|
optionalTypeAttributes cs decl =
|
||||||
|
schemaAttributes 1 (declAttributes decl)
|
||||||
|
<> namespaceAttribute
|
||||||
|
where
|
||||||
|
namespaceAttribute = if getIdlNamespace cs == getNamespace cs
|
||||||
|
then mempty
|
||||||
|
else [lt|[global::Bond.Namespace("#{getIdlQualifiedName $ getIdlNamespace cs}")]
|
||||||
|
|]
|
||||||
|
|
||||||
|
-- Attributes defined by the user in the schema
|
||||||
|
schemaAttributes indent = newlineSepEnd indent schemaAttribute
|
||||||
|
where
|
||||||
|
schemaAttribute Attribute {..} =
|
||||||
|
[lt|[global::Bond.Attribute("#{getIdlQualifiedName attrName}", "#{attrValue}")]|]
|
||||||
|
|
||||||
|
-- generic type parameter constraints
|
||||||
|
paramConstraints = newlineBeginSep 2 constraint
|
||||||
|
where
|
||||||
|
constraint (TypeParam _ Nothing) = mempty
|
||||||
|
constraint (TypeParam name (Just Value)) = [lt|where #{name} : struct|]
|
||||||
|
|
||||||
|
-- Initial value for C# field/property or Nothing if C# implicit default is OK
|
||||||
|
defaultValue cs Field {fieldDefault = Nothing, ..} = implicitDefault fieldType
|
||||||
|
where
|
||||||
|
newInstance t = Just [lt|new #{getInstanceTypeName cs t}()|]
|
||||||
|
implicitDefault BT_String = Just "string.Empty"
|
||||||
|
implicitDefault BT_WString = Just "string.Empty"
|
||||||
|
implicitDefault (BT_Bonded t) = Just [lt|global::Bond.Bonded<#{getTypeName cs t}>.Empty|]
|
||||||
|
implicitDefault t@(BT_TypeParam _) = Just [lt|global::Bond.GenericFactory.Create<#{getInstanceTypeName cs t}>()|]
|
||||||
|
implicitDefault t@(BT_List _) = newInstance t
|
||||||
|
implicitDefault t@(BT_Vector _) = newInstance t
|
||||||
|
implicitDefault t@(BT_Set _) = newInstance t
|
||||||
|
implicitDefault t@(BT_Map _ _) = newInstance t
|
||||||
|
implicitDefault t@BT_Blob = newInstance t
|
||||||
|
implicitDefault t@(BT_UserDefined a@Alias {..} args) =
|
||||||
|
case findAliasMapping cs a of
|
||||||
|
Nothing -> implicitDefault $ resolveAlias a args
|
||||||
|
Just _ -> newInstance t
|
||||||
|
implicitDefault t@(BT_UserDefined _ _) = newInstance t
|
||||||
|
implicitDefault _ = Nothing
|
||||||
|
|
||||||
|
defaultValue cs Field {fieldDefault = (Just def), ..} = explicitDefault def
|
||||||
|
where
|
||||||
|
explicitDefault (DefaultInteger x) = Just [lt|#{x}|]
|
||||||
|
explicitDefault (DefaultFloat x) = Just $ floatLiteral fieldType x
|
||||||
|
where
|
||||||
|
floatLiteral BT_Float x = [lt|#{x}F|]
|
||||||
|
floatLiteral BT_Double x = [lt|#{x}|]
|
||||||
|
explicitDefault (DefaultBool True) = Just "true"
|
||||||
|
explicitDefault (DefaultBool False) = Just "false"
|
||||||
|
explicitDefault (DefaultString x) = Just [lt|"#{x}"|]
|
||||||
|
explicitDefault (DefaultEnum x) = Just [lt|#{getTypeName cs fieldType}.#{x}|]
|
||||||
|
explicitDefault _ = Nothing
|
|
@ -0,0 +1,64 @@
|
||||||
|
-- Copyright (c) Microsoft. All rights reserved.
|
||||||
|
-- Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
|
module Bond.Template.CustomMapping
|
||||||
|
( parseAliasMapping
|
||||||
|
, parseNamespaceMapping
|
||||||
|
, AliasMapping(..)
|
||||||
|
, Fragment(..)
|
||||||
|
, NamespaceMapping(..)
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Data.Char
|
||||||
|
import Control.Applicative
|
||||||
|
import Text.Parsec hiding (many, optional, (<|>))
|
||||||
|
import Bond.Schema
|
||||||
|
|
||||||
|
data Fragment =
|
||||||
|
Fragment String |
|
||||||
|
Placeholder Int
|
||||||
|
|
||||||
|
data AliasMapping = AliasMapping
|
||||||
|
{ aliasName :: QualifiedName
|
||||||
|
, aliasTemplate :: [Fragment]
|
||||||
|
}
|
||||||
|
|
||||||
|
data NamespaceMapping = NamespaceMapping
|
||||||
|
{ fromNamespace :: QualifiedName
|
||||||
|
, toNamespace :: QualifiedName
|
||||||
|
}
|
||||||
|
|
||||||
|
whitespace = many (char ' ') <?> "whitespace"
|
||||||
|
identifier = many1 (alphaNum <|> char '_') <?> "identifier"
|
||||||
|
qualifiedName = sepBy1 identifier (char '.') <?> "qualified name"
|
||||||
|
symbol s = whitespace *> string s <* whitespace
|
||||||
|
equal = symbol "="
|
||||||
|
integer = decimal <$> many1 digit <?> "decimal number"
|
||||||
|
where
|
||||||
|
decimal = foldl (\x d -> 10 * x + toInteger (digitToInt d)) 0
|
||||||
|
|
||||||
|
-- parse alias mapping specification from the command line --using flags
|
||||||
|
-- e.g.: --using="OrderedSet=SortedSet<{0}>"
|
||||||
|
parseAliasMapping :: [String] -> IO [AliasMapping]
|
||||||
|
parseAliasMapping = mapM parseAliasMapping
|
||||||
|
where
|
||||||
|
parseAliasMapping s = case parse aliasMapping s s of
|
||||||
|
Left err -> fail $ show err
|
||||||
|
Right m -> return m
|
||||||
|
aliasMapping = AliasMapping <$> qualifiedName <* equal <*> many1 (placeholder <|> fragment) <* eof
|
||||||
|
where
|
||||||
|
placeholder = Placeholder <$> fromIntegral <$> between (char '{') (char '}') integer
|
||||||
|
fragment = Fragment <$> many1 (noneOf "{")
|
||||||
|
|
||||||
|
|
||||||
|
-- parse namespace mapping specification from the command line --namespace flags
|
||||||
|
-- e.g.: --namespace="bond="
|
||||||
|
parseNamespaceMapping :: [String] -> IO [NamespaceMapping]
|
||||||
|
parseNamespaceMapping = mapM parseNamespaceMapping
|
||||||
|
where
|
||||||
|
parseNamespaceMapping s = case parse namespaceMapping s s of
|
||||||
|
Left err -> fail $ show err
|
||||||
|
Right m -> return m
|
||||||
|
namespaceMapping = NamespaceMapping <$> qualifiedName <* equal <*> qualifiedName
|
|
@ -0,0 +1,335 @@
|
||||||
|
-- Copyright (c) Microsoft. All rights reserved.
|
||||||
|
-- Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
{-# LANGUAGE OverloadedStrings, RecordWildCards #-}
|
||||||
|
|
||||||
|
module Bond.Template.TypeMapping
|
||||||
|
( newMappingContext
|
||||||
|
, findAliasMapping
|
||||||
|
, setTypeMapping
|
||||||
|
, setNamespaces
|
||||||
|
, cppTypeMapping
|
||||||
|
, cppCustomAllocTypeMapping
|
||||||
|
, csTypeMapping
|
||||||
|
, csInterfaceTypeMapping
|
||||||
|
, csAnnotatedTypeMapping
|
||||||
|
, getNamespace
|
||||||
|
, getIdlNamespace
|
||||||
|
, getDeclNamespace
|
||||||
|
, getQualifiedName
|
||||||
|
, getIdlQualifiedName
|
||||||
|
, getGlobalQualifiedName
|
||||||
|
, getDeclQualifiedTypeName
|
||||||
|
, getTypeName
|
||||||
|
, getInstanceTypeName
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Data.List
|
||||||
|
import Data.Monoid
|
||||||
|
import Control.Applicative
|
||||||
|
import Control.Monad.Reader
|
||||||
|
import qualified Data.Text.Lazy as L
|
||||||
|
import Data.Text.Lazy.Builder
|
||||||
|
import Text.Shakespeare.Text
|
||||||
|
import Bond.Schema
|
||||||
|
import Bond.Util
|
||||||
|
import Bond.Template.Util
|
||||||
|
import Bond.Template.CustomMapping
|
||||||
|
|
||||||
|
data Context = Context
|
||||||
|
{ typeMapping :: TypeMapping
|
||||||
|
, aliasMapping :: [AliasMapping]
|
||||||
|
, namespaceMapping :: [NamespaceMapping]
|
||||||
|
, namespaces :: [Namespace]
|
||||||
|
}
|
||||||
|
|
||||||
|
data TypeMapping = TypeMapping
|
||||||
|
{ language :: Language
|
||||||
|
, global :: Builder
|
||||||
|
, separator :: Builder
|
||||||
|
, mapType :: Type -> TypeNameBuilder
|
||||||
|
, fixSyntax :: Builder -> Builder
|
||||||
|
, instanceMapping :: TypeMapping
|
||||||
|
, elementMapping :: TypeMapping
|
||||||
|
}
|
||||||
|
|
||||||
|
type TypeNameBuilder = Reader Context Builder
|
||||||
|
|
||||||
|
newMappingContext = Context
|
||||||
|
|
||||||
|
setTypeMapping :: Context -> TypeMapping -> Context
|
||||||
|
setTypeMapping c m = c { typeMapping = m }
|
||||||
|
|
||||||
|
setNamespaces :: Context -> [Namespace] -> Context
|
||||||
|
setNamespaces c n = c { namespaces = n }
|
||||||
|
|
||||||
|
getNamespace :: Context -> QualifiedName
|
||||||
|
getNamespace c@Context {..} = resolveNamespace c namespaces
|
||||||
|
|
||||||
|
getIdlNamespace :: Context -> QualifiedName
|
||||||
|
getIdlNamespace c@Context {..} = findNamespace c namespaces
|
||||||
|
|
||||||
|
getDeclNamespace :: Context -> Declaration -> QualifiedName
|
||||||
|
getDeclNamespace c = resolveNamespace c . declNamespaces
|
||||||
|
|
||||||
|
getQualifiedName :: TypeMapping -> QualifiedName -> Builder
|
||||||
|
getQualifiedName TypeMapping {..} = sep separator
|
||||||
|
|
||||||
|
getIdlQualifiedName :: QualifiedName -> Builder
|
||||||
|
getIdlQualifiedName = sep "."
|
||||||
|
|
||||||
|
getGlobalQualifiedName :: TypeMapping -> QualifiedName -> Builder
|
||||||
|
getGlobalQualifiedName m@TypeMapping {..} = (global <>) . getQualifiedName m
|
||||||
|
|
||||||
|
getDeclQualifiedTypeName :: Context -> Declaration -> Builder
|
||||||
|
getDeclQualifiedTypeName c = getGlobalQualifiedName (typeMapping c) . declQualifiedName c
|
||||||
|
|
||||||
|
getTypeName :: Context -> Type -> Builder
|
||||||
|
getTypeName c t = fix $ runReader (typeName t) c
|
||||||
|
where
|
||||||
|
fix = fixSyntax $ typeMapping c
|
||||||
|
|
||||||
|
getInstanceTypeName :: Context -> Type -> Builder
|
||||||
|
getInstanceTypeName c t = runReader (instanceTypeName t) c
|
||||||
|
|
||||||
|
-- type mappings for different languages/variants
|
||||||
|
cppTypeMapping = TypeMapping
|
||||||
|
Cpp
|
||||||
|
"::"
|
||||||
|
"::"
|
||||||
|
cppType
|
||||||
|
cppSyntaxFix
|
||||||
|
cppTypeMapping
|
||||||
|
cppTypeMapping
|
||||||
|
|
||||||
|
cppCustomAllocTypeMapping alloc = TypeMapping
|
||||||
|
Cpp
|
||||||
|
"::"
|
||||||
|
"::"
|
||||||
|
(cppTypeCustomAlloc $ toText alloc)
|
||||||
|
cppSyntaxFix
|
||||||
|
(cppCustomAllocTypeMapping alloc)
|
||||||
|
(cppCustomAllocTypeMapping alloc)
|
||||||
|
|
||||||
|
csTypeMapping = TypeMapping
|
||||||
|
Cs
|
||||||
|
"global::"
|
||||||
|
"."
|
||||||
|
csType
|
||||||
|
id
|
||||||
|
csTypeMapping
|
||||||
|
csTypeMapping
|
||||||
|
|
||||||
|
csInterfaceTypeMapping = TypeMapping
|
||||||
|
Cs
|
||||||
|
"global::"
|
||||||
|
"."
|
||||||
|
csIfaceType
|
||||||
|
id
|
||||||
|
csInterfaceInstanceTypeMapping
|
||||||
|
csInterfaceTypeMapping
|
||||||
|
|
||||||
|
csInterfaceInstanceTypeMapping = csInterfaceTypeMapping {mapType = csType}
|
||||||
|
|
||||||
|
csAnnotatedTypeMapping = TypeMapping
|
||||||
|
Cs
|
||||||
|
"global::"
|
||||||
|
"."
|
||||||
|
csTypeAnnotation
|
||||||
|
id
|
||||||
|
csAnnotatedTypeMapping
|
||||||
|
csAnnotatedTypeMapping
|
||||||
|
|
||||||
|
infixr 6 <<>>
|
||||||
|
|
||||||
|
(<<>>) :: (Monoid r, Monad m) => m r -> m r -> m r
|
||||||
|
(<<>>) = liftM2 (<>)
|
||||||
|
|
||||||
|
infixr 6 <>>
|
||||||
|
|
||||||
|
(<>>) :: (Monoid r, Monad m) => r -> m r -> m r
|
||||||
|
(<>>) x = liftM (x <>)
|
||||||
|
|
||||||
|
infixr 6 <<>
|
||||||
|
|
||||||
|
(<<>) :: (Monoid r, Monad m) => m r -> r -> m r
|
||||||
|
(<<>) x y = liftM (<> y) x
|
||||||
|
|
||||||
|
pureText :: ToText a => a -> TypeNameBuilder
|
||||||
|
pureText = pure . toText
|
||||||
|
|
||||||
|
commaSepTypeNames [] = return mempty
|
||||||
|
commaSepTypeNames [x] = typeName x
|
||||||
|
commaSepTypeNames (x:xs) = typeName x <<>> ", " <>> commaSepTypeNames xs
|
||||||
|
|
||||||
|
typeName :: Type -> TypeNameBuilder
|
||||||
|
typeName t = do
|
||||||
|
m <- asks $ mapType . typeMapping
|
||||||
|
m t
|
||||||
|
|
||||||
|
localWith :: (TypeMapping -> TypeMapping) -> TypeNameBuilder -> TypeNameBuilder
|
||||||
|
localWith f = local $ \c -> c { typeMapping = f $ typeMapping c }
|
||||||
|
|
||||||
|
elementTypeName :: Type -> TypeNameBuilder
|
||||||
|
elementTypeName = localWith elementMapping . typeName
|
||||||
|
|
||||||
|
instanceTypeName :: Type -> TypeNameBuilder
|
||||||
|
instanceTypeName = localWith instanceMapping . typeName
|
||||||
|
|
||||||
|
resolveNamespace :: Context -> [Namespace] -> QualifiedName
|
||||||
|
resolveNamespace c@Context {..} ns = maybe namespace toNamespace $ find ((namespace ==) . fromNamespace) namespaceMapping
|
||||||
|
where
|
||||||
|
namespace = findNamespace c ns
|
||||||
|
|
||||||
|
-- last namespace that is language-neutral or matches the language of the context's type mapping
|
||||||
|
findNamespace Context {..} ns =
|
||||||
|
nsName . last . filter (maybe True (language typeMapping ==) . nsLanguage) $ ns
|
||||||
|
|
||||||
|
declQualifiedName :: Context -> Declaration -> QualifiedName
|
||||||
|
declQualifiedName c decl = getDeclNamespace c decl ++ [declName decl]
|
||||||
|
|
||||||
|
declQualifiedTypeName :: Declaration -> TypeNameBuilder
|
||||||
|
declQualifiedTypeName decl = do
|
||||||
|
ctx <- ask
|
||||||
|
return $ getDeclQualifiedTypeName ctx decl
|
||||||
|
|
||||||
|
declTypeName :: Declaration -> TypeNameBuilder
|
||||||
|
declTypeName decl = do
|
||||||
|
ctx <- ask
|
||||||
|
if namespaces ctx == declNamespaces decl
|
||||||
|
then pureText $ declName decl
|
||||||
|
else declQualifiedTypeName decl
|
||||||
|
|
||||||
|
findAliasMapping :: Context -> Declaration -> Maybe AliasMapping
|
||||||
|
findAliasMapping ctx a = find isSameAlias $ aliasMapping ctx
|
||||||
|
where
|
||||||
|
aliasDeclName = declQualifiedName ctx a
|
||||||
|
isSameNs = namespaces ctx == declNamespaces a
|
||||||
|
isSameAlias m = aliasDeclName == aliasName m || isSameNs && [declName a] == aliasName m
|
||||||
|
|
||||||
|
aliasTypeName :: Declaration -> [Type] -> TypeNameBuilder
|
||||||
|
aliasTypeName a args = do
|
||||||
|
ctx <- ask
|
||||||
|
case findAliasMapping ctx a of
|
||||||
|
Just AliasMapping {..} -> foldr ((<<>>) . fragment) (pure mempty) aliasTemplate
|
||||||
|
Nothing -> typeName $ resolveAlias a args
|
||||||
|
where
|
||||||
|
fragment (Fragment s) = pureText s
|
||||||
|
fragment (Placeholder i) = typeName $ args !! i
|
||||||
|
|
||||||
|
-- C++ type mapping
|
||||||
|
cppType :: Type -> TypeNameBuilder
|
||||||
|
cppType BT_Int8 = pure "int8_t"
|
||||||
|
cppType BT_Int16 = pure "int16_t"
|
||||||
|
cppType BT_Int32 = pure "int32_t"
|
||||||
|
cppType BT_Int64 = pure "int64_t"
|
||||||
|
cppType BT_UInt8 = pure "uint8_t"
|
||||||
|
cppType BT_UInt16 = pure "uint16_t"
|
||||||
|
cppType BT_UInt32 = pure "uint32_t"
|
||||||
|
cppType BT_UInt64 = pure "uint64_t"
|
||||||
|
cppType BT_Float = pure "float"
|
||||||
|
cppType BT_Double = pure "double"
|
||||||
|
cppType BT_Bool = pure "bool"
|
||||||
|
cppType BT_String = pure "std::string"
|
||||||
|
cppType BT_WString = pure "std::wstring"
|
||||||
|
cppType BT_MetaName = pure "std::string"
|
||||||
|
cppType BT_MetaFullName = pure "std::string"
|
||||||
|
cppType BT_Blob = pure "bond::blob"
|
||||||
|
cppType (BT_IntTypeArg x) = pureText x
|
||||||
|
cppType (BT_Maybe type_) = "bond::maybe<" <>> elementTypeName type_ <<> ">"
|
||||||
|
cppType (BT_List element) = "std::list<" <>> elementTypeName element <<> ">"
|
||||||
|
cppType (BT_Nullable element) = "bond::nullable<" <>> elementTypeName element <<> ">"
|
||||||
|
cppType (BT_Vector element) = "std::vector<" <>> elementTypeName element <<> ">"
|
||||||
|
cppType (BT_Set element) = "std::set<" <>> elementTypeName element <<> ">"
|
||||||
|
cppType (BT_Map key value) = "std::map<" <>> elementTypeName key <<>> ", " <>> elementTypeName value <<> ">"
|
||||||
|
cppType (BT_Bonded type_) = "bond::bonded<" <>> elementTypeName type_ <<> ">"
|
||||||
|
cppType (BT_TypeParam param) = pureText $ paramName param
|
||||||
|
cppType (BT_UserDefined a@Alias {..} args) = aliasTypeName a args
|
||||||
|
cppType (BT_UserDefined decl args) = declQualifiedTypeName decl <<>> (angles <$> commaSepTypeNames args)
|
||||||
|
|
||||||
|
-- C++ type mapping with custom allocator
|
||||||
|
cppTypeCustomAlloc alloc BT_String = pure $ "std::basic_string<char, std::char_traits<char>, typename " <> alloc <> "::rebind<char>::other>"
|
||||||
|
cppTypeCustomAlloc alloc BT_WString = pure $ "std::basic_string<wchar_t, std::char_traits<wchar_t>, typename " <> alloc <> "::rebind<wchar_t>::other>"
|
||||||
|
cppTypeCustomAlloc alloc BT_MetaName = cppTypeCustomAlloc alloc BT_String
|
||||||
|
cppTypeCustomAlloc alloc BT_MetaFullName = cppTypeCustomAlloc alloc BT_String
|
||||||
|
cppTypeCustomAlloc alloc (BT_List element) = "std::list<" <>> elementTypeName element <<>> ", " <>> allocator alloc element <<> ">"
|
||||||
|
cppTypeCustomAlloc alloc (BT_Nullable element) | structType element = "bond::nullable<" <>> elementTypeName element <<> ", " <> alloc <> ">"
|
||||||
|
cppTypeCustomAlloc alloc (BT_Nullable element) = "bond::nullable<" <>> elementTypeName element <<> ">"
|
||||||
|
cppTypeCustomAlloc alloc (BT_Vector element) = "std::vector<" <>> elementTypeName element <<>> ", " <>> allocator alloc element <<> ">"
|
||||||
|
cppTypeCustomAlloc alloc (BT_Set element) = "std::set<" <>> elementTypeName element <<>> comparer element <<>> allocator alloc element <<> ">"
|
||||||
|
cppTypeCustomAlloc alloc (BT_Map key value) = "std::map<" <>> elementTypeName key <<>> ", " <>> elementTypeName value <<>> comparer key <<>> pairAllocator alloc key value <<> ">"
|
||||||
|
cppTypeCustomAlloc _ t = cppType t
|
||||||
|
|
||||||
|
comparer t = ", std::less<" <>> elementTypeName t <<> ">, "
|
||||||
|
|
||||||
|
allocator alloc element =
|
||||||
|
"typename " <>> alloc <>> "::rebind<" <>> elementTypeName element <<> ">::other"
|
||||||
|
|
||||||
|
pairAllocator alloc key value =
|
||||||
|
"typename " <>> alloc <>> "::rebind<" <>> "std::pair<const " <>> elementTypeName key <<>> ", " <>> elementTypeName value <<> "> >::other"
|
||||||
|
|
||||||
|
cppSyntaxFix :: Builder -> Builder
|
||||||
|
cppSyntaxFix = fromLazyText . snd . L.foldr fixInvalid (' ', mempty) . toLazyText
|
||||||
|
where
|
||||||
|
fixInvalid c r
|
||||||
|
-- C++98 requires space between consecutive angle brackets
|
||||||
|
| c == '>' && fst r == '>' = (c, L.cons c (L.cons ' ' $ snd r))
|
||||||
|
-- <: is digraph for [
|
||||||
|
| c == '<' && fst r == ':' = (c, L.cons c (L.cons ' ' $ snd r))
|
||||||
|
| otherwise = (c, L.cons c (snd r))
|
||||||
|
|
||||||
|
|
||||||
|
-- C# type mapping
|
||||||
|
csType :: Type -> TypeNameBuilder
|
||||||
|
csType BT_Int8 = pure "sbyte"
|
||||||
|
csType BT_Int16 = pure "short"
|
||||||
|
csType BT_Int32 = pure "int"
|
||||||
|
csType BT_Int64 = pure "long"
|
||||||
|
csType BT_UInt8 = pure "byte"
|
||||||
|
csType BT_UInt16 = pure "ushort"
|
||||||
|
csType BT_UInt32 = pure "uint"
|
||||||
|
csType BT_UInt64 = pure "ulong"
|
||||||
|
csType BT_Float = pure "float"
|
||||||
|
csType BT_Double = pure "double"
|
||||||
|
csType BT_Bool = pure "bool"
|
||||||
|
csType BT_String = pure "string"
|
||||||
|
csType BT_WString = pure "string"
|
||||||
|
csType BT_MetaName = pure "string"
|
||||||
|
csType BT_MetaFullName = pure "string"
|
||||||
|
csType BT_Blob = pure "System.ArraySegment<byte>"
|
||||||
|
csType (BT_IntTypeArg x) = pureText x
|
||||||
|
csType (BT_Maybe type_) = csType (BT_Nullable type_)
|
||||||
|
csType (BT_Nullable element) = typeName element <<> if scalarType element then "?" else mempty
|
||||||
|
csType (BT_List element) = "LinkedList<" <>> elementTypeName element <<> ">"
|
||||||
|
csType (BT_Vector element) = "List<" <>> elementTypeName element <<> ">"
|
||||||
|
csType (BT_Set element) = "HashSet<" <>> elementTypeName element <<> ">"
|
||||||
|
csType (BT_Map key value) = "Dictionary<" <>> elementTypeName key <<>> ", " <>> elementTypeName value <<> ">"
|
||||||
|
csType (BT_Bonded type_) = "global::Bond.IBonded<" <>> typeName type_ <<> ">"
|
||||||
|
csType (BT_TypeParam param) = pureText $ paramName param
|
||||||
|
csType (BT_UserDefined a@Alias {..} args) = aliasTypeName a args
|
||||||
|
csType (BT_UserDefined decl args) = declTypeName decl <<>> (angles <$> localWith (const csTypeMapping) (commaSepTypeNames args))
|
||||||
|
|
||||||
|
-- C# type mapping with collection interfaces
|
||||||
|
csIfaceType :: Type -> TypeNameBuilder
|
||||||
|
csIfaceType (BT_List element) = "ICollection<" <>> elementTypeName element <<> ">"
|
||||||
|
csIfaceType (BT_Vector element) = "IList<" <>> elementTypeName element <<> ">"
|
||||||
|
csIfaceType (BT_Set element) = "ISet<" <>> elementTypeName element <<> ">"
|
||||||
|
csIfaceType (BT_Map key value) = "IDictionary<" <>> elementTypeName key <<>> ", " <>> elementTypeName value <<> ">"
|
||||||
|
csIfaceType t = csType t
|
||||||
|
|
||||||
|
-- C# type annotation mapping
|
||||||
|
csTypeAnnotation :: Type -> TypeNameBuilder
|
||||||
|
csTypeAnnotation BT_WString = pure "global::Bond.Tag.wstring"
|
||||||
|
csTypeAnnotation (BT_Nullable element) = "global::Bond.Tag.nullable<" <>> typeName element <<> ">"
|
||||||
|
csTypeAnnotation (BT_Bonded type_) = "global::Bond.Tag.bonded<" <>> typeName type_ <<> ">"
|
||||||
|
csTypeAnnotation (BT_TypeParam (TypeParam _ Nothing)) = pure "global::Bond.Tag.classT"
|
||||||
|
csTypeAnnotation (BT_TypeParam (TypeParam _ (Just Value))) = pure "global::Bond.Tag.structT"
|
||||||
|
csTypeAnnotation (BT_UserDefined Alias {aliasType = BT_Blob} _) = pure "global::Bond.Tag.blob"
|
||||||
|
csTypeAnnotation t@(BT_UserDefined Alias {aliasType = (BT_Set _)} _) = csType t
|
||||||
|
csTypeAnnotation t@(BT_UserDefined Alias {aliasType = (BT_List _)} _) = csType t
|
||||||
|
csTypeAnnotation t@(BT_UserDefined Alias {aliasType = (BT_Map _ _)} _) = csType t
|
||||||
|
csTypeAnnotation t@(BT_UserDefined Alias {aliasType = (BT_Vector _)} _) = csType t
|
||||||
|
csTypeAnnotation (BT_UserDefined a@Alias {..} args) = typeName $ resolveAlias a args
|
||||||
|
csTypeAnnotation (BT_UserDefined decl args) = declTypeName decl <<>> (angles <$> commaSepTypeNames args)
|
||||||
|
csTypeAnnotation t = csType t
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
-- Copyright (c) Microsoft. All rights reserved.
|
||||||
|
-- Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
{-# LANGUAGE QuasiQuotes, OverloadedStrings #-}
|
||||||
|
|
||||||
|
module Bond.Template.Util
|
||||||
|
( toText
|
||||||
|
, commonHeader
|
||||||
|
, mconcatMap
|
||||||
|
, mconcatFor
|
||||||
|
, sepBy
|
||||||
|
, sepEndBy
|
||||||
|
, sepBeginBy
|
||||||
|
, sep
|
||||||
|
, commaSep
|
||||||
|
, newlineSep
|
||||||
|
, commaLineSep
|
||||||
|
, newlineSepEnd
|
||||||
|
, newlineBeginSep
|
||||||
|
, doubleLineSep
|
||||||
|
, doubleLineSepEnd
|
||||||
|
) where
|
||||||
|
|
||||||
|
import System.FilePath
|
||||||
|
import Data.Monoid
|
||||||
|
import Data.Word
|
||||||
|
import Data.String
|
||||||
|
import Data.Text.Lazy (justifyRight)
|
||||||
|
import Data.Text.Lazy.Builder
|
||||||
|
import Text.Shakespeare.Text
|
||||||
|
import Bond.Version
|
||||||
|
import Bond.Util
|
||||||
|
import Bond.Schema
|
||||||
|
|
||||||
|
instance ToText Word16 where
|
||||||
|
toText = toText . show
|
||||||
|
|
||||||
|
instance ToText Double where
|
||||||
|
toText = toText . show
|
||||||
|
|
||||||
|
instance ToText Integer where
|
||||||
|
toText = toText . show
|
||||||
|
|
||||||
|
mconcatMap f = foldr (mappend . f) mempty
|
||||||
|
|
||||||
|
mconcatFor m f = mconcatMap f m
|
||||||
|
|
||||||
|
sep s = sepBy s toText
|
||||||
|
|
||||||
|
commaSep :: ToText t => [t] -> Builder
|
||||||
|
commaSep = sepBy ", " toText
|
||||||
|
|
||||||
|
indent n = justifyRight (4 * n) ' ' ""
|
||||||
|
|
||||||
|
commaLine n = [lt|,
|
||||||
|
#{indent n}|]
|
||||||
|
|
||||||
|
newLine n = [lt|
|
||||||
|
#{indent n}|]
|
||||||
|
|
||||||
|
doubleLine n = [lt|
|
||||||
|
|
||||||
|
#{indent n}|]
|
||||||
|
|
||||||
|
newlineSep = sepBy . newLine
|
||||||
|
commaLineSep = sepBy . commaLine
|
||||||
|
newlineSepEnd = sepEndBy . newLine
|
||||||
|
newlineBeginSep = sepBeginBy . newLine
|
||||||
|
doubleLineSep = sepBy . doubleLine
|
||||||
|
doubleLineSepEnd = sepEndBy . doubleLine
|
||||||
|
|
||||||
|
commonHeader file = [lt|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// This code was generated by a tool.
|
||||||
|
//
|
||||||
|
// Tool : Bond Compiler #{majorVersion}.#{minorVersion}
|
||||||
|
// File : #{file}
|
||||||
|
//
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost when
|
||||||
|
// the code is regenerated.
|
||||||
|
// <auto-generated />
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|]
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
-- Copyright (c) Microsoft. All rights reserved.
|
||||||
|
-- Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
|
module Bond.Util
|
||||||
|
( sepBy
|
||||||
|
, sepEndBy
|
||||||
|
, sepBeginBy
|
||||||
|
, optional
|
||||||
|
, angles
|
||||||
|
, brackets
|
||||||
|
, braces
|
||||||
|
, parens
|
||||||
|
, between
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Data.Monoid
|
||||||
|
|
||||||
|
sepEndBy s f [] = mempty
|
||||||
|
sepEndBy s f (x:xs)
|
||||||
|
| next == mempty = rest
|
||||||
|
| otherwise = next <> s <> rest
|
||||||
|
where
|
||||||
|
next = f x
|
||||||
|
rest = sepEndBy s f xs
|
||||||
|
|
||||||
|
sepBeginBy s f [] = mempty
|
||||||
|
sepBeginBy s f (x:xs)
|
||||||
|
| next == mempty = rest
|
||||||
|
| otherwise = s <> next <> rest
|
||||||
|
where
|
||||||
|
next = f x
|
||||||
|
rest = sepBeginBy s f xs
|
||||||
|
|
||||||
|
sepBy s f [] = mempty
|
||||||
|
sepBy s f (x:xs)
|
||||||
|
| null xs = next
|
||||||
|
| next == mempty = rest
|
||||||
|
| otherwise = next <> sepBeginBy s f xs
|
||||||
|
where
|
||||||
|
next = f x
|
||||||
|
rest = sepBy s f xs
|
||||||
|
|
||||||
|
optional :: (Monoid m) => (a -> m) -> Maybe a -> m
|
||||||
|
optional = maybe mempty
|
||||||
|
|
||||||
|
between l r m
|
||||||
|
| m == mempty = mempty
|
||||||
|
| otherwise = l <> m <> r
|
||||||
|
|
||||||
|
angles m = between "<" ">" m
|
||||||
|
brackets m = between "[" "]" m
|
||||||
|
braces m = between "{" "}" m
|
||||||
|
parens m = between "(" ")" m
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
-- Copyright (c) Microsoft. All rights reserved.
|
||||||
|
-- Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
module Bond.Version
|
||||||
|
( majorVersion
|
||||||
|
, minorVersion
|
||||||
|
) where
|
||||||
|
|
||||||
|
majorVersion = "3"
|
||||||
|
minorVersion = "02"
|
|
@ -0,0 +1,68 @@
|
||||||
|
cmake_minimum_required (VERSION 2.8.12)
|
||||||
|
|
||||||
|
set (CMAKE_MODULE_PATH
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../cmake)
|
||||||
|
|
||||||
|
# required Haskell components
|
||||||
|
find_package (Cabal 1.20.0.0 REQUIRED)
|
||||||
|
find_package (GHC 7.4.1.0 REQUIRED)
|
||||||
|
|
||||||
|
set (sources
|
||||||
|
gbc.cabal
|
||||||
|
Main.hs
|
||||||
|
Options.hs
|
||||||
|
Bond/Lexer.hs
|
||||||
|
Bond/Parser.hs
|
||||||
|
Bond/Schema.hs
|
||||||
|
Bond/Util.hs
|
||||||
|
Bond/Version.hs
|
||||||
|
Bond/Template/CustomMapping.hs
|
||||||
|
Bond/Template/TypeMapping.hs
|
||||||
|
Bond/Template/Util.hs
|
||||||
|
Bond/Template/Cpp/Apply_cpp.hs
|
||||||
|
Bond/Template/Cpp/Apply_h.hs
|
||||||
|
Bond/Template/Cpp/Enum_h.hs
|
||||||
|
Bond/Template/Cpp/Reflection_h.hs
|
||||||
|
Bond/Template/Cpp/Types_cpp.hs
|
||||||
|
Bond/Template/Cpp/Types_h.hs
|
||||||
|
Bond/Template/Cpp/Util.hs
|
||||||
|
Bond/Template/Cs/Types_cs.hs
|
||||||
|
Bond/Template/Cs/Util.hs)
|
||||||
|
|
||||||
|
set (output_dir ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
set (output ${output_dir}/build/gbc/gbc${CMAKE_EXECUTABLE_SUFFIX})
|
||||||
|
set (completion_dir /etc/bash_completion.d)
|
||||||
|
set (completion ${output_dir}/gbc.comp)
|
||||||
|
set (GBC_EXECUTABLE ${output} PARENT_SCOPE)
|
||||||
|
|
||||||
|
add_custom_command (
|
||||||
|
COMMAND ${Haskell_CABAL_EXECUTABLE} sandbox init
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/cabal.sandbox.config)
|
||||||
|
|
||||||
|
add_custom_command (
|
||||||
|
DEPENDS ${sources} ${CMAKE_CURRENT_SOURCE_DIR}/cabal.sandbox.config
|
||||||
|
COMMAND ${Haskell_CABAL_EXECUTABLE} --require-sandbox install --with-compiler="${Haskell_GHC_EXECUTABLE}" --only-dependencies --jobs
|
||||||
|
COMMAND ${Haskell_CABAL_EXECUTABLE} --require-sandbox configure --with-compiler="${Haskell_GHC_EXECUTABLE}" --builddir=${output_dir}
|
||||||
|
COMMAND ${Haskell_CABAL_EXECUTABLE} --require-sandbox build --with-ghc="${Haskell_GHC_EXECUTABLE}" --ghc-option=-O2 --jobs --builddir=${output_dir}
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
OUTPUT ${output})
|
||||||
|
|
||||||
|
add_custom_command (
|
||||||
|
DEPENDS ${output}
|
||||||
|
COMMAND ${output} --help=bash > ${completion}
|
||||||
|
OUTPUT ${completion})
|
||||||
|
|
||||||
|
add_custom_target (compiler
|
||||||
|
SOURCES ${sources}
|
||||||
|
DEPENDS ${output} ${completion})
|
||||||
|
|
||||||
|
install (FILES ${output}
|
||||||
|
PERMISSIONS WORLD_EXECUTE
|
||||||
|
DESTINATION bin)
|
||||||
|
|
||||||
|
if(EXISTS "${completion_dir}" AND IS_DIRECTORY "${completion_dir}")
|
||||||
|
install (FILES ${completion}
|
||||||
|
RENAME gbc
|
||||||
|
DESTINATION ${completion_dir})
|
||||||
|
endif()
|
|
@ -0,0 +1,108 @@
|
||||||
|
-- Copyright (c) Microsoft. All rights reserved.
|
||||||
|
-- Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
{-# LANGUAGE RecordWildCards #-}
|
||||||
|
|
||||||
|
import System.Environment (getArgs, withArgs)
|
||||||
|
import System.Directory
|
||||||
|
import System.FilePath
|
||||||
|
import System.IO
|
||||||
|
import System.Exit
|
||||||
|
import Control.Applicative
|
||||||
|
import Data.Monoid
|
||||||
|
import Control.Monad
|
||||||
|
import Control.Monad.Reader
|
||||||
|
import Control.Monad.Loops (firstM)
|
||||||
|
import qualified Data.Text.Lazy.IO as L
|
||||||
|
import Bond.Parser
|
||||||
|
import Bond.Template.Util
|
||||||
|
import Bond.Template.Cpp.Reflection_h
|
||||||
|
import Bond.Template.Cpp.Types_h
|
||||||
|
import Bond.Template.Cpp.Apply_h
|
||||||
|
import Bond.Template.Cpp.Apply_cpp
|
||||||
|
import Bond.Template.Cpp.Enum_h
|
||||||
|
import Bond.Template.Cpp.Types_cpp
|
||||||
|
import Bond.Template.Cs.Types_cs
|
||||||
|
import Bond.Template.TypeMapping
|
||||||
|
import Bond.Template.CustomMapping
|
||||||
|
import Options
|
||||||
|
|
||||||
|
main :: IO()
|
||||||
|
main = do
|
||||||
|
args <- getArgs
|
||||||
|
options <- (if null args then withArgs ["--help"] else id) getOptions
|
||||||
|
case options of
|
||||||
|
Cpp {..} -> cppCodegen options
|
||||||
|
Cs {..} -> csCodegen options
|
||||||
|
_ -> print options
|
||||||
|
|
||||||
|
cppCodegen :: Options -> IO()
|
||||||
|
cppCodegen (Cpp {..}) = do
|
||||||
|
aliasMapping <- parseAliasMapping using
|
||||||
|
namespaceMapping <- parseNamespaceMapping namespace
|
||||||
|
let typeMapping = case allocator of
|
||||||
|
Nothing -> cppTypeMapping
|
||||||
|
Just a -> cppCustomAllocTypeMapping a
|
||||||
|
let mappingContext = newMappingContext typeMapping aliasMapping namespaceMapping []
|
||||||
|
forM_ files $ codeGen output_dir import_dir mappingContext $
|
||||||
|
[ reflection_h
|
||||||
|
, types_cpp
|
||||||
|
, types_h header enum_header allocator
|
||||||
|
, apply_h applyProto apply_attribute
|
||||||
|
, apply_cpp applyProto
|
||||||
|
] <>
|
||||||
|
[ enum_h | enum_header]
|
||||||
|
where
|
||||||
|
applyProto = map snd $ filter (enabled apply) protocols
|
||||||
|
enabled a p = null a || fst p `elem` a
|
||||||
|
protocols =
|
||||||
|
[ (Compact, Protocol "CompactBinaryReader" "CompactBinaryWriter")
|
||||||
|
, (Fast, Protocol "FastBinaryReader" "FastBinaryWriter")
|
||||||
|
, (Simple, Protocol "SimpleBinaryReader" "SimpleBinaryWriter")
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
csCodegen :: Options -> IO()
|
||||||
|
csCodegen (Cs {..}) = do
|
||||||
|
aliasMapping <- parseAliasMapping using
|
||||||
|
namespaceMapping <- parseNamespaceMapping namespace
|
||||||
|
let typeMapping = if collection_interfaces then csInterfaceTypeMapping else csTypeMapping
|
||||||
|
let mappingContext = newMappingContext typeMapping aliasMapping namespaceMapping []
|
||||||
|
forM_ files $ codeGen output_dir import_dir mappingContext
|
||||||
|
[ types_cs readonly_properties fields
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
codeGen outputDir importDirs mappingContext templates file = do
|
||||||
|
cwd <- getCurrentDirectory
|
||||||
|
input <- readFileUtf8 file
|
||||||
|
result <- runReaderT (parseBond file input) (newEnvironment (cwd </> file) (readImportFile importDirs))
|
||||||
|
let baseName = takeBaseName file
|
||||||
|
case result of
|
||||||
|
Left error -> do
|
||||||
|
print error
|
||||||
|
exitFailure
|
||||||
|
Right (Bond imports namespaces declarations) -> forM_ templates $ \template -> do
|
||||||
|
let mapping = setNamespaces mappingContext namespaces
|
||||||
|
let (suffix, code) = template mapping baseName imports declarations
|
||||||
|
let fileName = baseName ++ suffix
|
||||||
|
createDirectoryIfMissing True outputDir
|
||||||
|
L.writeFile (outputDir </> fileName) (commonHeader fileName <> code)
|
||||||
|
|
||||||
|
readImportFile :: [FilePath] -> FilePath -> FilePath -> IO (FilePath, String)
|
||||||
|
readImportFile importDirs parentFile file = do
|
||||||
|
path <- findFilePath (takeDirectory parentFile:importDirs)
|
||||||
|
case path of
|
||||||
|
Just path -> do
|
||||||
|
content <- readFileUtf8 path
|
||||||
|
return (path, content)
|
||||||
|
Nothing -> fail $ "Can't find import file " ++ file
|
||||||
|
where
|
||||||
|
findFilePath dirs = fmap (</> file) <$> firstM (doesFileExist . (</> file)) dirs
|
||||||
|
|
||||||
|
readFileUtf8 :: FilePath -> IO String
|
||||||
|
readFileUtf8 name = do
|
||||||
|
h <- openFile name ReadMode
|
||||||
|
hSetEncoding h utf8_bom
|
||||||
|
hGetContents h
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
-- Copyright (c) Microsoft. All rights reserved.
|
||||||
|
-- Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
{-# LANGUAGE DeriveDataTypeable #-}
|
||||||
|
{-# OPTIONS_GHC -fno-warn-missing-fields #-}
|
||||||
|
{-# OPTIONS_GHC -fno-cse #-}
|
||||||
|
|
||||||
|
module Options (getOptions, Options(..), ApplyOptions(..)) where
|
||||||
|
|
||||||
|
import Bond.Version
|
||||||
|
import System.Console.CmdArgs
|
||||||
|
import System.Console.CmdArgs.Explicit (Mode(..))
|
||||||
|
|
||||||
|
data ApplyOptions =
|
||||||
|
Compact |
|
||||||
|
Fast |
|
||||||
|
Simple
|
||||||
|
deriving (Show, Data, Typeable, Eq)
|
||||||
|
|
||||||
|
data Options
|
||||||
|
= Options
|
||||||
|
| Cpp
|
||||||
|
{ files :: [FilePath]
|
||||||
|
, import_dir :: [FilePath]
|
||||||
|
, output_dir :: FilePath
|
||||||
|
, using :: [String]
|
||||||
|
, namespace :: [String]
|
||||||
|
, header :: [String]
|
||||||
|
, enum_header :: Bool
|
||||||
|
, allocator :: Maybe String
|
||||||
|
, apply :: [ApplyOptions]
|
||||||
|
, apply_attribute :: Maybe String
|
||||||
|
}
|
||||||
|
| Cs
|
||||||
|
{ files :: [FilePath]
|
||||||
|
, import_dir :: [FilePath]
|
||||||
|
, output_dir :: FilePath
|
||||||
|
, using :: [String]
|
||||||
|
, namespace :: [String]
|
||||||
|
, collection_interfaces :: Bool
|
||||||
|
, readonly_properties :: Bool
|
||||||
|
, fields :: Bool
|
||||||
|
}
|
||||||
|
deriving (Show, Data, Typeable)
|
||||||
|
|
||||||
|
cpp = Cpp
|
||||||
|
{ files = def &= typFile &= args
|
||||||
|
, import_dir = def &= typDir &= help "Add the directory to import search path"
|
||||||
|
, output_dir = "." &= typDir &= help "Output generated files into the specified directory"
|
||||||
|
, using = def &= typ "MAPPING" &= help "Custom type alias mapping in the form alias=type"
|
||||||
|
, namespace = def &= typ "MAPPING" &= help "Custom namespace mapping in the from bond_namespace=language_namespace"
|
||||||
|
, header = def &= typ "HEADER" &= help "Emit #include HEADER into the generated files"
|
||||||
|
, enum_header = def &= help "Generate enums into a separate header file"
|
||||||
|
, allocator = def &= typ "ALLOCATOR" &= help "Generate types using the specified allocator"
|
||||||
|
, apply = def &= typ "PROTOCOL" &= help "Generate Apply function overloads for the specified protocol only; supported protocols: compact, fast and simple"
|
||||||
|
, apply_attribute = def &= typ "ATTRIBUTE" &= help "Prefix the declarations of Apply functions with the specified C++ attribute/declspec"
|
||||||
|
} &=
|
||||||
|
name "c++" &=
|
||||||
|
help "Generate C++ code"
|
||||||
|
|
||||||
|
cs = Cs
|
||||||
|
{ collection_interfaces = def &= help "Use interfaces rather than concrete collection types"
|
||||||
|
, readonly_properties = def &= help "Generate private property setters"
|
||||||
|
, fields = def &= help "Generate public fields rather than properties"
|
||||||
|
} &=
|
||||||
|
name "c#" &=
|
||||||
|
help "Generate C# code"
|
||||||
|
|
||||||
|
mode = cmdArgsMode $ modes [cpp, cs] &=
|
||||||
|
program "gbc" &=
|
||||||
|
help "Compile Bond schema definition file and generate specified output" &=
|
||||||
|
summary ("Bond Compiler " ++ majorVersion ++ "." ++ minorVersion ++ ", (C) Microsoft")
|
||||||
|
|
||||||
|
getOptions = cmdArgsRun mode
|
|
@ -0,0 +1 @@
|
||||||
|
require-sandbox: True
|
|
@ -0,0 +1,61 @@
|
||||||
|
-- The name of the package.
|
||||||
|
name: gbc
|
||||||
|
|
||||||
|
-- The package version. See the Haskell package versioning policy (PVP)
|
||||||
|
-- for standards guiding when and how versions should be incremented.
|
||||||
|
-- http://www.haskell.org/haskellwiki/Package_versioning_policy
|
||||||
|
-- PVP summary: +-+------- breaking API changes
|
||||||
|
-- | | +----- non-breaking API additions
|
||||||
|
-- | | | +--- code changes with no API change
|
||||||
|
version: 3.0.2.0
|
||||||
|
|
||||||
|
-- A short (one-line) description of the package.
|
||||||
|
synopsis: The Glorious Bond Compilation System
|
||||||
|
|
||||||
|
-- A longer description of the package.
|
||||||
|
-- description:
|
||||||
|
|
||||||
|
-- URL for the project homepage or repository.
|
||||||
|
homepage: https://github.com/Microsoft/bond
|
||||||
|
|
||||||
|
-- The license under which the package is released.
|
||||||
|
license: MIT
|
||||||
|
|
||||||
|
-- The file containing the license text.
|
||||||
|
-- license-file: LICENSE
|
||||||
|
|
||||||
|
-- The package author(s).
|
||||||
|
author: Adam Sapek
|
||||||
|
|
||||||
|
-- An email address to which users can send suggestions, bug reports, and
|
||||||
|
-- patches.
|
||||||
|
maintainer: adamsap@microsoft.com
|
||||||
|
|
||||||
|
-- A copyright notice.
|
||||||
|
copyright: Copyright (C) Microsoft. All rights reserved.
|
||||||
|
|
||||||
|
category: Language
|
||||||
|
|
||||||
|
build-type: Simple
|
||||||
|
|
||||||
|
-- Constraint on the version of Cabal needed to build this package.
|
||||||
|
cabal-version: >=1.8
|
||||||
|
|
||||||
|
|
||||||
|
executable gbc
|
||||||
|
-- .hs or .lhs file containing the Main module.
|
||||||
|
main-is: Main.hs
|
||||||
|
|
||||||
|
-- Modules included in this executable, other than Main.
|
||||||
|
-- other-modules:
|
||||||
|
|
||||||
|
-- Other library packages from which modules are imported.
|
||||||
|
build-depends: base >=4.5,
|
||||||
|
shakespeare >= 2.0,
|
||||||
|
text >=0.11,
|
||||||
|
parsec >=3.1,
|
||||||
|
filepath >=1.0,
|
||||||
|
cmdargs >= 0.10.10,
|
||||||
|
mtl >= 2.1,
|
||||||
|
directory >= 1.1,
|
||||||
|
monad-loops >= 0.4
|
|
@ -0,0 +1 @@
|
||||||
|
generated
|
|
@ -0,0 +1,75 @@
|
||||||
|
set (sources
|
||||||
|
${BOND_INCLUDE}/bond/core/bond.bond
|
||||||
|
${BOND_INCLUDE}/bond/core/bond_const.bond)
|
||||||
|
|
||||||
|
add_bond_codegen (${sources}
|
||||||
|
ENUM_HEADER
|
||||||
|
OUTPUT_DIR ${BOND_GENERATED}/bond/core)
|
||||||
|
|
||||||
|
# Generate again into intermediate dir so that we can build libraries
|
||||||
|
# concurrently.
|
||||||
|
# Despite what documentation says, there doesn't seem to be a way to build
|
||||||
|
# multiple targets from one set of generated files without serializing them.
|
||||||
|
# Even when using add_custom_target to define an intermediate target the custom
|
||||||
|
# command rules are added to each target and VS forces execution of all rules
|
||||||
|
# in a clean build, leading to collisions in multi-processor build.
|
||||||
|
add_bond_codegen (${sources}
|
||||||
|
ENUM_HEADER
|
||||||
|
OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/bond/core)
|
||||||
|
|
||||||
|
file (GLOB core_headers "inc/bond/core/*.h")
|
||||||
|
source_group ("core" FILES ${core_headers})
|
||||||
|
|
||||||
|
file (GLOB core_detail_headers "inc/bond/core/detail/*.h")
|
||||||
|
source_group ("core\\detail" FILES ${core_detail_headers})
|
||||||
|
|
||||||
|
file (GLOB protocol_headers "inc/bond/protocol/*.h")
|
||||||
|
source_group ("protocol" FILES ${protocol_headers})
|
||||||
|
|
||||||
|
file (GLOB protocol_detail_headers "inc/bond/protocol/detail/*.h")
|
||||||
|
source_group ("protocol\\detail" FILES ${protocol_detail_headers})
|
||||||
|
|
||||||
|
file (GLOB stream_headers "inc/bond/stream/*.h")
|
||||||
|
source_group ("stream" FILES ${stream_headers})
|
||||||
|
|
||||||
|
set (generated_files
|
||||||
|
${BOND_GENERATED}/bond/core/bond_types.cpp
|
||||||
|
${BOND_GENERATED}/bond/core/bond_const_types.cpp)
|
||||||
|
|
||||||
|
source_group ("generated" FILES ${generated_files})
|
||||||
|
|
||||||
|
add_library (bond
|
||||||
|
STATIC
|
||||||
|
${sources}
|
||||||
|
${generated_files}
|
||||||
|
${core_headers}
|
||||||
|
${core_detail_headers}
|
||||||
|
${protocol_headers}
|
||||||
|
${protocol_detail_headers}
|
||||||
|
${stream_headers})
|
||||||
|
|
||||||
|
# Boost thread is only needed if the C++ standard library doesn't support
|
||||||
|
# std::call_once. However std::once seems problematic on Linux
|
||||||
|
# (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60662) so for now we use
|
||||||
|
# std::call_once only on MSVC.
|
||||||
|
if (Boost_SYSTEM_FOUND AND Boost_THREAD_FOUND OR NOT MSVC)
|
||||||
|
target_link_libraries (bond INTERFACE
|
||||||
|
${Boost_SYSTEM_LIBRARY}
|
||||||
|
${Boost_THREAD_LIBRARY})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_library (bond_apply
|
||||||
|
STATIC
|
||||||
|
${sources}
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/bond/core/bond_apply.cpp
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/bond/core/bond_const_apply.cpp)
|
||||||
|
|
||||||
|
target_include_directories (bond_apply BEFORE PRIVATE
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR})
|
||||||
|
|
||||||
|
install (TARGETS bond bond_apply
|
||||||
|
EXPORT bond
|
||||||
|
ARCHIVE DESTINATION lib/bond
|
||||||
|
INCLUDES DESTINATION include)
|
||||||
|
|
||||||
|
add_subdirectory (test)
|
|
@ -0,0 +1,81 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable: 4702)
|
||||||
|
|
||||||
|
#include "bonded.h"
|
||||||
|
#include "bonded_void.h"
|
||||||
|
#include <bond/core/bond_reflection.h>
|
||||||
|
#include "parser.h"
|
||||||
|
#include "exception.h"
|
||||||
|
#include "detail/double_pass.h"
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Apply transform to serialized struct wrapped in bonded<T>
|
||||||
|
template <typename Transform, typename T, typename Reader>
|
||||||
|
typename boost::disable_if<detail::need_double_pass<Transform>, bool>::type inline
|
||||||
|
Apply(const Transform& transform, const bonded<T, Reader>& bonded)
|
||||||
|
{
|
||||||
|
return bonded._Apply(transform);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Apply transform to serialized container wrapped in value<T, Reader>
|
||||||
|
template <typename Transform, typename T, typename Reader>
|
||||||
|
void inline
|
||||||
|
Apply(const Transform& transform, const value<T, Reader>& value)
|
||||||
|
{
|
||||||
|
value._Apply(transform);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Apply transform to an instance of a struct
|
||||||
|
template <typename Transform, typename T>
|
||||||
|
typename boost::disable_if<detail::need_double_pass<Transform>, bool>::type inline
|
||||||
|
Apply(const Transform& transform, const T& value)
|
||||||
|
{
|
||||||
|
return StaticParser<const T&>(value).Apply(transform, typename schema<T>::type());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Apply transform which can modify an instance of a struct
|
||||||
|
template <typename Transform, typename T>
|
||||||
|
typename boost::enable_if<is_modifying_transform<Transform>, bool>::type inline
|
||||||
|
Apply(const Transform& transform, T& value)
|
||||||
|
{
|
||||||
|
return StaticParser<T&>(value).Apply(transform, typename schema<T>::type());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Specializations for transform requiring double-pass
|
||||||
|
template <typename Transform, typename T, typename Reader>
|
||||||
|
typename boost::enable_if<detail::need_double_pass<Transform>, bool>::type inline
|
||||||
|
Apply(const Transform& transform, const bonded<T, Reader>& bonded)
|
||||||
|
{
|
||||||
|
if (transform.NeedPass0())
|
||||||
|
return detail::DoublePassApply(transform, bonded);
|
||||||
|
else
|
||||||
|
return bonded._Apply(transform);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Transform, typename T>
|
||||||
|
typename boost::enable_if<detail::need_double_pass<Transform>, bool>::type inline
|
||||||
|
Apply(const Transform& transform, const T& value)
|
||||||
|
{
|
||||||
|
if (transform.NeedPass0())
|
||||||
|
return detail::DoublePassApply(transform, value);
|
||||||
|
else
|
||||||
|
return StaticParser<const T&>(value).Apply(transform, typename schema<T>::type());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace bond
|
||||||
|
|
||||||
|
#pragma warning(pop)
|
|
@ -0,0 +1,385 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
/** @file */
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "container_interface.h"
|
||||||
|
#include <boost/shared_array.hpp>
|
||||||
|
#include <boost/make_shared.hpp>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
/// namespace bond
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
|
||||||
|
/// @brief Memory blob
|
||||||
|
class blob
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef int8_t value_type;
|
||||||
|
typedef const char* const_iterator;
|
||||||
|
|
||||||
|
/// @brief Default constructor
|
||||||
|
blob()
|
||||||
|
: _buffer(),
|
||||||
|
_content(),
|
||||||
|
_length()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Construct from a raw pointer to memory buffer
|
||||||
|
///
|
||||||
|
/// Not recommended because of buffer lifetime management.
|
||||||
|
blob(const void* content, uint32_t length)
|
||||||
|
: _buffer(),
|
||||||
|
_content(static_cast<const char*>(content)),
|
||||||
|
_length(length)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Construct from a boost::shared_ptr to const memory buffer
|
||||||
|
blob(const boost::shared_ptr<const char[]>& buffer, uint32_t length)
|
||||||
|
: _buffer(buffer),
|
||||||
|
_content(_buffer.get()),
|
||||||
|
_length(length)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Construct from a boost::shared_ptr to const memory buffer
|
||||||
|
blob(const boost::shared_ptr<const char[]>& buffer, uint32_t offset, uint32_t length)
|
||||||
|
: _buffer(buffer),
|
||||||
|
_content(_buffer.get() + offset),
|
||||||
|
_length(length)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Construct from a boost::shared_ptr to memory buffer
|
||||||
|
blob(const boost::shared_ptr<char[]>& buffer, uint32_t length)
|
||||||
|
: _buffer(buffer),
|
||||||
|
_content(_buffer.get()),
|
||||||
|
_length(length)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Construct from a boost::shared_ptr to memory buffer
|
||||||
|
blob(const boost::shared_ptr<char[]>& buffer, uint32_t offset, uint32_t length)
|
||||||
|
: _buffer(buffer),
|
||||||
|
_content(_buffer.get() + offset),
|
||||||
|
_length(length)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Construct from a smart pointer other than boost::shared_ptr
|
||||||
|
///
|
||||||
|
/// Not recommended for performance reasons. Use boost::shared_ptr whenever possible.
|
||||||
|
template <typename T, template <typename U> class SmartPtr>
|
||||||
|
blob(const SmartPtr<T>& buffer, uint32_t length)
|
||||||
|
: _buffer(wrap_in_shared_ptr(buffer)),
|
||||||
|
_content(_buffer.get()),
|
||||||
|
_length(length)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Construct from a smart pointer other than boost::shared_ptr
|
||||||
|
///
|
||||||
|
/// Not recommended for performance reasons. Use boost::shared_ptr whenever possible.
|
||||||
|
template <typename T, template <typename U> class SmartPtr>
|
||||||
|
blob(const SmartPtr<T>& buffer, uint32_t offset, uint32_t length)
|
||||||
|
: _buffer(wrap_in_shared_ptr(buffer)),
|
||||||
|
_content(_buffer.get() + offset),
|
||||||
|
_length(length)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef BOND_NO_CXX11_RVALUE_REFERENCES
|
||||||
|
/// @brief Move constructor
|
||||||
|
blob(blob&& that)
|
||||||
|
: _buffer(std::move(that._buffer)),
|
||||||
|
_content(std::move(that._content)),
|
||||||
|
_length(std::move(that._length))
|
||||||
|
{
|
||||||
|
that._content = 0;
|
||||||
|
that._length = 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef BOND_NO_CXX11_DEFAULTED_FUNCTIONS
|
||||||
|
blob(const blob& that) = default;
|
||||||
|
blob& operator=(const blob& that) = default;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// @brief Assign a new value from another blob object or its part
|
||||||
|
void assign(const blob& from, uint32_t offset, uint32_t length)
|
||||||
|
{
|
||||||
|
BOOST_ASSERT((offset + length) <= from._length);
|
||||||
|
|
||||||
|
_buffer = from._buffer;
|
||||||
|
_content = from._content + offset;
|
||||||
|
_length = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Assign a new value from a raw or smart pointer
|
||||||
|
template <typename T>
|
||||||
|
void assign(const T& buffer, uint32_t length)
|
||||||
|
{
|
||||||
|
blob temp(buffer, length);
|
||||||
|
|
||||||
|
swap(temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Assign a new value from a raw or smart pointer
|
||||||
|
template <typename T>
|
||||||
|
void assign(const T& buffer, uint32_t offset, uint32_t length)
|
||||||
|
{
|
||||||
|
blob temp(buffer, offset, length);
|
||||||
|
|
||||||
|
swap(temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Return a blob object for a range of this object
|
||||||
|
blob range(uint32_t offset, uint32_t length) const
|
||||||
|
{
|
||||||
|
BOOST_ASSERT((offset + length) <= _length);
|
||||||
|
|
||||||
|
blob temp;
|
||||||
|
temp._buffer = _buffer;
|
||||||
|
temp._content = _content + offset;
|
||||||
|
temp._length = length;
|
||||||
|
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Return a blob object for a range from the specified offset to
|
||||||
|
/// the end of the buffer
|
||||||
|
blob range(uint32_t offset) const
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(offset <= _length);
|
||||||
|
|
||||||
|
blob temp = *this;
|
||||||
|
temp._content += offset;
|
||||||
|
temp._length -= offset;
|
||||||
|
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Swap with another blob
|
||||||
|
void swap(blob& src)
|
||||||
|
{
|
||||||
|
std::swap(_content, src._content);
|
||||||
|
std::swap(_length, src._length);
|
||||||
|
_buffer.swap(src._buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Clear reference to the underlying memory buffer and reset the
|
||||||
|
/// blob to empty
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
blob temp;
|
||||||
|
|
||||||
|
swap(temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Pointer to the content
|
||||||
|
const char* content() const
|
||||||
|
{
|
||||||
|
return _content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Void pointer to the content
|
||||||
|
const void* data() const
|
||||||
|
{
|
||||||
|
return _content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Length of the content
|
||||||
|
uint32_t length() const
|
||||||
|
{
|
||||||
|
return _length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Length of the content
|
||||||
|
uint32_t size() const
|
||||||
|
{
|
||||||
|
return _length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Check if the blob is empty (i.e. lenght == 0)
|
||||||
|
bool empty() const
|
||||||
|
{
|
||||||
|
return 0 == length();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const blob& src) const
|
||||||
|
{
|
||||||
|
return this == &src
|
||||||
|
|| ((_length == src._length)
|
||||||
|
&& (0 == ::memcmp(_content, src._content, _length)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Iterator for the beginning of the blob
|
||||||
|
const_iterator begin() const
|
||||||
|
{
|
||||||
|
return _content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Iterator for the end of the blob
|
||||||
|
const_iterator end() const
|
||||||
|
{
|
||||||
|
return _content + _length;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
friend T blob_cast(const blob& from);
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <typename T>
|
||||||
|
struct deleter
|
||||||
|
{
|
||||||
|
deleter(const T& p)
|
||||||
|
: p(p)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void operator()(void const *)
|
||||||
|
{
|
||||||
|
p.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
T p;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, template <typename U> class SmartPtr>
|
||||||
|
static boost::shared_ptr<const char[]> wrap_in_shared_ptr(const SmartPtr<T>& p)
|
||||||
|
{
|
||||||
|
boost::shared_ptr<const char[]> ptr(static_cast<const char*>(static_cast<const void*>(p.get())),
|
||||||
|
deleter<SmartPtr<T> >(p));
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::shared_ptr<const char[]> _buffer;
|
||||||
|
|
||||||
|
const char* _content;
|
||||||
|
|
||||||
|
uint32_t _length;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @brief Swap two blobs
|
||||||
|
inline void swap(blob& src, blob& dst)
|
||||||
|
{
|
||||||
|
src.swap(dst);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool operator != (const blob& x, const blob& y)
|
||||||
|
{
|
||||||
|
return !(x == y);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename A>
|
||||||
|
inline blob merge(const A& allocator, const blob& x, const blob& y)
|
||||||
|
{
|
||||||
|
if (x.empty() || y.empty())
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// one of provided blobs is empty,
|
||||||
|
// return the other one
|
||||||
|
//
|
||||||
|
return x.empty() ? y : x;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
uint32_t length = x.length() + y.length();
|
||||||
|
boost::shared_ptr<char[]> buffer = boost::allocate_shared<char[]>(allocator, length);
|
||||||
|
|
||||||
|
::memcpy(buffer.get(), x.content(), x.length());
|
||||||
|
::memcpy(buffer.get() + x.length(), y.content(), y.length());
|
||||||
|
|
||||||
|
return blob(buffer, length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename t_It, typename A>
|
||||||
|
inline blob merge(const A& allocator, t_It begin, t_It end)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// calculate the size of resulting blob
|
||||||
|
//
|
||||||
|
uint32_t length = 0;
|
||||||
|
for (t_It it = begin; it != end; ++it)
|
||||||
|
{
|
||||||
|
length += it->length();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 == length)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// empty blob to return
|
||||||
|
//
|
||||||
|
return blob();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if (length == begin->length())
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// just first blob in the sequence is not empty
|
||||||
|
//
|
||||||
|
return *begin;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// sequence of several non-empty blobs
|
||||||
|
//
|
||||||
|
BOOST_ASSERT(length > begin->length());
|
||||||
|
|
||||||
|
boost::shared_ptr<char[]> buffer = boost::allocate_shared<char[]>(allocator, length);
|
||||||
|
|
||||||
|
uint32_t offset = 0;
|
||||||
|
for (t_It it = begin; it != end; ++it)
|
||||||
|
{
|
||||||
|
::memcpy(buffer.get() + offset, it->content(), it->length());
|
||||||
|
|
||||||
|
offset += it->length();
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_ASSERT(offset == length);
|
||||||
|
return blob(buffer, length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline blob merge(const blob& x, const blob& y)
|
||||||
|
{
|
||||||
|
return merge(std::allocator<char>(), x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename t_It>
|
||||||
|
inline blob merge(t_It begin, t_It end)
|
||||||
|
{
|
||||||
|
return merge(std::allocator<char>(), begin, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <> struct
|
||||||
|
is_list_container<blob>
|
||||||
|
: true_type {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline T blob_cast(const blob& from)
|
||||||
|
{
|
||||||
|
if (from._buffer)
|
||||||
|
{
|
||||||
|
boost::shared_array<char> ptr(const_cast<char*>(static_cast<const char*>(static_cast<const void*>(from._buffer.get()))),
|
||||||
|
blob::deleter<boost::shared_ptr<const char[]> >(from._buffer));
|
||||||
|
|
||||||
|
return T(ptr, static_cast<uint32_t>(from._content - from._buffer.get()), from._length);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return T(from._content, from._length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // bond namespace
|
||||||
|
|
|
@ -0,0 +1,126 @@
|
||||||
|
import "bond_const.bond"
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
|
||||||
|
struct SerializableExceptionBase
|
||||||
|
{
|
||||||
|
8189: string message;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Empty schema with no fields
|
||||||
|
struct Void
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schema with the same memory layout as Windows GUID
|
||||||
|
struct GUID
|
||||||
|
{
|
||||||
|
0: uint32 Data1;
|
||||||
|
1: uint16 Data2;
|
||||||
|
2: uint16 Data3;
|
||||||
|
3: uint64 Data4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Field modifier enumerator
|
||||||
|
enum Modifier
|
||||||
|
{
|
||||||
|
Optional,
|
||||||
|
Required,
|
||||||
|
RequiredOptional
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schema used to represent field's default value
|
||||||
|
struct Variant
|
||||||
|
{
|
||||||
|
0: uint64 uint_value;
|
||||||
|
1: int64 int_value;
|
||||||
|
2: double double_value;
|
||||||
|
3: string string_value;
|
||||||
|
4: wstring wstring_value;
|
||||||
|
5: bool nothing;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schema representing field or struct metadata
|
||||||
|
struct Metadata
|
||||||
|
{
|
||||||
|
// Name of the field or struct
|
||||||
|
0: string name;
|
||||||
|
|
||||||
|
// Fully qualified name, used only for structs
|
||||||
|
1: string qualified_name;
|
||||||
|
|
||||||
|
// Attributes
|
||||||
|
2: map<string, string> attributes;
|
||||||
|
|
||||||
|
// Field modifier, not used for structs
|
||||||
|
3: Modifier modifier = Optional;
|
||||||
|
|
||||||
|
// Default value of the field, not used for structs
|
||||||
|
4: Variant default_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schema representing a type within SchemaDef
|
||||||
|
struct TypeDef
|
||||||
|
{
|
||||||
|
// Type identifier
|
||||||
|
0: BondDataType id = BT_STRUCT;
|
||||||
|
|
||||||
|
// Index of struct definition in SchemaDef.structs when id == BT_STRUCT
|
||||||
|
1: uint16 struct_def = 0;
|
||||||
|
|
||||||
|
// Type definition for:
|
||||||
|
// list elements (id == BT_LIST),
|
||||||
|
// set elements (id == BT_SET),
|
||||||
|
// or mapped value (id == BT_MAP)
|
||||||
|
2: nullable<TypeDef> element;
|
||||||
|
|
||||||
|
// Type definition for map key when id == BT_MAP
|
||||||
|
3: nullable<TypeDef> key;
|
||||||
|
|
||||||
|
// True if the type is bonded<T>; used only when id == BT_STRUCT
|
||||||
|
4: bool bonded_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schema representing a field definition
|
||||||
|
struct FieldDef
|
||||||
|
{
|
||||||
|
// Field metadata
|
||||||
|
0: Metadata metadata;
|
||||||
|
|
||||||
|
// Field ordinal
|
||||||
|
1: uint16 id;
|
||||||
|
|
||||||
|
// Field type definition
|
||||||
|
2: TypeDef type;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schema representing a struct definition
|
||||||
|
struct StructDef
|
||||||
|
{
|
||||||
|
// Struct metadata
|
||||||
|
0: Metadata metadata;
|
||||||
|
|
||||||
|
// Type definition of base struct
|
||||||
|
1: nullable<TypeDef> base_def;
|
||||||
|
|
||||||
|
// List of field definitions
|
||||||
|
2: vector<FieldDef> fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Schema used to represent schema definition
|
||||||
|
struct SchemaDef
|
||||||
|
{
|
||||||
|
// List of struct definitions referenced in the schema
|
||||||
|
0: vector<StructDef> structs;
|
||||||
|
|
||||||
|
// Root struct of the schema
|
||||||
|
1: TypeDef root;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generic schema with one field of specified type
|
||||||
|
struct Box<T>
|
||||||
|
{
|
||||||
|
0: T value;
|
||||||
|
}
|
|
@ -0,0 +1,128 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
/** @file */
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "apply.h"
|
||||||
|
#include "select_protocol.h"
|
||||||
|
|
||||||
|
/// namespace bond
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
|
||||||
|
/// @brief Serialize an object using a protocol writer
|
||||||
|
///
|
||||||
|
template <typename T, typename Writer>
|
||||||
|
inline void Serialize(const T& obj, Writer& output)
|
||||||
|
{
|
||||||
|
Apply(Serializer<Writer>(output), obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Deserialize an object from a protocol reader
|
||||||
|
template <typename T, typename Reader>
|
||||||
|
inline void Deserialize(Reader input, T& obj)
|
||||||
|
{
|
||||||
|
Apply(To<T>(obj), bonded<T, Reader&>(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Deserialize an object of type T from a protocol reader
|
||||||
|
template <typename T, typename Reader>
|
||||||
|
inline T Deserialize(Reader input)
|
||||||
|
{
|
||||||
|
T tmp;
|
||||||
|
Apply(To<T>(tmp), bonded<T, Reader&>(input));
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Deserialize an object from a protocol reader using runtime schema
|
||||||
|
template <typename T, typename Reader>
|
||||||
|
inline void Deserialize(Reader input, T& obj, const RuntimeSchema& schema)
|
||||||
|
{
|
||||||
|
Apply(To<T>(obj), bonded<void, Reader&>(input, schema));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Deserialize an object of type T from a protocol reader using runtime schema
|
||||||
|
template <typename T, typename Reader>
|
||||||
|
inline T Deserialize(Reader input, const RuntimeSchema& schema)
|
||||||
|
{
|
||||||
|
T tmp;
|
||||||
|
Apply(To<T>(tmp), bonded<void, Reader&>(input, schema));
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Marshal an object using a protocol writer
|
||||||
|
template <typename T, typename Writer>
|
||||||
|
inline void Marshal(const T& obj, Writer& output)
|
||||||
|
{
|
||||||
|
Apply(Marshaler<Writer>(output), obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Unmarshal an object from data stream
|
||||||
|
template <typename T, typename Buffer>
|
||||||
|
inline void Unmarshal(Buffer input, T& obj)
|
||||||
|
{
|
||||||
|
SelectProtocolAndApply<T>(input, To<T>(obj));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Unmarshal an object of type T from data stream
|
||||||
|
template <typename T, typename Buffer>
|
||||||
|
inline T Unmarshal(Buffer input)
|
||||||
|
{
|
||||||
|
T tmp;
|
||||||
|
SelectProtocolAndApply<T>(input, To<T>(tmp));
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Initialize a bonded<T> from data stream contained marshaled object
|
||||||
|
template <typename T, typename Buffer>
|
||||||
|
inline void Unmarshal(Buffer input, bonded<T>& obj)
|
||||||
|
{
|
||||||
|
SelectProtocolAndApply<T>(input, boost::ref(obj));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Unmarshal an object from data stream using a runtime schema
|
||||||
|
template <typename T, typename Buffer>
|
||||||
|
inline void Unmarshal(Buffer input, T& obj, const RuntimeSchema& schema)
|
||||||
|
{
|
||||||
|
SelectProtocolAndApply(schema, input, To<T>(obj));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Unmarshal an object of type T from data stream using a runtime schema
|
||||||
|
template <typename T, typename Buffer>
|
||||||
|
inline T Unmarshal(Buffer input, const RuntimeSchema& schema)
|
||||||
|
{
|
||||||
|
T tmp;
|
||||||
|
SelectProtocolAndApply(schema, input, To<T>(tmp));
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Initialize a bonded<T> from data stream contained marshaled object
|
||||||
|
/// using a runtime schema
|
||||||
|
template <typename T, typename Buffer>
|
||||||
|
inline void Unmarshal(Buffer input, bonded<T>& obj, const RuntimeSchema& schema)
|
||||||
|
{
|
||||||
|
SelectProtocolAndApply(schema, input, boost::ref(obj));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Merge an object with serialize data and write the result using
|
||||||
|
/// a protocol writer
|
||||||
|
template <typename T, typename Reader, typename Writer>
|
||||||
|
inline void Merge(const T& obj, Reader input, Writer& output)
|
||||||
|
{
|
||||||
|
Apply(Merger<T, Writer>(obj, output), bonded<T>(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
namespace bond
|
||||||
|
|
||||||
|
// Enumerator of Bond meta-schema types
|
||||||
|
enum BondDataType
|
||||||
|
{
|
||||||
|
BT_STOP = 0,
|
||||||
|
BT_STOP_BASE = 1,
|
||||||
|
BT_BOOL = 2,
|
||||||
|
BT_UINT8 = 3,
|
||||||
|
BT_UINT16 = 4,
|
||||||
|
BT_UINT32 = 5,
|
||||||
|
BT_UINT64 = 6,
|
||||||
|
BT_FLOAT = 7,
|
||||||
|
BT_DOUBLE = 8,
|
||||||
|
BT_STRING = 9,
|
||||||
|
BT_STRUCT = 10,
|
||||||
|
BT_LIST = 11,
|
||||||
|
BT_SET = 12,
|
||||||
|
BT_MAP = 13,
|
||||||
|
BT_INT8 = 14,
|
||||||
|
BT_INT16 = 15,
|
||||||
|
BT_INT32 = 16,
|
||||||
|
BT_INT64 = 17,
|
||||||
|
BT_WSTRING = 18,
|
||||||
|
BT_UNAVAILABLE= 127
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Magic numbers of predefined protocols
|
||||||
|
enum ProtocolType
|
||||||
|
{
|
||||||
|
// Actual protocol type is marshaled with payload
|
||||||
|
MARSHALED_PROTOCOL = 0,
|
||||||
|
|
||||||
|
// Fast binary protocol
|
||||||
|
FAST_PROTOCOL = 0x464d,
|
||||||
|
|
||||||
|
// Compact binary protocol
|
||||||
|
COMPACT_PROTOCOL = 0x4243,
|
||||||
|
|
||||||
|
// Simple JSON protocol
|
||||||
|
SIMPLE_JSON_PROTOCOL = 0x4a53,
|
||||||
|
|
||||||
|
// Simple binary protocol
|
||||||
|
SIMPLE_PROTOCOL = 0x5053,
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include <bond/core/bond_const_enum.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
|
||||||
|
class InputBuffer;
|
||||||
|
class RuntimeSchema;
|
||||||
|
|
||||||
|
template <typename Buffer>
|
||||||
|
struct ProtocolReader;
|
||||||
|
|
||||||
|
template <typename T, typename Reader = ProtocolReader<InputBuffer> >
|
||||||
|
class bonded;
|
||||||
|
|
||||||
|
template <typename Reader>
|
||||||
|
class bonded<void, Reader>;
|
||||||
|
|
||||||
|
template <typename V> struct
|
||||||
|
remove_bonded;
|
||||||
|
|
||||||
|
template <typename V> struct
|
||||||
|
remove_bonded<bonded<V> >;
|
||||||
|
|
||||||
|
template <typename T, typename Reader, typename Enable = void>
|
||||||
|
class value;
|
||||||
|
|
||||||
|
template <typename Reader>
|
||||||
|
class StaticParser;
|
||||||
|
|
||||||
|
template <typename Reader>
|
||||||
|
class DynamicParser;
|
||||||
|
|
||||||
|
template <typename Reader>
|
||||||
|
class DOMParser;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class RequiredFieldValiadator;
|
||||||
|
|
||||||
|
template <typename T, typename Validator = RequiredFieldValiadator<T> >
|
||||||
|
class To;
|
||||||
|
|
||||||
|
template <typename T, typename Enable = void> struct
|
||||||
|
schema_for_passthrough;
|
||||||
|
|
||||||
|
template<typename T> struct
|
||||||
|
get_type_id;
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
may_omit_fields;
|
||||||
|
|
||||||
|
template <typename Input>
|
||||||
|
struct base_input;
|
||||||
|
|
||||||
|
struct Metadata;
|
||||||
|
|
||||||
|
struct qualified_name_tag;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define BOND_VERSION 0x0302
|
||||||
|
#define BOND_MIN_CODEGEN_VERSION 0x0301
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
template <typename Buffer>
|
||||||
|
class SimpleBinaryReader;
|
||||||
|
|
||||||
|
template <typename BufferT>
|
||||||
|
class CompactBinaryReader;
|
||||||
|
|
||||||
|
static const uint16_t v1 = 0x0001;
|
||||||
|
static const uint16_t v2 = 0x0002;
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
default_version
|
||||||
|
{
|
||||||
|
static const uint16_t value = v1;
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,279 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "protocol.h"
|
||||||
|
#include "runtime_schema.h"
|
||||||
|
#include "detail/protocol_visitors.h"
|
||||||
|
#include "detail/double_pass.h"
|
||||||
|
#include "detail/marshaled_bonded.h"
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
is_bonded
|
||||||
|
: false_type {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Reader> struct
|
||||||
|
is_bonded<bonded<T, Reader> >
|
||||||
|
: true_type {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Reader, typename Unused = void> struct
|
||||||
|
is_marshaled_bonded
|
||||||
|
{
|
||||||
|
static const bool value = uses_marshaled_bonded<Reader, Unused>::value
|
||||||
|
&& is_bonded<T>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Buffer, typename Transform>
|
||||||
|
inline std::pair<ProtocolType, bool> SelectProtocolAndApply(
|
||||||
|
Buffer& input,
|
||||||
|
const Transform& transform);
|
||||||
|
|
||||||
|
|
||||||
|
#pragma warning(push)
|
||||||
|
// Disable warning when Reader parameter is a reference
|
||||||
|
// warning C4512: 'bond::bonded<T,Reader>' : assignment operator could not be generated
|
||||||
|
#pragma warning(disable : 4512)
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Represents data for a struct T known at compile-time
|
||||||
|
///
|
||||||
|
/// See [User's Manual](../../manual/bond_cpp.html#understanding-bondedt)
|
||||||
|
template <typename T, typename Reader>
|
||||||
|
class bonded
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// @brief Default constructor
|
||||||
|
bonded()
|
||||||
|
: _skip(false),
|
||||||
|
_base(false)
|
||||||
|
{}
|
||||||
|
|
||||||
|
/// @brief Copy constructor
|
||||||
|
bonded(const bonded& bonded)
|
||||||
|
: _data(bonded._data),
|
||||||
|
_schema(bonded._schema),
|
||||||
|
_skip(true),
|
||||||
|
_base(false)
|
||||||
|
{}
|
||||||
|
|
||||||
|
#ifndef BOND_NO_CXX11_RVALUE_REFERENCES
|
||||||
|
/// @brief Move constructor
|
||||||
|
bonded(bonded&& bonded)
|
||||||
|
: _data(std::move(bonded._data)),
|
||||||
|
_schema(std::move(bonded._schema)),
|
||||||
|
_skip(std::move(bonded._skip)),
|
||||||
|
_base(std::move(bonded._base))
|
||||||
|
{
|
||||||
|
bonded._skip = false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef BOND_NO_CXX11_DEFAULTED_FUNCTIONS
|
||||||
|
bonded& operator=(const bonded& rhs) = default;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// @brief Explicit up/down-casting from/to bonded of a derived type
|
||||||
|
template <typename U, typename ReaderT>
|
||||||
|
explicit
|
||||||
|
bonded(const bonded<U, ReaderT>& bonded)
|
||||||
|
: _data(bonded._data),
|
||||||
|
_schema(bonded._schema),
|
||||||
|
_skip(true),
|
||||||
|
_base(false)
|
||||||
|
{
|
||||||
|
BOOST_STATIC_ASSERT((is_base_of<U, T>::value || is_base_of<T, U>::value));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Explicit initialization from an instance of U which is convertible to Reader.
|
||||||
|
///
|
||||||
|
/// When `Reader` is an instance of `ProtocolReader` template, value can be one of:
|
||||||
|
/// - reference to an instance of object that is convertible to `T`;
|
||||||
|
/// will store a copy of the object
|
||||||
|
/// - `boost::shared_ptr<U>` to an object convertible to `T`;
|
||||||
|
/// will store an up-casted `shared_ptr`
|
||||||
|
/// - `boost::reference_wrapper<U>` to an object convertible to `T`;
|
||||||
|
/// will store an up-casted raw pointer to the object
|
||||||
|
template <typename U>
|
||||||
|
explicit
|
||||||
|
bonded(const U& value)
|
||||||
|
: _data(value),
|
||||||
|
_skip(true),
|
||||||
|
_base(false)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Initialize from serialized data
|
||||||
|
explicit
|
||||||
|
bonded(Reader data, bool base = false)
|
||||||
|
: _data(data),
|
||||||
|
_skip(true),
|
||||||
|
_base(base)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Explicit cast from `bonded<void>`
|
||||||
|
template <typename ReaderT>
|
||||||
|
explicit
|
||||||
|
bonded(const bonded<void, ReaderT>& bonded)
|
||||||
|
: _data(bonded._data),
|
||||||
|
_schema(bonded._schema),
|
||||||
|
_skip(true),
|
||||||
|
_base(false)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
~bonded()
|
||||||
|
{
|
||||||
|
// Skip the struct if it wasn't deserialized
|
||||||
|
if (_skip)
|
||||||
|
detail::Skip(_data, *this, std::nothrow);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Implicit up-casting to bonded of a base type
|
||||||
|
template <typename U, typename ReaderT>
|
||||||
|
operator bonded<U, ReaderT>() const
|
||||||
|
{
|
||||||
|
BOOST_STATIC_ASSERT((is_base_of<U, T>::value));
|
||||||
|
return bonded<U, ReaderT>(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Implicit conversion to `bonded<void>`
|
||||||
|
template <typename ReaderT>
|
||||||
|
operator bonded<void, ReaderT>() const
|
||||||
|
{
|
||||||
|
return bonded<void, ReaderT>(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Serialize bonded using specified protocol writer
|
||||||
|
template <typename Writer>
|
||||||
|
void Serialize(Writer& output) const
|
||||||
|
{
|
||||||
|
Apply(SerializeTo(output), *this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Deserialize an object of type T
|
||||||
|
T Deserialize() const
|
||||||
|
{
|
||||||
|
T tmp;
|
||||||
|
Apply(To<T>(tmp), *this);
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Deserialize an object of type X
|
||||||
|
template <typename X>
|
||||||
|
X Deserialize() const
|
||||||
|
{
|
||||||
|
X tmp;
|
||||||
|
Apply(To<X>(tmp), *this);
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Deserialize to an object of type X
|
||||||
|
template <typename X>
|
||||||
|
void Deserialize(X& var) const
|
||||||
|
{
|
||||||
|
Apply(To<X>(var), *this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Deserialize to a bonded<U>
|
||||||
|
template <typename U>
|
||||||
|
typename boost::enable_if<is_marshaled_bonded<T, Reader, U> >::type
|
||||||
|
Deserialize(bonded<U>& var) const
|
||||||
|
{
|
||||||
|
_SelectProtocolAndApply(boost::ref(var));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
typename boost::disable_if<is_marshaled_bonded<T, Reader, U> >::type
|
||||||
|
Deserialize(bonded<U>& var) const
|
||||||
|
{
|
||||||
|
var._data = _data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Update bonded<T> payload by merging it with an object of type X
|
||||||
|
template <typename X>
|
||||||
|
void Merge(const X& var)
|
||||||
|
{
|
||||||
|
detail::Merge(var, _data);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Skip struct data in the underlying payload
|
||||||
|
void Skip()
|
||||||
|
{
|
||||||
|
_skip = false;
|
||||||
|
detail::Skip(_data, *this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Compare for equality
|
||||||
|
///
|
||||||
|
/// Returns true if both `bonded` point to the same instance of `T` or the same input stream.
|
||||||
|
/// It does not compare values of objects `T` or contents of input streams.
|
||||||
|
bool operator==(const bonded& rhs) const
|
||||||
|
{
|
||||||
|
return _data == rhs._data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Transform, typename U, typename ReaderT>
|
||||||
|
friend typename boost::disable_if<detail::need_double_pass<Transform>, bool>::type inline
|
||||||
|
Apply(const Transform& transform, const bonded<U, ReaderT>& bonded);
|
||||||
|
|
||||||
|
template <typename Transform, typename U, typename ReaderT>
|
||||||
|
friend typename boost::enable_if<detail::need_double_pass<Transform>, bool>::type inline
|
||||||
|
Apply(const Transform& transform, const bonded<U, ReaderT>& bonded);
|
||||||
|
|
||||||
|
template <typename U, typename ReaderT>
|
||||||
|
friend class bonded;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Apply transform to serialized data
|
||||||
|
template <typename Transform>
|
||||||
|
typename boost::enable_if<is_marshaled_bonded<T, Reader, Transform>, bool>::type
|
||||||
|
_Apply(const Transform& transform) const
|
||||||
|
{
|
||||||
|
return _SelectProtocolAndApply(transform);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Transform>
|
||||||
|
typename boost::disable_if<is_marshaled_bonded<T, Reader, Transform>, bool>::type
|
||||||
|
_Apply(const Transform& transform) const
|
||||||
|
{
|
||||||
|
_skip = false;
|
||||||
|
return detail::Parse<T>(transform, _data, typename schema_for_passthrough<T>::type(), _schema.get(), _base);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Transform>
|
||||||
|
bool _SelectProtocolAndApply(const Transform& transform) const
|
||||||
|
{
|
||||||
|
_skip = false;
|
||||||
|
InputBuffer input(detail::ReadBlob(_data));
|
||||||
|
return SelectProtocolAndApply<typename remove_bonded<T>::type>(input, transform).second;
|
||||||
|
}
|
||||||
|
|
||||||
|
Reader _data;
|
||||||
|
RuntimeSchema _schema;
|
||||||
|
mutable bool _skip;
|
||||||
|
bool _base;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#pragma warning(pop)
|
||||||
|
|
||||||
|
};
|
|
@ -0,0 +1,186 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "bonded.h"
|
||||||
|
#include "schema.h"
|
||||||
|
#include "select_protocol.h"
|
||||||
|
#include "detail/nonassignable.h"
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Untyped specialization of bonded<T>, used for dynamic binding (i.e. schema known at runtime)
|
||||||
|
///
|
||||||
|
/// See [User's Manual](../../manual/bond_cpp.html#understanding-bondedt)
|
||||||
|
template <typename Reader>
|
||||||
|
class bonded<void, Reader>
|
||||||
|
: detail::nonassignable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// @brief Initialize from serialized data and runtime schema
|
||||||
|
bonded(Reader data, const RuntimeSchema& schema, bool base = false)
|
||||||
|
: _data(data),
|
||||||
|
_schema(schema),
|
||||||
|
_skip(true),
|
||||||
|
_base(base)
|
||||||
|
{}
|
||||||
|
|
||||||
|
/// @brief Copy constructor
|
||||||
|
bonded(const bonded& other)
|
||||||
|
: _data(other._data),
|
||||||
|
_schema(other._schema),
|
||||||
|
_skip(true),
|
||||||
|
_base(false)
|
||||||
|
{}
|
||||||
|
|
||||||
|
/// @brief Explicit cast from bonded<T>
|
||||||
|
template <typename T, typename ReaderT>
|
||||||
|
explicit bonded(const bonded<T, ReaderT>& other)
|
||||||
|
: _data(other._data),
|
||||||
|
_schema(other._schema.get() ? other._schema : GetRuntimeSchema<T>()),
|
||||||
|
_skip(true),
|
||||||
|
_base(false)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef BOND_NO_CXX11_RVALUE_REFERENCES
|
||||||
|
/// @brief Move constructor
|
||||||
|
bonded(bonded&& other)
|
||||||
|
: _data(std::move(other._data)),
|
||||||
|
_schema(std::move(other._schema)),
|
||||||
|
_skip(std::move(other._skip)),
|
||||||
|
_base(std::move(other._base))
|
||||||
|
{
|
||||||
|
other._skip = false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
~bonded()
|
||||||
|
{
|
||||||
|
// Skip the struct if it wasn't deserialized
|
||||||
|
if (_skip)
|
||||||
|
detail::Skip(_data, *this, std::nothrow);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Serialize bonded using specified protocol writer
|
||||||
|
template <typename Writer>
|
||||||
|
typename boost::disable_if<uses_marshaled_bonded<typename Writer::Reader> >::type
|
||||||
|
Serialize(Writer& output) const
|
||||||
|
{
|
||||||
|
Apply(SerializeTo(output), *this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Writer>
|
||||||
|
typename boost::enable_if<uses_marshaled_bonded<typename Writer::Reader> >::type
|
||||||
|
Serialize(Writer& output) const
|
||||||
|
{
|
||||||
|
if (_schema.GetType().bonded_type)
|
||||||
|
detail::MarshalToBlob(*this, output);
|
||||||
|
else
|
||||||
|
Apply(SerializeTo(output), *this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Deserialize an object of type T
|
||||||
|
template <typename T>
|
||||||
|
T Deserialize() const
|
||||||
|
{
|
||||||
|
T tmp;
|
||||||
|
Apply(To<T>(tmp), *this);
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Deserialize to an object of type T
|
||||||
|
template <typename T>
|
||||||
|
void Deserialize(T& var) const
|
||||||
|
{
|
||||||
|
Apply(To<T>(var), *this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Deserialize to a bonded<T>
|
||||||
|
template <typename T>
|
||||||
|
void Deserialize(bonded<T>& var) const
|
||||||
|
{
|
||||||
|
if (uses_marshaled_bonded<Reader>::value && _schema.GetType().bonded_type)
|
||||||
|
{
|
||||||
|
bonded<T> tmp;
|
||||||
|
_SelectProtocolAndApply(boost::ref(tmp));
|
||||||
|
tmp.Deserialize(var);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var = bonded<T>(*this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Skip struct data in the underlying payload
|
||||||
|
void Skip()
|
||||||
|
{
|
||||||
|
_skip = false;
|
||||||
|
detail::Skip(_data, *this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Transform, typename U, typename ReaderT>
|
||||||
|
friend typename boost::disable_if<detail::need_double_pass<Transform>, bool>::type inline
|
||||||
|
Apply(const Transform& transform, const bonded<U, ReaderT>& bonded);
|
||||||
|
|
||||||
|
template <typename Transform, typename U, typename ReaderT>
|
||||||
|
friend typename boost::enable_if<detail::need_double_pass<Transform>, bool>::type inline
|
||||||
|
Apply(const Transform& transform, const bonded<U, ReaderT>& bonded);
|
||||||
|
|
||||||
|
template <typename T, typename ReaderT>
|
||||||
|
friend class bonded;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Apply transform to serialized data
|
||||||
|
template <typename Transform>
|
||||||
|
bool _Apply(const Transform& transform) const
|
||||||
|
{
|
||||||
|
if (uses_marshaled_bonded<Reader>::value && _schema.GetType().bonded_type)
|
||||||
|
{
|
||||||
|
return _SelectProtocolAndApply(transform);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_skip = false;
|
||||||
|
return detail::Parse<void>(transform, _data, _schema, NULL, _base);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Transform>
|
||||||
|
typename boost::enable_if<uses_marshaled_bonded<Reader, Transform>, bool>::type
|
||||||
|
_SelectProtocolAndApply(const Transform& transform) const
|
||||||
|
{
|
||||||
|
_skip = false;
|
||||||
|
InputBuffer input(detail::ReadBlob(_data));
|
||||||
|
return SelectProtocolAndApply(_schema, input, transform).second;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Transform>
|
||||||
|
typename boost::disable_if<uses_marshaled_bonded<Reader, Transform>, bool>::type
|
||||||
|
_SelectProtocolAndApply(const Transform&) const
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Reader _data;
|
||||||
|
const RuntimeSchema _schema;
|
||||||
|
mutable bool _skip;
|
||||||
|
const bool _base;
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
|
@ -0,0 +1,31 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "bond.h"
|
||||||
|
#include "detail/cmdargs.h"
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
namespace cmd
|
||||||
|
{
|
||||||
|
// Output usage help to std::cerr
|
||||||
|
template <typename Options>
|
||||||
|
void ShowUsage(const char* program)
|
||||||
|
{
|
||||||
|
Options options;
|
||||||
|
Apply(detail::Usage(program), options);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read command line arguments
|
||||||
|
template <typename Options>
|
||||||
|
Options GetArgs(int argc, char** argv, bool partial = false)
|
||||||
|
{
|
||||||
|
Options options;
|
||||||
|
Apply(detail::CmdArg(argc, argv, partial), options);
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <boost/config.hpp>
|
||||||
|
|
||||||
|
#if defined(BOOST_NO_CXX11_DEFAULTED_FUNCTIONS)
|
||||||
|
#define BOND_NO_CXX11_DEFAULTED_FUNCTIONS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(BOOST_NO_CXX11_AUTO_DECLARATIONS) || defined(BOOST_NO_AUTO_DECLARATIONS)
|
||||||
|
#define BOND_NO_CXX11_AUTO_DECLARATIONS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(BOOST_NO_CXX11_AUTO_MULTIDECLARATIONS) || defined(BOOST_NO_AUTO_MULTIDECLARATIONS)
|
||||||
|
#define BOND_NO_CXX11_AUTO_MULTIDECLARATIONS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(BOOST_NO_CXX11_LAMBDAS) || defined(BOOST_NO_LAMBDAS)
|
||||||
|
#define BOND_NO_CXX11_LAMBDAS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(BOOST_NO_CXX11_NOEXCEPT) || defined(BOOST_NO_NOEXCEPT)
|
||||||
|
#define BOND_NO_CXX11_NOEXCEPT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(BOOST_NO_CXX11_NULLPTR) || defined(BOOST_NO_NULLPTR)
|
||||||
|
#define BOND_NO_CXX11_NULLPTR
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES) || defined(BOOST_NO_RVALUE_REFERENCES)
|
||||||
|
#define BOND_NO_CXX11_RVALUE_REFERENCES
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(BOOST_NO_CXX11_SCOPED_ENUMS) || defined(BOOST_NO_SCOPED_ENUMS)
|
||||||
|
#define BOND_NO_CXX11_SCOPED_ENUMS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) || defined(BOOST_NO_VARIADIC_TEMPLATES)
|
||||||
|
#define BOND_NO_CXX11_VARIADIC_TEMPLATES
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(BOOST_NO_CXX11_HDR_CODECVT) || defined(BOOST_NO_0X_HDR_CODECVT)
|
||||||
|
#define BOND_NO_CXX11_HDR_CODECVT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(BOOST_NO_CXX11_HDR_TYPE_TRAITS) && (_CPPLIB_VER < 520) && !defined(__GXX_EXPERIMENTAL_CXX0X__)
|
||||||
|
#define BOND_NO_CXX11_HDR_TYPE_TRAITS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// std::once seems problematic on Linux (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60662)
|
||||||
|
// For now we use std::call_once only on MSVC and boost::call_once on GCC/Clang.
|
||||||
|
#if defined(BOOST_NO_CXX11_HDR_MUTEX) || !defined(_MSC_VER)
|
||||||
|
#define BOND_NO_CX11_HDR_MUTEX
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(BOOST_NO_CXX11_ALLOCATOR)
|
||||||
|
#define BOND_NO_CXX11_ALLOCATOR
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#define BOND_CALL __cdecl
|
||||||
|
#define BOND_NO_INLINE __declspec(noinline)
|
||||||
|
#elif defined(__GNUC__) && defined(__i386) && !defined(__INTEL_COMPILER)
|
||||||
|
#define BOND_CALL __attribute__((cdecl))
|
||||||
|
#define BOND_NO_INLINE __attribute__((noinline))
|
||||||
|
#else
|
||||||
|
#define BOND_CALL
|
||||||
|
#define BOND_NO_INLINE __attribute__((noinline))
|
||||||
|
#endif
|
|
@ -0,0 +1,124 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "traits.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
|
||||||
|
//
|
||||||
|
// container traits - specialize for custom containers
|
||||||
|
//
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
is_set_container
|
||||||
|
: false_type {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
is_map_container
|
||||||
|
: false_type {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
is_list_container
|
||||||
|
: false_type {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
require_modify_element
|
||||||
|
: false_type {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
is_string
|
||||||
|
: false_type {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
is_wstring
|
||||||
|
: false_type {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
element_type
|
||||||
|
{
|
||||||
|
typedef typename T::value_type type;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// enumerators - specialize for custom containers
|
||||||
|
//
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class enumerator;
|
||||||
|
#if 0
|
||||||
|
{
|
||||||
|
explicit enumerator(T& list);
|
||||||
|
bool more() const;
|
||||||
|
typename element_type<T>::type& next();
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class const_enumerator;
|
||||||
|
#if 0
|
||||||
|
{
|
||||||
|
explicit const_enumerator(const T& container);
|
||||||
|
bool more() const;
|
||||||
|
const typename element_type<T>::type& next();
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// container functions - overload for custom containers
|
||||||
|
//
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
template <typename T>
|
||||||
|
uint32_t container_size(const T& container);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void resize_list(T& list, uint32_t size);
|
||||||
|
|
||||||
|
template <typename T, typename E, typename F>
|
||||||
|
void modify_element(T& list, E& element, F deserialize);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void clear_set(T& set);
|
||||||
|
|
||||||
|
template <typename S, typename T>
|
||||||
|
void set_insert(S& set, const T& item);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void clear_map(T& map);
|
||||||
|
|
||||||
|
template <typename M, typename K, typename T>
|
||||||
|
T& mapped_at(M& map, const K& key);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//
|
||||||
|
// string functions - overload for custom strings
|
||||||
|
//
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
template<typename C, typename T>
|
||||||
|
const C* string_data(const T& str);
|
||||||
|
|
||||||
|
template<typename C, typename T>
|
||||||
|
C* string_data(T& str);
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
uint32_t string_length(const T& str);
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void resize_string(T& str, uint32_t size);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
};
|
|
@ -0,0 +1,8 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "stl_containers.h"
|
||||||
|
#include "maybe.h"
|
||||||
|
#include "bond_fwd.h"
|
|
@ -0,0 +1,31 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "traits.h"
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
is_protocol_enabled
|
||||||
|
: false_type {};
|
||||||
|
|
||||||
|
|
||||||
|
struct protocols;
|
||||||
|
|
||||||
|
// User can modify set of protocols by specializing customize<protocols>
|
||||||
|
template <typename> struct
|
||||||
|
customize
|
||||||
|
{
|
||||||
|
template <typename T> struct
|
||||||
|
modify
|
||||||
|
{
|
||||||
|
typedef T type;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,561 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#pragma warning (push)
|
||||||
|
// warning C4512: 'boost::transform_iterator<UnaryFunc,Iterator,Reference,Value>' : assignment operator could not be generated
|
||||||
|
#pragma warning (disable : 4512)
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <boost/algorithm/string.hpp>
|
||||||
|
#include <boost/lexical_cast.hpp>
|
||||||
|
#include <boost/tokenizer.hpp>
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
namespace cmd
|
||||||
|
{
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
// Enum types already have generated overload for ToString.
|
||||||
|
// For other types we use boost::lexical_cast.
|
||||||
|
template <typename T>
|
||||||
|
std::string ToString(const T& value)
|
||||||
|
{
|
||||||
|
return boost::lexical_cast<std::string>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// lexical_cast treats uint8_t and int8_t as characters, not numbers
|
||||||
|
std::string ToString(const uint8_t& value)
|
||||||
|
{
|
||||||
|
return boost::lexical_cast<std::string>(static_cast<int>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ToString(const int8_t& value)
|
||||||
|
{
|
||||||
|
return boost::lexical_cast<std::string>(static_cast<int>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
class Metadata : boost::noncopyable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Metadata(const bond::Metadata& metadata)
|
||||||
|
: metadata(metadata)
|
||||||
|
{}
|
||||||
|
|
||||||
|
std::string Flag() const
|
||||||
|
{
|
||||||
|
std::string flag(metadata.name);
|
||||||
|
std::replace(flag.begin(), flag.end(), '_', '-');
|
||||||
|
return "--" + flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Param() const
|
||||||
|
{
|
||||||
|
if (IsNaked())
|
||||||
|
return boost::to_upper_copy(metadata.name);
|
||||||
|
else
|
||||||
|
return Flag() + "=" + boost::to_upper_copy(metadata.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Abbr() const
|
||||||
|
{
|
||||||
|
if (HasAttribute("abbr"))
|
||||||
|
return "-" + Attribute("abbr");
|
||||||
|
else
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
std::string Help() const
|
||||||
|
{
|
||||||
|
std::string help;
|
||||||
|
|
||||||
|
if (HasAttribute("help"))
|
||||||
|
help = Attribute("help");
|
||||||
|
else
|
||||||
|
help = HelpFromType<T>();
|
||||||
|
|
||||||
|
std::string default_value = Default<T>();
|
||||||
|
|
||||||
|
if (!default_value.empty())
|
||||||
|
{
|
||||||
|
if (!help.empty())
|
||||||
|
help += ", ";
|
||||||
|
|
||||||
|
help += "default " + default_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return help;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Help() const
|
||||||
|
{
|
||||||
|
return Attribute("help");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsNaked() const
|
||||||
|
{
|
||||||
|
return HasAttribute("naked");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsOptional() const
|
||||||
|
{
|
||||||
|
return metadata.modifier == bond::Optional;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool HasAttribute(const std::string& attr) const
|
||||||
|
{
|
||||||
|
return metadata.attributes.end() != metadata.attributes.find(attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Attribute(const std::string& attr) const
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string>::const_iterator it;
|
||||||
|
|
||||||
|
it = metadata.attributes.find(attr);
|
||||||
|
|
||||||
|
if (it != metadata.attributes.end())
|
||||||
|
return it->second;
|
||||||
|
else
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename boost::disable_if_c<bond::is_list_container<T>::value
|
||||||
|
|| is_enum<T>::value, std::string>::type
|
||||||
|
HelpFromType() const
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if<is_enum<T>, std::string>::type
|
||||||
|
HelpFromType() const
|
||||||
|
{
|
||||||
|
std::string enums;
|
||||||
|
|
||||||
|
const std::map<std::string, T>& names = bond::GetEnumNames<T>();
|
||||||
|
|
||||||
|
for (typename std::map<std::string, T>::const_iterator it = names.begin(); it != names.end(); ++it)
|
||||||
|
{
|
||||||
|
if (!enums.empty())
|
||||||
|
enums += " | ";
|
||||||
|
enums += it->first;
|
||||||
|
}
|
||||||
|
return enums;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if<bond::is_list_container<T>, std::string>::type
|
||||||
|
HelpFromType() const
|
||||||
|
{
|
||||||
|
std::string help = HelpFromType<typename bond::element_type<T>::type>();
|
||||||
|
|
||||||
|
if (!help.empty())
|
||||||
|
help = "comma-separated list of: " + help;
|
||||||
|
|
||||||
|
return help;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename boost::disable_if<bond::is_basic_type<T>, std::string>::type
|
||||||
|
Default() const
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if<bond::is_basic_type<T>, std::string>::type
|
||||||
|
Default() const
|
||||||
|
{
|
||||||
|
if (metadata.modifier == bond::Optional && !metadata.default_value.nothing)
|
||||||
|
{
|
||||||
|
T var;
|
||||||
|
|
||||||
|
bond::detail::VariantGet(metadata.default_value, var);
|
||||||
|
|
||||||
|
return ToString(var);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
const bond::Metadata& metadata;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Options
|
||||||
|
class Options
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// ctor
|
||||||
|
Options(int argc, char** argv)
|
||||||
|
{
|
||||||
|
for (int i = 1; i < argc; ++i)
|
||||||
|
{
|
||||||
|
std::string value = argv[i];
|
||||||
|
std::string name;
|
||||||
|
|
||||||
|
if (value[0] == '-' && value != "-" && value != "--")
|
||||||
|
{
|
||||||
|
size_t pos = value.find_first_of("=:");
|
||||||
|
|
||||||
|
name = value.substr(0, pos);
|
||||||
|
|
||||||
|
// unpack multiple abbreviated options in single param, e.g.:
|
||||||
|
// -fxd -> -f -x -d
|
||||||
|
if (name[1] != '-')
|
||||||
|
while(name.length() > 2)
|
||||||
|
{
|
||||||
|
params.push_back(Param(name.substr(0, 2), ""));
|
||||||
|
name.erase(1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos != std::string::npos)
|
||||||
|
value = value.substr(pos + 1);
|
||||||
|
else
|
||||||
|
value = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
params.push_back(Param(name, value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFlag
|
||||||
|
bool GetFlag(const std::string& flag, const std::string& abbr)
|
||||||
|
{
|
||||||
|
Params::iterator it = FindParam(flag, abbr);
|
||||||
|
|
||||||
|
if (it != params.end())
|
||||||
|
{
|
||||||
|
if (!it->value.empty())
|
||||||
|
throw std::runtime_error("Invalid parameter(s):\n " + it->value);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetParam
|
||||||
|
bool GetParam(std::string& value)
|
||||||
|
{
|
||||||
|
return GetParam("", "", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetParam
|
||||||
|
bool GetParam(const std::string& flag, const std::string& abbr, std::string& value)
|
||||||
|
{
|
||||||
|
Params::iterator it = FindParam(flag, abbr);
|
||||||
|
|
||||||
|
if (it != params.end())
|
||||||
|
{
|
||||||
|
value = it->value;
|
||||||
|
|
||||||
|
if(value.empty() && ++it != params.end() && !it->used && it->name.empty())
|
||||||
|
{
|
||||||
|
value = it->value;
|
||||||
|
it->used = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Leftovers
|
||||||
|
std::string Leftovers()
|
||||||
|
{
|
||||||
|
std::string leftovers;
|
||||||
|
|
||||||
|
for (Params::iterator it = params.begin(); it != params.end(); ++it)
|
||||||
|
if (!it->used)
|
||||||
|
{
|
||||||
|
leftovers += " " + it->name;
|
||||||
|
|
||||||
|
if (!it->name.empty() && !it->value.empty())
|
||||||
|
leftovers += "=";
|
||||||
|
|
||||||
|
leftovers += it->value + "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
return leftovers;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Param
|
||||||
|
{
|
||||||
|
Param(const std::string& name, const std::string& value)
|
||||||
|
: name(name),
|
||||||
|
value(value),
|
||||||
|
used(false)
|
||||||
|
{}
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
std::string value;
|
||||||
|
bool used;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::list<Param> Params;
|
||||||
|
|
||||||
|
Params::iterator FindParam(const std::string& flag, const std::string& abbr)
|
||||||
|
{
|
||||||
|
Params::iterator it;
|
||||||
|
|
||||||
|
for (it = params.begin(); it != params.end(); ++it)
|
||||||
|
if (!it->used && (it->name == flag || (it->name == abbr && !abbr.empty())))
|
||||||
|
{
|
||||||
|
it->used = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
Params params;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// CmdArg
|
||||||
|
class CmdArg
|
||||||
|
: public bond::ModifyingTransform
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CmdArg(int argc, char** argv, bool partial = false)
|
||||||
|
: options(boost::make_shared<Options>(argc, argv)),
|
||||||
|
partial(partial)
|
||||||
|
{}
|
||||||
|
|
||||||
|
CmdArg(const boost::shared_ptr<Options>& options, bool partial = false)
|
||||||
|
: options(options),
|
||||||
|
partial(partial)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void Begin(const detail::Metadata& /*metadata*/) const
|
||||||
|
{}
|
||||||
|
|
||||||
|
void End() const
|
||||||
|
{
|
||||||
|
if (!partial)
|
||||||
|
{
|
||||||
|
std::string leftovers = options->Leftovers();
|
||||||
|
|
||||||
|
if (!leftovers.empty())
|
||||||
|
throw std::runtime_error("Invalid parameter(s):\n" + leftovers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool Base(T& value) const
|
||||||
|
{
|
||||||
|
return bond::Apply(CmdArg(options, true), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if<bond::is_list_container<T>, bool>::type
|
||||||
|
Field(uint16_t /*id*/, const detail::Metadata& metadata, T& var) const
|
||||||
|
{
|
||||||
|
while(GetParam(metadata, var));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename boost::disable_if<bond::is_list_container<T>, bool>::type
|
||||||
|
Field(uint16_t /*id*/, const detail::Metadata& metadata, T& var) const
|
||||||
|
{
|
||||||
|
GetParam(metadata, var);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Parse enum
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if<is_enum<T> >::type
|
||||||
|
Parse(const std::string& value, T& var) const
|
||||||
|
{
|
||||||
|
if (!ToEnum(var, value.c_str()))
|
||||||
|
throw std::runtime_error("Invalid parameter(s):\n " + value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse number
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if<boost::is_arithmetic<T> >::type
|
||||||
|
Parse(const std::string& value, T& var) const
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var = boost::lexical_cast<T>(value);
|
||||||
|
}
|
||||||
|
catch(const std::exception&)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Invalid parameter(s):\n " + value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse string
|
||||||
|
void Parse(const std::string& value, std::string& var) const
|
||||||
|
{
|
||||||
|
var = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse list
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if<bond::is_list_container<T> >::type
|
||||||
|
Parse(const std::string& value, T& var) const
|
||||||
|
{
|
||||||
|
typedef boost::tokenizer<boost::escaped_list_separator<char> > tokenizer;
|
||||||
|
|
||||||
|
tokenizer tok(value);
|
||||||
|
|
||||||
|
for(tokenizer::iterator it = tok.begin(); it != tok.end(); ++it)
|
||||||
|
{
|
||||||
|
typename bond::element_type<T>::type tmp;
|
||||||
|
|
||||||
|
Parse(*it, tmp);
|
||||||
|
var.push_back(tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void Parse(const std::string& value, maybe<T>& var) const
|
||||||
|
{
|
||||||
|
Parse(value, var.set_value());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// bool param
|
||||||
|
bool GetParam(const detail::Metadata& metadata, bool& var) const
|
||||||
|
{
|
||||||
|
// required or naked bool flags don't make sense
|
||||||
|
BOOST_ASSERT(metadata.IsOptional());
|
||||||
|
BOOST_ASSERT(!metadata.IsNaked());
|
||||||
|
|
||||||
|
return (var = options->GetFlag(metadata.Flag(), metadata.Abbr()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// generic param
|
||||||
|
template <typename T>
|
||||||
|
bool GetParam(const detail::Metadata& metadata, T& var) const
|
||||||
|
{
|
||||||
|
std::string value;
|
||||||
|
|
||||||
|
if (metadata.IsNaked())
|
||||||
|
options->GetParam(value);
|
||||||
|
else
|
||||||
|
options->GetParam(metadata.Flag(), metadata.Abbr(), value);
|
||||||
|
|
||||||
|
if (value.empty())
|
||||||
|
{
|
||||||
|
if(!metadata.IsOptional())
|
||||||
|
throw std::runtime_error("Required parameter " + metadata.Param() + " missing.");
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Parse(value, var);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::shared_ptr<Options> options;
|
||||||
|
bool partial;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class Usage
|
||||||
|
: public bond::SerializingTransform
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Usage(const char* program, std::ostream& out = std::cerr)
|
||||||
|
: program(program),
|
||||||
|
out(out)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void Begin(const detail::Metadata& metadata) const
|
||||||
|
{
|
||||||
|
std::string help = metadata.Help();
|
||||||
|
|
||||||
|
if (!help.empty())
|
||||||
|
out << std::endl << "Usage: " << program << " " << help << std::endl << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void End() const
|
||||||
|
{}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool Base(const T& value) const
|
||||||
|
{
|
||||||
|
bond::Apply(*this, value);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Field(uint16_t /*id*/, const detail::Metadata& metadata, const bool& /*value*/) const
|
||||||
|
{
|
||||||
|
Print(metadata.Flag(), metadata.Abbr(), metadata.Help());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool Field(uint16_t /*id*/, const detail::Metadata& metadata, const bond::maybe<T>& /*value*/) const
|
||||||
|
{
|
||||||
|
std::string help = metadata.Help<T>();
|
||||||
|
|
||||||
|
Print(metadata.Param(), metadata.Abbr(), help);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool Field(uint16_t /*id*/, const detail::Metadata& metadata, const T& /*value*/) const
|
||||||
|
{
|
||||||
|
std::string help = metadata.Help<T>();
|
||||||
|
|
||||||
|
Print(metadata.Param(), metadata.Abbr(), help);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void Print(const std::string& arg, const std::string& abbr, const std::string& help) const
|
||||||
|
{
|
||||||
|
int indent = std::max(30, static_cast<int>(arg.size()) + 5);
|
||||||
|
std::string formated = FormatHelp(help, indent);
|
||||||
|
out << " ";
|
||||||
|
out.width(2);
|
||||||
|
out << abbr << " ";
|
||||||
|
out.setf(std::ios::left, std::ios::adjustfield);
|
||||||
|
out.width(indent - 4);
|
||||||
|
out << arg << formated << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string FormatHelp(const std::string& help, int indent) const
|
||||||
|
{
|
||||||
|
std::string formated = help;
|
||||||
|
int i = 79 - indent;
|
||||||
|
|
||||||
|
for (int begin = 0; i < static_cast<int>(formated.length()); begin += 80, i = begin + 79 - indent)
|
||||||
|
{
|
||||||
|
while (i >= begin && !isspace(formated[i]))
|
||||||
|
--i;
|
||||||
|
|
||||||
|
while (i < static_cast<int>(formated.length()) && isspace(formated[i]))
|
||||||
|
++i;
|
||||||
|
|
||||||
|
formated.insert(i, begin + 80 - i, ' ');
|
||||||
|
formated[i + begin + 79 - i - indent] = '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
return formated;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string program;
|
||||||
|
std::ostream& out;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
} // namespace cmd
|
||||||
|
} // namespace bond
|
|
@ -0,0 +1,56 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "omit_default.h"
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class OptionalDefault : boost::noncopyable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
OptionalDefault(const T& var)
|
||||||
|
: _var(var),
|
||||||
|
_default(true)
|
||||||
|
{
|
||||||
|
boost::mpl::for_each<typename schema<T>::type::fields>(boost::ref(*this));
|
||||||
|
}
|
||||||
|
|
||||||
|
operator bool()
|
||||||
|
{
|
||||||
|
return _default;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Field>
|
||||||
|
typename boost::enable_if_c<!is_bond_type<typename Field::field_type>::value
|
||||||
|
&& is_same<typename Field::field_modifier,
|
||||||
|
reflection::optional_field_modifier>::value>::type
|
||||||
|
operator()(const Field&)
|
||||||
|
{
|
||||||
|
_default = _default && is_default(Field::GetVariable(_var), Field::metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Field>
|
||||||
|
typename boost::disable_if_c<!is_bond_type<typename Field::field_type>::value
|
||||||
|
&& is_same<typename Field::field_modifier,
|
||||||
|
reflection::optional_field_modifier>::value>::type
|
||||||
|
operator()(const Field&)
|
||||||
|
{}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const T& _var;
|
||||||
|
bool _default;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
} // namespace bond
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
template <typename Transform, typename Enable = void> struct
|
||||||
|
need_double_pass
|
||||||
|
: false_type {};
|
||||||
|
|
||||||
|
template <typename Transform> struct
|
||||||
|
need_double_pass<
|
||||||
|
Transform,
|
||||||
|
typename boost::enable_if_c<!is_same<typename Transform::writer_type,
|
||||||
|
typename Transform::writer_type::Pass0>::value>::type
|
||||||
|
> : true_type {};
|
||||||
|
|
||||||
|
template <typename Transform, typename T>
|
||||||
|
inline bool DoublePassApply(const Transform& transform, const T& value)
|
||||||
|
{
|
||||||
|
typedef typename Transform::writer_type Writer;
|
||||||
|
|
||||||
|
typename Writer::Pass0::Buffer output;
|
||||||
|
typename Writer::Pass0 pass0(output, transform.Serializer<Writer>::_output);
|
||||||
|
|
||||||
|
Apply(transform.Rebind(pass0), value);
|
||||||
|
return transform.Serializer<Writer>::_output.WithPass0(pass0), Apply(transform, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
} // namespace bond
|
|
@ -0,0 +1,191 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Input>
|
||||||
|
struct base_input
|
||||||
|
{
|
||||||
|
BOOST_STATIC_ASSERT(is_reference<Input>::value);
|
||||||
|
typedef Input type;
|
||||||
|
static type from(type input)
|
||||||
|
{
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename T, typename Enable = void> struct
|
||||||
|
hierarchy_depth
|
||||||
|
{
|
||||||
|
static const uint16_t value = 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
hierarchy_depth<T, typename boost::enable_if<is_class<typename schema<typename T::base>::type> >::type>
|
||||||
|
{
|
||||||
|
static const uint16_t value = 1 + hierarchy_depth<typename schema<typename T::base>::type>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
expected_depth
|
||||||
|
{
|
||||||
|
static const uint16_t value = 0xffff;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
expected_depth<bond::To<T> >
|
||||||
|
{
|
||||||
|
static const uint16_t value = hierarchy_depth<typename schema<T>::type>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Input, typename T = void, typename Enable = void> struct
|
||||||
|
is_reader
|
||||||
|
: false_type {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Input, typename T> struct
|
||||||
|
is_reader<Input&, T, typename boost::enable_if<is_class<typename Input::Parser> >::type>
|
||||||
|
: true_type {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Base, typename T>
|
||||||
|
inline Base& base_cast(T& obj)
|
||||||
|
{
|
||||||
|
return static_cast<Base&>(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Base, typename T>
|
||||||
|
inline const Base& base_cast(const T& obj)
|
||||||
|
{
|
||||||
|
return static_cast<const Base&>(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Input, typename Parser>
|
||||||
|
class ParserInheritance
|
||||||
|
: boost::noncopyable
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
ParserInheritance(Input input, bool base)
|
||||||
|
: _input(input),
|
||||||
|
_base(base)
|
||||||
|
{}
|
||||||
|
|
||||||
|
// use compile-time schema
|
||||||
|
template <typename T, typename Transform>
|
||||||
|
typename boost::enable_if_c<(hierarchy_depth<T>::value > expected_depth<Transform>::value), bool>::type
|
||||||
|
Read(const T&, const Transform& transform)
|
||||||
|
{
|
||||||
|
typename base_input<Input>::type base(base_input<Input>::from(_input));
|
||||||
|
|
||||||
|
// The hierarchy of the payload schema is deeper than what the transform "expects".
|
||||||
|
// We recursively find the matching level to start parsing from.
|
||||||
|
// After we finish parsing the expected parts of the hierarchy, we give
|
||||||
|
// the parser a chance to skip the unexpected parts.
|
||||||
|
detail::StructBegin(_input, true);
|
||||||
|
|
||||||
|
bool result = Parser(base, _base).Read(typename schema<typename T::base>::type(), transform);
|
||||||
|
|
||||||
|
detail::StructEnd(_input, true);
|
||||||
|
|
||||||
|
static_cast<Parser*>(this)->SkipFields(typename boost::mpl::begin<typename T::fields>::type());
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Transform>
|
||||||
|
typename boost::disable_if_c<(hierarchy_depth<T>::value > expected_depth<Transform>::value), bool>::type
|
||||||
|
Read(const T&, const Transform& transform)
|
||||||
|
{
|
||||||
|
// We are at the expected level within the hierarchy.
|
||||||
|
// First we recurse into base structs (serialized data starts at the top of the hierarchy)
|
||||||
|
// and then we read to the transform the fields of the top level struct.
|
||||||
|
transform.Begin(T::metadata);
|
||||||
|
ReadBase(base_class<T>(), transform);
|
||||||
|
bool result = static_cast<Parser*>(this)->ReadFields(typename boost::mpl::begin<typename T::fields>::type(), transform);
|
||||||
|
transform.End();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Base, typename Transform>
|
||||||
|
typename boost::enable_if<is_reader<Input, Base>, bool>::type
|
||||||
|
ReadBase(const Base*, const Transform& transform)
|
||||||
|
{
|
||||||
|
typename base_input<Input>::type base(base_input<Input>::from(_input));
|
||||||
|
|
||||||
|
return transform.Base(bonded<Base, Input>(base, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Base, typename Transform>
|
||||||
|
typename boost::disable_if<is_reader<Input, Base>, bool>::type
|
||||||
|
ReadBase(const Base*, const Transform& transform)
|
||||||
|
{
|
||||||
|
return transform.Base(base_cast<Base>(_input));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Transform>
|
||||||
|
bool ReadBase(const no_base*, const Transform&)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// use runtime schema
|
||||||
|
template <typename Transform>
|
||||||
|
bool Read(const RuntimeSchema& schema, const Transform& transform)
|
||||||
|
{
|
||||||
|
// The logic is the same as for compile-time schemas, described in the comments above.
|
||||||
|
bool result;
|
||||||
|
|
||||||
|
typename base_input<Input>::type base(base_input<Input>::from(_input));
|
||||||
|
|
||||||
|
if (schema_depth(schema) > expected_depth<Transform>::value)
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(schema.HasBase());
|
||||||
|
|
||||||
|
detail::StructBegin(_input, true);
|
||||||
|
|
||||||
|
result = Parser(base, _base).Read(schema.GetBaseSchema(), transform);
|
||||||
|
|
||||||
|
detail::StructEnd(_input, true);
|
||||||
|
|
||||||
|
static_cast<Parser*>(this)->SkipFields(schema);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
transform.Begin(schema.GetStruct().metadata);
|
||||||
|
|
||||||
|
if (schema.HasBase())
|
||||||
|
transform.Base(bonded<void, Input>(base, schema.GetBaseSchema(), true));
|
||||||
|
|
||||||
|
result = static_cast<Parser*>(this)->ReadFields(schema, transform);
|
||||||
|
transform.End();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Input _input;
|
||||||
|
const bool _base;
|
||||||
|
};
|
||||||
|
|
||||||
|
}; // namespace detail
|
||||||
|
|
||||||
|
}; // namespace bond
|
|
@ -0,0 +1,40 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <bond/stream/output_buffer.h>
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename Reader>
|
||||||
|
blob ReadBlob(Reader& reader)
|
||||||
|
{
|
||||||
|
uint32_t size;
|
||||||
|
blob buffer;
|
||||||
|
|
||||||
|
reader.Read(size);
|
||||||
|
reader.Read(buffer, size);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Writer>
|
||||||
|
void MarshalToBlob(const T& obj, Writer& writer)
|
||||||
|
{
|
||||||
|
OutputBuffer output;
|
||||||
|
CompactBinaryWriter<OutputBuffer> cbw(output);
|
||||||
|
|
||||||
|
Marshal(obj, cbw);
|
||||||
|
blob data = output.GetBuffer();
|
||||||
|
|
||||||
|
writer.Write(data.size());
|
||||||
|
writer.Write(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
} // namespace bond
|
|
@ -0,0 +1,381 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "tags.h"
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
// Variant operator==
|
||||||
|
|
||||||
|
/// @brief Compares \a variant for equality against the provided
|
||||||
|
/// signed integer or enum \a value.
|
||||||
|
template <typename SignedT>
|
||||||
|
inline
|
||||||
|
typename boost::enable_if<is_signed_int_or_enum<SignedT>, bool>::type
|
||||||
|
operator==(const Variant& variant, SignedT value)
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(!variant.nothing);
|
||||||
|
return value == static_cast<SignedT>(variant.int_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Compares \a variant for equality against the provided
|
||||||
|
/// unsigned integer \a value.
|
||||||
|
template <typename UnsignedT>
|
||||||
|
inline
|
||||||
|
typename boost::enable_if<is_unsigned<UnsignedT>, bool>::type
|
||||||
|
operator==(const Variant& variant, UnsignedT value)
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(!variant.nothing);
|
||||||
|
return value == static_cast<UnsignedT>(variant.uint_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Compares \a variant for equality against the provided
|
||||||
|
/// boolean \a value.
|
||||||
|
inline bool
|
||||||
|
operator==(const Variant& variant, bool value)
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(!variant.nothing);
|
||||||
|
BOOST_STATIC_ASSERT((is_unsigned<bool>::value));
|
||||||
|
|
||||||
|
return value == !!variant.uint_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Compares \a variant for equality against the provided
|
||||||
|
/// floating point \a value.
|
||||||
|
inline bool
|
||||||
|
operator==(const Variant& variant, double value)
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(!variant.nothing);
|
||||||
|
return value == variant.double_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Compares \a variant for equality against the provided
|
||||||
|
/// string \a value.
|
||||||
|
inline bool
|
||||||
|
operator==(const Variant& variant, const char* value)
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(!variant.nothing);
|
||||||
|
return value == variant.string_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Compares \a variant for equality against the provided
|
||||||
|
/// string \a value.
|
||||||
|
inline bool
|
||||||
|
operator==(const Variant& variant, const std::string& value)
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(!variant.nothing);
|
||||||
|
return value == variant.string_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Compares \a variant for equality against the provided
|
||||||
|
/// wide string \a value.
|
||||||
|
inline bool
|
||||||
|
operator==(const Variant& variant, const wchar_t* value)
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(!variant.nothing);
|
||||||
|
return value == variant.wstring_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Compares \a variant for equality against the provided
|
||||||
|
/// wide string \a value.
|
||||||
|
inline bool
|
||||||
|
operator==(const Variant& variant, const std::wstring& value)
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(!variant.nothing);
|
||||||
|
return value == variant.wstring_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
// VariantSet
|
||||||
|
template <typename T>
|
||||||
|
inline
|
||||||
|
typename boost::enable_if<is_unsigned<T> >::type
|
||||||
|
VariantSet(bond::Variant& variant, const T& value)
|
||||||
|
{
|
||||||
|
variant.uint_value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline
|
||||||
|
typename boost::enable_if<is_signed_int_or_enum<T> >::type
|
||||||
|
VariantSet(bond::Variant& variant, const T& value)
|
||||||
|
{
|
||||||
|
variant.int_value = static_cast<int64_t>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline void
|
||||||
|
VariantSet(bond::Variant& variant, const char* value)
|
||||||
|
{
|
||||||
|
variant.string_value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline void
|
||||||
|
VariantSet(bond::Variant& variant, const wchar_t* value)
|
||||||
|
{
|
||||||
|
variant.wstring_value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void
|
||||||
|
VariantSet(bond::Variant& variant, const double& value)
|
||||||
|
{
|
||||||
|
variant.double_value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// VariantGet
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
VariantGet(const bond::Variant& variant, bool& var)
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(!variant.nothing);
|
||||||
|
var = !!variant.uint_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline
|
||||||
|
typename boost::enable_if<is_unsigned<T> >::type
|
||||||
|
VariantGet(const bond::Variant& variant, T& var)
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(!variant.nothing);
|
||||||
|
var = static_cast<T>(variant.uint_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline
|
||||||
|
typename boost::enable_if<is_signed_int_or_enum<T> >::type
|
||||||
|
VariantGet(const bond::Variant& variant, T& var)
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(!variant.nothing);
|
||||||
|
var = static_cast<T>(variant.int_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline
|
||||||
|
typename boost::enable_if<is_floating_point<T> >::type
|
||||||
|
VariantGet(const bond::Variant& variant, T& var)
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(!variant.nothing);
|
||||||
|
var = static_cast<T>(variant.double_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline
|
||||||
|
typename boost::enable_if<is_string<T> >::type
|
||||||
|
VariantGet(const bond::Variant& variant, T& var)
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(!variant.nothing);
|
||||||
|
resize_string(var, static_cast<uint32_t>(variant.string_value.size()));
|
||||||
|
variant.string_value.copy(string_data(var), variant.string_value.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline
|
||||||
|
typename boost::enable_if<is_wstring<T> >::type
|
||||||
|
VariantGet(const bond::Variant& variant, T& var)
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(!variant.nothing);
|
||||||
|
resize_string(var, static_cast<uint32_t>(variant.wstring_value.size()));
|
||||||
|
variant.wstring_value.copy(string_data(var), variant.wstring_value.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Returns name for basic types, overloaded by generated code for enums
|
||||||
|
template <typename T>
|
||||||
|
inline
|
||||||
|
typename boost::disable_if<is_enum<T>, const char*>::type
|
||||||
|
GetTypeName(T, const qualified_name_tag&)
|
||||||
|
{
|
||||||
|
switch (get_type_id<T>::value)
|
||||||
|
{
|
||||||
|
case BT_BOOL: return "bool";
|
||||||
|
case BT_UINT8: return "uint8";
|
||||||
|
case BT_UINT16: return "uint16";
|
||||||
|
case BT_UINT32: return "uint32";
|
||||||
|
case BT_UINT64: return "uint64";
|
||||||
|
case BT_FLOAT: return "float";
|
||||||
|
case BT_DOUBLE: return "double";
|
||||||
|
case BT_INT8: return "int8";
|
||||||
|
case BT_INT16: return "int16";
|
||||||
|
case BT_INT32: return "int32";
|
||||||
|
case BT_INT64: return "int64";
|
||||||
|
default: BOOST_ASSERT(false);
|
||||||
|
return "unknown_type";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline
|
||||||
|
typename boost::enable_if<is_enum<T>, const char*>::type
|
||||||
|
GetTypeName(T e, const qualified_name_tag&)
|
||||||
|
{
|
||||||
|
// In the older versions of generated code we didn't have the overload of
|
||||||
|
// GetTypeName for qualified names; delegating to the old GetTypeName.
|
||||||
|
return GetTypeName(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// basic types and enums
|
||||||
|
template <typename T, typename Enable = void>
|
||||||
|
struct type
|
||||||
|
{
|
||||||
|
static std::string name()
|
||||||
|
{
|
||||||
|
return GetTypeName(T(), qualified_name);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// string
|
||||||
|
template <typename T>
|
||||||
|
struct type<T, typename boost::enable_if<is_string<T> >::type>
|
||||||
|
{
|
||||||
|
static std::string name()
|
||||||
|
{
|
||||||
|
return "string";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// wstring
|
||||||
|
template <typename T>
|
||||||
|
struct type<T, typename boost::enable_if<is_wstring<T> >::type>
|
||||||
|
{
|
||||||
|
static std::string name()
|
||||||
|
{
|
||||||
|
return "wstring";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// blob
|
||||||
|
template <>
|
||||||
|
struct type<blob, void>
|
||||||
|
{
|
||||||
|
static std::string name()
|
||||||
|
{
|
||||||
|
return "blob";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// list
|
||||||
|
template <typename T>
|
||||||
|
struct type<T, typename boost::enable_if<is_list_container<T> >::type>
|
||||||
|
{
|
||||||
|
static std::string name()
|
||||||
|
{
|
||||||
|
return "list<" + type<typename element_type<T>::type>::name() + ">";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// set
|
||||||
|
template <typename T>
|
||||||
|
struct type<T, typename boost::enable_if<is_set_container<T> >::type>
|
||||||
|
{
|
||||||
|
static std::string name()
|
||||||
|
{
|
||||||
|
return "set<" + type<typename element_type<T>::type>::name() + ">";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// map
|
||||||
|
template <typename T>
|
||||||
|
struct type<T, typename boost::enable_if<is_map_container<T> >::type>
|
||||||
|
{
|
||||||
|
static std::string name()
|
||||||
|
{
|
||||||
|
return "map<" + type<typename element_type<T>::type::first_type>::name() + ", "
|
||||||
|
+ type<typename element_type<T>::type::second_type>::name() + ">";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// bonded
|
||||||
|
template <typename T>
|
||||||
|
struct type<bonded<T> >
|
||||||
|
{
|
||||||
|
static std::string name()
|
||||||
|
{
|
||||||
|
return "bonded<" + type<T>::name() + ">";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// maybe
|
||||||
|
template <typename T>
|
||||||
|
struct type<maybe<T> >
|
||||||
|
{
|
||||||
|
static std::string name()
|
||||||
|
{
|
||||||
|
return type<T>::name();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// struct
|
||||||
|
template <typename T>
|
||||||
|
struct type<T, typename boost::enable_if<has_schema<T> >::type>
|
||||||
|
{
|
||||||
|
static std::string name()
|
||||||
|
{
|
||||||
|
// We can't assume that the dependent type's Schema::metadata static
|
||||||
|
// member is initialized, so instead we call GetMetadata() method.
|
||||||
|
return schema<T>::type::GetMetadata().qualified_name;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// TypeListBuilder
|
||||||
|
class TypeListBuilder
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TypeListBuilder(std::string& name)
|
||||||
|
: _name(name)
|
||||||
|
{}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void operator()(const T*)
|
||||||
|
{
|
||||||
|
if (!_name.empty())
|
||||||
|
_name += ", ";
|
||||||
|
|
||||||
|
_name += type<T>::name();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
TypeListBuilder& operator=(const TypeListBuilder&);
|
||||||
|
|
||||||
|
std::string& _name;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace bond
|
|
@ -0,0 +1,19 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
class nonassignable
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
nonassignable() {}
|
||||||
|
~nonassignable() {}
|
||||||
|
private:
|
||||||
|
nonassignable& operator=(const nonassignable&);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct counter
|
||||||
|
{
|
||||||
|
counter()
|
||||||
|
{
|
||||||
|
// This assert indicates violation of One Definition Rule for T.
|
||||||
|
BOOST_ASSERT(!value++);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int value;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
int counter<T>::value;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename One, typename Variant>
|
||||||
|
struct one_definition
|
||||||
|
{
|
||||||
|
static detail::counter<One> value;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename One, typename Variant>
|
||||||
|
detail::counter<One> one_definition<One, Variant>::value;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,265 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
// default for maybe<T> is 'nothing'
|
||||||
|
template <typename T>
|
||||||
|
inline
|
||||||
|
bool is_default(const maybe<T>& value, const Metadata& /*metadata*/)
|
||||||
|
{
|
||||||
|
return value.is_nothing();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// compare basic fields with default value from metadata
|
||||||
|
template <typename T>
|
||||||
|
inline
|
||||||
|
typename boost::enable_if_c<is_basic_type<T>::value
|
||||||
|
&& !is_string_type<T>::value
|
||||||
|
&& !is_type_alias<T>::value, bool>::type
|
||||||
|
is_default(const T& value, const Metadata& metadata)
|
||||||
|
{
|
||||||
|
return (metadata.default_value == value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// compare wire value of type alias fields with default value from metadata
|
||||||
|
template <typename T>
|
||||||
|
inline
|
||||||
|
typename boost::enable_if<is_type_alias<T>, bool>::type
|
||||||
|
is_default(const T& value, const Metadata& metadata)
|
||||||
|
{
|
||||||
|
return (metadata.default_value == get_aliased_value(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// compare string fields with default value from metadata
|
||||||
|
template <typename T>
|
||||||
|
inline
|
||||||
|
typename boost::enable_if<is_string<typename remove_const<T>::type>, bool>::type
|
||||||
|
is_default(const T& value, const Metadata& metadata)
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(!metadata.default_value.nothing);
|
||||||
|
return !metadata.default_value.string_value.compare(0, std::string::npos, string_data(value), string_length(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline
|
||||||
|
typename boost::enable_if<is_wstring<typename remove_const<T>::type>, bool>::type
|
||||||
|
is_default(const T& value, const Metadata& metadata)
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(!metadata.default_value.nothing);
|
||||||
|
return !metadata.default_value.wstring_value.compare(0, std::wstring::npos, string_data(value), string_length(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// for containers default value is always empty
|
||||||
|
template <typename T>
|
||||||
|
inline
|
||||||
|
typename boost::enable_if<is_container<T>, bool>::type
|
||||||
|
is_default(const T& value, const Metadata& /*metadata*/)
|
||||||
|
{
|
||||||
|
return (container_size(value) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// structs don't have default value
|
||||||
|
template <typename T>
|
||||||
|
inline
|
||||||
|
typename boost::enable_if<is_bond_type<T>, bool>::type
|
||||||
|
is_default(const T& /*value*/, const Metadata& /*metadata*/)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// return true if the field may be omitted during serialization
|
||||||
|
template <typename Writer, typename T>
|
||||||
|
inline
|
||||||
|
typename boost::enable_if<may_omit_fields<Writer>, bool>::type
|
||||||
|
omit_field(const Metadata& metadata, const T& value)
|
||||||
|
{
|
||||||
|
// Validate that all compilation units in a program use the same
|
||||||
|
// specialization of may_omit_fields<Writer>
|
||||||
|
(void)one_definition<may_omit_fields<Writer>, true_type>::value;
|
||||||
|
|
||||||
|
// omit the field if it's optional and has default value
|
||||||
|
return metadata.modifier == Optional
|
||||||
|
&& is_default(value, metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Writer, typename T>
|
||||||
|
inline
|
||||||
|
typename boost::disable_if<may_omit_fields<Writer>, bool>::type
|
||||||
|
omit_field(const Metadata& /*metadata*/, const T& /*value*/)
|
||||||
|
{
|
||||||
|
// Validate that all compilation units in a program use the same
|
||||||
|
// specialization of may_omit_fields<Writer>
|
||||||
|
(void)one_definition<may_omit_fields<Writer>, false_type>::value;
|
||||||
|
|
||||||
|
// protocol doesn't allow omitting fields
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// when transcoding from one protocol to another fields are never omitted
|
||||||
|
template <typename Writer, typename Reader, typename T>
|
||||||
|
inline
|
||||||
|
bool omit_field(const Metadata& /*metadata*/, const value<T, Reader>& /*value*/)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Enable = void> struct
|
||||||
|
implements_field_omitting
|
||||||
|
: false_type {};
|
||||||
|
|
||||||
|
|
||||||
|
// WriteFieldOmitted is an optional protocol writer method which is called for
|
||||||
|
// omitted optional fields. It CAN be implemented by tagged protocols and MUST
|
||||||
|
// be implemented by untagged protocols that allow omitting optional fields.
|
||||||
|
template <typename Writer> struct
|
||||||
|
implements_field_omitting<Writer,
|
||||||
|
typename boost::enable_if<bond::check_method<void (Writer::*)(BondDataType, uint16_t, const Metadata&), &Writer::WriteFieldOmitted> >::type>
|
||||||
|
: true_type {};
|
||||||
|
|
||||||
|
|
||||||
|
// ReadFieldOmitted is an optional protocol reader method which MUST be implemented
|
||||||
|
// by untagged protocols that allow omitting optional fields.
|
||||||
|
template <typename Input> struct
|
||||||
|
implements_field_omitting<Input,
|
||||||
|
typename boost::enable_if<bond::check_method<bool (Input::*)(), &Input::ReadFieldOmitted> >::type>
|
||||||
|
: true_type {};
|
||||||
|
|
||||||
|
|
||||||
|
// WriteFieldOmitted
|
||||||
|
template <typename Writer>
|
||||||
|
typename boost::enable_if<implements_field_omitting<Writer> >::type
|
||||||
|
WriteFieldOmitted(Writer& output, BondDataType type, uint16_t id, const Metadata& metadata)
|
||||||
|
{
|
||||||
|
output.WriteFieldOmitted(type, id, metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Writer>
|
||||||
|
typename boost::disable_if<implements_field_omitting<Writer> >::type
|
||||||
|
WriteFieldOmitted(Writer& /*output*/, BondDataType /*type*/, uint16_t /*id*/, const Metadata& /*metadata*/)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
// ReadFieldOmitted
|
||||||
|
template <typename Input>
|
||||||
|
typename boost::enable_if<implements_field_omitting<Input>, bool>::type
|
||||||
|
ReadFieldOmitted(Input& input)
|
||||||
|
{
|
||||||
|
return input.ReadFieldOmitted();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Input>
|
||||||
|
typename boost::disable_if<implements_field_omitting<Input>, bool>::type
|
||||||
|
ReadFieldOmitted(Input& /*input*/)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ReadStructBegin and ReadStructEnd are optional methods for protocol
|
||||||
|
template <typename T, typename Enable = void> struct
|
||||||
|
implements_struct_begin
|
||||||
|
: false_type {};
|
||||||
|
|
||||||
|
|
||||||
|
// Intially ReadStructBegin/End methods had no parameters but later were extended
|
||||||
|
// to take a bool parameter indicating deserialization of base part of a struct.
|
||||||
|
template <typename T, typename Enable = void> struct
|
||||||
|
implements_struct_begin_with_base
|
||||||
|
: false_type {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Input> struct
|
||||||
|
implements_struct_begin<Input,
|
||||||
|
typename boost::enable_if<bond::check_method<void (Input::*)(), &Input::ReadStructBegin> >::type>
|
||||||
|
: true_type {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Input> struct
|
||||||
|
implements_struct_begin_with_base<Input,
|
||||||
|
typename boost::enable_if<bond::check_method<void (Input::*)(bool), &Input::ReadStructBegin> >::type>
|
||||||
|
: true_type {};
|
||||||
|
|
||||||
|
|
||||||
|
// StructBegin
|
||||||
|
template <typename Input>
|
||||||
|
typename boost::enable_if<implements_struct_begin<Input> >::type
|
||||||
|
StructBegin(Input& input, bool /*base*/)
|
||||||
|
{
|
||||||
|
return input.ReadStructBegin();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Input>
|
||||||
|
typename boost::enable_if<implements_struct_begin_with_base<Input> >::type
|
||||||
|
StructBegin(Input& input, bool base)
|
||||||
|
{
|
||||||
|
return input.ReadStructBegin(base);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Input>
|
||||||
|
typename boost::disable_if_c<implements_struct_begin<Input>::value
|
||||||
|
|| implements_struct_begin_with_base<Input>::value>::type
|
||||||
|
StructBegin(Input& /*input*/, bool /*base*/)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
// StructEnd
|
||||||
|
template <typename Input>
|
||||||
|
typename boost::enable_if<implements_struct_begin<Input> >::type
|
||||||
|
StructEnd(Input& input, bool /*base*/)
|
||||||
|
{
|
||||||
|
return input.ReadStructEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Input>
|
||||||
|
typename boost::enable_if<implements_struct_begin_with_base<Input> >::type
|
||||||
|
StructEnd(Input& input, bool base)
|
||||||
|
{
|
||||||
|
return input.ReadStructEnd(base);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Input>
|
||||||
|
typename boost::disable_if_c<implements_struct_begin<Input>::value
|
||||||
|
|| implements_struct_begin_with_base<Input>::value>::type
|
||||||
|
StructEnd(Input& /*input*/, bool /*base*/)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
|
||||||
|
// It is OK to omit optional fields with default values for all tagged protocols
|
||||||
|
// and for untagged protocols which implement field omitting support.
|
||||||
|
template <typename T> struct
|
||||||
|
may_omit_fields
|
||||||
|
{
|
||||||
|
static const bool value = !uses_static_parser<typename T::Reader>::value
|
||||||
|
|| detail::implements_field_omitting<T>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace bond
|
|
@ -0,0 +1,37 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if !defined(BOND_NO_CX11_HDR_MUTEX)
|
||||||
|
|
||||||
|
// Use std::call_once when building with a sane compiler.
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
using std::once_flag;
|
||||||
|
using std::call_once;
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
} // namespace bond
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#include <boost/thread/once.hpp>
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
using boost::once_flag;
|
||||||
|
using boost::call_once;
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
} // namespace bond
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,35 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <bond/core/traits.h>
|
||||||
|
#include <bond/core/blob.h>
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
// Fast pass-through is implemented by calling protocol reader to skip the value
|
||||||
|
// and then writing the skipped data as a single blob to protocol writer.
|
||||||
|
template <typename T, typename Reader, typename Writer>
|
||||||
|
void PassThrough(bonded<T, Reader&>& value, Reader& reader, Writer& writer)
|
||||||
|
{
|
||||||
|
BOOST_STATIC_ASSERT((is_protocol_same<Reader, Writer>::value));
|
||||||
|
|
||||||
|
blob before = GetCurrentBuffer(reader.GetBuffer());
|
||||||
|
|
||||||
|
value.Skip();
|
||||||
|
|
||||||
|
blob after = GetCurrentBuffer(reader.GetBuffer());
|
||||||
|
|
||||||
|
blob data;
|
||||||
|
|
||||||
|
data.assign(before, 0, before.length() - after.length());
|
||||||
|
|
||||||
|
WriteRawBlob(writer, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,267 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "tags.h"
|
||||||
|
#include "pass_through.h"
|
||||||
|
#include <bond/stream/input_buffer.h>
|
||||||
|
#include <bond/stream/output_buffer.h>
|
||||||
|
#include <bond/core/traits.h>
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
|
||||||
|
class RuntimeSchema;
|
||||||
|
|
||||||
|
template <typename Writer>
|
||||||
|
class Serializer;
|
||||||
|
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
// Visitor which applies protocol's parser to specified transform and data.
|
||||||
|
// It is used to dispatch to appropriate protocol at runtime.
|
||||||
|
template <typename T, typename Schema, typename Transform>
|
||||||
|
class _Parser
|
||||||
|
: public boost::static_visitor<bool>,
|
||||||
|
boost::noncopyable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
_Parser(const Transform& transform, const Schema& schema)
|
||||||
|
: _transform(transform),
|
||||||
|
_schema(schema)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Reader>
|
||||||
|
typename boost::enable_if<is_protocol_enabled<typename remove_const<Reader>::type>, bool>::type
|
||||||
|
operator()(Reader& reader) const
|
||||||
|
{
|
||||||
|
// Apply transform to serialized data
|
||||||
|
return Apply(_transform, reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Reader>
|
||||||
|
typename boost::disable_if<is_protocol_enabled<typename remove_const<Reader>::type>, bool>::type
|
||||||
|
operator()(Reader& /*reader*/) const
|
||||||
|
{
|
||||||
|
// Don't instantiate deserialization code for disabled protocol to speed up build
|
||||||
|
BOOST_ASSERT(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
template <template <typename U> class Reader, typename Writer>
|
||||||
|
typename boost::enable_if_c<is_protocol_same<Reader<InputBuffer>, Writer>::value
|
||||||
|
&& protocol_has_multiple_versions<Reader<InputBuffer> >::value, bool>::type
|
||||||
|
Apply(const Serializer<Writer>&, Reader<InputBuffer>& reader) const
|
||||||
|
{
|
||||||
|
if (is_protocol_version_same(reader, _transform._output))
|
||||||
|
return FastPassThrough(reader, _schema);
|
||||||
|
else
|
||||||
|
return typename Reader<InputBuffer>::Parser(reader, false).Apply(_transform, _schema);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <template <typename U> class Reader, typename Writer>
|
||||||
|
typename boost::enable_if_c<is_protocol_same<Reader<InputBuffer>, Writer>::value
|
||||||
|
&& !protocol_has_multiple_versions<Reader<InputBuffer> >::value, bool>::type
|
||||||
|
Apply(const Serializer<Writer>&, Reader<InputBuffer>& reader) const
|
||||||
|
{
|
||||||
|
return FastPassThrough(reader, _schema);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Reader, typename SchemaT>
|
||||||
|
bool FastPassThrough(Reader& reader, const SchemaT&) const
|
||||||
|
{
|
||||||
|
bonded<T, Reader&> value(reader);
|
||||||
|
detail::PassThrough(value, reader, _transform._output);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Reader>
|
||||||
|
bool FastPassThrough(Reader& reader, const RuntimeSchema&) const
|
||||||
|
{
|
||||||
|
bonded<void, Reader&> value(reader, _schema);
|
||||||
|
detail::PassThrough(value, reader, _transform._output);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename TransformT, typename Reader>
|
||||||
|
bool Apply(const TransformT&, Reader& reader) const
|
||||||
|
{
|
||||||
|
return typename Reader::Parser(reader, false).Apply(_transform, _schema);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const Transform& _transform;
|
||||||
|
const Schema& _schema;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Schema, typename Transform, typename Enable = void>
|
||||||
|
class Parser
|
||||||
|
: public _Parser<T, Schema, Transform>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Parser(const Transform& transform, const Schema& schema)
|
||||||
|
: _Parser<T, Schema, Transform>(transform, schema)
|
||||||
|
{}
|
||||||
|
|
||||||
|
using _Parser<T, Schema, Transform>::operator();
|
||||||
|
|
||||||
|
bool operator()(ValueReader& value) const
|
||||||
|
{
|
||||||
|
// "De-serializing" bonded<T> containing a non-serialized instance of T
|
||||||
|
BOOST_VERIFY(value.pointer == NULL);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Schema, typename Transform>
|
||||||
|
class Parser<T, Schema, Transform, typename boost::enable_if_c<is_serializing_transform<Transform>::value && !is_same<T, void>::value>::type>
|
||||||
|
: public _Parser<T, Schema, Transform>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Parser(const Transform& transform, const Schema& schema)
|
||||||
|
: _Parser<T, Schema, Transform>(transform, schema)
|
||||||
|
{}
|
||||||
|
|
||||||
|
using _Parser<T, Schema, Transform>::operator();
|
||||||
|
|
||||||
|
bool operator()(ValueReader& value) const
|
||||||
|
{
|
||||||
|
// Serializing bonded<T> containing a non-serialized instance of T
|
||||||
|
BOOST_ASSERT(value.pointer);
|
||||||
|
// NOTE TO USER: following assert may indicate that the generated file
|
||||||
|
// _reflection.h was not included in compilation unit where T is serialized.
|
||||||
|
BOOST_ASSERT(has_schema<T>::value);
|
||||||
|
return StaticParser<const T&>(*static_cast<const T*>(value.pointer)).Apply(_transform, typename schema_for_passthrough<T>::type());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
using _Parser<T, Schema, Transform>::_transform;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Reader, typename T>
|
||||||
|
inline void Skip(Reader& reader, const T& bonded)
|
||||||
|
{
|
||||||
|
reader.Skip(bonded);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Reader, typename T>
|
||||||
|
BOND_NO_INLINE void Skip(Reader& reader, const T& bonded, const std::nothrow_t&)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
reader.Skip(bonded);
|
||||||
|
}
|
||||||
|
catch(...)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Buffer>
|
||||||
|
inline void Skip(ProtocolReader<Buffer>& /*reader*/, const T& /*bonded*/)
|
||||||
|
{
|
||||||
|
// Not skipping for outer structures
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Buffer>
|
||||||
|
inline void Skip(ProtocolReader<Buffer>& /*reader*/, const T& /*bonded*/, const std::nothrow_t&)
|
||||||
|
{
|
||||||
|
// Not skipping for outer structures
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Transform, typename Reader, typename Schema>
|
||||||
|
inline bool Parse(const Transform& transform, Reader& reader, const Schema& schema, const RuntimeSchema* runtime_schema, bool base)
|
||||||
|
{
|
||||||
|
BOOST_VERIFY(!runtime_schema);
|
||||||
|
return typename Reader::Parser(reader, base).Apply(transform, schema);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Transform, typename Buffer, typename Schema>
|
||||||
|
inline bool Parse(const Transform& transform, ProtocolReader<Buffer> reader, const Schema& schema, const RuntimeSchema* runtime_schema, bool base)
|
||||||
|
{
|
||||||
|
BOOST_VERIFY(!base);
|
||||||
|
|
||||||
|
if (runtime_schema)
|
||||||
|
{
|
||||||
|
// Use named variable to avoid gcc silently copying objects (which
|
||||||
|
// causes build break, because Parse<> is non-copyable).
|
||||||
|
detail::Parser<void, RuntimeSchema, Transform> parser(transform, *runtime_schema);
|
||||||
|
return boost::apply_visitor(parser, reader.value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Use named variable to avoid gcc silently copying objects (which
|
||||||
|
// causes build break, because Parse<> is non-copyable).
|
||||||
|
detail::Parser<T, Schema, Transform> parser(transform, schema);
|
||||||
|
return boost::apply_visitor(parser, reader.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Visitor which updates in-situ bonded<T> playload by merging it with an object.
|
||||||
|
template <typename T, typename Buffer>
|
||||||
|
class InsituMerge
|
||||||
|
: public boost::static_visitor<>,
|
||||||
|
boost::noncopyable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
InsituMerge(const T& var, ProtocolReader<Buffer>& reader)
|
||||||
|
: _var(var),
|
||||||
|
_reader(reader)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
template <template <typename U> class Reader>
|
||||||
|
typename boost::enable_if<is_protocol_enabled<typename remove_const<Reader<Buffer> >::type> >::type
|
||||||
|
operator()(Reader<Buffer>& reader) const
|
||||||
|
{
|
||||||
|
OutputBuffer merged;
|
||||||
|
typename Reader<OutputBuffer>::Writer writer(merged);
|
||||||
|
|
||||||
|
Merge(_var, reader, writer);
|
||||||
|
|
||||||
|
_reader = Reader<Buffer>(merged.GetBuffer());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Reader>
|
||||||
|
typename boost::disable_if<is_protocol_enabled<typename remove_const<Reader>::type> >::type
|
||||||
|
operator()(Reader& /*reader*/) const
|
||||||
|
{
|
||||||
|
// Don't instantiate code for disabled protocol to speed up build
|
||||||
|
BOOST_ASSERT(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void operator()(ValueReader&) const
|
||||||
|
{
|
||||||
|
// Merge is undefined for non-serialized instance of T
|
||||||
|
BOOST_VERIFY(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const T& _var;
|
||||||
|
ProtocolReader<Buffer>& _reader;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Buffer>
|
||||||
|
inline void Merge(const T& var, ProtocolReader<Buffer>& reader)
|
||||||
|
{
|
||||||
|
boost::apply_visitor(detail::InsituMerge<T, Buffer>(var, reader), reader.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}; // namespace detail
|
||||||
|
|
||||||
|
|
||||||
|
}; // namespace bond
|
|
@ -0,0 +1,150 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
template<uint16_t Size, typename Allocator = std::allocator<char> >
|
||||||
|
class basic_string_stream
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
basic_string_stream()
|
||||||
|
{
|
||||||
|
buffer.reserve(Size);
|
||||||
|
buffer.push_back('\0');
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit basic_string_stream(const Allocator& allocator)
|
||||||
|
: buffer(allocator)
|
||||||
|
{
|
||||||
|
buffer.reserve(Size);
|
||||||
|
buffer.push_back('\0');
|
||||||
|
}
|
||||||
|
|
||||||
|
basic_string_stream& operator<<(const char* str)
|
||||||
|
{
|
||||||
|
while (*str)
|
||||||
|
{
|
||||||
|
write(*str++);
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
basic_string_stream& operator<<(const std::string& str)
|
||||||
|
{
|
||||||
|
write(str.begin(), str.end());
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
basic_string_stream& operator<<(char value)
|
||||||
|
{
|
||||||
|
write(value);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
basic_string_stream& operator<<(int value)
|
||||||
|
{
|
||||||
|
char str[256];
|
||||||
|
*str = '\0';
|
||||||
|
|
||||||
|
::sprintf(str, "%d", value);
|
||||||
|
return *this << str;
|
||||||
|
}
|
||||||
|
|
||||||
|
basic_string_stream& operator<<(unsigned int value)
|
||||||
|
{
|
||||||
|
char str[256];
|
||||||
|
*str = '\0';
|
||||||
|
|
||||||
|
::sprintf(str, "%u", value);
|
||||||
|
return *this << str;
|
||||||
|
}
|
||||||
|
|
||||||
|
basic_string_stream& operator<<(long value)
|
||||||
|
{
|
||||||
|
char str[256];
|
||||||
|
*str = '\0';
|
||||||
|
|
||||||
|
::sprintf(str, "%ld", value);
|
||||||
|
return *this << str;
|
||||||
|
}
|
||||||
|
|
||||||
|
basic_string_stream& operator<<(unsigned long value)
|
||||||
|
{
|
||||||
|
char str[256];
|
||||||
|
*str = '\0';
|
||||||
|
|
||||||
|
::sprintf(str, "%lu", value);
|
||||||
|
return *this << str;
|
||||||
|
}
|
||||||
|
|
||||||
|
basic_string_stream& operator<<(long long value)
|
||||||
|
{
|
||||||
|
char str[256];
|
||||||
|
*str = '\0';
|
||||||
|
|
||||||
|
::sprintf(str, "%lld", value);
|
||||||
|
return *this << str;
|
||||||
|
}
|
||||||
|
|
||||||
|
basic_string_stream& operator<<(unsigned long long value)
|
||||||
|
{
|
||||||
|
char str[256];
|
||||||
|
*str = '\0';
|
||||||
|
|
||||||
|
::sprintf(str, "%llu", value);
|
||||||
|
return *this << str;
|
||||||
|
}
|
||||||
|
|
||||||
|
basic_string_stream& operator<<(double value)
|
||||||
|
{
|
||||||
|
char str[256];
|
||||||
|
*str = '\0';
|
||||||
|
|
||||||
|
::sprintf(str, "%f", value);
|
||||||
|
return *this << str;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string str() const
|
||||||
|
{
|
||||||
|
return content();
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* content() const
|
||||||
|
{
|
||||||
|
return &buffer[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void write(char ch)
|
||||||
|
{
|
||||||
|
buffer.back() = ch;
|
||||||
|
buffer.push_back('\0');
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename I>
|
||||||
|
void write(I begin, I end)
|
||||||
|
{
|
||||||
|
for ( ; begin != end; ++begin)
|
||||||
|
{
|
||||||
|
write(*begin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<char, Allocator> buffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <bond/core/bond_fwd.h>
|
||||||
|
#include "nonassignable.h"
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
|
||||||
|
// Used to get qualified name of enum using generated GetTypeName
|
||||||
|
struct qualified_name_tag
|
||||||
|
{};
|
||||||
|
|
||||||
|
|
||||||
|
static const qualified_name_tag qualified_name = {};
|
||||||
|
|
||||||
|
|
||||||
|
// A serializing transform accepts fields of a structure
|
||||||
|
struct serializing_transform_tag
|
||||||
|
{};
|
||||||
|
|
||||||
|
|
||||||
|
// A deserializing transform accepts fields from serialized payload
|
||||||
|
struct deserializing_transform_tag
|
||||||
|
{};
|
||||||
|
|
||||||
|
|
||||||
|
// A modifying transform gets non-const access to struct fields
|
||||||
|
struct modifying_transform_tag
|
||||||
|
{};
|
||||||
|
|
||||||
|
|
||||||
|
struct Transform
|
||||||
|
: detail::nonassignable
|
||||||
|
{
|
||||||
|
bool OmittedField(uint16_t /*id*/, const Metadata& /*metadata*/, BondDataType /*type*/) const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Field>
|
||||||
|
bool OmittedField(const Field&) const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Base class for serializing transforms
|
||||||
|
struct SerializingTransform
|
||||||
|
: Transform
|
||||||
|
{
|
||||||
|
typedef serializing_transform_tag transform_category;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Base class for deserializing transforms
|
||||||
|
struct DeserializingTransform
|
||||||
|
: Transform
|
||||||
|
{
|
||||||
|
typedef deserializing_transform_tag transform_category;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Base class for transforms which modify a Bond type instance
|
||||||
|
struct ModifyingTransform
|
||||||
|
: Transform
|
||||||
|
{
|
||||||
|
typedef modifying_transform_tag transform_category;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Unused = void> struct
|
||||||
|
is_serializing_transform
|
||||||
|
: is_base_of<SerializingTransform, T> {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Unused = void> struct
|
||||||
|
is_deserializing_transform
|
||||||
|
: is_base_of<DeserializingTransform, T> {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Unused = void> struct
|
||||||
|
is_modifying_transform
|
||||||
|
: is_base_of<ModifyingTransform, T> {};
|
||||||
|
|
||||||
|
|
||||||
|
};
|
|
@ -0,0 +1,94 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename Tuple, uint16_t Id, typename T>
|
||||||
|
struct tuple_field
|
||||||
|
{
|
||||||
|
typedef Tuple struct_type;
|
||||||
|
typedef typename remove_reference<T>::type value_type;
|
||||||
|
typedef typename remove_maybe<value_type>::type field_type;
|
||||||
|
typedef reflection::optional_field_modifier field_modifier;
|
||||||
|
|
||||||
|
static const Metadata metadata;
|
||||||
|
static const uint16_t id = Id;
|
||||||
|
|
||||||
|
static const T& GetVariable(const struct_type& obj)
|
||||||
|
{
|
||||||
|
return std::get<id>(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
static T& GetVariable(struct_type& obj)
|
||||||
|
{
|
||||||
|
return std::get<id>(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Metadata GetMetadata()
|
||||||
|
{
|
||||||
|
Metadata metadata;
|
||||||
|
metadata.name = "item" + std::to_string(id);
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Tuple, uint16_t t_id, typename T>
|
||||||
|
const Metadata tuple_field<Tuple, t_id, T>::metadata
|
||||||
|
= tuple_field<Tuple, t_id, T>::GetMetadata();
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Tuple, uint16_t id, typename ...Rest> struct
|
||||||
|
tuple_fields;
|
||||||
|
|
||||||
|
template <typename Tuple, uint16_t id, typename T, typename ...Rest> struct
|
||||||
|
tuple_fields<Tuple, id, T, Rest...>
|
||||||
|
{
|
||||||
|
typedef typename boost::mpl::push_front<
|
||||||
|
typename tuple_fields<Tuple, id + 1, Rest...>::type,
|
||||||
|
tuple_field<Tuple, id, T>
|
||||||
|
>::type type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Tuple, uint16_t id, typename ...Rest> struct
|
||||||
|
tuple_fields<Tuple, id, const decltype(std::ignore)&, Rest...>
|
||||||
|
: tuple_fields<Tuple, id + 1, Rest...>
|
||||||
|
{};
|
||||||
|
|
||||||
|
template <typename Tuple, uint16_t id> struct
|
||||||
|
tuple_fields<Tuple, id>
|
||||||
|
{
|
||||||
|
typedef typename boost::mpl::list<>::type type;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename ...T> struct
|
||||||
|
param_list;
|
||||||
|
|
||||||
|
template <typename T, typename ...Rest> struct
|
||||||
|
param_list<T, Rest...>
|
||||||
|
{
|
||||||
|
typedef typename boost::mpl::push_front<
|
||||||
|
typename param_list<Rest...>::type,
|
||||||
|
typename std::add_pointer<T>::type
|
||||||
|
>::type type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename ...Rest> struct
|
||||||
|
param_list<const decltype(std::ignore)&, Rest...>
|
||||||
|
: param_list<Rest...>
|
||||||
|
{};
|
||||||
|
|
||||||
|
template <> struct
|
||||||
|
param_list<>
|
||||||
|
{
|
||||||
|
typedef boost::mpl::list<> type;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
} // namespace bond
|
|
@ -0,0 +1,806 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
// Sanity check to assert that manually expended switch statement are consistent
|
||||||
|
// with the canonical definition of type promotion in is_matching metafunction.
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool IsMatching(BondDataType type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case bond::BT_BOOL:
|
||||||
|
return is_matching<bool, T>::value;
|
||||||
|
|
||||||
|
case bond::BT_UINT8:
|
||||||
|
return is_matching<uint8_t, T>::value;
|
||||||
|
|
||||||
|
case bond::BT_UINT16:
|
||||||
|
return is_matching<uint16_t, T>::value;
|
||||||
|
|
||||||
|
case bond::BT_UINT32:
|
||||||
|
return is_matching<uint32_t, T>::value;
|
||||||
|
|
||||||
|
case bond::BT_UINT64:
|
||||||
|
return is_matching<uint64_t, T>::value;
|
||||||
|
|
||||||
|
case bond::BT_FLOAT:
|
||||||
|
return is_matching<float, T>::value;
|
||||||
|
|
||||||
|
case bond::BT_DOUBLE:
|
||||||
|
return is_matching<double, T>::value;
|
||||||
|
|
||||||
|
case bond::BT_STRING:
|
||||||
|
return is_matching<std::string, T>::value;
|
||||||
|
|
||||||
|
case bond::BT_WSTRING:
|
||||||
|
return is_matching<std::wstring, T>::value;
|
||||||
|
|
||||||
|
case bond::BT_INT8:
|
||||||
|
return is_matching<int8_t, T>::value;
|
||||||
|
|
||||||
|
case bond::BT_INT16:
|
||||||
|
return is_matching<int16_t, T>::value;
|
||||||
|
|
||||||
|
case bond::BT_INT32:
|
||||||
|
return is_matching<int32_t, T>::value;
|
||||||
|
|
||||||
|
case bond::BT_INT64:
|
||||||
|
return is_matching<int64_t, T>::value;
|
||||||
|
|
||||||
|
default:
|
||||||
|
BOOST_ASSERT(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Transform, typename Reader>
|
||||||
|
inline bool BasicTypeField(uint16_t id, const Metadata& metadata, BondDataType type, const Transform& transform, Reader& input)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case bond::BT_BOOL:
|
||||||
|
return transform.Field(id, metadata, value<bool, Reader&>(input));
|
||||||
|
|
||||||
|
case bond::BT_UINT8:
|
||||||
|
return transform.Field(id, metadata, value<uint8_t, Reader&>(input));
|
||||||
|
|
||||||
|
case bond::BT_UINT16:
|
||||||
|
return transform.Field(id, metadata, value<uint16_t, Reader&>(input));
|
||||||
|
|
||||||
|
case bond::BT_UINT32:
|
||||||
|
return transform.Field(id, metadata, value<uint32_t, Reader&>(input));
|
||||||
|
|
||||||
|
case bond::BT_UINT64:
|
||||||
|
return transform.Field(id, metadata, value<uint64_t, Reader&>(input));
|
||||||
|
|
||||||
|
case bond::BT_FLOAT:
|
||||||
|
return transform.Field(id, metadata, value<float, Reader&>(input));
|
||||||
|
|
||||||
|
case bond::BT_DOUBLE:
|
||||||
|
return transform.Field(id, metadata, value<double, Reader&>(input));
|
||||||
|
|
||||||
|
case bond::BT_STRING:
|
||||||
|
return transform.Field(id, metadata, value<std::string, Reader&>(input));
|
||||||
|
|
||||||
|
case bond::BT_WSTRING:
|
||||||
|
return transform.Field(id, metadata, value<std::wstring, Reader&>(input));
|
||||||
|
|
||||||
|
case bond::BT_INT8:
|
||||||
|
return transform.Field(id, metadata, value<int8_t, Reader&>(input));
|
||||||
|
|
||||||
|
case bond::BT_INT16:
|
||||||
|
return transform.Field(id, metadata, value<int16_t, Reader&>(input));
|
||||||
|
|
||||||
|
case bond::BT_INT32:
|
||||||
|
return transform.Field(id, metadata, value<int32_t, Reader&>(input));
|
||||||
|
|
||||||
|
case bond::BT_INT64:
|
||||||
|
return transform.Field(id, metadata, value<int64_t, Reader&>(input));
|
||||||
|
|
||||||
|
default:
|
||||||
|
BOOST_ASSERT(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Reader>
|
||||||
|
inline void BasicTypeContainer(T& var, BondDataType type, Reader& input, uint32_t size)
|
||||||
|
{
|
||||||
|
BOOST_STATIC_ASSERT(!is_container<T>::value);
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case bond::BT_BOOL:
|
||||||
|
return DeserializeElements(var, value<bool, Reader&>(input, false), size);
|
||||||
|
|
||||||
|
case bond::BT_UINT8:
|
||||||
|
return DeserializeElements(var, value<uint8_t, Reader&>(input, false), size);
|
||||||
|
|
||||||
|
case bond::BT_UINT16:
|
||||||
|
return DeserializeElements(var, value<uint16_t, Reader&>(input, false), size);
|
||||||
|
|
||||||
|
case bond::BT_UINT32:
|
||||||
|
return DeserializeElements(var, value<uint32_t, Reader&>(input, false), size);
|
||||||
|
|
||||||
|
case bond::BT_UINT64:
|
||||||
|
return DeserializeElements(var, value<uint64_t, Reader&>(input, false), size);
|
||||||
|
|
||||||
|
case bond::BT_FLOAT:
|
||||||
|
return DeserializeElements(var, value<float, Reader&>(input, false), size);
|
||||||
|
|
||||||
|
case bond::BT_DOUBLE:
|
||||||
|
return DeserializeElements(var, value<double, Reader&>(input, false), size);
|
||||||
|
|
||||||
|
case bond::BT_STRING:
|
||||||
|
return DeserializeElements(var, value<std::string, Reader&>(input, false), size);
|
||||||
|
|
||||||
|
case bond::BT_WSTRING:
|
||||||
|
return DeserializeElements(var, value<std::wstring, Reader&>(input, false), size);
|
||||||
|
|
||||||
|
case bond::BT_INT8:
|
||||||
|
return DeserializeElements(var, value<int8_t, Reader&>(input, false), size);
|
||||||
|
|
||||||
|
case bond::BT_INT16:
|
||||||
|
return DeserializeElements(var, value<int16_t, Reader&>(input, false), size);
|
||||||
|
|
||||||
|
case bond::BT_INT32:
|
||||||
|
return DeserializeElements(var, value<int32_t, Reader&>(input, false), size);
|
||||||
|
|
||||||
|
case bond::BT_INT64:
|
||||||
|
return DeserializeElements(var, value<int64_t, Reader&>(input, false), size);
|
||||||
|
|
||||||
|
default:
|
||||||
|
BOOST_ASSERT(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MatchingTypeContainer function are manually expended versions of BasicTypeContainer
|
||||||
|
// using the type information about destination container. This helps with compilation speed.
|
||||||
|
template <typename T, typename Reader>
|
||||||
|
typename boost::enable_if<is_type_alias<typename element_type<T>::type> >::type
|
||||||
|
inline MatchingTypeContainer(T& var, BondDataType type, Reader& input, uint32_t size)
|
||||||
|
{
|
||||||
|
if (type == get_type_id<typename element_type<T>::type>::value)
|
||||||
|
{
|
||||||
|
DeserializeElements(var, value<typename element_type<T>::type, Reader&>(input, false), size);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(!IsMatching<typename element_type<T>::type>(type));
|
||||||
|
|
||||||
|
while (size--)
|
||||||
|
input.Skip(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Reader>
|
||||||
|
typename boost::enable_if<is_same<bool, typename element_type<T>::type> >::type
|
||||||
|
inline MatchingTypeContainer(T& var, BondDataType type, Reader& input, uint32_t size)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case bond::BT_BOOL:
|
||||||
|
return DeserializeElements(var, value<bool, Reader&>(input, false), size);
|
||||||
|
|
||||||
|
default:
|
||||||
|
BOOST_ASSERT(!IsMatching<typename element_type<T>::type>(type));
|
||||||
|
|
||||||
|
while (size--)
|
||||||
|
input.Skip(type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Reader>
|
||||||
|
typename boost::enable_if<is_string<typename element_type<T>::type> >::type
|
||||||
|
inline MatchingTypeContainer(T& var, BondDataType type, Reader& input, uint32_t size)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case bond::BT_STRING:
|
||||||
|
return DeserializeElements(var, value<std::string, Reader&>(input, false), size);
|
||||||
|
|
||||||
|
default:
|
||||||
|
BOOST_ASSERT(!IsMatching<typename element_type<T>::type>(type));
|
||||||
|
|
||||||
|
while (size--)
|
||||||
|
input.Skip(type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Reader>
|
||||||
|
typename boost::enable_if<is_wstring<typename element_type<T>::type> >::type
|
||||||
|
inline MatchingTypeContainer(T& var, BondDataType type, Reader& input, uint32_t size)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case bond::BT_WSTRING:
|
||||||
|
return DeserializeElements(var, value<std::wstring, Reader&>(input, false), size);
|
||||||
|
|
||||||
|
default:
|
||||||
|
BOOST_ASSERT(!IsMatching<typename element_type<T>::type>(type));
|
||||||
|
|
||||||
|
while (size--)
|
||||||
|
input.Skip(type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Reader>
|
||||||
|
typename boost::enable_if<is_floating_point<typename element_type<T>::type> >::type
|
||||||
|
inline MatchingTypeContainer(T& var, BondDataType type, Reader& input, uint32_t size)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case bond::BT_FLOAT:
|
||||||
|
return DeserializeElements(var, value<float, Reader&>(input, false), size);
|
||||||
|
|
||||||
|
case bond::BT_DOUBLE:
|
||||||
|
return DeserializeElements(var, value<double, Reader&>(input, false), size);
|
||||||
|
|
||||||
|
default:
|
||||||
|
BOOST_ASSERT(!IsMatching<typename element_type<T>::type>(type));
|
||||||
|
|
||||||
|
while (size--)
|
||||||
|
input.Skip(type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Reader>
|
||||||
|
typename boost::enable_if<is_matching<uint8_t, typename element_type<T>::type> >::type
|
||||||
|
inline MatchingTypeContainer(T& var, BondDataType type, Reader& input, uint32_t size)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case bond::BT_UINT8:
|
||||||
|
return DeserializeElements(var, value<uint8_t, Reader&>(input, false), size);
|
||||||
|
|
||||||
|
case bond::BT_UINT16:
|
||||||
|
return DeserializeElements(var, value<uint16_t, Reader&>(input, false), size);
|
||||||
|
|
||||||
|
case bond::BT_UINT32:
|
||||||
|
return DeserializeElements(var, value<uint32_t, Reader&>(input, false), size);
|
||||||
|
|
||||||
|
case bond::BT_UINT64:
|
||||||
|
return DeserializeElements(var, value<uint64_t, Reader&>(input, false), size);
|
||||||
|
|
||||||
|
default:
|
||||||
|
BOOST_ASSERT(!IsMatching<typename element_type<T>::type>(type));
|
||||||
|
|
||||||
|
while (size--)
|
||||||
|
input.Skip(type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Reader>
|
||||||
|
typename boost::enable_if<is_matching<int8_t, typename element_type<T>::type> >::type
|
||||||
|
inline MatchingTypeContainer(T& var, BondDataType type, Reader& input, uint32_t size)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case bond::BT_INT8:
|
||||||
|
return DeserializeElements(var, value<int8_t, Reader&>(input, false), size);
|
||||||
|
|
||||||
|
case bond::BT_INT16:
|
||||||
|
return DeserializeElements(var, value<int16_t, Reader&>(input, false), size);
|
||||||
|
|
||||||
|
case bond::BT_INT32:
|
||||||
|
return DeserializeElements(var, value<int32_t, Reader&>(input, false), size);
|
||||||
|
|
||||||
|
case bond::BT_INT64:
|
||||||
|
return DeserializeElements(var, value<int64_t, Reader&>(input, false), size);
|
||||||
|
|
||||||
|
default:
|
||||||
|
BOOST_ASSERT(!IsMatching<typename element_type<T>::type>(type));
|
||||||
|
|
||||||
|
while (size--)
|
||||||
|
input.Skip(type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MatchingMapByKey function are manually expended versions of MapByKey using the type
|
||||||
|
// information about destination container. This helps with compilation speed.
|
||||||
|
|
||||||
|
template <typename T, typename E, typename Reader>
|
||||||
|
typename boost::enable_if<is_type_alias<typename element_type<T>::type::first_type> >::type
|
||||||
|
inline MatchingMapByKey(T& var, BondDataType keyType, const E& element, Reader& input, uint32_t size)
|
||||||
|
{
|
||||||
|
if (keyType == get_type_id<typename element_type<T>::type::first_type>::value)
|
||||||
|
{
|
||||||
|
return DeserializeMapElements(var, value<typename element_type<T>::type::first_type, Reader&>(input, false), element, size);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(!IsMatching<typename element_type<T>::type::first_type>(keyType));
|
||||||
|
|
||||||
|
while (size--)
|
||||||
|
{
|
||||||
|
input.Skip(keyType);
|
||||||
|
element.Skip();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename E, typename Reader>
|
||||||
|
typename boost::enable_if<is_same<bool, typename element_type<T>::type::first_type> >::type
|
||||||
|
inline MatchingMapByKey(T& var, BondDataType keyType, const E& element, Reader& input, uint32_t size)
|
||||||
|
{
|
||||||
|
switch (keyType)
|
||||||
|
{
|
||||||
|
case bond::BT_BOOL:
|
||||||
|
return DeserializeMapElements(var, value<bool, Reader&>(input, false), element, size);
|
||||||
|
|
||||||
|
default:
|
||||||
|
BOOST_ASSERT(!IsMatching<typename element_type<T>::type::first_type>(keyType));
|
||||||
|
|
||||||
|
while (size--)
|
||||||
|
{
|
||||||
|
input.Skip(keyType);
|
||||||
|
element.Skip();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename E, typename Reader>
|
||||||
|
typename boost::enable_if<is_string<typename element_type<T>::type::first_type> >::type
|
||||||
|
inline MatchingMapByKey(T& var, BondDataType keyType, const E& element, Reader& input, uint32_t size)
|
||||||
|
{
|
||||||
|
switch (keyType)
|
||||||
|
{
|
||||||
|
case bond::BT_STRING:
|
||||||
|
return DeserializeMapElements(var, value<std::string, Reader&>(input, false), element, size);
|
||||||
|
|
||||||
|
default:
|
||||||
|
BOOST_ASSERT(!IsMatching<typename element_type<T>::type::first_type>(keyType));
|
||||||
|
|
||||||
|
while (size--)
|
||||||
|
{
|
||||||
|
input.Skip(keyType);
|
||||||
|
element.Skip();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename E, typename Reader>
|
||||||
|
typename boost::enable_if<is_wstring<typename element_type<T>::type::first_type> >::type
|
||||||
|
inline MatchingMapByKey(T& var, BondDataType keyType, const E& element, Reader& input, uint32_t size)
|
||||||
|
{
|
||||||
|
switch (keyType)
|
||||||
|
{
|
||||||
|
case bond::BT_WSTRING:
|
||||||
|
return DeserializeMapElements(var, value<std::wstring, Reader&>(input, false), element, size);
|
||||||
|
|
||||||
|
default:
|
||||||
|
BOOST_ASSERT(!IsMatching<typename element_type<T>::type::first_type>(keyType));
|
||||||
|
|
||||||
|
while (size--)
|
||||||
|
{
|
||||||
|
input.Skip(keyType);
|
||||||
|
element.Skip();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename E, typename Reader>
|
||||||
|
typename boost::enable_if<is_floating_point<typename element_type<T>::type::first_type> >::type
|
||||||
|
inline MatchingMapByKey(T& var, BondDataType keyType, const E& element, Reader& input, uint32_t size)
|
||||||
|
{
|
||||||
|
switch (keyType)
|
||||||
|
{
|
||||||
|
case bond::BT_FLOAT:
|
||||||
|
return DeserializeMapElements(var, value<float, Reader&>(input, false), element, size);
|
||||||
|
|
||||||
|
case bond::BT_DOUBLE:
|
||||||
|
return DeserializeMapElements(var, value<double, Reader&>(input, false), element, size);
|
||||||
|
|
||||||
|
default:
|
||||||
|
BOOST_ASSERT(!IsMatching<typename element_type<T>::type::first_type>(keyType));
|
||||||
|
|
||||||
|
while (size--)
|
||||||
|
{
|
||||||
|
input.Skip(keyType);
|
||||||
|
element.Skip();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename E, typename Reader>
|
||||||
|
typename boost::enable_if<is_matching<uint8_t, typename element_type<T>::type::first_type> >::type
|
||||||
|
inline MatchingMapByKey(T& var, BondDataType keyType, const E& element, Reader& input, uint32_t size)
|
||||||
|
{
|
||||||
|
switch (keyType)
|
||||||
|
{
|
||||||
|
case bond::BT_UINT8:
|
||||||
|
return DeserializeMapElements(var, value<uint8_t, Reader&>(input, false), element, size);
|
||||||
|
|
||||||
|
case bond::BT_UINT16:
|
||||||
|
return DeserializeMapElements(var, value<uint16_t, Reader&>(input, false), element, size);
|
||||||
|
|
||||||
|
case bond::BT_UINT32:
|
||||||
|
return DeserializeMapElements(var, value<uint32_t, Reader&>(input, false), element, size);
|
||||||
|
|
||||||
|
case bond::BT_UINT64:
|
||||||
|
return DeserializeMapElements(var, value<uint64_t, Reader&>(input, false), element, size);
|
||||||
|
|
||||||
|
default:
|
||||||
|
BOOST_ASSERT(!IsMatching<typename element_type<T>::type::first_type>(keyType));
|
||||||
|
|
||||||
|
while (size--)
|
||||||
|
{
|
||||||
|
input.Skip(keyType);
|
||||||
|
element.Skip();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename E, typename Reader>
|
||||||
|
typename boost::enable_if<is_matching<int8_t, typename element_type<T>::type::first_type> >::type
|
||||||
|
inline MatchingMapByKey(T& var, BondDataType keyType, const E& element, Reader& input, uint32_t size)
|
||||||
|
{
|
||||||
|
switch (keyType)
|
||||||
|
{
|
||||||
|
case bond::BT_INT8:
|
||||||
|
return DeserializeMapElements(var, value<int8_t, Reader&>(input, false), element, size);
|
||||||
|
|
||||||
|
case bond::BT_INT16:
|
||||||
|
return DeserializeMapElements(var, value<int16_t, Reader&>(input, false), element, size);
|
||||||
|
|
||||||
|
case bond::BT_INT32:
|
||||||
|
return DeserializeMapElements(var, value<int32_t, Reader&>(input, false), element, size);
|
||||||
|
|
||||||
|
case bond::BT_INT64:
|
||||||
|
return DeserializeMapElements(var, value<int64_t, Reader&>(input, false), element, size);
|
||||||
|
|
||||||
|
default:
|
||||||
|
BOOST_ASSERT(!IsMatching<typename element_type<T>::type::first_type>(keyType));
|
||||||
|
|
||||||
|
while (size--)
|
||||||
|
{
|
||||||
|
input.Skip(keyType);
|
||||||
|
element.Skip();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename E, typename Reader>
|
||||||
|
typename boost::disable_if<is_map_container<T> >::type
|
||||||
|
inline MapByKey(T& var, BondDataType keyType, const E& element, Reader& input, uint32_t size)
|
||||||
|
{
|
||||||
|
switch (keyType)
|
||||||
|
{
|
||||||
|
case bond::BT_BOOL:
|
||||||
|
return DeserializeMapElements(var, value<bool, Reader&>(input, false), element, size);
|
||||||
|
|
||||||
|
case bond::BT_UINT8:
|
||||||
|
return DeserializeMapElements(var, value<uint8_t, Reader&>(input, false), element, size);
|
||||||
|
|
||||||
|
case bond::BT_UINT16:
|
||||||
|
return DeserializeMapElements(var, value<uint16_t, Reader&>(input, false), element, size);
|
||||||
|
|
||||||
|
case bond::BT_UINT32:
|
||||||
|
return DeserializeMapElements(var, value<uint32_t, Reader&>(input, false), element, size);
|
||||||
|
|
||||||
|
case bond::BT_UINT64:
|
||||||
|
return DeserializeMapElements(var, value<uint64_t, Reader&>(input, false), element, size);
|
||||||
|
|
||||||
|
case bond::BT_FLOAT:
|
||||||
|
return DeserializeMapElements(var, value<float, Reader&>(input, false), element, size);
|
||||||
|
|
||||||
|
case bond::BT_DOUBLE:
|
||||||
|
return DeserializeMapElements(var, value<double, Reader&>(input, false), element, size);
|
||||||
|
|
||||||
|
case bond::BT_STRING:
|
||||||
|
return DeserializeMapElements(var, value<std::string, Reader&>(input, false), element, size);
|
||||||
|
|
||||||
|
case bond::BT_WSTRING:
|
||||||
|
return DeserializeMapElements(var, value<std::wstring, Reader&>(input, false), element, size);
|
||||||
|
|
||||||
|
case bond::BT_INT8:
|
||||||
|
return DeserializeMapElements(var, value<int8_t, Reader&>(input, false), element, size);
|
||||||
|
|
||||||
|
case bond::BT_INT16:
|
||||||
|
return DeserializeMapElements(var, value<int16_t, Reader&>(input, false), element, size);
|
||||||
|
|
||||||
|
case bond::BT_INT32:
|
||||||
|
return DeserializeMapElements(var, value<int32_t, Reader&>(input, false), element, size);
|
||||||
|
|
||||||
|
case bond::BT_INT64:
|
||||||
|
return DeserializeMapElements(var, value<int64_t, Reader&>(input, false), element, size);
|
||||||
|
|
||||||
|
default:
|
||||||
|
BOOST_ASSERT(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename E, typename Reader>
|
||||||
|
typename boost::enable_if<is_map_element_matching<E, T> >::type
|
||||||
|
inline MapByKey(T& var, BondDataType keyType, const E& element, Reader& input, uint32_t size)
|
||||||
|
{
|
||||||
|
return MatchingMapByKey(var, keyType, element, input, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename E, typename Reader>
|
||||||
|
typename boost::disable_if_c<!is_map_container<T>::value || is_map_element_matching<E, T>::value>::type
|
||||||
|
inline MapByKey(T&, BondDataType keyType, const E& element, Reader& input, uint32_t size)
|
||||||
|
{
|
||||||
|
while (size--)
|
||||||
|
{
|
||||||
|
input.Skip(keyType);
|
||||||
|
element.Skip();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Reader>
|
||||||
|
inline void MapByElement(T& var, BondDataType keyType, BondDataType elementType, Reader& input, uint32_t size)
|
||||||
|
{
|
||||||
|
switch (elementType)
|
||||||
|
{
|
||||||
|
case bond::BT_BOOL:
|
||||||
|
return MapByKey(var, keyType, value<bool, Reader&>(input, false), input, size);
|
||||||
|
|
||||||
|
case bond::BT_UINT8:
|
||||||
|
return MapByKey(var, keyType, value<uint8_t, Reader&>(input, false), input, size);
|
||||||
|
|
||||||
|
case bond::BT_UINT16:
|
||||||
|
return MapByKey(var, keyType, value<uint16_t, Reader&>(input, false), input, size);
|
||||||
|
|
||||||
|
case bond::BT_UINT32:
|
||||||
|
return MapByKey(var, keyType, value<uint32_t, Reader&>(input, false), input, size);
|
||||||
|
|
||||||
|
case bond::BT_UINT64:
|
||||||
|
return MapByKey(var, keyType, value<uint64_t, Reader&>(input, false), input, size);
|
||||||
|
|
||||||
|
case bond::BT_FLOAT:
|
||||||
|
return MapByKey(var, keyType, value<float, Reader&>(input, false), input, size);
|
||||||
|
|
||||||
|
case bond::BT_DOUBLE:
|
||||||
|
return MapByKey(var, keyType, value<double, Reader&>(input, false), input, size);
|
||||||
|
|
||||||
|
case bond::BT_STRING:
|
||||||
|
return MapByKey(var, keyType, value<std::string, Reader&>(input, false), input, size);
|
||||||
|
|
||||||
|
case bond::BT_WSTRING:
|
||||||
|
return MapByKey(var, keyType, value<std::wstring, Reader&>(input, false), input, size);
|
||||||
|
|
||||||
|
case bond::BT_INT8:
|
||||||
|
return MapByKey(var, keyType, value<int8_t, Reader&>(input, false), input, size);
|
||||||
|
|
||||||
|
case bond::BT_INT16:
|
||||||
|
return MapByKey(var, keyType, value<int16_t, Reader&>(input, false), input, size);
|
||||||
|
|
||||||
|
case bond::BT_INT32:
|
||||||
|
return MapByKey(var, keyType, value<int32_t, Reader&>(input, false), input, size);
|
||||||
|
|
||||||
|
case bond::BT_INT64:
|
||||||
|
return MapByKey(var, keyType, value<int64_t, Reader&>(input, false), input, size);
|
||||||
|
|
||||||
|
default:
|
||||||
|
BOOST_ASSERT(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MatchingMapByElement function are manually expended versions of MapByElement using
|
||||||
|
// the type information about destination container. This helps with compilation speed.
|
||||||
|
|
||||||
|
template <typename T, typename Reader>
|
||||||
|
typename boost::enable_if<is_type_alias<typename element_type<T>::type::second_type> >::type
|
||||||
|
inline MatchingMapByElement(T& var, BondDataType keyType, BondDataType elementType, Reader& input, uint32_t size)
|
||||||
|
{
|
||||||
|
if (elementType == get_type_id<typename element_type<T>::type::second_type>::value)
|
||||||
|
{
|
||||||
|
MapByKey(var, keyType, value<typename element_type<T>::type::second_type, Reader&>(input, false), input, size);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(!IsMatching<typename element_type<T>::type::second_type>(elementType));
|
||||||
|
|
||||||
|
while (size--)
|
||||||
|
{
|
||||||
|
input.Skip(keyType);
|
||||||
|
input.Skip(elementType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Reader>
|
||||||
|
typename boost::enable_if<is_same<bool, typename element_type<T>::type::second_type> >::type
|
||||||
|
inline MatchingMapByElement(T& var, BondDataType keyType, BondDataType elementType, Reader& input, uint32_t size)
|
||||||
|
{
|
||||||
|
switch (elementType)
|
||||||
|
{
|
||||||
|
case bond::BT_BOOL:
|
||||||
|
return MapByKey(var, keyType, value<bool, Reader&>(input, false), input, size);
|
||||||
|
|
||||||
|
default:
|
||||||
|
BOOST_ASSERT(!IsMatching<typename element_type<T>::type::second_type>(elementType));
|
||||||
|
|
||||||
|
while (size--)
|
||||||
|
{
|
||||||
|
input.Skip(keyType);
|
||||||
|
input.Skip(elementType);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Reader>
|
||||||
|
typename boost::enable_if<is_string<typename element_type<T>::type::second_type> >::type
|
||||||
|
inline MatchingMapByElement(T& var, BondDataType keyType, BondDataType elementType, Reader& input, uint32_t size)
|
||||||
|
{
|
||||||
|
switch (elementType)
|
||||||
|
{
|
||||||
|
case bond::BT_STRING:
|
||||||
|
return MapByKey(var, keyType, value<std::string, Reader&>(input, false), input, size);
|
||||||
|
|
||||||
|
default:
|
||||||
|
BOOST_ASSERT(!IsMatching<typename element_type<T>::type::second_type>(elementType));
|
||||||
|
|
||||||
|
while (size--)
|
||||||
|
{
|
||||||
|
input.Skip(keyType);
|
||||||
|
input.Skip(elementType);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Reader>
|
||||||
|
typename boost::enable_if<is_wstring<typename element_type<T>::type::second_type> >::type
|
||||||
|
inline MatchingMapByElement(T& var, BondDataType keyType, BondDataType elementType, Reader& input, uint32_t size)
|
||||||
|
{
|
||||||
|
switch (elementType)
|
||||||
|
{
|
||||||
|
case bond::BT_WSTRING:
|
||||||
|
return MapByKey(var, keyType, value<std::wstring, Reader&>(input, false), input, size);
|
||||||
|
|
||||||
|
default:
|
||||||
|
BOOST_ASSERT(!IsMatching<typename element_type<T>::type::second_type>(elementType));
|
||||||
|
|
||||||
|
while (size--)
|
||||||
|
{
|
||||||
|
input.Skip(keyType);
|
||||||
|
input.Skip(elementType);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Reader>
|
||||||
|
typename boost::enable_if<is_floating_point<typename element_type<T>::type::second_type> >::type
|
||||||
|
inline MatchingMapByElement(T& var, BondDataType keyType, BondDataType elementType, Reader& input, uint32_t size)
|
||||||
|
{
|
||||||
|
switch (elementType)
|
||||||
|
{
|
||||||
|
case bond::BT_FLOAT:
|
||||||
|
return MapByKey(var, keyType, value<float, Reader&>(input, false), input, size);
|
||||||
|
|
||||||
|
case bond::BT_DOUBLE:
|
||||||
|
return MapByKey(var, keyType, value<double, Reader&>(input, false), input, size);
|
||||||
|
|
||||||
|
default:
|
||||||
|
BOOST_ASSERT(!IsMatching<typename element_type<T>::type::second_type>(elementType));
|
||||||
|
|
||||||
|
while (size--)
|
||||||
|
{
|
||||||
|
input.Skip(keyType);
|
||||||
|
input.Skip(elementType);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Reader>
|
||||||
|
typename boost::enable_if<is_matching<uint8_t, typename element_type<T>::type::second_type> >::type
|
||||||
|
inline MatchingMapByElement(T& var, BondDataType keyType, BondDataType elementType, Reader& input, uint32_t size)
|
||||||
|
{
|
||||||
|
switch (elementType)
|
||||||
|
{
|
||||||
|
case bond::BT_UINT8:
|
||||||
|
return MapByKey(var, keyType, value<uint8_t, Reader&>(input, false), input, size);
|
||||||
|
|
||||||
|
case bond::BT_UINT16:
|
||||||
|
return MapByKey(var, keyType, value<uint16_t, Reader&>(input, false), input, size);
|
||||||
|
|
||||||
|
case bond::BT_UINT32:
|
||||||
|
return MapByKey(var, keyType, value<uint32_t, Reader&>(input, false), input, size);
|
||||||
|
|
||||||
|
case bond::BT_UINT64:
|
||||||
|
return MapByKey(var, keyType, value<uint64_t, Reader&>(input, false), input, size);
|
||||||
|
|
||||||
|
default:
|
||||||
|
BOOST_ASSERT(!IsMatching<typename element_type<T>::type::second_type>(elementType));
|
||||||
|
|
||||||
|
while (size--)
|
||||||
|
{
|
||||||
|
input.Skip(keyType);
|
||||||
|
input.Skip(elementType);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Reader>
|
||||||
|
typename boost::enable_if<is_matching<int8_t, typename element_type<T>::type::second_type> >::type
|
||||||
|
inline MatchingMapByElement(T& var, BondDataType keyType, BondDataType elementType, Reader& input, uint32_t size)
|
||||||
|
{
|
||||||
|
switch (elementType)
|
||||||
|
{
|
||||||
|
case bond::BT_INT8:
|
||||||
|
return MapByKey(var, keyType, value<int8_t, Reader&>(input, false), input, size);
|
||||||
|
|
||||||
|
case bond::BT_INT16:
|
||||||
|
return MapByKey(var, keyType, value<int16_t, Reader&>(input, false), input, size);
|
||||||
|
|
||||||
|
case bond::BT_INT32:
|
||||||
|
return MapByKey(var, keyType, value<int32_t, Reader&>(input, false), input, size);
|
||||||
|
|
||||||
|
case bond::BT_INT64:
|
||||||
|
return MapByKey(var, keyType, value<int64_t, Reader&>(input, false), input, size);
|
||||||
|
|
||||||
|
default:
|
||||||
|
BOOST_ASSERT(!IsMatching<typename element_type<T>::type::second_type>(elementType));
|
||||||
|
|
||||||
|
while (size--)
|
||||||
|
{
|
||||||
|
input.Skip(keyType);
|
||||||
|
input.Skip(elementType);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}; // namespace detail
|
||||||
|
|
||||||
|
}; // namespace bond
|
|
@ -0,0 +1,286 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
inline bool ValidateType(BondDataType src, BondDataType dst)
|
||||||
|
{
|
||||||
|
switch (src)
|
||||||
|
{
|
||||||
|
case BT_UINT8:
|
||||||
|
case BT_UINT16:
|
||||||
|
case BT_UINT32:
|
||||||
|
switch (dst)
|
||||||
|
{
|
||||||
|
case BT_UINT16:
|
||||||
|
case BT_UINT32:
|
||||||
|
case BT_UINT64:
|
||||||
|
return src <= dst;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BT_INT8:
|
||||||
|
case BT_INT16:
|
||||||
|
case BT_INT32:
|
||||||
|
switch (dst)
|
||||||
|
{
|
||||||
|
case BT_INT16:
|
||||||
|
case BT_INT32:
|
||||||
|
case BT_INT64:
|
||||||
|
return src <= dst;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BT_FLOAT:
|
||||||
|
return (dst == BT_DOUBLE);
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (src == dst);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct struct_list
|
||||||
|
{
|
||||||
|
const struct_list* last;
|
||||||
|
const StructDef* dst;
|
||||||
|
const StructDef* src;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline void ValidateStruct(const RuntimeSchema& src,
|
||||||
|
const RuntimeSchema& dst,
|
||||||
|
const struct_list* list,
|
||||||
|
bool& identical);
|
||||||
|
|
||||||
|
inline bool ValidateType(const RuntimeSchema& src,
|
||||||
|
const RuntimeSchema& dst,
|
||||||
|
const struct_list* list,
|
||||||
|
bool& identical)
|
||||||
|
{
|
||||||
|
if (dst.GetTypeId() != src.GetTypeId())
|
||||||
|
{
|
||||||
|
identical = false;
|
||||||
|
return ValidateType(src.GetTypeId(), dst.GetTypeId());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dst.GetType().bonded_type != src.GetType().bonded_type)
|
||||||
|
{
|
||||||
|
identical = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dst.GetTypeId() == BT_STRUCT && !dst.GetType().bonded_type)
|
||||||
|
{
|
||||||
|
ValidateStruct(src, dst, list, identical);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dst.GetType().element.empty())
|
||||||
|
{
|
||||||
|
RuntimeSchema r_dst(dst, *dst.GetType().element);
|
||||||
|
RuntimeSchema r_src(src, *src.GetType().element);
|
||||||
|
|
||||||
|
if (!ValidateType(r_src, r_dst, list, identical))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dst.GetType().key.empty())
|
||||||
|
{
|
||||||
|
RuntimeSchema r_dst(dst, *dst.GetType().key);
|
||||||
|
RuntimeSchema r_src(src, *src.GetType().key);
|
||||||
|
|
||||||
|
if (!ValidateType(r_src, r_dst, list, identical))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void ValidateFields(const RuntimeSchema& src,
|
||||||
|
const RuntimeSchema& dst,
|
||||||
|
const struct_list* list,
|
||||||
|
bool& identical)
|
||||||
|
{
|
||||||
|
const StructDef& s_dst = dst.GetStruct();
|
||||||
|
const StructDef& s_src = src.GetStruct();
|
||||||
|
|
||||||
|
size_t n_dst = s_dst.fields.size();
|
||||||
|
size_t n_src = s_src.fields.size();
|
||||||
|
|
||||||
|
if (n_dst != n_src)
|
||||||
|
{
|
||||||
|
identical = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i_dst = 0, i_src = 0; i_dst < n_dst; )
|
||||||
|
{
|
||||||
|
const FieldDef* f_dst = &s_dst.fields[i_dst];
|
||||||
|
const FieldDef* f_src = (i_src < n_src) ? &s_src.fields[i_src] : NULL;
|
||||||
|
|
||||||
|
if (f_src && f_src->id < f_dst->id)
|
||||||
|
{
|
||||||
|
i_src++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!f_src || f_src->id > f_dst->id)
|
||||||
|
{
|
||||||
|
if (f_dst->metadata.modifier == Required)
|
||||||
|
{
|
||||||
|
RequiredFieldMissingException(s_dst, *f_dst);
|
||||||
|
}
|
||||||
|
|
||||||
|
i_dst++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (f_dst->metadata.modifier == Required &&
|
||||||
|
f_src->metadata.modifier == Optional)
|
||||||
|
{
|
||||||
|
OptionalToRequiredException(s_src, s_dst, *f_src, *f_dst);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ValidateType(RuntimeSchema(src, *f_src),
|
||||||
|
RuntimeSchema(dst, *f_dst),
|
||||||
|
list, identical))
|
||||||
|
{
|
||||||
|
FieldTypeIncompatibleException(s_src, s_dst, *f_src, *f_dst);
|
||||||
|
}
|
||||||
|
|
||||||
|
i_dst++;
|
||||||
|
i_src++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void ValidateStruct(const RuntimeSchema& src,
|
||||||
|
const RuntimeSchema& dst,
|
||||||
|
const struct_list* list,
|
||||||
|
bool& identical)
|
||||||
|
{
|
||||||
|
struct_list next = {
|
||||||
|
list, &dst.GetStruct(), &src.GetStruct()
|
||||||
|
};
|
||||||
|
|
||||||
|
for (; list; list = list->last)
|
||||||
|
{
|
||||||
|
if (list->dst == next.dst &&
|
||||||
|
list->src == next.src)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t d_dst = schema_depth(dst);
|
||||||
|
uint16_t d_src = schema_depth(src);
|
||||||
|
|
||||||
|
if (d_dst > d_src)
|
||||||
|
{
|
||||||
|
StructBaseDifferentException(src.GetStruct(), dst.GetStruct());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (d_src > d_dst)
|
||||||
|
{
|
||||||
|
return ValidateStruct(src.GetBaseSchema(), dst, &next, identical = false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dst.HasBase())
|
||||||
|
{
|
||||||
|
ValidateStruct(src.GetBaseSchema(), dst.GetBaseSchema(), &next, identical);
|
||||||
|
}
|
||||||
|
|
||||||
|
ValidateFields(src, dst, &next, identical);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Checks if payload contains unknown fields
|
||||||
|
class SchemaValidator
|
||||||
|
: public DeserializingTransform
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void Begin(const Metadata&) const
|
||||||
|
{}
|
||||||
|
|
||||||
|
void End() const
|
||||||
|
{}
|
||||||
|
|
||||||
|
void UnknownEnd() const
|
||||||
|
{}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool Base(const T& value) const
|
||||||
|
{
|
||||||
|
return Recurse(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool Field(uint16_t, const Metadata&, const T& value) const
|
||||||
|
{
|
||||||
|
return Recurse(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool UnknownField(uint16_t id, const T&) const
|
||||||
|
{
|
||||||
|
UnknownSchemaDefException(id);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void Container(const T& element, uint32_t size) const
|
||||||
|
{
|
||||||
|
while (size--)
|
||||||
|
Recurse(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Key, typename T>
|
||||||
|
void Container(const Key&, const T& value, uint32_t size) const
|
||||||
|
{
|
||||||
|
while (size--)
|
||||||
|
Recurse(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <typename T>
|
||||||
|
bool Recurse(const T&) const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename Reader>
|
||||||
|
typename boost::enable_if<is_basic_type<T>, bool>::type
|
||||||
|
Recurse(const value<T, Reader>&) const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename Reader>
|
||||||
|
typename boost::disable_if<is_basic_type<T>, bool>::type
|
||||||
|
Recurse(const value<T, Reader>& value) const
|
||||||
|
{
|
||||||
|
Apply(*this, value);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename Reader>
|
||||||
|
bool Recurse(const bonded<T, Reader>& value) const
|
||||||
|
{
|
||||||
|
return Apply(*this, value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
} // namespace bond
|
|
@ -0,0 +1,176 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "detail/string_stream.h"
|
||||||
|
#include <bond/core/bond_types.h>
|
||||||
|
|
||||||
|
#define BOND_THROW(x, y) throw x((bond::detail::basic_string_stream<1024>() << y).content());
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
|
||||||
|
//
|
||||||
|
// Base type for all bond exceptions.
|
||||||
|
//
|
||||||
|
class Exception
|
||||||
|
: public std::exception,
|
||||||
|
public SerializableExceptionBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
const char* what() const throw()
|
||||||
|
{
|
||||||
|
return message.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~Exception() throw()
|
||||||
|
{}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Exception(const char* msg) throw()
|
||||||
|
{
|
||||||
|
message = msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
Exception() throw()
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct CoreException
|
||||||
|
: Exception
|
||||||
|
{
|
||||||
|
CoreException(const char* message)
|
||||||
|
: Exception(message)
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
inline void MergerContainerException(uint32_t payload, uint32_t obj)
|
||||||
|
{
|
||||||
|
BOND_THROW(CoreException,
|
||||||
|
"Merge failed: container mismatch, length in the payload: "
|
||||||
|
<< payload << " length in the object: " << obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Key>
|
||||||
|
inline void ElementNotFoundException(const Key& key)
|
||||||
|
{
|
||||||
|
BOND_THROW(CoreException,
|
||||||
|
"Map element not found: key: " << key);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline void UnknownProtocolException()
|
||||||
|
{
|
||||||
|
BOND_THROW(CoreException,
|
||||||
|
"Unmarshaling failed: unsupported protocol");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline void UnknownProtocolException(uint16_t magic)
|
||||||
|
{
|
||||||
|
BOND_THROW(CoreException,
|
||||||
|
"Unsupported protocol: "
|
||||||
|
<< (char)(magic & 0xFF) << (char)(magic >> 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline void NothingException()
|
||||||
|
{
|
||||||
|
BOND_THROW(CoreException,
|
||||||
|
"Field value is 'nothing'");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline void InvalidEnumValueException(const char* value, const char* enum_)
|
||||||
|
{
|
||||||
|
BOND_THROW(bond::CoreException,
|
||||||
|
"Unexpected value " << value << " for enum " << enum_);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline void InvalidEnumValueException(int32_t value, const char* enum_)
|
||||||
|
{
|
||||||
|
BOND_THROW(bond::CoreException,
|
||||||
|
"Unexpected value " << value << " for enum " << enum_);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline void RapidJsonException(const char* error, size_t offset)
|
||||||
|
{
|
||||||
|
BOND_THROW(CoreException,
|
||||||
|
"JSON parser error: " << error << " at offset " << offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct StreamException
|
||||||
|
: Exception
|
||||||
|
{
|
||||||
|
StreamException(const char* message)
|
||||||
|
: Exception(message)
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct SchemaValidateException
|
||||||
|
: CoreException
|
||||||
|
{
|
||||||
|
SchemaValidateException(const char* message)
|
||||||
|
: CoreException(message)
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
inline void StructBaseDifferentException(const StructDef& src,
|
||||||
|
const StructDef& dst)
|
||||||
|
{
|
||||||
|
BOND_THROW(SchemaValidateException,
|
||||||
|
"Schemas are incompatible; struct base different: "
|
||||||
|
<< src.metadata.name << ", " << dst.metadata.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline void RequiredFieldMissingException(const StructDef& s_dst,
|
||||||
|
const FieldDef& f_dst)
|
||||||
|
{
|
||||||
|
BOND_THROW(SchemaValidateException,
|
||||||
|
"Schemas are incompatible; required field missing: "
|
||||||
|
<< s_dst.metadata.name << "::" << f_dst.metadata.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline void OptionalToRequiredException(const StructDef& s_src,
|
||||||
|
const StructDef& s_dst,
|
||||||
|
const FieldDef& f_src,
|
||||||
|
const FieldDef& f_dst)
|
||||||
|
{
|
||||||
|
BOND_THROW(SchemaValidateException,
|
||||||
|
"Schemas are incompatible; required modifier removed: "
|
||||||
|
<< s_src.metadata.name << "::" << f_src.metadata.name << ", "
|
||||||
|
<< s_dst.metadata.name << "::" << f_dst.metadata.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline void FieldTypeIncompatibleException(const StructDef& s_src,
|
||||||
|
const StructDef& s_dst,
|
||||||
|
const FieldDef& f_src,
|
||||||
|
const FieldDef& f_dst)
|
||||||
|
{
|
||||||
|
BOND_THROW(SchemaValidateException,
|
||||||
|
"Schemas are incompatible; field types incompatible: "
|
||||||
|
<< s_src.metadata.name << "::" << f_src.metadata.name << ", "
|
||||||
|
<< s_dst.metadata.name << "::" << f_dst.metadata.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline void UnknownSchemaDefException(uint16_t id)
|
||||||
|
{
|
||||||
|
BOND_THROW(SchemaValidateException,
|
||||||
|
"Failed to validate schema compatibility; "
|
||||||
|
"SchemaDef contains unknown field: " << id);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace bond
|
|
@ -0,0 +1,151 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
void NothingException();
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Type used for fields with default value of 'nothing'
|
||||||
|
/// See [User's Manual](../../manual/bond_cpp.html#default-value-of-nothing)
|
||||||
|
template <typename T>
|
||||||
|
class maybe
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef T value_type;
|
||||||
|
|
||||||
|
/// @brief Default constructor
|
||||||
|
maybe()
|
||||||
|
: _nothing(true)
|
||||||
|
{}
|
||||||
|
|
||||||
|
#ifndef BOND_NO_CXX11_RVALUE_REFERENCES
|
||||||
|
/// @brief Move constructor
|
||||||
|
maybe(maybe&& that)
|
||||||
|
: _value(std::move(that._value)),
|
||||||
|
_nothing(std::move(that._nothing))
|
||||||
|
{
|
||||||
|
that._nothing = true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef BOND_NO_CXX11_DEFAULTED_FUNCTIONS
|
||||||
|
maybe(const maybe& that) = default;
|
||||||
|
maybe& operator=(const maybe& that) = default;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// @brief Constructor from a value T
|
||||||
|
explicit
|
||||||
|
maybe(const T& value)
|
||||||
|
: _value(value),
|
||||||
|
_nothing(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Allocator>
|
||||||
|
explicit maybe(Allocator& alloc)
|
||||||
|
: _value(alloc),
|
||||||
|
_nothing(true)
|
||||||
|
{}
|
||||||
|
|
||||||
|
template <typename Compare, typename Allocator>
|
||||||
|
explicit maybe(const Compare& comp, Allocator& alloc)
|
||||||
|
: _value(comp, alloc),
|
||||||
|
_nothing(true)
|
||||||
|
{}
|
||||||
|
|
||||||
|
/// @brief Swap this object with that object
|
||||||
|
void swap(maybe& that)
|
||||||
|
{
|
||||||
|
using std::swap;
|
||||||
|
|
||||||
|
swap(_nothing, that._nothing);
|
||||||
|
swap(_value, that._value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Check if this object contains nothing
|
||||||
|
bool is_nothing() const
|
||||||
|
{
|
||||||
|
return _nothing;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Set to default instance of T and return reference to that value
|
||||||
|
T& set_value()
|
||||||
|
{
|
||||||
|
_nothing = false;
|
||||||
|
return _value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Set to nothing
|
||||||
|
void set_nothing()
|
||||||
|
{
|
||||||
|
_nothing = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Reference to the value
|
||||||
|
/// @throw NothingException if the object contains nothing
|
||||||
|
T& value()
|
||||||
|
{
|
||||||
|
if (_nothing)
|
||||||
|
NothingException();
|
||||||
|
|
||||||
|
return _value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Constant reference to the value
|
||||||
|
/// @throw NothingException if the object contains nothing
|
||||||
|
const T& value() const
|
||||||
|
{
|
||||||
|
if (_nothing)
|
||||||
|
NothingException();
|
||||||
|
|
||||||
|
return _value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Compare for equality
|
||||||
|
bool operator==(const maybe& that) const
|
||||||
|
{
|
||||||
|
return _nothing == that._nothing
|
||||||
|
&& (_nothing || _value == that._value);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const maybe& that) const
|
||||||
|
{
|
||||||
|
return !(*this == that);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Assign a value T
|
||||||
|
maybe& operator=(const T& value)
|
||||||
|
{
|
||||||
|
_value = value;
|
||||||
|
_nothing = false;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Convert to constant reference to value
|
||||||
|
/// @throw NothingException if the object contains nothing
|
||||||
|
operator const T&() const
|
||||||
|
{
|
||||||
|
return value();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
T _value;
|
||||||
|
bool _nothing;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline void swap(maybe<T>& x, maybe<T>& y)
|
||||||
|
{
|
||||||
|
x.swap(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace bond
|
|
@ -0,0 +1,157 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "reflection.h"
|
||||||
|
#include "exception.h"
|
||||||
|
#include "transforms.h"
|
||||||
|
#include "detail/tags.h"
|
||||||
|
#include "detail/omit_default.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Writer>
|
||||||
|
class Merger
|
||||||
|
: public Serializer<Writer>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef T FastPathType;
|
||||||
|
|
||||||
|
using Serializer<Writer>::Base;
|
||||||
|
using Serializer<Writer>::Field;
|
||||||
|
using Serializer<Writer>::Write;
|
||||||
|
using Serializer<Writer>::OmittedField;
|
||||||
|
using Serializer<Writer>::Container;
|
||||||
|
|
||||||
|
Merger(const T& var, Writer& output, bool base = false)
|
||||||
|
: Serializer<Writer>(output, base),
|
||||||
|
_var(var)
|
||||||
|
{}
|
||||||
|
|
||||||
|
template <typename Pass0>
|
||||||
|
Merger<T, Pass0> Rebind(Pass0& pass0) const
|
||||||
|
{
|
||||||
|
return Merger<T, Pass0>(_var, pass0);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename X, typename Reader>
|
||||||
|
typename boost::enable_if<has_schema<X>, bool>::type
|
||||||
|
Base(const bonded<X, Reader>& value) const
|
||||||
|
{
|
||||||
|
return Apply(Merger<typename schema<T>::type::base, Writer>(_var, _output, true), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename FieldT, typename X>
|
||||||
|
typename boost::enable_if_c<is_struct_field<FieldT>::value
|
||||||
|
|| is_struct_container_field<FieldT>::value, bool>::type
|
||||||
|
Field(const FieldT&, const X& value) const
|
||||||
|
{
|
||||||
|
_output.WriteFieldBegin(GetTypeId(value), FieldT::id, FieldT::metadata);
|
||||||
|
Merge(FieldT::GetVariable(_var), value);
|
||||||
|
_output.WriteFieldEnd();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename FieldT, typename X>
|
||||||
|
typename boost::disable_if_c<is_struct_field<FieldT>::value
|
||||||
|
|| is_struct_container_field<FieldT>::value, bool>::type
|
||||||
|
Field(const FieldT&, const X&) const
|
||||||
|
{
|
||||||
|
return this->Field(FieldT::id, FieldT::metadata, FieldT::GetVariable(_var));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename FieldT>
|
||||||
|
bool OmittedField(const FieldT&) const
|
||||||
|
{
|
||||||
|
return this->Field(FieldT::id, FieldT::metadata, FieldT::GetVariable(_var));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename X, typename Reader>
|
||||||
|
typename boost::enable_if<is_element_matching<value<X, Reader>, T> >::type
|
||||||
|
Container(const value<X, Reader>& element, uint32_t size) const
|
||||||
|
{
|
||||||
|
Merge(_var, element, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename X, typename Reader>
|
||||||
|
typename boost::enable_if<is_element_matching<bonded<X, Reader>, T> >::type
|
||||||
|
Container(const bonded<X, Reader>& element, uint32_t size) const
|
||||||
|
{
|
||||||
|
Merge(_var, element, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Key, typename X, typename Reader>
|
||||||
|
typename boost::enable_if_c<is_map_element_matching<X, T>::value
|
||||||
|
&& is_map_key_matching<Key, T>::value>::type
|
||||||
|
Container(const value<Key, Reader>& key, const X& value, uint32_t size) const
|
||||||
|
{
|
||||||
|
Merge(_var, key, value, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected:
|
||||||
|
using Serializer<Writer>::_output;
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <typename U, typename X>
|
||||||
|
void Merge(const U& var, const X& value) const
|
||||||
|
{
|
||||||
|
Apply(Merger<U, Writer>(var, _output), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename U, typename X>
|
||||||
|
void Merge(const U& var, const X& element, uint32_t size) const
|
||||||
|
{
|
||||||
|
if (size != container_size(var))
|
||||||
|
MergerContainerException(size, container_size(var));
|
||||||
|
|
||||||
|
_output.WriteContainerBegin(size, get_type_id<typename element_type<U>::type>::value);
|
||||||
|
|
||||||
|
for (const_enumerator<U> items(var); items.more();)
|
||||||
|
Merge(items.next(), element);
|
||||||
|
|
||||||
|
_output.WriteContainerEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename U, typename Key, typename X>
|
||||||
|
void Merge(const U& var, const Key& key, const X& value, uint32_t size) const
|
||||||
|
{
|
||||||
|
if (size != container_size(var))
|
||||||
|
MergerContainerException(size, container_size(var));
|
||||||
|
|
||||||
|
_output.WriteContainerBegin(size, get_type_id<typename element_type<U>::type>::value);
|
||||||
|
|
||||||
|
typename element_type<U>::type::first_type k;
|
||||||
|
|
||||||
|
while (size--)
|
||||||
|
{
|
||||||
|
key.Deserialize(k);
|
||||||
|
|
||||||
|
Write(k);
|
||||||
|
|
||||||
|
// Elements of a map migth be serialized out of order, so we must
|
||||||
|
// look up the element to merge by key.
|
||||||
|
Merge(mapped_at(var, k), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
_output.WriteContainerEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
const T& _var;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace bond
|
|
@ -0,0 +1,43 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "detail/tags.h"
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
|
||||||
|
class Null
|
||||||
|
: public DeserializingTransform
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void Begin(const Metadata&) const
|
||||||
|
{}
|
||||||
|
|
||||||
|
void End() const
|
||||||
|
{}
|
||||||
|
|
||||||
|
void UnknownEnd() const
|
||||||
|
{}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool Base(const T& base) const
|
||||||
|
{
|
||||||
|
return Apply(*this, base);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool Field(uint16_t, const Metadata&, const T&) const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool UnknownField(uint16_t, const T&) const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,655 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "container_interface.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <boost/assert.hpp>
|
||||||
|
#include <boost/static_assert.hpp>
|
||||||
|
#include <boost/noncopyable.hpp>
|
||||||
|
#include <boost/utility/enable_if.hpp>
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
use_value
|
||||||
|
{
|
||||||
|
static const bool value = is_list_container<T>::value
|
||||||
|
|| is_set_container<T>::value
|
||||||
|
|| is_map_container<T>::value
|
||||||
|
|| is_string<T>::value
|
||||||
|
|| is_wstring<T>::value
|
||||||
|
|| !is_class<T>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T, typename E = void> struct
|
||||||
|
has_compare
|
||||||
|
: false_type {};
|
||||||
|
|
||||||
|
template<typename T> struct
|
||||||
|
has_compare<T, typename boost::enable_if<is_class<typename T::key_compare> >::type>
|
||||||
|
: true_type {};
|
||||||
|
|
||||||
|
template<typename T, typename E = void> struct
|
||||||
|
has_allocator
|
||||||
|
: false_type {};
|
||||||
|
|
||||||
|
template<typename T> struct
|
||||||
|
has_allocator<T, typename boost::enable_if<is_class<typename T::allocator_type> >::type>
|
||||||
|
: true_type {};
|
||||||
|
|
||||||
|
struct no_allocator
|
||||||
|
{
|
||||||
|
template<typename T>
|
||||||
|
struct rebind
|
||||||
|
{
|
||||||
|
typedef no_allocator other;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T, typename E = void> struct
|
||||||
|
allocator_type
|
||||||
|
{
|
||||||
|
typedef no_allocator type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T> struct
|
||||||
|
allocator_type<T, typename boost::enable_if<has_allocator<T> >::type>
|
||||||
|
{
|
||||||
|
typedef typename T::allocator_type type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
typename boost::enable_if<has_allocator<T>, typename allocator_type<T>::type>::type
|
||||||
|
get_allocator(const T& value)
|
||||||
|
{
|
||||||
|
return value.get_allocator();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
typename boost::disable_if<has_allocator<T>, typename allocator_type<T>::type>::type
|
||||||
|
get_allocator(const T&)
|
||||||
|
{
|
||||||
|
return typename allocator_type<T>::type();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Nullable value
|
||||||
|
//
|
||||||
|
template<typename T,
|
||||||
|
typename Allocator = typename detail::allocator_type<T>::type,
|
||||||
|
bool useValue = detail::use_value<T>::value>
|
||||||
|
class nullable;
|
||||||
|
|
||||||
|
template<typename T, typename Allocator>
|
||||||
|
class nullable<T, Allocator, true>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef T value_type;
|
||||||
|
typedef T* pointer;
|
||||||
|
typedef const T* const_pointer;
|
||||||
|
typedef T& reference;
|
||||||
|
typedef const T& const_reference;
|
||||||
|
typedef Allocator allocator_type;
|
||||||
|
|
||||||
|
bool hasvalue() const
|
||||||
|
{
|
||||||
|
return _hasvalue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty() const
|
||||||
|
{
|
||||||
|
return !hasvalue();
|
||||||
|
}
|
||||||
|
|
||||||
|
void swap(nullable& src)
|
||||||
|
{
|
||||||
|
std::swap(_alloc, src._alloc);
|
||||||
|
std::swap(_hasvalue, src._hasvalue);
|
||||||
|
std::swap(_value, src._value);
|
||||||
|
}
|
||||||
|
|
||||||
|
nullable()
|
||||||
|
: _value(),
|
||||||
|
_hasvalue(false)
|
||||||
|
{}
|
||||||
|
|
||||||
|
explicit
|
||||||
|
nullable(const allocator_type& alloc)
|
||||||
|
: _alloc(alloc),
|
||||||
|
_value(make_value<value_type>()),
|
||||||
|
_hasvalue(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// deprecated!
|
||||||
|
template <typename Compare>
|
||||||
|
explicit
|
||||||
|
nullable(const Compare&,
|
||||||
|
const allocator_type& alloc)
|
||||||
|
: _alloc(alloc),
|
||||||
|
_value(make_value<value_type>()),
|
||||||
|
_hasvalue(false)
|
||||||
|
{}
|
||||||
|
|
||||||
|
explicit
|
||||||
|
nullable(const value_type& value)
|
||||||
|
: _alloc(detail::get_allocator(value)),
|
||||||
|
_value(value),
|
||||||
|
_hasvalue(true)
|
||||||
|
{}
|
||||||
|
|
||||||
|
nullable(const nullable& src)
|
||||||
|
: _alloc(src._alloc),
|
||||||
|
_value(src._value),
|
||||||
|
_hasvalue(src._hasvalue)
|
||||||
|
{}
|
||||||
|
|
||||||
|
nullable& operator=(const nullable& src)
|
||||||
|
{
|
||||||
|
nullable(src).swap(*this);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
reference value()
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(hasvalue());
|
||||||
|
return _value;
|
||||||
|
}
|
||||||
|
|
||||||
|
const_reference value() const
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(hasvalue());
|
||||||
|
return _value;
|
||||||
|
}
|
||||||
|
|
||||||
|
reference operator*()
|
||||||
|
{
|
||||||
|
return value();
|
||||||
|
}
|
||||||
|
|
||||||
|
const_reference operator*() const
|
||||||
|
{
|
||||||
|
return value();
|
||||||
|
}
|
||||||
|
|
||||||
|
pointer operator->()
|
||||||
|
{
|
||||||
|
return &value();
|
||||||
|
}
|
||||||
|
|
||||||
|
const_pointer operator->() const
|
||||||
|
{
|
||||||
|
return &value();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!() const
|
||||||
|
{
|
||||||
|
return empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
reference set()
|
||||||
|
{
|
||||||
|
_hasvalue = true;
|
||||||
|
return _value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set(const_reference value)
|
||||||
|
{
|
||||||
|
_value = value;
|
||||||
|
_hasvalue = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset()
|
||||||
|
{
|
||||||
|
_value = make_value<value_type>();
|
||||||
|
_hasvalue = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
allocator_type get_allocator() const
|
||||||
|
{
|
||||||
|
return _alloc;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef BOND_NO_CXX11_RVALUE_REFERENCES
|
||||||
|
explicit
|
||||||
|
nullable(value_type&& value)
|
||||||
|
: _alloc(detail::get_allocator(value)),
|
||||||
|
_value(std::move(value)),
|
||||||
|
_hasvalue(true)
|
||||||
|
{}
|
||||||
|
|
||||||
|
nullable(nullable&& src)
|
||||||
|
: _alloc(std::move(src._alloc)),
|
||||||
|
_value(std::move(src._value)),
|
||||||
|
_hasvalue(std::move(src._hasvalue))
|
||||||
|
{
|
||||||
|
src._hasvalue = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
nullable& operator=(nullable&& src)
|
||||||
|
{
|
||||||
|
if (this != &src)
|
||||||
|
{
|
||||||
|
_alloc = std::move(src._alloc);
|
||||||
|
_value = std::move(src._value);
|
||||||
|
_hasvalue = std::move(src._hasvalue);
|
||||||
|
src._hasvalue = false;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set(value_type&& value)
|
||||||
|
{
|
||||||
|
_value = std::move(value);
|
||||||
|
_hasvalue = true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private:
|
||||||
|
template<typename U>
|
||||||
|
typename boost::enable_if_c<detail::has_allocator<U>::value &&
|
||||||
|
detail::has_compare<U>::value, U>::type
|
||||||
|
make_value()
|
||||||
|
{
|
||||||
|
return U(typename U::key_compare(), _alloc);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename U>
|
||||||
|
typename boost::enable_if_c<detail::has_allocator<U>::value &&
|
||||||
|
!detail::has_compare<U>::value, U>::type
|
||||||
|
make_value()
|
||||||
|
{
|
||||||
|
return U(_alloc);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename U>
|
||||||
|
typename boost::disable_if<detail::has_allocator<U>, U>::type
|
||||||
|
make_value()
|
||||||
|
{
|
||||||
|
return U();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
allocator_type _alloc;
|
||||||
|
value_type _value;
|
||||||
|
bool _hasvalue;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/** @brief Nullable type */
|
||||||
|
/** See [User's Manual](../../manual/bond_cpp.html#nullable-types) */
|
||||||
|
template<typename T, typename Allocator>
|
||||||
|
class nullable<T, Allocator, false>
|
||||||
|
{
|
||||||
|
BOOST_STATIC_ASSERT(!detail::use_value<T>::value);
|
||||||
|
|
||||||
|
public:
|
||||||
|
typedef T value_type;
|
||||||
|
typedef T* pointer;
|
||||||
|
typedef const T* const_pointer;
|
||||||
|
typedef T& reference;
|
||||||
|
typedef const T& const_reference;
|
||||||
|
typedef Allocator allocator_type;
|
||||||
|
|
||||||
|
bool hasvalue() const
|
||||||
|
{
|
||||||
|
return _value != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Checks if the object is null
|
||||||
|
bool empty() const
|
||||||
|
{
|
||||||
|
return !hasvalue();
|
||||||
|
}
|
||||||
|
|
||||||
|
void swap(nullable& src)
|
||||||
|
{
|
||||||
|
std::swap(_alloc, src._alloc);
|
||||||
|
std::swap(_value, src._value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Default constructor
|
||||||
|
nullable()
|
||||||
|
: _value(0)
|
||||||
|
{}
|
||||||
|
|
||||||
|
/// @brief Construct nullable using specified allocator instance
|
||||||
|
explicit
|
||||||
|
nullable(const allocator_type& alloc)
|
||||||
|
: _alloc(alloc),
|
||||||
|
_value(0)
|
||||||
|
{}
|
||||||
|
|
||||||
|
/// @brief Construct from an instance T
|
||||||
|
explicit
|
||||||
|
nullable(const value_type& value,
|
||||||
|
const allocator_type& alloc = allocator_type())
|
||||||
|
: _alloc(alloc),
|
||||||
|
_value(new_value(_alloc, value))
|
||||||
|
{}
|
||||||
|
|
||||||
|
/// @brief Copy constructor
|
||||||
|
nullable(const nullable& src)
|
||||||
|
: _alloc(src._alloc),
|
||||||
|
_value(src.hasvalue() ? new_value(_alloc, src.value()) : 0)
|
||||||
|
{}
|
||||||
|
|
||||||
|
~nullable()
|
||||||
|
{
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Assignment operator
|
||||||
|
nullable& operator=(const nullable& src)
|
||||||
|
{
|
||||||
|
nullable(src).swap(*this);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Return reference to contained value
|
||||||
|
///
|
||||||
|
/// Undefined if the object is null
|
||||||
|
reference value()
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(hasvalue());
|
||||||
|
return *_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Return constant reference to contained value
|
||||||
|
///
|
||||||
|
/// Undefined if the object is null
|
||||||
|
const_reference value() const
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(hasvalue());
|
||||||
|
return *_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Dereference operator
|
||||||
|
///
|
||||||
|
/// Undefined if the object is null
|
||||||
|
reference operator*()
|
||||||
|
{
|
||||||
|
return value();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Dereference operator
|
||||||
|
///
|
||||||
|
/// Undefined if the object is null
|
||||||
|
const_reference operator*() const
|
||||||
|
{
|
||||||
|
return value();
|
||||||
|
}
|
||||||
|
|
||||||
|
pointer operator->()
|
||||||
|
{
|
||||||
|
return &value();
|
||||||
|
}
|
||||||
|
|
||||||
|
const_pointer operator->() const
|
||||||
|
{
|
||||||
|
return &value();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!() const
|
||||||
|
{
|
||||||
|
return empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Set to default instance of T and return reference to the value
|
||||||
|
reference set()
|
||||||
|
{
|
||||||
|
if (empty())
|
||||||
|
_value = new_value(_alloc);
|
||||||
|
return *_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Set to specified value
|
||||||
|
void set(const_reference value)
|
||||||
|
{
|
||||||
|
if (empty())
|
||||||
|
_value = new_value(_alloc, value);
|
||||||
|
else
|
||||||
|
*_value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Reset to null
|
||||||
|
void reset()
|
||||||
|
{
|
||||||
|
if (_value)
|
||||||
|
{
|
||||||
|
destroy(_alloc);
|
||||||
|
_value = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief The same as reset
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
allocator_type get_allocator() const
|
||||||
|
{
|
||||||
|
return _alloc;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef BOND_NO_CXX11_RVALUE_REFERENCES
|
||||||
|
explicit
|
||||||
|
nullable(value_type&& value,
|
||||||
|
const allocator_type& alloc = allocator_type())
|
||||||
|
: _alloc(alloc),
|
||||||
|
_value(new_value(_alloc, std::move(value)))
|
||||||
|
{}
|
||||||
|
|
||||||
|
nullable(nullable&& src)
|
||||||
|
: _alloc(std::move(src._alloc)),
|
||||||
|
_value(std::move(src._value))
|
||||||
|
{
|
||||||
|
src._value = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
nullable& operator=(nullable&& src)
|
||||||
|
{
|
||||||
|
nullable(std::move(src)).swap(*this);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set(value_type&& value)
|
||||||
|
{
|
||||||
|
if (empty())
|
||||||
|
_value = new_value(_alloc, std::move(value));
|
||||||
|
else
|
||||||
|
*_value = std::move(value);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private:
|
||||||
|
template<typename AllocatorT>
|
||||||
|
void destroy(AllocatorT& alloc)
|
||||||
|
{
|
||||||
|
alloc.destroy(_value);
|
||||||
|
alloc.deallocate(_value, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroy(detail::no_allocator&)
|
||||||
|
{
|
||||||
|
delete _value;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename AllocatorT, typename Arg1>
|
||||||
|
#ifndef BOND_NO_CXX11_RVALUE_REFERENCES
|
||||||
|
pointer new_value(AllocatorT& alloc, Arg1&& arg1)
|
||||||
|
#else
|
||||||
|
pointer new_value(AllocatorT& alloc, const Arg1& arg1)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
T* p = alloc.allocate(1);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
void* p1 = p;
|
||||||
|
return ::new(p1) T(arg1);
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
alloc.deallocate(p, 1);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename AllocatorT>
|
||||||
|
pointer new_value(AllocatorT& alloc)
|
||||||
|
{
|
||||||
|
return new_value(alloc, alloc);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Arg1>
|
||||||
|
#ifndef BOND_NO_CXX11_RVALUE_REFERENCES
|
||||||
|
pointer new_value(detail::no_allocator&, Arg1&& arg1)
|
||||||
|
#else
|
||||||
|
pointer new_value(detail::no_allocator&, const Arg1& arg1)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
return new T(arg1);
|
||||||
|
}
|
||||||
|
|
||||||
|
pointer new_value(detail::no_allocator&)
|
||||||
|
{
|
||||||
|
return new T();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
typename allocator_type::template rebind<value_type>::other _alloc;
|
||||||
|
pointer _value;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template<typename T, typename Allocator, bool useValue>
|
||||||
|
inline void swap(nullable<T, Allocator, useValue>& x,
|
||||||
|
nullable<T, Allocator, useValue>& y)
|
||||||
|
{
|
||||||
|
x.swap(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<typename T, typename Allocator, bool useValue>
|
||||||
|
inline bool operator==(const nullable<T, Allocator, useValue>& x,
|
||||||
|
const nullable<T, Allocator, useValue>& y)
|
||||||
|
{
|
||||||
|
return (x.hasvalue() == y.hasvalue()
|
||||||
|
&& (!x.hasvalue() || *x == *y));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<typename T, typename Allocator, bool useValue>
|
||||||
|
inline bool operator!= (const nullable<T, Allocator, useValue>& x,
|
||||||
|
const nullable<T, Allocator, useValue>& y)
|
||||||
|
{
|
||||||
|
return !(x == y);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// nullable<T> is internally treated as a list container with 0 or 1 element
|
||||||
|
|
||||||
|
// container_size
|
||||||
|
template <typename T, typename Allocator, bool useValue>
|
||||||
|
uint32_t container_size(const nullable<T, Allocator, useValue>& value)
|
||||||
|
{
|
||||||
|
return value.empty() ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// resize_list
|
||||||
|
template <typename T, typename Allocator, bool useValue>
|
||||||
|
void resize_list(nullable<T, Allocator, useValue>& value, uint32_t size)
|
||||||
|
{
|
||||||
|
if (size)
|
||||||
|
value.set();
|
||||||
|
else
|
||||||
|
value.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Allocator, bool useValue> struct
|
||||||
|
element_type<nullable<T, Allocator, useValue> >
|
||||||
|
{
|
||||||
|
typedef T type;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// enumerators
|
||||||
|
template <typename T, typename Allocator, bool useValue>
|
||||||
|
class const_enumerator<nullable<T, Allocator, useValue> >
|
||||||
|
: boost::noncopyable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
const_enumerator(const nullable<T, Allocator, useValue>& value)
|
||||||
|
: _value(value),
|
||||||
|
_more(value.hasvalue())
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool more()
|
||||||
|
{
|
||||||
|
return _more;
|
||||||
|
}
|
||||||
|
|
||||||
|
const T& next()
|
||||||
|
{
|
||||||
|
_more = false;
|
||||||
|
return _value.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const nullable<T, Allocator, useValue>& _value;
|
||||||
|
bool _more;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Allocator, bool useValue>
|
||||||
|
class enumerator<nullable<T, Allocator, useValue> >
|
||||||
|
: boost::noncopyable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enumerator(nullable<T, Allocator, useValue>& value)
|
||||||
|
: _value(value),
|
||||||
|
_more(value.hasvalue())
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool more()
|
||||||
|
{
|
||||||
|
return _more;
|
||||||
|
}
|
||||||
|
|
||||||
|
T& next()
|
||||||
|
{
|
||||||
|
_more = false;
|
||||||
|
return _value.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
nullable<T, Allocator, useValue>& _value;
|
||||||
|
bool _more;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Allocator, bool useValue> struct
|
||||||
|
is_list_container<nullable<T, Allocator, useValue> >
|
||||||
|
: true_type {};
|
||||||
|
|
||||||
|
|
||||||
|
}; // namespace bond
|
||||||
|
|
|
@ -0,0 +1,652 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "reflection.h"
|
||||||
|
#include "detail/typeid_value.h"
|
||||||
|
#include "value.h"
|
||||||
|
#include "transforms.h"
|
||||||
|
#include "merge.h"
|
||||||
|
#include "schema.h"
|
||||||
|
#include "detail/inheritance.h"
|
||||||
|
#include <bond/protocol/simple_binary_impl.h>
|
||||||
|
#include <bond/protocol/simple_json_reader_impl.h>
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
class ParserCommon
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
template <typename Transform>
|
||||||
|
bool
|
||||||
|
ReadFields(const boost::mpl::l_iter<boost::mpl::l_end>&, const Transform&)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Fields>
|
||||||
|
void SkipFields(const Fields&)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename Transform>
|
||||||
|
typename boost::disable_if<is_fast_path_field<T, Transform>, bool>::type
|
||||||
|
OmittedField(const T&, const Transform& transform)
|
||||||
|
{
|
||||||
|
return transform.OmittedField(T::id, T::metadata, get_type_id<typename T::field_type>::value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Transform>
|
||||||
|
typename boost::enable_if<is_fast_path_field<T, Transform>, bool>::type
|
||||||
|
OmittedField(const T& field, const Transform& transform)
|
||||||
|
{
|
||||||
|
return transform.OmittedField(field);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Transform>
|
||||||
|
struct UnknownFieldBinder
|
||||||
|
: detail::nonassignable
|
||||||
|
{
|
||||||
|
UnknownFieldBinder(Transform& transform)
|
||||||
|
: transform(transform)
|
||||||
|
{}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool Field(uint16_t id, const Metadata& /*metadata*/, const T& value) const
|
||||||
|
{
|
||||||
|
return transform.UnknownField(id, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
Transform& transform;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Transform>
|
||||||
|
UnknownFieldBinder<Transform> BindUnknownField(Transform& transform)
|
||||||
|
{
|
||||||
|
return UnknownFieldBinder<Transform>(transform);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
//
|
||||||
|
// StaticParser iterates serialized data using type schema and calls
|
||||||
|
// specified transform for each data field.
|
||||||
|
// The schema may be provided at compile-time (schema<T>::type::fields) or at runtime
|
||||||
|
// (const RuntimeSchema&). StaticParser is used with protocols which don't
|
||||||
|
// tag fields in serialized format with ids or types, e.g. Apache Avro protocol.
|
||||||
|
//
|
||||||
|
template <typename Input>
|
||||||
|
class StaticParser
|
||||||
|
: protected detail::ParserInheritance<Input, StaticParser<Input> >,
|
||||||
|
public detail::ParserCommon
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
StaticParser(Input input, bool base = false)
|
||||||
|
: detail::ParserInheritance<Input, StaticParser<Input> >(input, base)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Schema, typename Transform>
|
||||||
|
bool
|
||||||
|
Apply(const Transform& transform, const Schema& schema)
|
||||||
|
{
|
||||||
|
detail::StructBegin(_input, _base);
|
||||||
|
bool result = this->Read(schema, transform);
|
||||||
|
detail::StructEnd(_input, _base);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend class detail::ParserInheritance<Input, StaticParser<Input> >;
|
||||||
|
|
||||||
|
|
||||||
|
protected:
|
||||||
|
using detail::ParserInheritance<Input, StaticParser<Input> >::_input;
|
||||||
|
using detail::ParserInheritance<Input, StaticParser<Input> >::_base;
|
||||||
|
using detail::ParserCommon::ReadFields;
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <typename Fields>
|
||||||
|
void SkipFields(const Fields& fields)
|
||||||
|
{
|
||||||
|
// Skip the structure by reading fields to Null transform
|
||||||
|
ReadFields(fields, Null());
|
||||||
|
}
|
||||||
|
|
||||||
|
// use compile-time schema
|
||||||
|
template <typename Fields, typename Transform>
|
||||||
|
bool
|
||||||
|
ReadFields(const Fields&, const Transform& transform)
|
||||||
|
{
|
||||||
|
typedef typename boost::mpl::deref<Fields>::type Head;
|
||||||
|
|
||||||
|
if (detail::ReadFieldOmitted(_input))
|
||||||
|
OmittedField(Head(), transform);
|
||||||
|
else
|
||||||
|
if (bool done = NextField(Head(), transform))
|
||||||
|
return done;
|
||||||
|
|
||||||
|
return ReadFields(typename boost::mpl::next<Fields>::type(), transform);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Transform>
|
||||||
|
typename boost::enable_if_c<detail::is_reader<Input>::value && !is_nested_field<T>::value
|
||||||
|
&& !is_fast_path_field<T, Transform>::value, bool>::type
|
||||||
|
NextField(const T&, const Transform& transform)
|
||||||
|
{
|
||||||
|
return transform.Field(T::id, T::metadata, value<typename T::field_type, Input>(_input));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Transform>
|
||||||
|
typename boost::enable_if_c<detail::is_reader<Input>::value && !is_nested_field<T>::value
|
||||||
|
&& is_fast_path_field<T, Transform>::value, bool>::type
|
||||||
|
NextField(const T& field, const Transform& transform)
|
||||||
|
{
|
||||||
|
return transform.Field(field, value<typename T::field_type, Input>(_input));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Transform>
|
||||||
|
typename boost::enable_if_c<detail::is_reader<Input>::value && is_nested_field<T>::value
|
||||||
|
&& !is_fast_path_field<T, Transform>::value, bool>::type
|
||||||
|
NextField(const T&, const Transform& transform)
|
||||||
|
{
|
||||||
|
return transform.Field(T::id, T::metadata, bonded<typename T::field_type, Input>(_input));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Transform>
|
||||||
|
typename boost::enable_if_c<detail::is_reader<Input>::value && is_nested_field<T>::value
|
||||||
|
&& is_fast_path_field<T, Transform>::value, bool>::type
|
||||||
|
NextField(const T& field, const Transform& transform)
|
||||||
|
{
|
||||||
|
return transform.Field(field, bonded<typename T::field_type, Input>(_input));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Transform>
|
||||||
|
typename boost::disable_if<detail::is_reader<Input, T>, bool>::type
|
||||||
|
NextField(const T&, const Transform& transform)
|
||||||
|
{
|
||||||
|
return transform.Field(T::id, T::metadata, T::GetVariable(_input));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// use runtime schema
|
||||||
|
template <typename Transform>
|
||||||
|
bool
|
||||||
|
ReadFields(const RuntimeSchema& schema, const Transform& transform)
|
||||||
|
{
|
||||||
|
bool done = false;
|
||||||
|
|
||||||
|
for (const_enumerator<std::vector<FieldDef> > enumerator(schema.GetStruct().fields); enumerator.more() && !done;)
|
||||||
|
{
|
||||||
|
const FieldDef& field = enumerator.next();
|
||||||
|
|
||||||
|
if (detail::ReadFieldOmitted(_input))
|
||||||
|
{
|
||||||
|
transform.OmittedField(field.id, field.metadata, field.type.id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (field.type.id == bond::BT_STRUCT)
|
||||||
|
{
|
||||||
|
done = transform.Field(field.id, field.metadata, bonded<void, Input>(_input, RuntimeSchema(schema, field)));
|
||||||
|
}
|
||||||
|
else if (field.type.id == bond::BT_LIST || field.type.id == bond::BT_SET || field.type.id == bond::BT_MAP)
|
||||||
|
{
|
||||||
|
done = transform.Field(field.id, field.metadata, value<void, Input>(_input, RuntimeSchema(schema, field)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
done = detail::BasicTypeField(field.id, field.metadata, field.type.id, transform, _input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return done;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// DynamicParser iterates serialized data using field tags included in the
|
||||||
|
// data by the protocol and calls specified transform for each data field.
|
||||||
|
// DynamicParser uses schema only for auxiliary metadata, such as field
|
||||||
|
// names or modifiers, and determines what fields are present from the data itself.
|
||||||
|
// The schema may be provided at compile-time (schema<T>::type::fields) or at runtime
|
||||||
|
// (const RuntimeSchema&).
|
||||||
|
// DynamicParser is used with protocols which tag fields in serialized
|
||||||
|
// format with ids and types, e.g. Mafia, Thrift or Protocol Buffers.
|
||||||
|
//
|
||||||
|
template <typename Input>
|
||||||
|
class DynamicParser
|
||||||
|
: protected detail::ParserInheritance<Input, DynamicParser<Input> >,
|
||||||
|
public detail::ParserCommon
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DynamicParser(Input input, bool base)
|
||||||
|
: detail::ParserInheritance<Input, DynamicParser<Input> >(input, base)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Schema, typename Transform>
|
||||||
|
bool
|
||||||
|
Apply(const Transform& transform, const Schema& schema)
|
||||||
|
{
|
||||||
|
detail::StructBegin(_input, _base);
|
||||||
|
bool result = this->Read(schema, transform);
|
||||||
|
detail::StructEnd(_input, _base);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend class detail::ParserInheritance<Input, DynamicParser<Input> >;
|
||||||
|
|
||||||
|
|
||||||
|
protected:
|
||||||
|
using detail::ParserInheritance<Input, DynamicParser<Input> >::_input;
|
||||||
|
using detail::ParserInheritance<Input, DynamicParser<Input> >::_base;
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <typename Fields, typename Transform>
|
||||||
|
bool
|
||||||
|
ReadFields(const Fields& fields, const Transform& transform)
|
||||||
|
{
|
||||||
|
uint16_t id;
|
||||||
|
BondDataType type;
|
||||||
|
|
||||||
|
_input.ReadFieldBegin(type, id);
|
||||||
|
|
||||||
|
ReadFields(fields, id, type, transform);
|
||||||
|
|
||||||
|
if (!_base)
|
||||||
|
{
|
||||||
|
// If we are not parsing a base class, and we still didn't get to
|
||||||
|
// the end of the struct, it means that:
|
||||||
|
//
|
||||||
|
// 1) Actual data in the payload had deeper hierarchy than payload schema.
|
||||||
|
//
|
||||||
|
// or
|
||||||
|
//
|
||||||
|
// 2) We parsed only part of the hierarchy because that was what
|
||||||
|
// the transform "expected".
|
||||||
|
//
|
||||||
|
// In both cases we emit remaining fields as unknown
|
||||||
|
|
||||||
|
for (; type != bond::BT_STOP; _input.ReadFieldEnd(), _input.ReadFieldBegin(type, id))
|
||||||
|
{
|
||||||
|
if (type == bond::BT_STOP_BASE)
|
||||||
|
transform.UnknownEnd();
|
||||||
|
else
|
||||||
|
UnknownField(id, type, transform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_input.ReadFieldEnd();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// use compile-time schema
|
||||||
|
template <typename Fields, typename Transform>
|
||||||
|
void
|
||||||
|
ReadFields(const Fields&, uint16_t& id, BondDataType& type, const Transform& transform)
|
||||||
|
{
|
||||||
|
typedef typename boost::mpl::deref<Fields>::type Head;
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
if (Head::id == id && get_type_id<typename Head::field_type>::value == type)
|
||||||
|
{
|
||||||
|
// Exact match
|
||||||
|
NextField(Head(), transform);
|
||||||
|
}
|
||||||
|
else if (Head::id >= id && type != bond::BT_STOP && type != bond::BT_STOP_BASE)
|
||||||
|
{
|
||||||
|
// Unknown field or non-exact type match
|
||||||
|
UnknownFieldOrTypeMismatch(Head(), id, type, transform);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
OmittedField(Head(), transform);
|
||||||
|
goto NextSchemaField;
|
||||||
|
}
|
||||||
|
|
||||||
|
_input.ReadFieldEnd();
|
||||||
|
_input.ReadFieldBegin(type, id);
|
||||||
|
|
||||||
|
if (Head::id < id || type == bond::BT_STOP || type == bond::BT_STOP_BASE)
|
||||||
|
{
|
||||||
|
NextSchemaField: return ReadFields(typename boost::mpl::next<Fields>::type(), id, type, transform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Transform>
|
||||||
|
void
|
||||||
|
ReadFields(const boost::mpl::l_iter<boost::mpl::l_end>&, uint16_t& id, BondDataType& type, const Transform& transform)
|
||||||
|
{
|
||||||
|
for (; type != bond::BT_STOP && type != bond::BT_STOP_BASE;
|
||||||
|
_input.ReadFieldEnd(), _input.ReadFieldBegin(type, id))
|
||||||
|
{
|
||||||
|
UnknownField(id, type, transform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Transform>
|
||||||
|
typename boost::enable_if_c<is_nested_field<T>::value
|
||||||
|
&& !is_fast_path_field<T, Transform>::value, bool>::type
|
||||||
|
NextField(const T&, const Transform& transform)
|
||||||
|
{
|
||||||
|
return transform.Field(T::id, T::metadata, bonded<typename T::field_type, Input>(_input));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Transform>
|
||||||
|
typename boost::enable_if_c<is_nested_field<T>::value
|
||||||
|
&& is_fast_path_field<T, Transform>::value, bool>::type
|
||||||
|
NextField(const T& field, const Transform& transform)
|
||||||
|
{
|
||||||
|
return transform.Field(field, bonded<typename T::field_type, Input>(_input));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Transform>
|
||||||
|
typename boost::enable_if_c<!is_nested_field<T>::value
|
||||||
|
&& !is_fast_path_field<T, Transform>::value, bool>::type
|
||||||
|
NextField(const T&, const Transform& transform)
|
||||||
|
{
|
||||||
|
return transform.Field(T::id, T::metadata, value<typename T::field_type, Input>(_input));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Transform>
|
||||||
|
typename boost::enable_if_c<!is_nested_field<T>::value
|
||||||
|
&& is_fast_path_field<T, Transform>::value, bool>::type
|
||||||
|
NextField(const T& field, const Transform& transform)
|
||||||
|
{
|
||||||
|
return transform.Field(field, value<typename T::field_type, Input>(_input));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// This function is called only when payload has unknown field id or type is not
|
||||||
|
// matching exactly. This relativly rare so we don't inline the function to help
|
||||||
|
// the compiler to optimize the common path.
|
||||||
|
template <typename T, typename Transform>
|
||||||
|
BOND_NO_INLINE
|
||||||
|
typename boost::enable_if<is_basic_type<typename T::field_type>, bool>::type
|
||||||
|
UnknownFieldOrTypeMismatch(const T&, uint16_t id, BondDataType type, const Transform& transform)
|
||||||
|
{
|
||||||
|
if (id == T::id &&
|
||||||
|
type != bond::BT_LIST &&
|
||||||
|
type != bond::BT_SET &&
|
||||||
|
type != bond::BT_MAP &&
|
||||||
|
type != bond::BT_STRUCT)
|
||||||
|
{
|
||||||
|
return detail::BasicTypeField(T::id, T::metadata, type, transform, _input);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return UnknownField(id, type, transform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Transform>
|
||||||
|
BOND_NO_INLINE
|
||||||
|
typename boost::disable_if<is_basic_type<typename T::field_type>, bool>::type
|
||||||
|
UnknownFieldOrTypeMismatch(const T&, uint16_t id, BondDataType type, const Transform& transform)
|
||||||
|
{
|
||||||
|
return UnknownField(id, type, transform);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// use runtime schema
|
||||||
|
template <typename Transform>
|
||||||
|
bool
|
||||||
|
ReadFields(const RuntimeSchema& schema, const Transform& transform)
|
||||||
|
{
|
||||||
|
uint16_t id;
|
||||||
|
BondDataType type;
|
||||||
|
std::vector<FieldDef>::const_iterator it = schema.GetStruct().fields.begin(),
|
||||||
|
end = schema.GetStruct().fields.end();
|
||||||
|
|
||||||
|
_input.ReadFieldBegin(type, id);
|
||||||
|
|
||||||
|
for (;; _input.ReadFieldEnd(), _input.ReadFieldBegin(type, id))
|
||||||
|
{
|
||||||
|
while (it != end && (it->id < id || type == bond::BT_STOP || type == bond::BT_STOP_BASE))
|
||||||
|
{
|
||||||
|
const FieldDef& field = *it++;
|
||||||
|
transform.OmittedField(field.id, field.metadata, field.type.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == bond::BT_STOP || type == bond::BT_STOP_BASE)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (it != end && it->id == id)
|
||||||
|
{
|
||||||
|
const FieldDef& field = *it++;
|
||||||
|
|
||||||
|
if (type == bond::BT_STRUCT)
|
||||||
|
{
|
||||||
|
if (field.type.id == type)
|
||||||
|
{
|
||||||
|
transform.Field(id, field.metadata, bonded<void, Input>(_input, RuntimeSchema(schema, field)));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (type == bond::BT_LIST || type == bond::BT_SET || type == bond::BT_MAP)
|
||||||
|
{
|
||||||
|
if (field.type.id == type)
|
||||||
|
{
|
||||||
|
transform.Field(id, field.metadata, value<void, Input>(_input, RuntimeSchema(schema, field)));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
detail::BasicTypeField(id, field.metadata, type, transform, _input);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UnknownField(id, type, transform);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_base)
|
||||||
|
{
|
||||||
|
// If we are not parsing a base class, and we still didn't get to
|
||||||
|
// the end of the struct, it means that:
|
||||||
|
//
|
||||||
|
// 1) Actual data in the payload had deeper hierarchy than payload schema.
|
||||||
|
//
|
||||||
|
// or
|
||||||
|
//
|
||||||
|
// 2) We parsed only part of the hierarchy because that was what
|
||||||
|
// the transform "expected".
|
||||||
|
//
|
||||||
|
// In both cases we emit remaining fields as unknown
|
||||||
|
|
||||||
|
for (; type != bond::BT_STOP; _input.ReadFieldEnd(), _input.ReadFieldBegin(type, id))
|
||||||
|
{
|
||||||
|
if (type == bond::BT_STOP_BASE)
|
||||||
|
{
|
||||||
|
transform.UnknownEnd();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UnknownField(id, type, transform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_input.ReadFieldEnd();
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool UnknownField(uint16_t, BondDataType type, const To<T>&)
|
||||||
|
{
|
||||||
|
_input.Skip(type);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Transform>
|
||||||
|
bool UnknownField(uint16_t id, BondDataType type, const Transform& transform)
|
||||||
|
{
|
||||||
|
if (type == bond::BT_STRUCT)
|
||||||
|
{
|
||||||
|
return transform.UnknownField(id, bonded<void, Input>(_input, GetRuntimeSchema<Unknown>()));
|
||||||
|
}
|
||||||
|
else if (type == bond::BT_LIST || type == bond::BT_SET || type == bond::BT_MAP)
|
||||||
|
return transform.UnknownField(id, value<void, Input>(_input, type));
|
||||||
|
else
|
||||||
|
return detail::BasicTypeField(id, schema<Unknown>::type::metadata, type, BindUnknownField(transform), _input);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// DOM parser works with protocol implementations using Document Object Model,
|
||||||
|
// e.g. JSON or XML. The parser assumes that fields in DOM are unordered and
|
||||||
|
// are identified by either ordinal or metadata. DOM based protocols may loosly
|
||||||
|
// map to Bond meta-schema types thus the parser delegates to the protocol for
|
||||||
|
// field type match checking.
|
||||||
|
template <typename Input>
|
||||||
|
class DOMParser
|
||||||
|
: protected detail::ParserInheritance<Input, DOMParser<Input> >
|
||||||
|
{
|
||||||
|
typedef typename remove_reference<Input>::type Reader;
|
||||||
|
|
||||||
|
public:
|
||||||
|
DOMParser(Input input, bool base = false)
|
||||||
|
: detail::ParserInheritance<Input, DOMParser<Input> >(input, base)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Schema, typename Transform>
|
||||||
|
bool Apply(const Transform& transform, const Schema& schema)
|
||||||
|
{
|
||||||
|
if (!_base) _input.Parse();
|
||||||
|
return this->Read(schema, transform);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend class detail::ParserInheritance<Input, DOMParser<Input> >;
|
||||||
|
|
||||||
|
|
||||||
|
protected:
|
||||||
|
using detail::ParserInheritance<Input, DOMParser<Input> >::_input;
|
||||||
|
using detail::ParserInheritance<Input, DOMParser<Input> >::_base;
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <typename Fields>
|
||||||
|
void SkipFields(const Fields&)
|
||||||
|
{}
|
||||||
|
|
||||||
|
// use compile-time schema
|
||||||
|
template <typename Fields, typename Transform>
|
||||||
|
bool ReadFields(const Fields&, const Transform& transform)
|
||||||
|
{
|
||||||
|
typedef typename boost::mpl::deref<Fields>::type Head;
|
||||||
|
|
||||||
|
if (const typename Reader::Field* field = _input.FindField(
|
||||||
|
Head::id,
|
||||||
|
Head::metadata,
|
||||||
|
get_type_id<typename Head::field_type>::value,
|
||||||
|
is_enum<typename Head::field_type>::value))
|
||||||
|
{
|
||||||
|
Reader input(_input, *field);
|
||||||
|
NextField(Head(), transform, input);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ReadFields(typename boost::mpl::next<Fields>::type(), transform);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Transform>
|
||||||
|
bool ReadFields(const boost::mpl::l_iter<boost::mpl::l_end>&, const Transform&)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Transform>
|
||||||
|
typename boost::enable_if_c<is_nested_field<T>::value
|
||||||
|
&& !is_fast_path_field<T, Transform>::value, bool>::type
|
||||||
|
NextField(const T&, const Transform& transform, Input input)
|
||||||
|
{
|
||||||
|
return transform.Field(T::id, T::metadata, bonded<typename T::field_type, Input>(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Transform>
|
||||||
|
typename boost::enable_if_c<is_nested_field<T>::value
|
||||||
|
&& is_fast_path_field<T, Transform>::value, bool>::type
|
||||||
|
NextField(const T& field, const Transform& transform, Input input)
|
||||||
|
{
|
||||||
|
return transform.Field(field, bonded<typename T::field_type, Input>(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Transform>
|
||||||
|
typename boost::enable_if_c<!is_nested_field<T>::value
|
||||||
|
&& !is_fast_path_field<T, Transform>::value, bool>::type
|
||||||
|
NextField(const T&, const Transform& transform, Input input)
|
||||||
|
{
|
||||||
|
return transform.Field(T::id, T::metadata, value<typename T::field_type, Input>(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Transform>
|
||||||
|
typename boost::enable_if_c<!is_nested_field<T>::value
|
||||||
|
&& is_fast_path_field<T, Transform>::value, bool>::type
|
||||||
|
NextField(const T& field, const Transform& transform, Input input)
|
||||||
|
{
|
||||||
|
return transform.Field(field, value<typename T::field_type, Input>(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// use runtime schema
|
||||||
|
template <typename Transform>
|
||||||
|
bool ReadFields(const RuntimeSchema& schema, const Transform& transform)
|
||||||
|
{
|
||||||
|
bool done = false;
|
||||||
|
|
||||||
|
for (const_enumerator<std::vector<FieldDef> > enumerator(schema.GetStruct().fields); enumerator.more() && !done;)
|
||||||
|
{
|
||||||
|
const FieldDef& fieldDef = enumerator.next();
|
||||||
|
|
||||||
|
if (const typename Reader::Field* field = _input.FindField(fieldDef.id, fieldDef.metadata, fieldDef.type.id))
|
||||||
|
{
|
||||||
|
Reader input(_input, *field);
|
||||||
|
|
||||||
|
if (fieldDef.type.id == BT_STRUCT)
|
||||||
|
done = transform.Field(fieldDef.id, fieldDef.metadata, bonded<void, Input>(input, RuntimeSchema(schema, fieldDef)));
|
||||||
|
else if (fieldDef.type.id == BT_LIST || fieldDef.type.id == BT_SET || fieldDef.type.id == BT_MAP)
|
||||||
|
done = transform.Field(fieldDef.id, fieldDef.metadata, value<void, Input>(input, RuntimeSchema(schema, fieldDef)));
|
||||||
|
else
|
||||||
|
done = detail::BasicTypeField(fieldDef.id, fieldDef.metadata, fieldDef.type.id, transform, input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return done;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace bond
|
|
@ -0,0 +1,217 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#pragma warning(push)
|
||||||
|
// boost\variant\variant.hpp(762) :
|
||||||
|
// warning C4512: 'boost::detail::variant::comparer<Variant,Comp>' : assignment operator could not be generated
|
||||||
|
#pragma warning(disable : 4512 4702)
|
||||||
|
|
||||||
|
#include <boost/make_shared.hpp>
|
||||||
|
#include <boost/variant.hpp>
|
||||||
|
#include <boost/ref.hpp>
|
||||||
|
#include <boost/mpl/list.hpp>
|
||||||
|
#include <boost/mpl/push_front.hpp>
|
||||||
|
#include <boost/mpl/copy_if.hpp>
|
||||||
|
|
||||||
|
#pragma warning(pop)
|
||||||
|
|
||||||
|
#include "customize.h"
|
||||||
|
#include "detail/odr.h"
|
||||||
|
#include <bond/protocol/simple_binary.h>
|
||||||
|
#include <bond/protocol/compact_binary.h>
|
||||||
|
#include <bond/protocol/fast_binary.h>
|
||||||
|
#include <bond/protocol/simple_json_reader.h>
|
||||||
|
|
||||||
|
#if !defined(BOND_COMPACT_BINARY_PROTOCOL) \
|
||||||
|
&& !defined(BOND_SIMPLE_BINARY_PROTOCOL) \
|
||||||
|
&& !defined(BOND_FAST_BINARY_PROTOCOL) \
|
||||||
|
&& !defined(BOND_SIMPLE_JSON_PROTOCOL)
|
||||||
|
|
||||||
|
# define BOND_COMPACT_BINARY_PROTOCOL
|
||||||
|
# define BOND_SIMPLE_BINARY_PROTOCOL
|
||||||
|
# define BOND_FAST_BINARY_PROTOCOL
|
||||||
|
// BOND_SIMPLE_JSON_PROTOCOL disabled by default
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
|
||||||
|
#ifdef BOND_COMPACT_BINARY_PROTOCOL
|
||||||
|
template <typename Buffer> struct
|
||||||
|
is_protocol_enabled<CompactBinaryReader<Buffer> >
|
||||||
|
: true_type {};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef BOND_SIMPLE_BINARY_PROTOCOL
|
||||||
|
template <typename Buffer> struct
|
||||||
|
is_protocol_enabled<SimpleBinaryReader<Buffer> >
|
||||||
|
: true_type {};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef BOND_SIMPLE_JSON_PROTOCOL
|
||||||
|
template <typename Buffer> struct
|
||||||
|
is_protocol_enabled<SimpleJsonReader<Buffer> >
|
||||||
|
: true_type {};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef BOND_FAST_BINARY_PROTOCOL
|
||||||
|
template <typename Buffer> struct
|
||||||
|
is_protocol_enabled<FastBinaryReader<Buffer> >
|
||||||
|
: true_type {};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// uses_static_parser
|
||||||
|
template <typename Reader, typename Enable = void> struct
|
||||||
|
uses_static_parser
|
||||||
|
: false_type {};
|
||||||
|
|
||||||
|
template <typename Reader> struct
|
||||||
|
uses_static_parser<Reader, typename boost::enable_if<
|
||||||
|
is_same<typename Reader::Parser, StaticParser<Reader&> > >::type>
|
||||||
|
: true_type {};
|
||||||
|
|
||||||
|
template <typename Reader> struct
|
||||||
|
uses_static_parser<Reader&>
|
||||||
|
: uses_static_parser<Reader> {};
|
||||||
|
|
||||||
|
// uses_dynamic_parser
|
||||||
|
template <typename Reader, typename Enable = void> struct
|
||||||
|
uses_dynamic_parser
|
||||||
|
: false_type {};
|
||||||
|
|
||||||
|
template <typename Reader> struct
|
||||||
|
uses_dynamic_parser<Reader, typename boost::enable_if<
|
||||||
|
is_same<typename Reader::Parser, DynamicParser<Reader&> > >::type>
|
||||||
|
: true_type {};
|
||||||
|
|
||||||
|
template <typename Reader> struct
|
||||||
|
uses_dynamic_parser<Reader&>
|
||||||
|
: uses_dynamic_parser<Reader> {};
|
||||||
|
|
||||||
|
// uses_dom_parser
|
||||||
|
template <typename Reader, typename Enable = void> struct
|
||||||
|
uses_dom_parser
|
||||||
|
: false_type {};
|
||||||
|
|
||||||
|
template <typename Reader> struct
|
||||||
|
uses_dom_parser<Reader, typename boost::enable_if<
|
||||||
|
is_same<typename Reader::Parser, DOMParser<Reader&> > >::type>
|
||||||
|
: true_type {};
|
||||||
|
|
||||||
|
template <typename Reader> struct
|
||||||
|
uses_dom_parser<Reader&>
|
||||||
|
: uses_dom_parser<Reader> {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Reader, typename Unused> struct
|
||||||
|
uses_marshaled_bonded
|
||||||
|
: uses_static_parser<Reader> {};
|
||||||
|
|
||||||
|
|
||||||
|
struct ValueReader
|
||||||
|
{
|
||||||
|
// Constructors that explicitly declared throw() are needed for
|
||||||
|
// boost::variant to use optimized code path.
|
||||||
|
ValueReader() throw()
|
||||||
|
: pointer(NULL)
|
||||||
|
{}
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
ValueReader(boost::reference_wrapper<U> value) throw()
|
||||||
|
: pointer(&static_cast<const U&>(value))
|
||||||
|
{}
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
ValueReader(const U& value)
|
||||||
|
: instance(boost::make_shared<U>(value)),
|
||||||
|
pointer(instance.get())
|
||||||
|
{}
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
ValueReader(boost::shared_ptr<U> value) throw()
|
||||||
|
: instance(boost::static_pointer_cast<const void>(value)),
|
||||||
|
pointer(instance.get())
|
||||||
|
{}
|
||||||
|
|
||||||
|
ValueReader(const ValueReader& value) throw()
|
||||||
|
: instance(value.instance),
|
||||||
|
pointer(value.pointer)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool operator==(const ValueReader& rhs) const
|
||||||
|
{
|
||||||
|
return instance == rhs.instance
|
||||||
|
&& pointer == rhs.pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::shared_ptr<const void> instance;
|
||||||
|
const void* pointer;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
using boost::mpl::_;
|
||||||
|
|
||||||
|
template <typename Buffer>
|
||||||
|
struct Protocols
|
||||||
|
{
|
||||||
|
typedef typename boost::mpl::list<
|
||||||
|
CompactBinaryReader<Buffer>,
|
||||||
|
SimpleBinaryReader<Buffer>,
|
||||||
|
FastBinaryReader<Buffer>,
|
||||||
|
SimpleJsonReader<Buffer>
|
||||||
|
>::type built_in;
|
||||||
|
|
||||||
|
typedef typename customize<protocols>::modify<built_in>::type all;
|
||||||
|
|
||||||
|
typedef typename boost::mpl::copy_if<
|
||||||
|
all,
|
||||||
|
is_protocol_enabled<_>,
|
||||||
|
boost::mpl::front_inserter<boost::mpl::list<> > >::type type;
|
||||||
|
|
||||||
|
typedef typename boost::mpl::begin<type>::type begin;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Buffer>
|
||||||
|
struct ProtocolReader
|
||||||
|
{
|
||||||
|
typedef void Parser;
|
||||||
|
|
||||||
|
ProtocolReader()
|
||||||
|
: value()
|
||||||
|
{
|
||||||
|
// Validate that all compilation units in a program use the same set of protocols.
|
||||||
|
(void)one_definition<Protocols<Buffer>, typename Protocols<Buffer>::all>::value;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProtocolReader(const ValueReader& x)
|
||||||
|
: value(x)
|
||||||
|
{}
|
||||||
|
|
||||||
|
template <typename Reader>
|
||||||
|
ProtocolReader(const Reader& reader)
|
||||||
|
: value(reader)
|
||||||
|
{}
|
||||||
|
|
||||||
|
ProtocolReader(const ProtocolReader& that)
|
||||||
|
: value(that.value)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool operator==(const ProtocolReader& rhs) const
|
||||||
|
{
|
||||||
|
return value == rhs.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
typename boost::make_variant_over<
|
||||||
|
typename boost::mpl::push_front<
|
||||||
|
typename Protocols<Buffer>::all,
|
||||||
|
ValueReader
|
||||||
|
>::type
|
||||||
|
>::type value;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
};
|
|
@ -0,0 +1,812 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <boost/static_assert.hpp>
|
||||||
|
#include <boost/mpl/transform.hpp>
|
||||||
|
#include <boost/mpl/find_if.hpp>
|
||||||
|
#include <boost/mpl/for_each.hpp>
|
||||||
|
#include <boost/mpl/list.hpp>
|
||||||
|
#include <boost/mpl/push_front.hpp>
|
||||||
|
#include <boost/mpl/copy_if.hpp>
|
||||||
|
#include <boost/assign.hpp>
|
||||||
|
#include <boost/assign/list_of.hpp>
|
||||||
|
|
||||||
|
#include <bond/core/bond_types.h>
|
||||||
|
#include "bonded.h"
|
||||||
|
#include "detail/metadata.h"
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
|
||||||
|
using boost::mpl::_;
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
remove_maybe
|
||||||
|
{
|
||||||
|
typedef T type;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
remove_maybe<maybe<T> >
|
||||||
|
{
|
||||||
|
typedef typename remove_maybe<T>::type type;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static const uint16_t invalid_field_id = 0xffff;
|
||||||
|
|
||||||
|
|
||||||
|
/** namespace bond::reflection */
|
||||||
|
namespace reflection
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Helper classes/templates
|
||||||
|
//
|
||||||
|
typedef std::map<std::string, std::string> Attributes;
|
||||||
|
|
||||||
|
|
||||||
|
// field is required
|
||||||
|
struct required_field_modifier
|
||||||
|
{
|
||||||
|
static const bond::Modifier value = bond::Required;
|
||||||
|
};
|
||||||
|
|
||||||
|
// field is optional
|
||||||
|
struct optional_field_modifier
|
||||||
|
{
|
||||||
|
static const bond::Modifier value = bond::Optional;
|
||||||
|
};
|
||||||
|
|
||||||
|
// field is required optional
|
||||||
|
struct required_optional_field_modifier
|
||||||
|
{
|
||||||
|
static const bond::Modifier value = bond::RequiredOptional;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Field description in compile-time schema
|
||||||
|
template
|
||||||
|
<
|
||||||
|
uint16_t t_id,
|
||||||
|
typename t_modifierTag,
|
||||||
|
typename t_struct,
|
||||||
|
typename t_fieldType,
|
||||||
|
t_fieldType t_struct::*t_fieldAddr,
|
||||||
|
const bond::Metadata* t_metadata
|
||||||
|
>
|
||||||
|
struct FieldTemplate
|
||||||
|
{
|
||||||
|
/// @brief Type of the field's parent struct
|
||||||
|
typedef t_struct struct_type;
|
||||||
|
|
||||||
|
/// @brief Type of the field pointer
|
||||||
|
typedef t_fieldType t_struct::*field_pointer;
|
||||||
|
|
||||||
|
/// @brief Type of the field
|
||||||
|
typedef typename remove_maybe<t_fieldType>::type field_type;
|
||||||
|
|
||||||
|
/// @brief Type of the field value
|
||||||
|
typedef t_fieldType value_type;
|
||||||
|
|
||||||
|
/// @brief Modifier tag for the field
|
||||||
|
///
|
||||||
|
/// Can be one of:
|
||||||
|
/// - bond::reflection::optional_field_modifier
|
||||||
|
/// - bond::reflection::required_field_modifier
|
||||||
|
/// - bond::reflection::required_optional_field_modifier
|
||||||
|
typedef t_modifierTag field_modifier;
|
||||||
|
|
||||||
|
/// @brief Static data member describing field metadata
|
||||||
|
static const Metadata& metadata;
|
||||||
|
|
||||||
|
/// @brief Static data member representing the field pointer
|
||||||
|
static const field_pointer field_ptr;
|
||||||
|
|
||||||
|
/// @brief Static data member equal to the field ordinal
|
||||||
|
static const uint16_t id = t_id;
|
||||||
|
|
||||||
|
/// @brief Static method returning const reference to the field value for a particular object
|
||||||
|
static
|
||||||
|
const t_fieldType& GetVariable(const struct_type& object)
|
||||||
|
{
|
||||||
|
return object.*t_fieldAddr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Static method returning reference to the field value for a particular object
|
||||||
|
static
|
||||||
|
t_fieldType& GetVariable(struct_type& object)
|
||||||
|
{
|
||||||
|
return object.*t_fieldAddr;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_STATIC_ASSERT(t_id != invalid_field_id);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template
|
||||||
|
<
|
||||||
|
uint16_t t_id,
|
||||||
|
typename t_modifierTag,
|
||||||
|
typename t_struct,
|
||||||
|
typename t_fieldType,
|
||||||
|
t_fieldType t_struct::*t_fieldAddr,
|
||||||
|
const Metadata* t_metadata
|
||||||
|
>
|
||||||
|
const bond::Metadata& FieldTemplate<t_id, t_modifierTag, t_struct, t_fieldType, t_fieldAddr, t_metadata>::metadata = *t_metadata;
|
||||||
|
|
||||||
|
template
|
||||||
|
<
|
||||||
|
uint16_t t_id,
|
||||||
|
typename t_modifierTag,
|
||||||
|
typename t_struct,
|
||||||
|
typename t_fieldType,
|
||||||
|
t_fieldType t_struct::*t_fieldAddr,
|
||||||
|
const Metadata* t_metadata
|
||||||
|
>
|
||||||
|
const typename FieldTemplate<t_id, t_modifierTag, t_struct, t_fieldType, t_fieldAddr, t_metadata>::field_pointer
|
||||||
|
FieldTemplate<t_id, t_modifierTag, t_struct, t_fieldType, t_fieldAddr, t_metadata>::field_ptr = t_fieldAddr;
|
||||||
|
|
||||||
|
// Metadata initializer for fields
|
||||||
|
inline
|
||||||
|
bond::Metadata MetadataInit(const char* name)
|
||||||
|
{
|
||||||
|
bond::Metadata metadata;
|
||||||
|
|
||||||
|
metadata.name = name;
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
bond::Metadata MetadataInit(const char* name, bond::Modifier modifier, const Attributes& attributes)
|
||||||
|
{
|
||||||
|
bond::Metadata metadata;
|
||||||
|
|
||||||
|
metadata.name = name;
|
||||||
|
metadata.modifier = modifier;
|
||||||
|
metadata.attributes = attributes;
|
||||||
|
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bond::Metadata MetadataInit(const T& default_value, const char* name)
|
||||||
|
{
|
||||||
|
bond::Metadata metadata;
|
||||||
|
|
||||||
|
metadata.name = name;
|
||||||
|
detail::VariantSet(metadata.default_value, default_value);
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bond::Metadata MetadataInit(const T& default_value, const char* name, bond::Modifier modifier, const Attributes& attributes)
|
||||||
|
{
|
||||||
|
bond::Metadata metadata = MetadataInit(name, modifier, attributes);
|
||||||
|
|
||||||
|
detail::VariantSet(metadata.default_value, default_value);
|
||||||
|
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct nothing
|
||||||
|
{};
|
||||||
|
|
||||||
|
inline
|
||||||
|
bond::Metadata MetadataInit(const nothing&, const char* name)
|
||||||
|
{
|
||||||
|
bond::Metadata metadata;
|
||||||
|
|
||||||
|
metadata.name = name;
|
||||||
|
metadata.default_value.nothing = true;
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
bond::Metadata MetadataInit(const nothing&, const char* name, bond::Modifier modifier, const Attributes& attributes)
|
||||||
|
{
|
||||||
|
bond::Metadata metadata = MetadataInit(name, modifier, attributes);
|
||||||
|
|
||||||
|
metadata.default_value.nothing = true;
|
||||||
|
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Metadata initializer for structs
|
||||||
|
inline
|
||||||
|
bond::Metadata MetadataInit(const char* name, const char* qualified_name, const Attributes& attributes)
|
||||||
|
{
|
||||||
|
bond::Metadata metadata;
|
||||||
|
|
||||||
|
metadata.name = name;
|
||||||
|
metadata.qualified_name = qualified_name;
|
||||||
|
metadata.attributes = attributes;
|
||||||
|
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Metadata initializer for generic structs
|
||||||
|
template <typename Params>
|
||||||
|
bond::Metadata MetadataInit(const char* name, const char* qualified_name, const Attributes& attributes)
|
||||||
|
{
|
||||||
|
bond::Metadata metadata = MetadataInit(name, qualified_name, attributes);
|
||||||
|
|
||||||
|
std::string params;
|
||||||
|
|
||||||
|
// boost::mpl::for_each instantiates object of each type in the sequence.
|
||||||
|
// We transform the Params to a sequence of type pointers to avoid creating
|
||||||
|
// actual complex types that might not even support default ctor.
|
||||||
|
typedef typename boost::mpl::transform<Params, boost::add_pointer<_> >::type ParamsPtr;
|
||||||
|
|
||||||
|
boost::mpl::for_each<ParamsPtr>(detail::TypeListBuilder(params));
|
||||||
|
|
||||||
|
metadata.name += "<" + params + ">";
|
||||||
|
metadata.qualified_name += "<" + params + ">";
|
||||||
|
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}; // namespace reflection
|
||||||
|
|
||||||
|
|
||||||
|
const reflection::nothing nothing = {};
|
||||||
|
|
||||||
|
template <typename T, typename Iter> struct
|
||||||
|
field_id
|
||||||
|
{
|
||||||
|
static const uint16_t value = boost::mpl::deref<Iter>::type::id;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
field_id<T, typename boost::mpl::end<T>::type>
|
||||||
|
{
|
||||||
|
static const uint16_t value = invalid_field_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, uint16_t minId = 0> struct
|
||||||
|
next_required_field
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
template <typename Field> struct
|
||||||
|
is_next_required
|
||||||
|
{
|
||||||
|
static const bool value = Field::id >= minId
|
||||||
|
&& is_same<typename Field::field_modifier, typename reflection::required_field_modifier>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
static const uint16_t value = field_id<T, typename boost::mpl::find_if<T, is_next_required<_> >::type>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct no_base {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Enable = void> struct
|
||||||
|
is_writer
|
||||||
|
: false_type {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
is_writer<T, typename boost::enable_if<check_method<void (T::*)(const Metadata&, bool), &T::WriteStructBegin> >::type>
|
||||||
|
: true_type {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline typename T::base*
|
||||||
|
base_class()
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
remove_bonded
|
||||||
|
{
|
||||||
|
typedef T type;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
remove_bonded<bonded<T> >
|
||||||
|
{
|
||||||
|
typedef typename remove_bonded<T>::type type;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
is_bond_type
|
||||||
|
{
|
||||||
|
typedef typename remove_const<T>::type U;
|
||||||
|
|
||||||
|
static const bool value = is_bonded<U>::value
|
||||||
|
|| has_schema<U>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct Unknown;
|
||||||
|
|
||||||
|
template <typename Unused> struct
|
||||||
|
schema<Unknown, Unused>
|
||||||
|
{
|
||||||
|
struct type
|
||||||
|
{
|
||||||
|
typedef no_base base;
|
||||||
|
typedef boost::mpl::list<>::type fields;
|
||||||
|
static const Metadata metadata;
|
||||||
|
|
||||||
|
type()
|
||||||
|
{
|
||||||
|
(void)metadata;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Unused>
|
||||||
|
const Metadata schema<Unknown, Unused>::type::metadata
|
||||||
|
= reflection::MetadataInit("Unknown", "Unknown", reflection::Attributes());
|
||||||
|
|
||||||
|
template <typename T, typename Enable> struct
|
||||||
|
schema_for_passthrough
|
||||||
|
: schema<typename remove_bonded<T>::type>
|
||||||
|
{};
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
schema_for_passthrough<T, typename boost::disable_if<has_schema<typename remove_bonded<T>::type> >::type>
|
||||||
|
{
|
||||||
|
// If type T doesn't have schema we return schema of an empty struct;
|
||||||
|
// this allows pass-through of bonded<T> with only forward declaration for T.
|
||||||
|
typedef typename schema<Unknown>::type type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
is_container
|
||||||
|
{
|
||||||
|
typedef typename remove_const<T>::type U;
|
||||||
|
|
||||||
|
static const bool value = is_list_container<U>::value
|
||||||
|
|| is_set_container<U>::value
|
||||||
|
|| is_map_container<U>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Field, typename Transform, typename Enable = void> struct
|
||||||
|
is_fast_path_field
|
||||||
|
: false_type {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Field, typename Transform> struct
|
||||||
|
is_fast_path_field<Field, Transform, typename boost::enable_if<is_same<typename Field::struct_type,
|
||||||
|
typename Transform::FastPathType> >::type>
|
||||||
|
: true_type {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
is_nested_field
|
||||||
|
: is_bond_type<typename T::field_type> {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
is_struct_field
|
||||||
|
: has_schema<typename T::field_type> {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T1, typename T2, typename Enable = void> struct
|
||||||
|
is_matching_container
|
||||||
|
: false_type {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T1, typename T2> struct
|
||||||
|
is_matching_basic
|
||||||
|
{
|
||||||
|
static const bool value =
|
||||||
|
(is_string<T1>::value && is_string<T2>::value)
|
||||||
|
|| (is_wstring<T1>::value && is_wstring<T2>::value)
|
||||||
|
|| ((sizeof(T1) <= sizeof(T2))
|
||||||
|
&& ((is_unsigned<T1>::value && is_unsigned<T2>::value)
|
||||||
|
|| (is_signed_int_or_enum<T1>::value && is_signed_int_or_enum<T2>::value)));
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
is_matching_basic<typename aliased_type<T>::type, T>
|
||||||
|
: boost::true_type {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T1, typename T2> struct
|
||||||
|
is_matching
|
||||||
|
{
|
||||||
|
static const bool value =
|
||||||
|
(is_bond_type<T1>::value && is_bond_type<T2>::value)
|
||||||
|
|| (is_matching_basic<T1, T2>::value)
|
||||||
|
|| (is_matching_container<T1, T2>::value);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
is_matching_basic<T, T>
|
||||||
|
: true_type {};
|
||||||
|
|
||||||
|
|
||||||
|
template <> struct
|
||||||
|
is_matching_basic<bool, bool>
|
||||||
|
: true_type {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
is_matching_basic<bool, T>
|
||||||
|
: false_type {};
|
||||||
|
|
||||||
|
|
||||||
|
template <> struct
|
||||||
|
is_matching_basic<uint8_t, bool>
|
||||||
|
: false_type {};
|
||||||
|
|
||||||
|
|
||||||
|
template <> struct
|
||||||
|
is_matching_basic<float, double>
|
||||||
|
: true_type {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
get_type_id;
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T1, typename T2> struct
|
||||||
|
is_matching_container<T1, T2,
|
||||||
|
typename boost::enable_if_c<is_container<T1>::value
|
||||||
|
&& get_type_id<T1>::value == get_type_id<T2>::value>::type>
|
||||||
|
: is_matching<typename element_type<T1>::type,
|
||||||
|
typename element_type<T2>::type> {};
|
||||||
|
|
||||||
|
|
||||||
|
// tuples match if the elements match
|
||||||
|
template <typename T1, typename T2, typename U1, typename U2> struct
|
||||||
|
is_matching<std::pair<T1, T2>, std::pair<U1, U2> >
|
||||||
|
{
|
||||||
|
static const bool value = is_matching<T1, U1>::value
|
||||||
|
&& is_matching<T2, U2>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T1, typename T2> struct
|
||||||
|
is_matching<std::pair<T1, T2>, std::pair<T1, T2> >
|
||||||
|
: true_type {};
|
||||||
|
|
||||||
|
|
||||||
|
// value<T> matches if type matches
|
||||||
|
template <typename T1, typename Reader, typename T2> struct
|
||||||
|
is_matching<value<T1, Reader>, T2>
|
||||||
|
: is_matching<T1, T2> {};
|
||||||
|
|
||||||
|
|
||||||
|
// value<void> matches every container
|
||||||
|
template <typename T, typename Reader> struct
|
||||||
|
is_matching<value<void, Reader>, T>
|
||||||
|
: is_container<T> {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename X, typename Enable = void> struct
|
||||||
|
is_element_matching
|
||||||
|
: false_type {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename X> struct
|
||||||
|
is_element_matching<T, X, typename boost::enable_if<is_container<X> >::type>
|
||||||
|
: is_matching<T, typename element_type<X>::type> {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename X, typename Reader> struct
|
||||||
|
is_element_matching<value<void, Reader>, X, typename boost::enable_if<is_container<X> >::type>
|
||||||
|
{
|
||||||
|
static const bool value = is_bond_type<typename element_type<X>::type>::value
|
||||||
|
|| is_container<typename element_type<X>::type>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename X, typename Enable = void> struct
|
||||||
|
is_map_element_matching
|
||||||
|
: false_type {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename X, typename T> struct
|
||||||
|
is_map_element_matching<T, X, typename boost::enable_if<is_map_container<X> >::type>
|
||||||
|
: is_matching<T, typename element_type<X>::type::second_type> {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename X, typename Reader> struct
|
||||||
|
is_map_element_matching<value<void, Reader>, X, typename boost::enable_if<is_map_container<X> >::type>
|
||||||
|
{
|
||||||
|
static const bool value = is_bond_type<typename element_type<X>::type::second_type>::value
|
||||||
|
|| is_container<typename element_type<X>::type::second_type>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename X, typename Enable = void> struct
|
||||||
|
is_map_key_matching
|
||||||
|
: false_type {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename X> struct
|
||||||
|
is_map_key_matching<T, X, typename boost::enable_if<is_map_container<X> >::type>
|
||||||
|
: is_matching<T, typename element_type<X>::type::first_type> {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
is_basic_type
|
||||||
|
{
|
||||||
|
static const bool value = !(is_container<T>::value || is_bond_type<T>::value);
|
||||||
|
};
|
||||||
|
|
||||||
|
template <> struct
|
||||||
|
is_basic_type<void>
|
||||||
|
: false_type {};
|
||||||
|
|
||||||
|
|
||||||
|
// is_nested_container
|
||||||
|
template <typename T, typename Enable = void> struct
|
||||||
|
is_nested_container
|
||||||
|
: false_type {};
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
is_nested_container<T, typename boost::enable_if<is_map_container<T> >::type>
|
||||||
|
{
|
||||||
|
static const bool value = !is_basic_type<typename element_type<T>::type::second_type>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
is_nested_container<T, typename boost::enable_if<is_list_container<T> >::type>
|
||||||
|
{
|
||||||
|
static const bool value = !is_basic_type<typename element_type<T>::type>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Enable = void> struct
|
||||||
|
is_struct_container
|
||||||
|
: false_type {};
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
is_struct_container<T, typename boost::enable_if<is_map_container<T> >::type>
|
||||||
|
{
|
||||||
|
static const bool value = has_schema<typename element_type<T>::type::second_type>::value
|
||||||
|
|| is_struct_container<typename element_type<T>::type::second_type>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
is_struct_container<T, typename boost::enable_if<is_list_container<T> >::type>
|
||||||
|
{
|
||||||
|
static const bool value = has_schema<typename element_type<T>::type>::value
|
||||||
|
|| is_struct_container<typename element_type<T>::type>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// is_struct_container_field
|
||||||
|
template <typename T> struct
|
||||||
|
is_struct_container_field
|
||||||
|
: is_struct_container<typename T::field_type> {};
|
||||||
|
|
||||||
|
|
||||||
|
// is_basic_container
|
||||||
|
template <typename T, typename Enable = void> struct
|
||||||
|
is_basic_container
|
||||||
|
: false_type {};
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
is_basic_container<T, typename boost::enable_if<is_map_container<T> >::type>
|
||||||
|
: is_basic_type<typename element_type<T>::type::second_type> {};
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
is_basic_container<T, typename boost::enable_if<is_list_container<T> >::type>
|
||||||
|
: is_basic_type<typename element_type<T>::type> {};
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
is_basic_container<T, typename boost::enable_if<is_set_container<T> >::type>
|
||||||
|
: true_type {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename F> struct
|
||||||
|
is_matching_container_field
|
||||||
|
: is_matching_container<T, typename F::field_type> {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
is_container_field
|
||||||
|
: is_container<typename T::field_type> {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename F> struct
|
||||||
|
is_matching_basic_field
|
||||||
|
: is_matching_basic<T, typename F::field_type> {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Reader> struct
|
||||||
|
is_basic_type<value<T, Reader> >
|
||||||
|
: false_type {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T1, typename T2> struct
|
||||||
|
is_basic_type<std::pair<T1, T2> >
|
||||||
|
: false_type {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename X, typename Enable = void> struct
|
||||||
|
matching_fields
|
||||||
|
: boost::mpl::copy_if<typename schema<T>::type::fields,
|
||||||
|
is_matching_basic_field<X, _>,
|
||||||
|
boost::mpl::front_inserter<boost::mpl::list<> > >
|
||||||
|
{
|
||||||
|
BOOST_STATIC_ASSERT((is_basic_type<X>::value));
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename X> struct
|
||||||
|
matching_fields<T, X, typename boost::enable_if<is_container<X> >::type>
|
||||||
|
: boost::mpl::copy_if<typename schema<T>::type::fields,
|
||||||
|
is_matching_container_field<X, _>,
|
||||||
|
boost::mpl::front_inserter<boost::mpl::list<> > > {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
nested_fields
|
||||||
|
: boost::mpl::copy_if<typename schema<T>::type::fields,
|
||||||
|
is_nested_field<_>,
|
||||||
|
boost::mpl::front_inserter<boost::mpl::list<> > > {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
struct_fields
|
||||||
|
: boost::mpl::copy_if<typename schema<T>::type::fields,
|
||||||
|
is_struct_field<_>,
|
||||||
|
boost::mpl::front_inserter<boost::mpl::list<> > > {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
container_fields
|
||||||
|
: boost::mpl::copy_if<typename schema<T>::type::fields,
|
||||||
|
is_container_field<_>,
|
||||||
|
boost::mpl::front_inserter<boost::mpl::list<> > > {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
has_base
|
||||||
|
: has_schema<typename schema<T>::type::base> {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
BondDataType
|
||||||
|
GetTypeId(const T&)
|
||||||
|
{
|
||||||
|
return get_type_id<T>::value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Reader>
|
||||||
|
BondDataType
|
||||||
|
GetTypeId(const value<void, Reader>& value)
|
||||||
|
{
|
||||||
|
return value.GetTypeId();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Reader> struct
|
||||||
|
get_type_id<value<T, Reader> >
|
||||||
|
: get_type_id<T> {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T1, typename T2> struct
|
||||||
|
get_type_id<std::pair<T1, T2> >
|
||||||
|
{
|
||||||
|
static const std::pair<BondDataType, BondDataType> value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T1, typename T2>
|
||||||
|
const std::pair<BondDataType, BondDataType>
|
||||||
|
get_type_id<std::pair<T1, T2> >::value = std::make_pair(
|
||||||
|
get_type_id<typename remove_const<T1>::type>::value,
|
||||||
|
get_type_id<T2>::value);
|
||||||
|
|
||||||
|
template <> struct
|
||||||
|
get_type_id<bool>
|
||||||
|
: boost::integral_constant<BondDataType, BT_BOOL> {};
|
||||||
|
|
||||||
|
template <> struct
|
||||||
|
get_type_id<uint8_t>
|
||||||
|
: boost::integral_constant<BondDataType, BT_UINT8> {};
|
||||||
|
|
||||||
|
template <> struct
|
||||||
|
get_type_id<uint16_t>
|
||||||
|
: boost::integral_constant<BondDataType, BT_UINT16> {};
|
||||||
|
|
||||||
|
template <> struct
|
||||||
|
get_type_id<uint32_t>
|
||||||
|
: boost::integral_constant<BondDataType, BT_UINT32> {};
|
||||||
|
|
||||||
|
template <> struct
|
||||||
|
get_type_id<uint64_t>
|
||||||
|
: boost::integral_constant<BondDataType, BT_UINT64> {};
|
||||||
|
|
||||||
|
template <> struct
|
||||||
|
get_type_id<int8_t>
|
||||||
|
: boost::integral_constant<BondDataType, BT_INT8> {};
|
||||||
|
|
||||||
|
template <> struct
|
||||||
|
get_type_id<int16_t>
|
||||||
|
: boost::integral_constant<BondDataType, BT_INT16> {};
|
||||||
|
|
||||||
|
template <> struct
|
||||||
|
get_type_id<int32_t>
|
||||||
|
: boost::integral_constant<BondDataType, BT_INT32> {};
|
||||||
|
|
||||||
|
template <> struct
|
||||||
|
get_type_id<int64_t>
|
||||||
|
: boost::integral_constant<BondDataType, BT_INT64> {};
|
||||||
|
|
||||||
|
template <> struct
|
||||||
|
get_type_id<float>
|
||||||
|
: boost::integral_constant<BondDataType, BT_FLOAT> {};
|
||||||
|
|
||||||
|
template <> struct
|
||||||
|
get_type_id<double>
|
||||||
|
: boost::integral_constant<BondDataType, BT_DOUBLE> {};
|
||||||
|
|
||||||
|
template <> struct
|
||||||
|
get_type_id<void>
|
||||||
|
: boost::integral_constant<BondDataType, BT_UNAVAILABLE> {};
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
get_type_id
|
||||||
|
{
|
||||||
|
typedef typename remove_const<T>::type U;
|
||||||
|
|
||||||
|
static const BondDataType value =
|
||||||
|
is_enum<T>::value ? get_type_id<int32_t>::value :
|
||||||
|
is_bond_type<T>::value ? BT_STRUCT :
|
||||||
|
is_set_container<U>::value ? BT_SET :
|
||||||
|
is_map_container<U>::value ? BT_MAP :
|
||||||
|
is_list_container<U>::value ? BT_LIST :
|
||||||
|
is_string<U>::value ? BT_STRING :
|
||||||
|
is_wstring<U>::value ? BT_WSTRING :
|
||||||
|
get_type_id<typename aliased_type<T>::type>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
const BondDataType get_type_id<T>::value;
|
||||||
|
|
||||||
|
class PrimitiveTypes
|
||||||
|
{
|
||||||
|
struct Init
|
||||||
|
{
|
||||||
|
Init(uint32_t* _sizeof)
|
||||||
|
: _sizeof(_sizeof)
|
||||||
|
{}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void operator()(const T&)
|
||||||
|
{
|
||||||
|
_sizeof[get_type_id<T>::value] = sizeof(T);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t* _sizeof;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
typedef boost::mpl::list
|
||||||
|
<
|
||||||
|
bool, float, double,
|
||||||
|
uint8_t, uint16_t, uint32_t, uint64_t,
|
||||||
|
int8_t, int16_t, int32_t, int64_t
|
||||||
|
>type;
|
||||||
|
|
||||||
|
PrimitiveTypes(uint32_t* _sizeof)
|
||||||
|
{
|
||||||
|
boost::mpl::for_each<type>(Init(_sizeof));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
|
@ -0,0 +1,84 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <bond/core/bond_types.h>
|
||||||
|
#include "detail/nonassignable.h"
|
||||||
|
#include <boost/shared_ptr.hpp>
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
struct SchemaDef;
|
||||||
|
struct TypeDef;
|
||||||
|
struct StructDef;
|
||||||
|
struct FieldDef;
|
||||||
|
|
||||||
|
/// @brief Represents runtime schema
|
||||||
|
/// See [User's Manual](../../manual/bond_cpp.html#runtime-schema)
|
||||||
|
class RuntimeSchema
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// @brief Default constructor
|
||||||
|
RuntimeSchema()
|
||||||
|
: schema(NULL),
|
||||||
|
type(NULL)
|
||||||
|
{}
|
||||||
|
|
||||||
|
#ifndef BOND_NO_CXX11_RVALUE_REFERENCES
|
||||||
|
RuntimeSchema(RuntimeSchema&& rhs)
|
||||||
|
: schema(rhs.schema),
|
||||||
|
type(rhs.type),
|
||||||
|
instance(std::move(rhs.instance))
|
||||||
|
{}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef BOND_NO_CXX11_DEFAULTED_FUNCTIONS
|
||||||
|
RuntimeSchema& operator=(const RuntimeSchema& that) = default;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// @brief Construct from a share_ptr to a SchemaDef object
|
||||||
|
RuntimeSchema(const boost::shared_ptr<SchemaDef>& schema);
|
||||||
|
|
||||||
|
/// @brief Construct from a reference to a SchemaDef object
|
||||||
|
///
|
||||||
|
/// This ctor should be used only when it can be guaranteed that liftime of
|
||||||
|
/// the SchemaDef object referenced by the schema argument is longer than
|
||||||
|
/// lifetime of any copy of the RuntimeSchema object being constructed.
|
||||||
|
/// In most cases it is safer to use the ctor taking shared_ptr<SchemaDef>.
|
||||||
|
explicit RuntimeSchema(const SchemaDef& schema);
|
||||||
|
|
||||||
|
RuntimeSchema(const RuntimeSchema& schema);
|
||||||
|
RuntimeSchema(const RuntimeSchema& schema, const TypeDef& type);
|
||||||
|
RuntimeSchema(const RuntimeSchema& schema, const FieldDef& field);
|
||||||
|
|
||||||
|
const RuntimeSchema* get() const
|
||||||
|
{
|
||||||
|
return schema ? this : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Returns constant reference to SchemaDef object
|
||||||
|
const SchemaDef& GetSchema() const
|
||||||
|
{
|
||||||
|
return *schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TypeDef& GetType() const
|
||||||
|
{
|
||||||
|
return *type;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HasBase() const;
|
||||||
|
RuntimeSchema GetBaseSchema() const;
|
||||||
|
const StructDef& GetStruct() const;
|
||||||
|
BondDataType GetTypeId() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const SchemaDef* schema;
|
||||||
|
const TypeDef* type;
|
||||||
|
boost::shared_ptr<SchemaDef> instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace bond
|
|
@ -0,0 +1,30 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
// Trait defining what built-it type is aliased by T
|
||||||
|
template <typename T> struct
|
||||||
|
aliased_type
|
||||||
|
{
|
||||||
|
typedef void type;
|
||||||
|
};
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
|
||||||
|
// Overloaded function to set variable to an aliased value
|
||||||
|
template <typename T>
|
||||||
|
void set_aliased_value(T& var, typename aliased_type<T>::type value);
|
||||||
|
|
||||||
|
|
||||||
|
// Overloaded function to get an aliased value
|
||||||
|
template <typename T>
|
||||||
|
typename aliased_type<T>::type get_aliased_value(const T& value);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,355 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "reflection.h"
|
||||||
|
#include "runtime_schema.h"
|
||||||
|
#include "detail/tags.h"
|
||||||
|
#include "detail/once.h"
|
||||||
|
#include <boost/make_shared.hpp>
|
||||||
|
#include <boost/bind.hpp>
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
inline RuntimeSchema::RuntimeSchema(const RuntimeSchema& schema)
|
||||||
|
: schema(schema.schema),
|
||||||
|
type(schema.type),
|
||||||
|
instance(schema.instance)
|
||||||
|
{}
|
||||||
|
|
||||||
|
inline RuntimeSchema::RuntimeSchema(const SchemaDef& schema)
|
||||||
|
: schema(&schema),
|
||||||
|
type(&schema.root)
|
||||||
|
{}
|
||||||
|
|
||||||
|
inline RuntimeSchema::RuntimeSchema(const boost::shared_ptr<SchemaDef>& schema)
|
||||||
|
: schema(schema.get()),
|
||||||
|
type(&schema->root),
|
||||||
|
instance(schema)
|
||||||
|
{}
|
||||||
|
|
||||||
|
inline RuntimeSchema::RuntimeSchema(const RuntimeSchema& schema, const TypeDef& type)
|
||||||
|
: schema(schema.schema),
|
||||||
|
type(&type),
|
||||||
|
instance(schema.instance)
|
||||||
|
{}
|
||||||
|
|
||||||
|
inline RuntimeSchema::RuntimeSchema(const RuntimeSchema& schema, const FieldDef& field)
|
||||||
|
: schema(schema.schema),
|
||||||
|
type(&field.type),
|
||||||
|
instance(schema.instance)
|
||||||
|
{}
|
||||||
|
|
||||||
|
inline bool RuntimeSchema::HasBase() const
|
||||||
|
{
|
||||||
|
return !GetStruct().base_def.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline RuntimeSchema RuntimeSchema::GetBaseSchema() const
|
||||||
|
{
|
||||||
|
return RuntimeSchema(*this, GetStruct().base_def.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const StructDef& RuntimeSchema::GetStruct() const
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(type->id == BT_STRUCT);
|
||||||
|
return schema->structs[type->struct_def];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline BondDataType RuntimeSchema::GetTypeId() const
|
||||||
|
{
|
||||||
|
return type->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Writer>
|
||||||
|
inline void Serialize(const RuntimeSchema& schema, Writer& output)
|
||||||
|
{
|
||||||
|
Apply(SerializeTo(output), schema.GetSchema());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Writer>
|
||||||
|
inline void Marshal(const RuntimeSchema& schema, Writer& output)
|
||||||
|
{
|
||||||
|
Apply(MarshalTo(output), schema.GetSchema());
|
||||||
|
}
|
||||||
|
|
||||||
|
class InitSchemaDef;
|
||||||
|
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
inline uint16_t schema_depth(const RuntimeSchema& schema)
|
||||||
|
{
|
||||||
|
uint16_t depth = 1;
|
||||||
|
|
||||||
|
if (schema.HasBase())
|
||||||
|
depth += schema_depth(schema.GetBaseSchema());
|
||||||
|
|
||||||
|
return depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename Unused = void>
|
||||||
|
class SchemaCache
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static const SchemaDef& Get()
|
||||||
|
{
|
||||||
|
// The schema object can't be initialized as a global static
|
||||||
|
// because InitSchemaDef uses schema's and fields' metadata which
|
||||||
|
// are global static variables, and we can't depends on them being
|
||||||
|
// initialized before the schema. Instead we initialize the schema
|
||||||
|
// on the first call to Get().
|
||||||
|
// Note that older versions of GNU C++ don't handle rvalue argument
|
||||||
|
// forwarding in Boost call_once implementation so we are using
|
||||||
|
// the old trusty boost::bind.
|
||||||
|
call_once(flag, boost::bind(&AppendStructDef, &schema));
|
||||||
|
return schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void AppendStructDef(SchemaDef* s);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static SchemaDef schema;
|
||||||
|
static once_flag flag;
|
||||||
|
};
|
||||||
|
|
||||||
|
// We need the Unused template parameter because otherwise the 'schema'
|
||||||
|
// static would not be a static member of a class template and would have
|
||||||
|
// to be defined in a .cpp and we want Bond to be header-only for some
|
||||||
|
// scenarios.
|
||||||
|
template <typename Unused>
|
||||||
|
class SchemaCache<Unknown, Unused>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static const SchemaDef& Get()
|
||||||
|
{
|
||||||
|
return schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SchemaDef NewSchemaDef()
|
||||||
|
{
|
||||||
|
// SchemaDef for unknown types: struct with no fields
|
||||||
|
SchemaDef schema;
|
||||||
|
schema.root.id = BT_STRUCT;
|
||||||
|
schema.structs.resize(1);
|
||||||
|
return schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static const SchemaDef schema;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename Unused>
|
||||||
|
SchemaDef SchemaCache<T, Unused>::schema;
|
||||||
|
|
||||||
|
template <typename Unused>
|
||||||
|
const SchemaDef SchemaCache<Unknown, Unused>::schema
|
||||||
|
= SchemaCache<Unknown>::NewSchemaDef();
|
||||||
|
|
||||||
|
template <typename T, typename Unused>
|
||||||
|
once_flag SchemaCache<T, Unused>::flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// InitSchemaDef transform creates an instance of runtime schema for the input
|
||||||
|
//
|
||||||
|
class InitSchemaDef
|
||||||
|
: public SerializingTransform
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
InitSchemaDef(SchemaDef& schema)
|
||||||
|
: _schema(schema),
|
||||||
|
_struct_def(schema.structs.size())
|
||||||
|
{
|
||||||
|
_schema.structs.push_back(StructDef());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Begin(const Metadata& metadata) const
|
||||||
|
{
|
||||||
|
This().metadata = metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
void End() const
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool Base(const T& /*value*/) const
|
||||||
|
{
|
||||||
|
TypeDef type = GetTypeDef<T>();
|
||||||
|
This().base_def.set(type);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// field
|
||||||
|
template <typename T>
|
||||||
|
bool Field(uint16_t id, const Metadata& metadata, const T&) const
|
||||||
|
{
|
||||||
|
FieldDef field;
|
||||||
|
|
||||||
|
field.id = id;
|
||||||
|
field.metadata = metadata;
|
||||||
|
field.type = GetTypeDef<typename remove_maybe<T>::type>();
|
||||||
|
|
||||||
|
This().fields.push_back(field);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if<is_basic_type<T>, TypeDef>::type
|
||||||
|
GetTypeDef() const
|
||||||
|
{
|
||||||
|
TypeDef type;
|
||||||
|
|
||||||
|
type.id = get_type_id<T>::value;
|
||||||
|
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if_c<is_container<T>::value && !is_map_container<T>::value, TypeDef>::type
|
||||||
|
GetTypeDef() const
|
||||||
|
{
|
||||||
|
TypeDef type;
|
||||||
|
|
||||||
|
type.id = get_type_id<T>::value;
|
||||||
|
type.element.set() = GetTypeDef<typename element_type<T>::type>();
|
||||||
|
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if<is_map_container<T>, TypeDef>::type
|
||||||
|
GetTypeDef() const
|
||||||
|
{
|
||||||
|
TypeDef type;
|
||||||
|
|
||||||
|
type.id = get_type_id<T>::value;
|
||||||
|
type.key.set() = GetTypeDef<typename element_type<T>::type::first_type>();
|
||||||
|
type.element.set() = GetTypeDef<typename element_type<T>::type::second_type>();
|
||||||
|
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if<is_bond_type<T>, TypeDef>::type
|
||||||
|
GetTypeDef() const
|
||||||
|
{
|
||||||
|
TypeDef type;
|
||||||
|
|
||||||
|
type.id = get_type_id<T>::value;
|
||||||
|
type.struct_def = GetStructDef<typename remove_bonded<T>::type>();
|
||||||
|
type.bonded_type = is_bonded<T>::value;
|
||||||
|
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
uint16_t GetStructDef() const
|
||||||
|
{
|
||||||
|
size_t n;
|
||||||
|
|
||||||
|
for (n = 0; n < _schema.structs.size(); ++n)
|
||||||
|
if (_schema.structs[n].metadata.qualified_name == schema<T>::type::metadata.qualified_name)
|
||||||
|
return static_cast<uint16_t>(n);
|
||||||
|
|
||||||
|
detail::SchemaCache<T>::AppendStructDef(&_schema);
|
||||||
|
|
||||||
|
BOOST_ASSERT(n == static_cast<uint16_t>(n));
|
||||||
|
return static_cast<uint16_t>(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Note that This() returns a reference to StructDef in the structs vector
|
||||||
|
// which may be invalidated when the vector grows. In particular This()
|
||||||
|
// can't be used in an expression that may result in adding items to the
|
||||||
|
// vector.
|
||||||
|
StructDef& This() const
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(_schema.structs.size() > _struct_def);
|
||||||
|
return _schema.structs[_struct_def];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SchemaDef& _schema;
|
||||||
|
const size_t _struct_def;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename T, typename Unused>
|
||||||
|
void SchemaCache<T, Unused>::AppendStructDef(SchemaDef* s)
|
||||||
|
{
|
||||||
|
// To apply InitSchemaDef transform we need a reference to an
|
||||||
|
// object T. However the transform never accesses the object or
|
||||||
|
// its fields. We can't construct an actual object since we
|
||||||
|
// need to support stateful allocators that can't allocate from
|
||||||
|
// a default-constructed instance and containers which allocate in
|
||||||
|
// default constructor.
|
||||||
|
char o;
|
||||||
|
Apply(InitSchemaDef(*s), reinterpret_cast<const T&>(o));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
|
||||||
|
/// @brief Returns an instance of RuntimeSchema for a user defined struct
|
||||||
|
template <typename T>
|
||||||
|
inline RuntimeSchema GetRuntimeSchema()
|
||||||
|
{
|
||||||
|
return RuntimeSchema(detail::SchemaCache<T>::Get());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline RuntimeSchema GetRuntimeSchema(const T&)
|
||||||
|
{
|
||||||
|
return RuntimeSchema(detail::SchemaCache<T>::Get());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline RuntimeSchema element_schema(const RuntimeSchema& schema)
|
||||||
|
{
|
||||||
|
if (!schema.GetType().element.empty())
|
||||||
|
return RuntimeSchema(schema, schema.GetType().element.value());
|
||||||
|
else
|
||||||
|
return GetRuntimeSchema<Unknown>();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline RuntimeSchema key_schema(const RuntimeSchema& schema)
|
||||||
|
{
|
||||||
|
if (!schema.GetType().key.empty())
|
||||||
|
return RuntimeSchema(schema, schema.GetType().key.value());
|
||||||
|
else
|
||||||
|
return GetRuntimeSchema<Unknown>();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Returns a const reference to a map of values for a user defined enum
|
||||||
|
template<typename T>
|
||||||
|
inline const std::map<T, std::string>& GetEnumValues()
|
||||||
|
{
|
||||||
|
return GetValueToNameMap(T());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Returns a const reference to a map of names for a user defined enum
|
||||||
|
template<typename T>
|
||||||
|
inline const std::map<std::string, T>& GetEnumNames()
|
||||||
|
{
|
||||||
|
return GetNameToValueMap(T());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}; // namespace bond
|
|
@ -0,0 +1,276 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "exception.h"
|
||||||
|
#include "protocol.h"
|
||||||
|
#include "runtime_schema.h"
|
||||||
|
#include "exception.h"
|
||||||
|
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable: 4702)
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
// Overload of Apply used to extract bonded<T> from marshaled payload
|
||||||
|
template <typename T, typename U, typename Reader>
|
||||||
|
inline bool
|
||||||
|
Apply(const boost::reference_wrapper<bonded<T> >& ref, const bonded<U, Reader>& value)
|
||||||
|
{
|
||||||
|
value.Deserialize(ref.get());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Select protocol and apply transform using compile-time schema
|
||||||
|
template <typename T, typename Buffer, typename Transform>
|
||||||
|
inline std::pair<ProtocolType, bool> NextProtocol(
|
||||||
|
const boost::mpl::l_iter<boost::mpl::l_end>&,
|
||||||
|
Buffer&,
|
||||||
|
const Transform&)
|
||||||
|
{
|
||||||
|
UnknownProtocolException();
|
||||||
|
return std::make_pair(MARSHALED_PROTOCOL, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Buffer, typename Transform, typename Iter>
|
||||||
|
inline std::pair<ProtocolType, bool> NextProtocol(
|
||||||
|
const Iter&,
|
||||||
|
Buffer& input,
|
||||||
|
const Transform& transform)
|
||||||
|
{
|
||||||
|
typedef typename boost::mpl::deref<Iter>::type Reader;
|
||||||
|
|
||||||
|
Reader reader(input);
|
||||||
|
|
||||||
|
if (reader.ReadVersion())
|
||||||
|
{
|
||||||
|
return std::make_pair(
|
||||||
|
static_cast<ProtocolType>(Reader::magic),
|
||||||
|
Apply(transform, bonded<T, ProtocolReader<Buffer> >(reader)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return NextProtocol<T>(typename boost::mpl::next<Iter>::type(), input, transform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Select protocol and apply transform using runtime schema
|
||||||
|
template <typename Buffer, typename Transform>
|
||||||
|
inline std::pair<ProtocolType, bool> NextProtocol(
|
||||||
|
const boost::mpl::l_iter<boost::mpl::l_end>&,
|
||||||
|
const RuntimeSchema&,
|
||||||
|
Buffer&,
|
||||||
|
const Transform&)
|
||||||
|
{
|
||||||
|
UnknownProtocolException();
|
||||||
|
return std::make_pair(MARSHALED_PROTOCOL, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Buffer, typename Transform, typename Iter>
|
||||||
|
inline std::pair<ProtocolType, bool> NextProtocol(
|
||||||
|
const Iter&,
|
||||||
|
const RuntimeSchema& schema,
|
||||||
|
Buffer& input,
|
||||||
|
const Transform& transform)
|
||||||
|
{
|
||||||
|
typedef typename boost::mpl::deref<Iter>::type Reader;
|
||||||
|
|
||||||
|
Reader reader(input);
|
||||||
|
|
||||||
|
if (reader.ReadVersion())
|
||||||
|
{
|
||||||
|
return std::make_pair(
|
||||||
|
static_cast<ProtocolType>(Reader::magic),
|
||||||
|
Apply(transform, bonded<void, ProtocolReader<Buffer> >(reader, schema)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return NextProtocol(typename boost::mpl::next<Iter>::type(), schema, input, transform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Select protocol based on magic number and apply transform using compile-time schema
|
||||||
|
template <typename T, typename Buffer, typename Transform>
|
||||||
|
inline bool NextProtocol(
|
||||||
|
const boost::mpl::l_iter<boost::mpl::l_end>&,
|
||||||
|
Buffer&, const Transform&, uint16_t protocol)
|
||||||
|
{
|
||||||
|
UnknownProtocolException(protocol);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Buffer, typename Transform, typename Iter>
|
||||||
|
inline bool NextProtocol(
|
||||||
|
const Iter&,
|
||||||
|
Buffer& input,
|
||||||
|
const Transform& transform,
|
||||||
|
uint16_t protocol)
|
||||||
|
{
|
||||||
|
typedef typename boost::mpl::deref<Iter>::type Reader;
|
||||||
|
|
||||||
|
if (Reader::magic == protocol)
|
||||||
|
{
|
||||||
|
Reader reader(input);
|
||||||
|
return Apply(transform, bonded<T, Reader&>(reader));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return NextProtocol<T>(typename boost::mpl::next<Iter>::type(), input, transform, protocol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Select protocol based on magic number and apply transform using runtime schema
|
||||||
|
template <typename Buffer, typename Transform>
|
||||||
|
inline bool NextProtocol(
|
||||||
|
const boost::mpl::l_iter<boost::mpl::l_end>&,
|
||||||
|
const RuntimeSchema&, Buffer&, const Transform&, uint16_t protocol)
|
||||||
|
{
|
||||||
|
UnknownProtocolException(protocol);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Buffer, typename Transform, typename Iter>
|
||||||
|
inline bool NextProtocol(
|
||||||
|
const Iter&,
|
||||||
|
const RuntimeSchema& schema,
|
||||||
|
Buffer& input,
|
||||||
|
const Transform& transform,
|
||||||
|
uint16_t protocol)
|
||||||
|
{
|
||||||
|
typedef typename boost::mpl::deref<Iter>::type Reader;
|
||||||
|
|
||||||
|
if (Reader::magic == protocol)
|
||||||
|
{
|
||||||
|
Reader reader(input);
|
||||||
|
return Apply(transform, bonded<void, Reader&>(reader, schema));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return NextProtocol(typename boost::mpl::next<Iter>::type(), schema, input, transform, protocol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Select protocol based on magic number and apply instance of serializing transform
|
||||||
|
template <template <typename Writer> class Transform, typename Buffer, typename T>
|
||||||
|
inline bool NextProtocol(
|
||||||
|
const boost::mpl::l_iter<boost::mpl::l_end>&,
|
||||||
|
const T&, Buffer&, uint16_t protocol)
|
||||||
|
{
|
||||||
|
UnknownProtocolException(protocol);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <template <typename Writer> class Transform, typename Buffer, typename T, typename Iter>
|
||||||
|
inline bool NextProtocol(
|
||||||
|
const Iter&,
|
||||||
|
const T& value,
|
||||||
|
Buffer& output,
|
||||||
|
uint16_t protocol)
|
||||||
|
{
|
||||||
|
typedef typename boost::mpl::deref<Iter>::type Reader;
|
||||||
|
|
||||||
|
if (Reader::magic == protocol)
|
||||||
|
{
|
||||||
|
typename Reader::Writer writer(output);
|
||||||
|
return Apply(Transform<typename Reader::Writer>(writer), value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return NextProtocol<Transform>(typename boost::mpl::next<Iter>::type(), value, output, protocol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Apply transform to serialized data that was generated using Marshaler
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
// Use compile-time schema
|
||||||
|
template <typename T, typename Buffer, typename Transform>
|
||||||
|
inline std::pair<ProtocolType, bool> SelectProtocolAndApply(
|
||||||
|
Buffer& input,
|
||||||
|
const Transform& transform)
|
||||||
|
{
|
||||||
|
return detail::NextProtocol<T>(typename Protocols<Buffer>::begin(), input, transform);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Use runtime schema
|
||||||
|
template <typename Buffer, typename Transform>
|
||||||
|
inline std::pair<ProtocolType, bool> SelectProtocolAndApply(
|
||||||
|
const RuntimeSchema& schema,
|
||||||
|
Buffer& input,
|
||||||
|
const Transform& transform)
|
||||||
|
{
|
||||||
|
return detail::NextProtocol(typename Protocols<Buffer>::begin(), schema, input, transform);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Apply deserializing transform with a protocol specified by magic number
|
||||||
|
// Use compile-time schema
|
||||||
|
template <typename T, typename Buffer, typename Transform>
|
||||||
|
inline bool Apply(
|
||||||
|
const Transform& transform,
|
||||||
|
Buffer& input,
|
||||||
|
uint16_t protocol)
|
||||||
|
{
|
||||||
|
return detail::NextProtocol<T>(
|
||||||
|
typename Protocols<Buffer>::begin(),
|
||||||
|
input,
|
||||||
|
transform,
|
||||||
|
protocol
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Use runtime schema
|
||||||
|
template <typename Buffer, typename Transform>
|
||||||
|
inline bool Apply(
|
||||||
|
const Transform& transform,
|
||||||
|
const RuntimeSchema& schema,
|
||||||
|
Buffer& input,
|
||||||
|
uint16_t protocol)
|
||||||
|
{
|
||||||
|
return detail::NextProtocol(
|
||||||
|
typename Protocols<Buffer>::begin(),
|
||||||
|
schema,
|
||||||
|
input,
|
||||||
|
transform,
|
||||||
|
protocol
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Apply an instance of serializing transform with a protocol specified by magic number
|
||||||
|
template <template <typename Writer> class Transform, typename Buffer, typename T>
|
||||||
|
inline bool Apply(const T& value, Buffer& output, uint16_t protocol)
|
||||||
|
{
|
||||||
|
return detail::NextProtocol<Transform>(
|
||||||
|
typename Protocols<Buffer>::begin(),
|
||||||
|
value,
|
||||||
|
output,
|
||||||
|
protocol);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace bond
|
||||||
|
|
||||||
|
#pragma warning(pop)
|
|
@ -0,0 +1,475 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <boost/utility/enable_if.hpp>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string>
|
||||||
|
#include <list>
|
||||||
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
#include <set>
|
||||||
|
#include "container_interface.h"
|
||||||
|
#include "traits.h"
|
||||||
|
|
||||||
|
// Bond container interface on top of STL container classes
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename Key>
|
||||||
|
void ElementNotFoundException(const Key& key);
|
||||||
|
|
||||||
|
// is_string<std::basic_string<char, T, A> >
|
||||||
|
template<typename T, typename A> struct
|
||||||
|
is_string<std::basic_string<char, T, A> >
|
||||||
|
: true_type {};
|
||||||
|
|
||||||
|
|
||||||
|
// is_wstring<std::basic_string<wchar_t, T, A> >
|
||||||
|
template<typename T, typename A> struct
|
||||||
|
is_wstring<std::basic_string<wchar_t, T, A> >
|
||||||
|
: true_type {};
|
||||||
|
|
||||||
|
|
||||||
|
// is_string_type
|
||||||
|
template <typename T> struct
|
||||||
|
is_string_type
|
||||||
|
{
|
||||||
|
typedef typename remove_const<T>::type U;
|
||||||
|
|
||||||
|
static const bool value = is_string<U>::value
|
||||||
|
|| is_wstring<U>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// is_list_container<std::list<T, A> >
|
||||||
|
template <typename T, typename A> struct
|
||||||
|
is_list_container<std::list<T, A> >
|
||||||
|
: true_type {};
|
||||||
|
|
||||||
|
|
||||||
|
// is_list_container<std::vector<T, A> >
|
||||||
|
template <typename T, typename A> struct
|
||||||
|
is_list_container<std::vector<T, A> >
|
||||||
|
: true_type {};
|
||||||
|
|
||||||
|
|
||||||
|
// require_modify_element<std::vector<bool, A> >
|
||||||
|
template <typename A> struct
|
||||||
|
require_modify_element<std::vector<bool, A> >
|
||||||
|
: true_type {};
|
||||||
|
|
||||||
|
|
||||||
|
// is_set_container<std::set<T, C, A> >
|
||||||
|
template <typename T, typename C, typename A> struct
|
||||||
|
is_set_container<std::set<T, C, A> >
|
||||||
|
: true_type {};
|
||||||
|
|
||||||
|
|
||||||
|
// is_map_container<std::map<K, T, C, A> >
|
||||||
|
template <typename K, typename T, typename C, typename A> struct
|
||||||
|
is_map_container<std::map<K, T, C, A> >
|
||||||
|
: true_type {};
|
||||||
|
|
||||||
|
|
||||||
|
// specialize element_type for map becuase map::value_type is pair<const K, T>
|
||||||
|
template <typename K, typename T, typename C, typename A> struct
|
||||||
|
element_type<std::map<K, T, C, A> >
|
||||||
|
{
|
||||||
|
typedef typename std::pair<K, T> type;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// string_data
|
||||||
|
template<typename C, typename T, typename A>
|
||||||
|
inline
|
||||||
|
const C* string_data(const std::basic_string<C, T, A>& str)
|
||||||
|
{
|
||||||
|
return str.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<typename C, typename T, typename A>
|
||||||
|
inline
|
||||||
|
C* string_data(std::basic_string<C, T, A>& str)
|
||||||
|
{
|
||||||
|
// C++11 disallows COW string implementation (see [string.require] 21.4.1)
|
||||||
|
// however it was permitted in C++03. In order to support COW we can’t
|
||||||
|
// return data() here.
|
||||||
|
static C c;
|
||||||
|
return str.size() ? &*str.begin() : &c;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// string_length
|
||||||
|
template<typename C, typename T, typename A>
|
||||||
|
inline
|
||||||
|
uint32_t string_length(const std::basic_string<C, T, A>& str)
|
||||||
|
{
|
||||||
|
return static_cast<uint32_t>(str.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// resize_string
|
||||||
|
template<typename C, typename T, typename A>
|
||||||
|
inline
|
||||||
|
void resize_string(std::basic_string<C, T, A>& str, uint32_t size)
|
||||||
|
{
|
||||||
|
str.resize(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// container_size
|
||||||
|
template <typename T>
|
||||||
|
inline
|
||||||
|
uint32_t container_size(const T& list)
|
||||||
|
{
|
||||||
|
return static_cast<uint32_t>(list.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Enable = void> struct
|
||||||
|
has_key_compare
|
||||||
|
: false_type {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
has_key_compare<T, typename boost::enable_if<is_class<typename T::key_compare> >::type>
|
||||||
|
: true_type {};
|
||||||
|
|
||||||
|
|
||||||
|
// use_container_allocator_for_elements
|
||||||
|
template <typename T, typename Enable = void> struct
|
||||||
|
use_container_allocator_for_elements
|
||||||
|
: false_type {};
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(BOND_NO_CXX11_ALLOCATOR)
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
use_container_allocator_for_elements<T, typename boost::enable_if<
|
||||||
|
is_same<typename T::allocator_type::template rebind<int>::other,
|
||||||
|
typename element_type<T>::type::allocator_type::template rebind<int>::other> >::type>
|
||||||
|
{
|
||||||
|
static const bool value = !is_same<typename T::allocator_type::template rebind<int>::other,
|
||||||
|
std::allocator<int> >::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
use_container_allocator_for_elements<T, typename boost::enable_if_c<
|
||||||
|
has_schema<typename element_type<T>::type>::value
|
||||||
|
&& !is_same<typename T::allocator_type::template rebind<int>::other,
|
||||||
|
std::allocator<int> >::value>::type>
|
||||||
|
: true_type {};
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
use_container_allocator_for_elements<T, typename boost::enable_if_c<
|
||||||
|
std::uses_allocator<typename element_type<T>::type, typename T::allocator_type>::value>::type>
|
||||||
|
: true_type {};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// make_element
|
||||||
|
template <typename T>
|
||||||
|
inline
|
||||||
|
typename boost::disable_if_c<use_container_allocator_for_elements<T>::value, typename element_type<T>::type>::type
|
||||||
|
make_element(T& /*container*/)
|
||||||
|
{
|
||||||
|
return typename element_type<T>::type();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline
|
||||||
|
typename boost::enable_if_c<use_container_allocator_for_elements<T>::value
|
||||||
|
&& !has_key_compare<typename element_type<T>::type>::value, typename element_type<T>::type>::type
|
||||||
|
make_element(T& container)
|
||||||
|
{
|
||||||
|
return typename element_type<T>::type(container.get_allocator());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline
|
||||||
|
typename boost::enable_if_c<use_container_allocator_for_elements<T>::value
|
||||||
|
&& has_key_compare<typename element_type<T>::type>::value, typename element_type<T>::type>::type
|
||||||
|
make_element(T& container)
|
||||||
|
{
|
||||||
|
return typename element_type<T>::type(typename element_type<T>::type::key_compare(), container.get_allocator());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// resize_list
|
||||||
|
template <typename T>
|
||||||
|
inline
|
||||||
|
typename boost::disable_if<use_container_allocator_for_elements<T> >::type
|
||||||
|
resize_list(T& list, uint32_t size)
|
||||||
|
{
|
||||||
|
list.resize(size, make_element(list));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline
|
||||||
|
typename boost::enable_if<use_container_allocator_for_elements<T> >::type
|
||||||
|
resize_list(T& list, uint32_t size)
|
||||||
|
{
|
||||||
|
list.clear();
|
||||||
|
list.resize(size, make_element(list));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// modify_element
|
||||||
|
template <typename A, typename F>
|
||||||
|
inline
|
||||||
|
void modify_element(std::vector<bool, A>&,
|
||||||
|
typename std::vector<bool, A>::reference element,
|
||||||
|
F deserialize)
|
||||||
|
{
|
||||||
|
bool value;
|
||||||
|
|
||||||
|
deserialize(value);
|
||||||
|
element = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// clear_set
|
||||||
|
template <typename T, typename C, typename A>
|
||||||
|
inline
|
||||||
|
void clear_set(std::set<T, C, A>& set)
|
||||||
|
{
|
||||||
|
set.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// set_insert
|
||||||
|
template <typename T, typename C, typename A>
|
||||||
|
inline
|
||||||
|
void set_insert(std::set<T, C, A>& set, const T& item)
|
||||||
|
{
|
||||||
|
set.insert(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// clear_map
|
||||||
|
template <typename K, typename T, typename C, typename A>
|
||||||
|
inline
|
||||||
|
void clear_map(std::map<K, T, C, A>& map)
|
||||||
|
{
|
||||||
|
map.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// use_map_allocator_for_keys
|
||||||
|
template <typename T, typename Enable = void> struct
|
||||||
|
use_map_allocator_for_keys
|
||||||
|
: false_type {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
use_map_allocator_for_keys
|
||||||
|
<T, typename boost::enable_if<
|
||||||
|
is_same<typename T::allocator_type::template rebind<int>::other,
|
||||||
|
typename element_type<T>::type::first_type::allocator_type::template rebind<int>::other> >::type>
|
||||||
|
{
|
||||||
|
static const bool value = !is_same<typename T::allocator_type::template rebind<int>::other,
|
||||||
|
std::allocator<int> >::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// make_key
|
||||||
|
template <typename T>
|
||||||
|
inline
|
||||||
|
typename boost::disable_if<use_map_allocator_for_keys<T>, typename element_type<T>::type::first_type>::type
|
||||||
|
make_key(T& /*map*/)
|
||||||
|
{
|
||||||
|
return typename element_type<T>::type::first_type();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline
|
||||||
|
typename boost::enable_if<use_map_allocator_for_keys<T>, typename element_type<T>::type::first_type>::type
|
||||||
|
make_key(T& map)
|
||||||
|
{
|
||||||
|
return typename element_type<T>::type::first_type(map.get_allocator());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// use_map_allocator_for_values
|
||||||
|
template <typename T, typename Enable = void> struct
|
||||||
|
use_map_allocator_for_values
|
||||||
|
: false_type {};
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(BOND_NO_CXX11_ALLOCATOR)
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
use_map_allocator_for_values<T, typename boost::enable_if<
|
||||||
|
is_same<typename T::allocator_type::template rebind<int>::other,
|
||||||
|
typename element_type<T>::type::second_type::allocator_type::template rebind<int>::other> >::type>
|
||||||
|
{
|
||||||
|
static const bool value = !is_same<typename T::allocator_type::template rebind<int>::other,
|
||||||
|
std::allocator<int> >::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
use_map_allocator_for_values<T, typename boost::enable_if_c<
|
||||||
|
has_schema<typename element_type<T>::type::second_type>::value
|
||||||
|
&& !is_same<typename T::allocator_type::template rebind<int>::other,
|
||||||
|
std::allocator<int> >::value>::type>
|
||||||
|
: true_type {};
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
use_map_allocator_for_values<T, typename boost::enable_if_c<
|
||||||
|
std::uses_allocator<typename element_type<T>::type::second_type,
|
||||||
|
typename T::allocator_type>::value>::type>
|
||||||
|
: true_type {};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// make_value
|
||||||
|
template <typename T>
|
||||||
|
inline
|
||||||
|
typename boost::disable_if_c<use_map_allocator_for_values<T>::value,
|
||||||
|
typename element_type<T>::type::second_type>::type
|
||||||
|
make_value(T& /*map*/)
|
||||||
|
{
|
||||||
|
return typename element_type<T>::type::second_type();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline
|
||||||
|
typename boost::enable_if_c<use_map_allocator_for_values<T>::value
|
||||||
|
&& !has_key_compare<typename element_type<T>::type::second_type>::value,
|
||||||
|
typename element_type<T>::type::second_type>::type
|
||||||
|
make_value(T& map)
|
||||||
|
{
|
||||||
|
return typename element_type<T>::type::second_type(map.get_allocator());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline
|
||||||
|
typename boost::enable_if_c<use_map_allocator_for_values<T>::value
|
||||||
|
&& has_key_compare<typename element_type<T>::type::second_type>::value,
|
||||||
|
typename element_type<T>::type::second_type>::type
|
||||||
|
make_value(T& map)
|
||||||
|
{
|
||||||
|
return typename element_type<T>::type::second_type(typename element_type<T>::type::second_type::key_compare(), map.get_allocator());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// mapped_at
|
||||||
|
template <typename K, typename T, typename C, typename A>
|
||||||
|
inline
|
||||||
|
T& mapped_at(std::map<K, T, C, A>& map, const K& key)
|
||||||
|
{
|
||||||
|
return map.insert(typename std::map<K, T, C, A>::value_type(key, make_value(map))).first->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename K, typename T, typename C, typename A>
|
||||||
|
inline
|
||||||
|
const T& mapped_at(const std::map<K, T, C, A>& map, const K& key)
|
||||||
|
{
|
||||||
|
typename std::map<K, T, C, A>::const_iterator it = map.find(key);
|
||||||
|
|
||||||
|
if (it == map.end())
|
||||||
|
ElementNotFoundException(key);
|
||||||
|
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// enumerators
|
||||||
|
template <typename T>
|
||||||
|
class const_enumerator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit const_enumerator(const T& list)
|
||||||
|
: it(list.begin()),
|
||||||
|
end(list.end())
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool more() const
|
||||||
|
{
|
||||||
|
return it != end;
|
||||||
|
}
|
||||||
|
|
||||||
|
typename T::const_reference
|
||||||
|
next()
|
||||||
|
{
|
||||||
|
return *(it++);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
typename T::const_iterator it, end;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename A>
|
||||||
|
class const_enumerator<std::vector<bool, A> >
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit const_enumerator(const std::vector<bool, A>& list)
|
||||||
|
: it(list.begin()),
|
||||||
|
end(list.end())
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool more() const
|
||||||
|
{
|
||||||
|
return it != end;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool next()
|
||||||
|
{
|
||||||
|
return *(it++);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
typename std::vector<bool, A>::const_iterator it, end;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class enumerator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit enumerator(T& list)
|
||||||
|
: it(list.begin()),
|
||||||
|
end(list.end())
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool more() const
|
||||||
|
{
|
||||||
|
return it != end;
|
||||||
|
}
|
||||||
|
|
||||||
|
typename T::reference
|
||||||
|
next()
|
||||||
|
{
|
||||||
|
return *(it++);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
typename T::iterator it, end;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename K, typename V>
|
||||||
|
std::map<V, K> reverse_map(const std::map<K, V>& map)
|
||||||
|
{
|
||||||
|
std::map<V, K> reversed;
|
||||||
|
|
||||||
|
for (typename std::map<K, V>::const_iterator it = map.begin(); it != map.end(); ++it)
|
||||||
|
reversed[it->second] = it->first;
|
||||||
|
|
||||||
|
return reversed;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,133 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "scalar_interface.h"
|
||||||
|
#include <boost/utility/enable_if.hpp>
|
||||||
|
|
||||||
|
#ifndef BOND_NO_CXX11_HDR_TYPE_TRAITS
|
||||||
|
# include <type_traits>
|
||||||
|
# define BOND_TYPE_TRAITS_NAMESPACE ::std
|
||||||
|
#else
|
||||||
|
# include <boost/type_traits.hpp>
|
||||||
|
# define BOND_TYPE_TRAITS_NAMESPACE ::boost
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
|
||||||
|
using BOND_TYPE_TRAITS_NAMESPACE::false_type;
|
||||||
|
using BOND_TYPE_TRAITS_NAMESPACE::is_arithmetic;
|
||||||
|
using BOND_TYPE_TRAITS_NAMESPACE::is_base_of;
|
||||||
|
using BOND_TYPE_TRAITS_NAMESPACE::is_class;
|
||||||
|
using BOND_TYPE_TRAITS_NAMESPACE::is_enum;
|
||||||
|
using BOND_TYPE_TRAITS_NAMESPACE::is_floating_point;
|
||||||
|
using BOND_TYPE_TRAITS_NAMESPACE::is_integral;
|
||||||
|
using BOND_TYPE_TRAITS_NAMESPACE::is_object;
|
||||||
|
using BOND_TYPE_TRAITS_NAMESPACE::is_pod;
|
||||||
|
using BOND_TYPE_TRAITS_NAMESPACE::is_reference;
|
||||||
|
using BOND_TYPE_TRAITS_NAMESPACE::is_same;
|
||||||
|
using BOND_TYPE_TRAITS_NAMESPACE::is_signed;
|
||||||
|
using BOND_TYPE_TRAITS_NAMESPACE::is_unsigned;
|
||||||
|
using BOND_TYPE_TRAITS_NAMESPACE::is_void;
|
||||||
|
using BOND_TYPE_TRAITS_NAMESPACE::make_signed;
|
||||||
|
using BOND_TYPE_TRAITS_NAMESPACE::make_unsigned;
|
||||||
|
using BOND_TYPE_TRAITS_NAMESPACE::remove_const;
|
||||||
|
using BOND_TYPE_TRAITS_NAMESPACE::remove_reference;
|
||||||
|
using BOND_TYPE_TRAITS_NAMESPACE::true_type;
|
||||||
|
|
||||||
|
#undef BOND_TYPE_TRAITS_NAMESPACE
|
||||||
|
|
||||||
|
// is_signed_int
|
||||||
|
template <typename T> struct
|
||||||
|
is_signed_int
|
||||||
|
{
|
||||||
|
static const bool value = is_signed<T>::value
|
||||||
|
&& !is_floating_point<T>::value
|
||||||
|
&& !is_enum<T>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// is_signed_int_or_enum
|
||||||
|
template <typename T> struct
|
||||||
|
is_signed_int_or_enum
|
||||||
|
{
|
||||||
|
static const bool value = is_signed_int<T>::value
|
||||||
|
|| is_enum<T>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// schema
|
||||||
|
template <typename T, typename Enable = void> struct
|
||||||
|
schema;
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
schema<T, typename boost::enable_if<is_class<typename T::Schema::fields> >::type>
|
||||||
|
{
|
||||||
|
typedef typename T::Schema type;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// has_schema
|
||||||
|
template <typename T, typename Enable = void> struct
|
||||||
|
has_schema
|
||||||
|
: false_type {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
has_schema<T, typename boost::enable_if<is_class<typename schema<T>::type> >::type>
|
||||||
|
: true_type {};
|
||||||
|
|
||||||
|
|
||||||
|
// is_protocol_same
|
||||||
|
template <typename Reader, typename Writer> struct
|
||||||
|
is_protocol_same
|
||||||
|
: false_type {};
|
||||||
|
|
||||||
|
|
||||||
|
template <template <typename T> class Reader, typename I, template <typename T> class Writer, typename O> struct
|
||||||
|
is_protocol_same<Reader<I>, Writer<O> >
|
||||||
|
: is_same<typename Reader<O>::Writer, Writer<O> > {};
|
||||||
|
|
||||||
|
// For protocols that have multiple versions, specialize this template
|
||||||
|
template <typename Reader> struct
|
||||||
|
protocol_has_multiple_versions
|
||||||
|
: false_type {};
|
||||||
|
|
||||||
|
|
||||||
|
// ... and define this function.
|
||||||
|
template <typename Reader, typename Writer>
|
||||||
|
bool is_protocol_version_same(const Reader&, const Writer&);
|
||||||
|
|
||||||
|
|
||||||
|
// get_protocol_writer
|
||||||
|
template <typename Reader, typename OutputStream> struct
|
||||||
|
get_protocol_writer;
|
||||||
|
|
||||||
|
|
||||||
|
template <template <typename T> class Reader, typename I, typename OutputStream> struct
|
||||||
|
get_protocol_writer<Reader<I>, OutputStream>
|
||||||
|
{
|
||||||
|
typedef typename Reader<OutputStream>::Writer type;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, T> struct
|
||||||
|
check_method
|
||||||
|
: true_type {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Reader, typename Unused = void> struct
|
||||||
|
uses_marshaled_bonded;
|
||||||
|
|
||||||
|
|
||||||
|
// is_type_alias
|
||||||
|
template <typename T> struct
|
||||||
|
is_type_alias
|
||||||
|
: is_object<typename aliased_type<T>::type> {};
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace bond
|
||||||
|
|
|
@ -0,0 +1,778 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "reflection.h"
|
||||||
|
#include "exception.h"
|
||||||
|
#include "null.h"
|
||||||
|
#include "detail/tags.h"
|
||||||
|
#include "detail/odr.h"
|
||||||
|
#include "detail/omit_default.h"
|
||||||
|
#include "detail/debug.h"
|
||||||
|
#include "detail/double_pass.h"
|
||||||
|
#include "detail/marshaled_bonded.h"
|
||||||
|
|
||||||
|
#include <boost/static_assert.hpp>
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
|
||||||
|
// Transforms take input from parser as a series of calls to the Field methods.
|
||||||
|
// Arguments for the calls privide id, name and value of the fields. Return
|
||||||
|
// value from Field methods must be convertible to bool, with the value of true
|
||||||
|
// indicating that the transform has completed and the parser may exit.
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Serializer writes input using provided protocol writer.
|
||||||
|
// When the input is comming from parsing a struct, applying this transform is
|
||||||
|
// equivalent to serialization using the specfied protocol.
|
||||||
|
// Applying this transform to input from parsing serialized data is equivalent
|
||||||
|
// to transcoding from one protocol to another.
|
||||||
|
//
|
||||||
|
template <typename Writer>
|
||||||
|
class Serializer
|
||||||
|
: public SerializingTransform
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef Writer writer_type;
|
||||||
|
|
||||||
|
BOOST_STATIC_ASSERT(is_writer<Writer>::value);
|
||||||
|
|
||||||
|
Serializer(Writer& output, bool base = false)
|
||||||
|
: _output(output),
|
||||||
|
_base(base)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
bool NeedPass0() const
|
||||||
|
{
|
||||||
|
return _output.NeedPass0();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Pass0>
|
||||||
|
Serializer<Pass0> Rebind(Pass0& pass0) const
|
||||||
|
{
|
||||||
|
return Serializer<Pass0>(pass0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Begin(const Metadata& metadata) const
|
||||||
|
{
|
||||||
|
_output.WriteStructBegin(metadata, _base);
|
||||||
|
}
|
||||||
|
|
||||||
|
void End() const
|
||||||
|
{
|
||||||
|
_output.WriteStructEnd(_base);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UnknownEnd() const
|
||||||
|
{
|
||||||
|
_output.WriteStructEnd(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool Base(const T& value) const
|
||||||
|
{
|
||||||
|
// 'true' means that we are writing a base struct
|
||||||
|
Apply(Serializer<Writer>(_output, true), value);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool Field(uint16_t id, const Metadata& metadata, const T& value) const
|
||||||
|
{
|
||||||
|
if (detail::omit_field<Writer>(metadata, value))
|
||||||
|
{
|
||||||
|
detail::WriteFieldOmitted(_output, GetTypeId(value), id, metadata);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
WriteField(id, metadata, value);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool Field(uint16_t id, const Metadata& metadata, const maybe<T>& value) const
|
||||||
|
{
|
||||||
|
if (detail::omit_field<Writer>(metadata, value))
|
||||||
|
{
|
||||||
|
detail::WriteFieldOmitted(_output, get_type_id<T>::value, id, metadata);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
WriteField(id, metadata, value.value());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// unknown field
|
||||||
|
template <typename T>
|
||||||
|
bool UnknownField(uint16_t id, const T& value) const
|
||||||
|
{
|
||||||
|
_output.WriteFieldBegin(GetTypeId(value), id);
|
||||||
|
Write(value);
|
||||||
|
_output.WriteFieldEnd();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// omitted field
|
||||||
|
bool OmittedField(uint16_t id, const Metadata& metadata, BondDataType type) const
|
||||||
|
{
|
||||||
|
detail::WriteFieldOmitted(_output, type, id, metadata);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void Container(const T& element, uint32_t size) const
|
||||||
|
{
|
||||||
|
_output.WriteContainerBegin(size, GetTypeId(element));
|
||||||
|
|
||||||
|
while (size--)
|
||||||
|
Write(element);
|
||||||
|
|
||||||
|
_output.WriteContainerEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Key, typename T>
|
||||||
|
void Container(const Key& key, const T& value, uint32_t size) const
|
||||||
|
{
|
||||||
|
_output.WriteContainerBegin(size, std::make_pair(GetTypeId(key), GetTypeId(value)));
|
||||||
|
|
||||||
|
while (size--)
|
||||||
|
{
|
||||||
|
Write(key);
|
||||||
|
Write(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
_output.WriteContainerEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// basic type field
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if_c<is_basic_type<T>::value && !is_type_alias<T>::value && true>::type
|
||||||
|
WriteField(uint16_t id, const Metadata& metadata, const T& value) const
|
||||||
|
{
|
||||||
|
_output.WriteField(id, metadata, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// struct or container field
|
||||||
|
template <typename T>
|
||||||
|
typename boost::disable_if_c<is_basic_type<T>::value && !is_type_alias<T>::value>::type
|
||||||
|
WriteField(uint16_t id, const Metadata& metadata, const T& value) const
|
||||||
|
{
|
||||||
|
_output.WriteFieldBegin(GetTypeId(value), id, metadata);
|
||||||
|
Write(value);
|
||||||
|
_output.WriteFieldEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
// basic type value
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if_c<is_basic_type<T>::value && !is_type_alias<T>::value && true>::type
|
||||||
|
Write(const T& value) const
|
||||||
|
{
|
||||||
|
_output.Write(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// type alias
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if<is_type_alias<T> >::type
|
||||||
|
Write(const T& value) const
|
||||||
|
{
|
||||||
|
Write(get_aliased_value(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
// struct value or bonded<T>
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if<is_bond_type<T> >::type
|
||||||
|
Write(const T& value) const
|
||||||
|
{
|
||||||
|
Apply(Serializer<Writer>(_output), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// bonded<T> and untagged writer
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if<uses_marshaled_bonded<typename Writer::Reader, T> >::type
|
||||||
|
Write(const bonded<T>& value) const
|
||||||
|
{
|
||||||
|
detail::MarshalToBlob(value, _output);
|
||||||
|
}
|
||||||
|
|
||||||
|
// bonded<void> and untagged writer
|
||||||
|
template <typename Reader>
|
||||||
|
typename boost::enable_if<uses_marshaled_bonded<typename Writer::Reader, Reader> >::type
|
||||||
|
Write(const bonded<void, Reader>& value) const
|
||||||
|
{
|
||||||
|
value.Serialize(_output);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2-tuple
|
||||||
|
template <typename T1, typename T2>
|
||||||
|
void Write(const std::pair<T1, T2>& value) const
|
||||||
|
{
|
||||||
|
Write(value.first);
|
||||||
|
Write(value.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
// container value
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if<is_container<T> >::type
|
||||||
|
Write(const T& value) const
|
||||||
|
{
|
||||||
|
_output.WriteContainerBegin(container_size(value), get_type_id<typename element_type<T>::type>::value);
|
||||||
|
|
||||||
|
for (const_enumerator<T> items(value); items.more();)
|
||||||
|
{
|
||||||
|
Write(items.next());
|
||||||
|
}
|
||||||
|
|
||||||
|
_output.WriteContainerEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// blob
|
||||||
|
void Write(const blob& value) const
|
||||||
|
{
|
||||||
|
_output.WriteContainerBegin(value.length(), get_type_id<blob::value_type>::value);
|
||||||
|
_output.Write(value);
|
||||||
|
_output.WriteContainerEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// serialized value
|
||||||
|
template <typename Reader, typename T>
|
||||||
|
typename boost::enable_if<is_basic_type<T> >::type
|
||||||
|
Write(const value<T, Reader>& value) const
|
||||||
|
{
|
||||||
|
T data;
|
||||||
|
|
||||||
|
value.Deserialize(data);
|
||||||
|
Write(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Reader, typename T>
|
||||||
|
typename boost::disable_if<is_basic_type<T> >::type
|
||||||
|
Write(const value<T, Reader>& value) const
|
||||||
|
{
|
||||||
|
Apply(Serializer<Writer>(_output), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename WriterT>
|
||||||
|
friend class Merger;
|
||||||
|
|
||||||
|
template <typename T, typename Reader, typename Enable>
|
||||||
|
friend class value;
|
||||||
|
|
||||||
|
template <typename T, typename Schema, typename Transform>
|
||||||
|
friend class detail::_Parser;
|
||||||
|
|
||||||
|
template <typename Transform, typename T>
|
||||||
|
friend bool detail::DoublePassApply(const Transform&, const T&);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Writer& _output;
|
||||||
|
const bool _base;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// SerializeTo
|
||||||
|
template <typename Writer>
|
||||||
|
Serializer<Writer> SerializeTo(Writer& output)
|
||||||
|
{
|
||||||
|
return Serializer<Writer>(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Writer>
|
||||||
|
class Marshaler
|
||||||
|
: public Serializer<Writer>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Marshaler(Writer& output)
|
||||||
|
: Serializer<Writer>(output)
|
||||||
|
{}
|
||||||
|
|
||||||
|
template <typename Pass0>
|
||||||
|
Marshaler<Pass0> Rebind(Pass0& pass0) const
|
||||||
|
{
|
||||||
|
return Marshaler<Pass0>(pass0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Begin(const bond::Metadata& metadata) const
|
||||||
|
{
|
||||||
|
this->_output.WriteVersion();
|
||||||
|
Serializer<Writer>::Begin(metadata);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// MarshalTo
|
||||||
|
template <typename Writer>
|
||||||
|
Marshaler<Writer> MarshalTo(Writer& output)
|
||||||
|
{
|
||||||
|
return Marshaler<Writer>(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class RequiredFieldValiadator
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
void Begin() const
|
||||||
|
{
|
||||||
|
_required = next_required_field<typename schema<T>::type::fields>::value;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Head>
|
||||||
|
typename boost::enable_if<is_same<typename Head::field_modifier,
|
||||||
|
reflection::required_field_modifier> >::type
|
||||||
|
Validate() const
|
||||||
|
{
|
||||||
|
if (_required == Head::id)
|
||||||
|
_required = next_required_field<typename schema<T>::type::fields, Head::id + 1>::value;
|
||||||
|
else
|
||||||
|
MissingFieldException();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Schema>
|
||||||
|
typename boost::enable_if_c<next_required_field<typename Schema::fields>::value
|
||||||
|
!= invalid_field_id>::type
|
||||||
|
Validate() const
|
||||||
|
{
|
||||||
|
if (_required != invalid_field_id)
|
||||||
|
MissingFieldException();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Head>
|
||||||
|
typename boost::disable_if<is_same<typename Head::field_modifier,
|
||||||
|
reflection::required_field_modifier> >::type
|
||||||
|
Validate() const
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Schema>
|
||||||
|
typename boost::disable_if_c<next_required_field<typename Schema::fields>::value
|
||||||
|
!= invalid_field_id>::type
|
||||||
|
Validate() const
|
||||||
|
{}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void MissingFieldException() const;
|
||||||
|
|
||||||
|
mutable uint16_t _required;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void RequiredFieldValiadator<T>::MissingFieldException() const
|
||||||
|
{
|
||||||
|
BOND_THROW(CoreException,
|
||||||
|
"De-serialization failed: required field " << _required <<
|
||||||
|
" is missing from " << schema<T>::type::metadata.qualified_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// To<T> transforms the input field-by-field, matching both field ids and types,
|
||||||
|
// into an instance of a static bond type T.
|
||||||
|
//
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
class To
|
||||||
|
: public DeserializingTransform
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void UnknownEnd() const
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool UnknownField(...) const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
template <typename V, typename X>
|
||||||
|
void AssignToVar(V& var, const X& value) const
|
||||||
|
{
|
||||||
|
value.Deserialize(var);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename V, typename X>
|
||||||
|
void AssignToVar(maybe<V>& var, const X& value) const
|
||||||
|
{
|
||||||
|
value.Deserialize(var.set_value());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename V, typename X>
|
||||||
|
typename boost::enable_if<has_base<V>, bool>::type
|
||||||
|
AssignToBase(V& var, const X& value) const
|
||||||
|
{
|
||||||
|
return Apply(bond::To<typename schema<V>::type::base>(var), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename V, typename X>
|
||||||
|
typename boost::disable_if<has_base<V>, bool>::type
|
||||||
|
AssignToBase(V& /*var*/, const X& /*value*/) const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename X>
|
||||||
|
bool AssignToField(const boost::mpl::l_iter<boost::mpl::l_end>&, uint16_t /*id*/, const X& /*value*/) const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T, typename Validator>
|
||||||
|
class To
|
||||||
|
: public detail::To,
|
||||||
|
protected Validator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
To(T& var)
|
||||||
|
: _var(var)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void Begin(const Metadata& /*metadata*/) const
|
||||||
|
{
|
||||||
|
// Type T must be a Bond struct (i.e. struct generated by Bond codegen
|
||||||
|
// from a .bond file). If the assert fails for a Bond struct, the likely
|
||||||
|
// reason is that you didn't include the generated file *_reflection.h.
|
||||||
|
BOOST_STATIC_ASSERT(has_schema<T>::value);
|
||||||
|
|
||||||
|
// Triggering this assert means you are reusing an object w/o resetting
|
||||||
|
// it to default value first.
|
||||||
|
BOOST_ASSERT(detail::OptionalDefault<T>(_var));
|
||||||
|
|
||||||
|
Validator::Begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
void End() const
|
||||||
|
{
|
||||||
|
Validator::template Validate<typename schema<T>::type>();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename X>
|
||||||
|
bool Base(const X& value) const
|
||||||
|
{
|
||||||
|
return AssignToBase(_var, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Separate Field overloads for bonded<T>, basic types and containers allows us to use
|
||||||
|
// simpler predicates in boost::mpl::copy_if. This doesn't matter for runtime code but
|
||||||
|
// compiles significantly faster.
|
||||||
|
template <typename Reader, typename X>
|
||||||
|
bool Field(uint16_t id, const Metadata& /*metadata*/, const bonded<X, Reader>& value) const
|
||||||
|
{
|
||||||
|
return AssignToField(typename boost::mpl::begin<typename nested_fields<T>::type>::type(), id, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Reader, typename X>
|
||||||
|
bool Field(uint16_t id, const Metadata& /*metadata*/, const value<X, Reader>& value) const
|
||||||
|
{
|
||||||
|
return AssignToField(typename boost::mpl::begin<typename matching_fields<T, X>::type>::type(), id, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Reader>
|
||||||
|
bool Field(uint16_t id, const Metadata& /*metadata*/, const value<void, Reader>& value) const
|
||||||
|
{
|
||||||
|
return AssignToField(typename boost::mpl::begin<typename container_fields<T>::type>::type(), id, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Fast path for the common case when parser is using compile-time schema schema<T>::type
|
||||||
|
// and thus already knows schema type for each field.
|
||||||
|
typedef T FastPathType;
|
||||||
|
|
||||||
|
template <typename FieldT, typename X>
|
||||||
|
bool Field(const FieldT&, const X& value) const
|
||||||
|
{
|
||||||
|
Validator::template Validate<FieldT>();
|
||||||
|
AssignToVar(FieldT::GetVariable(_var), value);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
using detail::To::AssignToBase;
|
||||||
|
using detail::To::AssignToField;
|
||||||
|
|
||||||
|
template <typename Fields, typename X>
|
||||||
|
bool AssignToField(const Fields&, uint16_t id, const X& value) const
|
||||||
|
{
|
||||||
|
typedef typename boost::mpl::deref<Fields>::type Head;
|
||||||
|
|
||||||
|
if (id == Head::id)
|
||||||
|
{
|
||||||
|
Validator::template Validate<Head>();
|
||||||
|
AssignToVar(Head::GetVariable(_var), value);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return AssignToField(typename boost::mpl::next<Fields>::type(), id, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
T& _var;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct Mapping;
|
||||||
|
|
||||||
|
typedef std::vector<uint16_t> Path;
|
||||||
|
typedef std::map<uint16_t, Mapping> Mappings;
|
||||||
|
|
||||||
|
struct Mapping
|
||||||
|
{
|
||||||
|
Path path;
|
||||||
|
Mappings fields;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint16_t mapping_base = invalid_field_id;
|
||||||
|
|
||||||
|
//
|
||||||
|
// MapTo<T> maps the input fields onto an instance of a static bond type T,
|
||||||
|
// using provided mappings from field path in the source to field path in
|
||||||
|
// the type T. Field paths are expressed as a lists of field ids.
|
||||||
|
//
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
class MapTo
|
||||||
|
: public DeserializingTransform
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void Begin(const Metadata& /*metadata*/) const
|
||||||
|
{}
|
||||||
|
|
||||||
|
void End(bool = false) const
|
||||||
|
{}
|
||||||
|
|
||||||
|
void UnknownEnd() const
|
||||||
|
{}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool UnknownField(uint16_t, const T&) const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
struct PathView
|
||||||
|
: boost::noncopyable
|
||||||
|
{
|
||||||
|
PathView(const Path& path)
|
||||||
|
: path(path),
|
||||||
|
current(path.begin())
|
||||||
|
{}
|
||||||
|
|
||||||
|
PathView(const Path& path, Path::const_iterator current)
|
||||||
|
: path(path),
|
||||||
|
current(current)
|
||||||
|
{}
|
||||||
|
|
||||||
|
size_t size() const
|
||||||
|
{
|
||||||
|
return path.end() - current;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Path& path;
|
||||||
|
const Path::const_iterator current;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename V, typename X>
|
||||||
|
bool Assign(V& var, const PathView& ids, const X& value) const
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(ids.size() > 0);
|
||||||
|
|
||||||
|
if (*ids.current == mapping_base)
|
||||||
|
return AssignToBase(base_class<typename schema<V>::type>(), var, ids, value);
|
||||||
|
|
||||||
|
if (ids.size() == 1)
|
||||||
|
return AssignToField(var, *ids.current, value);
|
||||||
|
else
|
||||||
|
return AssignToNested(var, ids, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename V, typename X>
|
||||||
|
bool AssignToNested(V& var, const PathView& ids, const X& value) const
|
||||||
|
{
|
||||||
|
return AssignToNested(typename boost::mpl::begin<typename struct_fields<V>::type>::type(), var, ids, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename BaseT, typename V, typename X>
|
||||||
|
bool AssignToBase(const BaseT*, V& var, const PathView& ids, const X& value) const
|
||||||
|
{
|
||||||
|
return Assign(static_cast<BaseT&>(var), PathView(ids.path, ids.current + 1), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename V, typename X>
|
||||||
|
bool AssignToBase(const no_base*, V& /*var*/, const PathView& /*ids*/, const X& /*value*/) const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Nested, typename V, typename X>
|
||||||
|
bool AssignToNested(const Nested&, V& var, const PathView& ids, const X& value) const
|
||||||
|
{
|
||||||
|
typedef typename boost::mpl::deref<Nested>::type Head;
|
||||||
|
|
||||||
|
if (*ids.current == Head::id)
|
||||||
|
return Assign(Head::GetVariable(var), PathView(ids.path, ids.current + 1), value);
|
||||||
|
else
|
||||||
|
return AssignToNested(typename boost::mpl::next<Nested>::type(), var, ids, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename V, typename X>
|
||||||
|
bool AssignToNested(const boost::mpl::l_iter<boost::mpl::l_end>&, V& /*var*/, const PathView& /*ids*/, const X& /*value*/) const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Separate AssignToField overloads for bonded<T>, basic types and containers allows us
|
||||||
|
// to use simpler predicates in boost::mpl::copy_if. This doesn't matter for runtime code
|
||||||
|
// but compiles significantly faster.
|
||||||
|
template <typename Reader, typename V, typename X>
|
||||||
|
bool AssignToField(V& var, uint16_t id, const bonded<X, Reader>& value) const
|
||||||
|
{
|
||||||
|
return AssignToField(typename boost::mpl::begin<typename nested_fields<V>::type>::type(), var, id, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Reader, typename V, typename X>
|
||||||
|
bool AssignToField(V& var, uint16_t id, const value<X, Reader>& value) const
|
||||||
|
{
|
||||||
|
return AssignToField(typename boost::mpl::begin<typename matching_fields<V, X>::type>::type(), var, id, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Reader, typename V>
|
||||||
|
bool AssignToField(V& var, uint16_t id, const value<void, Reader>& value) const
|
||||||
|
{
|
||||||
|
return AssignToField(typename boost::mpl::begin<typename container_fields<V>::type>::type(), var, id, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Fields, typename V, typename X>
|
||||||
|
bool AssignToField(const Fields&, V& var, uint16_t id, const X& value) const
|
||||||
|
{
|
||||||
|
typedef typename boost::mpl::deref<Fields>::type Head;
|
||||||
|
|
||||||
|
if (id == Head::id)
|
||||||
|
{
|
||||||
|
AssignToVar(Head::GetVariable(var), value);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return AssignToField(typename boost::mpl::next<Fields>::type(), var, id, value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename V, typename X>
|
||||||
|
bool AssignToField(const boost::mpl::l_iter<boost::mpl::l_end>&, V& /*var*/, uint16_t /*id*/, const X& /*value*/) const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename V, typename X>
|
||||||
|
void AssignToVar(V& var, const X& value) const
|
||||||
|
{
|
||||||
|
value.Deserialize(var);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename V, typename X>
|
||||||
|
void AssignToVar(maybe<V>& var, const X& value) const
|
||||||
|
{
|
||||||
|
value.Deserialize(var.set_value());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class MapTo
|
||||||
|
: public detail::MapTo
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BOOST_STATIC_ASSERT(has_schema<T>::value);
|
||||||
|
|
||||||
|
MapTo(T& var, const Mappings& mappings)
|
||||||
|
: _var(var),
|
||||||
|
_mappings(mappings)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename X>
|
||||||
|
bool Base(const X& value) const
|
||||||
|
{
|
||||||
|
Mappings::const_iterator it = _mappings.find(mapping_base);
|
||||||
|
|
||||||
|
if (it != _mappings.end())
|
||||||
|
return Apply(MapTo(_var, it->second.fields), value);
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Reader, typename X>
|
||||||
|
bool Field(uint16_t id, const Metadata& /*metadata*/, const bonded<X, Reader>& value) const
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(id != mapping_base);
|
||||||
|
|
||||||
|
Mappings::const_iterator it = _mappings.find(id);
|
||||||
|
|
||||||
|
if (it != _mappings.end())
|
||||||
|
{
|
||||||
|
if (!it->second.fields.empty())
|
||||||
|
return Apply(MapTo(_var, it->second.fields), value);
|
||||||
|
|
||||||
|
if (!it->second.path.empty())
|
||||||
|
return Assign(_var, it->second.path, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename X>
|
||||||
|
bool Field(uint16_t id, const Metadata& /*metadata*/, const X& value) const
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(id != mapping_base);
|
||||||
|
|
||||||
|
Mappings::const_iterator it = _mappings.find(id);
|
||||||
|
|
||||||
|
if (it != _mappings.end() && !it->second.path.empty())
|
||||||
|
return Assign(_var, it->second.path, value);
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
T& _var;
|
||||||
|
const Mappings& _mappings;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <tuple>
|
||||||
|
#include "bond.h"
|
||||||
|
#include "detail/tuple_fields.h"
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
|
||||||
|
// Specialize bond::schema<T> for std::tuple<T...>
|
||||||
|
// This allows treating instances of std::tuple as Bond structs.
|
||||||
|
template <typename ...T>
|
||||||
|
struct schema<std::tuple<T...>>
|
||||||
|
{
|
||||||
|
struct type
|
||||||
|
{
|
||||||
|
typedef no_base base;
|
||||||
|
typedef typename detail::tuple_fields<std::tuple<T...>, 0, T...>::type fields;
|
||||||
|
|
||||||
|
static const Metadata metadata;
|
||||||
|
|
||||||
|
type()
|
||||||
|
{
|
||||||
|
// Force instantiation of template statics
|
||||||
|
(void)metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Metadata GetMetadata()
|
||||||
|
{
|
||||||
|
Metadata metadata = reflection::MetadataInit(
|
||||||
|
"tuple", "bond.tuple", reflection::Attributes());
|
||||||
|
|
||||||
|
std::string params;
|
||||||
|
|
||||||
|
boost::mpl::for_each<typename detail::param_list<T...>::type>(
|
||||||
|
detail::TypeListBuilder(params));
|
||||||
|
|
||||||
|
metadata.name += "<" + params + ">";
|
||||||
|
metadata.qualified_name += "<" + params + ">";
|
||||||
|
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename ...T>
|
||||||
|
const Metadata schema<std::tuple<T...>>::type::metadata
|
||||||
|
= schema<std::tuple<T...>>::type::GetMetadata();
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Writer, typename ...T>
|
||||||
|
inline void Pack(Writer& writer, T&&...args)
|
||||||
|
{
|
||||||
|
Serialize(std::forward_as_tuple(args...), writer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Reader, typename ...T>
|
||||||
|
inline void Unpack(Reader reader, T&...arg)
|
||||||
|
{
|
||||||
|
auto pack = std::tie(arg...);
|
||||||
|
Deserialize(reader, pack);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namepsace bond
|
|
@ -0,0 +1,84 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "exception.h"
|
||||||
|
#include "schema.h"
|
||||||
|
#include "apply.h"
|
||||||
|
#include "detail/validate.h"
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
|
||||||
|
/// @brief Validate compatibility of schemas
|
||||||
|
/// @param src Source schema
|
||||||
|
/// @param dst Destination schema
|
||||||
|
/// @return 'true' if schemas are wire-format equivalent, 'false' if schemas
|
||||||
|
/// are different but payload in source schema can be deserialized as destination.
|
||||||
|
/// @throw SchemaValidateException if payload in source schema is incompatible
|
||||||
|
/// with destination schema.
|
||||||
|
inline bool Validate(const RuntimeSchema& src,
|
||||||
|
const RuntimeSchema& dst)
|
||||||
|
{
|
||||||
|
// Create instances to drop the shared_ptr member of the original objects
|
||||||
|
// for performance
|
||||||
|
RuntimeSchema r_dst(RuntimeSchema(dst.GetSchema()), dst.GetType());
|
||||||
|
RuntimeSchema r_src(RuntimeSchema(src.GetSchema()), src.GetType());
|
||||||
|
bool identical = true;
|
||||||
|
detail::ValidateStruct(r_src, r_dst, NULL, identical);
|
||||||
|
return identical;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Validate compatibility of schemas
|
||||||
|
/// @param src Serialized source SchemaDef
|
||||||
|
/// @param dst Destination schema
|
||||||
|
/// @return 'true' if schemas are wire-format equivalent, 'false' if schemas
|
||||||
|
/// are different but payload in source schema can be deserialized as destination.
|
||||||
|
/// @throw SchemaValidateException if payload in source schema is incompatible
|
||||||
|
/// with destination schema or the schema of source SchemaDef is unknown.
|
||||||
|
inline bool Validate(const bonded<SchemaDef>& src,
|
||||||
|
const RuntimeSchema& dst)
|
||||||
|
{
|
||||||
|
Apply(detail::SchemaValidator(), src);
|
||||||
|
|
||||||
|
SchemaDef schema;
|
||||||
|
src.Deserialize(schema);
|
||||||
|
return Validate(RuntimeSchema(schema), dst);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Validate compatibility of schemas
|
||||||
|
/// @param src Source schema
|
||||||
|
/// @param dst Serialized destination SchemaDef
|
||||||
|
/// @return 'true' if schemas are wire-format equivalent, 'false' if schemas
|
||||||
|
/// are different but payload in source schema can be deserialized as destination.
|
||||||
|
/// @throw SchemaValidateException if payload in source schema is incompatible
|
||||||
|
/// with destination schema or the schema of destination SchemaDef is unknown.
|
||||||
|
inline bool Validate(const RuntimeSchema& src,
|
||||||
|
const bonded<SchemaDef>& dst)
|
||||||
|
{
|
||||||
|
Apply(detail::SchemaValidator(), dst);
|
||||||
|
|
||||||
|
SchemaDef schema;
|
||||||
|
dst.Deserialize(schema);
|
||||||
|
return Validate(src, RuntimeSchema(schema));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Validate two-way compatibility of schemas
|
||||||
|
/// @param s1 Schema to compare
|
||||||
|
/// @param s2 Schema to compare
|
||||||
|
/// @return 'true' if schemas are wire-format equivalent, 'false' if schemas
|
||||||
|
/// are different but payload in source can be deserialized as destination,
|
||||||
|
/// and vice versa.
|
||||||
|
/// @throw SchemaValidateException if payload in any one schema is incompatible
|
||||||
|
/// with the other schema.
|
||||||
|
template <typename T1, typename T2>
|
||||||
|
inline bool ValidateTwoWay(const T1& s1, const T2& s2)
|
||||||
|
{
|
||||||
|
return Validate(s1, s2) & Validate(s2, s1);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace bond
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,34 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// C4296: '<' : expression is always false
|
||||||
|
#pragma warning(disable: 4296)
|
||||||
|
|
||||||
|
// C4127: conditional expression is constant
|
||||||
|
#pragma warning(disable: 4127)
|
||||||
|
|
||||||
|
// C4996: 'std::copy': Function call with parameters that may be unsafe
|
||||||
|
#pragma warning(disable: 4996)
|
||||||
|
|
||||||
|
// C4503: decorated name length exceeded, name was truncated
|
||||||
|
#pragma warning(disable: 4503)
|
||||||
|
|
||||||
|
// C4702: unreachable code
|
||||||
|
#pragma warning(disable: 4702)
|
||||||
|
|
||||||
|
// C4103: alignment changed after including header, may be due to missing #pragma pack(pop)
|
||||||
|
#pragma warning(disable: 4103)
|
||||||
|
|
||||||
|
// C4482: nonstandard extension used: enum 'enum' used in qualified name
|
||||||
|
#pragma warning(disable: 4482)
|
||||||
|
|
||||||
|
// C4456: declaration of 'symbol' hides previous local declaration
|
||||||
|
#pragma warning(disable: 4456)
|
||||||
|
|
||||||
|
// C4457: declaration of 'symbol' hides function parameter
|
||||||
|
#pragma warning(disable: 4457)
|
||||||
|
|
||||||
|
// C4458: declaration of 'symbol' hides class member
|
||||||
|
#pragma warning(disable: 4458)
|
|
@ -0,0 +1,781 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "encoding.h"
|
||||||
|
#include "detail/simple_array.h"
|
||||||
|
#include <bond/core/bond_version.h>
|
||||||
|
#include <bond/core/traits.h>
|
||||||
|
#include <bond/stream/output_counter.h>
|
||||||
|
#include <boost/call_traits.hpp>
|
||||||
|
#include <boost/noncopyable.hpp>
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
.----------.--------------. .----------.---------.
|
||||||
|
struct (v1) | fields | BT_STOP_BASE |...| fields | BT_STOP |
|
||||||
|
'----------'--------------' '----------'---------'
|
||||||
|
|
||||||
|
.----------.----------.--------------. .----------.---------.
|
||||||
|
struct (v2) | length | fields | BT_STOP_BASE |...| fields | BT_STOP |
|
||||||
|
'----------'----------'--------------' '----------'---------'
|
||||||
|
|
||||||
|
length variable int encoded uint32 length of following fields, up to and
|
||||||
|
including BT_STOP but excluding length itself.
|
||||||
|
|
||||||
|
.----------.----------. .----------.
|
||||||
|
fields | field | field |...| field |
|
||||||
|
'----------'----------' '----------'
|
||||||
|
|
||||||
|
.----------.----------.
|
||||||
|
field | id+type | value |
|
||||||
|
'----------'----------'
|
||||||
|
|
||||||
|
.---.---.---.---.---.---.---.---. i - id bits
|
||||||
|
id+type 0 <= id <= 5 | i | i | i | t | t | t | t | t | t - type bits
|
||||||
|
'---'---'---'---'---'---'---'---' v - value bits
|
||||||
|
2 0 4 0
|
||||||
|
|
||||||
|
.---.---.---.---.---.---.---.---.---. .---.
|
||||||
|
5 < id <= 0xff | 1 | 1 | 0 | t | t | t | t | t | i |...| i |
|
||||||
|
'---'---'---'---'---'---'---'---'---' '---'
|
||||||
|
4 0 7 0
|
||||||
|
|
||||||
|
.---.---.---.---.---.---.---.---.---. .---.---. .---.
|
||||||
|
0xff < id <= 0xffff | 1 | 1 | 1 | t | t | t | t | t | i |...| i | i |...| i |
|
||||||
|
'---'---'---'---'---'---'---'---'---' '---'---' '---'
|
||||||
|
4 0 7 0 15 8
|
||||||
|
|
||||||
|
|
||||||
|
.---.---.---.---.---.---.---.---.
|
||||||
|
value bool | | | | | | | | v |
|
||||||
|
'---'---'---'---'---'---'---'---'
|
||||||
|
0
|
||||||
|
|
||||||
|
.---.---.---.---.---.---.---.---.
|
||||||
|
int8, uint8 | v | v | v | v | v | v | v | v |
|
||||||
|
'---'---'---'---'---'---'---'---'
|
||||||
|
7 0
|
||||||
|
|
||||||
|
.---.---. .---.---.---. .---.
|
||||||
|
uint16, uint32, | 1 | v |...| v | 0 | v |...| v | [...]
|
||||||
|
uint64 '---'---' '---'---'---' '---'
|
||||||
|
6 0 13 7
|
||||||
|
|
||||||
|
variable encoding, high bit of every byte
|
||||||
|
indicates if there is another byte
|
||||||
|
|
||||||
|
|
||||||
|
int16, int32, zig zag encoded to unsigned integer:
|
||||||
|
int64
|
||||||
|
0 -> 0
|
||||||
|
-1 -> 1
|
||||||
|
1 -> 2
|
||||||
|
-2 -> 3
|
||||||
|
...
|
||||||
|
|
||||||
|
and then encoded as unsigned integer
|
||||||
|
|
||||||
|
|
||||||
|
float, double little endian
|
||||||
|
|
||||||
|
|
||||||
|
.-------.------------.
|
||||||
|
string, wstring | count | characters |
|
||||||
|
'-------'------------'
|
||||||
|
|
||||||
|
count variable encoded uint32 count of 1-byte or 2-byte characters
|
||||||
|
|
||||||
|
characters 1-byte or 2-byte characters
|
||||||
|
|
||||||
|
|
||||||
|
.-------.-------.-------.
|
||||||
|
blob, list, set, | type | count | items |
|
||||||
|
vector, nullable '-------'-------'-------'
|
||||||
|
|
||||||
|
.---.---.---.---.---.---.---.---.
|
||||||
|
type (v1) | | | | t | t | t | t | t |
|
||||||
|
'---'---'---'---'---'---'---'---'
|
||||||
|
4 0
|
||||||
|
|
||||||
|
.---.---.---.---.---.---.---.---.
|
||||||
|
type (v2) | c | c | c | t | t | t | t | t |
|
||||||
|
'---'---'---'---'---'---'---'---'
|
||||||
|
2 0 4 0
|
||||||
|
|
||||||
|
if count of items is < 7, 'c' are bit of (count + 1),
|
||||||
|
otherwise 'c' bits are 0.
|
||||||
|
|
||||||
|
count variable encoded uint32 count of items
|
||||||
|
omitted in v2 if 'c' bits within type byte are not 0
|
||||||
|
|
||||||
|
items each item encoded according to its type
|
||||||
|
|
||||||
|
|
||||||
|
.----------.------------.-------.-----.-------.
|
||||||
|
map | key type | value type | count | key | value |
|
||||||
|
'----------'------------'-------'-----'-------'
|
||||||
|
|
||||||
|
.---.---.---.---.---.---.---.---.
|
||||||
|
key type, | | | | t | t | t | t | t |
|
||||||
|
value type '---'---'---'---'---'---'---'---'
|
||||||
|
4 0
|
||||||
|
|
||||||
|
count variable encoded uint32 count of {key,mapped} pairs
|
||||||
|
|
||||||
|
key, mapped each item encoded according to its type
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
#pragma warning(push)
|
||||||
|
// Disable warning when Buffer parameter is a reference
|
||||||
|
// warning C4512: 'bond::CompactBinaryReader<Buffer>' : assignment operator could not be generated
|
||||||
|
#pragma warning(disable:4512)
|
||||||
|
|
||||||
|
|
||||||
|
template <typename BufferT>
|
||||||
|
class CompactBinaryWriter;
|
||||||
|
|
||||||
|
/// @brief Reader for Compact Binary Protocol
|
||||||
|
template <typename BufferT>
|
||||||
|
class CompactBinaryReader
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef BufferT Buffer;
|
||||||
|
typedef DynamicParser<CompactBinaryReader&> Parser;
|
||||||
|
typedef CompactBinaryWriter<Buffer> Writer;
|
||||||
|
|
||||||
|
static const uint16_t magic; // = COMPACT_PROTOCOL
|
||||||
|
static const uint16_t version = v2;
|
||||||
|
|
||||||
|
/// @brief Construct from input buffer/stream containing serialized data.
|
||||||
|
CompactBinaryReader(typename boost::call_traits<Buffer>::param_type input,
|
||||||
|
uint16_t version = default_version<CompactBinaryReader>::value)
|
||||||
|
: _input(input),
|
||||||
|
_version(version)
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(_version <= CompactBinaryReader::version);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// This identical to compiler generated ctor except for throw() declaration.
|
||||||
|
// Copy ctor that is explicitly declared throw() is needed for boost::variant
|
||||||
|
// to use optimized code path.
|
||||||
|
/// @brief Copy constructor
|
||||||
|
CompactBinaryReader(const CompactBinaryReader& that) throw()
|
||||||
|
: _input(that._input),
|
||||||
|
_version(that._version)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Comparison operator
|
||||||
|
bool operator==(const CompactBinaryReader& rhs) const
|
||||||
|
{
|
||||||
|
return _input == rhs._input;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Access to underlaying buffer
|
||||||
|
typename boost::call_traits<Buffer>::const_reference
|
||||||
|
GetBuffer() const
|
||||||
|
{
|
||||||
|
return _input;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool ReadVersion()
|
||||||
|
{
|
||||||
|
uint16_t magic;
|
||||||
|
|
||||||
|
_input.Read(magic);
|
||||||
|
_input.Read(_version);
|
||||||
|
|
||||||
|
return magic == CompactBinaryReader::magic
|
||||||
|
&& _version <= CompactBinaryReader::version;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ReadStructBegin
|
||||||
|
void ReadStructBegin(bool base = false)
|
||||||
|
{
|
||||||
|
if (!base && v2 == _version)
|
||||||
|
{
|
||||||
|
uint32_t length;
|
||||||
|
Read(length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadStructEnd
|
||||||
|
void ReadStructEnd(bool = false)
|
||||||
|
{}
|
||||||
|
|
||||||
|
// ReadFieldBegin
|
||||||
|
void ReadFieldBegin(BondDataType& type, uint16_t& id)
|
||||||
|
{
|
||||||
|
uint8_t raw;
|
||||||
|
|
||||||
|
_input.Read(raw);
|
||||||
|
|
||||||
|
type = static_cast<BondDataType>(raw & 0x1f);
|
||||||
|
id = static_cast<uint16_t>(raw & (0x07 << 5));
|
||||||
|
|
||||||
|
if (id == (0x07 << 5))
|
||||||
|
{
|
||||||
|
_input.Read(id);
|
||||||
|
}
|
||||||
|
else if (id == (0x06 << 5))
|
||||||
|
{
|
||||||
|
_input.Read(reinterpret_cast<uint8_t&>(id));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
id >>= 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadFieldEnd
|
||||||
|
void ReadFieldEnd()
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
// ReadContainerBegin
|
||||||
|
void ReadContainerBegin(uint32_t& size, BondDataType& type)
|
||||||
|
{
|
||||||
|
uint8_t raw;
|
||||||
|
|
||||||
|
_input.Read(raw);
|
||||||
|
type = static_cast<BondDataType>(raw & 0x1f);
|
||||||
|
|
||||||
|
if (v2 == _version && (raw & (0x07 << 5)))
|
||||||
|
size = (raw >> 5) - 1;
|
||||||
|
else
|
||||||
|
Read(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// container of 2-tuple (e.g. map)
|
||||||
|
void ReadContainerBegin(uint32_t& size, std::pair<BondDataType, BondDataType>& type)
|
||||||
|
{
|
||||||
|
uint8_t raw;
|
||||||
|
|
||||||
|
_input.Read(raw);
|
||||||
|
type.first = static_cast<BondDataType>(raw);
|
||||||
|
|
||||||
|
_input.Read(raw);
|
||||||
|
type.second = static_cast<BondDataType>(raw);
|
||||||
|
|
||||||
|
Read(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ReadContainerEnd
|
||||||
|
void ReadContainerEnd()
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
// Read for floating point
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if<is_floating_point<T> >::type
|
||||||
|
Read(T& value)
|
||||||
|
{
|
||||||
|
_input.Read(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read for unsigned integers
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if<is_unsigned<T> >::type
|
||||||
|
Read(T& value)
|
||||||
|
{
|
||||||
|
ReadVariableUnsigned(_input, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read for signed integers
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if<is_signed_int<T> >::type
|
||||||
|
Read(T& value)
|
||||||
|
{
|
||||||
|
typename make_unsigned<T>::type unsigned_value;
|
||||||
|
|
||||||
|
ReadVariableUnsigned(_input, unsigned_value);
|
||||||
|
value = DecodeZigZag(unsigned_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Read for enums
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if<is_enum<T> >::type
|
||||||
|
Read(T& value)
|
||||||
|
{
|
||||||
|
BOOST_STATIC_ASSERT(sizeof(value) == sizeof(int32_t));
|
||||||
|
Read(*reinterpret_cast<int32_t*>(&value));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Read for int8_t
|
||||||
|
void Read(int8_t& value)
|
||||||
|
{
|
||||||
|
_input.Read(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Read for uint8_t
|
||||||
|
void Read(uint8_t& value)
|
||||||
|
{
|
||||||
|
_input.Read(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Read for bool
|
||||||
|
void Read(bool& value)
|
||||||
|
{
|
||||||
|
_input.Read(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Read for strings
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if<is_string_type<T> >::type
|
||||||
|
Read(T& value)
|
||||||
|
{
|
||||||
|
uint32_t length = 0;
|
||||||
|
|
||||||
|
Read(length);
|
||||||
|
detail::ReadStringData(_input, value, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Read for blob
|
||||||
|
void Read(blob& value, uint32_t size)
|
||||||
|
{
|
||||||
|
_input.Read(value, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void Skip()
|
||||||
|
{
|
||||||
|
Skip(get_type_id<T>::value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void Skip(const bonded<T, CompactBinaryReader&>&)
|
||||||
|
{
|
||||||
|
SkipComplex(bond::BT_STRUCT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Skip(BondDataType type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case bond::BT_FLOAT:
|
||||||
|
_input.Skip(sizeof(float));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case bond::BT_DOUBLE:
|
||||||
|
_input.Skip(sizeof(double));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case bond::BT_BOOL:
|
||||||
|
case bond::BT_UINT8:
|
||||||
|
case bond::BT_INT8:
|
||||||
|
_input.Skip(sizeof(uint8_t));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case bond::BT_UINT64:
|
||||||
|
case bond::BT_UINT32:
|
||||||
|
case bond::BT_UINT16:
|
||||||
|
case bond::BT_INT64:
|
||||||
|
case bond::BT_INT32:
|
||||||
|
case bond::BT_INT16:
|
||||||
|
{
|
||||||
|
uint64_t value;
|
||||||
|
Read(value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
SkipComplex(type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void SkipComplex(BondDataType type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case bond::BT_STRING:
|
||||||
|
{
|
||||||
|
uint32_t length;
|
||||||
|
|
||||||
|
Read(length);
|
||||||
|
_input.Skip(length);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case bond::BT_WSTRING:
|
||||||
|
{
|
||||||
|
uint32_t length;
|
||||||
|
|
||||||
|
Read(length);
|
||||||
|
_input.Skip(length * sizeof(uint16_t));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case bond::BT_SET:
|
||||||
|
case bond::BT_LIST:
|
||||||
|
{
|
||||||
|
BondDataType type;
|
||||||
|
uint32_t size;
|
||||||
|
|
||||||
|
ReadContainerBegin(size, type);
|
||||||
|
for(uint32_t i = 0; i < size; ++i)
|
||||||
|
{
|
||||||
|
Skip(type);
|
||||||
|
}
|
||||||
|
ReadContainerEnd();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case bond::BT_MAP:
|
||||||
|
{
|
||||||
|
std::pair<BondDataType, BondDataType> type;
|
||||||
|
uint32_t size;
|
||||||
|
|
||||||
|
ReadContainerBegin(size, type);
|
||||||
|
for(uint32_t i = 0; i < size; ++i)
|
||||||
|
{
|
||||||
|
Skip(type.first);
|
||||||
|
Skip(type.second);
|
||||||
|
}
|
||||||
|
ReadContainerEnd();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case bond::BT_STRUCT:
|
||||||
|
{
|
||||||
|
if (v2 == _version)
|
||||||
|
{
|
||||||
|
uint32_t length;
|
||||||
|
Read(length);
|
||||||
|
_input.Skip(length);
|
||||||
|
}
|
||||||
|
else for(;;)
|
||||||
|
{
|
||||||
|
ReadStructBegin();
|
||||||
|
|
||||||
|
uint16_t id;
|
||||||
|
BondDataType type;
|
||||||
|
|
||||||
|
for (ReadFieldBegin(type, id);
|
||||||
|
type != bond::BT_STOP && type != bond::BT_STOP_BASE;
|
||||||
|
ReadFieldEnd(), ReadFieldBegin(type, id))
|
||||||
|
{
|
||||||
|
Skip(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadStructEnd();
|
||||||
|
|
||||||
|
if (type == bond::BT_STOP)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Buffer _input;
|
||||||
|
uint16_t _version;
|
||||||
|
|
||||||
|
template <typename Input, typename Output>
|
||||||
|
friend
|
||||||
|
bool is_protocol_version_same(const CompactBinaryReader<Input>&,
|
||||||
|
const CompactBinaryWriter<Output>&);
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Buffer>
|
||||||
|
const uint16_t CompactBinaryReader<Buffer>::magic = COMPACT_PROTOCOL;
|
||||||
|
|
||||||
|
#pragma warning(pop)
|
||||||
|
|
||||||
|
|
||||||
|
class OutputCounter;
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Writer for Compact Binary Protocol
|
||||||
|
template <typename BufferT>
|
||||||
|
class CompactBinaryWriter
|
||||||
|
: boost::noncopyable
|
||||||
|
{
|
||||||
|
struct Pass1
|
||||||
|
{
|
||||||
|
Pass1(CompactBinaryWriter* writer)
|
||||||
|
: writer(writer)
|
||||||
|
{}
|
||||||
|
|
||||||
|
~Pass1()
|
||||||
|
{
|
||||||
|
writer->_it = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
CompactBinaryWriter* writer;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
typedef BufferT Buffer;
|
||||||
|
typedef CompactBinaryReader<Buffer> Reader;
|
||||||
|
typedef CompactBinaryWriter<OutputCounter> Pass0;
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Construct from output buffer/stream.
|
||||||
|
CompactBinaryWriter(Buffer& output,
|
||||||
|
uint16_t version = default_version<Reader>::value)
|
||||||
|
: _output(output),
|
||||||
|
_it(NULL),
|
||||||
|
_version(version)
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(_version <= Reader::version);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
CompactBinaryWriter(OutputCounter& output,
|
||||||
|
const CompactBinaryWriter<T>& pass1)
|
||||||
|
: _output(output),
|
||||||
|
_version(pass1._version)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
bool NeedPass0()
|
||||||
|
{
|
||||||
|
return v2 == _version && !_it;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Pass1 WithPass0(Pass0& pass0)
|
||||||
|
{
|
||||||
|
_it = pass0._lengths.begin();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteVersion()
|
||||||
|
{
|
||||||
|
_output.Write(Reader::magic);
|
||||||
|
_output.Write(_version);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void WriteStructBegin(const Metadata& /*metadata*/, bool base)
|
||||||
|
{
|
||||||
|
if (!base)
|
||||||
|
{
|
||||||
|
LengthBegin(_output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteStructEnd(bool base = false)
|
||||||
|
{
|
||||||
|
if (base)
|
||||||
|
{
|
||||||
|
_output.Write(static_cast<uint8_t>(BT_STOP_BASE));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_output.Write(static_cast<uint8_t>(BT_STOP));
|
||||||
|
LengthEnd(_output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteField for basic types
|
||||||
|
template <typename T>
|
||||||
|
void WriteField(uint16_t id, const bond::Metadata& /*metadata*/, const T& value)
|
||||||
|
{
|
||||||
|
WriteFieldBegin(get_type_id<T>::value, id);
|
||||||
|
Write(value);
|
||||||
|
WriteFieldEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteFieldBegin
|
||||||
|
void WriteFieldBegin(BondDataType type, uint16_t id, const bond::Metadata& /*metadata*/)
|
||||||
|
{
|
||||||
|
WriteFieldBegin(type, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteFieldBegin(BondDataType type, uint16_t id)
|
||||||
|
{
|
||||||
|
BOOST_ASSERT((type & 0x1f) == type);
|
||||||
|
|
||||||
|
if (id <= 5)
|
||||||
|
{
|
||||||
|
_output.Write(static_cast<uint8_t>(type | ((id) << 5)));
|
||||||
|
}
|
||||||
|
else if (id <= 0xff)
|
||||||
|
{
|
||||||
|
_output.Write(static_cast<uint8_t>(type | (0x06 << 5)));
|
||||||
|
_output.Write(static_cast<uint8_t>(id));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_output.Write(static_cast<uint8_t>(type | (0x07 << 5)));
|
||||||
|
_output.Write(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteFieldEnd
|
||||||
|
void WriteFieldEnd()
|
||||||
|
{}
|
||||||
|
|
||||||
|
// WriteContainerBegin
|
||||||
|
void WriteContainerBegin(uint32_t size, BondDataType type)
|
||||||
|
{
|
||||||
|
BOOST_ASSERT((type & 0x1f) == type);
|
||||||
|
|
||||||
|
if (v2 == _version && size < 7)
|
||||||
|
{
|
||||||
|
Write(static_cast<uint8_t>(type | ((size + 1) << 5)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Write(static_cast<uint8_t>(type));
|
||||||
|
Write(size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// container of 2-tuples (e.g. map)
|
||||||
|
void WriteContainerBegin(uint32_t size, std::pair<BondDataType, BondDataType> type)
|
||||||
|
{
|
||||||
|
Write(static_cast<uint8_t>(type.first));
|
||||||
|
Write(static_cast<uint8_t>(type.second));
|
||||||
|
WriteVariableUnsigned(_output, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteContainerEnd
|
||||||
|
void WriteContainerEnd()
|
||||||
|
{}
|
||||||
|
|
||||||
|
// Write for floating point
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if<is_floating_point<T> >::type
|
||||||
|
Write(const T& value)
|
||||||
|
{
|
||||||
|
_output.Write(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write for unsigned integers
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if<is_unsigned<T> >::type
|
||||||
|
Write(const T& value)
|
||||||
|
{
|
||||||
|
WriteVariableUnsigned(_output, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write for signed integers
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if<is_signed_int<T> >::type
|
||||||
|
Write(const T& value)
|
||||||
|
{
|
||||||
|
WriteVariableUnsigned(_output, EncodeZigZag(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write for enums
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if<is_enum<T> >::type
|
||||||
|
Write(const T& value)
|
||||||
|
{
|
||||||
|
BOOST_STATIC_ASSERT(sizeof(value) == sizeof(int32_t));
|
||||||
|
Write(static_cast<int32_t>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write for int8_t
|
||||||
|
void Write(const int8_t& value)
|
||||||
|
{
|
||||||
|
_output.Write(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write for uint8_t
|
||||||
|
void Write(const uint8_t& value)
|
||||||
|
{
|
||||||
|
_output.Write(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write for bool
|
||||||
|
void Write(const bool& value)
|
||||||
|
{
|
||||||
|
_output.Write(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write for strings
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if<is_string_type<T> >::type
|
||||||
|
Write(const T& value)
|
||||||
|
{
|
||||||
|
uint32_t length = string_length(value);
|
||||||
|
|
||||||
|
Write(length);
|
||||||
|
detail::WriteStringData(_output, value, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write for blob
|
||||||
|
void Write(const blob& value)
|
||||||
|
{
|
||||||
|
_output.Write(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
template <typename Buffer>
|
||||||
|
friend class CompactBinaryWriter;
|
||||||
|
|
||||||
|
void LengthBegin(OutputCounter& counter)
|
||||||
|
{
|
||||||
|
_stack.push(_lengths.size());
|
||||||
|
_lengths.push(counter.GetCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
void LengthEnd(OutputCounter& counter)
|
||||||
|
{
|
||||||
|
uint32_t& length = _lengths[_stack.pop()];
|
||||||
|
|
||||||
|
length = counter.GetCount() - length;
|
||||||
|
counter.WriteVariableUnsigned(length);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void LengthBegin(T&)
|
||||||
|
{
|
||||||
|
if (v2 == _version)
|
||||||
|
{
|
||||||
|
Write(*_it++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void LengthEnd(T&)
|
||||||
|
{}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Buffer& _output;
|
||||||
|
const uint32_t* _it;
|
||||||
|
uint16_t _version;
|
||||||
|
detail::SimpleArray<uint32_t> _stack;
|
||||||
|
detail::SimpleArray<uint32_t> _lengths;
|
||||||
|
|
||||||
|
template <typename Input, typename Output>
|
||||||
|
friend
|
||||||
|
bool is_protocol_version_same(const CompactBinaryReader<Input>&,
|
||||||
|
const CompactBinaryWriter<Output>&);
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Input> struct
|
||||||
|
protocol_has_multiple_versions<bond::CompactBinaryReader<Input> >
|
||||||
|
: true_type {};
|
||||||
|
|
||||||
|
template <typename Input, typename Output>
|
||||||
|
bool is_protocol_version_same(const bond::CompactBinaryReader<Input>& reader,
|
||||||
|
const bond::CompactBinaryWriter<Output>& writer)
|
||||||
|
{
|
||||||
|
return reader._version == writer._version;
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // namespace bond
|
|
@ -0,0 +1,311 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define RAPIDJSON_NO_INT64DEFINE
|
||||||
|
#define RAPIDJSON_ASSERT BOOST_ASSERT
|
||||||
|
#define RAPIDJSON_PARSE_ERROR(err, offset) bond::RapidJsonException(rapidjson::GetParseError_En(err), offset)
|
||||||
|
|
||||||
|
// disable warnings in rapidjson
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable:4100 4201 4127 4701 4512)
|
||||||
|
|
||||||
|
#include <bond/core/bond_const_enum.h>
|
||||||
|
#include <bond/core/exception.h>
|
||||||
|
#include <boost/call_traits.hpp>
|
||||||
|
#include <boost/noncopyable.hpp>
|
||||||
|
#include <boost/locale.hpp>
|
||||||
|
#include "rapidjson/rapidjson.h"
|
||||||
|
#include "rapidjson/error/en.h"
|
||||||
|
#include "rapidjson/document.h"
|
||||||
|
#include "rapidjson/writer.h"
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
// Adapter from Bond input stream to rapidjson read-only stream
|
||||||
|
template <typename Buffer>
|
||||||
|
class RapidJsonInputStream
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef char Ch;
|
||||||
|
|
||||||
|
RapidJsonInputStream(typename boost::call_traits<Buffer>::reference input)
|
||||||
|
: input(&input),
|
||||||
|
count(0)
|
||||||
|
{
|
||||||
|
input.Read(current);
|
||||||
|
}
|
||||||
|
|
||||||
|
RapidJsonInputStream(const RapidJsonInputStream& that, typename boost::call_traits<Buffer>::reference input)
|
||||||
|
: input(&input),
|
||||||
|
current(that.current),
|
||||||
|
count(that.count)
|
||||||
|
{}
|
||||||
|
|
||||||
|
char Peek()
|
||||||
|
{
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Tell() const
|
||||||
|
{
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
char Take()
|
||||||
|
{
|
||||||
|
char c = current;
|
||||||
|
|
||||||
|
if (!input->IsEof())
|
||||||
|
{
|
||||||
|
input->Read(current);
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
current = '\x0';
|
||||||
|
}
|
||||||
|
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
// not implemented for read only stream
|
||||||
|
char* PutBegin() { BOOST_ASSERT(false); return 0; }
|
||||||
|
void Put(char c) { BOOST_ASSERT(false); }
|
||||||
|
size_t PutEnd(char* begin) { BOOST_ASSERT(false); return 0; }
|
||||||
|
|
||||||
|
RapidJsonInputStream& operator=(const RapidJsonInputStream& that)
|
||||||
|
{
|
||||||
|
// rapidjson reader makes a local copy of stream within some functions
|
||||||
|
// and assigns it back to its main stream variable before function exit.
|
||||||
|
BOOST_ASSERT(input == that.input);
|
||||||
|
current = that.current;
|
||||||
|
count = that.count;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Buffer* input;
|
||||||
|
uint8_t current;
|
||||||
|
size_t count;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Adapter from Bond output stream to rapidjson write-only stream
|
||||||
|
template <typename Buffer>
|
||||||
|
class RapidJsonOutputStream
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RapidJsonOutputStream(typename boost::call_traits<Buffer>::reference output)
|
||||||
|
: output(output)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// not implemented for write-only stream
|
||||||
|
char Peek() { BOOST_ASSERT(false); return 0; }
|
||||||
|
size_t Tell() const { BOOST_ASSERT(false); return 0; }
|
||||||
|
char Take() { BOOST_ASSERT(false); return 0; }
|
||||||
|
|
||||||
|
char* PutBegin()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Put(char c)
|
||||||
|
{
|
||||||
|
output.Write(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t PutEnd(char* begin)
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(begin == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Buffer& output;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Specialization to allow using string as input buffer for simple JSON reader
|
||||||
|
template <>
|
||||||
|
struct RapidJsonInputStream<const rapidjson::UTF8<>::Ch*> : rapidjson::StringStream
|
||||||
|
{
|
||||||
|
RapidJsonInputStream(const char* buffer)
|
||||||
|
: rapidjson::StringStream(buffer)
|
||||||
|
{}
|
||||||
|
|
||||||
|
RapidJsonInputStream(const RapidJsonInputStream& that, const char*)
|
||||||
|
: rapidjson::StringStream(that)
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class JsonTypeMatching : boost::noncopyable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
JsonTypeMatching(BondDataType type, BondDataType schema, bool is_enum)
|
||||||
|
: matchesObject(type == BT_STRUCT && type == schema),
|
||||||
|
matchesArray((type == BT_MAP || type == BT_LIST || type == BT_SET) && type == schema),
|
||||||
|
matchesNull(type == BT_LIST && type == schema),
|
||||||
|
matchesInt(type >= BT_INT8 && type <= BT_INT64),
|
||||||
|
matchesInt64(type == BT_INT64),
|
||||||
|
matchesUint(type >= BT_UINT8 && type <= BT_UINT64),
|
||||||
|
matchesUint64(type == BT_UINT64),
|
||||||
|
matchesNumber(type >= BT_FLOAT && type <= BT_DOUBLE),
|
||||||
|
matchesString(type == BT_STRING || type == BT_WSTRING || is_enum),
|
||||||
|
matchesBool(type == BT_BOOL)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
bool TypeMatch(const rapidjson::Value& value) const
|
||||||
|
{
|
||||||
|
return ComplexTypeMatch(value) || BasicTypeMatch(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ComplexTypeMatch(const rapidjson::Value& value) const
|
||||||
|
{
|
||||||
|
return ((value.IsObject() && matchesObject)
|
||||||
|
|| (value.IsArray() && matchesArray)
|
||||||
|
|| (value.IsNull() && matchesNull));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BasicTypeMatch(const rapidjson::Value& value) const
|
||||||
|
{
|
||||||
|
return ((value.IsString() && matchesString)
|
||||||
|
|| (value.IsUint() && matchesUint)
|
||||||
|
|| (value.IsInt() && matchesInt)
|
||||||
|
|| (value.IsUint64() && matchesUint64)
|
||||||
|
|| (value.IsInt64() && matchesInt64)
|
||||||
|
|| (value.IsNumber() && matchesNumber)
|
||||||
|
|| (value.IsBool() && matchesBool));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const bool matchesObject;
|
||||||
|
const bool matchesArray;
|
||||||
|
const bool matchesNull;
|
||||||
|
const bool matchesInt;
|
||||||
|
const bool matchesInt64;
|
||||||
|
const bool matchesUint;
|
||||||
|
const bool matchesUint64;
|
||||||
|
const bool matchesNumber;
|
||||||
|
const bool matchesString;
|
||||||
|
const bool matchesBool;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// bool
|
||||||
|
inline void Read(const rapidjson::Value& value, bool& var)
|
||||||
|
{
|
||||||
|
var = value.GetBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
// enum
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if<is_enum<T> >::type
|
||||||
|
Read(const rapidjson::Value& value, T& var)
|
||||||
|
{
|
||||||
|
if (value.IsString())
|
||||||
|
ToEnum(var, value.GetString());
|
||||||
|
else
|
||||||
|
var = static_cast<T>(value.GetInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
// floating point
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if<is_floating_point<T> >::type
|
||||||
|
Read(const rapidjson::Value& value, T& var)
|
||||||
|
{
|
||||||
|
var = static_cast<T>(value.GetDouble());
|
||||||
|
}
|
||||||
|
|
||||||
|
// signed integer
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if<is_signed_int<T> >::type
|
||||||
|
Read(const rapidjson::Value& value, T& var)
|
||||||
|
{
|
||||||
|
var = static_cast<T>(value.GetInt64());
|
||||||
|
}
|
||||||
|
|
||||||
|
// unsigned integer
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if<is_unsigned<T> >::type
|
||||||
|
Read(const rapidjson::Value& value, T& var)
|
||||||
|
{
|
||||||
|
var = static_cast<T>(value.GetUint64());
|
||||||
|
}
|
||||||
|
|
||||||
|
// strings
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if<is_string<T> >::type
|
||||||
|
Read(const rapidjson::Value& value, T& var)
|
||||||
|
{
|
||||||
|
uint32_t length = value.GetStringLength();
|
||||||
|
|
||||||
|
resize_string(var, length);
|
||||||
|
memcpy(string_data(var), value.GetString(), length);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// wstring
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if<is_wstring<T> >::type
|
||||||
|
Read(const rapidjson::Value& value, T& var)
|
||||||
|
{
|
||||||
|
std::basic_string<uint16_t> str =
|
||||||
|
boost::locale::conv::utf_to_utf<uint16_t>(
|
||||||
|
value.GetString(), value.GetString() + value.GetStringLength(), boost::locale::conv::stop);
|
||||||
|
const uint32_t length = static_cast<uint32_t>(str.size());
|
||||||
|
|
||||||
|
resize_string(var, length);
|
||||||
|
std::copy(str.begin(), str.end(), string_data(var));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// type alias
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if<is_type_alias<T> >::type
|
||||||
|
Read(const rapidjson::Value& value, T& var)
|
||||||
|
{
|
||||||
|
typename aliased_type<T>::type x;
|
||||||
|
Read(value, x);
|
||||||
|
set_aliased_value(var, x);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Reader>
|
||||||
|
value<void, Reader&>
|
||||||
|
MakeValue(Reader& reader, const value<void, Reader&>& element)
|
||||||
|
{
|
||||||
|
return value<void, Reader&>(reader, element.GetRuntimeSchema());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Reader, typename T>
|
||||||
|
value<T, Reader&>
|
||||||
|
MakeValue(Reader& reader, const value<T, Reader&>&)
|
||||||
|
{
|
||||||
|
return value<T, Reader&>(reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const std::string& FieldName(const Metadata& metadata)
|
||||||
|
{
|
||||||
|
std::map<std::string, std::string>::const_iterator it
|
||||||
|
= metadata.attributes.find("JsonName");
|
||||||
|
|
||||||
|
if (it != metadata.attributes.end())
|
||||||
|
return it->second;
|
||||||
|
|
||||||
|
return metadata.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
} // namespace bond
|
||||||
|
|
||||||
|
#pragma warning(pop)
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename T, uint32_t N = 64>
|
||||||
|
class SimpleArray
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SimpleArray()
|
||||||
|
: _size(0),
|
||||||
|
_capacity(N),
|
||||||
|
_data(_insitu)
|
||||||
|
{}
|
||||||
|
|
||||||
|
~SimpleArray()
|
||||||
|
{
|
||||||
|
memfree();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t size() const
|
||||||
|
{
|
||||||
|
return _size;
|
||||||
|
}
|
||||||
|
|
||||||
|
const T* begin() const
|
||||||
|
{
|
||||||
|
return _data;
|
||||||
|
}
|
||||||
|
|
||||||
|
T pop()
|
||||||
|
{
|
||||||
|
return _data[--_size];
|
||||||
|
}
|
||||||
|
|
||||||
|
T& operator[](uint32_t i)
|
||||||
|
{
|
||||||
|
return _data[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
void push(T x)
|
||||||
|
{
|
||||||
|
if (_size < _capacity)
|
||||||
|
_data[_size++] = x;
|
||||||
|
else
|
||||||
|
grow(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void grow(T x)
|
||||||
|
{
|
||||||
|
T* new_data = new T[_capacity <<= 1];
|
||||||
|
memcpy(new_data, _data, _size * sizeof(T));
|
||||||
|
memfree();
|
||||||
|
(_data = new_data)[_size++] = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
void memfree()
|
||||||
|
{
|
||||||
|
if (_data != _insitu)
|
||||||
|
delete [] _data;
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_STATIC_ASSERT(is_pod<T>::value);
|
||||||
|
|
||||||
|
uint32_t _size;
|
||||||
|
uint32_t _capacity;
|
||||||
|
T _insitu[N];
|
||||||
|
T* _data;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
} // namespace bond
|
|
@ -0,0 +1,231 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <exception>
|
||||||
|
#include <bond/core/containers.h>
|
||||||
|
#include <bond/core/blob.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Buffer, typename T, typename Enable = void> struct
|
||||||
|
implements_varint_write
|
||||||
|
: false_type {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Buffer, typename T> struct
|
||||||
|
implements_varint_write<Buffer, T, typename boost::enable_if<bond::check_method<void (Buffer::*)(T), &Buffer::WriteVariableUnsigned> >::type>
|
||||||
|
: true_type {};
|
||||||
|
|
||||||
|
|
||||||
|
template<typename Buffer, typename T>
|
||||||
|
inline
|
||||||
|
typename boost::enable_if<implements_varint_write<Buffer, T> >::type
|
||||||
|
WriteVariableUnsigned(Buffer& output, T value)
|
||||||
|
{
|
||||||
|
BOOST_STATIC_ASSERT(is_unsigned<T>::value);
|
||||||
|
|
||||||
|
// Use Buffer's implementation of WriteVariableUnsigned
|
||||||
|
output.WriteVariableUnsigned(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<typename Buffer, typename T>
|
||||||
|
inline
|
||||||
|
typename boost::disable_if<implements_varint_write<Buffer, T> >::type
|
||||||
|
WriteVariableUnsigned(Buffer& output, T value)
|
||||||
|
{
|
||||||
|
BOOST_STATIC_ASSERT(is_unsigned<T>::value);
|
||||||
|
|
||||||
|
// Use generic WriteVariableUnsigned
|
||||||
|
GenericWriteVariableUnsigned(output, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<typename Buffer, typename T>
|
||||||
|
BOND_NO_INLINE
|
||||||
|
void GenericWriteVariableUnsigned(Buffer& output, T value)
|
||||||
|
{
|
||||||
|
T x = value;
|
||||||
|
|
||||||
|
if (value >>= 7)
|
||||||
|
{
|
||||||
|
output.Write(static_cast<uint8_t>(x | 0x80));
|
||||||
|
WriteVariableUnsigned(output, value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
output.Write(static_cast<uint8_t>(x));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Buffer, typename T, typename Enable = void> struct
|
||||||
|
implements_varint_read
|
||||||
|
: false_type {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Buffer, typename T> struct
|
||||||
|
implements_varint_read<Buffer, T, typename boost::enable_if<bond::check_method<void (Buffer::*)(T&), &Buffer::ReadVariableUnsigned> >::type>
|
||||||
|
: true_type {};
|
||||||
|
|
||||||
|
|
||||||
|
template<typename Buffer, typename T>
|
||||||
|
inline
|
||||||
|
typename boost::enable_if<implements_varint_read<Buffer, T> >::type
|
||||||
|
ReadVariableUnsigned(Buffer& input, T& value)
|
||||||
|
{
|
||||||
|
BOOST_STATIC_ASSERT(is_unsigned<T>::value);
|
||||||
|
|
||||||
|
// Use Buffer's implementation of ReadVariableUnsigned
|
||||||
|
input.ReadVariableUnsigned(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<typename Buffer, typename T>
|
||||||
|
BOND_NO_INLINE
|
||||||
|
void GenericReadVariableUnsigned(Buffer& input, T& value)
|
||||||
|
{
|
||||||
|
value = 0;
|
||||||
|
uint8_t byte;
|
||||||
|
uint32_t shift = 0;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
input.Read(byte);
|
||||||
|
|
||||||
|
T part = byte & 0x7f;
|
||||||
|
value += part << shift;
|
||||||
|
shift += 7;
|
||||||
|
}
|
||||||
|
while(byte >= 0x80);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<typename Buffer, typename T>
|
||||||
|
inline
|
||||||
|
typename boost::disable_if<implements_varint_read<Buffer, T> >::type
|
||||||
|
ReadVariableUnsigned(Buffer& input, T& value)
|
||||||
|
{
|
||||||
|
BOOST_STATIC_ASSERT(is_unsigned<T>::value);
|
||||||
|
|
||||||
|
// Use generic ReadVariableUnsigned
|
||||||
|
GenericReadVariableUnsigned(input, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// If protocol's Write(const bond::blob&) method doesn't write raw blob to
|
||||||
|
// output, this function needs to be overloaded appropriately.
|
||||||
|
template <typename Writer>
|
||||||
|
inline void WriteRawBlob(Writer& writer, const blob& data)
|
||||||
|
{
|
||||||
|
writer.Write(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ZigZag encoding
|
||||||
|
template<typename T>
|
||||||
|
inline
|
||||||
|
typename make_unsigned<T>::type EncodeZigZag(T value)
|
||||||
|
{
|
||||||
|
return (value << 1) ^ (value >> (sizeof(T) * 8 - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable: 4146)
|
||||||
|
|
||||||
|
// ZigZag decoding
|
||||||
|
template<typename T>
|
||||||
|
inline
|
||||||
|
typename make_signed<T>::type DecodeZigZag(T value)
|
||||||
|
{
|
||||||
|
return (value >> 1) ^ (-(value & 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma warning(pop)
|
||||||
|
|
||||||
|
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
// HexDigit
|
||||||
|
inline char HexDigit(int n)
|
||||||
|
{
|
||||||
|
char d = n & 0xf;
|
||||||
|
return d < 10 ? ('0' + d) : ('a' + d - 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int HexDigit(char c)
|
||||||
|
{
|
||||||
|
if (c >= 'a' && c <= 'f')
|
||||||
|
return c - 'a' + 10;
|
||||||
|
else if (c >= 'A' && c <= 'F')
|
||||||
|
return c - 'A' + 10;
|
||||||
|
else
|
||||||
|
return c - '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename Enable = void> struct
|
||||||
|
string_char_int_type;
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
string_char_int_type<T, typename boost::enable_if<is_string<T> >::type>
|
||||||
|
{
|
||||||
|
typedef uint8_t type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> struct
|
||||||
|
string_char_int_type<T, typename boost::enable_if<is_wstring<T> >::type>
|
||||||
|
{
|
||||||
|
typedef uint16_t type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Buffer, typename T>
|
||||||
|
typename boost::enable_if_c<(sizeof(typename element_type<T>::type) == sizeof(typename string_char_int_type<T>::type))>::type
|
||||||
|
inline ReadStringData(Buffer& input, T& value, uint32_t length)
|
||||||
|
{
|
||||||
|
resize_string(value, length);
|
||||||
|
input.Read(string_data(value), length * sizeof(typename element_type<T>::type));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Buffer, typename T>
|
||||||
|
typename boost::enable_if_c<(sizeof(typename element_type<T>::type) > sizeof(typename string_char_int_type<T>::type))>::type
|
||||||
|
inline ReadStringData(Buffer& input, T& value, uint32_t length)
|
||||||
|
{
|
||||||
|
resize_string(value, length);
|
||||||
|
typename element_type<T>::type* data = string_data(value);
|
||||||
|
typename string_char_int_type<T>::type ch;
|
||||||
|
for (int i = 0; i < length; ++i)
|
||||||
|
{
|
||||||
|
input.Read(ch);
|
||||||
|
data[i] = static_cast<typename element_type<T>::type>(ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Buffer, typename T>
|
||||||
|
typename boost::enable_if_c<(sizeof(typename element_type<T>::type) == sizeof(typename string_char_int_type<T>::type))>::type
|
||||||
|
inline WriteStringData(Buffer& output, const T& value, uint32_t length)
|
||||||
|
{
|
||||||
|
output.Write(string_data(value), length * sizeof(typename element_type<T>::type));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Buffer, typename T>
|
||||||
|
typename boost::enable_if_c<(sizeof(typename element_type<T>::type) > sizeof(typename string_char_int_type<T>::type))>::type
|
||||||
|
inline WriteStringData(Buffer& output, const T& value, uint32_t length)
|
||||||
|
{
|
||||||
|
const typename element_type<T>::type* data = string_data(value);
|
||||||
|
typename string_char_int_type<T>::type ch;
|
||||||
|
for (int i = 0; i < length; ++i)
|
||||||
|
{
|
||||||
|
ch = static_cast<typename string_char_int_type<T>::type>(data[i]);
|
||||||
|
output.Write(ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
} // namespace bond
|
|
@ -0,0 +1,486 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "encoding.h"
|
||||||
|
#include <bond/core/bond_version.h>
|
||||||
|
#include <boost/call_traits.hpp>
|
||||||
|
#include <boost/noncopyable.hpp>
|
||||||
|
|
||||||
|
/*
|
||||||
|
.----------.--------------. .----------.---------.
|
||||||
|
struct hierarchy | struct | BT_STOP_BASE |...| struct | BT_STOP |
|
||||||
|
'----------'--------------' '----------'---------'
|
||||||
|
|
||||||
|
.----------.----------. .----------.
|
||||||
|
struct | field | field |...| field |
|
||||||
|
'----------'----------' '----------'
|
||||||
|
|
||||||
|
.------.----.----------.
|
||||||
|
field | type | id | value |
|
||||||
|
'------'----'----------'
|
||||||
|
|
||||||
|
.---.---.---.---.---.---.---.---. i - id bits
|
||||||
|
type | 0 | 0 | 0 | t | t | t | t | t | t - type bits
|
||||||
|
'---'---'---'---'---'---'---'---' v - value bits
|
||||||
|
4 0
|
||||||
|
|
||||||
|
id .---. .---.---. .---.
|
||||||
|
| i |...| i | i |...| i |
|
||||||
|
'---' '---'---' '---'
|
||||||
|
7 0 15 8
|
||||||
|
|
||||||
|
.---.---.---.---.---.---.---.---.
|
||||||
|
value bool | | | | | | | | v |
|
||||||
|
'---'---'---'---'---'---'---'---'
|
||||||
|
0
|
||||||
|
|
||||||
|
integer, little endian
|
||||||
|
float, double
|
||||||
|
|
||||||
|
.-------.------------.
|
||||||
|
string, wstring | count | characters |
|
||||||
|
'-------'------------'
|
||||||
|
|
||||||
|
count variable uint32 count of 1-byte or 2-byte characters
|
||||||
|
|
||||||
|
characters 1-byte or 2-byte characters
|
||||||
|
|
||||||
|
|
||||||
|
.-------.-------.-------.
|
||||||
|
blob, list, set, | type | count | items |
|
||||||
|
vector, nullable '-------'-------'-------'
|
||||||
|
|
||||||
|
.---.---.---.---.---.---.---.---.
|
||||||
|
type | | | | t | t | t | t | t |
|
||||||
|
'---'---'---'---'---'---'---'---'
|
||||||
|
4 0
|
||||||
|
|
||||||
|
count variable uint32 count of items
|
||||||
|
|
||||||
|
items each item encoded according to its type
|
||||||
|
|
||||||
|
|
||||||
|
.----------.------------.-------.-----.-------.
|
||||||
|
map | key type | value type | count | key | value |
|
||||||
|
'----------'------------'-------'-----'-------'
|
||||||
|
|
||||||
|
.---.---.---.---.---.---.---.---.
|
||||||
|
key type, | | | | t | t | t | t | t |
|
||||||
|
value type '---'---'---'---'---'---'---'---'
|
||||||
|
4 0
|
||||||
|
|
||||||
|
count variable encoded uint32 count of {key,mapped} pairs
|
||||||
|
|
||||||
|
key, mapped each item encoded according to its type
|
||||||
|
|
||||||
|
|
||||||
|
variable uint32
|
||||||
|
.---.---. .---..---.---. .---..---.---. .---..---.---. .---..---.---.---.---. .---.
|
||||||
|
| 1 | v |...| v || 1 | v |...| v || 1 | v |...| v || 1 | v |...| v || 0 | 0 | 0 | v |...| v |
|
||||||
|
'---'---' '---''---'---' '---''---'---' '---''---'---' '---''---'---'---'---' '---'
|
||||||
|
6 0 13 7 20 14 27 21 31 28
|
||||||
|
|
||||||
|
1 to 5 bytes, high bit of every byte indicates if there is another byte
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
|
||||||
|
#pragma warning(push)
|
||||||
|
// Disable warning when Buffer parameter is a reference
|
||||||
|
// warning C4512: 'bond::FastBinaryReader<Buffer>' : assignment operator could not be generated
|
||||||
|
#pragma warning(disable:4512)
|
||||||
|
|
||||||
|
template <typename BufferT>
|
||||||
|
class FastBinaryWriter;
|
||||||
|
|
||||||
|
/// @brief Reader for Fast Binary protocol
|
||||||
|
template <typename BufferT>
|
||||||
|
class FastBinaryReader
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef BufferT Buffer;
|
||||||
|
typedef DynamicParser<FastBinaryReader&> Parser;
|
||||||
|
typedef FastBinaryWriter<Buffer> Writer;
|
||||||
|
|
||||||
|
static const uint16_t magic; // = FAST_PROTOCOL
|
||||||
|
static const uint16_t version; // = v1
|
||||||
|
|
||||||
|
/// @brief Construct from input buffer/stream containing serialized data.
|
||||||
|
FastBinaryReader(typename boost::call_traits<Buffer>::param_type buffer)
|
||||||
|
: _input(buffer)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
// This identical to compiler generated ctor except for throw() declaration.
|
||||||
|
// Copy ctor that is explicitly declared throw() is needed for boost::variant
|
||||||
|
// to use optimized code path.
|
||||||
|
/// @brief Copy constructor
|
||||||
|
FastBinaryReader(const FastBinaryReader& that) throw()
|
||||||
|
: _input(that._input)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Comparison operator
|
||||||
|
bool operator==(const FastBinaryReader& rhs) const
|
||||||
|
{
|
||||||
|
return _input == rhs._input;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Access to underlaying buffer
|
||||||
|
typename boost::call_traits<Buffer>::const_reference
|
||||||
|
GetBuffer() const
|
||||||
|
{
|
||||||
|
return _input;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool ReadVersion()
|
||||||
|
{
|
||||||
|
uint16_t magic, version;
|
||||||
|
|
||||||
|
_input.Read(magic);
|
||||||
|
_input.Read(version);
|
||||||
|
|
||||||
|
return magic == FastBinaryReader::magic
|
||||||
|
&& version <= FastBinaryReader::version;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Read for primitive types
|
||||||
|
template <typename T>
|
||||||
|
typename boost::disable_if<is_string_type<T> >::type
|
||||||
|
Read(T& value)
|
||||||
|
{
|
||||||
|
_input.Read(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Read for strings
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if<is_string_type<T> >::type
|
||||||
|
Read(T& value)
|
||||||
|
{
|
||||||
|
uint32_t length = 0;
|
||||||
|
|
||||||
|
ReadVariableUnsigned(_input, length);
|
||||||
|
detail::ReadStringData(_input, value, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Read for blob
|
||||||
|
void Read(blob& value, uint32_t size)
|
||||||
|
{
|
||||||
|
_input.Read(value, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReadStructBegin()
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
void ReadStructEnd()
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
void ReadFieldBegin(BondDataType& type, uint16_t& id)
|
||||||
|
{
|
||||||
|
ReadType(type);
|
||||||
|
|
||||||
|
if (type != BT_STOP && type != BT_STOP_BASE)
|
||||||
|
Read(id);
|
||||||
|
else
|
||||||
|
id = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ReadFieldEnd()
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
void ReadContainerBegin(uint32_t& size, BondDataType& type)
|
||||||
|
{
|
||||||
|
ReadType(type);
|
||||||
|
ReadVariableUnsigned(_input, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// container of 2-tuple (e.g. map)
|
||||||
|
void ReadContainerBegin(uint32_t& size, std::pair<BondDataType, BondDataType>& type)
|
||||||
|
{
|
||||||
|
ReadType(type.first);
|
||||||
|
ReadType(type.second);
|
||||||
|
ReadVariableUnsigned(_input, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ReadContainerEnd()
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void Skip()
|
||||||
|
{
|
||||||
|
Skip(get_type_id<T>::value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void Skip(const bonded<T, FastBinaryReader&>&)
|
||||||
|
{
|
||||||
|
SkipComplex(BT_STRUCT);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Skip(BondDataType type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case BT_BOOL:
|
||||||
|
case BT_UINT8:
|
||||||
|
case BT_INT8:
|
||||||
|
_input.Skip(sizeof(uint8_t));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BT_UINT16:
|
||||||
|
case BT_INT16:
|
||||||
|
_input.Skip(sizeof(uint16_t));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BT_UINT32:
|
||||||
|
case BT_INT32:
|
||||||
|
_input.Skip(sizeof(uint32_t));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BT_UINT64:
|
||||||
|
case BT_INT64:
|
||||||
|
_input.Skip(sizeof(uint64_t));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BT_FLOAT:
|
||||||
|
_input.Skip(sizeof(float));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BT_DOUBLE:
|
||||||
|
_input.Skip(sizeof(double));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
SkipComplex(type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void ReadType(BondDataType& type)
|
||||||
|
{
|
||||||
|
uint8_t byte;
|
||||||
|
|
||||||
|
Read(byte);
|
||||||
|
type = static_cast<BondDataType>(byte);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SkipComplex(BondDataType type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case BT_STRING:
|
||||||
|
{
|
||||||
|
uint32_t size = 0;
|
||||||
|
|
||||||
|
ReadVariableUnsigned(_input, size);
|
||||||
|
_input.Skip(size);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BT_WSTRING:
|
||||||
|
{
|
||||||
|
uint32_t size = 0;
|
||||||
|
|
||||||
|
ReadVariableUnsigned(_input, size);
|
||||||
|
_input.Skip(size * sizeof(uint16_t));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BT_STRUCT:
|
||||||
|
{
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
ReadStructBegin();
|
||||||
|
|
||||||
|
uint16_t id;
|
||||||
|
BondDataType type;
|
||||||
|
|
||||||
|
for (ReadFieldBegin(type, id);
|
||||||
|
type != BT_STOP && type != BT_STOP_BASE;
|
||||||
|
ReadFieldEnd(), ReadFieldBegin(type, id))
|
||||||
|
{
|
||||||
|
Skip(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadStructEnd();
|
||||||
|
|
||||||
|
if (type == BT_STOP)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BT_SET:
|
||||||
|
case BT_LIST:
|
||||||
|
{
|
||||||
|
BondDataType type;
|
||||||
|
uint32_t size;
|
||||||
|
|
||||||
|
ReadContainerBegin(size, type);
|
||||||
|
|
||||||
|
for(uint32_t i = 0; i < size; i++)
|
||||||
|
{
|
||||||
|
Skip(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadContainerEnd();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BT_MAP:
|
||||||
|
{
|
||||||
|
std::pair<BondDataType, BondDataType> type;
|
||||||
|
uint32_t size;
|
||||||
|
|
||||||
|
ReadContainerBegin(size, type);
|
||||||
|
|
||||||
|
for(uint32_t i = 0; i < size; i++)
|
||||||
|
{
|
||||||
|
Skip(type.first);
|
||||||
|
Skip(type.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadContainerEnd();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Buffer _input;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Buffer>
|
||||||
|
const uint16_t FastBinaryReader<Buffer>::magic = FAST_PROTOCOL;
|
||||||
|
|
||||||
|
template <typename Buffer>
|
||||||
|
const uint16_t FastBinaryReader<Buffer>::version = v1;
|
||||||
|
|
||||||
|
#pragma warning(pop)
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Writer for Fast Binary protocol
|
||||||
|
template <typename BufferT>
|
||||||
|
class FastBinaryWriter
|
||||||
|
: boost::noncopyable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef BufferT Buffer;
|
||||||
|
typedef FastBinaryReader<Buffer> Reader;
|
||||||
|
|
||||||
|
/// @brief Construct from output buffer/stream.
|
||||||
|
FastBinaryWriter(Buffer& buffer)
|
||||||
|
: _output(buffer)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteVersion()
|
||||||
|
{
|
||||||
|
_output.Write(Reader::magic);
|
||||||
|
_output.Write(Reader::version);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Write methods
|
||||||
|
//
|
||||||
|
void WriteStructBegin(const Metadata& /*metadata*/, bool /*base*/)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void WriteStructEnd(bool base = false)
|
||||||
|
{
|
||||||
|
WriteType(base ? BT_STOP_BASE : BT_STOP);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void WriteField(uint16_t id, const bond::Metadata& /*metadata*/, const T& value)
|
||||||
|
{
|
||||||
|
WriteFieldBegin(get_type_id<T>::value, id);
|
||||||
|
Write(value);
|
||||||
|
WriteFieldEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteFieldBegin(BondDataType type, uint16_t id, const bond::Metadata& /*metadata*/)
|
||||||
|
{
|
||||||
|
WriteFieldBegin(type, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteFieldBegin(BondDataType type, uint16_t id)
|
||||||
|
{
|
||||||
|
WriteType(type);
|
||||||
|
Write(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteFieldEnd()
|
||||||
|
{}
|
||||||
|
|
||||||
|
void WriteContainerBegin(uint32_t size, BondDataType type)
|
||||||
|
{
|
||||||
|
WriteType(type);
|
||||||
|
WriteVariableUnsigned(_output, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// container of 2-tuples (e.g. map)
|
||||||
|
void WriteContainerBegin(uint32_t size, std::pair<BondDataType, BondDataType> type)
|
||||||
|
{
|
||||||
|
WriteType(type.first);
|
||||||
|
WriteType(type.second);
|
||||||
|
WriteVariableUnsigned(_output, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteContainerEnd()
|
||||||
|
{}
|
||||||
|
|
||||||
|
// Write for primitive types
|
||||||
|
template<typename T>
|
||||||
|
typename boost::disable_if<is_string_type<T> >::type
|
||||||
|
Write(const T& value)
|
||||||
|
{
|
||||||
|
_output.Write(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write for strings
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if<is_string_type<T> >::type
|
||||||
|
Write(const T& value)
|
||||||
|
{
|
||||||
|
uint32_t length = string_length(value);
|
||||||
|
|
||||||
|
WriteVariableUnsigned(_output, length);
|
||||||
|
detail::WriteStringData(_output, value, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write for blob
|
||||||
|
void Write(const blob& value)
|
||||||
|
{
|
||||||
|
_output.Write(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void WriteType(BondDataType type)
|
||||||
|
{
|
||||||
|
_output.Write(static_cast<uint8_t>(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
Buffer& _output;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace bond
|
||||||
|
|
|
@ -0,0 +1,215 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <bond/core/containers.h>
|
||||||
|
#include <bond/core/blob.h>
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class RandomProtocolEngine
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// We don't need a good pseudo-random number generator but we do need one
|
||||||
|
// that is consistent accross compilers/libraries so that we can use
|
||||||
|
// randomly generated data files from one platfom to verify compatibility
|
||||||
|
// on another platform.
|
||||||
|
RandomProtocolEngine()
|
||||||
|
{}
|
||||||
|
|
||||||
|
// Variant of Marsaglia’s xorshift generator
|
||||||
|
// http://arxiv.org/pdf/1404.0390v1.pdf
|
||||||
|
uint64_t Next()
|
||||||
|
{
|
||||||
|
uint64_t s1 = state[0];
|
||||||
|
const uint64_t s0 = state[1];
|
||||||
|
state[0] = s0;
|
||||||
|
s1 ^= s1 << 23;
|
||||||
|
return (state[1] = (s1 ^ s0 ^ (s1 >> 17) ^ (s0 >> 26))) + s0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Seed(uint64_t s0 = seed1, uint64_t s1 = seed2)
|
||||||
|
{
|
||||||
|
state[0] = s0;
|
||||||
|
state[1] = s1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t state[2];
|
||||||
|
const static uint64_t seed1 = 234578354U;
|
||||||
|
const static uint64_t seed2 = 753478U;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
uint64_t RandomProtocolEngine<T>::state[2] = {seed1, seed2};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Protocol which generates a random stream of bits.
|
||||||
|
// In some cases it may be useful for initializing data in tests, e.g.:
|
||||||
|
//
|
||||||
|
// Params param;
|
||||||
|
// Apply(bond::To<Params>(param), bond::bonded<Params>(bond::RandomProtocolReader()));
|
||||||
|
//
|
||||||
|
class RandomProtocolReader
|
||||||
|
: public RandomProtocolEngine<RandomProtocolReader>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef bond::StaticParser<RandomProtocolReader&> Parser;
|
||||||
|
|
||||||
|
RandomProtocolReader(uint32_t max_string_length = 50, uint32_t max_list_size = 20, bool json = false)
|
||||||
|
: _max_string_length(max_string_length),
|
||||||
|
_max_list_size(max_list_size),
|
||||||
|
_json(json)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool operator==(const RandomProtocolReader&) const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ReadVersion()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename boost::disable_if<is_string_type<T> >::type
|
||||||
|
Read(T& value)
|
||||||
|
{
|
||||||
|
uint64_t random = RandomProtocolEngine::Next();
|
||||||
|
|
||||||
|
value = *static_cast<T*>(static_cast<void*>(&random));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Read(uint64_t& value)
|
||||||
|
{
|
||||||
|
value = RandomProtocolEngine::Next();
|
||||||
|
|
||||||
|
if (_json)
|
||||||
|
{
|
||||||
|
// NewtonsoftJson used by C# implementation doesn't support
|
||||||
|
// numbers larger that max int64.
|
||||||
|
value >>= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Read(bool& value)
|
||||||
|
{
|
||||||
|
int8_t n;
|
||||||
|
Read(n);
|
||||||
|
value = n > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Read(double& value)
|
||||||
|
{
|
||||||
|
uint8_t sign;
|
||||||
|
uint8_t exponent;
|
||||||
|
uint32_t mantissa;
|
||||||
|
|
||||||
|
Read(sign);
|
||||||
|
Read(exponent);
|
||||||
|
Read(mantissa);
|
||||||
|
|
||||||
|
// don't return special values: infinity, NaN
|
||||||
|
if (exponent == 0)
|
||||||
|
exponent = 0x80;
|
||||||
|
|
||||||
|
uint64_t bits = ((uint64_t)(sign) << 63) | ((uint64_t)(exponent) << (52 + 3)) | (uint64_t)mantissa;
|
||||||
|
|
||||||
|
*reinterpret_cast<uint64_t*>(&value) = bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Read(float& value)
|
||||||
|
{
|
||||||
|
uint8_t sign;
|
||||||
|
uint8_t exponent;
|
||||||
|
uint16_t mantissa;
|
||||||
|
|
||||||
|
Read(sign);
|
||||||
|
Read(exponent);
|
||||||
|
Read(mantissa);
|
||||||
|
|
||||||
|
// don't return special values: infinity, NaN
|
||||||
|
if (exponent == 0 || exponent == 0xff)
|
||||||
|
exponent = 0x80;
|
||||||
|
|
||||||
|
uint32_t bits = ((uint32_t)(sign) << 31) | ((uint32_t)(exponent) << 23) | (uint32_t)mantissa;
|
||||||
|
|
||||||
|
*reinterpret_cast<uint32_t*>(&value) = bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if<is_string_type<T> >::type
|
||||||
|
Read(T& value)
|
||||||
|
{
|
||||||
|
uint32_t length = 0;
|
||||||
|
|
||||||
|
Read(length);
|
||||||
|
|
||||||
|
length %= _max_string_length;
|
||||||
|
length = length ? length : 1;
|
||||||
|
|
||||||
|
resize_string(value, length);
|
||||||
|
|
||||||
|
typename element_type<T>::type* p = string_data(value);
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < length; ++i)
|
||||||
|
{
|
||||||
|
uint8_t c;
|
||||||
|
|
||||||
|
Read(c);
|
||||||
|
|
||||||
|
p[i] = typename element_type<T>::type(' ') + c % ('z' - ' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read for blob
|
||||||
|
void Read(blob& value, uint32_t size)
|
||||||
|
{
|
||||||
|
boost::shared_array<char> buffer(new char[size]);
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < size; ++i)
|
||||||
|
Read(buffer[i]);
|
||||||
|
|
||||||
|
value.assign(buffer, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void Skip()
|
||||||
|
{}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void Skip(const T&)
|
||||||
|
{}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void ReadContainerBegin(uint32_t& size, T&)
|
||||||
|
{
|
||||||
|
Read(size);
|
||||||
|
|
||||||
|
size %= _max_list_size;
|
||||||
|
size = size ? size : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReadContainerEnd() const
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint32_t _max_string_length;
|
||||||
|
uint32_t _max_list_size;
|
||||||
|
bool _json;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Unused> struct
|
||||||
|
uses_marshaled_bonded<RandomProtocolReader, Unused>
|
||||||
|
: boost::false_type {};
|
||||||
|
|
||||||
|
template <typename Unused> struct
|
||||||
|
uses_marshaled_bonded<RandomProtocolReader&, Unused>
|
||||||
|
: boost::false_type {};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,342 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "encoding.h"
|
||||||
|
#include <bond/core/traits.h>
|
||||||
|
#include <bond/core/bond_version.h>
|
||||||
|
#include <boost/call_traits.hpp>
|
||||||
|
#include <boost/noncopyable.hpp>
|
||||||
|
|
||||||
|
#pragma warning(push)
|
||||||
|
// Disable warning when Buffer parameter is a reference
|
||||||
|
// warning C4512: 'bond::SimpleBinaryReader<Buffer>' : assignment operator could not be generated
|
||||||
|
#pragma warning(disable:4512)
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
template <typename BufferT>
|
||||||
|
class SimpleBinaryWriter;
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Reader for Simple Binary protocol
|
||||||
|
template <typename BufferT>
|
||||||
|
class SimpleBinaryReader
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef BufferT Buffer;
|
||||||
|
typedef StaticParser<SimpleBinaryReader&> Parser;
|
||||||
|
typedef SimpleBinaryWriter<Buffer> Writer;
|
||||||
|
|
||||||
|
static const uint16_t magic; // = SIMPLE_PROTOCOL
|
||||||
|
static const uint16_t version = v2;
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Construct from input buffer/stream containing serialized data.
|
||||||
|
SimpleBinaryReader(typename boost::call_traits<Buffer>::param_type input,
|
||||||
|
uint16_t version = default_version<SimpleBinaryReader>::value)
|
||||||
|
: _input(input),
|
||||||
|
_version(version)
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(_version <= SimpleBinaryReader::version);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// This identical to compiler generated ctor except for throw() declaration.
|
||||||
|
// Copy ctor that is explicitly declared throw() is needed for boost::variant
|
||||||
|
// to use optimized code path.
|
||||||
|
/// @brief Copy constructor
|
||||||
|
SimpleBinaryReader(const SimpleBinaryReader& that) throw()
|
||||||
|
: _input(that._input),
|
||||||
|
_version(that._version)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Comparison operator
|
||||||
|
bool operator==(const SimpleBinaryReader& rhs) const
|
||||||
|
{
|
||||||
|
return _input == rhs._input;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Access to underlaying buffer
|
||||||
|
typename boost::call_traits<Buffer>::const_reference
|
||||||
|
GetBuffer() const
|
||||||
|
{
|
||||||
|
return _input;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool ReadVersion()
|
||||||
|
{
|
||||||
|
uint16_t magic;
|
||||||
|
|
||||||
|
_input.Read(magic);
|
||||||
|
_input.Read(_version);
|
||||||
|
|
||||||
|
return magic == SimpleBinaryReader::magic
|
||||||
|
&& _version <= SimpleBinaryReader::version;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Read for basic types
|
||||||
|
template <typename T>
|
||||||
|
typename boost::disable_if<is_string_type<T> >::type
|
||||||
|
Read(T& var)
|
||||||
|
{
|
||||||
|
_input.Read(var);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Read for strings
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if<is_string_type<T> >::type
|
||||||
|
Read(T& var)
|
||||||
|
{
|
||||||
|
uint32_t length = 0;
|
||||||
|
|
||||||
|
ReadSize(length);
|
||||||
|
detail::ReadStringData(_input, var, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Read for blob
|
||||||
|
void Read(blob& var, uint32_t size)
|
||||||
|
{
|
||||||
|
_input.Read(var, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Skip for basic types
|
||||||
|
template <typename T>
|
||||||
|
typename boost::disable_if<is_string_type<T> >::type
|
||||||
|
Skip()
|
||||||
|
{
|
||||||
|
_input.Skip(sizeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void Skip(const bonded<T, SimpleBinaryReader&>& bonded);
|
||||||
|
|
||||||
|
|
||||||
|
// Skip for strings
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if<is_string_type<T> >::type
|
||||||
|
Skip()
|
||||||
|
{
|
||||||
|
uint32_t length;
|
||||||
|
|
||||||
|
ReadSize(length);
|
||||||
|
_input.Skip(length * sizeof(typename detail::string_char_int_type<T>::type));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Skip(BondDataType type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case BT_BOOL:
|
||||||
|
case BT_UINT8:
|
||||||
|
case BT_INT8:
|
||||||
|
_input.Skip(sizeof(uint8_t));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BT_UINT16:
|
||||||
|
case BT_INT16:
|
||||||
|
_input.Skip(sizeof(uint16_t));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BT_UINT32:
|
||||||
|
case BT_INT32:
|
||||||
|
_input.Skip(sizeof(uint32_t));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BT_UINT64:
|
||||||
|
case BT_INT64:
|
||||||
|
_input.Skip(sizeof(uint64_t));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BT_FLOAT:
|
||||||
|
_input.Skip(sizeof(float));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BT_DOUBLE:
|
||||||
|
_input.Skip(sizeof(double));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BT_STRING:
|
||||||
|
Skip<std::string>();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BT_WSTRING:
|
||||||
|
Skip<std::wstring>();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void ReadContainerBegin(uint32_t& size, T&)
|
||||||
|
{
|
||||||
|
ReadSize(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReadContainerEnd()
|
||||||
|
{}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void ReadSize(uint32_t& size)
|
||||||
|
{
|
||||||
|
if (_version == v1)
|
||||||
|
Read(size);
|
||||||
|
else
|
||||||
|
ReadVariableUnsigned(_input, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Input, typename Output>
|
||||||
|
friend
|
||||||
|
bool is_protocol_version_same(const SimpleBinaryReader<Input>&,
|
||||||
|
const SimpleBinaryWriter<Output>&);
|
||||||
|
|
||||||
|
Buffer _input;
|
||||||
|
uint16_t _version;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Buffer>
|
||||||
|
const uint16_t SimpleBinaryReader<Buffer>::magic = SIMPLE_PROTOCOL;
|
||||||
|
|
||||||
|
#pragma warning(pop)
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Writer for Simple Binary protocol
|
||||||
|
template <typename BufferT>
|
||||||
|
class SimpleBinaryWriter
|
||||||
|
: boost::noncopyable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef BufferT Buffer;
|
||||||
|
typedef SimpleBinaryReader<Buffer> Reader;
|
||||||
|
|
||||||
|
/// @brief Construct from output buffer/stream.
|
||||||
|
SimpleBinaryWriter(Buffer& output,
|
||||||
|
uint16_t version = default_version<Reader>::value)
|
||||||
|
: _output(output),
|
||||||
|
_version(version)
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(_version <= Reader::version);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteVersion()
|
||||||
|
{
|
||||||
|
_output.Write(Reader::magic);
|
||||||
|
_output.Write(_version);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteStructBegin(const Metadata& /*metadata*/, bool /*base*/)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void WriteStructEnd(bool = false)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void WriteFieldBegin(BondDataType /*type*/, uint16_t /*id*/, const Metadata& /*metadata*/)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void WriteFieldBegin(BondDataType /*type*/, uint16_t /*id*/)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void WriteFieldEnd()
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
// WriteContainerBegin
|
||||||
|
template <typename T>
|
||||||
|
void WriteContainerBegin(uint32_t size, T)
|
||||||
|
{
|
||||||
|
WriteSize(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// WriteContainerEnd
|
||||||
|
void WriteContainerEnd()
|
||||||
|
{}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void WriteField(uint16_t /*id*/, const bond::Metadata& /*metadata*/, const T& value)
|
||||||
|
{
|
||||||
|
Write(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteFieldOmitted(BondDataType type, uint16_t /*id*/, const Metadata& metadata);
|
||||||
|
|
||||||
|
// Write for basic types
|
||||||
|
template <typename T>
|
||||||
|
typename boost::disable_if<is_string_type<T> >::type
|
||||||
|
Write(const T& value)
|
||||||
|
{
|
||||||
|
_output.Write(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write for strings
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if<is_string_type<T> >::type
|
||||||
|
Write(const T& value)
|
||||||
|
{
|
||||||
|
uint32_t length = string_length(value);
|
||||||
|
|
||||||
|
WriteSize(length);
|
||||||
|
detail::WriteStringData(_output, value, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write for blob
|
||||||
|
void Write(const blob& value)
|
||||||
|
{
|
||||||
|
_output.Write(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void WriteSize(uint32_t& size)
|
||||||
|
{
|
||||||
|
if (_version == v1)
|
||||||
|
Write(size);
|
||||||
|
else
|
||||||
|
WriteVariableUnsigned(_output, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Input, typename Output>
|
||||||
|
friend
|
||||||
|
bool is_protocol_version_same(const SimpleBinaryReader<Input>&,
|
||||||
|
const SimpleBinaryWriter<Output>&);
|
||||||
|
|
||||||
|
Buffer& _output;
|
||||||
|
uint16_t _version;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Input> struct
|
||||||
|
protocol_has_multiple_versions<SimpleBinaryReader<Input> >
|
||||||
|
: true_type {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Input, typename Output>
|
||||||
|
bool is_protocol_version_same(const SimpleBinaryReader<Input>& reader,
|
||||||
|
const SimpleBinaryWriter<Output>& writer)
|
||||||
|
{
|
||||||
|
return reader._version == writer._version;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Output> struct
|
||||||
|
may_omit_fields<SimpleBinaryWriter<Output> >
|
||||||
|
: false_type {};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#include "simple_binary.h"
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename BufferT>
|
||||||
|
template <typename T>
|
||||||
|
inline void SimpleBinaryReader<BufferT>::Skip(const bonded<T, SimpleBinaryReader&>& bonded)
|
||||||
|
{
|
||||||
|
// Skip the structure field-by-field by applying Null transform
|
||||||
|
Apply(Null(), bonded);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename BufferT>
|
||||||
|
inline void SimpleBinaryWriter<BufferT>::WriteFieldOmitted(BondDataType type, uint16_t /*id*/, const Metadata& metadata)
|
||||||
|
{
|
||||||
|
// Simple doesn't support omitting fields so instead we write a default value
|
||||||
|
BOOST_ASSERT(!metadata.default_value.nothing);
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case BT_BOOL:
|
||||||
|
Write(!!metadata.default_value.uint_value);
|
||||||
|
break;
|
||||||
|
case BT_UINT8:
|
||||||
|
Write(static_cast<uint8_t>(metadata.default_value.uint_value));
|
||||||
|
break;
|
||||||
|
case BT_UINT16:
|
||||||
|
Write(static_cast<uint16_t>(metadata.default_value.uint_value));
|
||||||
|
break;
|
||||||
|
case BT_UINT32:
|
||||||
|
Write(static_cast<uint32_t>(metadata.default_value.uint_value));
|
||||||
|
break;
|
||||||
|
case BT_UINT64:
|
||||||
|
Write(static_cast<uint64_t>(metadata.default_value.uint_value));
|
||||||
|
break;
|
||||||
|
case BT_FLOAT:
|
||||||
|
Write(static_cast<float>(metadata.default_value.double_value));
|
||||||
|
break;
|
||||||
|
case BT_DOUBLE:
|
||||||
|
Write(metadata.default_value.double_value);
|
||||||
|
break;
|
||||||
|
case BT_STRING:
|
||||||
|
Write(metadata.default_value.string_value);
|
||||||
|
break;
|
||||||
|
case BT_STRUCT:
|
||||||
|
BOOST_ASSERT(false);
|
||||||
|
break;
|
||||||
|
case BT_LIST:
|
||||||
|
case BT_SET:
|
||||||
|
case BT_MAP:
|
||||||
|
WriteContainerBegin(0, type);
|
||||||
|
break;
|
||||||
|
case BT_INT8:
|
||||||
|
Write(static_cast<int8_t>(metadata.default_value.int_value));
|
||||||
|
break;
|
||||||
|
case BT_INT16:
|
||||||
|
Write(static_cast<int16_t>(metadata.default_value.int_value));
|
||||||
|
break;
|
||||||
|
case BT_INT32:
|
||||||
|
Write(static_cast<int32_t>(metadata.default_value.int_value));
|
||||||
|
break;
|
||||||
|
case BT_INT64:
|
||||||
|
Write(static_cast<int64_t>(metadata.default_value.int_value));
|
||||||
|
break;
|
||||||
|
case BT_WSTRING:
|
||||||
|
Write(metadata.default_value.wstring_value);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
BOOST_ASSERT(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,190 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "encoding.h"
|
||||||
|
#include "detail/rapidjson_helper.h"
|
||||||
|
#include <boost/call_traits.hpp>
|
||||||
|
|
||||||
|
#pragma warning(push)
|
||||||
|
|
||||||
|
// Disable warning when Buffer parameter is a reference
|
||||||
|
// warning C4512: 'bond::SimpleJsonReader<Buffer>' : assignment operator could not be generated
|
||||||
|
#pragma warning(disable:4512)
|
||||||
|
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename BufferT>
|
||||||
|
class SimpleJsonWriter;
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Reader for Simple JSON
|
||||||
|
template <typename BufferT>
|
||||||
|
class SimpleJsonReader
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef BufferT Buffer;
|
||||||
|
typedef DOMParser<SimpleJsonReader&> Parser;
|
||||||
|
typedef SimpleJsonWriter<Buffer> Writer;
|
||||||
|
typedef rapidjson::Value Field;
|
||||||
|
|
||||||
|
static const uint16_t magic; // = SIMPLE_JSON_PROTOCOL;
|
||||||
|
static const uint16_t version = 0x0001;
|
||||||
|
|
||||||
|
/// @brief Construct from input buffer/stream containing serialized data.
|
||||||
|
SimpleJsonReader(typename boost::call_traits<Buffer>::param_type input)
|
||||||
|
: _input(input),
|
||||||
|
_stream(_input),
|
||||||
|
_document(new rapidjson::Document),
|
||||||
|
_value(NULL)
|
||||||
|
{}
|
||||||
|
|
||||||
|
SimpleJsonReader(const SimpleJsonReader& that, const Field& value)
|
||||||
|
: _stream(that._stream),
|
||||||
|
_document(that._document),
|
||||||
|
_value(&value)
|
||||||
|
{}
|
||||||
|
|
||||||
|
/// @brief Copy constructor
|
||||||
|
SimpleJsonReader(const SimpleJsonReader& that)
|
||||||
|
: _input(that._input),
|
||||||
|
_stream(that._stream, _input),
|
||||||
|
_document(that._document),
|
||||||
|
_value(that._value)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool ReadVersion()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Parse()
|
||||||
|
{
|
||||||
|
// Don't need to reparse for nested fields
|
||||||
|
if (!_value || _value == _document.get())
|
||||||
|
{
|
||||||
|
_document->ParseStream<rapidjson::kParseStopWhenDoneFlag>(_stream);
|
||||||
|
_value = _document.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Field* FindField(uint16_t id, const Metadata& metadata, BondDataType type)
|
||||||
|
{
|
||||||
|
// BT_INT32 may be an enum. This allows us to decode symbolic enum values
|
||||||
|
// when parsing using runtime schema. The assumption is that runtime schema
|
||||||
|
// matches JSON payload. If it doesn't, nothing horrible will happen, but
|
||||||
|
// we might not indicate a required field missing for an int32 field if we
|
||||||
|
// mistake a string member with matching name for it.
|
||||||
|
return FindField(id, metadata, type, type == BT_INT32);
|
||||||
|
}
|
||||||
|
|
||||||
|
const Field* FindField(uint16_t id, const Metadata& metadata, BondDataType type, bool is_enum);
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void Read(T& var)
|
||||||
|
{
|
||||||
|
detail::Read(*_value, var);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void ReadContainerBegin(uint32_t&, T&)
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReadContainerEnd()
|
||||||
|
{
|
||||||
|
BOOST_ASSERT(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void Skip()
|
||||||
|
{}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void Skip(const T&)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
bool operator==(const SimpleJsonReader& rhs) const
|
||||||
|
{
|
||||||
|
return _value == rhs._value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
rapidjson::Value::ConstMemberIterator MemberBegin() const
|
||||||
|
{
|
||||||
|
return _value->IsObject() ? _value->MemberBegin() : rapidjson::Value::ConstMemberIterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
rapidjson::Value::ConstMemberIterator MemberEnd() const
|
||||||
|
{
|
||||||
|
return _value->IsObject() ? _value->MemberEnd() : rapidjson::Value::ConstMemberIterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
rapidjson::Value::ConstValueIterator ArrayBegin() const
|
||||||
|
{
|
||||||
|
return _value->IsArray() ? _value->Begin() : rapidjson::Value::ConstValueIterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
rapidjson::Value::ConstValueIterator ArrayEnd() const
|
||||||
|
{
|
||||||
|
return _value->IsArray() ? _value->End() : rapidjson::Value::ConstValueIterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t ArraySize() const
|
||||||
|
{
|
||||||
|
return _value->IsArray() ? _value->Size() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Input>
|
||||||
|
friend struct base_input;
|
||||||
|
|
||||||
|
template <typename A, typename T, typename Buffer>
|
||||||
|
friend void DeserializeContainer(std::vector<bool, A>&, const T&, SimpleJsonReader<Buffer>&);
|
||||||
|
|
||||||
|
template <typename T, typename Buffer>
|
||||||
|
friend void DeserializeContainer(blob&, const T&, SimpleJsonReader<Buffer>&);
|
||||||
|
|
||||||
|
template <typename X, typename T, typename Buffer>
|
||||||
|
friend typename boost::enable_if<is_list_container<X> >::type
|
||||||
|
DeserializeContainer(X&, const T&, SimpleJsonReader<Buffer>&);
|
||||||
|
|
||||||
|
template <typename X, typename T, typename Buffer>
|
||||||
|
friend typename boost::enable_if<is_set_container<X> >::type
|
||||||
|
DeserializeContainer(X&, const T&, SimpleJsonReader<Buffer>&);
|
||||||
|
|
||||||
|
template <typename X, typename T, typename Buffer>
|
||||||
|
friend typename boost::enable_if<is_map_container<X> >::type
|
||||||
|
DeserializeMap(X&, BondDataType, const T&, SimpleJsonReader<Buffer>&);
|
||||||
|
|
||||||
|
SimpleJsonReader(const SimpleJsonReader& that, const char* name)
|
||||||
|
: _stream(that._stream),
|
||||||
|
_document(that._document),
|
||||||
|
_value(&(*that._value)[name])
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
Buffer _input;
|
||||||
|
detail::RapidJsonInputStream<Buffer> _stream;
|
||||||
|
boost::shared_ptr<rapidjson::Document> _document;
|
||||||
|
const rapidjson::Value* _value;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Buffer>
|
||||||
|
const uint16_t SimpleJsonReader<Buffer>::magic = SIMPLE_JSON_PROTOCOL;
|
||||||
|
|
||||||
|
// Disable fast pass-through optimization for Simple JSON
|
||||||
|
template <typename Input, typename Output> struct
|
||||||
|
is_protocol_same<SimpleJsonReader<Input>, SimpleJsonWriter<Output> >
|
||||||
|
: false_type {};
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace bond
|
||||||
|
|
||||||
|
#pragma warning(pop)
|
|
@ -0,0 +1,163 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "simple_json_reader.h"
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename BufferT>
|
||||||
|
inline const typename SimpleJsonReader<BufferT>::Field*
|
||||||
|
SimpleJsonReader<BufferT>::FindField(uint16_t id, const Metadata& metadata, BondDataType type, bool is_enum)
|
||||||
|
{
|
||||||
|
rapidjson::Value::ConstMemberIterator it = MemberBegin();
|
||||||
|
|
||||||
|
if (it != MemberEnd())
|
||||||
|
{
|
||||||
|
char ids[6];
|
||||||
|
const char* name = detail::FieldName(metadata).c_str();
|
||||||
|
detail::JsonTypeMatching jsonType(type, type, is_enum);
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
_itoa(id, ids, 10);
|
||||||
|
#else
|
||||||
|
sprintf(ids, "%u", id);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Match member by type of value and either metadata name, or string reprentation of id
|
||||||
|
for (rapidjson::Value::ConstMemberIterator end = MemberEnd(); it != end; ++it)
|
||||||
|
if (jsonType.TypeMatch(it->value))
|
||||||
|
if (!strcmp(it->name.GetString(), name) || !strcmp(it->name.GetString(), ids))
|
||||||
|
return &it->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// deserialize std::vector<bool>
|
||||||
|
template <typename A, typename T, typename Buffer>
|
||||||
|
inline void DeserializeContainer(std::vector<bool, A>& var, const T& /*element*/, SimpleJsonReader<Buffer>& reader)
|
||||||
|
{
|
||||||
|
rapidjson::Value::ConstValueIterator it = reader.ArrayBegin();
|
||||||
|
resize_list(var, reader.ArraySize());
|
||||||
|
|
||||||
|
for (enumerator<std::vector<bool, A> > items(var); items.more(); ++it)
|
||||||
|
{
|
||||||
|
items.next() = it->IsTrue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// deserialize blob
|
||||||
|
template <typename T, typename Buffer>
|
||||||
|
inline void DeserializeContainer(blob& var, const T& /*element*/, SimpleJsonReader<Buffer>& reader)
|
||||||
|
{
|
||||||
|
if (uint32_t size = reader.ArraySize())
|
||||||
|
{
|
||||||
|
boost::shared_ptr<char[]> buffer = boost::make_shared<char[]>(size);
|
||||||
|
uint32_t i = 0;
|
||||||
|
|
||||||
|
for (rapidjson::Value::ConstValueIterator it = reader.ArrayBegin(), end = reader.ArrayEnd(); it != end && i < size; ++it)
|
||||||
|
if (it->IsInt())
|
||||||
|
buffer[i++] = static_cast<blob::value_type>(it->GetInt());
|
||||||
|
|
||||||
|
var.assign(buffer, i);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
var.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// deserialize list
|
||||||
|
template <typename X, typename T, typename Buffer>
|
||||||
|
inline typename boost::enable_if<is_list_container<X> >::type
|
||||||
|
DeserializeContainer(X& var, const T& element, SimpleJsonReader<Buffer>& reader)
|
||||||
|
{
|
||||||
|
detail::JsonTypeMatching type(get_type_id<typename element_type<X>::type>::value,
|
||||||
|
GetTypeId(element),
|
||||||
|
is_enum<typename element_type<X>::type>::value);
|
||||||
|
|
||||||
|
rapidjson::Value::ConstValueIterator it = reader.ArrayBegin();
|
||||||
|
resize_list(var, reader.ArraySize());
|
||||||
|
|
||||||
|
for (enumerator<X> items(var); items.more(); ++it)
|
||||||
|
{
|
||||||
|
if (type.ComplexTypeMatch(*it))
|
||||||
|
{
|
||||||
|
SimpleJsonReader<Buffer> input(reader, *it);
|
||||||
|
DeserializeElement(var, items.next(), detail::MakeValue(input, element));
|
||||||
|
}
|
||||||
|
else if (type.BasicTypeMatch(*it))
|
||||||
|
{
|
||||||
|
SimpleJsonReader<Buffer> input(reader, *it);
|
||||||
|
DeserializeElement(var, items.next(), value<typename element_type<X>::type, SimpleJsonReader<Buffer>&>(input));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
items.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// deserialize set
|
||||||
|
template <typename X, typename T, typename Buffer>
|
||||||
|
inline typename boost::enable_if<is_set_container<X> >::type
|
||||||
|
DeserializeContainer(X& var, const T& element, SimpleJsonReader<Buffer>& reader)
|
||||||
|
{
|
||||||
|
detail::JsonTypeMatching type(get_type_id<typename element_type<X>::type>::value,
|
||||||
|
GetTypeId(element),
|
||||||
|
is_enum<typename element_type<X>::type>::value);
|
||||||
|
clear_set(var);
|
||||||
|
|
||||||
|
typename element_type<X>::type e(make_element(var));
|
||||||
|
|
||||||
|
for (rapidjson::Value::ConstValueIterator it = reader.ArrayBegin(), end = reader.ArrayEnd(); it != end; ++it)
|
||||||
|
{
|
||||||
|
if (type.BasicTypeMatch(*it))
|
||||||
|
{
|
||||||
|
detail::Read(*it, e);
|
||||||
|
set_insert(var, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// deserialize map
|
||||||
|
template <typename X, typename T, typename Buffer>
|
||||||
|
inline typename boost::enable_if<is_map_container<X> >::type
|
||||||
|
DeserializeMap(X& var, BondDataType keyType, const T& element, SimpleJsonReader<Buffer>& reader)
|
||||||
|
{
|
||||||
|
detail::JsonTypeMatching key_type(
|
||||||
|
get_type_id<typename element_type<X>::type::first_type>::value,
|
||||||
|
keyType,
|
||||||
|
is_enum<typename element_type<X>::type::first_type>::value);
|
||||||
|
|
||||||
|
detail::JsonTypeMatching value_type(
|
||||||
|
get_type_id<typename element_type<X>::type::second_type>::value,
|
||||||
|
GetTypeId(element),
|
||||||
|
is_enum<typename element_type<X>::type::second_type>::value);
|
||||||
|
|
||||||
|
clear_map(var);
|
||||||
|
|
||||||
|
typename element_type<X>::type::first_type key(make_key(var));
|
||||||
|
|
||||||
|
for (rapidjson::Value::ConstValueIterator it = reader.ArrayBegin(), end = reader.ArrayEnd(); it != end; ++it)
|
||||||
|
{
|
||||||
|
if (key_type.BasicTypeMatch(*it))
|
||||||
|
{
|
||||||
|
detail::Read(*it, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
SimpleJsonReader<Buffer> input(reader, *++it);
|
||||||
|
|
||||||
|
if (value_type.ComplexTypeMatch(*it))
|
||||||
|
detail::MakeValue(input, element).Deserialize(mapped_at(var, key));
|
||||||
|
else
|
||||||
|
value<typename element_type<X>::type::second_type, SimpleJsonReader<Buffer>&>(input).Deserialize(mapped_at(var, key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,481 @@
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "encoding.h"
|
||||||
|
#include "detail/rapidjson_helper.h"
|
||||||
|
#include <bond/core/transforms.h>
|
||||||
|
|
||||||
|
namespace bond
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Buffer>
|
||||||
|
class SimpleJsonReader;
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Writer for Simple JSON
|
||||||
|
template <typename BufferT>
|
||||||
|
class SimpleJsonWriter
|
||||||
|
: protected rapidjson::Writer<detail::RapidJsonOutputStream<BufferT> >,
|
||||||
|
boost::noncopyable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef BufferT Buffer;
|
||||||
|
typedef SimpleJsonReader<Buffer> Reader;
|
||||||
|
|
||||||
|
/// @brief Construct from output buffer/stream.
|
||||||
|
/// @param output reference to output buffer/stream
|
||||||
|
/// @param pretty true to generate output with whitespaces, default false
|
||||||
|
/// @param indent number of spaces of indentation, default 4
|
||||||
|
/// @param all_fields if false, optional fields may be omitted, default true
|
||||||
|
SimpleJsonWriter(Buffer& output, bool pretty = false, int indent = 4, bool all_fields = true)
|
||||||
|
: rapidjson::Writer<detail::RapidJsonOutputStream<BufferT> >(_stream),
|
||||||
|
_stream(output),
|
||||||
|
_output(output),
|
||||||
|
_level(0),
|
||||||
|
_indent((std::min)(indent, 8)),
|
||||||
|
_pretty(pretty),
|
||||||
|
_all_fields(all_fields)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void WriteVersion()
|
||||||
|
{}
|
||||||
|
|
||||||
|
void WriteOpen(char bracket)
|
||||||
|
{
|
||||||
|
_output.Write(bracket);
|
||||||
|
++_level;
|
||||||
|
_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteClose(char bracket)
|
||||||
|
{
|
||||||
|
--_level;
|
||||||
|
++_count;
|
||||||
|
NewLine();
|
||||||
|
_output.Write(bracket);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if<is_string_type<T> >::type
|
||||||
|
WriteName(const T& name)
|
||||||
|
{
|
||||||
|
WriteString(name);
|
||||||
|
_output.Write(": ", _pretty ? 2 : 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteName(uint16_t id)
|
||||||
|
{
|
||||||
|
_output.Write('\"');
|
||||||
|
this->WriteUint(id);
|
||||||
|
_output.Write("\": ", _pretty ? 3 : 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Write(bool value)
|
||||||
|
{
|
||||||
|
if (value)
|
||||||
|
_output.Write("true", 4);
|
||||||
|
else
|
||||||
|
_output.Write("false", 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if<is_string_type<T> >::type
|
||||||
|
Write(const T& value)
|
||||||
|
{
|
||||||
|
WriteString(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if<is_signed_int<T> >::type
|
||||||
|
Write(T value)
|
||||||
|
{
|
||||||
|
this->WriteInt64(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if<is_unsigned<T> >::type
|
||||||
|
Write(T value)
|
||||||
|
{
|
||||||
|
this->WriteUint64(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Write(double value)
|
||||||
|
{
|
||||||
|
this->WriteDouble(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if<is_enum<T> >::type
|
||||||
|
Write(const T& value)
|
||||||
|
{
|
||||||
|
this->WriteInt(static_cast<int>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if<is_type_alias<T> >::type
|
||||||
|
Write(const T& value)
|
||||||
|
{
|
||||||
|
Write(get_aliased_value(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteNull()
|
||||||
|
{
|
||||||
|
_output.Write("null", 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteSeparator(const int per_line = 1)
|
||||||
|
{
|
||||||
|
if (_count)
|
||||||
|
_output.Write(", ", _pretty ? 2 : 1);
|
||||||
|
|
||||||
|
if (_count++ % per_line == 0)
|
||||||
|
NewLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
using rapidjson::Writer<detail::RapidJsonOutputStream<BufferT> >::WriteString;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if<is_string<T> >::type
|
||||||
|
WriteString(const T& value)
|
||||||
|
{
|
||||||
|
WriteString(string_data(value), string_length(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if<is_wstring<T> >::type
|
||||||
|
WriteString(const T& value)
|
||||||
|
{
|
||||||
|
_output.Write('\"');
|
||||||
|
for (const wchar_t *p = string_data(value), *end = p + string_length(value); p < end; ++p)
|
||||||
|
{
|
||||||
|
wchar_t c = *p;
|
||||||
|
|
||||||
|
if (c < L'\x20' || c == '"' || c == '\\' || c == '/')
|
||||||
|
{
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case L'\b': c = L'b'; break;
|
||||||
|
case L'\f': c = L'f'; break;
|
||||||
|
case L'\n': c = L'n'; break;
|
||||||
|
case L'\r': c = L'r'; break;
|
||||||
|
case L'\t': c = L't'; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c >= L'\x20')
|
||||||
|
_output.Write('\\');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c >= L'\x20' && c < L'\x80')
|
||||||
|
{
|
||||||
|
_output.Write(static_cast<char>(c));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WriteUnicode(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_output.Write('\"');
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteUnicode(wchar_t c)
|
||||||
|
{
|
||||||
|
char u[6] = "\\u";
|
||||||
|
u[2] = detail::HexDigit(c >> 12);
|
||||||
|
u[3] = detail::HexDigit(c >> 8);
|
||||||
|
u[4] = detail::HexDigit(c >> 4);
|
||||||
|
u[5] = detail::HexDigit(c >> 0);
|
||||||
|
_output.Write(u, sizeof(u));
|
||||||
|
}
|
||||||
|
|
||||||
|
void NewLine()
|
||||||
|
{
|
||||||
|
if (!_pretty)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_output.Write('\n');
|
||||||
|
for (int i = _level; i--;)
|
||||||
|
_output.Write(" ", _indent);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Writer>
|
||||||
|
friend class Serializer;
|
||||||
|
|
||||||
|
detail::RapidJsonOutputStream<BufferT> _stream;
|
||||||
|
Buffer& _output;
|
||||||
|
int _count;
|
||||||
|
int _level;
|
||||||
|
const int _indent;
|
||||||
|
const bool _pretty;
|
||||||
|
const bool _all_fields;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Buffer> struct
|
||||||
|
is_writer<SimpleJsonWriter<Buffer>, void>
|
||||||
|
: true_type {};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Buffer>
|
||||||
|
class Serializer<SimpleJsonWriter<Buffer> >
|
||||||
|
: public SerializingTransform
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef SimpleJsonWriter<Buffer> writer_type;
|
||||||
|
|
||||||
|
Serializer(writer_type& writer)
|
||||||
|
: _output(writer),
|
||||||
|
_level(0)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void Begin(const Metadata& /*metadata*/) const
|
||||||
|
{
|
||||||
|
if (!_level++)
|
||||||
|
_output.WriteOpen('{');
|
||||||
|
}
|
||||||
|
|
||||||
|
void End() const
|
||||||
|
{
|
||||||
|
WriteEnd(--_level != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UnknownEnd() const
|
||||||
|
{
|
||||||
|
WriteEnd(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool Base(const T& value) const
|
||||||
|
{
|
||||||
|
Apply(*this, value);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool Field(uint16_t /*id*/, const Metadata& metadata, const maybe<T>& value) const
|
||||||
|
{
|
||||||
|
if (!value.is_nothing())
|
||||||
|
{
|
||||||
|
WriteName(detail::FieldName(metadata));
|
||||||
|
Write(value.value());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool Field(uint16_t /*id*/, const Metadata& metadata, const T& value) const
|
||||||
|
{
|
||||||
|
if (_output._all_fields
|
||||||
|
|| !detail::omit_field<writer_type>(metadata, value))
|
||||||
|
{
|
||||||
|
WriteName(detail::FieldName(metadata));
|
||||||
|
Write(value);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool UnknownField(uint16_t id, const T& value) const
|
||||||
|
{
|
||||||
|
WriteName(id);
|
||||||
|
Write(value);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OmittedField(uint16_t id, const Metadata& metadata, BondDataType type) const
|
||||||
|
{
|
||||||
|
if (_output._all_fields && !metadata.default_value.nothing)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case BT_BOOL:
|
||||||
|
Field(id, metadata, !!metadata.default_value.uint_value);
|
||||||
|
break;
|
||||||
|
case BT_UINT8:
|
||||||
|
case BT_UINT16:
|
||||||
|
case BT_UINT32:
|
||||||
|
case BT_UINT64:
|
||||||
|
Field(id, metadata, metadata.default_value.uint_value);
|
||||||
|
break;
|
||||||
|
case BT_FLOAT:
|
||||||
|
case BT_DOUBLE:
|
||||||
|
Field(id, metadata, metadata.default_value.double_value);
|
||||||
|
break;
|
||||||
|
case BT_STRING:
|
||||||
|
Field(id, metadata, metadata.default_value.string_value);
|
||||||
|
break;
|
||||||
|
case BT_STRUCT:
|
||||||
|
BOOST_ASSERT(false);
|
||||||
|
break;
|
||||||
|
case BT_LIST:
|
||||||
|
case BT_SET:
|
||||||
|
WriteName(detail::FieldName(metadata));
|
||||||
|
_output.WriteOpen('[');
|
||||||
|
_output.WriteClose(']');
|
||||||
|
break;
|
||||||
|
case BT_MAP:
|
||||||
|
WriteName(detail::FieldName(metadata));
|
||||||
|
_output.WriteOpen('{');
|
||||||
|
_output.WriteClose('}');
|
||||||
|
break;
|
||||||
|
case BT_INT8:
|
||||||
|
case BT_INT16:
|
||||||
|
case BT_INT32:
|
||||||
|
case BT_INT64:
|
||||||
|
Field(id, metadata, metadata.default_value.int_value);
|
||||||
|
break;
|
||||||
|
case BT_WSTRING:
|
||||||
|
Field(id, metadata, metadata.default_value.wstring_value);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
BOOST_ASSERT(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename Reader>
|
||||||
|
void Container(const value<T, Reader>& element, uint32_t size) const
|
||||||
|
{
|
||||||
|
_output.WriteOpen('[');
|
||||||
|
|
||||||
|
while (size--)
|
||||||
|
{
|
||||||
|
_output.WriteSeparator();
|
||||||
|
Write(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
_output.WriteClose(']');
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Key, typename T, typename Reader>
|
||||||
|
void Container(const value<Key, Reader>& key, const T& value, uint32_t size) const
|
||||||
|
{
|
||||||
|
_output.WriteOpen('[');
|
||||||
|
|
||||||
|
while (size--)
|
||||||
|
{
|
||||||
|
_output.WriteSeparator();
|
||||||
|
Write(key);
|
||||||
|
_output.WriteSeparator();
|
||||||
|
Write(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
_output.WriteClose(']');
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void WriteEnd(bool base) const
|
||||||
|
{
|
||||||
|
if (!base)
|
||||||
|
_output.WriteClose('}');
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void WriteName(const T& name) const
|
||||||
|
{
|
||||||
|
_output.WriteSeparator();
|
||||||
|
_output.WriteName(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// basic, non-enum type value
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if<is_basic_type<T> >::type
|
||||||
|
Write(const T& value) const
|
||||||
|
{
|
||||||
|
_output.Write(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// nullable<T> value
|
||||||
|
template <typename T>
|
||||||
|
void Write(const nullable<T>& value) const
|
||||||
|
{
|
||||||
|
if (!value)
|
||||||
|
{
|
||||||
|
_output.WriteNull();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_output.WriteOpen('[');
|
||||||
|
Write(value.value());
|
||||||
|
_output.WriteClose(']');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// struct or bonded<T>
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if<is_bond_type<T> >::type
|
||||||
|
Write(const T& value) const
|
||||||
|
{
|
||||||
|
Apply(SerializeTo(_output), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2-tuple
|
||||||
|
template <typename T1, typename T2>
|
||||||
|
void Write(const std::pair<T1, T2>& value) const
|
||||||
|
{
|
||||||
|
Write(value.first);
|
||||||
|
_output.WriteSeparator(is_basic_type<T2>::value ? 2 : 1);
|
||||||
|
Write(value.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
// container value
|
||||||
|
template <typename T>
|
||||||
|
typename boost::enable_if<is_container<T> >::type
|
||||||
|
Write(const T& value) const
|
||||||
|
{
|
||||||
|
_output.WriteOpen('[');
|
||||||
|
|
||||||
|
for (const_enumerator<T> elements(value); elements.more();)
|
||||||
|
{
|
||||||
|
_output.WriteSeparator();
|
||||||
|
Write(elements.next());
|
||||||
|
}
|
||||||
|
|
||||||
|
_output.WriteClose(']');
|
||||||
|
}
|
||||||
|
|
||||||
|
// blob
|
||||||
|
void Write(const blob& value) const
|
||||||
|
{
|
||||||
|
_output.WriteOpen('[');
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < value.size(); ++i)
|
||||||
|
{
|
||||||
|
_output.WriteSeparator();
|
||||||
|
_output.Write(static_cast<int8_t>(value.content()[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
_output.WriteClose(']');
|
||||||
|
}
|
||||||
|
|
||||||
|
// serialized value
|
||||||
|
template <typename Reader, typename T>
|
||||||
|
typename boost::enable_if<is_basic_type<T> >::type
|
||||||
|
Write(const value<T, Reader>& value) const
|
||||||
|
{
|
||||||
|
T data;
|
||||||
|
|
||||||
|
value.Deserialize(data);
|
||||||
|
_output.Write(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Reader, typename T>
|
||||||
|
typename boost::disable_if<is_basic_type<T> >::type
|
||||||
|
Write(const value<T, Reader>& value) const
|
||||||
|
{
|
||||||
|
Apply(SerializeTo(_output), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
writer_type& _output;
|
||||||
|
mutable uint32_t _level;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace bond
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче