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