我正在玩一些代码,取自Vittorio Romeo的Avoid if-else branching in string to type dispatching回答,但改写为使用C ++ 14导致Vittorios版本使用C ++ 17倍表达式。我还认为改写是一个很好的练习。
这是代码:
#include <type_traits>
#include <iostream>
#include <utility>
#include <string>
template<char... Cs>
using ct_str = std::integer_sequence<char, Cs...>;
template<typename T, T... Cs>
constexpr ct_str<Cs...> operator""_cs() { return {}; }
template<typename Name, typename T>
struct named_type
{
using name = Name;
using type = T;
};
template<typename... Ts>
struct named_type_list { };
using my_types = named_type_list<
named_type<decltype("int"_cs), int>,
named_type<decltype("bool"_cs), bool>,
named_type<decltype("long"_cs), long>,
named_type<decltype("float"_cs), float>,
named_type<decltype("double"_cs), double>,
named_type<decltype("string"_cs), std::string>
>;
template<std::size_t... Is, char... Cs>
constexpr bool same_impl(const std::string& s,
std::integer_sequence<char, Cs...>,
std::index_sequence<Is...>)
{
const char c_arr[] = {Cs...};
for (std::size_t i = 0; i != sizeof...(Cs); ++i) {
if (s[i] != c_arr[i]) return false;
}
return true;
//Original C++17 (fold expression)
//return ((s[Is] == Cs) && ...);
}
template<char... Cs>
constexpr bool same(const std::string& s, std::integer_sequence<char, Cs...> seq)
{
std::cout << "checking '" << s << "' against '";
std::initializer_list<bool>{ bool(std::cout << Cs)... };
std::cout << "'\n";
return s.size() >= sizeof...(Cs)
&& same_impl(s, seq, std::make_index_sequence<sizeof...(Cs)>{});
}
template<typename... Ts, typename F>
void handle(named_type_list<Ts...>, const std::string& input, F&& f)
{
using expand_type = int[];
expand_type{ 0, (same(input, typename Ts::name{}) && (f(Ts{}), false), 0)... };
//(void)std::initializer_list<int> {
// ( (same(input, typename Ts::name{}) && (f(Ts{}), false) ), 0)...
//};
//Original C++17 (fold expression)
//( (same(input, typename Ts::name{}) && (f(Ts{}), true) ) || ...);
}
int main(int argc, char** argv)
{
const std::string input{"float"};
handle(my_types{}, input, [](auto t)
{
std::cout << typeid(typename decltype(t)::type).name() << "\n";
// TEST: define std::vector with value_type (matched type "float") and add a few values
using mtype = typename decltype(t)::type;
std::vector<mtype> x;
x.push_back(2.2); // <-- does not compile
});
return 0;
}
我认为问题在于handle
函数似乎没有正确停止评估。如果匹配,它应该在f()
的第一次调用时停止。相反,它会在匹配的情况下按预期执行f(),但继续执行named_type_list
中的其余类型。
当前代码导致此输出:
checking 'float' against 'int'
checking 'float' against 'bool'
checking 'float' against 'long'
checking 'float' against 'float'
f
checking 'float' against 'double'
checking 'float' against 'string'
实际上我不知道如何解决这个问题。我尝试使用std::initializer_list
技巧重写C ++ 17折叠表达式,并尝试使用扩展器(handle
体中未注释的部分。所以我猜这是表达式本身无法正常工作
不幸的是,我现在的想法是不是真的发生了,而且我没有使用元编程/编译时评估的经验。
可能使用此代码会产生另一个问题:
我的用例是在XML属性读取器中,我有类型/值标签,例如<attribute type="double" value="2.5"/>
,应用handle
函数之类的内容从typename
属性值中获取type
。我可以用这种类型来进一步处理价值。
为此我添加了handle
f()
- main()
3行中的正文,定义了std::vector
找到的类型,并尝试为其添加值。此代码无法编译,g ++以
error: no matching function for call to ‘std::vector<std::basic_string<char>, std::allocator<std::basic_string<char> > >::push_back(double)’
我想这是编译时和运行时行为的混淆,它不能以这种方式工作,这让我很好奇如何进一步处理/使用匹配类型。
感谢您的解释时间,非常感谢任何帮助!
答案 0 :(得分:0)
||
当然是短路。你的版本没有。我不认为短路对于正确性至关重要,但如果你愿意,可以通过额外的bool
轻松实现:
bool found = false;
expand_type{ 0, (!found &&
same(input, typename Ts::name{}) &&
(f(Ts{}), found = true), 0)... };
第二个问题是因为您的处理函数必须对类型列表中的每个可能类型都有效可调用,但您不能push_back
2.2
进入std::vector<std::string>
。例如,您可能会将某些值作为字符串获取,并且处理程序正文可以lexical_cast
将其作为mtype
。