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