OXYGENHUB
◆ main openmsx / src / commands / TclObject.hh
10.0 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
e8555c89 Andy McCall 1
#ifndef TCLOBJECT_HH
2
#define TCLOBJECT_HH
3
4
#include "tcl.hh"
5
6
#include "narrow.hh"
7
#include "small_buffer.hh"
8
#include "xxhash.hh"
9
#include "zstring_view.hh"
10
11
#include <algorithm>
12
#include <cassert>
13
#include <climits>
14
#include <cstdint>
15
#include <initializer_list>
16
#include <iterator>
17
#include <optional>
18
#include <ranges>
19
#include <span>
20
#include <string_view>
21
22
struct Tcl_Obj;
23
24
namespace openmsx {
25
26
class Interpreter;
27
28
class TclObject
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_, size_t 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
		[[nodiscard]] zstring_view operator[](int o) const {
52
			return obj->getListIndexUnchecked(i + o).getString();
53
		}
54
55
		iterator& operator++() {
56
			++i;
57
			return *this;
58
		}
59
		iterator operator++(int) {
60
			iterator result = *this;
61
			++i;
62
			return result;
63
		}
64
		iterator& operator--() {
65
			--i;
66
			return *this;
67
		}
68
		iterator operator--(int) {
69
			iterator result = *this;
70
			--i;
71
			return result;
72
		}
73
	private:
74
		const TclObject* obj;
75
		size_t i;
76
	};
77
	static_assert(std::bidirectional_iterator<iterator>);
78
79
public:
80
	TclObject()                                    { init(Tcl_NewObj()); }
81
	explicit TclObject(Tcl_Obj* o)                 { init(o); }
82
	template<typename T> explicit TclObject(T&& t) { init(newObj(std::forward<T>(t))); }
83
	TclObject(const TclObject&  o)                 { init(newObj(o)); }
84
	TclObject(      TclObject&& o) noexcept        { init(newObj(o)); }
85
86
	struct MakeListTag {};
87
	template<typename... Args>
88
	explicit TclObject(MakeListTag, Args&&... args) {
89
		init(newList({newObj(std::forward<Args>(args))...}));
90
	}
91
92
	struct MakeDictTag {};
93
	template<typename... Args>
94
	explicit TclObject(MakeDictTag, Args&&... args) {
95
		init(Tcl_NewDictObj());
96
		addDictKeyValues(std::forward<Args>(args)...);
97
	}
98
99
	~TclObject() { Tcl_DecrRefCount(obj); }
100
101
	// assignment operators
102
	TclObject& operator=(const TclObject& other) {
103
		if (&other != this) {
104
			Tcl_DecrRefCount(obj);
105
			init(other.obj);
106
		}
107
		return *this;
108
	}
109
	TclObject& operator=(TclObject& other) {
110
		if (&other != this) {
111
			Tcl_DecrRefCount(obj);
112
			init(other.obj);
113
		}
114
		return *this;
115
	}
116
	TclObject& operator=(TclObject&& other) noexcept {
117
		std::swap(obj, other.obj);
118
		return *this;
119
	}
120
	template<typename T>
121
	TclObject& operator=(T&& t) {
122
		if (Tcl_IsShared(obj)) {
123
			Tcl_DecrRefCount(obj);
124
			obj = newObj(std::forward<T>(t));
125
			Tcl_IncrRefCount(obj);
126
		} else {
127
			assign(std::forward<T>(t));
128
		}
129
		return *this;
130
	}
131
132
	// get underlying Tcl_Obj
133
	[[nodiscard]] Tcl_Obj* getTclObject() { return obj; }
134
	[[nodiscard]] Tcl_Obj* getTclObjectNonConst() const { return obj; }
135
136
	// add elements to a Tcl list
137
	template<typename T> void addListElement(const T& t) { addListElement(newObj(t)); }
138
139
	template<std::input_iterator Iterator, std::sentinel_for<Iterator> Sentinel>
140
	void addListElements(Iterator first, Sentinel last) {
141
		addListElementsImpl(first, last);
142
	}
143
	template<std::ranges::input_range Range> void addListElements(Range&& range) {
144
		addListElements(std::ranges::begin(range), std::ranges::end(range));
145
	}
146
	template<typename... Args> void addListElement(Args&&... args) {
147
		addListElementsImpl({newObj(std::forward<Args>(args))...});
148
	}
149
150
	// add key-value pair(s) to a Tcl dict
151
	template<typename Key, typename Value>
152
	void addDictKeyValue(const Key& key, const Value& value) {
153
		addDictKeyValues({newObj(key), newObj(value)});
154
	}
155
	template<typename... Args> void addDictKeyValues(Args&&... args) {
156
		addDictKeyValues({newObj(std::forward<Args>(args))...});
157
	}
158
159
	// value getters
160
	[[nodiscard]] zstring_view getString() const;
161
	[[nodiscard]] int getInt      (Interpreter& interp) const;
162
	[[nodiscard]] int64_t getInt64(Interpreter& interp) const;
163
	[[nodiscard]] bool getBoolean (Interpreter& interp) const;
164
	[[nodiscard]] float  getFloat (Interpreter& interp) const;
165
	[[nodiscard]] double getDouble(Interpreter& interp) const;
166
	[[nodiscard]] std::span<const uint8_t> getBinary(Interpreter& interp) const;
167
	[[nodiscard]] size_t getListLength(Interpreter& interp) const;
168
	[[nodiscard]] TclObject getListIndex(Interpreter& interp, size_t index) const;
169
	[[nodiscard]] TclObject getListIndexUnchecked(size_t index) const;
170
	void removeListIndex(Interpreter& interp, size_t index);
171
	void setDictValue(Interpreter& interp, const TclObject& key, const TclObject& value);
172
	[[nodiscard]] TclObject getDictValue(Interpreter& interp, const TclObject& key) const;
173
	template<typename Key>
174
	[[nodiscard]] TclObject getDictValue(Interpreter& interp, const Key& key) const {
175
		return getDictValue(interp, TclObject(key));
176
	}
177
	[[nodiscard]] std::optional<TclObject> getOptionalDictValue(const TclObject& key) const;
178
	[[nodiscard]] std::optional<int> getOptionalInt() const;
179
	[[nodiscard]] std::optional<int64_t> getOptionalInt64() const;
180
	[[nodiscard]] std::optional<bool> getOptionalBool() const;
181
	[[nodiscard]] std::optional<double> getOptionalDouble() const;
182
	[[nodiscard]] std::optional<float> getOptionalFloat() const;
183
184
	// STL-like interface when interpreting this TclObject as a list of
185
	// strings. Invalid Tcl lists are silently interpreted as empty lists.
186
	[[nodiscard]] size_t size() const { return getListLengthUnchecked(); }
187
	[[nodiscard]] bool empty() const { return size() == 0; }
188
	[[nodiscard]] iterator begin() const { return {*this, 0}; }
189
	[[nodiscard]] iterator end()   const { return {*this, size()}; }
190
191
	// expressions
192
	[[nodiscard]] bool evalBool(Interpreter& interp) const;
193
	[[nodiscard]] TclObject eval(Interpreter& interp) const;
194
195
	/** Interpret this TclObject as a command and execute it.
196
	  * @param interp The Tcl interpreter
197
	  * @param compile Should the command be compiled to bytecode? The
198
	  *           bytecode is stored inside the TclObject can speed up
199
	  *           future invocations of the same command. Only set this
200
	  *           flag when the command will be executed more than once.
201
	  */
202
	TclObject executeCommand(Interpreter& interp, bool compile = false);
203
204
	[[nodiscard]] friend bool operator==(const TclObject& x, const TclObject& y) {
205
		return x.getString() == y.getString();
206
	}
207
	[[nodiscard]] friend bool operator==(const TclObject& x, std::string_view y) {
208
		return x.getString() == y;
209
	}
210
211
private:
212
	void init(Tcl_Obj* obj_) noexcept {
213
		obj = obj_;
214
		Tcl_IncrRefCount(obj);
215
	}
216
217
	[[nodiscard]] static Tcl_Obj* newObj(std::string_view s) {
218
		return Tcl_NewStringObj(s.data(), int(s.size()));
219
	}
220
	[[nodiscard]] static Tcl_Obj* newObj(const char* s) {
221
		return Tcl_NewStringObj(s, int(strlen(s)));
222
	}
223
	[[nodiscard]] static Tcl_Obj* newObj(bool b) {
224
		return Tcl_NewBooleanObj(b);
225
	}
226
	[[nodiscard]] static Tcl_Obj* newObj(int i) {
227
		return Tcl_NewIntObj(i);
228
	}
229
	[[nodiscard]] static Tcl_Obj* newObj(unsigned u) {
230
		return Tcl_NewIntObj(narrow_cast<int>(u));
231
	}
232
	[[nodiscard]] static Tcl_Obj* newObj(int64_t i) {
233
		return Tcl_NewWideIntObj(Tcl_WideInt(i));
234
	}
235
	[[nodiscard]] static Tcl_Obj* newObj(uint64_t u) {
236
		return Tcl_NewWideIntObj(Tcl_WideInt(u));
237
	}
238
	[[nodiscard]] static Tcl_Obj* newObj(float f) {
239
		return Tcl_NewDoubleObj(double(f));
240
	}
241
	[[nodiscard]] static Tcl_Obj* newObj(double d) {
242
		return Tcl_NewDoubleObj(d);
243
	}
244
	[[nodiscard]] static Tcl_Obj* newObj(std::span<const uint8_t> buf) {
245
		return Tcl_NewByteArrayObj(buf.data(), int(buf.size()));
246
	}
247
	[[nodiscard]] static Tcl_Obj* newObj(const TclObject& o) {
248
		return o.obj;
249
	}
250
	[[nodiscard]] static Tcl_Obj* newList(std::initializer_list<Tcl_Obj*> l) {
251
		return Tcl_NewListObj(int(l.size()), l.begin());
252
	}
253
254
	void assign(std::string_view s) {
255
		Tcl_SetStringObj(obj, s.data(), int(s.size()));
256
	}
257
	void assign(const char* s) {
258
		Tcl_SetStringObj(obj, s, int(strlen(s)));
259
	}
260
	void assign(bool b) {
261
		Tcl_SetBooleanObj(obj, b);
262
	}
263
	void assign(int i) {
264
		Tcl_SetIntObj(obj, i);
265
	}
266
	void assign(unsigned u) {
267
		Tcl_SetIntObj(obj, narrow_cast<int>(u));
268
	}
269
	void assign(int64_t i) {
270
		Tcl_SetWideIntObj(obj, Tcl_WideInt(i));
271
	}
272
	void assign(uint64_t u) {
273
		Tcl_SetWideIntObj(obj, Tcl_WideInt(u));
274
	}
275
	void assign(float f) {
276
		Tcl_SetDoubleObj(obj, double(f));
277
	}
278
	void assign(double d) {
279
		Tcl_SetDoubleObj(obj, d);
280
	}
281
	void assign(std::span<const uint8_t> b) {
282
		Tcl_SetByteArrayObj(obj, b.data(), int(b.size()));
283
	}
284
285
	template<std::input_iterator Iterator, std::sentinel_for<Iterator> Sentinel>
286
	void addListElementsImpl(Iterator first, Sentinel last) {
287
		for (auto it = first; it != last; ++it) {
288
			addListElement(*it);
289
		}
290
	}
291
	template<std::random_access_iterator Iterator, std::sentinel_for<Iterator> Sentinel>
292
	void addListElementsImpl(Iterator first, Sentinel last) {
293
		small_buffer<Tcl_Obj*, 128> objv(std::views::transform(std::ranges::subrange(first, last),
294
			[](const auto& t) { return newObj(t); }));
295
		addListElementsImpl(narrow<int>(objv.size()), objv.data());
296
	}
297
298
	void addListElement(Tcl_Obj* element);
299
	void addListElementsImpl(int objc, Tcl_Obj* const* objv);
300
	void addListElementsImpl(std::initializer_list<Tcl_Obj*> l);
301
	void addDictKeyValues(std::initializer_list<Tcl_Obj*> keyValuePairs);
302
	[[nodiscard]] size_t getListLengthUnchecked() const;
303
304
private:
305
	Tcl_Obj* obj;
306
};
307
308
// We want to be able to reinterpret_cast a Tcl_Obj* as a TclObject.
309
static_assert(sizeof(TclObject) == sizeof(Tcl_Obj*));
310
311
template<typename... Args>
312
[[nodiscard]] TclObject makeTclList(Args&&... args)
313
{
314
	return TclObject(TclObject::MakeListTag{}, std::forward<Args>(args)...);
315
}
316
317
template<typename... Args>
318
[[nodiscard]] TclObject makeTclDict(Args&&... args)
319
{
320
	return TclObject(TclObject::MakeDictTag{}, std::forward<Args>(args)...);
321
}
322
323
struct XXTclHasher {
324
	[[nodiscard]] uint32_t operator()(std::string_view str) const {
325
		return xxhash(str);
326
	}
327
	[[nodiscard]] uint32_t operator()(const TclObject& obj) const {
328
		return xxhash(obj.getString());
329
	}
330
};
331
332
} // namespace openmsx
333
334
#endif