Salut !
Je suis en train d’essayer de coder un interpréteur Lisp en C++ (C++14). J’ai créé une classe template pour stocker un arbre binaire, sauf que ça segfault un peu n’importe comment …
Voici mon main :
1 2 3 4 5 6 7 8 9 10 | int main() { tree::ast t1{tree::ast{"lorem"}, tree::ast{"ipsum"}}; tree::ast t2{{"+"}, {{"a"}, {{{"-"}, {{"1"}, {{"2"}, {"nil"}}}}, {"nil"}}}}; std::cout << t1 << std::endl << t2; std::cout << tree::ast{tree::ast{"lorem"}, tree::ast{"ipsum"}}; return 0; } |
Mon problème est que quand j’utilise des variables temporaires et que je les affiche (l.6) tout fonctionne alors que quand je passe directement le constructeur à l’affichage (l.8) ça segfault. Je n’arrive pas à poster la sortie ici dans une balise secret sans que ça fasse une bouillie de charactères alors tout est sur github :
La sortie quand je lance mon programme : https://github.com/Joh11/lisp-interpreter-v2/blob/master/log.txt
Et ma classe binary_tree
(ast
n’est qu’un typedef pour binary_tree<std::string>
) : https://github.com/Joh11/lisp-interpreter-v2/blob/master/tree.hpp
En gros ma classe se présente sous cette forme :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 | namespace tree { template <typename T> class binary_tree { public: // The enum to determine the type of the node enum class node_type {LEAF, BRANCH}; // The two constructors to build a leaf and a branch binary_tree(T const& value = T()) : _value{value}, _nodeType{node_type::LEAF} {} binary_tree(binary_tree const& left, binary_tree const& right) : _children{new binary_tree{left}, new |binary_tree{right}}, _nodeType{node_type::BRANCH} {} // Copy constructor (1/5) binary_tree(binary_tree const& other) : _nodeType{other._nodeType} { // We have to deep copy (recursively) if(_nodeType == node_type::LEAF) _value = other._value; else { _children.left = new binary_tree{*other._children.left}; _children.right = new binary_tree{*other._children.right}; } } // Copy assignment (2/5) // We cannot use copy and swap for this one because of ambiguity binary_tree & operator=(binary_tree const& other) { if(&other == this) return *this; auto copy{other}; _nodeType = copy._nodeType; if(copy._nodeType == node_type::LEAF) std::swap(_value, copy._value); else { std::swap(_children.left, copy._children.left); std::swap(_children.right, copy._children.right); } return *this; } // Destructor (3/5) // We have to free the subtrees if it's a branch ~binary_tree() { if(_nodeType == node_type::BRANCH) { delete _children.left; delete _children.right; } } // Move constructor (4/5) binary_tree(binary_tree && other) : _nodeType{std::move(other._nodeType)} { if(_nodeType == node_type::LEAF) _value = std::move(other._value); else { _children.left = other._children.left; _children.right = other._children.right; // Set the other's children to nullptr to not delete our new children // We can do this because "delete nullptr;" is legit it seems other._children.left = nullptr; other._children.right = nullptr; } } // Move assignment operator (5/5) binary_tree & operator=(binary_tree && other) { _nodeType = std::move(other._nodeType); if(_nodeType == node_type::LEAF) _value = std::move(other._value); else { _children.left = other._children.left; _children.right = other._children.right; // Set the other's children to nullptr to not delete our new children // We can do this because "delete nullptr;" is legit it seems other._children.left = nullptr; other._children.right = nullptr; } return *this; } std::ostream & display(std::ostream & o, bool first = true) const { if(_nodeType == node_type::LEAF) return o << _value; if(_children.right->is_leaf() && _children.right->value() != "nil") { o << "("; _children.left->display(o); o << " . "; _children.right->display(o); return o << ")"; } if(first) { o << "("; _children.left->display(o); if(_children.right->is_leaf() && _children.right->value() == "nil") { return o << ")"; } o << " "; return _children.right->display(o, false); } else { if(_children.right->is_leaf() && _children.right->value() == "nil") { _children.left->display(o); return o << ")"; } else { _children.left->display(o); o << " "; return _children.right->display(o, false); } } } private: union { // The leaf part T _value; // The branch part struct { binary_tree *left; binary_tree *right; } _children; }; node_type _nodeType; }; // A function to display a binary tree // The template type has to be <<able template <typename T> std::ostream & operator<<(std::ostream & o, binary_tree<T> const& t) { return t.display(o); } // Aliases and type stuff using ast = binary_tree<std::string>; // Return (nil . nil) inline ast empty_branch() { return {{"nil"}, {"nil"}}; } } |
Je soupçonne que c’est quelque chose avec le constructeur de copie, le constructeur de déplacement vu que c’est quand je n’utilise pas de variables temporaires que ça coince. J’ai essayé de ne pas définir le constructeur de déplacement et l’opérateur d’affectation par déplacement mais ça ne marche toujours pas…
Je suis sous Linux Mint avec GCC 5.4, avec comme options -g -Wall si ça change quelque chose.
Merci d’avance !