Skip to content

Commit b210eb1

Browse files
Feature/ros2 (#40)
* don't warn about missing findpackage * (wip) start on ros2 interface * add new launch extension package * drop broken unit test install * switch config tag format and work on parsing correctly * work on performing substitutions on parsed param fields * allow optional new YAML merging behavior * (wip) start on context * outline basic commandline infrastructure * remove rclcpp and add context command line function * fix linting warnings and add more coverage for external registry * split parsing and utils tests * forward extend_sequence arg and add merge tests * add additional yaml parsing test coverage * fix broken test and make merge test more complicated * (wip) initial implementation of parsing * condense files and allow for multiple tokens * implement custom parser because ros can't escape correctly * drop debug print * add ability to namespace files * drop launch extension * add command line tests and fix application order * fix function name and add cli docs * mention multiple flags * add full api to context plus quick documentation * add doc links * remove logging TODOs * log to stderr for errors and warnings by default
1 parent 09f2d73 commit b210eb1

29 files changed

+1381
-115
lines changed

config_utilities/CMakeLists.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ option(CONFIG_UTILS_ENABLE_ROS "Export roscpp and build related code" ON)
1414
option(CONFIG_UTILS_ENABLE_EIGEN "Export Eigen and build related code" ON)
1515
option(CONFIG_UTILS_ENABLE_GLOG "Export glog and build related code" ON)
1616
option(CONFIG_UTILS_BUILD_TESTS "Build unit tests" ON)
17-
option(CONFIG_UTILS_INSTALL_TESTS "Install test executable" ON)
1817
option(CONFIG_UTILS_BUILD_DEMOS "Build demo executables" ON)
1918

2019
find_package(yaml-cpp REQUIRED)
@@ -26,6 +25,9 @@ find_optional_pkgcfg(libglog CONFIG_UTILS_ENABLE_GLOG)
2625
add_library(
2726
${PROJECT_NAME}
2827
src/asl_formatter.cpp
28+
src/commandline.cpp
29+
src/config_context.cpp # global singleton
30+
src/context.cpp # parsing
2931
src/conversions.cpp
3032
src/external_registry.cpp
3133
src/factory.cpp
@@ -74,6 +76,7 @@ if(CONFIG_UTILS_BUILD_DEMOS)
7476
endif()
7577

7678
if(CONFIG_UTILS_BUILD_TESTS)
79+
include(CTest)
7780
enable_testing()
7881
add_subdirectory(test)
7982
endif()

config_utilities/cmake/OptionalPackage.cmake

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
# not the package should be used
99
macro(FIND_OPTIONAL package_name package_enabled)
1010
if(${package_enabled})
11-
find_package(${package_name})
11+
find_package(${package_name} QUIET)
1212
endif()
1313

1414
if(${${package_name}_FOUND})

config_utilities/cmake/config_utilitiesConfig.cmake.in

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
@PACKAGE_INIT@
22

3-
get_filename_component(config_utilities_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
3+
get_filename_component(config_utilities_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}"
4+
PATH)
45
include(CMakeFindDependencyMacro)
56

67
find_dependency(yaml-cpp REQUIRED)
@@ -11,6 +12,11 @@ endif()
1112

1213
if(@ENABLE_roscpp@)
1314
find_dependency(roscpp REQUIRED)
15+
set(config_utilities_FOUND_CATKIN_PROJECT TRUE)
16+
endif()
17+
18+
if(@ENABLE_rclcpp@)
19+
find_dependency(rclcpp REQUIRED)
1420
endif()
1521

1622
if(@ENABLE_libglog@)
@@ -24,6 +30,4 @@ endif()
2430

2531
set(config_utilities_LIBRARIES config_utilities::config_utilities)
2632

27-
set(config_utilities_FOUND_CATKIN_PROJECT TRUE)
28-
2933
check_required_components(config_utilities)

config_utilities/include/config_utilities/internal/checks.h

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535

3636
#pragma once
3737

38+
#include <algorithm>
3839
#include <functional>
3940
#include <memory>
4041
#include <sstream>
@@ -82,7 +83,7 @@ struct CompareMessageTrait {
8283
template <typename T, typename Compare>
8384
class BinaryCheck : public CheckBase {
8485
public:
85-
BinaryCheck(const T& param, const T& value, const std::string name = "")
86+
BinaryCheck(const T& param, const T& value, const std::string& name = "")
8687
: param_(param), value_(value), name_(name) {}
8788

8889
bool valid() const override { return Compare{}(param_, value_); }
@@ -186,12 +187,8 @@ class CheckIsOneOf : public CheckBase {
186187
: param_(param), candidates_(candidates), name_(name) {}
187188

188189
bool valid() const override {
189-
for (const T& cadidate : candidates_) {
190-
if (param_ == cadidate) {
191-
return true;
192-
}
193-
}
194-
return false;
190+
// check that param matches any candidate
191+
return std::any_of(candidates_.begin(), candidates_.end(), [this](const auto& c) { return c == param_; });
195192
}
196193

197194
std::string message() const override {
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/** -----------------------------------------------------------------------------
2+
* Copyright (c) 2023 Massachusetts Institute of Technology.
3+
* All Rights Reserved.
4+
*
5+
* AUTHORS: Lukas Schmid <[email protected]>, Nathan Hughes <[email protected]>
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 <string>
39+
40+
#include <yaml-cpp/yaml.h>
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 <typename BaseT, typename... ConstructorArguments>
61+
static std::unique_ptr<BaseT> create(ConstructorArguments... args) {
62+
return internal::ObjectWithConfigFactory<BaseT, ConstructorArguments...>::create(instance().contents_, args...);
63+
}
64+
65+
template <typename BaseT, typename... ConstructorArguments>
66+
static std::unique_ptr<BaseT> createNamespaced(const std::string& name_space, ConstructorArguments... args) {
67+
const auto ns_node = internal::lookupNamespace(instance().contents_, name_space);
68+
return internal::ObjectWithConfigFactory<BaseT, ConstructorArguments...>::create(ns_node, args...);
69+
}
70+
71+
template <typename ConfigT>
72+
static ConfigT loadConfig(const std::string& name_space = "") {
73+
ConfigT config;
74+
internal::Visitor::setValues(config, internal::lookupNamespace(instance().contents_, name_space), true);
75+
return config;
76+
}
77+
78+
private:
79+
Context() = default;
80+
static Context& instance();
81+
82+
YAML::Node contents_;
83+
};
84+
85+
} // namespace config::internal

config_utilities/include/config_utilities/internal/logger.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@
4242

4343
namespace config::internal {
4444

45-
// Enum for different severity levels of logging.
46-
enum class Severity { kInfo, kWarning, kError, kFatal };
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 };
4747

4848
std::string severityToString(const Severity severity);
4949

config_utilities/include/config_utilities/internal/yaml_utils.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,10 @@ namespace config::internal {
4444

4545
/**
4646
* @brief Merges node b into a, overwriting values previously defined in a if they can not be
47-
* merged. Modifies node a, whereas b is const.
47+
* merged. Modifies node a, whereas b is const. Sequences can optionally be appended together at the same level of the
48+
* YAML tree.
4849
*/
49-
void mergeYamlNodes(YAML::Node& a, const YAML::Node& b);
50+
void mergeYamlNodes(YAML::Node& a, const YAML::Node& b, bool extend_sequences = false);
5051

5152
/**
5253
* @brief Get a pointer to the final node of the specified namespace if it exists, where each map in the yaml is

config_utilities/include/config_utilities/logging/log_to_stdout.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,21 @@ namespace config::internal {
4545
*/
4646
class StdoutLogger : public Logger {
4747
public:
48-
StdoutLogger() = default;
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+
4955
virtual ~StdoutLogger() = default;
5056

5157
protected:
5258
void logImpl(const Severity severity, const std::string& message) override;
5359

5460
private:
61+
const Severity min_severity_;
62+
const Severity stderr_severity_;
5563
// Factory registration to allow setting of formatters via Settings::setLogger().
5664
inline static const auto registration_ = Registration<Logger, StdoutLogger>("stdout");
5765

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
/** -----------------------------------------------------------------------------
2+
* Copyright (c) 2023 Massachusetts Institute of Technology.
3+
* All Rights Reserved.
4+
*
5+
* AUTHORS: Lukas Schmid <[email protected]>, Nathan Hughes <[email protected]>
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 <memory>
39+
#include <string>
40+
41+
#include "config_utilities/factory.h"
42+
#include "config_utilities/internal/visitor.h"
43+
#include "config_utilities/internal/yaml_utils.h"
44+
45+
namespace config {
46+
namespace internal {
47+
48+
/**
49+
* @brief Parse and collate YAML node from arguments, optionally removing arguments
50+
* @param argc Number of command line arguments
51+
* @param argv Command line argument strings
52+
*/
53+
YAML::Node loadFromArguments(int& argc, char* argv[], bool remove_args);
54+
55+
} // namespace internal
56+
57+
/**
58+
* @brief Loads a config based on collated YAML data specified via the command line
59+
*
60+
* See fromYaml() for more specific behavioral information.
61+
*
62+
* @tparam ConfigT The config type. This can also be a VirtualConfig<BaseT> or a std::vector<ConfigT>.
63+
* @param argc Number of arguments.
64+
* @param argv Actual command line arguments.
65+
* @param name_space Optional namespace to use under the resolved YAML parameter tree.
66+
* @returns The config.
67+
*/
68+
template <typename ConfigT>
69+
ConfigT fromCLI(int argc, char* argv[], const std::string& name_space = "") {
70+
// when parsing CLI locally we don't want to modify the arguments ever
71+
const auto node = internal::loadFromArguments(argc, argv, false);
72+
73+
ConfigT config;
74+
internal::Visitor::setValues(config, internal::lookupNamespace(node, name_space), true);
75+
return config;
76+
}
77+
78+
/**
79+
* @brief Create a derived type object based on collated YAML data specified via the command line
80+
*
81+
* See createFromYaml() for more specific behavioral information.
82+
*
83+
* @tparam BaseT Type of the base class to be constructed.
84+
* @tparam Args Other constructor arguments.
85+
* @param argc Number of arguments.
86+
* @param argv Actual command line arguments.
87+
* @param args Other constructor arguments.
88+
* @returns Unique pointer of type base that contains the derived object.
89+
*/
90+
template <typename BaseT, typename... ConstructorArguments>
91+
std::unique_ptr<BaseT> createFromCLI(int argc, char* argv[], ConstructorArguments... args) {
92+
// when parsing CLI locally we don't want to modify the arguments ever
93+
const auto node = internal::loadFromArguments(argc, argv, false);
94+
return internal::ObjectWithConfigFactory<BaseT, ConstructorArguments...>::create(node, args...);
95+
}
96+
97+
/**
98+
* @brief Create a derived type object based on collated YAML data specified via the command line
99+
*
100+
* See createFromYamlWithNamespace() for more specific behavioral information.
101+
*
102+
* @tparam BaseT Type of the base class to be constructed.
103+
* @tparam Args Other constructor arguments.
104+
* @param argc Number of arguments.
105+
* @param argv Actual command line arguments.
106+
* @param name_space Optionally specify a name space to create the object from.
107+
* @param args Other constructor arguments.
108+
* @returns Unique pointer of type base that contains the derived object.
109+
*/
110+
template <typename BaseT, typename... ConstructorArguments>
111+
std::unique_ptr<BaseT> createFromCLIWithNamespace(int argc,
112+
char* argv[],
113+
const std::string& name_space,
114+
ConstructorArguments... args) {
115+
// when parsing CLI locally we don't want to modify the arguments ever
116+
const auto node = internal::loadFromArguments(argc, argv, false);
117+
const auto ns_node = internal::lookupNamespace(node, name_space);
118+
return internal::ObjectWithConfigFactory<BaseT, ConstructorArguments...>::create(ns_node, args...);
119+
}
120+
121+
} // namespace config

0 commit comments

Comments
 (0)