2017-03-24 2 views
0

En C++ 17, j'implémente un vm jouet. J'utilise un std :: variant() comme les éléments de la pile de VM. Je dois pousser des chaînes sur la pile qui représentent différents types d'opérandes, à savoir:std :: variant stocke plusieurs types de chaînes et les différencie

  • un nom de variable
  • un identifiant
  • une chaîne entre guillemets

Tous les 3 types d'opérandes peuvent être de type std :: string_view. La variante est définie comme ceci:

std::variant<bool, int, double, std::string_view>; 

Afin de discriminer le type de vue chaîne réelle, est la bonne façon de faire quelque chose comme ça?

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>; 

Ou est-il possible d'encoder le type de chaîne directement sur le variant. L'un des avantages de le faire au niveau de la variante est que le type peut être obtenu en appelant std :: variant :: index(). Sinon, je dois vérifier .index() == 3, std :: get < 3> (var), puis inspecter le type pour voir si sa chaîne, symbole, ou var.

Répondre

0

Je viens d'essayer, et je l'aime:

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>; 
0

Une alternative consiste à créer une classe à "tag templated" les différents types ENUM. Cela évite d'avoir besoin d'une instruction "using" pour chaque valeur d'énumération afin que vous vous répétiez moins.

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>>; 

En outre, vous avez mentionné en utilisant std::variant::index() pour obtenir le type contenu dans la variante. Une autre façon de le faire est d'utiliser std::visit() de la façon suivante pour être un peu plus sûr du type:

#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; 
} 

Ce programme affiche:

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

Comment cela?

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>;