aboutsummaryrefslogtreecommitdiffstats
path: root/host
diff options
context:
space:
mode:
authorMartin Braun <martin.braun@ettus.com>2017-11-10 21:43:09 -0800
committerMartin Braun <martin.braun@ettus.com>2018-02-01 06:35:37 +0100
commit4ebfea45f32cc845d7887e552e8c6084d8ef3067 (patch)
tree03072dbab4870b9d648d154331d0ee7911da1b24 /host
parent21be43d40a0fe33af9597e77fb3465caa7079033 (diff)
downloaduhd-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.hpp121
-rw-r--r--host/lib/utils/CMakeLists.txt1
-rw-r--r--host/lib/utils/config_parser.cpp59
-rw-r--r--host/tests/CMakeLists.txt13
-rw-r--r--host/tests/config_parser_test.cpp97
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 &section);
+
+ /*! 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 &section,
+ 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 &section,
+ 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 &section,
+ 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 &section)
+{
+ 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();
+}