This commit is contained in:
Adam Sapek 2015-01-08 20:31:09 -08:00
Коммит 222e20fd00
544 изменённых файлов: 73888 добавлений и 0 удалений

3
.gitignore поставляемый Normal file
Просмотреть файл

@ -0,0 +1,3 @@
# Vim
*.swp
*.swo

3
.gitmodules поставляемый Normal file
Просмотреть файл

@ -0,0 +1,3 @@
[submodule "thirdparty/rapidjson"]
path = thirdparty/rapidjson
url = https://github.com/miloyip/rapidjson.git

40
CMakeLists.txt Normal file
Просмотреть файл

@ -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)

22
LICENSE Normal file
Просмотреть файл

@ -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.

161
README.md Normal file
Просмотреть файл

@ -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

140
cmake/Bond.cmake Normal file
Просмотреть файл

@ -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()

23
cmake/Compiler.cmake Normal file
Просмотреть файл

@ -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()

44
cmake/Config.cmake Normal file
Просмотреть файл

@ -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)

13
cmake/FindCabal.cmake Normal file
Просмотреть файл

@ -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)

13
cmake/FindGHC.cmake Normal file
Просмотреть файл

@ -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)

38
cmake/HaskellUtil.cmake Normal file
Просмотреть файл

@ -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()

12
cmake/NoDebug.cmake Normal file
Просмотреть файл

@ -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()

29
cmake/PythonTest.cmake Normal file
Просмотреть файл

@ -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()

4
compiler/.gitignore поставляемый Normal file
Просмотреть файл

@ -0,0 +1,4 @@
# Cabal build artifacts
dist
.cabal-sandbox
cabal.sandbox.config

146
compiler/Bond/Lexer.hs Normal file
Просмотреть файл

@ -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

312
compiler/Bond/Parser.hs Normal file
Просмотреть файл

@ -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_

272
compiler/Bond/Schema.hs Normal file
Просмотреть файл

@ -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 />
//------------------------------------------------------------------------------
|]

56
compiler/Bond/Util.hs Normal file
Просмотреть файл

@ -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

10
compiler/Bond/Version.hs Normal file
Просмотреть файл

@ -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"

68
compiler/CMakeLists.txt Normal file
Просмотреть файл

@ -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()

108
compiler/Main.hs Normal file
Просмотреть файл

@ -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

74
compiler/Options.hs Normal file
Просмотреть файл

@ -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

1
compiler/cabal.config Normal file
Просмотреть файл

@ -0,0 +1 @@
require-sandbox: True

61
compiler/gbc.cabal Normal file
Просмотреть файл

@ -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

1
cpp/.gitignore поставляемый Normal file
Просмотреть файл

@ -0,0 +1 @@
generated

75
cpp/CMakeLists.txt Normal file
Просмотреть файл

@ -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)

81
cpp/inc/bond/core/apply.h Normal file
Просмотреть файл

@ -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)

385
cpp/inc/bond/core/blob.h Normal file
Просмотреть файл

@ -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

126
cpp/inc/bond/core/bond.bond Normal file
Просмотреть файл

@ -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;
}

128
cpp/inc/bond/core/bond.h Normal file
Просмотреть файл

@ -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;
};
}

279
cpp/inc/bond/core/bonded.h Normal file
Просмотреть файл

@ -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

151
cpp/inc/bond/core/maybe.h Normal file
Просмотреть файл

@ -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

157
cpp/inc/bond/core/merge.h Normal file
Просмотреть файл

@ -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

43
cpp/inc/bond/core/null.h Normal file
Просмотреть файл

@ -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

652
cpp/inc/bond/core/parser.h Normal file
Просмотреть файл

@ -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
}

355
cpp/inc/bond/core/schema.h Normal file
Просмотреть файл

@ -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 cant
// 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;
}
}

133
cpp/inc/bond/core/traits.h Normal file
Просмотреть файл

@ -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;
};
}

69
cpp/inc/bond/core/tuple.h Normal file
Просмотреть файл

@ -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

1006
cpp/inc/bond/core/value.h Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -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 Marsaglias 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

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше