2017-03-24 1 views
0

In C++ 17, ich bin ein Spielzeug vm implementieren. Ich verwende eine std :: variant() als Elemente des VM-Stacks. Ich brauche Saiten auf den Stapel zu schieben, die verschiedenen Arten von Operanden darstellen, nämlich:std :: Variante speichern mehrere String-Typen und sagen, sie auseinander

  • ein Variablenname
  • eine Kennung
  • eine Zeichenfolge in Anführungszeichen

Alle drei Arten von Operanden sein können vom Typ std :: string_view. Die Variante wird wie so definiert:

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

Um über die tatsächliche Art der String-Ansicht zu unterscheiden, ist der richtige Weg, so etwas zu tun?

Oder ist es möglich, die String-Art direkt bei der Variante zu codieren. Einer der Vorteile, es auf der Variantenebene zu tun, besteht darin, dass der Typ durch Aufrufen von std :: variant :: index() erhalten werden kann. Ansonsten muss ich für .index überprüfen() == 3 std :: get < 3> (var) und dann den Typ überprüfen, ob seine Schnur, Symbol zu sehen, oder var.

Antwort

0

Ich habe gerade versucht dies, und Ich mag es:

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

Eine Alternative ist es, eine Templat-Klasse „Tag“ die verschiedenen Aufzählungstypen zu erstellen. Dies vermeidet die Notwendigkeit, eine "using" -Anweisung für jeden Aufzählungswert zu haben, so dass Sie sich weniger wiederholen.

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

Auch erwähnten Sie std::variant::index() mit der Art in der Variante enthalten ist, zu erhalten. Ein anderer Weg, dies zu tun ist std::visit() in der folgenden Art und Weise zu verwenden, etwas Art sicher zu sein:

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

Dieses Programm Ausgänge:

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

Wie wäre das?

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