From edf6bdc62c009c859b6bcd0e9fd44cf340c3444e Mon Sep 17 00:00:00 2001 From: Ara Ayvazyan Date: Fri, 17 Nov 2017 11:30:05 -0800 Subject: [PATCH] [c++] Reduce included headers for Simple JSON This change introduces a `BOND_LIB_TYPE` macro and moves some utility functions used by the Simple JSON protocol to corresponding source file which reduces the number of included files by over 240. The `BOND_LIB_TYPE` macro will allow: - to keep some expensive-to-compile headers away from public ones - pre-compile some common parts of the library --- CMakeLists.txt | 1 + cmake/Config.cmake | 2 +- cpp/CMakeLists.txt | 12 ++++++ cpp/inc/bond/core/config.h | 21 ++++++++++ .../bond/protocol/detail/rapidjson_helper.h | 30 +++++--------- .../bond/protocol/detail/rapidjson_utils.h | 31 +++++++++++++++ .../protocol/detail/rapidjson_utils_impl.h | 39 +++++++++++++++++++ .../bond/protocol/simple_json_reader_impl.h | 3 +- .../bond/protocol/detail/rapidjson_utils.cpp | 11 ++++++ cpp/test/core/CMakeLists.txt | 7 ++-- doc/src/bond_cpp.md | 4 ++ 11 files changed, 134 insertions(+), 27 deletions(-) create mode 100644 cpp/inc/bond/protocol/detail/rapidjson_utils.h create mode 100644 cpp/inc/bond/protocol/detail/rapidjson_utils_impl.h create mode 100644 cpp/src/bond/protocol/detail/rapidjson_utils.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index bae389b1..2135f360 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,6 +22,7 @@ set (BOND_IDL ${CMAKE_CURRENT_SOURCE_DIR}/idl) 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_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/cpp/src) set (BOND_COMPAT_TEST_DIR ${CMAKE_CURRENT_SOURCE_DIR}/test/compat) include (Config) diff --git a/cmake/Config.cmake b/cmake/Config.cmake index 89231cc9..b5aff720 100644 --- a/cmake/Config.cmake +++ b/cmake/Config.cmake @@ -203,7 +203,7 @@ set (BOND_LIBRARIES_ONLY set (BOND_LIBRARIES_INSTALL_CPP "FALSE" - CACHE BOOL "If TRUE, the generated .cpp files for the Bond libraries will be installed under src/ as part of the INSTALL target.") + CACHE BOOL "If TRUE, the .cpp files for the Bond libraries will be installed under src/ as part of the INSTALL target.") set (BOND_ENABLE_COMM "FALSE" diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 2f14b0cc..327f1716 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -54,6 +54,10 @@ set (generated_files_apply) list (APPEND generated_files_apply ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/bond/core/bond_apply.cpp) +set (precompiled_sources) +list (APPEND precompiled_sources + "src/bond/protocol/detail/rapidjson_utils.cpp") + list (APPEND headers ${core_headers} ${core_detail_headers} @@ -140,8 +144,11 @@ add_library (bond STATIC ${schemas} ${generated_files_types} + ${precompiled_sources} ${headers}) +target_compile_definitions (bond PUBLIC -DBOND_LIB_TYPE=BOND_LIB_TYPE_STATIC) + # 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 @@ -177,6 +184,11 @@ if (BOND_LIBRARIES_INSTALL_CPP) DIRECTORY ${BOND_GENERATED}/bond/ DESTINATION src/bond/generated FILES_MATCHING PATTERN "*.cpp") + + install ( + DIRECTORY ${BOND_SOURCE_DIR}/ + DESTINATION src/ + FILES_MATCHING PATTERN "*.cpp") endif() add_subdirectory (test) diff --git a/cpp/inc/bond/core/config.h b/cpp/inc/bond/core/config.h index f64eeb6b..01792df3 100644 --- a/cpp/inc/bond/core/config.h +++ b/cpp/inc/bond/core/config.h @@ -40,3 +40,24 @@ #define BOND_CONSTEXPR BOOST_CONSTEXPR #define BOND_CONSTEXPR_OR_CONST BOOST_CONSTEXPR_OR_CONST #define BOND_STATIC_CONSTEXPR BOOST_STATIC_CONSTEXPR + + +#define BOND_LIB_TYPE_HEADER 1 +#define BOND_LIB_TYPE_STATIC 2 +#define BOND_LIB_TYPE_DYNAMIC 3 // Not implemented + +#ifndef BOND_LIB_TYPE +#define BOND_LIB_TYPE BOND_LIB_TYPE_HEADER +#elif (BOND_LIB_TYPE != BOND_LIB_TYPE_HEADER) && (BOND_LIB_TYPE != BOND_LIB_TYPE_STATIC) +#error Unsupported library type is defined for BOND_LIB_TYPE +#endif + +#ifndef BOND_DETAIL_HEADER_ONLY_INLINE +#if BOND_LIB_TYPE == BOND_LIB_TYPE_HEADER +#define BOND_DETAIL_HEADER_ONLY_INLINE inline +#else +#define BOND_DETAIL_HEADER_ONLY_INLINE +#endif +#else +#error BOND_DETAIL_HEADER_ONLY_INLINE is already defined +#endif diff --git a/cpp/inc/bond/protocol/detail/rapidjson_helper.h b/cpp/inc/bond/protocol/detail/rapidjson_helper.h index cb171ca1..a434ab9d 100644 --- a/cpp/inc/bond/protocol/detail/rapidjson_helper.h +++ b/cpp/inc/bond/protocol/detail/rapidjson_helper.h @@ -9,13 +9,12 @@ #include #include -#include #include #include -#include #include "rapidjson/rapidjson.h" #include "rapidjson/error/en.h" +#include "rapidjson_utils.h" // rapidjson/document.h v1.1 uses std::min/max in ways that conflict // with macros defined in windows. This works around the issue. @@ -279,26 +278,17 @@ template typename boost::enable_if >::type Read(const rapidjson::Value& value, T& var) { - try - { - const std::basic_string str = - boost::locale::conv::utf_to_utf( - value.GetString(), - value.GetString() + value.GetStringLength(), - boost::locale::conv::stop); + const std::basic_string str = utf_to_utf( + value.GetString(), + value.GetString() + value.GetStringLength()); - const size_t length = str.size(); - resize_string(var, static_cast(length)); + const size_t length = str.size(); + resize_string(var, static_cast(length)); - std::copy( - str.begin(), - str.end(), - make_checked_array_iterator(string_data(var), length)); - } - catch (const boost::locale::conv::conversion_error &) - { - UnicodeConversionException(); - } + std::copy( + str.begin(), + str.end(), + make_checked_array_iterator(string_data(var), length)); } diff --git a/cpp/inc/bond/protocol/detail/rapidjson_utils.h b/cpp/inc/bond/protocol/detail/rapidjson_utils.h new file mode 100644 index 00000000..135d0c05 --- /dev/null +++ b/cpp/inc/bond/protocol/detail/rapidjson_utils.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 +#include + + +namespace bond +{ +namespace detail +{ + +std::basic_string utf_to_utf(const char* begin, const char* end); + +bool try_lexical_convert(const char* str, uint16_t& result); + + +} // namespace detail + +} // namespace bond + + +#ifdef BOND_LIB_TYPE +#if BOND_LIB_TYPE == BOND_LIB_TYPE_HEADER +#include "rapidjson_utils_impl.h" +#endif +#else +#error BOND_LIB_TYPE is undefined +#endif diff --git a/cpp/inc/bond/protocol/detail/rapidjson_utils_impl.h b/cpp/inc/bond/protocol/detail/rapidjson_utils_impl.h new file mode 100644 index 00000000..0ff0b462 --- /dev/null +++ b/cpp/inc/bond/protocol/detail/rapidjson_utils_impl.h @@ -0,0 +1,39 @@ +// 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 +#include +#include + + +namespace bond +{ +namespace detail +{ + +BOND_DETAIL_HEADER_ONLY_INLINE +std::basic_string utf_to_utf(const char* begin, const char* end) +{ + try + { + return boost::locale::conv::utf_to_utf(begin, end, boost::locale::conv::stop); + } + catch (const boost::locale::conv::conversion_error&) + { + UnicodeConversionException(); + } +} + + +BOND_DETAIL_HEADER_ONLY_INLINE +bool try_lexical_convert(const char* str, uint16_t& result) +{ + return boost::conversion::try_lexical_convert(str, result); +} + + +} // namespace detail + +} // namespace bond diff --git a/cpp/inc/bond/protocol/simple_json_reader_impl.h b/cpp/inc/bond/protocol/simple_json_reader_impl.h index 13418873..f0bfc5f3 100644 --- a/cpp/inc/bond/protocol/simple_json_reader_impl.h +++ b/cpp/inc/bond/protocol/simple_json_reader_impl.h @@ -4,7 +4,6 @@ #pragma once #include "simple_json_reader.h" -#include "boost/lexical_cast.hpp" namespace bond { @@ -32,7 +31,7 @@ SimpleJsonReader::FindField(uint16_t id, const Metadata& metadata, Bond } uint16_t parsedId; - if (boost::conversion::try_lexical_convert(it->name.GetString(), parsedId) && id == parsedId) + if (detail::try_lexical_convert(it->name.GetString(), parsedId) && id == parsedId) { // string id match return &it->value; diff --git a/cpp/src/bond/protocol/detail/rapidjson_utils.cpp b/cpp/src/bond/protocol/detail/rapidjson_utils.cpp new file mode 100644 index 00000000..511fcbb4 --- /dev/null +++ b/cpp/src/bond/protocol/detail/rapidjson_utils.cpp @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include + +#if BOND_LIB_TYPE == BOND_LIB_TYPE_HEADER +#error This source file should not be compiled for BOND_LIB_TYPE_HEADER +#endif + +#include +#include diff --git a/cpp/test/core/CMakeLists.txt b/cpp/test/core/CMakeLists.txt index 23e28c22..5b4532cf 100644 --- a/cpp/test/core/CMakeLists.txt +++ b/cpp/test/core/CMakeLists.txt @@ -23,9 +23,7 @@ function (add_unit_test) -DBOND_FAST_BINARY_PROTOCOL -DBOND_SIMPLE_JSON_PROTOCOL) target_link_libraries (${name} PRIVATE - bond - core_test_common - ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) + core_test_common) endfunction() @@ -61,7 +59,8 @@ target_compile_definitions (core_test_common PUBLIC -DBOND_SIMPLE_BINARY_PROTOCOL -DBOND_FAST_BINARY_PROTOCOL -DBOND_SIMPLE_JSON_PROTOCOL) -target_link_libraries (core_test_common PRIVATE +target_link_libraries (core_test_common PUBLIC + bond ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) diff --git a/doc/src/bond_cpp.md b/doc/src/bond_cpp.md index 3431b9cc..1b64983d 100644 --- a/doc/src/bond_cpp.md +++ b/doc/src/bond_cpp.md @@ -1775,6 +1775,10 @@ to reuse the pre-instantiated code. The one exception are applications using custom protocols - by definition templates pre-instantiated at the time Bond library was built can't support custom protocols. +If header-only consumption of the library is not required, then defining the +`BOND_LIB_TYPE` macro to `BOND_LIB_TYPE_STATIC` will pre-compile more common +code into the `bond.lib`/`libbond.a` library and will reduce the number of included headers. + When using the Microsoft Visual Studio toolchain there are two important things to be aware related to build time. First, always use 64-bit tools. In particular the 32-bit version of link.exe is not capable of linking large