├── .clang-format ├── .github └── workflows │ ├── catkin_build.yml │ └── cmake_build.yml ├── .gitignore ├── .pre-commit-config.yaml ├── LICENSE ├── README.md ├── config_utilities ├── CMakeLists.txt ├── app │ └── composite_configs.cpp ├── cmake │ ├── HandleInstall.cmake │ ├── OptionalPackage.cmake │ └── config_utilitiesConfig.cmake.in ├── demos │ ├── CMakeLists.txt │ ├── demo_config.cpp │ ├── demo_factory.cpp │ ├── demo_inheritance.cpp │ ├── demo_ros.cpp │ ├── demo_ros.launch │ └── resources │ │ ├── factory.yaml │ │ ├── inheritance.yaml │ │ ├── invalid_params.yaml │ │ └── params.yaml ├── include │ └── config_utilities │ │ ├── config.h │ │ ├── config_utilities.h │ │ ├── external_registry.h │ │ ├── factory.h │ │ ├── formatting │ │ └── asl.h │ │ ├── getters.h │ │ ├── globals.h │ │ ├── internal │ │ ├── checks.h │ │ ├── config_context.h │ │ ├── formatter.h │ │ ├── logger.h │ │ ├── meta_data.h │ │ ├── namespacing.h │ │ ├── string_utils.h │ │ ├── visitor.h │ │ ├── visitor_impl.hpp │ │ ├── yaml_parser.h │ │ └── yaml_utils.h │ │ ├── logging │ │ ├── log_to_glog.h │ │ ├── log_to_ros.h │ │ └── log_to_stdout.h │ │ ├── parsing │ │ ├── commandline.h │ │ ├── context.h │ │ ├── ros.h │ │ └── yaml.h │ │ ├── printing.h │ │ ├── settings.h │ │ ├── substitution_parsers.h │ │ ├── substitutions.h │ │ ├── traits.h │ │ ├── types │ │ ├── conversions.h │ │ ├── eigen_matrix.h │ │ ├── enum.h │ │ └── path.h │ │ ├── update.h │ │ ├── validation.h │ │ └── virtual_config.h ├── package.xml ├── scripts │ └── run_demo.py ├── src │ ├── asl_formatter.cpp │ ├── commandline.cpp │ ├── config_context.cpp │ ├── context.cpp │ ├── conversions.cpp │ ├── external_registry.cpp │ ├── factory.cpp │ ├── formatter.cpp │ ├── log_to_stdout.cpp │ ├── logger.cpp │ ├── meta_data.cpp │ ├── namespacing.cpp │ ├── path.cpp │ ├── settings.cpp │ ├── string_utils.cpp │ ├── substitution_parsers.cpp │ ├── substitutions.cpp │ ├── validation.cpp │ ├── visitor.cpp │ ├── yaml_parser.cpp │ └── yaml_utils.cpp └── test │ ├── CMakeLists.txt │ ├── include │ └── config_utilities │ │ └── test │ │ ├── default_config.h │ │ ├── external_types.h │ │ └── utils.h │ ├── main.cpp │ ├── resources │ ├── bar.yaml │ ├── foo.yaml │ └── invalid.yaml │ ├── src │ ├── default_config.cpp │ ├── external_plugins.cpp │ └── utils.cpp │ └── tests │ ├── asl_formatter.cpp │ ├── commandline.cpp │ ├── config_arrays.cpp │ ├── config_maps.cpp │ ├── conversions.cpp │ ├── enums.cpp │ ├── external_registry.cpp │ ├── factory.cpp │ ├── getters.cpp │ ├── inheritance.cpp │ ├── missing_fields.cpp │ ├── namespacing.cpp │ ├── path.cpp │ ├── string_utils.cpp │ ├── subconfigs.cpp │ ├── substitutions.cpp │ ├── traits.cpp │ ├── update.cpp │ ├── validity_checks.cpp │ ├── virtual_config.cpp │ ├── yaml_parsing.cpp │ └── yaml_utils.cpp └── docs ├── Advanced.md ├── Compositing.md ├── Configs.md ├── External.md ├── Factories.md ├── Headers.md ├── Parsing.md ├── README.md ├── Types.md └── Varia.md /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | BasedOnStyle: Google 4 | AccessModifierOffset: -1 5 | AlignAfterOpenBracket: Align 6 | AlignConsecutiveAssignments: false 7 | AlignConsecutiveDeclarations: false 8 | AlignOperands: true 9 | AlignTrailingComments: true 10 | AllowAllParametersOfDeclarationOnNextLine: false 11 | AllowShortBlocksOnASingleLine: false 12 | AllowShortCaseLabelsOnASingleLine: false 13 | AllowShortFunctionsOnASingleLine: All 14 | AllowShortIfStatementsOnASingleLine: true 15 | AllowShortLoopsOnASingleLine: true 16 | AlwaysBreakAfterDefinitionReturnType: None 17 | AlwaysBreakAfterReturnType: None 18 | AlwaysBreakBeforeMultilineStrings: true 19 | AlwaysBreakTemplateDeclarations: true 20 | BinPackArguments: false 21 | BinPackParameters: false 22 | BraceWrapping: 23 | AfterClass: false 24 | AfterControlStatement: false 25 | AfterEnum: false 26 | AfterFunction: false 27 | AfterNamespace: false 28 | AfterObjCDeclaration: false 29 | AfterStruct: false 30 | AfterUnion: false 31 | BeforeCatch: false 32 | BeforeElse: false 33 | IndentBraces: false 34 | BreakBeforeBinaryOperators: None 35 | BreakBeforeBraces: Attach 36 | BreakBeforeTernaryOperators: true 37 | BreakConstructorInitializersBeforeComma: false 38 | CommentPragmas: "^ IWYU pragma:" 39 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 40 | ConstructorInitializerIndentWidth: 4 41 | ContinuationIndentWidth: 4 42 | Cpp11BracedListStyle: true 43 | DerivePointerAlignment: true 44 | DisableFormat: false 45 | ExperimentalAutoDetectBinPacking: false 46 | ForEachMacros: 47 | - foreach 48 | - Q_FOREACH 49 | - BOOST_FOREACH 50 | IncludeCategories: 51 | # Spacers 52 | - Regex: "^$" 53 | Priority: 15 54 | - Regex: "^$" 55 | Priority: 25 56 | - Regex: "^$" 57 | Priority: 35 58 | - Regex: "^$" 59 | Priority: 45 60 | # C system headers 61 | - Regex: '^[<"](aio|arpa/inet|assert|complex|cpio|ctype|curses|dirent|dlfcn|errno|fcntl|fenv|float|fmtmsg|fnmatch|ftw|glob|grp|iconv|inttypes|iso646|langinfo|libgen|limits|locale|math|monetary|mqueue|ndbm|netdb|net/if|netinet/in|netinet/tcp|nl_types|poll|pthread|pwd|regex|sched|search|semaphore|setjmp|signal|spawn|stdalign|stdarg|stdatomic|stdbool|stddef|stdint|stdio|stdlib|stdnoreturn|string|strings|stropts|sys/ipc|syslog|sys/mman|sys/msg|sys/resource|sys/select|sys/sem|sys/shm|sys/socket|sys/stat|sys/statvfs|sys/time|sys/times|sys/types|sys/uio|sys/un|sys/utsname|sys/wait|tar|term|termios|tgmath|threads|time|trace|uchar|ulimit|uncntrl|unistd|utime|utmpx|wchar|wctype|wordexp)\.h[">]$' 62 | Priority: 10 63 | # C++ system headers 64 | - Regex: '^[<"](algorithm|any|array|atomic|bitset|cassert|ccomplex|cctype|cerrno|cfenv|cfloat|charconv|chrono|cinttypes|ciso646|climits|clocale|cmath|codecvt|complex|condition_variable|csetjmp|csignal|cstdalign|cstdarg|cstdbool|cstddef|cstdint|cstdio|cstdlib|cstring|ctgmath|ctime|cuchar|cwchar|cwctype|deque|exception|execution|filesystem|forward_list|fstream|functional|future|initializer_list|iomanip|ios|iosfwd|iostream|istream|iterator|limits|list|locale|map|memory|memory_resource|mutex|new|numeric|optional|ostream|queue|random|ratio|regex|scoped_allocator|set|shared_mutex|sstream|stack|stdexcept|streambuf|string|string_view|strstream|system_error|thread|tuple|type_traits|typeindex|typeinfo|unordered_map|unordered_set|utility|valarray|variant|vector)[">]$' 65 | Priority: 20 66 | # Other library h files (with angles) 67 | - Regex: "^<" 68 | Priority: 30 69 | # Your project's h files (with angles) 70 | - Regex: "^ 54 | $) 55 | target_compile_options(${PROJECT_NAME} PRIVATE -Wall) 56 | set_property(TARGET ${PROJECT_NAME} PROPERTY POSITION_INDEPENDENT_CODE 1) 57 | add_library(config_utilities::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) 58 | 59 | add_executable(composite-configs app/composite_configs.cpp) 60 | target_link_libraries(composite-configs ${PROJECT_NAME}) 61 | 62 | if(ENABLE_roscpp) 63 | target_link_libraries(${PROJECT_NAME} PUBLIC ${roscpp_LIBRARIES}) 64 | target_include_directories(${PROJECT_NAME} PUBLIC ${roscpp_INCLUDE_DIRS}) 65 | endif() 66 | 67 | if(ENABLE_Eigen3) 68 | target_link_libraries(${PROJECT_NAME} PUBLIC Eigen3::Eigen) 69 | endif() 70 | 71 | if(ENABLE_libglog) 72 | target_link_libraries(${PROJECT_NAME} PUBLIC PkgConfig::libglog) 73 | endif() 74 | 75 | message(STATUS "Eigen features enabled: ${ENABLE_Eigen3}") 76 | message(STATUS "ROS features enabled: ${ENABLE_roscpp}") 77 | message(STATUS "glog features enabled: ${ENABLE_libglog}") 78 | 79 | if(CONFIG_UTILS_BUILD_DEMOS) 80 | add_subdirectory(demos) 81 | endif() 82 | 83 | if(CONFIG_UTILS_BUILD_TESTS) 84 | include(CTest) 85 | enable_testing() 86 | add_subdirectory(test) 87 | endif() 88 | 89 | include(HandleInstall) 90 | -------------------------------------------------------------------------------- /config_utilities/app/composite_configs.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | const std::string help_msg = 6 | R"""(Usage: composite-config [--config-utilities-yaml YAML_TOKEN ...]... [--config-utilities-file FILEPATH[@NAMESPACE]]... 7 | 8 | Merges the input YAML values from left to right and outputs the resulting composite YAML to stdout. 9 | Invalid YAML or missing files get dropped during compositing. 10 | 11 | Options: 12 | -h/--help: Show this message. 13 | -c/--config-utilities-yaml: Takes an arbitrary set of tokens that form a valid YAML string. 14 | Spaces are not required to be escaped, so `--config-utilities foo: value` 15 | is parsed the same as `--config-utilities 'foo: value'`. Can be specified 16 | multiple times. 17 | -f/--config-utilities-file: Takes a filepath to YAML to load and composite. The YAML can optionally 18 | be namespaced by `@NAMESPACE` where 'FILE@a/b' maps to 19 | '{a: {b: FILE_CONTENTS}}'. Can be specified multiple times. 20 | -v/--config-utilities-var: Takes a KEY=VALUE pair to add to the substitution context. Can be specified 21 | multiple times. 22 | -d/--disable-substitutions: Turn off substitutions resolution 23 | --no-disable-substitutions: Turn substitution resolution on (currently on by default) 24 | 25 | Example: 26 | > echo "{a: 42, bar: hello}" > /tmp/test_in.yaml 27 | > composite-configs --config-utilities-yaml "{foo: {bar: value, b: -1.0, c: $}}" --config-utilities-file /tmp/test_in.yaml@foo -v other=42 28 | {foo: {bar: hello, b: -1.0, c: 42, a: 42}} 29 | 30 | See https://github.com/MIT-SPARK/config_utilities/blob/main/docs/Parsing.md#parse-from-the-command-line 31 | for more information. 32 | )"""; 33 | 34 | namespace { 35 | 36 | inline bool isContainer(YAML::Node node) { 37 | return node.Type() == YAML::NodeType::Sequence || node.Type() == YAML::NodeType::Map; 38 | } 39 | 40 | inline bool isComplexNode(YAML::Node node) { 41 | switch (node.Type()) { 42 | case YAML::NodeType::Sequence: 43 | for (const auto& child : node) { 44 | if (isContainer(child)) { 45 | return true; 46 | } 47 | } 48 | return false; 49 | case YAML::NodeType::Map: 50 | for (const auto& child : node) { 51 | if (isContainer(child.second)) { 52 | return true; 53 | } 54 | } 55 | return false; 56 | case YAML::NodeType::Null: 57 | case YAML::NodeType::Undefined: 58 | case YAML::NodeType::Scalar: 59 | default: 60 | return false; 61 | } 62 | } 63 | 64 | inline void forceBlockForNonLeaves(YAML::Node node) { 65 | switch (node.Type()) { 66 | case YAML::NodeType::Sequence: 67 | if (isComplexNode(node)) { 68 | node.SetStyle(YAML::EmitterStyle::Block); 69 | } 70 | for (const auto& child : node) { 71 | forceBlockForNonLeaves(child); 72 | } 73 | break; 74 | case YAML::NodeType::Map: 75 | if (isComplexNode(node)) { 76 | node.SetStyle(YAML::EmitterStyle::Block); 77 | } 78 | for (const auto& child : node) { 79 | forceBlockForNonLeaves(child.second); 80 | } 81 | break; 82 | case YAML::NodeType::Null: 83 | case YAML::NodeType::Undefined: 84 | case YAML::NodeType::Scalar: 85 | default: 86 | return; 87 | } 88 | } 89 | 90 | } // namespace 91 | 92 | int main(int argc, char* argv[]) { 93 | config::internal::ParserInfo info; 94 | auto result = config::internal::loadFromArguments(argc, argv, false, &info); 95 | if (info.help_present) { 96 | std::cerr << help_msg << std::endl; 97 | return 1; 98 | } 99 | 100 | forceBlockForNonLeaves(result); 101 | 102 | YAML::Emitter emit; 103 | switch (result.Type()) { 104 | case YAML::NodeType::Null: 105 | case YAML::NodeType::Undefined: 106 | default: 107 | break; 108 | case YAML::NodeType::Scalar: 109 | case YAML::NodeType::Sequence: 110 | case YAML::NodeType::Map: 111 | emit << result; 112 | std::cout << std::string(emit.c_str()) << std::endl; 113 | break; 114 | } 115 | 116 | return 0; 117 | } 118 | -------------------------------------------------------------------------------- /config_utilities/cmake/HandleInstall.cmake: -------------------------------------------------------------------------------- 1 | include(GNUInstallDirs) 2 | install( 3 | TARGETS ${PROJECT_NAME} composite-configs 4 | EXPORT config_utilities-targets 5 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 6 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 7 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) 8 | install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) 9 | install( 10 | EXPORT config_utilities-targets 11 | FILE config_utilitiesTargets.cmake 12 | NAMESPACE config_utilities:: 13 | DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/config_utilities) 14 | 15 | include(CMakePackageConfigHelpers) 16 | configure_package_config_file( 17 | ${CMAKE_CURRENT_LIST_DIR}/config_utilitiesConfig.cmake.in 18 | ${CMAKE_CURRENT_BINARY_DIR}/config_utilitiesConfig.cmake 19 | INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/config_utilities) 20 | write_basic_package_version_file( 21 | config_utilitiesConfigVersion.cmake 22 | VERSION ${PACKAGE_VERSION} 23 | COMPATIBILITY AnyNewerVersion) 24 | install(FILES ${CMAKE_CURRENT_BINARY_DIR}/config_utilitiesConfig.cmake 25 | ${CMAKE_CURRENT_BINARY_DIR}/config_utilitiesConfigVersion.cmake 26 | DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/config_utilities) 27 | -------------------------------------------------------------------------------- /config_utilities/cmake/OptionalPackage.cmake: -------------------------------------------------------------------------------- 1 | # Finds an optional dependency gated on an enable flag 2 | # 3 | # Sets ENABLE_${package_name} to TRUE or FALSE depending on availability. All 4 | # results of find_package(package_name) are available if ENABLE_${package_name} 5 | # is TRUE 6 | # 7 | # Args: package_name: Name of the package to find package_enabled: Whether or 8 | # not the package should be used 9 | macro(FIND_OPTIONAL package_name package_enabled) 10 | if(${package_enabled}) 11 | find_package(${package_name} QUIET) 12 | endif() 13 | 14 | if(${${package_name}_FOUND}) 15 | set(ENABLE_${package_name} TRUE) 16 | else() 17 | set(ENABLE_${package_name} FALSE) 18 | if(${package_enabled}) 19 | message( 20 | WARNING 21 | "${package_name} not found. Either disable or install the package") 22 | endif() 23 | endif() 24 | endmacro() 25 | 26 | # Finds an optional dependency gated on an enable flag via PkgConfig. 27 | # 28 | # Sets ENABLE_${package_name} to TRUE or FALSE depending on availability. The 29 | # dependency will be available under PkgConfig::package_name 30 | # 31 | # Args: package_name: Name of the package to find package_enabled: Whether or 32 | # not the package should be used 33 | macro(FIND_OPTIONAL_PKGCFG package_name package_enabled) 34 | unset(${package_name}_FOUND CACHE) 35 | if(${package_enabled}) 36 | find_package(PkgConfig REQUIRED) 37 | pkg_check_modules(${package_name} IMPORTED_TARGET ${package_name}) 38 | endif() 39 | 40 | if(${${package_name}_FOUND}) 41 | set(ENABLE_${package_name} TRUE) 42 | else() 43 | set(ENABLE_${package_name} FALSE) 44 | if(${package_enabled}) 45 | message( 46 | WARNING 47 | "${package_name} not found. Either disable or install the package") 48 | endif() 49 | endif() 50 | endmacro() 51 | -------------------------------------------------------------------------------- /config_utilities/cmake/config_utilitiesConfig.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | get_filename_component(config_utilities_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" 4 | PATH) 5 | include(CMakeFindDependencyMacro) 6 | 7 | find_dependency(yaml-cpp REQUIRED) 8 | 9 | if(@ENABLE_Eigen3@) 10 | find_dependency(Eigen3 REQUIRED) 11 | endif() 12 | 13 | if(@ENABLE_roscpp@) 14 | find_dependency(roscpp REQUIRED) 15 | set(config_utilities_FOUND_CATKIN_PROJECT TRUE) 16 | endif() 17 | 18 | if(@ENABLE_rclcpp@) 19 | find_dependency(rclcpp REQUIRED) 20 | endif() 21 | 22 | if(@ENABLE_libglog@) 23 | find_dependency(PkgConfig REQUIRED) 24 | pkg_check_modules(libglog IMPORTED_TARGET libglog) 25 | endif() 26 | 27 | if(NOT TARGET config_utilities::config_utilities) 28 | include("${config_utilities_CMAKE_DIR}/config_utilitiesTargets.cmake") 29 | endif() 30 | 31 | set(config_utilities_LIBRARIES config_utilities::config_utilities) 32 | 33 | check_required_components(config_utilities) 34 | -------------------------------------------------------------------------------- /config_utilities/demos/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if(ENABLE_Eigen3) 2 | add_executable(demo_config demo_config.cpp) 3 | target_link_libraries(demo_config ${PROJECT_NAME}) 4 | endif() 5 | 6 | add_executable(demo_factory demo_factory.cpp) 7 | target_link_libraries(demo_factory ${PROJECT_NAME}) 8 | 9 | add_executable(demo_inheritance demo_inheritance.cpp) 10 | target_link_libraries(demo_inheritance ${PROJECT_NAME}) 11 | 12 | if(ENABLE_Eigen3 AND ENABLE_roscpp) 13 | add_executable(demo_ros demo_ros.cpp) 14 | target_link_libraries(demo_ros ${PROJECT_NAME}) 15 | 16 | include(GNUInstallDirs) 17 | install(TARGETS demo_ros 18 | RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR}/${PROJECT_NAME}) 19 | endif() 20 | -------------------------------------------------------------------------------- /config_utilities/demos/demo_ros.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /config_utilities/demos/resources/factory.yaml: -------------------------------------------------------------------------------- 1 | special_ns: 2 | type: "DerivedD" 3 | f: 987.654 4 | s: "a string in a special namespace" 5 | 6 | type: "DerivedC" 7 | f: 123.456 8 | s: "a string in a file" 9 | d: 3.14159 10 | -------------------------------------------------------------------------------- /config_utilities/demos/resources/inheritance.yaml: -------------------------------------------------------------------------------- 1 | valid_ns: 2 | f: 3.45 3 | s: "asdasd" 4 | 5 | invalid_ns: 6 | f: -1 7 | d: -1 8 | vec: [1, 2, 3, 4, 5] 9 | b: false 10 | -------------------------------------------------------------------------------- /config_utilities/demos/resources/invalid_params.yaml: -------------------------------------------------------------------------------- 1 | vec: "Invalid Value to create a vector from" 2 | map: "Invalid Value to create a map from" 3 | mat: 4 | - [1, 2, 3] 5 | - [4, 5, 6] 6 | - [7, 8, 9, 10] 7 | i: "Value that can't be cast to int" 8 | my_enum: "D" 9 | sub_ns: 10 | color: "Not a valid color" 11 | f: "Not a float" 12 | size: -1 13 | -------------------------------------------------------------------------------- /config_utilities/demos/resources/params.yaml: -------------------------------------------------------------------------------- 1 | i: 123 2 | s: "a string in a file" 3 | distance: -0.9876 4 | b: false 5 | vec: [5, 4, 3, 2, 1] 6 | map: { b: 2, c: 3, d: 4, e: 5 } 7 | mat: 8 | - [1, 2, 3] 9 | - [4, 5, 6] 10 | - [7, 8, 9] 11 | my_enum: B 12 | sub_ns: 13 | color: [255, 255, 255] 14 | f: 55555.55555 15 | size: 101010101 16 | -------------------------------------------------------------------------------- /config_utilities/include/config_utilities/config_utilities.h: -------------------------------------------------------------------------------- 1 | /** ----------------------------------------------------------------------------- 2 | * Copyright (c) 2023 Massachusetts Institute of Technology. 3 | * All Rights Reserved. 4 | * 5 | * AUTHORS: Lukas Schmid , Nathan Hughes 6 | * AFFILIATION: MIT-SPARK Lab, Massachusetts Institute of Technology 7 | * YEAR: 2023 8 | * LICENSE: BSD 3-Clause 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * 13 | * 1. Redistributions of source code must retain the above copyright notice, this 14 | * list of conditions and the following disclaimer. 15 | * 16 | * 2. Redistributions in binary form must reproduce the above copyright notice, 17 | * this list of conditions and the following disclaimer in the documentation 18 | * and/or other materials provided with the distribution. 19 | * 20 | * 3. Neither the name of the copyright holder nor the names of its 21 | * contributors may be used to endorse or promote products derived from 22 | * this software without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | * -------------------------------------------------------------------------- */ 35 | 36 | #pragma once 37 | 38 | // Utility file that includes the entire C++ suite of config utilities for easy use. Includes and initializes logging to 39 | // stdout and formatting in ASL style. 40 | 41 | #include "config_utilities/config.h" 42 | #include "config_utilities/factory.h" 43 | #include "config_utilities/formatting/asl.h" 44 | #include "config_utilities/logging/log_to_stdout.h" 45 | #include "config_utilities/printing.h" 46 | #include "config_utilities/settings.h" 47 | #include "config_utilities/types/conversions.h" 48 | #include "config_utilities/types/enum.h" 49 | #include "config_utilities/validation.h" 50 | #include "config_utilities/virtual_config.h" 51 | -------------------------------------------------------------------------------- /config_utilities/include/config_utilities/formatting/asl.h: -------------------------------------------------------------------------------- 1 | /** ----------------------------------------------------------------------------- 2 | * Copyright (c) 2023 Massachusetts Institute of Technology. 3 | * All Rights Reserved. 4 | * 5 | * AUTHORS: Lukas Schmid , Nathan Hughes 6 | * AFFILIATION: MIT-SPARK Lab, Massachusetts Institute of Technology 7 | * YEAR: 2023 8 | * LICENSE: BSD 3-Clause 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * 13 | * 1. Redistributions of source code must retain the above copyright notice, this 14 | * list of conditions and the following disclaimer. 15 | * 16 | * 2. Redistributions in binary form must reproduce the above copyright notice, 17 | * this list of conditions and the following disclaimer in the documentation 18 | * and/or other materials provided with the distribution. 19 | * 20 | * 3. Neither the name of the copyright holder nor the names of its 21 | * contributors may be used to endorse or promote products derived from 22 | * this software without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | * -------------------------------------------------------------------------- */ 35 | 36 | #pragma once 37 | 38 | #include 39 | #include 40 | #include 41 | 42 | #include "config_utilities/factory.h" 43 | #include "config_utilities/internal/formatter.h" 44 | #include "config_utilities/internal/meta_data.h" 45 | 46 | namespace config::internal { 47 | 48 | /** 49 | * @brief Implements formatting of text in the style of https://github.com/ethz-asl/config_utilities, emphasizing 50 | * readability wenn printed to the console. 51 | */ 52 | class AslFormatter : public Formatter { 53 | public: 54 | AslFormatter() = default; 55 | ~AslFormatter() override = default; 56 | 57 | protected: 58 | std::string formatErrorsImpl(const MetaData& data, const std::string& what, const Severity severity) override; 59 | std::string formatMissingImpl(const MetaData& data, const std::string& what, const Severity severity) override; 60 | std::string formatConfigImpl(const MetaData& data) override; 61 | std::string formatConfigsImpl(const std::vector& data) override; 62 | 63 | private: 64 | // Factory registration to allow setting of formatters via Settings::setFormatter(). 65 | inline static const auto registration_ = Registration("asl"); 66 | 67 | // Initialize the asl formatter to be used if included. 68 | inline static const struct Initializer { 69 | Initializer() { Formatter::setFormatter(std::make_unique()); } 70 | } initializer_; 71 | 72 | // Helper functions. 73 | std::string formatErrorsRecursive(const MetaData& data, const std::string& sev, const size_t length); 74 | std::string formatMissingRecursive(const MetaData& data, const std::string& sev, const size_t length); 75 | std::string formatChecksInternal(const MetaData& data, const std::string& sev, const size_t length); 76 | std::string formatErrorsInternal(const MetaData& data, const std::string& sev, const size_t length); 77 | std::string toStringInternal(const MetaData& data, size_t indent) const; 78 | std::string formatField(const FieldInfo& info, size_t indent) const; 79 | std::string formatSubconfig(const MetaData& data, size_t indent) const; 80 | std::string resolveConfigName(const MetaData& data) const; 81 | 82 | // Formatting options, currently not exposed in global settings but work if want changed. 83 | // TODO(lschmid): Global formatting options should probably be a config of the formatter. 84 | // If true add subconfig types after the fieldname. 85 | constexpr static bool indicate_subconfig_types_ = true; 86 | // If true label subconfigs as default if all their values are default. 87 | constexpr static bool indicate_subconfig_default_ = true; 88 | // If true indicate that a config is a virtual config in the config name. 89 | constexpr static bool indicate_virtual_configs_ = true; 90 | // If true indicate the number of a check and total number of checks in failed checks. 91 | constexpr static bool indicate_num_checks_ = true; 92 | 93 | // Variables. 94 | std::string name_prefix_; 95 | size_t total_num_checks_; 96 | size_t current_check_; 97 | bool is_first_divider_; 98 | }; 99 | 100 | } // namespace config::internal 101 | -------------------------------------------------------------------------------- /config_utilities/include/config_utilities/getters.h: -------------------------------------------------------------------------------- 1 | /** ----------------------------------------------------------------------------- 2 | * Copyright (c) 2023 Massachusetts Institute of Technology. 3 | * All Rights Reserved. 4 | * 5 | * AUTHORS: Lukas Schmid , Nathan Hughes 6 | * AFFILIATION: MIT-SPARK Lab, Massachusetts Institute of Technology 7 | * YEAR: 2023 8 | * LICENSE: BSD 3-Clause 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * 13 | * 1. Redistributions of source code must retain the above copyright notice, this 14 | * list of conditions and the following disclaimer. 15 | * 16 | * 2. Redistributions in binary form must reproduce the above copyright notice, 17 | * this list of conditions and the following disclaimer in the documentation 18 | * and/or other materials provided with the distribution. 19 | * 20 | * 3. Neither the name of the copyright holder nor the names of its 21 | * contributors may be used to endorse or promote products derived from 22 | * this software without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | * -------------------------------------------------------------------------- */ 35 | 36 | #include 37 | 38 | #include 39 | #include 40 | #include 41 | 42 | namespace config { 43 | 44 | /** 45 | * @brief Lists all the fields of the given configuration. 46 | * 47 | * This function retrieves metadata from the provided configuration object 48 | * and extracts the names of all fields, returning them in a vector. 49 | * 50 | * @note This function does not list fields of nested and sub-configurations. 51 | * @tparam ConfigT The type of the configuration. 52 | * @param config The configuration object whose fields are to be listed. 53 | * @return A vector containing the names of all fields in the configuration. 54 | */ 55 | template 56 | std::vector listFields(const ConfigT& config) { 57 | internal::MetaData data = internal::Visitor::getValues(config); 58 | std::vector fields; 59 | for (const auto& field_info : data.field_infos) { 60 | fields.emplace_back(field_info.name); 61 | } 62 | return fields; 63 | } 64 | 65 | /** 66 | * @brief Retrieves the value of a specified field from the given configuration. 67 | * 68 | * This function searches for a field with the specified name in the provided 69 | * configuration object and attempts to convert its value to the requested type. 70 | * If the field is found and the conversion is successful, the value is returned 71 | * as an optional. If the field is not found or the conversion fails, a warning 72 | * is logged and an empty optional is returned. 73 | * 74 | * @tparam ConfigT The type of the configuration. 75 | * @tparam T The type to which the field value should be converted. 76 | * @param config The configuration object from which the field value is to be retrieved. 77 | * @param field_name The name of the field whose value is to be retrieved. 78 | * @return An optional containing the value of the field if found and successfully converted, 79 | * otherwise an empty optional. 80 | */ 81 | template 82 | std::optional getField(const ConfigT& config, const std::string& field_name) { 83 | internal::MetaData data = internal::Visitor::getValues(config); 84 | for (const auto& field_info : data.field_infos) { 85 | if (field_info.name == field_name) { 86 | std::string error; 87 | std::optional value = internal::YamlParser::fromYaml(field_info.value, &error); 88 | if (!error.empty()) { 89 | internal::Logger::logWarning("Field '" + field_name + 90 | "' could not be converted to the requested type: " + error); 91 | } 92 | return value; 93 | } 94 | } 95 | internal::Logger::logWarning("Field '" + field_name + "' not found in config."); 96 | return std::nullopt; 97 | } 98 | 99 | } // namespace config -------------------------------------------------------------------------------- /config_utilities/include/config_utilities/globals.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | // TODO(nathan) drop once dependencies don't include this 6 | 7 | namespace config { 8 | std::string printAllValidConfigs(bool flag) { throw std::runtime_error("deprecated"); } 9 | } // namespace config 10 | -------------------------------------------------------------------------------- /config_utilities/include/config_utilities/internal/config_context.h: -------------------------------------------------------------------------------- 1 | /** ----------------------------------------------------------------------------- 2 | * Copyright (c) 2023 Massachusetts Institute of Technology. 3 | * All Rights Reserved. 4 | * 5 | * AUTHORS: Lukas Schmid , Nathan Hughes 6 | * AFFILIATION: MIT-SPARK Lab, Massachusetts Institute of Technology 7 | * YEAR: 2023 8 | * LICENSE: BSD 3-Clause 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * 13 | * 1. Redistributions of source code must retain the above copyright notice, this 14 | * list of conditions and the following disclaimer. 15 | * 16 | * 2. Redistributions in binary form must reproduce the above copyright notice, 17 | * this list of conditions and the following disclaimer in the documentation 18 | * and/or other materials provided with the distribution. 19 | * 20 | * 3. Neither the name of the copyright holder nor the names of its 21 | * contributors may be used to endorse or promote products derived from 22 | * this software without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | * -------------------------------------------------------------------------- */ 35 | 36 | #pragma once 37 | 38 | #include 39 | 40 | #include 41 | 42 | #include "config_utilities/factory.h" 43 | #include "config_utilities/internal/visitor.h" 44 | 45 | namespace config::internal { 46 | 47 | /** 48 | * @brief Context is a singleton that holds the raw parsed information used to generate configs 49 | */ 50 | class Context { 51 | public: 52 | ~Context() = default; 53 | 54 | static void update(const YAML::Node& other, const std::string& ns); 55 | 56 | static void clear(); 57 | 58 | static YAML::Node toYaml(); 59 | 60 | template 61 | static std::unique_ptr create(ConstructorArguments... args) { 62 | return internal::ObjectWithConfigFactory::create(instance().contents_, 63 | std::move(args)...); 64 | } 65 | 66 | template 67 | static std::unique_ptr createNamespaced(const std::string& name_space, ConstructorArguments... args) { 68 | const auto ns_node = internal::lookupNamespace(instance().contents_, name_space); 69 | return internal::ObjectWithConfigFactory::create(ns_node, std::move(args)...); 70 | } 71 | 72 | template 73 | static ConfigT loadConfig(const std::string& name_space = "") { 74 | ConfigT config; 75 | internal::Visitor::setValues(config, internal::lookupNamespace(instance().contents_, name_space), true); 76 | return config; 77 | } 78 | 79 | private: 80 | Context() = default; 81 | static Context& instance(); 82 | 83 | YAML::Node contents_; 84 | }; 85 | 86 | } // namespace config::internal 87 | -------------------------------------------------------------------------------- /config_utilities/include/config_utilities/internal/formatter.h: -------------------------------------------------------------------------------- 1 | /** ----------------------------------------------------------------------------- 2 | * Copyright (c) 2023 Massachusetts Institute of Technology. 3 | * All Rights Reserved. 4 | * 5 | * AUTHORS: Lukas Schmid , Nathan Hughes 6 | * AFFILIATION: MIT-SPARK Lab, Massachusetts Institute of Technology 7 | * YEAR: 2023 8 | * LICENSE: BSD 3-Clause 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * 13 | * 1. Redistributions of source code must retain the above copyright notice, this 14 | * list of conditions and the following disclaimer. 15 | * 16 | * 2. Redistributions in binary form must reproduce the above copyright notice, 17 | * this list of conditions and the following disclaimer in the documentation 18 | * and/or other materials provided with the distribution. 19 | * 20 | * 3. Neither the name of the copyright holder nor the names of its 21 | * contributors may be used to endorse or promote products derived from 22 | * this software without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | * -------------------------------------------------------------------------- */ 35 | 36 | #pragma once 37 | 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | #include "config_utilities/internal/logger.h" 44 | #include "config_utilities/internal/meta_data.h" 45 | 46 | namespace config::internal { 47 | 48 | /** 49 | * @brief Abstract interface class for formatters. Formatters implement these methods to format the configs for 50 | * toString() calls and printing of validity checks. 51 | */ 52 | class Formatter { 53 | public: 54 | using Ptr = std::shared_ptr; 55 | 56 | // Constructor and destructor. 57 | Formatter() = default; 58 | virtual ~Formatter() = default; 59 | 60 | // Accessing the formatter. 61 | // Format all errors in the meta data into the display string. 62 | static std::string formatErrors(const MetaData& data, 63 | const std::string& what = "", 64 | const Severity severity = Severity::kWarning); 65 | 66 | // Format all missing fields in the meta data into the display string. 67 | static std::string formatMissing(const MetaData& data, 68 | const std::string& what = "", 69 | const Severity severity = Severity::kWarning); 70 | 71 | // Format the content of a single config the display string. 72 | static std::string formatConfig(const MetaData& data); 73 | 74 | // Format the content of multiple configs the display string. 75 | static std::string formatConfigs(const std::vector& data); 76 | 77 | // Set the global formatter. 78 | static void setFormatter(Formatter::Ptr formatter); 79 | 80 | protected: 81 | virtual std::string formatErrorsImpl(const MetaData& data, const std::string& what, const Severity severity); 82 | virtual std::string formatMissingImpl(const MetaData& data, const std::string& what, const Severity severity); 83 | virtual std::string formatConfigImpl(const MetaData& data); 84 | virtual std::string formatConfigsImpl(const std::vector& data); 85 | 86 | private: 87 | std::string getUnspecifiedString() const; 88 | inline static Formatter::Ptr formatter_ = std::make_shared(); 89 | }; 90 | 91 | } // namespace config::internal 92 | -------------------------------------------------------------------------------- /config_utilities/include/config_utilities/internal/logger.h: -------------------------------------------------------------------------------- 1 | /** ----------------------------------------------------------------------------- 2 | * Copyright (c) 2023 Massachusetts Institute of Technology. 3 | * All Rights Reserved. 4 | * 5 | * AUTHORS: Lukas Schmid , Nathan Hughes 6 | * AFFILIATION: MIT-SPARK Lab, Massachusetts Institute of Technology 7 | * YEAR: 2023 8 | * LICENSE: BSD 3-Clause 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * 13 | * 1. Redistributions of source code must retain the above copyright notice, this 14 | * list of conditions and the following disclaimer. 15 | * 16 | * 2. Redistributions in binary form must reproduce the above copyright notice, 17 | * this list of conditions and the following disclaimer in the documentation 18 | * and/or other materials provided with the distribution. 19 | * 20 | * 3. Neither the name of the copyright holder nor the names of its 21 | * contributors may be used to endorse or promote products derived from 22 | * this software without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | * -------------------------------------------------------------------------- */ 35 | 36 | #pragma once 37 | 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | namespace config::internal { 44 | 45 | // Enum for different severity levels of logging. Enum values are used for comparing logging levels. 46 | enum class Severity { kInfo = 0, kWarning = 1, kError = 2, kFatal = 3 }; 47 | 48 | std::string severityToString(const Severity severity); 49 | 50 | /** 51 | * @brief Abstract interface class for all logging classes. Calls to logging will happen through a global instance of 52 | * the logger that can be set by the implementation. 53 | */ 54 | class Logger { 55 | public: 56 | using Ptr = std::shared_ptr; 57 | 58 | // Constructor and destructor. 59 | Logger() = default; 60 | virtual ~Logger() = default; 61 | 62 | // Severity levels for logging. Important: Implementations of logFatal are expected to stop execution of the program. 63 | static void log(const Severity severity, const std::string& message); 64 | 65 | // Convenience interfaces to log to a specific severity. 66 | static void logInfo(const std::string& message); 67 | static void logWarning(const std::string& message); 68 | static void logError(const std::string& message); 69 | static void logFatal(const std::string& message); 70 | 71 | // Set the global logger. 72 | static void setLogger(Logger::Ptr logger); 73 | 74 | protected: 75 | // Interface to be implemented by loggers. 76 | virtual void logImpl(const Severity severity, const std::string& message); 77 | 78 | private: 79 | static void dispatch(const Severity severity, const std::string& message); 80 | 81 | static Logger::Ptr logger_; 82 | }; 83 | 84 | } // namespace config::internal 85 | -------------------------------------------------------------------------------- /config_utilities/include/config_utilities/internal/namespacing.h: -------------------------------------------------------------------------------- 1 | /** ----------------------------------------------------------------------------- 2 | * Copyright (c) 2023 Massachusetts Institute of Technology. 3 | * All Rights Reserved. 4 | * 5 | * AUTHORS: Lukas Schmid , Nathan Hughes 6 | * AFFILIATION: MIT-SPARK Lab, Massachusetts Institute of Technology 7 | * YEAR: 2023 8 | * LICENSE: BSD 3-Clause 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * 13 | * 1. Redistributions of source code must retain the above copyright notice, this 14 | * list of conditions and the following disclaimer. 15 | * 16 | * 2. Redistributions in binary form must reproduce the above copyright notice, 17 | * this list of conditions and the following disclaimer in the documentation 18 | * and/or other materials provided with the distribution. 19 | * 20 | * 3. Neither the name of the copyright holder nor the names of its 21 | * contributors may be used to endorse or promote products derived from 22 | * this software without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | * -------------------------------------------------------------------------- */ 35 | 36 | #pragma once 37 | 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | namespace config { 44 | 45 | /** 46 | * @brief Enters a sub-namespace to get or set object fields. The namespace remains active until 'exit_namespace()' or 47 | * 'clear_namespaces()' is called. 48 | * @param name_space The sub-namespace to enter. This can be a nested namespace separated by slashes, e.g. "foo/bar". 49 | */ 50 | void enter_namespace(const std::string& name_space); 51 | 52 | /** 53 | * @brief Exits the last entered sub-namespace and re-enters the previous namespace, i.e. undoes the last call to 54 | * 'enter_namespace()'. 55 | */ 56 | void exit_namespace(); 57 | 58 | /** 59 | * @brief Exits all entered sub-namespaces and re-enters the root namespace. 60 | */ 61 | void clear_namespaces(); 62 | 63 | /** 64 | * @brief Switches the currently open sub-namespace to the specified namespace. This is equivalent to calling 65 | * 'exit_namespaces()' and then 'enter_namespace()'. 66 | */ 67 | void switch_namespace(const std::string& name_space); 68 | 69 | /** 70 | * @brief Get the current sub-namespace used for getting or setting params. 71 | */ 72 | std::string current_namespace(); 73 | 74 | /** 75 | * @brief Enters a sub-namespace to get or set object fields for the duration of the NameSpace's lifetime. 76 | */ 77 | class NameSpace { 78 | public: 79 | /** 80 | * @brief Enters a sub-namespace to get or set object fields for the duration of the NameSpace's lifetime. 81 | * @param name_space The sub-namespace to enter. This can be a nested namespace separated by slashes, e.g. "foo/bar". 82 | */ 83 | explicit NameSpace(const std::string& name_space); 84 | virtual ~NameSpace(); 85 | 86 | // Additional methods to repeatedly enter and exit namespaces if requested. 87 | /** 88 | * @brief Exits the sub-namespace specified by this NameSpace and re-enters the previous namespace. 89 | */ 90 | void exit(); 91 | 92 | /** 93 | * @brief Re-enters the sub-namespace specified by this NameSpace. The namespace needs to be exited before it can be 94 | * re-entered. 95 | */ 96 | void enter(); 97 | 98 | // private: 99 | const std::string sub_ns_; 100 | std::string previous_ns_; 101 | bool is_open_ = false; 102 | }; 103 | 104 | namespace internal { 105 | 106 | // Struct that keeps namespaces around in the visitor and manages keeping namespaces constant when visiting. 107 | struct OpenNameSpace { 108 | using Stack = std::vector>; 109 | 110 | explicit OpenNameSpace(const std::string& name_space); 111 | 112 | static void performOperationWithGuardedNs(Stack& stack, const std::function& operation); 113 | 114 | bool isLocked() const; 115 | 116 | private: 117 | // Interaction. Private so the lock count can not be messed with. 118 | void lock(); 119 | void unlock(); 120 | 121 | // Data. 122 | int locks = 0; 123 | const NameSpace ns; 124 | }; 125 | 126 | } // namespace internal 127 | 128 | } // namespace config 129 | -------------------------------------------------------------------------------- /config_utilities/include/config_utilities/internal/yaml_utils.h: -------------------------------------------------------------------------------- 1 | /** ----------------------------------------------------------------------------- 2 | * Copyright (c) 2023 Massachusetts Institute of Technology. 3 | * All Rights Reserved. 4 | * 5 | * AUTHORS: Lukas Schmid , Nathan Hughes 6 | * AFFILIATION: MIT-SPARK Lab, Massachusetts Institute of Technology 7 | * YEAR: 2023 8 | * LICENSE: BSD 3-Clause 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * 13 | * 1. Redistributions of source code must retain the above copyright notice, this 14 | * list of conditions and the following disclaimer. 15 | * 16 | * 2. Redistributions in binary form must reproduce the above copyright notice, 17 | * this list of conditions and the following disclaimer in the documentation 18 | * and/or other materials provided with the distribution. 19 | * 20 | * 3. Neither the name of the copyright holder nor the names of its 21 | * contributors may be used to endorse or promote products derived from 22 | * this software without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | * -------------------------------------------------------------------------- */ 35 | 36 | #pragma once 37 | 38 | #include 39 | #include 40 | 41 | #include 42 | 43 | namespace config::internal { 44 | 45 | enum class MergeMode { 46 | //! @brief Combine the two trees, recursing into matching sequence entries 47 | UPDATE, 48 | //! @brief Combine the two trees, appending right sequences into the left 49 | APPEND, 50 | //! @brief Combine the two trees, replacing left sequences with the right 51 | REPLACE 52 | }; 53 | 54 | /** 55 | * @brief Merges node b into a with conflicting keys handled by choice of mode 56 | * 57 | * Recurses through the YAML "tree" of b, adding all non-conflicting nodes to a. Conflicting nodes (i.e. map keys or 58 | * shared indices in sequences that already exist in a) are handled according to the mode selection. For `REPLACE`, any 59 | * conflicting node stops the recursion, and the conflicting node is replaced by the value in b. For 'APPEND', any 60 | * conflicting sequence node will stop the recursion and cause the entire contents of the node in b to be append to the 61 | * node in a. For 'UPDATE', any conflicting map or sequence node recursively calls `mergeYamlNodes` with the children of 62 | * the conflicting nodes as the new roots. 63 | * 64 | * @param a Node to merge into ("left" node and will be changed). 65 | * @param b Node to merge from ("right" node and remains constant). 66 | * @param mode Mode to use when merging 67 | */ 68 | void mergeYamlNodes(YAML::Node& a, const YAML::Node& b, MergeMode mode = MergeMode::UPDATE); 69 | 70 | /** 71 | * @brief Get a pointer to the final node of the specified namespace if it exists, where each map in the yaml is 72 | * separated by the separator. 73 | */ 74 | YAML::Node lookupNamespace(const YAML::Node& node, const std::string& name_space, const std::string& separator = "/"); 75 | 76 | /** 77 | * @brief Move the node down the specified namespace, where each namespace separated by the separator is represented as 78 | * a map key. 79 | */ 80 | void moveDownNamespace(YAML::Node& node, const std::string& name_space, const std::string& separator = "/"); 81 | 82 | /** 83 | * @brief Check whether two yaml nodes are equal. Note that since since yaml-cpp operator== checks for identity and not 84 | * equality, scalar values will be compared by string representation. 85 | */ 86 | bool isEqual(const YAML::Node& a, const YAML::Node& b); 87 | 88 | /** 89 | * @brief Convert a yaml node that contains a map or sequence to a list of corresponding nodes. 90 | * @param node The node to convert. 91 | * @return The list of nodes. Nodes stored in this struct are references to the original data. 92 | */ 93 | std::vector getNodeArray(const YAML::Node& node); 94 | 95 | /** 96 | * @brief Convert a yaml node that contains a map or sequence to a list of corresponding nodes. 97 | * @param node The node to convert. 98 | * @return The list of nodes. Nodes stored in this struct are references to the original data. 99 | */ 100 | std::vector> getNodeMap(const YAML::Node& node); 101 | 102 | } // namespace config::internal 103 | -------------------------------------------------------------------------------- /config_utilities/include/config_utilities/logging/log_to_glog.h: -------------------------------------------------------------------------------- 1 | /** ----------------------------------------------------------------------------- 2 | * Copyright (c) 2023 Massachusetts Institute of Technology. 3 | * All Rights Reserved. 4 | * 5 | * AUTHORS: Lukas Schmid , Nathan Hughes 6 | * AFFILIATION: MIT-SPARK Lab, Massachusetts Institute of Technology 7 | * YEAR: 2023 8 | * LICENSE: BSD 3-Clause 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * 13 | * 1. Redistributions of source code must retain the above copyright notice, this 14 | * list of conditions and the following disclaimer. 15 | * 16 | * 2. Redistributions in binary form must reproduce the above copyright notice, 17 | * this list of conditions and the following disclaimer in the documentation 18 | * and/or other materials provided with the distribution. 19 | * 20 | * 3. Neither the name of the copyright holder nor the names of its 21 | * contributors may be used to endorse or promote products derived from 22 | * this software without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | * -------------------------------------------------------------------------- */ 35 | 36 | #pragma once 37 | 38 | #include 39 | 40 | #include "config_utilities/factory.h" 41 | #include "config_utilities/internal/logger.h" 42 | 43 | namespace config::internal { 44 | 45 | /** 46 | * @brief Implements logging to glog. This file pulls in glog as a dependency, but glog is not required if this file is 47 | * not included in the project. 48 | */ 49 | class GlogLogger : public Logger { 50 | public: 51 | GlogLogger() = default; 52 | virtual ~GlogLogger() = default; 53 | 54 | protected: 55 | void logImpl(const Severity severity, const std::string& message) override { 56 | switch (severity) { 57 | case Severity::kInfo: 58 | LOG(INFO) << message; 59 | break; 60 | 61 | case Severity::kWarning: 62 | LOG(WARNING) << message; 63 | 64 | break; 65 | 66 | case Severity::kError: 67 | LOG(ERROR) << message; 68 | break; 69 | 70 | case Severity::kFatal: 71 | LOG(FATAL) << message; 72 | } 73 | } 74 | 75 | private: 76 | // Factory registration to allow setting of formatters via Settings::setLogger(). 77 | inline static const auto registration_ = Registration("glog"); 78 | }; 79 | 80 | } // namespace config::internal 81 | -------------------------------------------------------------------------------- /config_utilities/include/config_utilities/logging/log_to_ros.h: -------------------------------------------------------------------------------- 1 | /** ----------------------------------------------------------------------------- 2 | * Copyright (c) 2023 Massachusetts Institute of Technology. 3 | * All Rights Reserved. 4 | * 5 | * AUTHORS: Lukas Schmid , Nathan Hughes 6 | * AFFILIATION: MIT-SPARK Lab, Massachusetts Institute of Technology 7 | * YEAR: 2023 8 | * LICENSE: BSD 3-Clause 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * 13 | * 1. Redistributions of source code must retain the above copyright notice, this 14 | * list of conditions and the following disclaimer. 15 | * 16 | * 2. Redistributions in binary form must reproduce the above copyright notice, 17 | * this list of conditions and the following disclaimer in the documentation 18 | * and/or other materials provided with the distribution. 19 | * 20 | * 3. Neither the name of the copyright holder nor the names of its 21 | * contributors may be used to endorse or promote products derived from 22 | * this software without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | * -------------------------------------------------------------------------- */ 35 | 36 | #pragma once 37 | 38 | #include 39 | 40 | #include "config_utilities/factory.h" 41 | #include "config_utilities/internal/logger.h" 42 | 43 | namespace config::internal { 44 | 45 | /** 46 | * @brief Implements logging to roslog. This file pulls in ros as a dependency, but its not required if this file is 47 | * not included in the project. 48 | */ 49 | class RosLogger : public Logger { 50 | public: 51 | RosLogger() = default; 52 | virtual ~RosLogger() = default; 53 | 54 | protected: 55 | void logImpl(const Severity severity, const std::string& message) override { 56 | switch (severity) { 57 | case Severity::kInfo: 58 | ROS_INFO_STREAM(message); 59 | break; 60 | 61 | case Severity::kWarning: 62 | ROS_WARN_STREAM(message); 63 | break; 64 | 65 | case Severity::kError: 66 | ROS_ERROR_STREAM(message); 67 | break; 68 | 69 | case Severity::kFatal: 70 | ROS_FATAL_STREAM(message); 71 | } 72 | } 73 | 74 | private: 75 | // Factory registration to allow setting of formatters via Settings::setLogger(). 76 | inline static const auto registration_ = Registration("ros"); 77 | }; 78 | 79 | } // namespace config::internal 80 | -------------------------------------------------------------------------------- /config_utilities/include/config_utilities/logging/log_to_stdout.h: -------------------------------------------------------------------------------- 1 | /** ----------------------------------------------------------------------------- 2 | * Copyright (c) 2023 Massachusetts Institute of Technology. 3 | * All Rights Reserved. 4 | * 5 | * AUTHORS: Lukas Schmid , Nathan Hughes 6 | * AFFILIATION: MIT-SPARK Lab, Massachusetts Institute of Technology 7 | * YEAR: 2023 8 | * LICENSE: BSD 3-Clause 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * 13 | * 1. Redistributions of source code must retain the above copyright notice, this 14 | * list of conditions and the following disclaimer. 15 | * 16 | * 2. Redistributions in binary form must reproduce the above copyright notice, 17 | * this list of conditions and the following disclaimer in the documentation 18 | * and/or other materials provided with the distribution. 19 | * 20 | * 3. Neither the name of the copyright holder nor the names of its 21 | * contributors may be used to endorse or promote products derived from 22 | * this software without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | * -------------------------------------------------------------------------- */ 35 | 36 | #pragma once 37 | 38 | #include "config_utilities/factory.h" 39 | #include "config_utilities/internal/logger.h" 40 | 41 | namespace config::internal { 42 | 43 | /** 44 | * @brief Implements logging to std::cout. 45 | */ 46 | class StdoutLogger : public Logger { 47 | public: 48 | /** 49 | * @brief Construct a logger to output to stdout or stderr depending on configuration 50 | * @param min_severity Mininum severity to output 51 | * @param stderr_severity Mininum severity to log to stderr instead of stdout 52 | */ 53 | StdoutLogger(Severity min_severity = Severity::kWarning, Severity stderr_severity = Severity::kError); 54 | 55 | virtual ~StdoutLogger() = default; 56 | 57 | protected: 58 | void logImpl(const Severity severity, const std::string& message) override; 59 | 60 | private: 61 | const Severity min_severity_; 62 | const Severity stderr_severity_; 63 | // Factory registration to allow setting of formatters via Settings::setLogger(). 64 | inline static const auto registration_ = Registration("stdout"); 65 | 66 | // Initialize the stdout logger to be used if included. 67 | inline static const struct Initializer { Initializer(); } initializer_ = Initializer(); 68 | }; 69 | 70 | } // namespace config::internal 71 | -------------------------------------------------------------------------------- /config_utilities/include/config_utilities/parsing/context.h: -------------------------------------------------------------------------------- 1 | /** ----------------------------------------------------------------------------- 2 | * Copyright (c) 2023 Massachusetts Institute of Technology. 3 | * All Rights Reserved. 4 | * 5 | * AUTHORS: Lukas Schmid , Nathan Hughes 6 | * AFFILIATION: MIT-SPARK Lab, Massachusetts Institute of Technology 7 | * YEAR: 2023 8 | * LICENSE: BSD 3-Clause 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * 13 | * 1. Redistributions of source code must retain the above copyright notice, this 14 | * list of conditions and the following disclaimer. 15 | * 16 | * 2. Redistributions in binary form must reproduce the above copyright notice, 17 | * this list of conditions and the following disclaimer in the documentation 18 | * and/or other materials provided with the distribution. 19 | * 20 | * 3. Neither the name of the copyright holder nor the names of its 21 | * contributors may be used to endorse or promote products derived from 22 | * this software without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | * -------------------------------------------------------------------------- */ 35 | 36 | #pragma once 37 | 38 | #include 39 | 40 | #include "config_utilities/internal/config_context.h" 41 | 42 | namespace config { 43 | 44 | /** 45 | * @brief Initialize global config context from the command line 46 | * @param argc Number of arguments. 47 | * @param argv Actual command line arguments. 48 | * @param remove_arguments Remove parsed command line arguments. 49 | */ 50 | void initContext(int& argc, char* argv[], bool remove_arguments = true); 51 | 52 | /** 53 | * @brief Aggregate YAML node into global context 54 | * @param node YAML to add to context 55 | * @param ns Optional namespace 56 | */ 57 | void pushToContext(const YAML::Node& node, const std::string& ns = ""); 58 | 59 | /** 60 | * @brief Delete parsed context 61 | */ 62 | void clearContext(); 63 | 64 | /** 65 | * @brief Dump current context for exporting or saving 66 | */ 67 | YAML::Node contextToYaml(); 68 | 69 | /** 70 | * @brief Loads a config from the global context 71 | * 72 | * @tparam ConfigT The config type. This can also be a VirtualConfig or a std::vector. 73 | * @param name_space Optionally specify a name space to create the config from. Separate names with slashes '/'. 74 | * Example: "my_config/my_sub_config". 75 | * @returns The config. 76 | */ 77 | template 78 | ConfigT fromContext(const std::string& name_space = "") { 79 | return internal::Context::loadConfig(name_space); 80 | } 81 | 82 | /** 83 | * @brief Create a derived type object based on currently stored YAML in config::internal::Context. 84 | * 85 | * @tparam BaseT Type of the base class to be constructed. 86 | * @tparam Args Other constructor arguments. Note that each unique set of constructor arguments will result in a 87 | * different base-entry in the factory. 88 | * @param args Other constructor arguments. 89 | * @returns Unique pointer of type base that contains the derived object. 90 | */ 91 | template 92 | std::unique_ptr createFromContext(ConstructorArguments... args) { 93 | return internal::Context::create(std::move(args)...); 94 | } 95 | 96 | /** 97 | * @brief Create a derived type object based on currently stored YAML in config::internal::Context. 98 | * 99 | * See createFromYamlWithNamespace() for more specific behavioral information. 100 | * 101 | * @tparam BaseT Type of the base class to be constructed. 102 | * @tparam Args Other constructor arguments. 103 | * @param name_space Optionally specify a name space to create the object from. 104 | * @param args Other constructor arguments. 105 | * @returns Unique pointer of type base that contains the derived object. 106 | */ 107 | template 108 | std::unique_ptr createFromContextWithNamespace(const std::string& name_space, ConstructorArguments... args) { 109 | return internal::Context::createNamespaced(name_space, args...); 110 | } 111 | 112 | } // namespace config 113 | -------------------------------------------------------------------------------- /config_utilities/include/config_utilities/settings.h: -------------------------------------------------------------------------------- 1 | /** ----------------------------------------------------------------------------- 2 | * Copyright (c) 2023 Massachusetts Institute of Technology. 3 | * All Rights Reserved. 4 | * 5 | * AUTHORS: Lukas Schmid , Nathan Hughes 6 | * AFFILIATION: MIT-SPARK Lab, Massachusetts Institute of Technology 7 | * YEAR: 2023 8 | * LICENSE: BSD 3-Clause 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * 13 | * 1. Redistributions of source code must retain the above copyright notice, this 14 | * list of conditions and the following disclaimer. 15 | * 16 | * 2. Redistributions in binary form must reproduce the above copyright notice, 17 | * this list of conditions and the following disclaimer in the documentation 18 | * and/or other materials provided with the distribution. 19 | * 20 | * 3. Neither the name of the copyright holder nor the names of its 21 | * contributors may be used to endorse or promote products derived from 22 | * this software without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | * -------------------------------------------------------------------------- */ 35 | 36 | #pragma once 37 | 38 | #include 39 | 40 | namespace config { 41 | 42 | namespace internal { 43 | 44 | /** 45 | * @brief Global settings for how config_utilities-based configs behave. These 46 | * can be dynamically set and changed throughout a program. 47 | */ 48 | struct Settings { 49 | // Singleton access to the global settings. 50 | static Settings& instance(); 51 | 52 | /* Printing Settings. */ 53 | // TODO(lschmid): These should probably be moved into a config or so for different formatters. 54 | // @brief Width of the 'toString()' output of configs. 55 | unsigned int print_width = 80u; 56 | 57 | // @brief Indent after which values are printed. 58 | unsigned int print_indent = 30u; 59 | 60 | // @brief Indent for nested configs. 61 | unsigned int subconfig_indent = 3u; 62 | 63 | // @brief If true, indicate which values are identical to the default. 64 | bool indicate_default_values = true; 65 | 66 | // @brief If true, also display the unit of each parameter where provided. 67 | bool indicate_units = true; 68 | 69 | // @brief If true integrate subconfig fields into the main config, if false print them separately. 70 | bool inline_subconfig_field_names = true; 71 | 72 | // @brief If true, store all validated configs for global printing. 73 | bool store_valid_configs = true; 74 | 75 | // @brief If true, attempts to print floats and float-like fields with default stream precision 76 | bool reformat_floats = true; 77 | 78 | // @brief If true, prints fields that had no value present when being parsed 79 | bool print_missing = false; 80 | 81 | /* Factory settings */ 82 | // @brief The factory will look for this param to deduce the type of the object to be created. 83 | std::string factory_type_param_name = "type"; 84 | 85 | //! @brief Whether or not loading external libraries are enabled 86 | bool allow_external_libraries = true; 87 | 88 | //! @brief Whether or not loading and unloading libraries should be verbose 89 | bool verbose_external_load = true; 90 | 91 | //! @brief Log any factory creation from an external library (for debugging purposes) 92 | bool print_external_allocations = false; 93 | 94 | //! @brief Control whether config_utilities is initialized to log to stdout/stderr by default 95 | bool disable_default_stdout_logger = false; 96 | 97 | /* Options to specify the logger and formatter at run time. */ 98 | // Specify the default logger to be used for printing. Loggers register themselves if included. 99 | void setLogger(const std::string& name); 100 | 101 | // Specify the formatter to be used for printing. Formatters register themselves if included. 102 | void setFormatter(const std::string& name); 103 | 104 | // Reset all settings to their default values. 105 | void restoreDefaults() { *this = Settings(); } 106 | 107 | private: 108 | Settings() = default; 109 | Settings(const Settings& other) = default; 110 | Settings& operator=(const Settings& other) = default; 111 | static Settings instance_; 112 | }; 113 | 114 | } // namespace internal 115 | 116 | // Access function in regular namespace. 117 | 118 | /** 119 | * @brief Global settings for how config_utilities-based configs behave. These 120 | * can be dynamically set and changed throughout a program. 121 | */ 122 | inline internal::Settings& Settings() { return internal::Settings::instance(); } 123 | 124 | } // namespace config 125 | -------------------------------------------------------------------------------- /config_utilities/include/config_utilities/substitution_parsers.h: -------------------------------------------------------------------------------- 1 | /** ----------------------------------------------------------------------------- 2 | * Copyright (c) 2023 Massachusetts Institute of Technology. 3 | * All Rights Reserved. 4 | * 5 | * AUTHORS: Lukas Schmid , Nathan Hughes 6 | * AFFILIATION: MIT-SPARK Lab, Massachusetts Institute of Technology 7 | * YEAR: 2023 8 | * LICENSE: BSD 3-Clause 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * 13 | * 1. Redistributions of source code must retain the above copyright notice, this 14 | * list of conditions and the following disclaimer. 15 | * 16 | * 2. Redistributions in binary form must reproduce the above copyright notice, 17 | * this list of conditions and the following disclaimer in the documentation 18 | * and/or other materials provided with the distribution. 19 | * 20 | * 3. Neither the name of the copyright holder nor the names of its 21 | * contributors may be used to endorse or promote products derived from 22 | * this software without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | * -------------------------------------------------------------------------- */ 35 | 36 | #pragma once 37 | 38 | #include "config_utilities/substitutions.h" 39 | 40 | namespace config { 41 | 42 | /** 43 | * @brief Attempts to replace `$(env VAR)` with the value of VAR from the environment 44 | */ 45 | struct EnvSubstitution : public Substitution { 46 | inline static const std::string NAME = "env"; 47 | std::string process(const ParserContext& context, const std::string& contents) const override; 48 | }; 49 | 50 | /** 51 | * @brief Attempts to replace `$(env VAR)` with the value of VAR from the environment 52 | */ 53 | struct VarSubstitution : public Substitution { 54 | inline static const std::string NAME = "var"; 55 | std::string process(const ParserContext& context, const std::string& contents) const override; 56 | }; 57 | 58 | } // namespace config 59 | -------------------------------------------------------------------------------- /config_utilities/include/config_utilities/substitutions.h: -------------------------------------------------------------------------------- 1 | /** ----------------------------------------------------------------------------- 2 | * Copyright (c) 2023 Massachusetts Institute of Technology. 3 | * All Rights Reserved. 4 | * 5 | * AUTHORS: Lukas Schmid , Nathan Hughes 6 | * AFFILIATION: MIT-SPARK Lab, Massachusetts Institute of Technology 7 | * YEAR: 2023 8 | * LICENSE: BSD 3-Clause 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * 13 | * 1. Redistributions of source code must retain the above copyright notice, this 14 | * list of conditions and the following disclaimer. 15 | * 16 | * 2. Redistributions in binary form must reproduce the above copyright notice, 17 | * this list of conditions and the following disclaimer in the documentation 18 | * and/or other materials provided with the distribution. 19 | * 20 | * 3. Neither the name of the copyright holder nor the names of its 21 | * contributors may be used to endorse or promote products derived from 22 | * this software without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | * -------------------------------------------------------------------------- */ 35 | 36 | #pragma once 37 | 38 | #include 39 | #include 40 | #include 41 | 42 | #include 43 | 44 | namespace config { 45 | 46 | /** 47 | * @brief Context for substitution parsing 48 | * 49 | * Together, the suffix, prefix and separator define the substitution grammar 50 | * 51 | * TAG: LETTER (LETTER | DIGIT | "_" | "-")* 52 | * 53 | * expression: .* | substitution | (\S* .* substitution .*)* 54 | * 55 | * substitution: "prefix" TAG "separator" expression "suffix" 56 | */ 57 | struct ParserContext { 58 | //! Prefix for any substitution 59 | std::string prefix = R"""(\$<)"""; 60 | //! Suffix for any substitution 61 | std::string suffix = R"""(>)"""; 62 | //! Separator between substitution tag and substitution input 63 | std::string separator = R"""( *(\|| ) *)"""; 64 | //! Whether or not substitutions are allowed 65 | bool allow_substitutions = true; 66 | //! Name-value pairs for use in substitution 67 | std::map vars; 68 | 69 | //! @brief Flag that an error was encountered in parsing 70 | void error() const { error_ = true; } 71 | 72 | //! @brief Return if an error was encountered 73 | operator bool() const { return !error_; } 74 | 75 | private: 76 | mutable bool error_ = false; 77 | }; 78 | 79 | struct Substitution { 80 | virtual ~Substitution() = default; 81 | 82 | /** 83 | * @brief Process arguments to substitution 84 | * @param[in] contents Arguments following {{ 85 | */ 86 | virtual std::string process(const ParserContext& context, const std::string& contents) const = 0; 87 | }; 88 | 89 | class RegisteredSubstitutions { 90 | public: 91 | template 92 | struct Registration { 93 | Registration(); 94 | }; 95 | 96 | ~RegisteredSubstitutions() = default; 97 | 98 | static const Substitution* getEntry(const std::string& tag); 99 | 100 | private: 101 | template 102 | friend struct Registration; 103 | 104 | static RegisteredSubstitutions& instance(); 105 | 106 | static void addEntry(const std::string& tag, std::unique_ptr&& proc); 107 | 108 | RegisteredSubstitutions(); 109 | static std::unique_ptr s_instance_; 110 | std::map> entries_; 111 | }; 112 | 113 | template 114 | RegisteredSubstitutions::Registration::Registration() { 115 | RegisteredSubstitutions::addEntry(T::NAME, std::make_unique()); 116 | } 117 | 118 | /** 119 | * @brief Iterate through the node, resolving tags 120 | * @param[in] node Node to resolve tags for 121 | */ 122 | void resolveSubstitutions(YAML::Node node, const ParserContext& context = {}, bool strict = true); 123 | 124 | } // namespace config 125 | -------------------------------------------------------------------------------- /config_utilities/include/config_utilities/traits.h: -------------------------------------------------------------------------------- 1 | /** ----------------------------------------------------------------------------- 2 | * Copyright (c) 2023 Massachusetts Institute of Technology. 3 | * All Rights Reserved. 4 | * 5 | * AUTHORS: Lukas Schmid , Nathan Hughes 6 | * AFFILIATION: MIT-SPARK Lab, Massachusetts Institute of Technology 7 | * YEAR: 2023 8 | * LICENSE: BSD 3-Clause 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * 13 | * 1. Redistributions of source code must retain the above copyright notice, this 14 | * list of conditions and the following disclaimer. 15 | * 16 | * 2. Redistributions in binary form must reproduce the above copyright notice, 17 | * this list of conditions and the following disclaimer in the documentation 18 | * and/or other materials provided with the distribution. 19 | * 20 | * 3. Neither the name of the copyright holder nor the names of its 21 | * contributors may be used to endorse or promote products derived from 22 | * this software without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | * -------------------------------------------------------------------------- */ 35 | 36 | #pragma once 37 | 38 | #include 39 | #include 40 | 41 | namespace config { 42 | namespace internal { 43 | 44 | // Check for existence of the function declare_config(T&) via SFINAE. 45 | template 46 | struct is_config_impl : std::false_type {}; 47 | 48 | template 49 | struct is_config_impl()))>> : std::true_type {}; 50 | 51 | // Check whether an object is a virtual config. 52 | template 53 | struct is_virtual_config : std::false_type {}; 54 | 55 | // ODR workaround 56 | template 57 | constexpr T static_const{}; 58 | 59 | // Define which types are considered ints and floats to check overflow. Define these explicitly as we don't want 60 | // std::is_integral to account for bools. 61 | template 62 | constexpr bool is_int = std::is_integral::value && !std::is_same::value; 63 | 64 | } // namespace internal 65 | 66 | /** 67 | * @brief Check whether a function declare_config(T&) is implemented for a struct T. 68 | */ 69 | template 70 | constexpr bool isConfig() { 71 | return internal::is_config_impl::value; 72 | } 73 | 74 | template 75 | constexpr bool isConfig(const T& config) { 76 | return internal::is_config_impl::value; 77 | } 78 | 79 | } // namespace config 80 | -------------------------------------------------------------------------------- /config_utilities/include/config_utilities/types/conversions.h: -------------------------------------------------------------------------------- 1 | /** ----------------------------------------------------------------------------- 2 | * Copyright (c) 2023 Massachusetts Institute of Technology. 3 | * All Rights Reserved. 4 | * 5 | * AUTHORS: Lukas Schmid , Nathan Hughes 6 | * AFFILIATION: MIT-SPARK Lab, Massachusetts Institute of Technology 7 | * YEAR: 2023 8 | * LICENSE: BSD 3-Clause 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * 13 | * 1. Redistributions of source code must retain the above copyright notice, this 14 | * list of conditions and the following disclaimer. 15 | * 16 | * 2. Redistributions in binary form must reproduce the above copyright notice, 17 | * this list of conditions and the following disclaimer in the documentation 18 | * and/or other materials provided with the distribution. 19 | * 20 | * 3. Neither the name of the copyright holder nor the names of its 21 | * contributors may be used to endorse or promote products derived from 22 | * this software without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | * -------------------------------------------------------------------------- */ 35 | 36 | #pragma once 37 | 38 | #include 39 | #include 40 | #include 41 | 42 | namespace config { 43 | 44 | /** @brief Conversion to and from char that avoids issues with integer <-> ascii casts 45 | * 46 | * This parses the config field to a string before extracting the desired character. When 47 | * the parsed string ends up being multiple characters, this grabs the first. Note that this 48 | * disallows non-ascii characters. 49 | */ 50 | struct CharConversion { 51 | static std::string toIntermediate(char value, std::string& error); 52 | static void fromIntermediate(const std::string& intermediate, char& value, std::string& error); 53 | }; 54 | 55 | /** @brief Conversion that remaps a specified number of threads to the total number of avaiable cores 56 | * 57 | * If the original value is less than or equal to 0, this returns the total number of cores detected, 58 | * otherwise it returns the originally specified value. 59 | */ 60 | struct ThreadNumConversion { 61 | static int toIntermediate(int value, std::string& error); 62 | 63 | template 64 | static void fromIntermediate(int intermediate, T& value, std::string& error) { 65 | value = ThreadNumConversion::getNumThreads(intermediate); 66 | } 67 | 68 | static int getNumThreads(int intermediate); 69 | }; 70 | 71 | } // namespace config 72 | -------------------------------------------------------------------------------- /config_utilities/include/config_utilities/validation.h: -------------------------------------------------------------------------------- 1 | /** ----------------------------------------------------------------------------- 2 | * Copyright (c) 2023 Massachusetts Institute of Technology. 3 | * All Rights Reserved. 4 | * 5 | * AUTHORS: Lukas Schmid , Nathan Hughes 6 | * AFFILIATION: MIT-SPARK Lab, Massachusetts Institute of Technology 7 | * YEAR: 2023 8 | * LICENSE: BSD 3-Clause 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * 13 | * 1. Redistributions of source code must retain the above copyright notice, this 14 | * list of conditions and the following disclaimer. 15 | * 16 | * 2. Redistributions in binary form must reproduce the above copyright notice, 17 | * this list of conditions and the following disclaimer in the documentation 18 | * and/or other materials provided with the distribution. 19 | * 20 | * 3. Neither the name of the copyright holder nor the names of its 21 | * contributors may be used to endorse or promote products derived from 22 | * this software without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | * -------------------------------------------------------------------------- */ 35 | 36 | #pragma once 37 | 38 | #include 39 | #include 40 | #include 41 | 42 | #include "config_utilities/internal/formatter.h" 43 | #include "config_utilities/internal/logger.h" 44 | #include "config_utilities/internal/visitor.h" 45 | #include "config_utilities/settings.h" 46 | #include "config_utilities/traits.h" 47 | 48 | namespace config { 49 | 50 | namespace internal { 51 | 52 | bool hasNoInvalidChecks(const MetaData& data); 53 | 54 | } // namespace internal 55 | 56 | /** 57 | * @brief Check if a config is valid. 58 | * 59 | * @tparam ConfigT The config type. 60 | * @param config The config to check. 61 | * @param print_warnings Whether to print warnings if the config is not valid. This is off by default. 62 | * @returns True if the config is valid, false otherwise. 63 | */ 64 | template 65 | bool isValid(const ConfigT& config, bool print_warnings = false) { 66 | static_assert(isConfig(), 67 | "Can not use 'config::isValid()' on non-config type. Please implement 'void declare_config(ConfigT&)' " 68 | "for your struct."); 69 | internal::MetaData data = internal::Visitor::getChecks(config); 70 | 71 | if (internal::hasNoInvalidChecks(data)) { 72 | return true; 73 | } 74 | if (print_warnings) { 75 | internal::Logger::logWarning( 76 | internal::Formatter::formatErrors(data, "Invalid config", internal::Severity::kWarning)); 77 | } 78 | return false; 79 | } 80 | 81 | /** 82 | * @brief Assert that a config is valid. This will terminate the program if invalid. 83 | * 84 | * @tparam ConfigT The config type. 85 | * @param config The config to check. 86 | */ 87 | template 88 | const ConfigT& checkValid(const ConfigT& config) { 89 | static_assert( 90 | isConfig(), 91 | "Can not use 'config::checkValid()' on non-config type. Please implement 'void declare_config(ConfigT&)' " 92 | "for your struct."); 93 | internal::MetaData data = internal::Visitor::getChecks(config); 94 | 95 | if (internal::hasNoInvalidChecks(data)) { 96 | return config; 97 | } 98 | 99 | internal::Logger::logFatal(internal::Formatter::formatErrors(data, "Invalid config", internal::Severity::kFatal)); 100 | return config; 101 | } 102 | 103 | } // namespace config 104 | -------------------------------------------------------------------------------- /config_utilities/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | config_utilities 4 | 0.0.1 5 | Tools for working with C++ config structs. 6 | 7 | Lukas Schmid 8 | Nathan Hughes 9 | 10 | Lukas Schmid 11 | Nathan Hughes 12 | 13 | BSD-3-Clause 14 | 15 | cmake 16 | yaml-cpp 17 | 18 | 19 | cmake 20 | 21 | 22 | -------------------------------------------------------------------------------- /config_utilities/scripts/run_demo.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # ------------------------------------------------------------------------------ 3 | # Copyright (c) 2023, Massachusetts Institute of Technology. 4 | # All Rights Reserved 5 | # 6 | # AUTHORS: Lukas Schmid , Nathan Hughes 7 | # AFFILIATION: MIT-SPARK Lab, Massachusetts Institute of Technology 8 | # YEAR: 2023 9 | # LICENSE: BSD 3-Clause 10 | # 11 | # Redistribution and use in source and binary forms, with or without 12 | # modification, are permitted provided that the following conditions are met: 13 | # 14 | # 1. Redistributions of source code must retain the above copyright notice, this 15 | # list of conditions and the following disclaimer. 16 | # 17 | # 2. Redistributions in binary form must reproduce the above copyright notice, 18 | # this list of conditions and the following disclaimer in the documentation 19 | # and/or other materials provided with the distribution. 20 | # 21 | # 3. Neither the name of the copyright holder nor the names of its 22 | # contributors may be used to endorse or promote products derived from 23 | # this software without specific prior written permission. 24 | # 25 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 26 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 27 | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 28 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 29 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 31 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 32 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 33 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 34 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 | # ------------------------------------------------------------------------------ 36 | """Shim script for running one of the demo executables.""" 37 | import pathlib 38 | import subprocess 39 | import argparse 40 | import sys 41 | 42 | 43 | def _autodetect_build_path(): 44 | script_path = pathlib.Path(__file__).absolute().parent 45 | ret = subprocess.run( 46 | ["catkin", "locate", "-b", "config_utilities"], 47 | stdout=subprocess.PIPE, 48 | stderr=subprocess.PIPE, 49 | cwd=str(script_path), 50 | check=False, 51 | ) 52 | if ret.returncode != 0: 53 | return None 54 | 55 | return pathlib.Path(ret.stdout.decode("utf-8").strip("\n")) 56 | 57 | 58 | def _get_resource_path(): 59 | project_dir = pathlib.Path(__file__).absolute().parent.parent 60 | return project_dir / "demos" / "resources" 61 | 62 | 63 | def _verify_name(build_path, name): 64 | demo_path = build_path / "demos" 65 | executable_path = demo_path / f"demo_{name}" 66 | if not executable_path.exists(): 67 | demos = [x for x in demo_path.glob("demo_*")] 68 | print("available demos:") 69 | for demo in demos: 70 | print(f" - {demo.stem[5:]}") 71 | 72 | return None 73 | 74 | return executable_path 75 | 76 | 77 | def main(): 78 | """Run script.""" 79 | parser = argparse.ArgumentParser( 80 | description="shim for running demo executables") 81 | parser.add_argument("name", help="executable name") 82 | parser.add_argument("--build_path", 83 | "-b", 84 | help="path to build directory", 85 | nargs="?", 86 | default=None) 87 | args = parser.parse_args() 88 | 89 | resource_path = _get_resource_path() 90 | print(f"using resource path: {resource_path}") 91 | 92 | if not args.build_path: 93 | build_path = _autodetect_build_path() 94 | if not build_path: 95 | print( 96 | "Unable to detect build path! Specify manually with -b/--build_path" 97 | ) 98 | sys.exit(1) 99 | else: 100 | build_path = pathlib.Path(args.build_path) 101 | 102 | if not build_path.exists(): 103 | print(f"{build_path} does not exist") 104 | sys.exit(1) 105 | 106 | executable_path = _verify_name(build_path, args.name) 107 | if not executable_path: 108 | print("invalid demo name") 109 | sys.exit(1) 110 | 111 | subprocess.run([str(executable_path), str(resource_path)], check=False) 112 | 113 | 114 | if __name__ == "__main__": 115 | main() 116 | -------------------------------------------------------------------------------- /config_utilities/src/config_context.cpp: -------------------------------------------------------------------------------- 1 | /** ----------------------------------------------------------------------------- 2 | * Copyright (c) 2023 Massachusetts Institute of Technology. 3 | * All Rights Reserved. 4 | * 5 | * AUTHORS: Lukas Schmid , Nathan Hughes 6 | * AFFILIATION: MIT-SPARK Lab, Massachusetts Institute of Technology 7 | * YEAR: 2023 8 | * LICENSE: BSD 3-Clause 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * 13 | * 1. Redistributions of source code must retain the above copyright notice, this 14 | * list of conditions and the following disclaimer. 15 | * 16 | * 2. Redistributions in binary form must reproduce the above copyright notice, 17 | * this list of conditions and the following disclaimer in the documentation 18 | * and/or other materials provided with the distribution. 19 | * 20 | * 3. Neither the name of the copyright holder nor the names of its 21 | * contributors may be used to endorse or promote products derived from 22 | * this software without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | * -------------------------------------------------------------------------- */ 35 | 36 | #include "config_utilities/internal/config_context.h" 37 | 38 | #include "config_utilities/internal/yaml_utils.h" 39 | 40 | namespace config::internal { 41 | 42 | Context& Context::instance() { 43 | static Context s_instance; 44 | return s_instance; 45 | } 46 | 47 | void Context::update(const YAML::Node& other, const std::string& ns) { 48 | auto& context = instance(); 49 | auto node = YAML::Clone(other); 50 | moveDownNamespace(node, ns); 51 | // default behavior of context is to act like the ROS1 param server and extend sequences 52 | mergeYamlNodes(context.contents_, node, MergeMode::APPEND); 53 | } 54 | 55 | void Context::clear() { instance().contents_ = YAML::Node(); } 56 | 57 | YAML::Node Context::toYaml() { return YAML::Clone(instance().contents_); } 58 | 59 | } // namespace config::internal 60 | -------------------------------------------------------------------------------- /config_utilities/src/context.cpp: -------------------------------------------------------------------------------- 1 | /** ----------------------------------------------------------------------------- 2 | * Copyright (c) 2023 Massachusetts Institute of Technology. 3 | * All Rights Reserved. 4 | * 5 | * AUTHORS: Lukas Schmid , Nathan Hughes 6 | * AFFILIATION: MIT-SPARK Lab, Massachusetts Institute of Technology 7 | * YEAR: 2023 8 | * LICENSE: BSD 3-Clause 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * 13 | * 1. Redistributions of source code must retain the above copyright notice, this 14 | * list of conditions and the following disclaimer. 15 | * 16 | * 2. Redistributions in binary form must reproduce the above copyright notice, 17 | * this list of conditions and the following disclaimer in the documentation 18 | * and/or other materials provided with the distribution. 19 | * 20 | * 3. Neither the name of the copyright holder nor the names of its 21 | * contributors may be used to endorse or promote products derived from 22 | * this software without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | * -------------------------------------------------------------------------- */ 35 | 36 | #include "config_utilities/parsing/context.h" 37 | 38 | #include "config_utilities/internal/config_context.h" 39 | #include "config_utilities/parsing/commandline.h" 40 | 41 | namespace config { 42 | 43 | void initContext(int& argc, char* argv[], bool remove_arguments) { 44 | const auto node = internal::loadFromArguments(argc, argv, remove_arguments); 45 | internal::Context::update(node, ""); 46 | } 47 | 48 | void pushToContext(const YAML::Node& node, const std::string& ns) { internal::Context::update(node, ns); } 49 | 50 | void clearContext() { internal::Context::clear(); } 51 | 52 | YAML::Node contextToYaml() { return internal::Context::toYaml(); } 53 | 54 | } // namespace config 55 | -------------------------------------------------------------------------------- /config_utilities/src/conversions.cpp: -------------------------------------------------------------------------------- 1 | /** ----------------------------------------------------------------------------- 2 | * Copyright (c) 2023 Massachusetts Institute of Technology. 3 | * All Rights Reserved. 4 | * 5 | * AUTHORS: Lukas Schmid , Nathan Hughes 6 | * AFFILIATION: MIT-SPARK Lab, Massachusetts Institute of Technology 7 | * YEAR: 2023 8 | * LICENSE: BSD 3-Clause 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * 13 | * 1. Redistributions of source code must retain the above copyright notice, this 14 | * list of conditions and the following disclaimer. 15 | * 16 | * 2. Redistributions in binary form must reproduce the above copyright notice, 17 | * this list of conditions and the following disclaimer in the documentation 18 | * and/or other materials provided with the distribution. 19 | * 20 | * 3. Neither the name of the copyright holder nor the names of its 21 | * contributors may be used to endorse or promote products derived from 22 | * this software without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | * -------------------------------------------------------------------------- */ 35 | 36 | #include "config_utilities/types/conversions.h" 37 | 38 | #include 39 | 40 | #include "config_utilities/internal/formatter.h" 41 | #include "config_utilities/internal/logger.h" 42 | #include "config_utilities/internal/visitor.h" 43 | 44 | namespace config { 45 | 46 | std::string CharConversion::toIntermediate(char value, std::string&) { return {value}; } 47 | 48 | void CharConversion::fromIntermediate(const std::string& intermediate, char& value, std::string& error) { 49 | if (intermediate.empty()) { 50 | error = "Unable to parse char from empty string"; 51 | return; 52 | } 53 | 54 | if (intermediate.size() > 1) { 55 | error = "Multiple character string will result in the first character being used"; 56 | return; 57 | } 58 | value = intermediate.at(0); 59 | } 60 | 61 | int ThreadNumConversion::toIntermediate(int value, std::string&) { return value; } 62 | 63 | int ThreadNumConversion::getNumThreads(int intermediate) { 64 | return intermediate <= 0 ? std::thread::hardware_concurrency() : intermediate; 65 | } 66 | 67 | } // namespace config 68 | -------------------------------------------------------------------------------- /config_utilities/src/formatter.cpp: -------------------------------------------------------------------------------- 1 | /** ----------------------------------------------------------------------------- 2 | * Copyright (c) 2023 Massachusetts Institute of Technology. 3 | * All Rights Reserved. 4 | * 5 | * AUTHORS: Lukas Schmid , Nathan Hughes 6 | * AFFILIATION: MIT-SPARK Lab, Massachusetts Institute of Technology 7 | * YEAR: 2023 8 | * LICENSE: BSD 3-Clause 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * 13 | * 1. Redistributions of source code must retain the above copyright notice, this 14 | * list of conditions and the following disclaimer. 15 | * 16 | * 2. Redistributions in binary form must reproduce the above copyright notice, 17 | * this list of conditions and the following disclaimer in the documentation 18 | * and/or other materials provided with the distribution. 19 | * 20 | * 3. Neither the name of the copyright holder nor the names of its 21 | * contributors may be used to endorse or promote products derived from 22 | * this software without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | * -------------------------------------------------------------------------- */ 35 | 36 | #include "config_utilities/internal/formatter.h" 37 | 38 | namespace config::internal { 39 | 40 | std::string Formatter::formatErrors(const MetaData& data, const std::string& what, const Severity severity) { 41 | return formatter_->formatErrorsImpl(data, what, severity); 42 | } 43 | 44 | std::string Formatter::formatMissing(const MetaData& data, const std::string& what, const Severity severity) { 45 | return formatter_->formatMissingImpl(data, what, severity); 46 | } 47 | 48 | std::string Formatter::formatConfig(const MetaData& data) { return formatter_->formatConfigImpl(data); } 49 | 50 | std::string Formatter::formatConfigs(const std::vector& data) { return formatter_->formatConfigsImpl(data); } 51 | 52 | void Formatter::setFormatter(Formatter::Ptr formatter) { 53 | if (formatter) { 54 | formatter_ = std::move(formatter); 55 | } 56 | } 57 | 58 | std::string Formatter::formatErrorsImpl(const MetaData& data, const std::string& what, const Severity severity) { 59 | return getUnspecifiedString(); 60 | } 61 | 62 | std::string Formatter::formatMissingImpl(const MetaData& data, const std::string& what, const Severity severity) { 63 | return getUnspecifiedString(); 64 | } 65 | 66 | std::string Formatter::formatConfigImpl(const MetaData& data) { return getUnspecifiedString(); } 67 | 68 | std::string Formatter::formatConfigsImpl(const std::vector& data) { return getUnspecifiedString(); } 69 | 70 | std::string Formatter::getUnspecifiedString() const { 71 | return "No format specified. Specify a format by including one of 'config_utilities/formatters/.h'."; 72 | } 73 | 74 | } // namespace config::internal 75 | -------------------------------------------------------------------------------- /config_utilities/src/log_to_stdout.cpp: -------------------------------------------------------------------------------- 1 | /** ----------------------------------------------------------------------------- 2 | * Copyright (c) 2023 Massachusetts Institute of Technology. 3 | * All Rights Reserved. 4 | * 5 | * AUTHORS: Lukas Schmid , Nathan Hughes 6 | * AFFILIATION: MIT-SPARK Lab, Massachusetts Institute of Technology 7 | * YEAR: 2023 8 | * LICENSE: BSD 3-Clause 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * 13 | * 1. Redistributions of source code must retain the above copyright notice, this 14 | * list of conditions and the following disclaimer. 15 | * 16 | * 2. Redistributions in binary form must reproduce the above copyright notice, 17 | * this list of conditions and the following disclaimer in the documentation 18 | * and/or other materials provided with the distribution. 19 | * 20 | * 3. Neither the name of the copyright holder nor the names of its 21 | * contributors may be used to endorse or promote products derived from 22 | * this software without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | * -------------------------------------------------------------------------- */ 35 | 36 | #include "config_utilities/logging/log_to_stdout.h" 37 | 38 | #include 39 | #include 40 | 41 | namespace config::internal { 42 | 43 | StdoutLogger::StdoutLogger(Severity min_severity, Severity stderr_severity) 44 | : min_severity_(min_severity), stderr_severity_(stderr_severity) {} 45 | 46 | void StdoutLogger::logImpl(const Severity severity, const std::string& message) { 47 | if (severity < min_severity_ && severity != Severity::kFatal) { 48 | return; 49 | } 50 | 51 | std::stringstream ss; 52 | switch (severity) { 53 | case Severity::kInfo: 54 | ss << "[INFO] " << message; 55 | break; 56 | 57 | case Severity::kWarning: 58 | ss << "\033[33m[WARNING] " << message << "\033[0m"; 59 | break; 60 | 61 | case Severity::kError: 62 | ss << "\033[31m[ERROR] " << message << "\033[0m"; 63 | break; 64 | 65 | case Severity::kFatal: 66 | throw std::runtime_error(message); 67 | } 68 | 69 | if (severity < stderr_severity_) { 70 | std::cout << ss.str() << std::endl; 71 | } else { 72 | std::cerr << ss.str() << std::endl; 73 | } 74 | } 75 | 76 | StdoutLogger::Initializer::Initializer() { Logger::setLogger(std::make_shared()); } 77 | 78 | } // namespace config::internal 79 | -------------------------------------------------------------------------------- /config_utilities/src/logger.cpp: -------------------------------------------------------------------------------- 1 | /** ----------------------------------------------------------------------------- 2 | * Copyright (c) 2023 Massachusetts Institute of Technology. 3 | * All Rights Reserved. 4 | * 5 | * AUTHORS: Lukas Schmid , Nathan Hughes 6 | * AFFILIATION: MIT-SPARK Lab, Massachusetts Institute of Technology 7 | * YEAR: 2023 8 | * LICENSE: BSD 3-Clause 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * 13 | * 1. Redistributions of source code must retain the above copyright notice, this 14 | * list of conditions and the following disclaimer. 15 | * 16 | * 2. Redistributions in binary form must reproduce the above copyright notice, 17 | * this list of conditions and the following disclaimer in the documentation 18 | * and/or other materials provided with the distribution. 19 | * 20 | * 3. Neither the name of the copyright holder nor the names of its 21 | * contributors may be used to endorse or promote products derived from 22 | * this software without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | * -------------------------------------------------------------------------- */ 35 | 36 | #include "config_utilities/internal/logger.h" 37 | 38 | #include 39 | 40 | #include "config_utilities/logging/log_to_stdout.h" 41 | 42 | namespace config::internal { 43 | 44 | Logger::Ptr Logger::logger_; 45 | 46 | std::string severityToString(const Severity severity) { 47 | switch (severity) { 48 | case Severity::kInfo: 49 | return "Info"; 50 | case Severity::kWarning: 51 | return "Warning"; 52 | case Severity::kError: 53 | return "Error"; 54 | case Severity::kFatal: 55 | return "Fatal"; 56 | } 57 | return "UNKNOWN"; 58 | } 59 | 60 | void Logger::log(const Severity severity, const std::string& message) { dispatch(severity, message); } 61 | 62 | void Logger::logInfo(const std::string& message) { dispatch(Severity::kInfo, message); } 63 | 64 | void Logger::logWarning(const std::string& message) { dispatch(Severity::kWarning, message); } 65 | 66 | void Logger::logError(const std::string& message) { dispatch(Severity::kError, message); } 67 | 68 | void Logger::logFatal(const std::string& message) { dispatch(Severity::kFatal, message); } 69 | 70 | void Logger::setLogger(Logger::Ptr logger) { 71 | if (logger) { 72 | logger_ = std::move(logger); 73 | } 74 | } 75 | 76 | void Logger::dispatch(const Severity severity, const std::string& message) { 77 | if (!logger_) { 78 | // NOTE(nathan) we default to logging to stdout/stderr to make sure warnings and errors are visible 79 | logger_ = Settings::instance().disable_default_stdout_logger ? std::make_shared() 80 | : std::make_shared(); 81 | } 82 | 83 | logger_->logImpl(severity, message); 84 | } 85 | 86 | void Logger::logImpl(const Severity severity, const std::string& message) { 87 | // Empty logger does not log anything. Implementations of logFatal are expected to stop execution of the program. 88 | if (severity == Severity::kFatal) { 89 | throw std::runtime_error("FATAL: " + message); 90 | } 91 | } 92 | 93 | } // namespace config::internal 94 | -------------------------------------------------------------------------------- /config_utilities/src/meta_data.cpp: -------------------------------------------------------------------------------- 1 | /** ----------------------------------------------------------------------------- 2 | * Copyright (c) 2023 Massachusetts Institute of Technology. 3 | * All Rights Reserved. 4 | * 5 | * AUTHORS: Lukas Schmid , Nathan Hughes 6 | * AFFILIATION: MIT-SPARK Lab, Massachusetts Institute of Technology 7 | * YEAR: 2023 8 | * LICENSE: BSD 3-Clause 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * 13 | * 1. Redistributions of source code must retain the above copyright notice, this 14 | * list of conditions and the following disclaimer. 15 | * 16 | * 2. Redistributions in binary form must reproduce the above copyright notice, 17 | * this list of conditions and the following disclaimer in the documentation 18 | * and/or other materials provided with the distribution. 19 | * 20 | * 3. Neither the name of the copyright holder nor the names of its 21 | * contributors may be used to endorse or promote products derived from 22 | * this software without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | * -------------------------------------------------------------------------- */ 35 | 36 | #include "config_utilities/internal/meta_data.h" 37 | 38 | namespace config::internal { 39 | 40 | bool MetaData::hasErrors() const { 41 | if (!errors.empty()) { 42 | return true; 43 | } 44 | for (const auto& sub_config : sub_configs) { 45 | if (sub_config.hasErrors()) { 46 | return true; 47 | } 48 | } 49 | return false; 50 | } 51 | 52 | bool MetaData::hasMissing() const { 53 | // first check all current fields 54 | for (const auto& field_info : field_infos) { 55 | if (!field_info.was_parsed) { 56 | return true; 57 | } 58 | } 59 | 60 | // next check all subconfigs recursively 61 | for (const auto& sub_config : sub_configs) { 62 | if (sub_config.hasMissing()) { 63 | return true; 64 | } 65 | } 66 | 67 | return false; 68 | } 69 | 70 | void MetaData::performOnAll(const std::function& func) { 71 | func(*this); 72 | for (MetaData& sub_config : sub_configs) { 73 | sub_config.performOnAll(func); 74 | } 75 | } 76 | 77 | void MetaData::performOnAll(const std::function& func) const { 78 | func(*this); 79 | for (const MetaData& sub_config : sub_configs) { 80 | sub_config.performOnAll(func); 81 | } 82 | } 83 | 84 | } // namespace config::internal 85 | -------------------------------------------------------------------------------- /config_utilities/src/namespacing.cpp: -------------------------------------------------------------------------------- 1 | /** ----------------------------------------------------------------------------- 2 | * Copyright (c) 2023 Massachusetts Institute of Technology. 3 | * All Rights Reserved. 4 | * 5 | * AUTHORS: Lukas Schmid , Nathan Hughes 6 | * AFFILIATION: MIT-SPARK Lab, Massachusetts Institute of Technology 7 | * YEAR: 2023 8 | * LICENSE: BSD 3-Clause 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * 13 | * 1. Redistributions of source code must retain the above copyright notice, this 14 | * list of conditions and the following disclaimer. 15 | * 16 | * 2. Redistributions in binary form must reproduce the above copyright notice, 17 | * this list of conditions and the following disclaimer in the documentation 18 | * and/or other materials provided with the distribution. 19 | * 20 | * 3. Neither the name of the copyright holder nor the names of its 21 | * contributors may be used to endorse or promote products derived from 22 | * this software without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | * -------------------------------------------------------------------------- */ 35 | 36 | #include "config_utilities/internal/namespacing.h" 37 | 38 | #include "config_utilities/internal/logger.h" 39 | #include "config_utilities/internal/string_utils.h" 40 | #include "config_utilities/internal/visitor.h" 41 | 42 | namespace config { 43 | 44 | NameSpace::NameSpace(const std::string& name_space) : sub_ns_(name_space) { enter(); } 45 | 46 | NameSpace::~NameSpace() { 47 | if (is_open_ && internal::Visitor::hasInstance()) { 48 | exit(); 49 | } 50 | } 51 | 52 | void NameSpace::enter() { 53 | if (is_open_) { 54 | internal::Logger::logWarning("NameSpace::enter() called on already open namespace."); 55 | return; 56 | } 57 | 58 | internal::Visitor& visitor = internal::Visitor::instance(); 59 | previous_ns_ = visitor.name_space; 60 | visitor.name_space = internal::joinNamespace(previous_ns_, sub_ns_); 61 | is_open_ = true; 62 | } 63 | 64 | void NameSpace::exit() { 65 | if (!is_open_) { 66 | internal::Logger::logWarning("NameSpace::exit() called on already closed namespace."); 67 | return; 68 | } 69 | 70 | // Verify that this namespace has not been implicitly closed by a previous sub-namespace. 71 | internal::Visitor& visitor = internal::Visitor::instance(); 72 | if (visitor.name_space.find(previous_ns_) != 0) { 73 | // NOTE(lschmid): This results still in valid namespace behavior, but we warn the user that this is not intended. 74 | internal::Logger::logWarning("NameSpace::exit() called on namespace that was implicitly closed."); 75 | return; 76 | } 77 | 78 | // NOTE(lschmid): If the namespace is exited in a different order than it was entered, this simply closes all 79 | // namespaces that were opened later which is ok behavior. 80 | visitor.name_space = previous_ns_; 81 | is_open_ = false; 82 | } 83 | 84 | void enter_namespace(const std::string& name_space) { 85 | internal::Visitor::instance().open_namespaces.emplace_back(std::make_unique(name_space)); 86 | } 87 | 88 | void exit_namespace() { 89 | internal::Visitor& visitor = internal::Visitor::instance(); 90 | 91 | if (visitor.open_namespaces.empty()) { 92 | internal::Logger::logWarning("exit_namespace() called on empty namespace stack."); 93 | return; 94 | } 95 | if (!visitor.open_namespaces.back()->isLocked()) { 96 | visitor.open_namespaces.pop_back(); 97 | } 98 | } 99 | 100 | void clear_namespaces() { 101 | internal::Visitor& visitor = internal::Visitor::instance(); 102 | while (!visitor.open_namespaces.empty() && !visitor.open_namespaces.back()->isLocked()) { 103 | visitor.open_namespaces.pop_back(); 104 | } 105 | } 106 | 107 | void switch_namespace(const std::string& name_space) { 108 | exit_namespace(); 109 | enter_namespace(name_space); 110 | } 111 | 112 | std::string current_namespace() { return internal::Visitor::instance().name_space; } 113 | 114 | internal::OpenNameSpace::OpenNameSpace(const std::string& name_space) : ns(name_space) {} 115 | 116 | void internal::OpenNameSpace::lock() { ++locks; } 117 | 118 | void internal::OpenNameSpace::unlock() { 119 | if (locks > 0) { 120 | --locks; 121 | } 122 | } 123 | 124 | bool internal::OpenNameSpace::isLocked() const { return locks > 0; } 125 | 126 | void internal::OpenNameSpace::performOperationWithGuardedNs(Stack& stack, const std::function& operation) { 127 | // Lock each namespace in the stack. This should prevent namespace operations from changing existing namespaces. 128 | for (const auto& ns : stack) { 129 | ns->lock(); 130 | } 131 | 132 | // Perform the operation. 133 | operation(); 134 | 135 | // Unlock each namespace in the stack. 136 | for (const auto& ns : stack) { 137 | ns->unlock(); 138 | } 139 | 140 | // Remove all trailing namespaces that are not locked anymore. 141 | while (!stack.empty() && !stack.back()->isLocked()) { 142 | stack.pop_back(); 143 | } 144 | } 145 | 146 | } // namespace config 147 | -------------------------------------------------------------------------------- /config_utilities/src/settings.cpp: -------------------------------------------------------------------------------- 1 | /** ----------------------------------------------------------------------------- 2 | * Copyright (c) 2023 Massachusetts Institute of Technology. 3 | * All Rights Reserved. 4 | * 5 | * AUTHORS: Lukas Schmid , Nathan Hughes 6 | * AFFILIATION: MIT-SPARK Lab, Massachusetts Institute of Technology 7 | * YEAR: 2023 8 | * LICENSE: BSD 3-Clause 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * 13 | * 1. Redistributions of source code must retain the above copyright notice, this 14 | * list of conditions and the following disclaimer. 15 | * 16 | * 2. Redistributions in binary form must reproduce the above copyright notice, 17 | * this list of conditions and the following disclaimer in the documentation 18 | * and/or other materials provided with the distribution. 19 | * 20 | * 3. Neither the name of the copyright holder nor the names of its 21 | * contributors may be used to endorse or promote products derived from 22 | * this software without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | * -------------------------------------------------------------------------- */ 35 | 36 | #include "config_utilities/settings.h" 37 | 38 | #include 39 | 40 | #include "config_utilities/factory.h" 41 | #include "config_utilities/internal/formatter.h" 42 | #include "config_utilities/internal/logger.h" 43 | 44 | namespace config::internal { 45 | 46 | Settings Settings::instance_; 47 | 48 | Settings& Settings::instance() { return instance_; } 49 | 50 | void Settings::setLogger(const std::string& name) { 51 | if (name == "none") { 52 | Logger::setLogger(std::make_unique()); 53 | return; 54 | } 55 | std::unique_ptr new_logger = create(name); 56 | if (new_logger) { 57 | Logger::setLogger(std::move(new_logger)); 58 | } 59 | } 60 | 61 | void Settings::setFormatter(const std::string& name) { 62 | if (name == "none") { 63 | Formatter::setFormatter(std::make_unique()); 64 | return; 65 | } 66 | std::unique_ptr new_formatter = create(name); 67 | if (new_formatter) { 68 | Formatter::setFormatter(std::move(new_formatter)); 69 | } 70 | } 71 | 72 | } // namespace config::internal 73 | -------------------------------------------------------------------------------- /config_utilities/src/substitution_parsers.cpp: -------------------------------------------------------------------------------- 1 | /** ----------------------------------------------------------------------------- 2 | * Copyright (c) 2023 Massachusetts Institute of Technology. 3 | * All Rights Reserved. 4 | * 5 | * AUTHORS: Lukas Schmid , Nathan Hughes 6 | * AFFILIATION: MIT-SPARK Lab, Massachusetts Institute of Technology 7 | * YEAR: 2023 8 | * LICENSE: BSD 3-Clause 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * 13 | * 1. Redistributions of source code must retain the above copyright notice, this 14 | * list of conditions and the following disclaimer. 15 | * 16 | * 2. Redistributions in binary form must reproduce the above copyright notice, 17 | * this list of conditions and the following disclaimer in the documentation 18 | * and/or other materials provided with the distribution. 19 | * 20 | * 3. Neither the name of the copyright holder nor the names of its 21 | * contributors may be used to endorse or promote products derived from 22 | * this software without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | * -------------------------------------------------------------------------- */ 35 | 36 | #include "config_utilities/substitution_parsers.h" 37 | 38 | #include 39 | 40 | #include "config_utilities/internal/logger.h" 41 | 42 | namespace config { 43 | namespace { 44 | 45 | static const auto env_reg = RegisteredSubstitutions::Registration(); 46 | static const auto var_reg = RegisteredSubstitutions::Registration(); 47 | 48 | } // namespace 49 | 50 | std::string EnvSubstitution::process(const ParserContext& context, const std::string& contents) const { 51 | const auto ret = std::getenv(contents.c_str()); 52 | if (!ret) { 53 | context.error(); 54 | internal::Logger::logError("Failed to get envname from '" + contents + "'"); 55 | return contents; 56 | } 57 | 58 | return std::string(ret); 59 | } 60 | 61 | std::string VarSubstitution::process(const ParserContext& context, const std::string& contents) const { 62 | auto iter = context.vars.find(contents); 63 | if (iter == context.vars.end()) { 64 | internal::Logger::logError("Unknown var '" + contents + "'"); 65 | context.error(); 66 | return contents; 67 | } 68 | 69 | return iter->second; 70 | } 71 | 72 | } // namespace config 73 | -------------------------------------------------------------------------------- /config_utilities/src/validation.cpp: -------------------------------------------------------------------------------- 1 | /** ----------------------------------------------------------------------------- 2 | * Copyright (c) 2023 Massachusetts Institute of Technology. 3 | * All Rights Reserved. 4 | * 5 | * AUTHORS: Lukas Schmid , Nathan Hughes 6 | * AFFILIATION: MIT-SPARK Lab, Massachusetts Institute of Technology 7 | * YEAR: 2023 8 | * LICENSE: BSD 3-Clause 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * 13 | * 1. Redistributions of source code must retain the above copyright notice, this 14 | * list of conditions and the following disclaimer. 15 | * 16 | * 2. Redistributions in binary form must reproduce the above copyright notice, 17 | * this list of conditions and the following disclaimer in the documentation 18 | * and/or other materials provided with the distribution. 19 | * 20 | * 3. Neither the name of the copyright holder nor the names of its 21 | * contributors may be used to endorse or promote products derived from 22 | * this software without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | * -------------------------------------------------------------------------- */ 35 | 36 | #include "config_utilities/validation.h" 37 | 38 | namespace config::internal { 39 | 40 | bool hasNoInvalidChecks(const MetaData& data) { 41 | bool is_valid = true; 42 | data.performOnAll([&is_valid](const MetaData& d) { 43 | for (const auto& check : d.checks) { 44 | if (!check->valid() || !is_valid) { 45 | is_valid = false; 46 | return; 47 | } 48 | } 49 | }); 50 | return is_valid; 51 | } 52 | 53 | } // namespace config::internal 54 | -------------------------------------------------------------------------------- /config_utilities/src/visitor.cpp: -------------------------------------------------------------------------------- 1 | /** ----------------------------------------------------------------------------- 2 | * Copyright (c) 2023 Massachusetts Institute of Technology. 3 | * All Rights Reserved. 4 | * 5 | * AUTHORS: Lukas Schmid , Nathan Hughes 6 | * AFFILIATION: MIT-SPARK Lab, Massachusetts Institute of Technology 7 | * YEAR: 2023 8 | * LICENSE: BSD 3-Clause 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * 13 | * 1. Redistributions of source code must retain the above copyright notice, this 14 | * list of conditions and the following disclaimer. 15 | * 16 | * 2. Redistributions in binary form must reproduce the above copyright notice, 17 | * this list of conditions and the following disclaimer in the documentation 18 | * and/or other materials provided with the distribution. 19 | * 20 | * 3. Neither the name of the copyright holder nor the names of its 21 | * contributors may be used to endorse or promote products derived from 22 | * this software without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | * -------------------------------------------------------------------------- */ 35 | 36 | #include "config_utilities/internal/visitor.h" 37 | 38 | #include 39 | 40 | #include "config_utilities/internal/yaml_utils.h" 41 | #include "config_utilities/settings.h" 42 | 43 | namespace config::internal { 44 | 45 | thread_local std::vector Visitor::instances = {}; 46 | 47 | Visitor::~Visitor() { instances.pop_back(); } 48 | 49 | bool Visitor::hasInstance() { return !instances.empty(); } 50 | 51 | Visitor::Visitor(Mode _mode, const std::string& _name_space, const std::string& field_name) 52 | : mode(_mode), name_space(_name_space) { 53 | data.field_name = field_name; 54 | // Create instances in a stack per thread and store the reference to it. 55 | instances.emplace_back(this); 56 | } 57 | 58 | Visitor& Visitor::instance() { 59 | if (instances.empty()) { 60 | // This should never happen as visitors are managed internally. Caught here for debugging. 61 | throw std::runtime_error( 62 | "Visitor instance was accessed but no visitor was created before. Visitor::instance() should only be called " 63 | "from within a visitor."); 64 | } 65 | return *instances.back(); 66 | } 67 | 68 | void Visitor::visitName(const std::string& name) { 69 | std::string& current_name = Visitor::instance().data.name; 70 | // Avoid overriding names. If the name is to be set, it should be cleared previously. 71 | if (current_name.empty()) { 72 | current_name = name; 73 | } 74 | } 75 | 76 | void Visitor::visitCheck(const CheckBase& check) { 77 | Visitor& visitor = Visitor::instance(); 78 | if (visitor.mode != Visitor::Mode::kCheck) { 79 | return; 80 | } 81 | visitor.data.checks.emplace_back(check.clone()); 82 | } 83 | 84 | std::optional Visitor::visitVirtualConfig(bool is_set, bool is_optional, const std::string& type) { 85 | Visitor& visitor = Visitor::instance(); 86 | visitor.data.is_virtual_config = true; 87 | 88 | // Treat the validity of virtual configs as checks. 89 | if (visitor.mode == Visitor::Mode::kCheck) { 90 | if (!is_set && !is_optional) { 91 | const std::string field_name = visitor.data.field_name.empty() ? "" : "'" + visitor.data.field_name + "' "; 92 | visitor.data.checks.emplace_back( 93 | new Check(false, "Virtual config " + field_name + "is not set and not marked optional")); 94 | } else { 95 | visitor.data.checks.emplace_back(new Check(true, "")); 96 | } 97 | } 98 | 99 | if (visitor.mode == Visitor::Mode::kGet) { 100 | if (is_set) { 101 | // Also write the type param back to file. 102 | std::string error; 103 | YAML::Node type_node = 104 | YamlParser::toYaml(Settings::instance().factory_type_param_name, type, visitor.name_space, error); 105 | mergeYamlNodes(visitor.data.data, type_node); 106 | } 107 | } 108 | 109 | if (visitor.mode == Visitor::Mode::kSet) { 110 | // Return the data to intialize the virtual config if this is the first time setting it. 111 | return lookupNamespace(visitor.data.data, visitor.name_space); 112 | } 113 | 114 | return std::nullopt; 115 | } 116 | 117 | } // namespace config::internal 118 | -------------------------------------------------------------------------------- /config_utilities/src/yaml_parser.cpp: -------------------------------------------------------------------------------- 1 | /** ----------------------------------------------------------------------------- 2 | * Copyright (c) 2023 Massachusetts Institute of Technology. 3 | * All Rights Reserved. 4 | * 5 | * AUTHORS: Lukas Schmid , Nathan Hughes 6 | * AFFILIATION: MIT-SPARK Lab, Massachusetts Institute of Technology 7 | * YEAR: 2023 8 | * LICENSE: BSD 3-Clause 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * 13 | * 1. Redistributions of source code must retain the above copyright notice, this 14 | * list of conditions and the following disclaimer. 15 | * 16 | * 2. Redistributions in binary form must reproduce the above copyright notice, 17 | * this list of conditions and the following disclaimer in the documentation 18 | * and/or other materials provided with the distribution. 19 | * 20 | * 3. Neither the name of the copyright holder nor the names of its 21 | * contributors may be used to endorse or promote products derived from 22 | * this software without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | * -------------------------------------------------------------------------- */ 35 | 36 | #include "config_utilities/internal/yaml_parser.h" 37 | 38 | namespace config::internal { 39 | 40 | void YamlParser::fromYamlImpl(uint8_t& value, const YAML::Node& node, std::string& error) { 41 | const int int_val = node.as(); 42 | const int max = std::numeric_limits::max(); 43 | const int min = std::numeric_limits::lowest(); 44 | if (int_val > max) { 45 | std::stringstream ss; 46 | ss << "Value '" << int_val << "' overflows storage max of '" << max << "'."; 47 | error = ss.str(); 48 | return; 49 | } 50 | if (int_val < min) { 51 | std::stringstream ss; 52 | ss << "Value '" << int_val << "' underflows storage min of '" << min << "'."; 53 | error = ss.str(); 54 | return; 55 | } 56 | value = node.as(); 57 | } 58 | 59 | YAML::Node YamlParser::toYamlImpl(const std::string& name, const uint8_t& value, std::string& error) { 60 | YAML::Node node; 61 | node[name] = static_cast(value); 62 | return node; 63 | } 64 | 65 | } // namespace config::internal 66 | -------------------------------------------------------------------------------- /config_utilities/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(GTest REQUIRED) 2 | include(GoogleTest) 3 | enable_testing() 4 | 5 | add_library(test_${PROJECT_NAME}_plugins src/external_plugins.cpp) 6 | target_include_directories(test_${PROJECT_NAME}_plugins PRIVATE include) 7 | target_link_libraries(test_${PROJECT_NAME}_plugins ${PROJECT_NAME}) 8 | target_compile_options(test_${PROJECT_NAME}_plugins PRIVATE -fvisibility=hidden) 9 | 10 | # Add new test resources here to ensure they get copied to the approriate build 11 | # dir. 12 | set(TEST_RESOURCES resources/foo.yaml resources/bar.yaml resources/invalid.yaml) 13 | 14 | list(TRANSFORM TEST_RESOURCES PREPEND "${CMAKE_CURRENT_BINARY_DIR}/" 15 | OUTPUT_VARIABLE TEST_RESOURCE_BUILD_PATHS) 16 | list(TRANSFORM TEST_RESOURCES PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/" 17 | OUTPUT_VARIABLE TEST_RESOURCE_SRC_PATHS) 18 | 19 | add_custom_command( 20 | OUTPUT ${TEST_RESOURCE_BUILD_PATHS} 21 | COMMAND ${CMAKE_COMMAND} -E make_directory 22 | ${CMAKE_CURRENT_BINARY_DIR}/resources 23 | COMMAND ${CMAKE_COMMAND} -E copy ${TEST_RESOURCE_SRC_PATHS} 24 | ${CMAKE_CURRENT_BINARY_DIR}/resources/ 25 | COMMAND_EXPAND_LISTS 26 | COMMENT "Copy test resources to build directory" 27 | DEPENDS ${TEST_RESOURCE_SRC_PATHS}) 28 | add_custom_target( 29 | copy_test_resources 30 | COMMENT "Target for test resources" 31 | DEPENDS ${TEST_RESOURCE_BUILD_PATHS}) 32 | 33 | add_executable( 34 | test_${PROJECT_NAME} 35 | main.cpp 36 | src/default_config.cpp 37 | src/utils.cpp 38 | tests/asl_formatter.cpp 39 | tests/commandline.cpp 40 | tests/config_arrays.cpp 41 | tests/config_maps.cpp 42 | tests/conversions.cpp 43 | tests/enums.cpp 44 | tests/external_registry.cpp 45 | tests/factory.cpp 46 | tests/getters.cpp 47 | tests/inheritance.cpp 48 | tests/missing_fields.cpp 49 | tests/namespacing.cpp 50 | tests/path.cpp 51 | tests/string_utils.cpp 52 | tests/subconfigs.cpp 53 | tests/substitutions.cpp 54 | tests/traits.cpp 55 | tests/update.cpp 56 | tests/validity_checks.cpp 57 | tests/virtual_config.cpp 58 | tests/yaml_parsing.cpp 59 | tests/yaml_utils.cpp) 60 | add_dependencies(test_${PROJECT_NAME} copy_test_resources) 61 | target_include_directories(test_${PROJECT_NAME} PRIVATE include) 62 | target_link_libraries(test_${PROJECT_NAME} PRIVATE ${PROJECT_NAME} 63 | GTest::gtest_main) 64 | gtest_add_tests(TARGET test_${PROJECT_NAME}) 65 | -------------------------------------------------------------------------------- /config_utilities/test/include/config_utilities/test/default_config.h: -------------------------------------------------------------------------------- 1 | /** ----------------------------------------------------------------------------- 2 | * Copyright (c) 2023 Massachusetts Institute of Technology. 3 | * All Rights Reserved. 4 | * 5 | * AUTHORS: Lukas Schmid , Nathan Hughes 6 | * AFFILIATION: MIT-SPARK Lab, Massachusetts Institute of Technology 7 | * YEAR: 2023 8 | * LICENSE: BSD 3-Clause 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * 13 | * 1. Redistributions of source code must retain the above copyright notice, this 14 | * list of conditions and the following disclaimer. 15 | * 16 | * 2. Redistributions in binary form must reproduce the above copyright notice, 17 | * this list of conditions and the following disclaimer in the documentation 18 | * and/or other materials provided with the distribution. 19 | * 20 | * 3. Neither the name of the copyright holder nor the names of its 21 | * contributors may be used to endorse or promote products derived from 22 | * this software without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | * -------------------------------------------------------------------------- */ 35 | 36 | #pragma once 37 | 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | #include "config_utilities/types/eigen_matrix.h" 44 | 45 | namespace config::test { 46 | 47 | struct SubSubConfig { 48 | int i = 1; 49 | }; 50 | 51 | struct SubConfig { 52 | int i = 1; 53 | SubSubConfig sub_sub_config; 54 | }; 55 | 56 | struct DefaultConfig { 57 | int i = 1; 58 | float f = 2.1f; 59 | double d = 3.2; 60 | bool b = true; 61 | uint8_t u8 = 4; 62 | std::string s = "test string"; 63 | std::vector vec = {1, 2, 3}; 64 | std::map map = {{"a", 1}, {"b", 2}, {"c", 3}}; 65 | std::set set = {1.1f, 2.2, 3.3f}; 66 | Eigen::Matrix mat = Eigen::Matrix::Identity(); 67 | enum class Enum { kA, kB, kC } my_enum = Enum::kA; 68 | enum class StrangeEnum : int { kX = 0, kY = 42, kZ = -7 } my_strange_enum = StrangeEnum::kX; 69 | SubConfig sub_config; 70 | SubSubConfig sub_sub_config; 71 | 72 | static YAML::Node defaultValues(); 73 | static YAML::Node modifiedValues(); 74 | void expectDefaultValues(); 75 | void expectModifiedValues(); 76 | }; 77 | 78 | void declare_config(SubSubConfig& config); 79 | 80 | void declare_config(SubConfig& config); 81 | 82 | void declare_config(DefaultConfig& config); 83 | 84 | } // namespace config::test 85 | -------------------------------------------------------------------------------- /config_utilities/test/include/config_utilities/test/external_types.h: -------------------------------------------------------------------------------- 1 | #include "config_utilities/config.h" 2 | #include "config_utilities/factory.h" 3 | 4 | namespace config::test { 5 | 6 | struct Talker { 7 | virtual ~Talker() = default; 8 | virtual std::string talk() const = 0; 9 | }; 10 | 11 | struct InternalTalker : Talker { 12 | std::string talk() const override { return "internal"; } 13 | inline static const auto reg = config::Registration("internal"); 14 | }; 15 | 16 | struct RepeatingTalker : Talker { 17 | struct Config { 18 | size_t repeats = 5; 19 | std::string message = "hello"; 20 | } const config; 21 | 22 | explicit RepeatingTalker(const Config& config) : config(config) {} 23 | 24 | std::string talk() const override { 25 | std::stringstream ss; 26 | for (size_t i = 0; i < config.repeats; ++i) { 27 | ss << config.message; 28 | } 29 | return ss.str(); 30 | } 31 | 32 | inline static const auto reg = config::RegistrationWithConfig("repeating"); 33 | }; 34 | 35 | void declare_config(RepeatingTalker::Config& config) { 36 | config::name("RepeatingTalker::Config"); 37 | config::field(config.repeats, "repeats"); 38 | config::field(config.message, "message"); 39 | } 40 | 41 | } // namespace config::test 42 | -------------------------------------------------------------------------------- /config_utilities/test/include/config_utilities/test/utils.h: -------------------------------------------------------------------------------- 1 | /** ----------------------------------------------------------------------------- 2 | * Copyright (c) 2023 Massachusetts Institute of Technology. 3 | * All Rights Reserved. 4 | * 5 | * AUTHORS: Lukas Schmid , Nathan Hughes 6 | * AFFILIATION: MIT-SPARK Lab, Massachusetts Institute of Technology 7 | * YEAR: 2023 8 | * LICENSE: BSD 3-Clause 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * 13 | * 1. Redistributions of source code must retain the above copyright notice, this 14 | * list of conditions and the following disclaimer. 15 | * 16 | * 2. Redistributions in binary form must reproduce the above copyright notice, 17 | * this list of conditions and the following disclaimer in the documentation 18 | * and/or other materials provided with the distribution. 19 | * 20 | * 3. Neither the name of the copyright holder nor the names of its 21 | * contributors may be used to endorse or promote products derived from 22 | * this software without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | * -------------------------------------------------------------------------- */ 35 | 36 | #pragma once 37 | 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | #include 44 | 45 | #include "config_utilities/factory.h" 46 | #include "config_utilities/internal/logger.h" 47 | 48 | namespace config::test { 49 | 50 | bool expectEqual(const YAML::Node& a, const YAML::Node& b); 51 | 52 | class TestLogger : public internal::Logger { 53 | public: 54 | using Message = std::pair; 55 | using Messages = std::vector; 56 | 57 | TestLogger() = default; 58 | virtual ~TestLogger() = default; 59 | 60 | const Messages& messages() const { return messages_; } 61 | int numMessages() const { return messages_.size(); } 62 | void clear() { messages_.clear(); } 63 | void print() const; 64 | bool hasMessages() const { return !messages_.empty(); } 65 | const std::string& lastMessage() const { return messages_.back().second; } 66 | 67 | static std::shared_ptr create(); 68 | 69 | protected: 70 | void logImpl(const internal::Severity severity, const std::string& message) override; 71 | 72 | private: 73 | Messages messages_; 74 | 75 | inline static const auto registration_ = Registration("test_logger"); 76 | }; 77 | 78 | } // namespace config::test 79 | -------------------------------------------------------------------------------- /config_utilities/test/main.cpp: -------------------------------------------------------------------------------- 1 | /** ----------------------------------------------------------------------------- 2 | * Copyright (c) 2023 Massachusetts Institute of Technology. 3 | * All Rights Reserved. 4 | * 5 | * AUTHORS: Lukas Schmid , Nathan Hughes 6 | * AFFILIATION: MIT-SPARK Lab, Massachusetts Institute of Technology 7 | * YEAR: 2023 8 | * LICENSE: BSD 3-Clause 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * 13 | * 1. Redistributions of source code must retain the above copyright notice, this 14 | * list of conditions and the following disclaimer. 15 | * 16 | * 2. Redistributions in binary form must reproduce the above copyright notice, 17 | * this list of conditions and the following disclaimer in the documentation 18 | * and/or other materials provided with the distribution. 19 | * 20 | * 3. Neither the name of the copyright holder nor the names of its 21 | * contributors may be used to endorse or promote products derived from 22 | * this software without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | * -------------------------------------------------------------------------- */ 35 | 36 | #include 37 | 38 | int main(int argc, char** argv) { 39 | testing::InitGoogleTest(&argc, argv); 40 | 41 | return RUN_ALL_TESTS(); 42 | } 43 | -------------------------------------------------------------------------------- /config_utilities/test/resources/bar.yaml: -------------------------------------------------------------------------------- 1 | a: 6.0 2 | b: [4] 3 | d: world! 4 | -------------------------------------------------------------------------------- /config_utilities/test/resources/foo.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | a: 5.0 3 | b: [1, 2, 3] 4 | c: hello 5 | -------------------------------------------------------------------------------- /config_utilities/test/resources/invalid.yaml: -------------------------------------------------------------------------------- 1 | invalid: {incomplete: dict 2 | -------------------------------------------------------------------------------- /config_utilities/test/src/external_plugins.cpp: -------------------------------------------------------------------------------- 1 | /** ----------------------------------------------------------------------------- 2 | * Copyright (c) 2023 Massachusetts Institute of Technology. 3 | * All Rights Reserved. 4 | * 5 | * AUTHORS: Lukas Schmid , Nathan Hughes 6 | * AFFILIATION: MIT-SPARK Lab, Massachusetts Institute of Technology 7 | * YEAR: 2023 8 | * LICENSE: BSD 3-Clause 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * 13 | * 1. Redistributions of source code must retain the above copyright notice, this 14 | * list of conditions and the following disclaimer. 15 | * 16 | * 2. Redistributions in binary form must reproduce the above copyright notice, 17 | * this list of conditions and the following disclaimer in the documentation 18 | * and/or other materials provided with the distribution. 19 | * 20 | * 3. Neither the name of the copyright holder nor the names of its 21 | * contributors may be used to endorse or promote products derived from 22 | * this software without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | * -------------------------------------------------------------------------- */ 35 | 36 | #include 37 | 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | #include "config_utilities/test/external_types.h" 44 | 45 | namespace external { 46 | 47 | struct ExternalTalker : config::test::Talker { 48 | std::string talk() const override { return "external"; } 49 | inline static const auto reg = config::Registration("external"); 50 | }; 51 | 52 | struct FancyTalker : config::test::Talker { 53 | struct Config { 54 | std::string prefix = "* "; 55 | std::string suffix = " *"; 56 | } const config; 57 | 58 | explicit FancyTalker(const Config& config) : config(config) {} 59 | 60 | std::string talk() const override { return config.prefix + "derived" + config.suffix; } 61 | 62 | inline static const auto reg = config::RegistrationWithConfig("fancy"); 63 | }; 64 | 65 | void declare_config(FancyTalker::Config& config) { 66 | config::name("FancyTalker::Config"); 67 | config::field(config.prefix, "prefix"); 68 | config::field(config.suffix, "suffix"); 69 | } 70 | 71 | struct ExternalLogger : config::internal::Logger { 72 | void logImpl(const config::internal::Severity, const std::string&) { throw std::runtime_error("External logger!"); } 73 | inline static const auto reg = config::Registration("external_logger"); 74 | }; 75 | 76 | } // namespace external 77 | -------------------------------------------------------------------------------- /config_utilities/test/src/utils.cpp: -------------------------------------------------------------------------------- 1 | /** ----------------------------------------------------------------------------- 2 | * Copyright (c) 2023 Massachusetts Institute of Technology. 3 | * All Rights Reserved. 4 | * 5 | * AUTHORS: Lukas Schmid , Nathan Hughes 6 | * AFFILIATION: MIT-SPARK Lab, Massachusetts Institute of Technology 7 | * YEAR: 2023 8 | * LICENSE: BSD 3-Clause 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * 13 | * 1. Redistributions of source code must retain the above copyright notice, this 14 | * list of conditions and the following disclaimer. 15 | * 16 | * 2. Redistributions in binary form must reproduce the above copyright notice, 17 | * this list of conditions and the following disclaimer in the documentation 18 | * and/or other materials provided with the distribution. 19 | * 20 | * 3. Neither the name of the copyright holder nor the names of its 21 | * contributors may be used to endorse or promote products derived from 22 | * this software without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | * -------------------------------------------------------------------------- */ 35 | 36 | #include "config_utilities/test/utils.h" 37 | 38 | #include 39 | 40 | namespace config::test { 41 | namespace { 42 | 43 | bool expectEqualImpl(const YAML::Node& a, const YAML::Node& b) { 44 | EXPECT_EQ(a.Type(), b.Type()); 45 | if (a.Type() != b.Type()) { 46 | return false; 47 | } 48 | switch (a.Type()) { 49 | case YAML::NodeType::Scalar: 50 | EXPECT_EQ(a.Scalar(), b.Scalar()); 51 | return a.Scalar() == b.Scalar(); 52 | case YAML::NodeType::Sequence: 53 | EXPECT_EQ(a.size(), b.size()); 54 | if (a.size() != b.size()) { 55 | return false; 56 | } 57 | for (size_t i = 0; i < a.size(); ++i) { 58 | EXPECT_TRUE(expectEqualImpl(a[i], b[i])); 59 | if (!expectEqualImpl(a[i], b[i])) { 60 | return false; 61 | } 62 | } 63 | return true; 64 | case YAML::NodeType::Map: 65 | EXPECT_EQ(a.size(), b.size()); 66 | if (a.size() != b.size()) { 67 | return false; 68 | } 69 | for (const auto& kv_pair : a) { 70 | const std::string key = kv_pair.first.Scalar(); 71 | if (!b[key]) { 72 | ADD_FAILURE() << "Key " << key << " not found in b."; 73 | return false; 74 | } 75 | EXPECT_TRUE(expectEqualImpl(kv_pair.second, b[key])); 76 | if (!expectEqualImpl(kv_pair.second, b[key])) { 77 | return false; 78 | } 79 | } 80 | return true; 81 | case YAML::NodeType::Null: 82 | return true; 83 | case YAML::NodeType::Undefined: 84 | return true; 85 | } 86 | return false; 87 | } 88 | 89 | } // namespace 90 | 91 | bool expectEqual(const YAML::Node& a, const YAML::Node& b) { 92 | const auto equal = expectEqualImpl(a, b); 93 | EXPECT_TRUE(equal) << "---\na:\n---\n" << a << "\n---\nb:\n---\n" << b; 94 | return equal; 95 | } 96 | 97 | void TestLogger::logImpl(const internal::Severity severity, const std::string& message) { 98 | messages_.emplace_back(severity, message); 99 | } 100 | 101 | std::shared_ptr TestLogger::create() { 102 | auto logger = std::make_shared(); 103 | internal::Logger::setLogger(logger); 104 | return logger; 105 | } 106 | 107 | void TestLogger::print() const { 108 | for (const auto& message : messages_) { 109 | std::cout << internal::severityToString(message.first) << ": " << message.second << std::endl; 110 | } 111 | } 112 | } // namespace config::test 113 | -------------------------------------------------------------------------------- /config_utilities/test/tests/getters.cpp: -------------------------------------------------------------------------------- 1 | /** ----------------------------------------------------------------------------- 2 | * Copyright (c) 2023 Massachusetts Institute of Technology. 3 | * All Rights Reserved. 4 | * 5 | * AUTHORS: Lukas Schmid , Nathan Hughes 6 | * AFFILIATION: MIT-SPARK Lab, Massachusetts Institute of Technology 7 | * YEAR: 2023 8 | * LICENSE: BSD 3-Clause 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * 13 | * 1. Redistributions of source code must retain the above copyright notice, this 14 | * list of conditions and the following disclaimer. 15 | * 16 | * 2. Redistributions in binary form must reproduce the above copyright notice, 17 | * this list of conditions and the following disclaimer in the documentation 18 | * and/or other materials provided with the distribution. 19 | * 20 | * 3. Neither the name of the copyright holder nor the names of its 21 | * contributors may be used to endorse or promote products derived from 22 | * this software without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | * -------------------------------------------------------------------------- */ 35 | 36 | #include "config_utilities/getters.h" 37 | 38 | #include 39 | 40 | #include "config_utilities/config.h" 41 | #include "config_utilities/parsing/yaml.h" 42 | #include "config_utilities/test/default_config.h" 43 | #include "config_utilities/test/utils.h" 44 | 45 | namespace config::test { 46 | 47 | struct GetterStruct { 48 | int some_number; 49 | std::string some_string; 50 | }; 51 | 52 | void declare_config(GetterStruct& config) { 53 | name("GetterStruct"); 54 | field(config.some_number, "some_number"); 55 | field(config.some_string, "some_string"); 56 | } 57 | 58 | TEST(ConfigGetters, Getters) { 59 | const std::string yaml_string = R"yaml( 60 | some_number: 5 61 | some_string: "Hello" 62 | )yaml"; 63 | const auto node = YAML::Load(yaml_string); 64 | 65 | const auto config = fromYaml(node); 66 | EXPECT_EQ(config.some_number, 5); 67 | EXPECT_EQ(config.some_string, "Hello"); 68 | 69 | const auto fields = listFields(config); 70 | EXPECT_EQ(fields.size(), 2); 71 | EXPECT_EQ(fields[0], "some_number"); 72 | EXPECT_EQ(fields[1], "some_string"); 73 | 74 | const auto number = getField(config, "some_number"); 75 | EXPECT_TRUE(number.has_value()); 76 | EXPECT_EQ(number.value(), 5); 77 | 78 | const auto string = getField(config, "some_string"); 79 | EXPECT_TRUE(string.has_value()); 80 | EXPECT_EQ(string.value(), "Hello"); 81 | 82 | auto logger = TestLogger::create(); 83 | const auto wrong = getField(config, "some_string"); 84 | EXPECT_FALSE(wrong.has_value()); 85 | EXPECT_EQ(logger->numMessages(), 1); 86 | EXPECT_EQ(logger->lastMessage(), "Field 'some_string' could not be converted to the requested type: bad conversion"); 87 | 88 | const auto wrong2 = getField(config, "non_existent_field"); 89 | EXPECT_FALSE(wrong2.has_value()); 90 | EXPECT_EQ(logger->numMessages(), 2); 91 | EXPECT_EQ(logger->lastMessage(), "Field 'non_existent_field' not found in config."); 92 | } 93 | 94 | } // namespace config::test 95 | -------------------------------------------------------------------------------- /config_utilities/test/tests/missing_fields.cpp: -------------------------------------------------------------------------------- 1 | /** ----------------------------------------------------------------------------- 2 | * Copyright (c) 2023 Massachusetts Institute of Technology. 3 | * All Rights Reserved. 4 | * 5 | * AUTHORS: Lukas Schmid , Nathan Hughes 6 | * AFFILIATION: MIT-SPARK Lab, Massachusetts Institute of Technology 7 | * YEAR: 2023 8 | * LICENSE: BSD 3-Clause 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * 13 | * 1. Redistributions of source code must retain the above copyright notice, this 14 | * list of conditions and the following disclaimer. 15 | * 16 | * 2. Redistributions in binary form must reproduce the above copyright notice, 17 | * this list of conditions and the following disclaimer in the documentation 18 | * and/or other materials provided with the distribution. 19 | * 20 | * 3. Neither the name of the copyright holder nor the names of its 21 | * contributors may be used to endorse or promote products derived from 22 | * this software without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | * -------------------------------------------------------------------------- */ 35 | 36 | #include 37 | 38 | #include "config_utilities/config.h" 39 | #include "config_utilities/formatting/asl.h" 40 | #include "config_utilities/internal/visitor.h" 41 | #include "config_utilities/logging/log_to_stdout.h" 42 | #include "config_utilities/printing.h" 43 | #include "config_utilities/virtual_config.h" 44 | 45 | namespace config::test { 46 | 47 | struct SimpleConfig { 48 | double a = 1.0; 49 | int b = 2; 50 | std::string c = "3"; 51 | }; 52 | 53 | void declare_config(SimpleConfig& config) { 54 | name("SimpleConfig"); 55 | field(config.a, "a"); 56 | field(config.b, "b"); 57 | field(config.c, "c"); 58 | check(config.b, GT, 2, "b"); 59 | } 60 | 61 | bool operator==(const SimpleConfig& lhs, const SimpleConfig& rhs) { 62 | return lhs.a == rhs.a && lhs.b == rhs.b && lhs.c == rhs.c; 63 | } 64 | 65 | struct ComposedConfig { 66 | SimpleConfig simple; 67 | double other = 0.0; 68 | }; 69 | 70 | void declare_config(ComposedConfig& config) { 71 | name("ComposedConfig"); 72 | field(config.simple, "simple"); 73 | field(config.other, "other"); 74 | check(config.other, GT, 1.0, "other"); 75 | } 76 | 77 | void PrintTo(const SimpleConfig& config, std::ostream* os) { *os << toString(config); } 78 | void PrintTo(const ComposedConfig& config, std::ostream* os) { *os << toString(config); } 79 | 80 | TEST(MissingFields, ParseMissing) { 81 | const std::string contents = "{simple: {a: 2.0, b: 3}}"; 82 | const auto node = YAML::Load(contents); 83 | ComposedConfig result; 84 | const auto data = config::internal::Visitor::setValues(result, node); 85 | 86 | EXPECT_TRUE(data.hasMissing()); 87 | const auto message = internal::Formatter::formatMissing(data); 88 | const std::string expected_message = R"msg( 'ComposedConfig': 89 | ================================ ComposedConfig ================================ 90 | Warning: Missing field 'other'! 91 | Warning: Missing field 'simple.c'! 92 | ================================================================================)msg"; 93 | EXPECT_EQ(message, expected_message); 94 | } 95 | 96 | TEST(MissingFields, ParseNoMissing) { 97 | const std::string contents = "{a: 2.0, b: 3, c: '4'}"; 98 | const auto node = YAML::Load(contents); 99 | SimpleConfig result; 100 | const auto data = config::internal::Visitor::setValues(result, node); 101 | 102 | SimpleConfig expected{2.0, 3, "4"}; 103 | EXPECT_EQ(expected, result); 104 | EXPECT_FALSE(data.hasMissing()); 105 | } 106 | 107 | } // namespace config::test 108 | -------------------------------------------------------------------------------- /config_utilities/test/tests/string_utils.cpp: -------------------------------------------------------------------------------- 1 | /** ----------------------------------------------------------------------------- 2 | * Copyright (c) 2023 Massachusetts Institute of Technology. 3 | * All Rights Reserved. 4 | * 5 | * AUTHORS: Lukas Schmid , Nathan Hughes 6 | * AFFILIATION: MIT-SPARK Lab, Massachusetts Institute of Technology 7 | * YEAR: 2023 8 | * LICENSE: BSD 3-Clause 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * 13 | * 1. Redistributions of source code must retain the above copyright notice, this 14 | * list of conditions and the following disclaimer. 15 | * 16 | * 2. Redistributions in binary form must reproduce the above copyright notice, 17 | * this list of conditions and the following disclaimer in the documentation 18 | * and/or other materials provided with the distribution. 19 | * 20 | * 3. Neither the name of the copyright holder nor the names of its 21 | * contributors may be used to endorse or promote products derived from 22 | * this software without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | * -------------------------------------------------------------------------- */ 35 | 36 | #include "config_utilities/internal/string_utils.h" 37 | 38 | #include 39 | 40 | namespace config::test { 41 | 42 | TEST(StringUtils, pruneWhitespace) { 43 | std::string has_whitespace = "hello world "; 44 | std::string has_no_whitespace = "hello world"; 45 | EXPECT_EQ(internal::pruneTrailingWhitespace(has_whitespace), has_no_whitespace); 46 | EXPECT_EQ(internal::pruneTrailingWhitespace(has_no_whitespace), has_no_whitespace); 47 | 48 | std::string empty = ""; 49 | std::string all_whitespace = " "; 50 | EXPECT_EQ(internal::pruneTrailingWhitespace(all_whitespace), empty); 51 | EXPECT_EQ(internal::pruneTrailingWhitespace(empty), empty); 52 | 53 | std::string has_leading_whitespace = " hello world"; 54 | EXPECT_EQ(internal::pruneLeadingWhitespace(has_leading_whitespace), has_no_whitespace); 55 | EXPECT_EQ(internal::pruneLeadingWhitespace(has_no_whitespace), has_no_whitespace); 56 | EXPECT_EQ(internal::pruneLeadingWhitespace(empty), empty); 57 | EXPECT_EQ(internal::pruneLeadingWhitespace(all_whitespace), empty); 58 | } 59 | 60 | TEST(StringUtils, wrapString) { 61 | std::string str = "short."; 62 | std::string wrapped = internal::wrapString(str, 12); 63 | EXPECT_EQ(wrapped, str); 64 | 65 | str = "short. "; 66 | wrapped = internal::wrapString(str, 12); 67 | EXPECT_EQ(wrapped, "short."); 68 | 69 | str = "A very long string that needs to be wrapped to fit into a given width."; 70 | wrapped = internal::wrapString(str, 12); 71 | std::string expected = R"""(A very long 72 | string that 73 | needs to be 74 | wrapped to f 75 | it into a gi 76 | ven width.)"""; 77 | EXPECT_EQ(wrapped, expected); 78 | 79 | std::string wrapped_with_indent = internal::wrapString(str, 12, 2); 80 | expected = R"""( A very lon 81 | g string t 82 | hat needs 83 | to be wrap 84 | ped to fit 85 | into a giv 86 | en width.)"""; 87 | EXPECT_EQ(wrapped_with_indent, expected); 88 | 89 | std::string wrapped_with_indent_no_first_line = internal::wrapString(str, 12, 2, false); 90 | expected = R"""(A very long 91 | string tha 92 | t needs to 93 | be wrapped 94 | to fit int 95 | o a given 96 | width.)"""; 97 | EXPECT_EQ(wrapped_with_indent_no_first_line, expected); 98 | 99 | str = " A very long string with leading and trailing whitespaces. "; 100 | wrapped = internal::wrapString(str, 12); 101 | expected = R"""( A very l 102 | ong string w 103 | ith leading 104 | and trailing 105 | whitespaces.)"""; 106 | EXPECT_EQ(wrapped, expected); 107 | 108 | wrapped_with_indent = internal::wrapString(str, 12, 2); 109 | expected = R"""( A very 110 | long strin 111 | g with lea 112 | ding and t 113 | railing wh 114 | itespaces.)"""; 115 | EXPECT_EQ(wrapped_with_indent, expected); 116 | 117 | wrapped_with_indent_no_first_line = internal::wrapString(str, 12, 2, false); 118 | expected = R"""( A very l 119 | ong string 120 | with leadi 121 | ng and tra 122 | iling whit 123 | espaces.)"""; 124 | EXPECT_EQ(wrapped_with_indent_no_first_line, expected); 125 | } 126 | 127 | } // namespace config::test 128 | -------------------------------------------------------------------------------- /config_utilities/test/tests/subconfigs.cpp: -------------------------------------------------------------------------------- 1 | /** ----------------------------------------------------------------------------- 2 | * Copyright (c) 2023 Massachusetts Institute of Technology. 3 | * All Rights Reserved. 4 | * 5 | * AUTHORS: Lukas Schmid , Nathan Hughes 6 | * AFFILIATION: MIT-SPARK Lab, Massachusetts Institute of Technology 7 | * YEAR: 2023 8 | * LICENSE: BSD 3-Clause 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * 13 | * 1. Redistributions of source code must retain the above copyright notice, this 14 | * list of conditions and the following disclaimer. 15 | * 16 | * 2. Redistributions in binary form must reproduce the above copyright notice, 17 | * this list of conditions and the following disclaimer in the documentation 18 | * and/or other materials provided with the distribution. 19 | * 20 | * 3. Neither the name of the copyright holder nor the names of its 21 | * contributors may be used to endorse or promote products derived from 22 | * this software without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | * -------------------------------------------------------------------------- */ 35 | 36 | #include 37 | 38 | #include "config_utilities/config.h" 39 | #include "config_utilities/parsing/yaml.h" 40 | #include "config_utilities/printing.h" 41 | 42 | namespace config::test { 43 | 44 | struct FakeVector { 45 | int x = 0; 46 | int y = 0; 47 | int z = 0; 48 | }; 49 | 50 | void declare_config(FakeVector& vector) { 51 | name("FakeVector"); 52 | field(vector.x, "x"); 53 | field(vector.y, "y"); 54 | field(vector.z, "z"); 55 | } 56 | 57 | struct VectorConfig { 58 | FakeVector a; 59 | FakeVector b; 60 | }; 61 | 62 | struct FlatVectorConfig : public VectorConfig {}; 63 | struct NamespacedVectorConfig : public VectorConfig {}; 64 | struct DoubleNamespacedVectorConfig : public VectorConfig {}; 65 | 66 | void declare_config(VectorConfig& config) { 67 | name("VectorConfig"); 68 | field(config.a, "a"); 69 | field(config.b, "b"); 70 | } 71 | 72 | void declare_config(FlatVectorConfig& config) { 73 | name("FlatVectorConfig"); 74 | field(config.a, "a", false); 75 | field(config.b, "b", false); 76 | } 77 | 78 | void declare_config(NamespacedVectorConfig& config) { 79 | name("NamespacedVectorConfig"); 80 | field(config.a, "a"); 81 | NameSpace ns("a"); 82 | field(config.b, "b", false); 83 | } 84 | 85 | void declare_config(DoubleNamespacedVectorConfig& config) { 86 | name("DoubleNamespacedVectorConfig"); 87 | field(config.a, "a"); 88 | NameSpace ns("a"); 89 | field(config.b, "b", true); 90 | } 91 | 92 | bool operator==(const FakeVector& lhs, const FakeVector& rhs) { 93 | return lhs.x == rhs.x && lhs.y == rhs.y && lhs.z == rhs.z; 94 | } 95 | 96 | bool operator==(const VectorConfig& lhs, const VectorConfig& rhs) { return lhs.a == rhs.a && lhs.b == rhs.b; } 97 | 98 | VectorConfig makeExpected(int a_x, int a_y, int a_z, int b_x, int b_y, int b_z) { 99 | FakeVector a{a_x, a_y, a_z}; 100 | FakeVector b{b_x, b_y, b_z}; 101 | return {a, b}; 102 | } 103 | 104 | void PrintTo(const VectorConfig& conf, std::ostream* os) { *os << toString(conf); } 105 | void PrintTo(const FlatVectorConfig& conf, std::ostream* os) { *os << toString(conf); } 106 | void PrintTo(const NamespacedVectorConfig& conf, std::ostream* os) { *os << toString(conf); } 107 | void PrintTo(const DoubleNamespacedVectorConfig& conf, std::ostream* os) { *os << toString(conf); } 108 | 109 | TEST(Subconfigs, SubconfigNamespacing) { 110 | const std::string yaml_str = R"( 111 | x: 1 112 | y: 2 113 | z: 3 114 | a: 115 | x: 4 116 | y: 5 117 | z: 6 118 | b: {x: 7, y: 8, z: 9} 119 | b: {x: -1, y: -2, z: -3} 120 | )"; 121 | const auto node = YAML::Load(yaml_str); 122 | 123 | auto nested_config = fromYaml(node); 124 | auto flat_config = fromYaml(node); 125 | auto namespaced_config = fromYaml(node); 126 | auto double_namespaced_config = fromYaml(node); 127 | EXPECT_EQ(makeExpected(4, 5, 6, -1, -2, -3), nested_config); 128 | EXPECT_EQ(makeExpected(1, 2, 3, 1, 2, 3), flat_config); 129 | EXPECT_EQ(makeExpected(4, 5, 6, 4, 5, 6), namespaced_config); 130 | EXPECT_EQ(makeExpected(4, 5, 6, 7, 8, 9), double_namespaced_config); 131 | } 132 | 133 | } // namespace config::test 134 | -------------------------------------------------------------------------------- /config_utilities/test/tests/substitutions.cpp: -------------------------------------------------------------------------------- 1 | /** ----------------------------------------------------------------------------- 2 | * Copyright (c) 2023 Massachusetts Institute of Technology. 3 | * All Rights Reserved. 4 | * 5 | * AUTHORS: Lukas Schmid , Nathan Hughes 6 | * AFFILIATION: MIT-SPARK Lab, Massachusetts Institute of Technology 7 | * YEAR: 2023 8 | * LICENSE: BSD 3-Clause 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * 13 | * 1. Redistributions of source code must retain the above copyright notice, this 14 | * list of conditions and the following disclaimer. 15 | * 16 | * 2. Redistributions in binary form must reproduce the above copyright notice, 17 | * this list of conditions and the following disclaimer in the documentation 18 | * and/or other materials provided with the distribution. 19 | * 20 | * 3. Neither the name of the copyright holder nor the names of its 21 | * contributors may be used to endorse or promote products derived from 22 | * this software without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | * -------------------------------------------------------------------------- */ 35 | 36 | #include "config_utilities/substitutions.h" 37 | 38 | #include 39 | 40 | #include "config_utilities/test/utils.h" 41 | 42 | namespace config::test { 43 | 44 | namespace { 45 | 46 | inline YAML::Node doResolve(const YAML::Node& orig, 47 | const std::map& args = {}, 48 | bool strict = true) { 49 | auto result = YAML::Clone(orig); 50 | ParserContext context; 51 | context.vars = args; 52 | resolveSubstitutions(result, context, strict); 53 | return result; 54 | } 55 | 56 | } // namespace 57 | 58 | TEST(Substitutions, clearLeftoverTags) { 59 | const auto node = YAML::Load(R"""( 60 | root: 61 | children: 62 | - {a: !append [4]} 63 | - {c: !replace [0]} 64 | map_with_tags: 65 | !append tagged_key: 66 | foo: 67 | - a: {b: !replace 2} 68 | other: 69 | bar: 42 70 | foo: 7 71 | )"""); 72 | 73 | auto result = doResolve(node); 74 | const auto expected = YAML::Load(R"""( 75 | root: 76 | children: 77 | - {a: [4]} 78 | - {c: [0]} 79 | map_with_tags: 80 | tagged_key: 81 | foo: [{a: {b: 2}}] 82 | other: {bar: 42, foo: 7} 83 | )"""); 84 | expectEqual(result, expected); 85 | } 86 | 87 | TEST(Substitutions, resolveEnv) { 88 | { // check that we don't try to pass non-scalars to getenv 89 | const auto node = YAML::Load("root: $"); 90 | const auto result = doResolve(node, {}, false); 91 | const auto expected = YAML::Load("root: $"); 92 | } 93 | 94 | auto unset = std::getenv("/some/random/env/variable"); 95 | if (unset) { 96 | FAIL() << "environment variable '/some/random/env/variable' is set"; 97 | } else { 98 | try { 99 | const auto node = YAML::Load("root: $"); 100 | const auto result = doResolve(node); 101 | FAIL(); 102 | } catch (const std::runtime_error& e) { 103 | std::string msg(e.what()); 104 | EXPECT_NE(msg.find("Invalid substitution in node"), std::string::npos); 105 | } 106 | } 107 | 108 | auto set = std::getenv("HOME"); 109 | if (!set) { 110 | FAIL() << "required environment variable 'HOME' not set"; 111 | } else { 112 | const auto node = YAML::Load("root: $"); 113 | const auto result = doResolve(node); 114 | const auto expected = YAML::Load("root: " + std::string(set)); 115 | expectEqual(result, expected); 116 | } 117 | } 118 | 119 | TEST(Substitutions, interpolateFlatSubstitutions) { 120 | std::map args{{"foo", "a"}, {"bar", "b"}}; 121 | const auto node = YAML::Load("root: other/$/test a/$/c"); 122 | const auto result = doResolve(node, args); 123 | const auto expected = YAML::Load("root: other/a/test a/b/c"); 124 | expectEqual(result, expected); 125 | } 126 | 127 | TEST(Substitutions, nestedSubstitutions) { 128 | { // nested subs without terminal suffixes and prefixes 129 | std::map args{{"foo", "a"}, {"bar", "b"}, {"a", "foo"}}; 130 | const auto node = YAML::Load("root: other/$>/test a/$/c"); 131 | const auto result = doResolve(node, args); 132 | const auto expected = YAML::Load("root: other/foo/test a/b/c"); 133 | expectEqual(result, expected); 134 | } 135 | 136 | { // nested subs with terminal suffixes and prefixes 137 | std::map args{{"foo", "a"}, {"bar", "b"}, {"baa", "foo"}}; 138 | const auto node = YAML::Load("root: \"other/$a>/test\na/$aa>>/c\""); 139 | const auto result = doResolve(node, args); 140 | const auto expected = YAML::Load("root: \"other/foo/test\na/a/c\""); 141 | expectEqual(result, expected); 142 | } 143 | } 144 | 145 | } // namespace config::test 146 | -------------------------------------------------------------------------------- /config_utilities/test/tests/traits.cpp: -------------------------------------------------------------------------------- 1 | /** ----------------------------------------------------------------------------- 2 | * Copyright (c) 2023 Massachusetts Institute of Technology. 3 | * All Rights Reserved. 4 | * 5 | * AUTHORS: Lukas Schmid , Nathan Hughes 6 | * AFFILIATION: MIT-SPARK Lab, Massachusetts Institute of Technology 7 | * YEAR: 2023 8 | * LICENSE: BSD 3-Clause 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions are met: 12 | * 13 | * 1. Redistributions of source code must retain the above copyright notice, this 14 | * list of conditions and the following disclaimer. 15 | * 16 | * 2. Redistributions in binary form must reproduce the above copyright notice, 17 | * this list of conditions and the following disclaimer in the documentation 18 | * and/or other materials provided with the distribution. 19 | * 20 | * 3. Neither the name of the copyright holder nor the names of its 21 | * contributors may be used to endorse or promote products derived from 22 | * this software without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | * -------------------------------------------------------------------------- */ 35 | 36 | #include "config_utilities/traits.h" 37 | 38 | #include 39 | 40 | namespace config::test { 41 | 42 | struct Config {}; 43 | 44 | void declare_config(Config& config) {} 45 | 46 | struct NotAConfig {}; 47 | 48 | TEST(Traits, IsConfig) { 49 | Config config; 50 | NotAConfig not_a_config; 51 | EXPECT_EQ(isConfig(), true); 52 | EXPECT_EQ(isConfig(), false); 53 | EXPECT_EQ(isConfig(config), true); 54 | EXPECT_EQ(isConfig(not_a_config), false); 55 | } 56 | 57 | } // namespace config::test 58 | -------------------------------------------------------------------------------- /docs/Compositing.md: -------------------------------------------------------------------------------- 1 | # Compositing multiple YAML sources 2 | 3 | **Contents:** 4 | - [Compositing](#compositing) 5 | - [How compositing works](#how-compositing-works) 6 | - [Controlling compositing behavior](#controlling-compositing-behavior) 7 | - [Substitutions](#substitutions) 8 | 9 | ## Compositing 10 | 11 | `config_utilities` includes a command line tool that will take multiple sources of YAML and dump the combined (composited) YAML to `stdout`. This is designed for the use-case where a base YAML file contains the majority of a configuration, and small overlays are used to adapt or fill in the configuration for different variants of a system. 12 | 13 | You can run `composite-configs -h` for more information on the utility. 14 | 15 | ## How compositing works 16 | 17 | This tutorial explains how different sources of YAML data are combined into a single YAML tree, and is primarily a more in-depth explanation of the behavior of the command line based YAML parser. 18 | Functionally, the command line parser is doing the following after parsing the various command line options. 19 | 20 | ```cpp 21 | std::vector inputs; // parsed YAML from either files or raw YAML strings 22 | 23 | YAML::Node combined; // result YAML node 24 | for (const auto& input : inputs) { 25 | internal::mergeYamlNodes(combined, input, MergeMode::APPEND); 26 | } 27 | ``` 28 | 29 | ## Controlling compositing behavior 30 | 31 | The default ROS-like merging behavior (append) can be overridden by inline tags. The following behaviors are currently available: 32 | - `!append`: Matched sequences are appended together (specifically, the right sequence is appended to the left) 33 | - `!replace`: Matched keys result in the right key overriding the left 34 | - `!merge`: Matched keys (including sequence indices) are recursed into. Any unmatched keys are added 35 | 36 | These merging behaviors apply to all children below the tag (until another tag is present). 37 | 38 | Example behavior: 39 | ```yaml 40 | # original YAML (left) 41 | root: {child: {a: 42, c: 0}, numbers: [1, 2, 3], scalar: -1} 42 | # new YAML to merge (right) 43 | root: !TAG {child: {a: 12, b: 13}, numbers: [4, 5], other: temp} 44 | 45 | # result of merging right into left with !append in place of !TAG 46 | root: {child: {a: 12, c: 0, b: 13}, numbers: [1, 2, 3, 4, 5], scalar: -1, other: temp} 47 | # result of merging right into left with !replace in place of !TAG 48 | root: {child: {a: 12, b: 13}, numbers: [4, 5], scalar: -1, other: temp} 49 | # result of merging right into left with !merge in place of !TAG 50 | root: {child: {a: 12, c: 0, b: 13}, numbers: [4, 5, 3], scalar: -1, other: temp} 51 | ``` 52 | 53 | ## Substitutions 54 | 55 | We also support a substitution language for interpolating values into YAML data. 56 | These (closely) resemble the ROS substitution language. 57 | Substitutions are performed with respect to a context that may have some variables that can be used. 58 | 59 | As an example, resolving substitutions in the following YAML (with the context `num_prints = 5`) 60 | ```yaml 61 | input_filepath: $/some/random/file 62 | num_prints: $ 63 | ``` 64 | would result in 65 | ```yaml 66 | input_filepath: /home/user/some/random/file 67 | num_prints: 5 68 | ``` 69 | after calling `resolveSubstitutions` on the parsed YAML. 70 | -------------------------------------------------------------------------------- /docs/External.md: -------------------------------------------------------------------------------- 1 | # Loading External Factories 2 | This tutorial explains how utilize factories in libraries that are not compiled into the current executable. 3 | You should be familiar with how the `config_utilities` factories work to get the most out of this tutorial. 4 | See [here](Factories.md) for an overview. 5 | 6 | > **:warning: Warning**
7 | > Loading code from external libraries comes with several caveats. 8 | > 1. Loading an external library executes code by default. Only load external libraries that are from a trusted source! 9 | > 2. Anything instantiated from an external library has to be deallocated before the external library is unloaded. 10 | > 3. Symbol conflicts can occur. See [here](https://www.boost.org/doc/libs/1_64_0/doc/html/boost_dll/tutorial.html#boost_dll.tutorial.symbol_shadowing_problem__linux_) 11 | 12 | The features described in this tutorial require including `config_utilities/external_registry.h`. 13 | 14 | **Contents:** 15 | - [Loading an external library](#loading-an-external-library) 16 | - [Managed instances](#managed-instances) 17 | - [Debugging](#debugging) 18 | 19 | ## Loading an external library 20 | To use registered factories from external libraries, you first have to load the libraries themselves. 21 | This is done via `config::loadExternalFactories()`. 22 | 23 | Assuming you have the following definitions: 24 | ```c++ 25 | /******** compiled in main executable **********************************************************************/ 26 | 27 | namespace talker { 28 | 29 | struct Talker { 30 | virtual std::string talk() const = 0; 31 | }; 32 | 33 | } // namespace talker 34 | 35 | /******** compiled in a separate library (`libexternal_talker_plugin.so`) *************************************/ 36 | 37 | namespace external { 38 | 39 | struct ExternalTalker : public Talker { 40 | std::string talk() const { return "Hello"; } 41 | inline static const auto registration = config::Registration("my_talker"); 42 | }; 43 | 44 | } // namespace external 45 | ``` 46 | 47 | You could instantiate `ExternalTalker` from the external library as follows: 48 | ```c++ 49 | { // scope where external library is loaded 50 | const auto guard = config::loadExternalFactories("external_talker_plugin"); 51 | { // scope to ensure anything instantiated is cleaned up 52 | const auto talker = config::create("my_talker"); 53 | std::cout << talker.talk() << std::endl; // should get 'Hello' 54 | } // end scope 55 | } // external library is unloaded after this point 56 | 57 | // note that calling create at this point with "my_talker" would fail! 58 | ``` 59 | 60 | > **ℹ️ Note**
61 | > `loadExternalFactories` also supports a vector of libraries as an argument for convenience 62 | 63 | Note that in this example (on Linux), we are actually loading the file `libexternal_talker_plugin.so`, which is assumed to be available somewhere on `LD_LIBRARY_PATH`. 64 | You can also load libraries via an absolute path (optionally omitting the `lib` prefix and the `.so` extension). 65 | 66 | Either version of `config::loadExternalFactories()` returns a scope-based guard that will unload the external libraries as soon as the guard is no longer in scope. 67 | The recommended design for using external libraries is to call `config::loadExternalFactories()` near the beginning of `main()` and then enter a new scope. 68 | 69 | ## Managed instances 70 | 71 | There are often cases where ensuring that resources allocated from an external library are deallocated before the external library is unloaded (e.g., static instances). 72 | In these cases, it may make sense to use the `ManagedInstance` class. 73 | 74 | Assuming we have the same external library as before, we can do this instead: 75 | ```c++ 76 | config::ManagedInstance talker; 77 | { // scope where external library is loaded 78 | const auto guard = config::loadExternalFactories("external_talker_plugin"); 79 | talker = config::createManaged(config::create("my_talker")); 80 | const auto view = talker.get(); 81 | std::cout << view->talk() << std::end; // should get 'Hello' 82 | } // external library is unloaded after this point and the managed instance is no longer valid 83 | 84 | const auto view = talker.get(); 85 | // view->talk(); // this would segfault 86 | 87 | // views can be implicitly converted to a bool to determine whether or not they are valid 88 | if (!view) { 89 | std::cout << "world!" << std::endl; 90 | } 91 | ``` 92 | 93 | > **:warning: Warning**
94 | > All `ManagedInstance` instantiations will be invalidated as soon as any library is unloaded. 95 | > There is no safe way to determine whether or not the underlying types still have the necessary underlying libraries available. 96 | 97 | Note that `ManagedInstance` may be instantiated by factories that are already compiled into the executable. 98 | 99 | ## Debugging 100 | 101 | Tracking down issues with code loaded from external libraries can be hard. 102 | You may find it helpful to turn on allocation logging by doing the following: 103 | ```c++ 104 | #include // or your preferred logger 105 | #include 106 | 107 | config::Settings::instance().print_external_allocations = true; 108 | ``` 109 | 110 | You can also disable loading external libraries by doing the following: 111 | ```c++ 112 | config::Settings::instance().allow_external_libraries = false; 113 | ``` 114 | 115 | Finally, we intentionally print to stderr when a library is being unloaded. 116 | You can turn this behavior off by default by doing 117 | ```c++ 118 | config::Settings::instance().verbose_external_load = false; 119 | ``` 120 | -------------------------------------------------------------------------------- /docs/Factories.md: -------------------------------------------------------------------------------- 1 | # Automatic object creation via factories 2 | This tutorial explains how to register derived objects to a factory, to automatically create them from string identifiers, config parsing, or virtual configs. 3 | 4 | **Contents:** 5 | - [Automatic object creation](#automatic-object-creation) 6 | - [Creating objects with individual configs](#creating-objects-with-individual-configs) 7 | - [Delayed object creation with virtual configs](#delayed-object-creation-with-virtual-configs) 8 | 9 | ## Automatic object creation 10 | Objects that derive from a common base can be automatically created using the `config_utilities` factory tools. 11 | 12 | > **ℹ️ Note**
13 | > This section covers objects that all have an indentical constructor, whereas objects with specific configs are explained [below](creating-objects-with-individual-configs). 14 | 15 | Register object using a static registration struct: 16 | ```c++ 17 | struct Base { 18 | virtual void doMagic() = 0; 19 | }; 20 | 21 | struct DerivedA : public Base { 22 | virtual void doMagic() override { ... }; 23 | 24 | private: 25 | // Register the class as creatable module for Base with a string identifier using a static registration struct. 26 | // Signature: Registration(string identifier, whether to use a config). 27 | inline static const auto registration_ = config::Registration("DerivedA"); 28 | }; 29 | 30 | struct DerivedB : public Base { /* as above */ ... }; 31 | ``` 32 | 33 | Then objects can be created using the defined string-identifier: 34 | ```c++ 35 | std::unique_ptr object = create("DerivedA"); 36 | ``` 37 | 38 | > **✅ Supports**
39 | > This interface also supports additional constructor arguments. These need to also be declared in the registration. For example: 40 | > ```c++ 41 | > struct Base { 42 | > Base(int i, float f); 43 | > }; 44 | > 45 | > struct Derived : public Base { 46 | > Derived(int i, float f) : Base(i, f) {} 47 | > inline static const auto registration_ = config::Registration("Derived"); 48 | > }; 49 | > 50 | > std::unique_ptr object = create("Derived", 42, 1.23f); 51 | > ``` 52 | 53 | > **⚠️ Important**
54 | > Note that different constructor argument template parameters will result in different factories at lookup time. For objects with optional constructor arguments, a registration for each version of the constructor needs to be declared. 55 | Note that references are silently dropped when calling `create` and will result in looking for a different factory constructor than registered. Pay careful attention to the different argument types between the constructor and the registration in the following example! 56 | ```c++ 57 | struct Bar { 58 | float f; 59 | }; 60 | 61 | struct Foo { 62 | Foo(int i, const Bar& bar) {} 63 | // Foo is already instantiable as a base class, so it is repeated twice as an argument 64 | inline static const auto registration_ = config::Registration("Foo"); 65 | } 66 | ``` 67 | 68 | ## Creating objects with individual configs 69 | 70 | We further provide a version of the factory that allows the declaration of an additional config struct for each derived type. The config is expected to be a `config_utilities` config, the first argument to the constructor, and will be created from the data provided to create the object: 71 | 72 | ```c++ 73 | class Derived : public Base { 74 | public: 75 | // Member struct config definition. 76 | struct Config { ... }; 77 | 78 | // Constructore must take the config as first argument. 79 | DerivedC(const Config& config, const int i) : Base(i), config_(config::checkValid(config)) {} 80 | 81 | private: 82 | const Config config_; 83 | 84 | // Register the module to the factory with a static registration struct. Signature: 85 | // RegistrationWithConfig(string identifier). 86 | inline static const auto registration_ = 87 | config::RegistrationWithConfig("Derived"); 88 | }; 89 | 90 | void declare_config(Derived::Config& config) { ... } 91 | 92 | // Create an object from a data source: 93 | std::unique_ptr = createFromYamlFile(file_name, 42); 94 | ``` 95 | 96 | Note that the type of object to create is read from the data source. By default, a param with name `type` needs to be set. This can be changed in the `config_utilities` [settings](Varia.md#settings). A sample file could look like: 97 | ```yaml 98 | type: "Derived" 99 | derived_config_param: value 100 | ``` 101 | 102 | > **✅ Supports**
103 | > Additional constructor arguments are supplied separately to the create call. If they are also to be loaded from the data source, you can make them part of the config or a common base config! 104 | 105 | ## Delayed object creation with virtual configs 106 | [Virtual configs](Types.md#virtual-configs) wrap the above functionality in a config struct. When the virtual config or parent config is setup, the type and content of the virtual config is set and later be used to create `Base` objects: 107 | 108 | ```c++ 109 | struct Config { 110 | VirtualConfig base_config; 111 | } 112 | 113 | Config config = fromYaml(data) 114 | 115 | // The config uses the 'type' param in the data to set up its type. Once it's setup it can be queried: 116 | std::string type = config.getType(); // E.g. ='Derived' in the above example. 117 | 118 | // The virtual config now contains the config and type to create the object: 119 | std::unique_ptr object = config.create(); 120 | ``` 121 | 122 | > **✅ Supports**
123 | > Virtual configs can wrap all the functionality of `createFromDatasource`-style calls in pure C++, if e.g. the datasource is not available in your core project. 124 | -------------------------------------------------------------------------------- /docs/Headers.md: -------------------------------------------------------------------------------- 1 | # Overview of functionalities and headers 2 | `config_utilities` is designed as a support library with minimal dependencies (`yaml-cpp` and `C++` standard library). 3 | The library is structured into directories by functionalities and dependencies. 4 | Certain headers contain functionalities to interface with dependencies that are only required by the host project. 5 | These dependencies are pointed out below where present. 6 | The following directories and files exist: 7 | 8 | ```bash 9 | │ # All core functionalities are in the unnamed include and depend only on yaml-cpp and C++. 10 | ├── config_utilities.h # Collection of all core headers for easy use. 11 | ├── config.h # To define configs using 'declare_config()'. 12 | ├── factory.h # Enables automatic object creation via 'create()'. 13 | ├── globals.h # Functionality to print all global configs. 14 | ├── printing.h # Defines 'toString()' and 'operator<<' for configs. 15 | ├── settings.h # Enables setting global properties via 'Settings()' 16 | ├── traits.h # Enables 'isConfig()'. 17 | ├── validation.h # Enables 'isValid()' and 'checkVaid()'. 18 | ├── virtual_config.h # Defines 'VirtualConfig' for later factory creation. 19 | │ 20 | ├── internal # All files in 'internal' make config_utilities work internally. They have no extra dependencies and need not be included. 21 | │ └── ... 22 | │ 23 | ├── formatting # Specify a formatter to parse configs and warnings to text. 24 | │ └── asl.h # Formatter specialized for human-readable prints to fixed-width consoles (Default). 25 | │ 26 | ├── logging # Sepcify an output logger to log warnings and errors to. 27 | │ ├── log_to_glog.h # Log to glog. Dependes on 'google/logging'. 28 | │ ├── log_to_ros.h # Log to roslog. Depends on 'ros/console'. 29 | │ └── log_to_stdout.h # Log to stdout console (Default). 30 | │ 31 | ├── parsing # Specify input parsers to get configs or create objects from source data. 32 | │ ├── ros.h # Tools to create configs and objects from ROS parameter server. Depends on 'ros/nodehandle' 33 | │ └── yaml.h # Tools to create/save configs and objects from/to yaml nodes or files. Depends on 'yaml-cpp'. 34 | │ 35 | └── types # Support for various types that need special conversions. 36 | ├── conversions.h # Support for custom conversions, such as uchars. 37 | ├── eigen_matrix.h # Safe and verbose parsing of any Eigen-matrix types. 38 | ├── enum.h # Safe and verbose parsing of custom enum types. 39 | └── path.h # Conversion for std::filesystem::path and checks for paths and path-strings. 40 | ``` 41 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # `config_utilities` Tutorials 2 | We provide detailed introductions about everything you need to know about `config_utilities` in the following [tutorials](#tutorials) and some verbose example [demos](#demos) that you can run. 3 | 4 | ## Tutorials 5 | The following tutorials will guide you through functionalities of `config_utilities`, from beginner to expert: 6 | 7 | 1. [**Overview of functionalities and headers**](Headers.md) 8 | 9 | 2. [**Config essentials**](Configs.md) 10 | - [Declaring a struct a config](Configs.md#declaring-a-struct-a-config) 11 | - [Checking for valid configurations](Configs.md#checking-for-valid-configurations) 12 | - [Printing configs](Configs.md#printing-configs) 13 | 14 | 3. [**Parsing configs from data sources**](Parsing.md) 15 | - [Parse from yaml](Parsing.md#parse-from-yaml) 16 | - [Parse from ROS](Parsing.md#parse-from-ros) 17 | - [Parse from the command line](Parsing.md#parse-from-the-command-line) 18 | - [Parse via global context](Parsing.md#parse-via-global-context) 19 | 20 | 4. [**Handling complex configs or types**](Types.md) 21 | - [Sub-configs](Types.md#sub-configs) 22 | - [Inheritance](Types.md#inheritance) 23 | - [Virtual configs](Types.md#virtual-configs) 24 | - [Type conversions](Types.md#type-conversions) 25 | - [Namespaces](Types.md#namespaces) 26 | 27 | 5. [**Automatic object creation via factories**](Factories.md) 28 | - [Automatic object creation](Factories.md#automatic-object-creation) 29 | - [Creating objects with individual configs](Factories.md#creating-objects-with-individual-configs) 30 | - [Delayed object creation with virtual configs](Factories.md#delayed-object-creation-with-virtual-configs) 31 | 32 | 6. [**Compositing data sources**](Compositing.md) 33 | - [Compositing](Compositing.md#compositing) 34 | - [How compositing works](Compositing.md#how-compositing-works) 35 | - [Controlling compositing behavior](Compositing.md#controlling-compositing-behavior) 36 | - [Substitutions](Compositing.md#substitutions) 37 | 38 | 7. [**Advanced features**](Advanced.md) 39 | - [Adding custom types](Advanced.md#adding-custom-types) 40 | - [Adding custom conversions](Advanced.md#adding-custom-conversions) 41 | - [Adding custom checks](Advanced.md#adding-custom-checks) 42 | - [Adding custom loggers](Advanced.md#adding-custom-loggers) 43 | - [Adding custom formatters](Advanced.md#adding-custom-formatters) 44 | - [Adding custom parsers](Advanced.md#adding-custom-parsers) 45 | - [Adding custom substitutions](Advanced.md#adding-custom-substitutions) 46 | 47 | 8. [**External Plugins**](External.md) 48 | - [Loading an external library](External.md#loading-an-external-library) 49 | - [Managed instances](External.md#managed-instances) 50 | - [Debugging](External.md#debugging) 51 | 52 | 9. [**Varia**](Varia.md) 53 | - [Settings](Varia.md#settings) 54 | - [Globals](Varia.md#globals) 55 | 56 | 57 | ## Demos 58 | The (non-ros) demos can be run via the `run_demo.py` utility in the scripts directory. If you are building this library via catkin, you can run one of the following to see the results of one of the corresponding demo files: 59 | ``` 60 | python3 scripts/run_demo.py config 61 | python3 scripts/run_demo.py inheritance 62 | python3 scripts/run_demo.py factory 63 | ``` 64 | 65 | > **ℹ️ Note**
66 | > If you're building via cmake, you can point `run_demo.py` to the build directory with `-b/--build_path`. 67 | 68 | The ros demo can be run via: 69 | ``` 70 | roslaunch config_utilities demo_ros.launch 71 | ``` 72 | 73 | If you are looking for a specific use case that is not in the tutorials or demos, chances are you can find a good example in the `tests/` directory! 74 | -------------------------------------------------------------------------------- /docs/Varia.md: -------------------------------------------------------------------------------- 1 | # Varia 2 | 3 | This tutorial explains various additional `config_utilities` functionalities. 4 | 5 | **Contents:** 6 | - [Settings](#settings) 7 | - [Globals](#globals) 8 | 9 | ## Settings 10 | `config_utilities` provides some configuration options that can be set at runtime using the `Settings` struct: 11 | ```c++ 12 | // Example settings for formatting and printing. 13 | Settings().print_width = 80; 14 | 15 | // Example settings for factory creation. 16 | Settings().factory_type_param_name = "type"; 17 | 18 | // You can set the formatting or logging at runtime. 19 | Settings().setLogger("stdout"); 20 | Settings().setFormatter("asl"); 21 | ``` 22 | 23 | ## Globals 24 | `config_utilities` also provides some preliminary functionalities for global processing. For example, it can keep track of all configs that have been checked for validity using `checkValid()`. This can be used as a proxy for all configs used in a system and can also be disabled in the settings. 25 | 26 | ```c++ 27 | { /* build a complicated architecture using configs */ } 28 | std::ofstream config_log(log_dest); 29 | 30 | // Write the realized configuration of the system, clearing the memory used to store this information. 31 | config_log << Globals().printAllValidConfigs(true); 32 | ``` 33 | --------------------------------------------------------------------------------