std :: shared_ptr в Bison, вызывающий ошибку члена - PullRequest
1 голос
/ 08 мая 2020

Я пытаюсь сделать bison более эффективным с точки зрения памяти, используя std::shared_ptr. Я не хочу использовать необработанные указатели. Я использую систему узлов в качестве дерева синтаксического анализа, поэтому я определяю YYTYPE как std::shared_ptr<Node>. После запуска с простой грамматикой я получаю ошибку компиляции:

C2039 'blockNode': не является членом 'std :: shared_ptr'

Я считаю это странно, поскольку эквивалентный код, запущенный на C ++, отлично работает

std::shared_ptr<Node> test = std::make_shared<BlockNode>();

Что мне не хватает?

Требуется

%code requires {
    typedef void* yyscan_t;
    #include "node.h"
    #include <memory>
    #define YYSTYPE std::shared_ptr<Node>
}

Union

%union {
    std::shared_ptr<BlockNode> blockNode;
    std::shared_ptr<TestNode> testNode;
}

%type <blockNode> input
%type <testNode> expr

Грамматика

%start program

%%

program : input { *result = $1; } // <- error here
        ;

input: '\n'      { $$ = std::make_shared<BlockNode>();} // <- error here
     ;

%%

Node.h

class Node /*: public std::enable_shared_from_this<Node>*/ {
public:
    std::string myString;
    Node() {}
    ~Node() { std::cout << "destoryed" << myString << std::endl; }
};

class BlockNode : public  Node {
public:
    BlockNode() {
        myString = "block node";
        std::cout << "created" << myString << std::endl;
    }

};

Ответы [ 2 ]

3 голосов
/ 08 мая 2020

Первое, что вы должны знать, это то, что этот дизайн не может работать. Если вы используете API-интерфейс C по умолчанию для bison, вы не можете использовать тип semanti c, который нельзя тривиально копировать, потому что bison будет побайтно копировать свой стек, если ему нужно перераспределить его (и я считаю, что есть другие проблемы, связанные с делать перезапись байтов без вызова деструкторов). Если вы хотите использовать общие указатели, вам следует понять, как использовать C ++ API, который, как мне кажется, достигает некоторой степени зрелости (хотя я мало использовал его). Вы, вероятно, будете намного более довольны результатом.

Независимо от этого, есть некоторые другие проблемы с вашим кодом.

Во-первых, современные приложения bison не должны #define YYSTYPE, даже внутри блок %code requires. Вместо этого вы должны использовать

 %define api.value.type { /* SemanticType */ }

Если бы вы это сделали, bison смог бы сказать вам, что вы не можете использовать одновременно объявление %union и фиксированный api.value.type. Если тип semanti c является объединением, это объединение. Это также не может быть shared_pointer. Поскольку вы, кажется, хотите, чтобы это было объединение, члены которого являются общими указателями, тогда это объединение, и вы не хотите определять его иначе.

Если вы действительно используете #define YYSTYPE, а также %union, тогда вы обнаружите, что %union никогда не применяется. %union вставляет определение по умолчанию YYSTYPE (как union YYSTYPE), но ваше явное определение YYSTYPE отменяет это. Но bison не знает, что вы это сделали - это не станет очевидным, пока компилятор C не скомпилирует сгенерированный код - поэтому он переписывает ссылки на значения semanti c, используя теги, которые вы указали в %type декларации. Другими словами, когда вы говорите %type <blockNode> input, bison автоматически изменит любую ссылку на $n, где это относится к экземпляру input non-terminal, добавив ссылку на поле, как если бы вы написали $n.blockNode (который, из Конечно, вы не должны этого делать, потому что bison уже добавил ссылку на поле). Но #define -определяемый YYSTYPE не является объединением, это shared_pointer<Node>, а shared_pointer<Node> не имеет члена blockNode, как указано в сообщении об ошибке компилятора C ++.

Аналогично, в правилах для input объявление %type заставляет bison генерировать код, который будет назначен (несуществующему) члену blockNode.

В качестве иллюстрации моего первого пункта - что вы не может использовать shared_pointer как тип семанти c или член объединения с генератором кода C - я "исправил" ваш код, применив предложенное выше (то есть удалил #define YYSTYPE и сделал больше или меньший минимальный набор изменений, чтобы избежать других ошибок bison и компилятора, в результате получился следующий уменьшенный воспроизводимый пример :

Файл tom.yy

%code requires {
    #include "tom_node.h"
    #include <memory>
}

%code {
    std::shared_ptr<Node> result;
    void yyerror(const char* msg) {
      std::cerr << msg << '\n';
    }
    int yylex();
}

%union {
    std::shared_ptr<BlockNode> blockNode;
    std::shared_ptr<Node> testNode;
}

%type <blockNode> input

%%

program : input  { *result = *$1; /* ?? I don't know the actual intent */ }

input: '\n'      { $$ = std::make_shared<BlockNode>();}

Файл tom_node.h

#ifndef TOM_NODE_H
#define TOM_NODE_H

#include <iostream>
#include <string>

class Node /*: public std::enable_shared_from_this<Node>*/ {
public:
    std::string myString;
    Node() {}
    ~Node() { std::cout << "destroyed" << myString << std::endl; }
};

class BlockNode : public  Node {
public:
    BlockNode() {
        myString = "block node";
        std::cout << "created" << myString << std::endl;
    }

};
#endif

Результатом является последовательность похожих ошибок, все из которых связаны с тем, что std::shared_pointer не является тривиальным типом. Вот несколько первых:

$ bison -o tom.cc tom.yy
$ gcc -Wall -o tom tom.cc -ly
tom.cc:956:9: error: use of deleted function ‘YYSTYPE::YYSTYPE()’
 YYSTYPE yylval;
         ^~~~~~
tom.cc:104:7: note: ‘YYSTYPE::YYSTYPE()’ is implicitly deleted because the default definition would be ill-formed:
 union YYSTYPE
       ^~~~~~~
tom.yy:15:32: error: union member ‘YYSTYPE::blockNode’ with non-trivial ‘constexpr std::shared_ptr<_Tp>::shared_ptr() [with _Tp = BlockNode]’
     std::shared_ptr<BlockNode> blockNode;
                                ^~~~~~~~~
tom.yy:16:27: error: union member ‘YYSTYPE::testNode’ with non-trivial ‘constexpr std::shared_ptr<_Tp>::shared_ptr() [with _Tp = Node]’
     std::shared_ptr<Node> testNode;
                           ^~~~~~~~
tom.cc: In function ‘int yyparse()’:
tom.cc:985:30: error: use of deleted function ‘YYSTYPE::YYSTYPE()’
     YYSTYPE yyvsa[YYINITDEPTH];
                              ^
0 голосов
/ 13 августа 2020

это ограничение объединения в C ++, член объединения не может иметь конструктора.

union {
    std::shared_ptr<BlockNode> blockNode; //not allowed
    std::shared_ptr<Node> *testNode;      //allowed
}

поэтому , в вашем случае использовать shared_ptr не обязательно, просто

union {
    Node *testNode;      
}
...