{"id":18621503,"url":"https://github.com/ethz-asl/config_utilities","last_synced_at":"2025-04-11T03:30:47.771Z","repository":{"id":45048799,"uuid":"284644981","full_name":"ethz-asl/config_utilities","owner":"ethz-asl","description":"Utility tools to make working with config structs for (ROS) C++ libraries more uniform, readable and convenient.","archived":false,"fork":false,"pushed_at":"2023-12-04T22:30:14.000Z","size":177,"stargazers_count":23,"open_issues_count":0,"forks_count":4,"subscribers_count":3,"default_branch":"main","last_synced_at":"2024-05-03T16:21:31.591Z","etag":null,"topics":["config","config-utilities","cpp","ros","tools","utility"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ethz-asl.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-08-03T08:26:34.000Z","updated_at":"2024-05-03T16:21:31.592Z","dependencies_parsed_at":"2023-01-31T04:45:34.363Z","dependency_job_id":null,"html_url":"https://github.com/ethz-asl/config_utilities","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ethz-asl%2Fconfig_utilities","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ethz-asl%2Fconfig_utilities/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ethz-asl%2Fconfig_utilities/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ethz-asl%2Fconfig_utilities/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ethz-asl","download_url":"https://codeload.github.com/ethz-asl/config_utilities/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223455090,"owners_count":17147848,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["config","config-utilities","cpp","ros","tools","utility"],"created_at":"2024-11-07T04:12:02.313Z","updated_at":"2024-11-07T04:12:02.948Z","avatar_url":"https://github.com/ethz-asl.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"![Ubuntu 18 + ROS Melodic: Build + Tests](https://github.com/ethz-asl/config_utilities/actions/workflows/build_test_18.yml/badge.svg) ![Ubuntu 20 + ROS Noetic: Build + Tests](https://github.com/ethz-asl/config_utilities/actions/workflows/build_test_20.yml/badge.svg)\n\n# config_utilities\nUtility tools to make working with config structs for ROS (and non-ROS) C++ libraries more uniform, readable, and convenient.\n\n\u003e **Important Note (Nov 2023):**\n\u003e \n\u003e A newer version of config_utilities has been released, that supports everything config_utilities can do and more with cleaner interfaces and tools!\n\u003e You can find the new release at [https://github.com/MIT-SPARK/config_utilities](https://github.com/MIT-SPARK/config_utilities).\n\u003e This version of config_utilities will no longer be actively maintained.\n\n* **Author:** Lukas Schmid \u003cschmluk@ethz.ch\u003e.\n* **Affiliation:** Autonomous Systems Lab (ASL), ETH Zürich.\n* **Version:** 1.3.1\n* **License:** BSD-3-Clause.\n\n### Table of contents\n- [config_utilities](#config_utilities)\n    - [Table of contents](#table-of-contents)\n- [Why config_utilities](#why-config_utilities)\n- [Installation](#installation)\n- [Interfaces and Tools](#interfaces-and-tools)\n    - [Settings](#settings)\n    - [Configs](#configs)\n      - [Public Member Functions](#public-member-functions)\n      - [Virtual Member Functions](#virtual-member-functions)\n      - [Protected Member Functions](#protected-member-functions)\n    - [Factory](#factory)\n    - [Variable Config](#variable-config)\n- [Demos](#demos)\n  - [Config Checker](#config-checker)\n  - [Config](#config)\n  - [ROS Param](#ros-param)\n  - [Inheritance](#inheritance)\n  - [Factory](#factory-1)\n  - [ROS Factory](#ros-factory)\n  - [Variable Config](#variable-config-1)\n  - [Global Settings](#global-settings)\n  \n  \n# Why config_utilities\nThis library was developed to make working with config structs for object-oriented C++ libraries as simple as possible.\nUsing config_utilities-based configs has the following advantages:\n\n* Having all parameters in a config struct, rather than with other variables, makes code clearer and more readable:\n  ```c++\n  if (x_ \u003c config_.x_max) { doMagic(); }\n  ```\n* Configs can be easily checked for validity with verbose warnings to avoid runtime issues:\n  ```c++\n  MyClass::MyClass(const Config\u0026 config) : config_(config.checkValid()) {}\n  ```\n* For projects consisting of a library and a ROS-package, the configs don't have any ROS dependency and can be used in the library.\n  In the ROS-package, configs can be created from NodeHandles without requiring additional code:\n  ```c++\n  MyConfig c = config_utilities::getConfigFromRos\u003cMyConfig\u003e(nh_private);\n  ```\n* Verbose and clear printing for debugging or verification can be setup for the entire project:\n  ```c++\n  config_utilities::Global::Settings().default_print_width = 80;\n  std::cout \u003c\u003c config.toString() \u003c\u003c std::endl;\n  std::ofstream(log_file) \u003c\u003c config_utilities::Global::printAllConfigs();\n  ```\n* Everything related to a config is located at its definition/implementation, making all its properties clear and easy to change.\n  No need for additional code in other files where changes could be overlooked.\n  ```c++\n  my_class_using_configs.h / my_class_using_configs.cpp {\n    // Contains *all* variables, defaults, valid values, printing, ROS-creation, factory registration, ...\n  }\n  ```\n* Easy registration and factory creation for arbitrary classes with and without configs:\n  ```c++\n  static config_utilities::Factory::Registration\u003cBase, Derived\u003e registration(\"MyDerivedKey\");\n  std::shared_ptr\u003cBase\u003e object = config_utilities::Factory::create\u003cBase\u003e(\"MyDerivedKey\");\n  ```\n\n# Installation\n* **Header-Only**\n\n  This mini-library can be used as a header only library by simply copying `config_utilities.hpp` into your project.\n   Requires [glog](https://github.com/google/glog) and [xmlrpc++](http://xmlrpc.com/).\n  * Dependencies:\n    ```sh\n    # As System Install:\n    sudo apt update\n    sudo apt install libxmlrpc-c++8-dev\n    sudo apt-get install libgoogle-glog-dev\n    ```\n    ```sh\n    # Alternatively, as Catkin Package with prior ROS installation:\n    cd ~/catkin_ws/src\n    git clone git@github.com:ethz-asl/glog_catkin.git\n    catkin build glog_catkin\n    ```\n\n* **Demos Package**\n\n  To run the demos, the ROS package can be conveniently installed via catkin: \n  ```sh\n  cd ~/catkin_ws/src\n  git clone https://github.com/ethz-asl/config_utilities.git\n  \n  # Install all dependencies via rosinstall\n  wstool init . ./config_utilities/config_utilities.rosinstall  # new workspace\n  wstool merge -t . ./config_utilities/config_utilities.rosinstall # existing workspace\n  wstool update\n  \n  cd config_utilities\n  catkin build --this\n  ```\n  \n# Interfaces and Tools\nBriefly describes the interfaces available and how to use them.\n\n### Settings\nSet default settings for the entire project. Set these before instantiating a config.\n```c++\nconfig_utilities::Global::Settings().default_print_width = 80;\nconfig_utilities::Global::Settings().default_print_indent = 30;\n```\n### Configs\nDefine configs by inheriting from the provided `config_utilities::Config` and templating itself. \nAll following interfaces are part of such a `Config`.\n```c++\nstruct MyConfig : public config_utilities::Config\u003cMyConfig\u003e {\n  double x_max = 1.0;\n};\n```\n#### Public Member Functions\nUse these to interact with a `Config`.\n```c++\nbool isValid(bool print_warnings=false) const;  // Validity information.\nConfig checkValid() const;  // Enforce validity.\nConfig\u0026 checkValid(); \nstring toString() const;  // Printing.\n```\n\n#### Virtual Member Functions\nOverride these functions to implement the corresponding behavior.\n```c++\n  virtual void initializeDependentVariableDefaults();  // Initialization.\n  virtual void checkParams() const;  // Param validity checks.\n  virtual void printFields() const;  // Printing behavior.\n  virtual void fromRosParam();  // ROS-creation behavior.\n  virtual void setupParamsAndPrinting();  // Combines fromRosParam() and printFields() in a single call. Precedes but does not exclude these functions if implemented. \n```\n\n#### Protected Member Functions\nUse these tools within the virtual functions to create the desired behavior.\n```c++\n// General settings.\nvoid setConfigName(const std::string\u0026 name);\nvoid setPrintWidth(int width);\nvoid setPrintIndent(int indent);\n\n// Set these values in the constructor.\nMyConfig::MyConfig() {\n  setConfigName(\"MyConfig\");\n  ...\n}\n```\n```c++\n// Parameter validity constraints.\nvoid checkParamGT\u003cT\u003e(const T\u0026 param, const T\u0026 value, const std::string\u0026 name) const;\nvoid checkParamGE\u003cT\u003e(const T\u0026 param, const T\u0026 value, const std::string\u0026 name) const;\nvoid checkParamLT\u003cT\u003e(const T\u0026 param, const T\u0026 value, const std::string\u0026 name) const;\nvoid checkParamLE\u003cT\u003e(const T\u0026 param, const T\u0026 value, const std::string\u0026 name) const;\nvoid checkParamEq\u003cT\u003e(const T\u0026 param, const T\u0026 value, const std::string\u0026 name) const;\nvoid checkParamNE\u003cT\u003e(const T\u0026 param, const T\u0026 value, const std::string\u0026 name) const;\n// Any condition can be checked using checkParamCond().\nvoid checkParamCond(bool condition, const std::string \u0026warning) const;\n// Validity of member configs can be checked using checkParamConfig().\nvoid checkParamConfig(const Config\u0026 config) const;\n\n// Use these checks within checkParams().\nMyConfig::checkParams() const {\n  checkParamGT(x_max, 0.0, \"x_max\");\n  ...\n}\n```\n```c++\n// Printing.\nvoid printField\u003cT\u003e(const std::string\u0026 name, const T\u0026 field, const std::string\u0026 unit=\"\") const;\nvoid printText(const std::string\u0026 text) const;\n\n// Use these tools within printFields().\nMyConfig::printFields() const {\n  printField(\"x_max\", x_max);\n  ...\n}\n```\n```c++\n// Creation from ROS params.\nvoid rosParam\u003cT\u003e(const std::string\u0026 name, T* param);\n// Also works for configs, these don't require a name but an optional sub_namespace.\nvoid rosParam(Config* config, const std::string\u0026 sub_namespace = \"\");\n// The namespace of the creating nodehandle can be queried via rosParamNameSpace().\nstring rosParamNameSpace();\n\n// Use these tools within fromRosParam(). Defaults should be set at variable declaration.\nMyConfig::fromRosParam() {\n  rosParam(\"x_max\", \u0026x_max);\n  ...\n}\n```\n```c++\n// Merged param and printing setup. Internally uses the same tools as printField() and rosParam() to avoid code duplication.\nvoid setupParam\u003cT\u003e(const std::string\u0026 name, T* param, const std::string\u0026 unit);\n\n// Use these tools within setupParamsAndPrinting().\nMyConfig::setupParamsAndPrinting() {\n  setupParam(\"x_max\", \u0026x_max, \"m\");\n  ...\n}\n```\n\n### Factory\nUse these tools to let derived classes register themselves to the factory and create them based on a string or from the ROS parameter server.\n```c++\n// Register any class to the factory using a static struct.\nstatic config_utilities::Factory::Registration\u003cBaseT, DerivedT, ConstructorArgs...\u003e registration(\"IdentifierString\");\n// Register a class that has a Config struct as a member to enable ROS creation.\nstatic config_utilities::Factory::RegistrationRos\u003cBaseT, DerivedT, ConstructorArgs...\u003e registration(\"IdentifierString\");\n// Create any class registered to the factory.\nstd::unique_ptr\u003cBaseT\u003e config_utilities::Factory::create\u003cBaseT\u003e(\"IdentifierString\", constructor_args);\n// Create a that uses a Config from ros params. The param 'type' is expected to provide the identifier string.\n// The constructors of each DerivedT is expected to take as first argument a DerivedT::Config.\nstd::unique_ptr\u003cBaseT\u003e config_utilities::FactoryRos::create\u003cBaseT\u003e(const ros::NodeHandle\u0026 nh, constructor_args);\n```\n\n\n\n### Variable Config\n\nUse these configs like regular sub-configs as a member of a config. These can hold varying configs to create components downstream. The contained objects need to be registered via the ROS factory. Variable configs can be filled in via the `getConfigFromRos()` function. Variable configs need to be templated on the base type they create. Additional functionalities include:\n\n```c++\n// Check whether the config is setup.\nbool isSetup() const;\n// Get the string identifier for the type to be created (ROS Factory).\nstd::string getType() const;\n// Create the downstream object using this config. Args are additional constructor args.\nstd::unique_ptr\u003cBaseT\u003e create(Args... args) const;\n```\n\n# Demos\n\nVerbose examples of the most important functionalities are given in the demos folder. They can easily be run after building the config_utilities ROS-package.\n\n## Config Checker\nThis demo describes how to use the `ConfigChecker` class to verify non-config_utilities configs in a readable way:\n```sh\nrosrun config_utilities demo_config_checker\n```\nRuns a validity check and prints all warnings to console:\n```\n============================== IndependentConfig ===============================\nWarning: Param 'a' is expected \u003e= '0' (is: '-1').\nWarning: Param 'c' is expected to be 'this is c' (is: 'test').\n================================================================================\n```\n\n## Config\nThis demo describes how to define custom classes that utilize a `Config` struct:\n```sh\nrosrun config_utilities demo_config\n```\nThis will setup a class using a valid config and print it to console, as well as a creation attempt with an invalid config:\n```\n================================ MyClass-Config ================================\na:                            1 (default)\nb:                            2.34 (default)\nb_half:                       1.17 (default)\nc:                            this is c (default)\nAn_extremely_unecessarily_and_unreasonably_long_param_name: \n                              A_similarly_unreasonably_long_param_value. (defaul\n                              t)\nAnd a custom message.\n================================================================================\n\n================================ MyClass-Config ================================\nWarning: Param 'a' is expected \u003e= '0' (is: '-1').\nWarning: Param 'c' is expected to be 'this is c' (is: 'test').\nWarning: b is expected \u003e a.\n================================================================================\n```\n\n## ROS Param\nThis demo describes how to use the `config_utilities::getConfigFromRos\u003cConfig\u003e()` function to setup configs via the ROS parameter server:\n```sh\nroscore \u0026 rosrun config_utilities demo_ros_param\n```\nSets config params from ros and prints them to console:\n```\n================= Config (from ROS params) =================\na:             123\nb:             45.6\nc:             seven-eight-nine\nvec:           [1, 2, 3]\nmap:           {m1: 1, m2: 2}\nT:             t: [0, 0, 0] RPY°: [-0, 0, -0]\nnamespace:     /demo_ros_param\n============================================================\n```\n\n## Inheritance\nThis demo describes how to use nested configs, which can be used to setup derived and base classes:\n```sh\nroscore \u0026 rosrun config_utilities demo_inheritance\n```\nSets up a derived class from ROS, prints its nested config, and check for validity:\n```\n===================== MyDerivedConfig ======================\ne:                  Bananas are yellow.\nf:                  6\nother_config:\n   a:               11.1\n   b:               222\nbase_config:\n   c:               False\n   d:               3.45\n   other_config:\n      a:            1\n      b:            2\n============================================================\n\n======================= OtherConfig ========================\nWarning: Param 'a' is expected \u003e= '0' (is: '-1').\n============================================================\n========================== MyBase ==========================\nWarning: Member config 'OtherConfig' is not valid.\n============================================================\n===================== MyDerivedConfig ======================\nWarning: Member config 'MyBase' is not valid.\n============================================================\n```\n## Factory\nThis demo describes how to use the `config_utilities::Factory::Registration()` and `config_utilities::Factory::create()` tools to instantiate various objects.\n```sh\nrosrun config_utilities demo_factory\n```\nDefines two derived classes and registers them statically to the factory, which can then be created using a string identifier:\n```\nThis is a DerivedA with i=0, f=0.\nThis is a DerivedB with i=1, f=2.\nE1104 20:45:29.080973  6629 config_utilities.hpp:1152] No module with name 'DerivedC' registered to the factory for base '4Base' and constructor arguments 'i, f'. Registered are: DerivedB, DerivedA.\n'object' is invalid.\n```\n\n## ROS Factory\nThis demo describes how to use the `config_utilities::Factory::RegistrationRos()` and `config_utilities::FactoryRos::create()` tools to create different objects that use varying custom configs from the parameter server.\n```sh\nroscore \u0026 rosrun config_utilities demo_ros_factory\n```\n\nDefines two derived classes that use different configs and creates them from a ROS nodehandle:\n\n```\nThis is a DerivedA with i=1, f=2.345, and info 'How to create a DerivedA'.\nThis is a DerivedB with info 'Now the type param has changed'.\n=============================== DerivedB Config ================================\ns:                            param text.\nf:                            2.345\n================================================================================\n```\n\n## Variable Config\n\nThis demo describes how to use the `config_utilities::Factory::RegistrationRos()` and `config_utilities::VariableConfig` parameter struct to adaptively create downstream objects without direct access to a ROS nodehandle.\n\n```\nroscore \u0026 rosrun config_utilities demo_variable_config\n```\n\nCreates a component of the primary object downstream using a Variable Config:\n\n```\n================================ Object Config =================================\ni:                            5 (default)\nbase_config:                  Uninitialized Variable Config.\n================================================================================\nConfig is valid: false\n================================ Object Config =================================\ni:                            10\nbase_config (Variable Config: DerivedB):\n   s:                         text for derived B.\n================================================================================\nConfig is valid: true\nThis is a DerivedB with s='text for derived B.' and base data='10'.\n```\n\n\n## Global Settings\nThis demo describes how to use the `config_utilities::Global` tools to dynamically change settings and get information on all configs.\n\n```\nrosrun config_utilities demo_global_settings\n```\n\nCreates three confings A,B, and C, prints C with two different printing layouts defined by global settings, and then summarizes all existing configs.\n\n```\n=================================== ConfigC ====================================\nconfig_a:\n   a [m]:                     123 (default)\n   aa:                        config a text (default)\nconfig_b:\n   b [Hz]:                    45\n   bb:                        varied config b text\n================================================================================\n=============== ConfigC ================\nconfig_a:\n          a:   123\n          aa:  config a text\nconfig_b:\n          b:   45\n          bb:  varied config b text\n========================================\n\nValue of all existing params: \n=============== ConfigA ================\na:             123\naa:            config a text\n=============== ConfigB ================\nb:             30\nbb:            config b text\n=============== ConfigC ================\nconfig_a:\n          a:   123\n          aa:  config a text\nconfig_b:\n          b:   45\n          bb:  varied config b text\n========================================\n```\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fethz-asl%2Fconfig_utilities","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fethz-asl%2Fconfig_utilities","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fethz-asl%2Fconfig_utilities/lists"}