diff options
author | Martin Braun <martin.braun@ettus.com> | 2017-11-10 21:43:09 -0800 |
---|---|---|
committer | Martin Braun <martin.braun@ettus.com> | 2018-02-01 06:35:37 +0100 |
commit | 4ebfea45f32cc845d7887e552e8c6084d8ef3067 (patch) | |
tree | 03072dbab4870b9d648d154331d0ee7911da1b24 /host | |
parent | 21be43d40a0fe33af9597e77fb3465caa7079033 (diff) | |
download | uhd-4ebfea45f32cc845d7887e552e8c6084d8ef3067.tar.gz uhd-4ebfea45f32cc845d7887e552e8c6084d8ef3067.tar.bz2 uhd-4ebfea45f32cc845d7887e552e8c6084d8ef3067.zip |
lib: Add config_parser class
This class is not publicly exported. It is meant to read config files in
the INI format.
Reviewed-by: Brent Stapleton <brent.stapleton@ettus.com>
Diffstat (limited to 'host')
-rw-r--r-- | host/lib/include/uhdlib/utils/config_parser.hpp | 121 | ||||
-rw-r--r-- | host/lib/utils/CMakeLists.txt | 1 | ||||
-rw-r--r-- | host/lib/utils/config_parser.cpp | 59 | ||||
-rw-r--r-- | host/tests/CMakeLists.txt | 13 | ||||
-rw-r--r-- | host/tests/config_parser_test.cpp | 97 |
5 files changed, 291 insertions, 0 deletions
diff --git a/host/lib/include/uhdlib/utils/config_parser.hpp b/host/lib/include/uhdlib/utils/config_parser.hpp new file mode 100644 index 000000000..5d01aa8c4 --- /dev/null +++ b/host/lib/include/uhdlib/utils/config_parser.hpp @@ -0,0 +1,121 @@ +// +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0+ +// + +#ifndef INCLUDED_LIBUHD_CONFIG_PARSER_HPP +#define INCLUDED_LIBUHD_CONFIG_PARSER_HPP + +#include <uhd/exception.hpp> +#include <boost/property_tree/ptree.hpp> +#include <vector> + +namespace uhd { + +/*! Represent a config file (INI format) + * + * These files have the following format: + * ``` + * ; Comment + * [section] + * key=value + * ``` + * + * The API of this class was designed to match the ConfigParser class from + * Python https://docs.python.org/3/library/configparser.html, which we use in + * MPM. This does mean the API is somewhat different from typical C++ APIs, + * and does not name getters get_*. + */ +class config_parser +{ +public: + /*! + * \param path Path to the INI file + * + * \throws uhd::runtime_error if the parsing failed. + */ + config_parser(const std::string &path=""); + + /*! Load another config file and update the current values. + * + * If a value exists in both the new and current file, the new value wins. + */ + void read_file(const std::string &path); + + //! Return a list of sections + std::vector<std::string> sections(); + + //! Return a list of options (keys) in a section, or an empty list if + // section does not exist + std::vector<std::string> options(const std::string §ion); + + /*! Return the value of a key + * + * \param section The name of the section in which this key is + * \param key Name of the key + * \param def Default value, in case the key does not exist + */ + template <typename T> + T get( + const std::string §ion, + const std::string &key, + const T &def + ) { + try { + const auto child = _pt.get_child(section); + return child.get<T>(key, def); + } catch (const boost::property_tree::ptree_bad_path &) { + return def; + } + } + + /*! Return the value of a key + * + * \param section The name of the section in which this key is + * \param key Name of the key + * + * \throws uhd::key_error if the key or the section don't exist + */ + template <typename T> + T get( + const std::string §ion, + const std::string &key + ) { + try { + const auto child = _pt.get_child(section); + return child.get<T>(key); + } catch (const boost::property_tree::ptree_bad_path &) { + throw uhd::key_error( + std::string("[config_parser] Key ") + key + + " not found in section " + section + ); + } + } + + template <typename T> + void set( + const std::string §ion, + const std::string &key, + const T &value + ) { + _pt.put<T>(section + "." + key, value); + } + +private: + template <typename T> + static std::vector<std::string> _options(T key_bearing_iterable) + { + std::vector<std::string> sections; + for (const auto& node : key_bearing_iterable) { + sections.push_back(node.first); + } + return sections; + } + + boost::property_tree::ptree _pt; +}; + +} /* namespace uhd */ + +#endif /* INCLUDED_LIBUHD_CONFIG_PARSER_HPP */ diff --git a/host/lib/utils/CMakeLists.txt b/host/lib/utils/CMakeLists.txt index 91a579853..afaf99274 100644 --- a/host/lib/utils/CMakeLists.txt +++ b/host/lib/utils/CMakeLists.txt @@ -159,6 +159,7 @@ SET_SOURCE_FILES_PROPERTIES( ######################################################################## LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/csv.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/config_parser.cpp ${CMAKE_CURRENT_SOURCE_DIR}/eeprom_utils.cpp ${CMAKE_CURRENT_SOURCE_DIR}/gain_group.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ihex.cpp diff --git a/host/lib/utils/config_parser.cpp b/host/lib/utils/config_parser.cpp new file mode 100644 index 000000000..a88a99a01 --- /dev/null +++ b/host/lib/utils/config_parser.cpp @@ -0,0 +1,59 @@ +// +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0+ +// + +#include <uhdlib/utils/config_parser.hpp> +#include <boost/format.hpp> +#include <boost/filesystem.hpp> +#include <boost/property_tree/ini_parser.hpp> + +using namespace uhd; + +config_parser::config_parser(const std::string &path) +{ + if (not path.empty() and boost::filesystem::exists(path)) { + try { + boost::property_tree::ini_parser::read_ini(path, _pt); + } catch (const boost::property_tree::ini_parser_error &) { + throw uhd::runtime_error(str( + boost::format("Unable to parse file %s") + % path + )); + } + } +} + +void config_parser::read_file(const std::string &path) +{ + config_parser new_config(path); + for (const auto& section : new_config.sections()) { + for (const auto& key : new_config.options(section)) { + set<std::string>( + section, + key, + new_config.get<std::string>(section, key) + ); + } + } +} + +std::vector<std::string> config_parser::sections() +{ + try { + return _options(_pt); + } catch (const boost::property_tree::ptree_bad_path &) { + return std::vector<std::string>{}; + } +} + +std::vector<std::string> config_parser::options(const std::string §ion) +{ + try { + return _options(_pt.get_child(section)); + } catch (const boost::property_tree::ptree_bad_path &) { + return std::vector<std::string>{}; + } +} + diff --git a/host/tests/CMakeLists.txt b/host/tests/CMakeLists.txt index 0741191f2..b22f5722c 100644 --- a/host/tests/CMakeLists.txt +++ b/host/tests/CMakeLists.txt @@ -111,6 +111,19 @@ TARGET_LINK_LIBRARIES(nocscript_parser_test uhd ${Boost_LIBRARIES}) UHD_ADD_TEST(nocscript_parser_test nocscript_parser_test) UHD_INSTALL(TARGETS nocscript_parser_test RUNTIME DESTINATION ${PKG_LIB_DIR}/tests COMPONENT tests) +ADD_EXECUTABLE(config_parser_test + config_parser_test.cpp + ${CMAKE_SOURCE_DIR}/lib/utils/config_parser.cpp +) +TARGET_LINK_LIBRARIES(config_parser_test uhd ${Boost_LIBRARIES}) +UHD_ADD_TEST(config_parser_test config_parser_test) +UHD_INSTALL(TARGETS + config_parser_test + RUNTIME + DESTINATION ${PKG_LIB_DIR}/tests + COMPONENT tests +) + ######################################################################## # demo of a loadable module ######################################################################## diff --git a/host/tests/config_parser_test.cpp b/host/tests/config_parser_test.cpp new file mode 100644 index 000000000..e0ad3e919 --- /dev/null +++ b/host/tests/config_parser_test.cpp @@ -0,0 +1,97 @@ +// +// Copyright 2017 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0+ +// + +#include <uhdlib/utils/config_parser.hpp> +#include <boost/test/unit_test.hpp> +#include <boost/assign/list_of.hpp> +#include <fstream> +#include <cstdio> + +const std::string INI1_FILENAME = "test1.ini"; +const std::string INI1 = + "[section1]\n" + "key1=value1\n" + "key2=4\n" + "\n" + "; This is a comment\n" + "[section2]\n" + "key3=value with spaces\n" + "key4= leading and trailing spaces \n" +; + +const std::string INI2_FILENAME = "test2.ini"; +const std::string INI2 = + "[section2]\n" + "key3=value with even more spaces\n" + "\n" + "[section3]\n" + "key4=\"with quotes\"\n"; + +namespace { + //! Create files that can be read by the CUT + void make_config_parsers() + { + std::ofstream ini1(INI1_FILENAME); + ini1 << INI1 << std::endl; + ini1.close(); + std::ofstream ini2(INI2_FILENAME); + ini2 << INI2 << std::endl; + ini2.close(); + } + + //! Tidy up after us + void cleanup_config_parsers() + { + std::remove(INI1_FILENAME.c_str()); + std::remove(INI2_FILENAME.c_str()); + } +} + +BOOST_AUTO_TEST_CASE(test_config_parser){ + make_config_parsers(); + uhd::config_parser I(INI1_FILENAME); + BOOST_CHECK_EQUAL(I.sections().size(), 2); + BOOST_CHECK_EQUAL(I.options("section1").size(), 2); + BOOST_CHECK_EQUAL( + I.get<std::string>("section1", "key1"), + "value1" + ); + BOOST_CHECK_EQUAL( + I.get<int>("section1", "key2"), + 4 + ); + BOOST_CHECK_EQUAL( + I.get<std::string>("section2", "key3"), + "value with spaces" + ); + BOOST_CHECK_EQUAL( + I.get<std::string>("section2", "key4"), + "leading and trailing spaces" + ); + BOOST_CHECK_EQUAL( + I.get<std::string>("section2", "non_existent_key", "default"), + "default" + ); + BOOST_REQUIRE_THROW( + I.get<std::string>("section2", "non_existent_key"), + uhd::key_error + ); + I.read_file(INI2_FILENAME); + BOOST_CHECK_EQUAL( + I.get<std::string>("section2", "key3"), + "value with even more spaces" + ); + BOOST_CHECK_EQUAL( + I.get<std::string>("section1", "key1"), + "value1" + ); + BOOST_CHECK_EQUAL( + I.get<std::string>("section3", "key4"), + "\"with quotes\"" + ); + + cleanup_config_parsers(); +} |