避免在字符串中进行if-else分支以进行类型调度 [英] Avoid if-else branching in string to type dispatching
问题描述
通常,当您编写一个接受参数的CLI工具时,必须对其进行处理。大多数情况下,您希望根据参数的值在行为之间进行切换。
Usually when you write a CLI tool which accepts parameter you have to deal with them. Most of the time you want to switch between behaviours based on the value of an argument.
以下是一个常见的用例,其中程序接受一个类型然后打印基于这种类型的东西。我正在使用Boost进行预处理并自动生成整个 if-else
分支。
就可维护性而言,这非常好,因为当我引入新类型时,我只需要更新 define
。另一方面,它远非现代而优雅。
The following is a common use case, where the program accepts a type and then prints something based on that type. I am using Boost to pre-process and auto generate the whole if-else
branches.
This is very nice in terms of maintainability as I only need to update a define
when I introduce a new type. On the other hand it is quite far from being modern and elegant.
我考虑过使用更好的枚举
来避免使用 if-else
使用 _from_string 实用程序功能。但是,从枚举转换为类型的方法对我来说还是很模糊。
I thought about using better-enums
to avoid using the if-else
to convert from string into an enum using the _from_string utility function. But then the way to go from enum to a type is still obscure to me.
关于如何保持当前实现的良好可维护性但避免使用pre的任何建议处理器和宏功能?
Any suggestion on how to keep the nice maintainability of the current implementation but avoid to use pre-processor and macro functionalities?
#include <iostream>
#include <cstdlib>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/preprocessor/seq/for_each.hpp>
#include <type_traits>
using a_type = int;
using b_type = long;
using c_type = float;
using d_type = double;
#define TYPES (a)(b)(c)(d)
template<typename T>
void foo(){
T num = 1;
std::cout << typeid(decltype(num)).name() << " : "<< num << std::endl;
};
int main(int argc, char **argv)
{
if (argc < 1) {
return 1;
}
std::string type = argv[1];
if (false) {
#define LOOP_BODY(R, DATA, T) \
} \
else if (type == BOOST_PP_STRINGIZE(T)) { \
foo<BOOST_PP_CAT(T, _type)>(); \
BOOST_PP_SEQ_FOR_EACH(LOOP_BODY, _, TYPES);
#undef LOOP_BODY
} else {
std::cout << "ERROR: Unknown type " << type << std::endl;
}
}
在 https://wandbox.org/permlink/60bAwoqYxzU1EUdw
推荐答案
另一种方法是使用纯数组和 std :: find_if
代替 if-else
:
Another way is to use a plain array and std::find_if
instead of if-else
:
#include <algorithm>
#include <iostream>
#include <iterator>
#include <string>
#include <typeinfo>
struct Handler {
char const* name;
void(*fn)(std::string const&); // Or std::function<> to accept lambdas.
};
struct A {};
struct B {};
template<class T>
void foo(std::string const& name) {
std::cout << "foo<" << typeid(T).name() << ">: " << name << '\n';
}
int main(int, char** av) {
Handler const handlers[] = {
{"a", foo<A>}
, {"b", foo<B>}
};
std::string const name = av[1];
auto handler = std::find_if(std::begin(handlers), std::end(handlers), [&name](auto const& h) {
return name == h.name;
});
if(handler != std::end(handlers))
handler->fn(name);
}
这篇关于避免在字符串中进行if-else分支以进行类型调度的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!