5

私は単純な Lisp に似たプログラミング言語のインタプリタを書いています。コードをノードに処理します。それらにはすべて型があり、そのうちのいくつかはインデックス順に子ノードを持つ場合があります。情報の性質の違いにより、すべてのノード値に同じ長さの型を使用することはできません。それらの型の名前は列挙型ですが、値の型について私が持っている唯一のアイデアはvoid *. でもこれを使うときは、かなり気をつけないといけないなと思います。つまり、デフォルトのデストラクタを使用することはできません。ノード タイプを考慮するデストラクタを作成する必要があります。また、値にアクセスするためにも、多くのキャストを使用する必要があります。

これは私が話していることです:

enum NodeType {/* Some node types */}

class Node
{
public:
    Node(string input_code);
private:
    NodeType type; // Having this I can know the type of value
    void* value;
};

より安全で、より良いコードを作成しながら、void ポインターの使用と同じくらい効率的な方法はありますか?

4

4 に答える 4

6

私が考えることができる2つのオプションがあります。1つは、抽象基本クラスNodeといくつかの型固有のサブクラスを持つポリモーフィズムを使用することです。おそらく次のようなものです:

class Node
{
public:
  virtual ~Node() = 0;
};

class String : public Node
{
public:
  ~String() {}
};

class Float : public Node
{
public:
  ~Float() {}
};

これらのノードを保存するときは、Node*ではなく保存しますvoid*。基本クラスに(抽象)仮想デストラクタが存在すると、次のように、基本クラスポインタを介して具象オブジェクトを適切に破棄できます。

Node* obj = new String;
delete obj;

基本クラスで宣言されたメソッドを呼び出して、それらのメソッドが基本クラスで仮想である場合は、正しい派生クラスでコードを実行させることもできます。これらは、次のような純粋な仮想でもあることがよくあります。

class Node
{
public:
  std::string Speak() const = 0; // pure virt
};

class String : public Node
{
public:
  std::string Speak() const { return "Hello"; }
};

別のオプションは、ある種のバリアントクラスを使用することです。C ++自体には言語に組み込まれたバリアントクラスはありませんが、そのようなクラスを提供するBoostなどの一部のライブラリが作成されています。

于 2012-11-29T14:23:57.850 に答える
2

継承/インターフェースを使用する:

struct BaseNode {
    std::vector<BaseNode*> _children;
    /*Some functions here*/
}

を継承する新しいクラスを作成しtypeます。enum NodeTypeBaseNode

于 2012-11-29T14:17:50.527 に答える
0

以下は、boost::variantベースのノード システムの簡単なスケッチです。

class Node;
struct Empty {};

// Keep enum and variant in sync:
enum NodeType {
  eEmptyNode,
  eStringNode,
  eIntNode,
  eListNode,
};
typedef std::vector< const Node > NodeList;
typedef boost::variant< std::string, int, NodeList > NodeData;

// Keep this in sync with types in Node and enum:
NodeType GetNodeType( Empty const& ) { return eEmptyNode; }
NodeType GetNodeType( std::string const& ) { return eStringNode; }
NodeType GetNodeType( int const& ) { return eIntNode; }
NodeType GetNodeType( NodeList const& ) { return eListNode; }


// Some helper code:
struct GetNodeType_visitor
{
  typedef NodeType return_type;
  template<typename T>
  NodeType operator()( T const& t ) const { return GetNodeType(t); }
};

template<typename T, typename Function>
struct OneType_visitor
{
  typedef bool return_type;
  Function func;
  OneType_visitor( Function const& f ):func(f) {}
  template<typename U>
  bool operator()( U const& u ) const { return false; }
  bool operator()( T const& t ) const { func(t); return true; }
};

struct Node
{
  NodeData data;
  NodeType GetType() { return boost::apply_visitor( GetNodeType_visitor, data ); }
  template<typename T, typename Function>
  bool Apply( Function const& func ) const
  {
    return boost::apply_visitor( OneType_visitor<T>(func), data );
  }
  template<typename T>
  Node( T const& t ):data(t) {}
  Node():data(Empty()) {}
};

// example usage:
int main()
{
  NodeList nodes;
  nodes.push_back( Node<int>( 7 ) );
  nodes.push_back( Node<std::string>( "hello" ) );
  Node root( nodes );

  Assert( root.GetType() == eListNode );
  std::function<void(Node const&)> PrintNode;
  auto PrintInt = [](int const& i) { std::cout << "#" << i; };
  auto PrintString = [](std::string const& s) { std::cout << "\"" << s << "\""; };
  auto PrintList = [&](NodeList const& list) {
    std::cout << "[";
    for (auto it = list.begin(); it !=list.end(); ++it)
    {
      if (it != list.begin()) std::cout << ",";
      PrintNode( *it );
    }
    std::cout << "]";
  }
  auto PrintEmpty = [](Empty const&) { std::cout << "{}"; }
  PrintNode = [&](Node const& n)
  {
    bool bPrinted = false;
    bPrinted = n.Apply<int>( PrintInt ) || bPrinted;
    bPrinted = n.Apply<std::string>( PrintString ) || bPrinted;
    bPrinted = n.Apply<NodeList>( PrintList ) || bPrinted;
    bPrinted = n.Apply<Empty>( PrintEmpty ) || bPrinted;
    Assert(bPrinted);
  }
  PrintNode(root);
}

コードはテストされていませんが、基本的な考え方は保持されます。

これは Lisp に似た言語用であるため、不変ノードを使用していることに注意してください。2 つのツリーがデータを共有できるように、実際には、std::shared_ptr<const Node>またはそのようなものを使用する必要があります。

boost::variant動的型付けの問題を処理します。

于 2012-11-29T15:25:30.787 に答える
0

ある種のバリアント型を使用する必要があります。Boost には 1 つあり、詳細はこちらで確認できます。

于 2012-11-29T14:16:11.907 に答える