2017-03-24 18 views
0

C++ 17では、私はおもちゃVMを実装しています。私はVMのスタックの要素としてstd :: variant()を使用しています。私はつまり、オペランドの種類を表すスタック上に文字列をプッシュする必要があります。std :: variant複数の文字列型を格納して区別する

  • 変数名
  • 識別子
  • 引用符で囲まれた文字列

オペランドのすべての3種類が可能タイプstd :: string_viewの。バリアントはそうのように定義されています。

std::variant<bool, int, double, 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>; 

または、バリアントで文字列kindを正しくエンコードすることは可能ですか?バリアントレベルでそれを行う利点の1つは、std :: variant :: index()を呼び出すことによって型を保持できることです。それ以外の場合は、.index()== 3、std :: get < 3>(var)をチェックしてから、その文字列、シンボル、またはvarがあるかどうかを調べる必要があります。

答えて

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

1つの選択肢は、テンプレートクラスの「タグ」、様々な列挙型を作成することです。これにより、各enum値に "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()を使用して、バリアントに含まれる型を取得すると説明しました。

#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) 
0

どのようにこれを行うための別の方法は、もう少しタイプセーフであるとは、次のようにstd::visit()を使用することですか?

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>; 
関連する問題