openMSX
TclObject.hh
Go to the documentation of this file.
1#ifndef TCLOBJECT_HH
2#define TCLOBJECT_HH
3
4#include "narrow.hh"
5#include "small_buffer.hh"
6#include "stl.hh"
7#include "view.hh"
8#include "xxhash.hh"
9#include "zstring_view.hh"
10
11#include <tcl.h>
12
13#include <algorithm>
14#include <cassert>
15#include <cstdint>
16#include <initializer_list>
17#include <iterator>
18#include <optional>
19#include <span>
20#include <string_view>
21
22struct Tcl_Obj;
23
24namespace openmsx {
25
26class Interpreter;
27
29{
30 // For STL interface, see below
31 struct iterator {
32 using value_type = zstring_view;
33 using reference = zstring_view;
34 using pointer = zstring_view*;
35 using difference_type = ptrdiff_t;
36 using iterator_category = std::bidirectional_iterator_tag;
37
38 iterator() : obj(nullptr), i(0) {}
39 iterator(const TclObject& obj_, unsigned i_)
40 : obj(&obj_), i(i_) {}
41
42 [[nodiscard]] bool operator==(const iterator& other) const {
43 assert(obj == other.obj);
44 return i == other.i;
45 }
46
47 [[nodiscard]] zstring_view operator*() const {
48 return obj->getListIndexUnchecked(i).getString();
49 }
50
51 iterator& operator++() {
52 ++i;
53 return *this;
54 }
55 iterator operator++(int) {
56 iterator result = *this;
57 ++i;
58 return result;
59 }
60 iterator& operator--() {
61 --i;
62 return *this;
63 }
64 iterator operator--(int) {
65 iterator result = *this;
66 --i;
67 return result;
68 }
69 private:
70 const TclObject* obj;
71 unsigned i;
72 };
73 static_assert(std::bidirectional_iterator<iterator>);
74
75public:
76 TclObject() { init(Tcl_NewObj()); }
77 explicit TclObject(Tcl_Obj* o) { init(o); }
78 template<typename T> explicit TclObject(T t) { init(newObj(t)); }
79 TclObject(const TclObject& o) { init(newObj(o)); }
80 TclObject( TclObject&& o) noexcept { init(newObj(o)); }
81
82 struct MakeListTag {};
83 template<typename... Args>
84 TclObject(MakeListTag, Args&&... args) {
85 init(newList({newObj(std::forward<Args>(args))...}));
86 }
87
88 struct MakeDictTag {};
89 template<typename... Args>
90 TclObject(MakeDictTag, Args&&... args) {
91 init(Tcl_NewDictObj());
92 addDictKeyValues(std::forward<Args>(args)...);
93 }
94
95 ~TclObject() { Tcl_DecrRefCount(obj); }
96
97 // assignment operators
98 TclObject& operator=(const TclObject& other) {
99 if (&other != this) {
100 Tcl_DecrRefCount(obj);
101 init(other.obj);
102 }
103 return *this;
104 }
106 if (&other != this) {
107 Tcl_DecrRefCount(obj);
108 init(other.obj);
109 }
110 return *this;
111 }
112 TclObject& operator=(TclObject&& other) noexcept {
113 std::swap(obj, other.obj);
114 return *this;
115 }
116 template<typename T>
118 if (Tcl_IsShared(obj)) {
119 Tcl_DecrRefCount(obj);
120 obj = newObj(std::forward<T>(t));
121 Tcl_IncrRefCount(obj);
122 } else {
123 assign(std::forward<T>(t));
124 }
125 return *this;
126 }
127
128 // get underlying Tcl_Obj
129 [[nodiscard]] Tcl_Obj* getTclObject() { return obj; }
130 [[nodiscard]] Tcl_Obj* getTclObjectNonConst() const { return obj; }
131
132 // add elements to a Tcl list
133 template<typename T> void addListElement(const T& t) { addListElement(newObj(t)); }
134 template<typename ITER> void addListElements(ITER first, ITER last) {
135 addListElementsImpl(first, last,
136 typename std::iterator_traits<ITER>::iterator_category());
137 }
138 template<typename Range> void addListElements(Range&& range) {
139 addListElements(std::begin(range), std::end(range));
140 }
141 template<typename... Args> void addListElement(Args&&... args) {
142 addListElementsImpl({newObj(std::forward<Args>(args))...});
143 }
144
145 // add key-value pair(s) to a Tcl dict
146 template<typename Key, typename Value>
147 void addDictKeyValue(const Key& key, const Value& value) {
148 addDictKeyValues({newObj(key), newObj(value)});
149 }
150 template<typename... Args> void addDictKeyValues(Args&&... args) {
151 addDictKeyValues({newObj(std::forward<Args>(args))...});
152 }
153
154 // value getters
155 [[nodiscard]] zstring_view getString() const;
156 [[nodiscard]] int getInt (Interpreter& interp) const;
157 [[nodiscard]] bool getBoolean (Interpreter& interp) const;
158 [[nodiscard]] float getFloat (Interpreter& interp) const;
159 [[nodiscard]] double getDouble(Interpreter& interp) const;
160 [[nodiscard]] std::span<const uint8_t> getBinary() const;
161 [[nodiscard]] unsigned getListLength(Interpreter& interp) const;
162 [[nodiscard]] TclObject getListIndex(Interpreter& interp, unsigned index) const;
163 [[nodiscard]] TclObject getListIndexUnchecked(unsigned index) const;
164 void removeListIndex(Interpreter& interp, unsigned index);
165 void setDictValue(Interpreter& interp, const TclObject& key, const TclObject& value);
166 [[nodiscard]] TclObject getDictValue(Interpreter& interp, const TclObject& key) const;
167 template<typename Key>
168 [[nodiscard]] TclObject getDictValue(Interpreter& interp, const Key& key) const {
169 return getDictValue(interp, TclObject(key));
170 }
171 [[nodiscard]] std::optional<TclObject> getOptionalDictValue(const TclObject& key) const;
172 [[nodiscard]] std::optional<int> getOptionalInt() const;
173 [[nodiscard]] std::optional<bool> getOptionalBool() const;
174 [[nodiscard]] std::optional<double> getOptionalDouble() const;
175 [[nodiscard]] std::optional<float> getOptionalFloat() const;
176
177 // STL-like interface when interpreting this TclObject as a list of
178 // strings. Invalid Tcl lists are silently interpreted as empty lists.
179 [[nodiscard]] unsigned size() const { return getListLengthUnchecked(); }
180 [[nodiscard]] bool empty() const { return size() == 0; }
181 [[nodiscard]] iterator begin() const { return {*this, 0}; }
182 [[nodiscard]] iterator end() const { return {*this, size()}; }
183
184 // expressions
185 [[nodiscard]] bool evalBool(Interpreter& interp) const;
186 [[nodiscard]] TclObject eval(Interpreter& interp) const;
187
195 TclObject executeCommand(Interpreter& interp, bool compile = false);
196
197 [[nodiscard]] friend bool operator==(const TclObject& x, const TclObject& y) {
198 return x.getString() == y.getString();
199 }
200 [[nodiscard]] friend bool operator==(const TclObject& x, std::string_view y) {
201 return x.getString() == y;
202 }
203
204private:
205 void init(Tcl_Obj* obj_) noexcept {
206 obj = obj_;
207 Tcl_IncrRefCount(obj);
208 }
209
210 [[nodiscard]] static Tcl_Obj* newObj(std::string_view s) {
211 return Tcl_NewStringObj(s.data(), int(s.size()));
212 }
213 [[nodiscard]] static Tcl_Obj* newObj(const char* s) {
214 return Tcl_NewStringObj(s, int(strlen(s)));
215 }
216 [[nodiscard]] static Tcl_Obj* newObj(bool b) {
217 return Tcl_NewBooleanObj(b);
218 }
219 [[nodiscard]] static Tcl_Obj* newObj(int i) {
220 return Tcl_NewIntObj(i);
221 }
222 [[nodiscard]] static Tcl_Obj* newObj(unsigned u) {
223 return Tcl_NewIntObj(narrow_cast<int>(u));
224 }
225 [[nodiscard]] static Tcl_Obj* newObj(float f) {
226 return Tcl_NewDoubleObj(double(f));
227 }
228 [[nodiscard]] static Tcl_Obj* newObj(double d) {
229 return Tcl_NewDoubleObj(d);
230 }
231 [[nodiscard]] static Tcl_Obj* newObj(std::span<const uint8_t> buf) {
232 return Tcl_NewByteArrayObj(buf.data(), int(buf.size()));
233 }
234 [[nodiscard]] static Tcl_Obj* newObj(const TclObject& o) {
235 return o.obj;
236 }
237 [[nodiscard]] static Tcl_Obj* newList(std::initializer_list<Tcl_Obj*> l) {
238 return Tcl_NewListObj(int(l.size()), l.begin());
239 }
240
241 void assign(std::string_view s) {
242 Tcl_SetStringObj(obj, s.data(), int(s.size()));
243 }
244 void assign(const char* s) {
245 Tcl_SetStringObj(obj, s, int(strlen(s)));
246 }
247 void assign(bool b) {
248 Tcl_SetBooleanObj(obj, b);
249 }
250 void assign(int i) {
251 Tcl_SetIntObj(obj, i);
252 }
253 void assign(unsigned u) {
254 Tcl_SetIntObj(obj, narrow_cast<int>(u));
255 }
256 void assign(float f) {
257 Tcl_SetDoubleObj(obj, double(f));
258 }
259 void assign(double d) {
260 Tcl_SetDoubleObj(obj, d);
261 }
262 void assign(std::span<const uint8_t> b) {
263 Tcl_SetByteArrayObj(obj, b.data(), int(b.size()));
264 }
265
266 template<typename ITER>
267 void addListElementsImpl(ITER first, ITER last, std::input_iterator_tag) {
268 for (ITER it = first; it != last; ++it) {
269 addListElement(*it);
270 }
271 }
272 template<typename ITER>
273 void addListElementsImpl(ITER first, ITER last, std::random_access_iterator_tag) {
275 [](const auto& t) { return newObj(t); }));
276 addListElementsImpl(narrow<int>(objv.size()), objv.data());
277 }
278
279 void addListElement(Tcl_Obj* element);
280 void addListElementsImpl(int objc, Tcl_Obj* const* objv);
281 void addListElementsImpl(std::initializer_list<Tcl_Obj*> l);
282 void addDictKeyValues(std::initializer_list<Tcl_Obj*> keyValuePairs);
283 [[nodiscard]] unsigned getListLengthUnchecked() const;
284
285private:
286 Tcl_Obj* obj;
287};
288
289// We want to be able to reinterpret_cast a Tcl_Obj* as a TclObject.
290static_assert(sizeof(TclObject) == sizeof(Tcl_Obj*));
291
292template<typename... Args>
293[[nodiscard]] TclObject makeTclList(Args&&... args)
294{
295 return TclObject(TclObject::MakeListTag{}, std::forward<Args>(args)...);
296}
297
298template<typename... Args>
299[[nodiscard]] TclObject makeTclDict(Args&&... args)
300{
301 return TclObject(TclObject::MakeDictTag{}, std::forward<Args>(args)...);
302}
303
305 [[nodiscard]] uint32_t operator()(std::string_view str) const {
306 return xxhash(str);
307 }
308 [[nodiscard]] uint32_t operator()(const TclObject& obj) const {
309 return xxhash(obj.getString());
310 }
311};
312
313} // namespace openmsx
314
315#endif
TclObject t
bool getBoolean(Interpreter &interp) const
Definition TclObject.cc:88
TclObject executeCommand(Interpreter &interp, bool compile=false)
Interpret this TclObject as a command and execute it.
Definition TclObject.cc:248
TclObject & operator=(const TclObject &other)
Definition TclObject.hh:98
iterator begin() const
Definition TclObject.hh:181
unsigned getListLength(Interpreter &interp) const
Definition TclObject.cc:155
friend bool operator==(const TclObject &x, const TclObject &y)
Definition TclObject.hh:197
TclObject getListIndexUnchecked(unsigned index) const
Definition TclObject.cc:182
TclObject getListIndex(Interpreter &interp, unsigned index) const
Definition TclObject.cc:173
bool evalBool(Interpreter &interp) const
Definition TclObject.cc:228
bool empty() const
Definition TclObject.hh:180
std::optional< TclObject > getOptionalDictValue(const TclObject &key) const
Definition TclObject.cc:219
void removeListIndex(Interpreter &interp, unsigned index)
Definition TclObject.cc:191
TclObject(const TclObject &o)
Definition TclObject.hh:79
double getDouble(Interpreter &interp) const
Definition TclObject.cc:122
TclObject(TclObject &&o) noexcept
Definition TclObject.hh:80
void addListElement(Args &&... args)
Definition TclObject.hh:141
float getFloat(Interpreter &interp) const
Definition TclObject.cc:107
TclObject & operator=(TclObject &other)
Definition TclObject.hh:105
TclObject eval(Interpreter &interp) const
Definition TclObject.cc:238
void addListElements(Range &&range)
Definition TclObject.hh:138
TclObject(MakeListTag, Args &&... args)
Definition TclObject.hh:84
void addListElement(const T &t)
Definition TclObject.hh:133
std::span< const uint8_t > getBinary() const
Definition TclObject.cc:148
std::optional< float > getOptionalFloat() const
Definition TclObject.cc:114
void addListElements(ITER first, ITER last)
Definition TclObject.hh:134
TclObject getDictValue(Interpreter &interp, const Key &key) const
Definition TclObject.hh:168
Tcl_Obj * getTclObjectNonConst() const
Definition TclObject.hh:130
int getInt(Interpreter &interp) const
Definition TclObject.cc:69
TclObject & operator=(T &&t)
Definition TclObject.hh:117
TclObject(Tcl_Obj *o)
Definition TclObject.hh:77
TclObject(MakeDictTag, Args &&... args)
Definition TclObject.hh:90
friend bool operator==(const TclObject &x, std::string_view y)
Definition TclObject.hh:200
void addDictKeyValue(const Key &key, const Value &value)
Definition TclObject.hh:147
void addDictKeyValues(Args &&... args)
Definition TclObject.hh:150
std::optional< bool > getOptionalBool() const
Definition TclObject.cc:98
TclObject getDictValue(Interpreter &interp, const TclObject &key) const
Definition TclObject.cc:209
unsigned size() const
Definition TclObject.hh:179
std::optional< int > getOptionalInt() const
Definition TclObject.cc:79
zstring_view getString() const
Definition TclObject.cc:141
iterator end() const
Definition TclObject.hh:182
Tcl_Obj * getTclObject()
Definition TclObject.hh:129
TclObject & operator=(TclObject &&other) noexcept
Definition TclObject.hh:112
std::optional< double > getOptionalDouble() const
Definition TclObject.cc:132
void setDictValue(Interpreter &interp, const TclObject &key, const TclObject &value)
Definition TclObject.cc:200
Like std::string_view, but with the extra guarantee that it refers to a zero-terminated string.
This file implemented 3 utility functions:
Definition Autofire.cc:11
TclObject makeTclList(Args &&... args)
Definition TclObject.hh:293
TclObject makeTclDict(Args &&... args)
Definition TclObject.hh:299
constexpr auto transform(Range &&range, UnaryOp op)
Definition view.hh:520
uint32_t operator()(std::string_view str) const
Definition TclObject.hh:305
uint32_t operator()(const TclObject &obj) const
Definition TclObject.hh:308
uint32_t xxhash(std::string_view key)
Definition xxhash.hh:139