将用户输入转换为C ++中的类型时,if / else语句过多 [英] Too many if/else statements in converting user inputs to types in C++
问题描述
我有一个带有3个模板参数的模板类。
I have a template class with 3 template arguments.
template <class T, class U, class Y>
class MyClass {};
我想通过CLI参数(例如)从用户那里获取输入。 float driver-x load
I wanna get input from users by CLI arguments, something like ./cli float driver-x load
- 第一个arg可以是
float
或double
- 第二个arg是驱动程序名称:
driver-x
,driver-y
,... - 第三个参数与操作类型有关:
加载
,卸载
,...
- The first arg can be
float
ordouble
- The second arg is a driver name:
driver-x
,driver-y
, ... - The third argument is about the action type:
load
,unload
, ...
如果要基于用户输入创建 MyClass
的新实例,则必须定义许多 if / else
语句。因为用户输入的是 string
,所以我必须为其准备条件。
因此,将是这样的:
If I want to create a new instance of MyClass
based on user inputs, I have to define many if/else
statements. Because a user inputs are string
and I have to prepare a condition on them.
So, it will be something like this:
if (data_type == "float")
if (driver == "driver-x")
if (action == "load")
MyClass<float, DriverX, Load> t;
t......
据我所知,无法存储
因此,有什么方法可以改善 if / else
语句?像这样的东西:
So, is there any way exists to improve the if/else
statements? Something like:
if (data_type == "float")
//
if (driver == "driver-x")
//
if (action == "load")
//
MyClass<......> t;
t.....;
还是其他方式?
I我正在寻找改善这些 if / else
语句的方法。
I'm looking for a way to improve these if/else
statements.
推荐答案
您可以构建一些机制来为您执行此操作,将其提取到函数调用中。
You can build some machinery to do this for you, extracting it into a function call.
例如,在这里,我构建一个包含字符串和类型的元组,然后我对照所有这些字符串检查了传递的字符串:
For example, here I build a tuple which contains strings and types, then I check a passed string against all of them:
#include <string_view>
#include <cstddef>
#include <tuple>
#include <utility>
#include <type_traits>
template<class T>
struct mapped_type {
const std::string_view key;
using type = T;
explicit constexpr operator bool() const noexcept {
return true;
}
};
namespace detail {
template<class K, class F, class M, std::size_t I>
constexpr void lookup_impl(const K& key, F&& f, M&& m, std::integral_constant<std::size_t, I>) {
using tuple_t = typename std::remove_cv<typename std::remove_reference<M>::type>::type;
if constexpr (I < std::tuple_size<tuple_t>::value) {
const auto& mapping = std::get<I>(m);
if (mapping.key == key) {
std::forward<F>(f)(mapping);
return;
}
lookup_impl(key, std::forward<F>(f), std::forward<M>(m), std::integral_constant<std::size_t, I + 1>{});
} else {
std::forward<F>(f)(std::false_type{});
}
}
}
// Calls `f` with the first value from `m` that matches the key
// or `std::false_type{}` if no key matches.
template<class K, class F, class M>
constexpr void lookup(const K& key, F&& f, M&& m) {
detail::lookup_impl(key, std::forward<F>(f), std::forward<M>(m), std::integral_constant<std::size_t, 0>{});
}
// This is our mapping for the first argument
inline constexpr auto data_type_map = std::make_tuple(
mapped_type<float>{ "float" },
mapped_type<double>{ "double" }
);
// Example usage
#include <iostream>
int main() {
const char* s = "float";
lookup(s, [](const auto& arg) {
if constexpr (!arg) {
std::cout << "Invalid type\n";
} else {
using type = typename std::remove_cv<typename std::remove_reference<decltype(arg)>::type>::type::type;
std::cout << "Got type: " << typeid(type).name() << '\n';
}
}, data_type_map);
}
然后您可以在lambda内递归调用它。
And then you can call this recursively inside the lambda.
您还可以创建一个包含键元组和值元组的版本,以使用多个参数调用一个函数:
You could also create a version that takes a tuple of keys and a tuple of values to call one function with many arguments:
#include <string_view>
#include <tuple>
#include <utility>
#include <type_traits>
template<class T>
struct mapped_type {
const std::string_view key;
using type = T;
explicit constexpr operator bool() const noexcept {
return true;
}
};
namespace detail {
template<class K, class F, class M, std::size_t I>
constexpr void lookup_impl(F&& f, const K& key, M&& m, std::integral_constant<std::size_t, I>) {
using tuple_t = typename std::remove_cv<typename std::remove_reference<M>::type>::type;
if constexpr (I < std::tuple_size<tuple_t>::value) {
const auto& mapping = std::get<I>(m);
if (mapping.key == key) {
std::forward<F>(f)(mapping);
return;
}
lookup_impl(std::forward<F>(f), key, std::forward<M>(m), std::integral_constant<std::size_t, I + 1>{});
} else {
std::forward<F>(f)(std::false_type{});
}
}
template<class F, class K, class M, std::size_t I>
constexpr void multilookup_impl(F&& f, const K& keys, M&& mappings, std::integral_constant<std::size_t, I>) {
constexpr std::size_t size = std::tuple_size<typename std::remove_cv<typename std::remove_reference<K>::type>::type>::value;
if constexpr (I >= size) {
std::forward<F>(f)();
} else {
lookup_impl([&](const auto& current_lookup) {
multilookup_impl(
[&](const auto&... args) { std::forward<F>(f)(current_lookup, args...); },
keys, mappings, std::integral_constant<std::size_t, I + 1>{}
);
}, std::get<I>(keys), std::get<I>(mappings), std::integral_constant<std::size_t, 0>{});
}
}
}
template<class F, class K, class M>
constexpr void lookup(F&& f, const K& keys, M&& mappings) {
using map_tuple_t = typename std::remove_cv<typename std::remove_reference<M>::type>::type;
using key_tuple_t = typename std::remove_cv<typename std::remove_reference<K>::type>::type;
constexpr std::size_t size = std::tuple_size<key_tuple_t>::value;
static_assert(size == std::tuple_size<map_tuple_t>::value, "Wrong number of keys for given number of maps");
detail::multilookup_impl(std::forward<F>(f), keys, mappings, std::integral_constant<std::size_t, 0>{});
}
看上去几乎相同,但通话水平更高。
Which looks almost the same, but there's one more level of calls.
它会像这样使用:
#include <iostream>
inline constexpr auto data_type_map = std::make_tuple(
mapped_type<float>{ "float" },
mapped_type<double>{ "double" }
);
inline constexpr auto driver_type_map = std::make_tuple(
mapped_type<DriverX>{ "driver-x" },
mapped_type<DriverY>{ "driver-y" }
);
inline constexpr auto action_type_map = std::make_tuple(
mapped_type<Load>{ "load" },
mapped_type<Unload>{ "unload" }
);
int main() {
const char* a = "float";
const char* b = "driver-x";
const char* c = "load";
lookup([](const auto& data, const auto& driver, const auto& action) {
if constexpr (!data) {
std::cout << "Could not parse data!\n";
} else if constexpr (!driver) {
std::cout << "Could not parse driver!\n";
} else if constexpr (!action) {
std::cout << "Could not parse action!\n";
} else {
using data_type = typename std::remove_cv<typename std::remove_reference<decltype(data)>::type>::type::type;
using driver_type = typename std::remove_cv<typename std::remove_reference<decltype(driver)>::type>::type::type;
using action_type = typename std::remove_cv<typename std::remove_reference<decltype(action)>::type>::type::type;
MyClass<data_type, driver_type, action_type> t;
std::cout << "Constructed a " << typeid(decltype(t)).name() << '\n';
}
},
std::array<const char*, 3>{ a, b, c },
std::forward_as_tuple(data_type_map, driver_type_map, action_type_map)
);
}
这篇关于将用户输入转换为C ++中的类型时,if / else语句过多的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!