diff options
-rw-r--r-- | gr-soapy/include/gnuradio/soapy/block.h | 97 | ||||
-rw-r--r-- | gr-soapy/lib/block_impl.cc | 113 | ||||
-rw-r--r-- | gr-soapy/lib/block_impl.h | 17 | ||||
-rw-r--r-- | gr-soapy/python/soapy/bindings/CMakeLists.txt | 1 | ||||
-rw-r--r-- | gr-soapy/python/soapy/bindings/block_python.cc | 131 | ||||
-rw-r--r-- | gr-soapy/python/soapy/bindings/docstrings/block_pydoc_template.h | 36 | ||||
-rw-r--r-- | gr-soapy/python/soapy/bindings/soapy_common.cc | 130 | ||||
-rw-r--r-- | gr-soapy/python/soapy/bindings/soapy_common.h | 29 | ||||
-rw-r--r-- | gr-soapy/python/soapy/bindings/soapy_types_python.cc | 114 |
9 files changed, 555 insertions, 113 deletions
diff --git a/gr-soapy/include/gnuradio/soapy/block.h b/gr-soapy/include/gnuradio/soapy/block.h index 583d7af4b6..f69a2379e8 100644 --- a/gr-soapy/include/gnuradio/soapy/block.h +++ b/gr-soapy/include/gnuradio/soapy/block.h @@ -354,6 +354,103 @@ public: * \return the name of the clock source */ virtual std::string get_clock_source() const = 0; + + /*! + * List the available global readback sensors. + * A sensor can represent a reference lock, RSSI, temperature. + * \return a list of available sensor string names + */ + virtual std::vector<std::string> list_sensors() const = 0; + + /*! + * Get meta-information about a sensor. + * Example: displayable name, type, range. + * \param key the ID name of an available sensor + * \return meta-information about a sensor + */ + virtual arginfo_t get_sensor_info(const std::string& key) const = 0; + + /*! + * Readback a global sensor given the name. + * The value returned is a string which can represent + * a boolean ("true"/"false"), an integer, or float. + * \param key the ID name of an available sensor + * \return the current value of the sensor + */ + virtual std::string read_sensor(const std::string& key) const = 0; + + /*! + * List the available channel readback sensors. + * A sensor can represent a reference lock, RSSI, temperature. + * \param channel an available channel on the device + * \return a list of available sensor string names + */ + virtual std::vector<std::string> list_sensors(size_t channel) const = 0; + + /*! + * Get meta-information about a channel sensor. + * Example: displayable name, type, range. + * \param channel an available channel on the device + * \param key the ID name of an available sensor + * \return meta-information about a sensor + */ + virtual arginfo_t get_sensor_info(size_t channel, const std::string& key) const = 0; + + /*! + * Readback a channel sensor given the name. + * The value returned is a string which can represent + * a boolean ("true"/"false"), an integer, or float. + * \param channel an available channel on the device + * \param key the ID name of an available sensor + * \return the current value of the sensor + */ + virtual std::string read_sensor(size_t channel, const std::string& key) const = 0; + + /*! + * Describe the allowed keys and values used for settings. + * \return a list of argument info structures + */ + virtual arginfo_list_t get_setting_info() const = 0; + + /*! + * Write an arbitrary setting on the device. + * The interpretation is up the implementation. + * \param key the setting identifier + * \param value the setting value + */ + virtual void write_setting(const std::string& key, const std::string& value) = 0; + + /*! + * Read an arbitrary setting on the device. + * \param key the setting identifier + * \return the setting value + */ + virtual std::string read_setting(const std::string& key) const = 0; + + /*! + * Describe the allowed keys and values used for channel settings. + * \param channel an available channel on the device + * \return a list of argument info structures + */ + virtual arginfo_list_t get_setting_info(size_t channel) const = 0; + + /*! + * Write an arbitrary channel setting on the device. + * The interpretation is up the implementation. + * \param channel an available channel on the device + * \param key the setting identifier + * \param value the setting value + */ + virtual void + write_setting(size_t channel, const std::string& key, const std::string& value) = 0; + + /*! + * Read an arbitrary channel setting on the device. + * \param channel an available channel on the device + * \param key the setting identifier + * \return the setting value + */ + virtual std::string read_setting(size_t channel, const std::string& key) const = 0; }; } // namespace soapy diff --git a/gr-soapy/lib/block_impl.cc b/gr-soapy/lib/block_impl.cc index d9c895d80b..0e41182b40 100644 --- a/gr-soapy/lib/block_impl.cc +++ b/gr-soapy/lib/block_impl.cc @@ -727,6 +727,119 @@ void block_impl::set_clock_source(const std::string& clock_source) std::string block_impl::get_clock_source() const { return d_device->getClockSource(); } +std::vector<std::string> block_impl::list_sensors() const +{ + return d_device->listSensors(); +} + +arginfo_t block_impl::get_sensor_info(const std::string& key) const +{ + const auto sensors = d_device->listSensors(); + + if (vector_contains(sensors, key)) { + return d_device->getSensorInfo(key); + } else { + throw std::invalid_argument("Invalid sensor: " + key); + } +} + +std::string block_impl::read_sensor(const std::string& key) const +{ + const auto sensors = d_device->listSensors(); + + if (vector_contains(sensors, key)) { + return d_device->readSensor(key); + } else { + throw std::invalid_argument("Invalid sensor: " + key); + } +} + +std::vector<std::string> block_impl::list_sensors(size_t channel) const +{ + validate_channel(channel); + return d_device->listSensors(d_direction, channel); +} + +arginfo_t block_impl::get_sensor_info(size_t channel, const std::string& key) const +{ + validate_channel(channel); + const auto sensors = d_device->listSensors(d_direction, channel); + + if (vector_contains(sensors, key)) { + return d_device->getSensorInfo(d_direction, channel, key); + } else { + throw std::invalid_argument("Invalid sensor: " + key); + } +} + +std::string block_impl::read_sensor(size_t channel, const std::string& key) const +{ + validate_channel(channel); + const auto sensors = d_device->listSensors(d_direction, channel); + + if (vector_contains(sensors, key)) { + return d_device->readSensor(d_direction, channel, key); + } else { + throw std::invalid_argument("Invalid sensor: " + key); + } +} + +arginfo_list_t block_impl::get_setting_info() const { return d_device->getSettingInfo(); } + +void block_impl::write_setting(const std::string& key, const std::string& value) +{ + const auto setting_info = d_device->getSettingInfo(); + + if (arg_info_has_key(setting_info, key)) { + d_device->writeSetting(key, value); + } else { + throw std::invalid_argument("Invalid setting: " + key); + } +} + +std::string block_impl::read_setting(const std::string& key) const +{ + const auto setting_info = d_device->getSettingInfo(); + + if (arg_info_has_key(setting_info, key)) { + return d_device->readSetting(key); + } else { + throw std::invalid_argument("Invalid setting: " + key); + } +} + +arginfo_list_t block_impl::get_setting_info(size_t channel) const +{ + validate_channel(channel); + return d_device->getSettingInfo(d_direction, channel); +} + +void block_impl::write_setting(size_t channel, + const std::string& key, + const std::string& value) +{ + validate_channel(channel); + const auto setting_info = d_device->getSettingInfo(d_direction, channel); + + if (arg_info_has_key(setting_info, key)) { + d_device->writeSetting(d_direction, channel, key, value); + } else { + throw std::invalid_argument("Invalid setting: " + key); + } +} + +std::string block_impl::read_setting(size_t channel, const std::string& key) const +{ + validate_channel(channel); + const auto setting_info = d_device->getSettingInfo(d_direction, channel); + + if (arg_info_has_key(setting_info, key)) { + return d_device->readSetting(d_direction, channel, key); + } else { + throw std::invalid_argument("Invalid setting: " + key); + } +} + /* End public API implementation */ void block_impl::cmd_handler_frequency(pmt::pmt_t val, size_t channel) diff --git a/gr-soapy/lib/block_impl.h b/gr-soapy/lib/block_impl.h index 3fa1bec68d..819ee5a03f 100644 --- a/gr-soapy/lib/block_impl.h +++ b/gr-soapy/lib/block_impl.h @@ -156,6 +156,23 @@ public: void set_clock_source(const std::string& clock_source) override; std::string get_clock_source() const override; + std::vector<std::string> list_sensors() const override; + arginfo_t get_sensor_info(const std::string& key) const override; + std::string read_sensor(const std::string& key) const override; + std::vector<std::string> list_sensors(size_t channel) const override; + arginfo_t get_sensor_info(size_t channel, const std::string& key) const override; + std::string read_sensor(size_t channel, const std::string& key) const override; + + virtual arginfo_list_t get_setting_info() const override; + virtual void write_setting(const std::string& key, const std::string& value) override; + virtual std::string read_setting(const std::string& key) const override; + virtual arginfo_list_t get_setting_info(size_t channel) const override; + virtual void write_setting(size_t channel, + const std::string& key, + const std::string& value) override; + virtual std::string read_setting(size_t channel, + const std::string& key) const override; + /*** End public API implementation ***/ protected: diff --git a/gr-soapy/python/soapy/bindings/CMakeLists.txt b/gr-soapy/python/soapy/bindings/CMakeLists.txt index 902cf9c42b..5c54909eae 100644 --- a/gr-soapy/python/soapy/bindings/CMakeLists.txt +++ b/gr-soapy/python/soapy/bindings/CMakeLists.txt @@ -13,6 +13,7 @@ include(GrPybind) list(APPEND soapy_python_files block_python.cc + soapy_common.cc soapy_types_python.cc source_python.cc sink_python.cc diff --git a/gr-soapy/python/soapy/bindings/block_python.cc b/gr-soapy/python/soapy/bindings/block_python.cc index 29311ef026..53b0a1ec65 100644 --- a/gr-soapy/python/soapy/bindings/block_python.cc +++ b/gr-soapy/python/soapy/bindings/block_python.cc @@ -15,9 +15,11 @@ /* BINDTOOL_GEN_AUTOMATIC(0) */ /* BINDTOOL_USE_PYGCCXML(0) */ /* BINDTOOL_HEADER_FILE(block.h) */ -/* BINDTOOL_HEADER_FILE_HASH(7742c1003b34ca79350bfaa736f25ce2) */ +/* BINDTOOL_HEADER_FILE_HASH(6aea02a7cec962d25665546a614ee437) */ /***********************************************************************************/ +#include "soapy_common.h" + #include <pybind11/complex.h> #include <pybind11/pybind11.h> #include <pybind11/stl.h> @@ -28,6 +30,24 @@ namespace py = pybind11; // pydoc.h is automatically generated in the build directory #include <block_pydoc.h> +#include <algorithm> +#include <cassert> + +// Assumption: we've validated that this key exists, +// probably through get_setting_info. +static gr::soapy::arginfo_t +get_specific_arginfo(const gr::soapy::arginfo_list_t& arginfo_list, + const std::string& key) +{ + auto iter = std::find_if( + arginfo_list.begin(), + arginfo_list.end(), + [&key](const gr::soapy::arginfo_t& arginfo) { return (arginfo.key == key); }); + assert(iter != arginfo_list.end()); + + return (*iter); +} + void bind_block(py::module& m) { @@ -103,13 +123,14 @@ void bind_block(py::module& m) .def("get_frequency_range", - (SoapySDR::RangeList(block::*)(size_t) const) & block::get_frequency_range, + (gr::soapy::range_list_t(block::*)(size_t) const) & + block::get_frequency_range, py::arg("channel"), D(block, get_frequency_range, 0)) .def("get_frequency_range", - (SoapySDR::RangeList(block::*)(size_t, const std::string&) const) & + (gr::soapy::range_list_t(block::*)(size_t, const std::string&) const) & block::get_frequency_range, py::arg("channel"), py::arg("name"), @@ -203,13 +224,13 @@ void bind_block(py::module& m) .def("get_gain_range", - (SoapySDR::Range(block::*)(size_t) const) & block::get_gain_range, + (gr::soapy::range_t(block::*)(size_t) const) & block::get_gain_range, py::arg("channel"), D(block, get_gain_range, 0)) .def("get_gain_range", - (SoapySDR::Range(block::*)(size_t, const std::string&) const) & + (gr::soapy::range_t(block::*)(size_t, const std::string&) const) & block::get_gain_range, py::arg("channel"), py::arg("name"), @@ -321,5 +342,103 @@ void bind_block(py::module& m) .def("get_clock_source", &block::get_clock_source, D(block, get_clock_source)) - ; + .def("list_sensors", + (std::vector<std::string>(block::*)() const) & block::list_sensors, + D(block, list_sensors, 0)) + + .def("get_sensor_info", + (gr::soapy::arginfo_t(block::*)(const std::string&) const) & + block::get_sensor_info, + py::arg("key"), + D(block, get_sensor_info, 0)) + + .def( + "read_sensor", + [](const block& self, const std::string& key) -> py::object { + const auto arginfo = self.get_sensor_info(key); + + return cast_string_to_arginfo_type(arginfo.type, arginfo.value); + }, + py::arg("key"), + D(block, read_sensor, 0)) + + .def("list_sensors", + (std::vector<std::string>(block::*)(size_t) const) & block::list_sensors, + py::arg("channel"), + D(block, list_sensors, 1)) + + .def("get_sensor_info", + (gr::soapy::arginfo_t(block::*)(size_t, const std::string&) const) & + block::get_sensor_info, + py::arg("channel"), + py::arg("key"), + D(block, get_sensor_info, 1)) + + .def( + "read_sensor", + [](const block& self, size_t channel, const std::string& key) -> py::object { + const auto arginfo = self.get_sensor_info(channel, key); + + return cast_string_to_arginfo_type(arginfo.type, arginfo.value); + }, + py::arg("channel"), + py::arg("key"), + D(block, read_sensor, 1)) + + .def("get_setting_info", + (gr::soapy::arginfo_list_t(block::*)() const) & block::get_setting_info, + D(block, get_setting_info, 0)) + + .def( + "write_setting", + [](block& self, const std::string& key, py::object value) -> void { + auto setting_info = cast_pyobject_to_arginfo_string(value); + + self.write_setting(key, setting_info.value); + }, + py::arg("key"), + py::arg("value"), + D(block, write_setting, 0)) + + .def( + "read_setting", + [](const block& self, const std::string& key) -> py::object { + const auto setting_info = + get_specific_arginfo(self.get_setting_info(), key); + + return cast_string_to_arginfo_type(setting_info.type, setting_info.value); + }, + py::arg("key"), + D(block, read_setting, 0)) + + .def("get_setting_info", + (gr::soapy::arginfo_list_t(block::*)(size_t) const) & + block::get_setting_info, + py::arg("channel"), + D(block, get_setting_info, 0)) + + .def( + "write_setting", + [](block& self, size_t channel, const std::string& key, py::object value) + -> void { + auto setting_info = cast_pyobject_to_arginfo_string(value); + + self.write_setting(channel, key, setting_info.value); + }, + py::arg("channel"), + py::arg("key"), + py::arg("value"), + D(block, write_setting, 0)) + + .def( + "read_setting", + [](const block& self, size_t channel, const std::string& key) -> py::object { + const auto setting_info = + get_specific_arginfo(self.get_setting_info(channel), key); + + return cast_string_to_arginfo_type(setting_info.type, setting_info.value); + }, + py::arg("channel"), + py::arg("key"), + D(block, read_setting, 0)); } diff --git a/gr-soapy/python/soapy/bindings/docstrings/block_pydoc_template.h b/gr-soapy/python/soapy/bindings/docstrings/block_pydoc_template.h index b690474aa1..5318204af6 100644 --- a/gr-soapy/python/soapy/bindings/docstrings/block_pydoc_template.h +++ b/gr-soapy/python/soapy/bindings/docstrings/block_pydoc_template.h @@ -166,3 +166,39 @@ static const char* __doc_gr_soapy_block_set_clock_source = R"doc()doc"; static const char* __doc_gr_soapy_block_get_clock_source = R"doc()doc"; + + +static const char* __doc_gr_soapy_block_list_sensors_0 = R"doc()doc"; + + +static const char* __doc_gr_soapy_block_get_sensor_info_0 = R"doc()doc"; + + +static const char* __doc_gr_soapy_block_read_sensor_0 = R"doc()doc"; + + +static const char* __doc_gr_soapy_block_list_sensors_1 = R"doc()doc"; + + +static const char* __doc_gr_soapy_block_get_sensor_info_1 = R"doc()doc"; + + +static const char* __doc_gr_soapy_block_read_sensor_1 = R"doc()doc"; + + +static const char* __doc_gr_soapy_block_get_setting_info_0 = R"doc()doc"; + + +static const char* __doc_gr_soapy_block_write_setting_0 = R"doc()doc"; + + +static const char* __doc_gr_soapy_block_read_setting_0 = R"doc()doc"; + + +static const char* __doc_gr_soapy_block_get_setting_info_1 = R"doc()doc"; + + +static const char* __doc_gr_soapy_block_write_setting_1 = R"doc()doc"; + + +static const char* __doc_gr_soapy_block_read_setting_1 = R"doc()doc"; diff --git a/gr-soapy/python/soapy/bindings/soapy_common.cc b/gr-soapy/python/soapy/bindings/soapy_common.cc new file mode 100644 index 0000000000..98d59be032 --- /dev/null +++ b/gr-soapy/python/soapy/bindings/soapy_common.cc @@ -0,0 +1,130 @@ +/* + * Copyright 2021 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ + +#include "soapy_common.h" + +#include <SoapySDR/Types.hpp> +#include <SoapySDR/Version.h> + +// SoapySDR doesn't have an API define for SettingToString, so we need +// to check the version. 0.8 is the first tagged version to have this +// functionality. +#if SOAPY_SDR_API_VERSION >= 0x080000 + +template <typename T> +static inline T string_to_setting(const std::string& str) +{ + return SoapySDR::StringToSetting<T>(str); +} + +template <typename T> +static inline std::string setting_to_string(const T& setting) +{ + return SoapySDR::SettingToString<T>(setting); +} + +#else + +// Copied from SoapySDR 0.8 +#define SOAPY_SDR_TRUE "true" +#define SOAPY_SDR_FALSE "false" + +#include <sstream> + +template <typename T> +static inline T string_to_setting(const std::string& str) +{ + std::stringstream sstream(str); + T setting; + + sstream >> setting; + + return setting; +} + +// Copied from SoapySDR 0.8 +template <> +inline bool string_to_setting<bool>(const std::string& str) +{ + if (str == SOAPY_SDR_TRUE) + return true; + if (str == SOAPY_SDR_FALSE) + return false; + + // zeros and empty strings are false + if (str == "0") + return false; + if (str == "0.0") + return false; + if (str == "") + return false; + + // other values are true + return true; +} + +template <typename T> +static inline std::string setting_to_string(const T& setting) +{ + return std::to_string(setting); +} + +template <> +inline std::string setting_to_string<bool>(const bool& setting) +{ + return setting ? SOAPY_SDR_TRUE : SOAPY_SDR_FALSE; +} + +#endif + +py::object cast_string_to_arginfo_type(gr::soapy::argtype_t argtype, + const std::string& str) +{ + py::object ret; + switch (argtype) { + case SoapySDR::ArgInfo::BOOL: + ret = py::bool_(string_to_setting<bool>(str)); + break; + + case SoapySDR::ArgInfo::INT: + ret = py::int_(string_to_setting<int>(str)); + break; + + case SoapySDR::ArgInfo::FLOAT: + ret = py::float_(string_to_setting<double>(str)); + break; + + default: + ret = py::str(str); + break; + } + + return ret; +} + +setting_info cast_pyobject_to_arginfo_string(py::object obj) +{ + setting_info info; + + if (py::isinstance<py::bool_>(obj)) { + info.value = setting_to_string(bool(py::cast<py::bool_>(obj))); + info.type = SoapySDR::ArgInfo::BOOL; + } else if (py::isinstance<py::int_>(obj)) { + info.value = setting_to_string(int(py::cast<py::int_>(obj))); + info.type = SoapySDR::ArgInfo::INT; + } else if (py::isinstance<py::float_>(obj)) { + info.value = setting_to_string(double(py::cast<py::float_>(obj))); + info.type = SoapySDR::ArgInfo::FLOAT; + } else { + info.value = py::str(obj); + info.type = SoapySDR::ArgInfo::STRING; + } + + return info; +} diff --git a/gr-soapy/python/soapy/bindings/soapy_common.h b/gr-soapy/python/soapy/bindings/soapy_common.h new file mode 100644 index 0000000000..90d5b90351 --- /dev/null +++ b/gr-soapy/python/soapy/bindings/soapy_common.h @@ -0,0 +1,29 @@ +/* + * Copyright 2021 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ + +#pragma once + +#include <pybind11/complex.h> +#include <pybind11/operators.h> +#include <pybind11/pybind11.h> +#include <pybind11/stl.h> + +namespace py = pybind11; + +#include <gnuradio/soapy/soapy_types.h> + +py::object cast_string_to_arginfo_type(gr::soapy::argtype_t argtype, + const std::string& str); + +struct setting_info { + std::string value; + gr::soapy::argtype_t type; +}; + +setting_info cast_pyobject_to_arginfo_string(py::object obj); diff --git a/gr-soapy/python/soapy/bindings/soapy_types_python.cc b/gr-soapy/python/soapy/bindings/soapy_types_python.cc index abcfa182a3..f91e03f619 100644 --- a/gr-soapy/python/soapy/bindings/soapy_types_python.cc +++ b/gr-soapy/python/soapy/bindings/soapy_types_python.cc @@ -7,6 +7,8 @@ * */ +#include "soapy_common.h" + #include <pybind11/complex.h> #include <pybind11/operators.h> #include <pybind11/pybind11.h> @@ -16,80 +18,6 @@ namespace py = pybind11; #include <gnuradio/soapy/soapy_types.h> -#include <SoapySDR/Types.hpp> -#include <SoapySDR/Version.h> - -// SoapySDR doesn't have an API define for SettingToString, so we need -// to check the version. 0.8 is the first tagged version to have this -// functionality. -#if SOAPY_SDR_API_VERSION >= 0x080000 - -template <typename T> -static inline T string_to_setting(const std::string& str) -{ - return SoapySDR::StringToSetting<T>(str); -} - -template <typename T> -static inline std::string setting_to_string(const T& setting) -{ - return SoapySDR::SettingToString<T>(setting); -} - -#else - -// Copied from SoapySDR 0.8 -#define SOAPY_SDR_TRUE "true" -#define SOAPY_SDR_FALSE "false" - -#include <sstream> - -template <typename T> -static inline T string_to_setting(const std::string& str) -{ - std::stringstream sstream(str); - T setting; - - sstream >> setting; - - return setting; -} - -// Copied from SoapySDR 0.8 -template <> -inline bool string_to_setting<bool>(const std::string& str) -{ - if (str == SOAPY_SDR_TRUE) - return true; - if (str == SOAPY_SDR_FALSE) - return false; - - // zeros and empty strings are false - if (str == "0") - return false; - if (str == "0.0") - return false; - if (str == "") - return false; - - // other values are true - return true; -} - -template <typename T> -static inline std::string setting_to_string(const T& setting) -{ - return std::to_string(setting); -} - -template <> -inline std::string setting_to_string<bool>(const bool& setting) -{ - return setting ? SOAPY_SDR_TRUE : SOAPY_SDR_FALSE; -} - -#endif - void bind_soapy_types(py::module& m) { py::class_<gr::soapy::range_t>(m, "range_t") @@ -131,42 +59,14 @@ void bind_soapy_types(py::module& m) .def_property( "value", [](const gr::soapy::arginfo_t& arginfo) -> py::object { - py::object ret; - switch (arginfo.type) { - case gr::soapy::arginfo_t::BOOL: - ret = py::bool_(string_to_setting<bool>(arginfo.value)); - break; - - case gr::soapy::arginfo_t::INT: - ret = py::int_(string_to_setting<int>(arginfo.value)); - break; - - case gr::soapy::arginfo_t::FLOAT: - ret = py::float_(string_to_setting<double>(arginfo.value)); - break; - - default: - ret = py::str(arginfo.value); - break; - } - - return ret; + return cast_string_to_arginfo_type(arginfo.type, arginfo.value); }, // So we can implicitly convert to Soapy's convention [](gr::soapy::arginfo_t& arginfo, py::object obj) -> void { - if (py::isinstance<py::bool_>(obj)) { - arginfo.value = setting_to_string(bool(py::cast<py::bool_>(obj))); - arginfo.type = gr::soapy::arginfo_t::BOOL; - } else if (py::isinstance<py::int_>(obj)) { - arginfo.value = setting_to_string(int(py::cast<py::int_>(obj))); - arginfo.type = gr::soapy::arginfo_t::INT; - } else if (py::isinstance<py::float_>(obj)) { - arginfo.value = setting_to_string(double(py::cast<py::float_>(obj))); - arginfo.type = gr::soapy::arginfo_t::FLOAT; - } else { - arginfo.value = py::str(obj); - arginfo.type = gr::soapy::arginfo_t::STRING; - } + const auto info = cast_pyobject_to_arginfo_string(obj); + + arginfo.value = info.value; + arginfo.type = info.type; }) .def_readwrite("name", &gr::soapy::arginfo_t::name) |