字符串文字到类型匹配器中的短路调度和停止条件

Short circuit dispatching and stop condition in a string-literal to type matcher

我正在玩一些代码,取自 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 正文中未注释的部分。所以我猜这是表达式本身无法正常工作.

不幸的是,我不知道此时到底发生了什么,而且我对 Meta-Programming/Compile-time 评估没有经验。

可能使用此代码会出现另一个问题: 我的用例将在 XML 属性 reader 中,其中我有 type/value 标签,例如<attribute type="double" value="2.5"/>,应用类似于 handle 函数的东西从 type 属性值中获取 typename。我可以用来进一步处理该值的类型。

为此,我在 handle f()-body 中添加了 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)’

我想这是编译时和 运行 时行为的混合,它不能以这种方式工作,这让我很好奇如何进一步 process/use 匹配的类型。

感谢您花时间解释,非常感谢您的帮助!

||,当然是short-circuits。你的版本没有。我不认为 short-circuiting 对这里的正确性至关重要,但如果你愿意,可以通过额外的 bool:

轻松实现
bool found = false;
expand_type{ 0, (!found &&
                 same(input, typename Ts::name{}) && 
                 (f(Ts{}), found = true), 0)... };

第二个问题是因为您的处理程序函数必须对类型列表中的每个可能类型有效调用,但您不能 push_back 2.2std::vector<std::string>。例如,您可能有一些东西以字符串形式获取值,并且处理程序主体可以 lexical_cast 将其 mtype.