uhd/host/lib/include/uhdlib/usrp/constrained_device_args.hpp
Lane Kolbly 5802a362f8 uhd: Replace include guards with pragma once
Pragma once is the more modern version of include guards, eliminating
any potential problems with mistyping include guards. Let's use those.
2020-04-08 15:16:06 -05:00

328 lines
9.6 KiB
C++

//
// Copyright 2014 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: GPL-3.0-or-later
//
#pragma once
#include <uhd/exception.hpp>
#include <uhd/types/device_addr.hpp>
#include <uhd/utils/cast.hpp>
#include <unordered_map>
#include <boost/algorithm/string.hpp>
#include <boost/assign/list_of.hpp>
#include <boost/format.hpp>
#include <sstream>
#include <string>
#include <vector>
namespace uhd { namespace usrp {
/*!
* constrained_device_args_t provides a base and utilities to
* map key=value pairs passed in through the device creation
* args interface (device_addr_t).
*
* Inherit from this class to create typed device specific
* arguments and use the base class methods to handle parsing
* the device_addr or any key=value string to populate the args
*
* This file contains a library of different types of args the
* the user can pass in. The library can be extended to support
* non-intrinsic types by the client.
*
*/
class constrained_device_args_t
{
public: // Types
/*!
* Base argument type. All other arguments inherit from this.
*/
class generic_arg
{
public:
generic_arg(const std::string& key) : _key(key) {}
inline const std::string& key() const
{
return _key;
}
inline virtual std::string to_string() const = 0;
private:
std::string _key;
};
/*!
* String argument type. Can be case sensitive or insensitive
*/
template <bool case_sensitive>
class str_arg : public generic_arg
{
public:
str_arg(const std::string& name, const std::string& default_value)
: generic_arg(name)
{
set(default_value);
}
inline void set(const std::string& value)
{
_value = case_sensitive ? value : boost::algorithm::to_lower_copy(value);
}
inline const std::string& get() const
{
return _value;
}
inline void parse(const std::string& str_rep)
{
set(str_rep);
}
inline virtual std::string to_string() const
{
return key() + "=" + get();
}
inline bool operator==(const std::string& rhs) const
{
return get() == boost::algorithm::to_lower_copy(rhs);
}
private:
std::string _value;
};
typedef str_arg<false> str_ci_arg;
typedef str_arg<true> str_cs_arg;
/*!
* Numeric argument type. The template type data_t allows the
* client to constrain the type of the number.
*/
template <typename data_t>
class num_arg : public generic_arg
{
public:
num_arg(const std::string& name, const data_t default_value) : generic_arg(name)
{
set(default_value);
}
inline void set(const data_t value)
{
_value = value;
}
inline const data_t get() const
{
return _value;
}
inline void parse(const std::string& str_rep)
{
try {
_value = boost::lexical_cast<data_t>(str_rep);
} catch (std::exception& ex) {
throw uhd::value_error(
str(boost::format("Error parsing numeric parameter %s: %s.") % key()
% ex.what()));
}
}
inline virtual std::string to_string() const
{
return key() + "=" + std::to_string(get());
}
private:
data_t _value;
};
/*!
* Enumeration argument type. The template type enum_t allows the
* client to use their own enum and specify a string mapping for
* the values of the enum
*/
template <typename enum_t>
class enum_arg : public generic_arg
{
public:
enum_arg(const std::string& name,
const enum_t default_value,
const std::unordered_map<std::string, enum_t>& values)
: generic_arg(name), _str_values(_enum_map_to_lowercase<enum_t>(values))
{
set(default_value);
}
inline void set(const enum_t value)
{
_value = value;
}
inline const enum_t get() const
{
return _value;
}
inline void parse(const std::string& str_rep, const bool assert_invalid = true)
{
const std::string str_rep_lowercase =
boost::algorithm::to_lower_copy(str_rep);
if (_str_values.count(str_rep_lowercase) == 0) {
if (assert_invalid) {
std::string valid_values_str = "";
for (const auto& value : _str_values) {
valid_values_str +=
(valid_values_str.empty() ? "" : ", ") + value.first;
}
throw uhd::value_error(
str(boost::format("Invalid device arg value: %s=%s (Valid: {%s})")
% key() % str_rep % valid_values_str));
} else {
return;
}
}
set(_str_values.at(str_rep_lowercase));
}
inline virtual std::string to_string() const
{
std::string repr;
for (const auto& value : _str_values) {
if (value.second == _value) {
repr = value.first;
break;
}
}
UHD_ASSERT_THROW(!repr.empty());
return key() + "=" + repr;
}
private:
enum_t _value;
const std::unordered_map<std::string, enum_t> _str_values;
};
/*!
* Boolean argument type.
*/
class bool_arg : public generic_arg
{
public:
bool_arg(const std::string& name, const bool default_value) : generic_arg(name)
{
set(default_value);
}
inline void set(const bool value)
{
_value = value;
}
inline bool get() const
{
return _value;
}
inline void parse(const std::string& str_rep)
{
try {
if (str_rep.empty()) {
// If str_rep is empty, the flag is interpreted as set
_value = true;
} else {
_value = uhd::cast::from_str<bool>(str_rep);
}
} catch (std::exception& ex) {
throw uhd::value_error(
str(boost::format("Error parsing boolean parameter %s: %s.")
% key() % ex.what()));
}
}
inline virtual std::string to_string() const
{
return key() + "=" + (get() ? "true" : "false");
}
private:
bool _value;
};
public: // Methods
constrained_device_args_t() {}
virtual ~constrained_device_args_t() {}
void parse(const std::string& str_args)
{
device_addr_t dev_args(str_args);
_parse(dev_args);
}
void parse(const device_addr_t& dev_args)
{
_parse(dev_args);
}
inline virtual std::string to_string() const = 0;
template <typename arg_type>
void parse_arg_default(const device_addr_t& dev_args, arg_type& constrained_arg)
{
if (dev_args.has_key(constrained_arg.key())) {
constrained_arg.parse(dev_args[constrained_arg.key()]);
}
}
protected: // Methods
// Override _parse to provide an implementation to parse all
// client specific device args
virtual void _parse(const device_addr_t& dev_args) = 0;
/*!
* Utility: Ensure that the value of the device arg is between min and max
*/
template <typename num_data_t>
static inline void _enforce_range(
const num_arg<num_data_t>& arg, const num_data_t& min, const num_data_t& max)
{
if (arg.get() > max || arg.get() < min) {
throw uhd::value_error(str(
boost::format("Invalid device arg value: %s (Minimum: %s, Maximum: %s)")
% arg.to_string() % std::to_string(min) % std::to_string(max)));
}
}
/*!
* Utility: Ensure that the value of the device arg is is contained in valid_values
*/
template <typename arg_t, typename data_t>
static inline void _enforce_discrete(
const arg_t& arg, const std::vector<data_t>& valid_values)
{
bool match = false;
for (const data_t& val : valid_values) {
if (val == arg.get()) {
match = true;
break;
}
}
if (!match) {
std::string valid_values_str;
for (size_t i = 0; i < valid_values.size(); i++) {
std::stringstream valid_values_ss;
valid_values_ss << ((i == 0) ? "" : ", ") << valid_values[i];
throw uhd::value_error(
str(boost::format("Invalid device arg value: %s (Valid: {%s})")
% arg.to_string() % valid_values_ss.str()));
}
}
}
//! Helper for enum_arg: Create a new map where keys are converted to
// lowercase.
template <typename enum_t>
static std::unordered_map<std::string, enum_t> _enum_map_to_lowercase(
const std::unordered_map<std::string, enum_t>& in_map)
{
std::unordered_map<std::string, enum_t> new_map;
for (const auto& str_to_enum : in_map) {
new_map.insert(std::pair<std::string, enum_t>(
boost::algorithm::to_lower_copy(str_to_enum.first), str_to_enum.second));
}
return new_map;
}
};
}} // namespace uhd::usrp