std :: variant存储多个字符串类型并将它们区分开来

时间:2017-03-24 00:54:38

标签: c++ std c++17 variant

在C ++ 17中,我实现了玩具vm。我使用std :: variant()作为VM堆栈的元素。我需要将字符串压入堆栈,代表不同类型的操作数,即:

  • 变量名称
  • 标识符
  • 引用字符串

所有3种类型的操作数都可以是std :: string_view类型。变体的定义如下:

enum StringKind { Symbol, String, Var };
using Stringy = std::tuple<StringKind, std::string_view>; // SV can be symbol, var, string
std::variant<bool, int, double, Stringy>;

为了区分字符串视图的实际类型,是否有正确的方法来做这样的事情?

app.post('/login', function(req, res, next){
    passport.authenticate('local', (err, user, info),{
        if(err) {
            return res.send({err: err, info:info});
        }
        res.send(user);
    })(req, res, next);
}); 

或者可以在变体上编码字符串种类。在变体级别执行此操作的一个好处是可以通过调用std :: variant :: index()来获得类型。否则,我必须检查.index()== 3,std :: get&lt; 3&gt;(var),然后检查类型以查看其字符串,符号或变量。

3 个答案:

答案 0 :(得分:0)

我刚试过这个,我喜欢它:

enum StackStringType {};
enum StackSymbolType {}
enum StackVarType {};

using StackString = std::tuple<StackStringType, std::string_view>;
using StackSymbol = std::tuple<StackSymbolType, std::string_view>;
using StackVar    = std::tuple<StackVarType, std::string_view>;

using StackType = std::variant<bool, StackString, StackSymbol, StackVar>;

答案 1 :(得分:0)

另一种方法是创建一个模板化的类来“标记”各种枚举类型。这样就不需要为每个枚举值设置一个“using”语句,这样你就可以重复一遍了。

enum class StringKind { Symbol, String, Var };
template <StringKind T> struct TaggedString { std::string_view value; };
using StackType = std::variant<bool,
                               int,
                               double,
                               TaggedString<StringKind::Symbol>,
                               TaggedString<StringKind::String>,
                               TaggedString<StringKind::Var>>;

另外,您提到使用std::variant::index()来获取变体中包含的类型。另一种方法是使用std::visit()以下列方式更安全:

#include <iostream>
#include <string_view>
#include <variant>

template<typename T> struct always_false : std::false_type { };
enum class StringKind { Symbol, String, Var };
template <StringKind T> struct TaggedString { std::string_view value; };
using StackType = std::variant<bool,
                               int,
                               double,
                               TaggedString<StringKind::Symbol>,
                               TaggedString<StringKind::String>,
                               TaggedString<StringKind::Var>>;

std::ostream& operator<<(std::ostream& stream, const StackType& st) {
    std::visit([&stream](auto&& var) {
        using T = std::decay_t<decltype(var)>;
        if constexpr (std::is_same_v<T, bool>) {
            stream << "Bool(" << var << ")";
        } else if constexpr (std::is_same_v<T, int>) {
            stream << "Int(" << var << ")";
        } else if constexpr (std::is_same_v<T, double>) {
            stream << "Double(" << var << ")";
        } else if constexpr (std::is_same_v<T, TaggedString<StringKind::Symbol>>) {
            stream << "Symbol(" << var.value << ")";
        } else if constexpr (std::is_same_v<T, TaggedString<StringKind::String>>) {
            stream << "String(" << var.value << ")";
        } else if constexpr (std::is_same_v<T, TaggedString<StringKind::Var>>) {
            stream << "Var(" << var.value << ")";
        } else {
            static_assert(always_false<T>::value, "non-exhaustive visitor!");
        }
    }, st);
    return stream;
}

int main(int, char**) {
    StackType t_bool   = true;
    StackType t_int    = 3;
    StackType t_double = 3.0;
    StackType t_symbol = TaggedString<StringKind::Symbol>{"Foo"};
    StackType t_string = TaggedString<StringKind::String>{"Bar"};
    StackType t_var    = TaggedString<StringKind::Var>{"Baz"};
    std::cout << t_bool   << std::endl
              << t_int    << std::endl
              << t_double << std::endl
              << t_symbol << std::endl
              << t_string << std::endl
              << t_var    << std::endl;
}

该程序输出:

Bool(1)
Int(3)
Double(3)
Symbol(Foo)
String(Bar)
Var(Baz)

答案 2 :(得分:0)

这个怎么样?

struct StackString : std::string_view {};
struct StackSymbol : std::string_view {};
struct StackVar : std::string_view {};

using StackType = std::variant<bool, int, double, StackString, StackSymbol, StackVar>;