openMSX
TclObject.hh
Go to the documentation of this file.
1#ifndef TCLOBJECT_HH
2#define TCLOBJECT_HH
3
4#include "vla.hh"
5#include "xxhash.hh"
6#include "zstring_view.hh"
7#include <tcl.h>
8#include <algorithm>
9#include <cassert>
10#include <cstdint>
11#include <initializer_list>
12#include <iterator>
13#include <optional>
14#include <span>
15#include <string_view>
16
17struct Tcl_Obj;
18
19namespace openmsx {
20
21class Interpreter;
22
24{
25 // For STL interface, see below
26 struct iterator {
27 using value_type = zstring_view;
28 using reference = zstring_view;
29 using pointer = zstring_view*;
30 using difference_type = ptrdiff_t;
31 using iterator_category = std::bidirectional_iterator_tag;
32
33 iterator(const TclObject& obj_, unsigned i_)
34 : obj(&obj_), i(i_) {}
35
36 [[nodiscard]] bool operator==(const iterator& other) const {
37 assert(obj == other.obj);
38 return i == other.i;
39 }
40
41 [[nodiscard]] zstring_view operator*() const {
42 return obj->getListIndexUnchecked(i).getString();
43 }
44
45 iterator& operator++() {
46 ++i;
47 return *this;
48 }
49 iterator operator++(int) {
50 iterator result = *this;
51 ++result;
52 return result;
53 }
54 iterator& operator--() {
55 --i;
56 return *this;
57 }
58 iterator operator--(int) {
59 iterator result = *this;
60 --result;
61 return result;
62 }
63 private:
64 const TclObject* obj;
65 unsigned i;
66 };
67
68public:
69
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]] double getDouble(Interpreter& interp) const;
153 [[nodiscard]] std::span<const uint8_t> getBinary() const;
154 [[nodiscard]] unsigned getListLength(Interpreter& interp) const;
155 [[nodiscard]] TclObject getListIndex(Interpreter& interp, unsigned index) const;
156 [[nodiscard]] TclObject getDictValue(Interpreter& interp, const TclObject& key) const;
157 template<typename Key>
158 [[nodiscard]] TclObject getDictValue(Interpreter& interp, const Key& key) const {
159 return getDictValue(interp, TclObject(key));
160 }
161 [[nodiscard]] std::optional<int> getOptionalInt() const;
162
163 // STL-like interface when interpreting this TclObject as a list of
164 // strings. Invalid Tcl lists are silently interpreted as empty lists.
165 [[nodiscard]] unsigned size() const { return getListLengthUnchecked(); }
166 [[nodiscard]] bool empty() const { return size() == 0; }
167 [[nodiscard]] iterator begin() const { return iterator(*this, 0); }
168 [[nodiscard]] iterator end() const { return iterator(*this, size()); }
169
170 // expressions
171 [[nodiscard]] bool evalBool(Interpreter& interp) const;
172
180 TclObject executeCommand(Interpreter& interp, bool compile = false);
181
182 [[nodiscard]] friend bool operator==(const TclObject& x, const TclObject& y) {
183 return x.getString() == y.getString();
184 }
185 [[nodiscard]] friend bool operator==(const TclObject& x, std::string_view y) {
186 return x.getString() == y;
187 }
188
189private:
190 void init(Tcl_Obj* obj_) noexcept {
191 obj = obj_;
192 Tcl_IncrRefCount(obj);
193 }
194
195 [[nodiscard]] static Tcl_Obj* newObj(std::string_view s) {
196 return Tcl_NewStringObj(s.data(), int(s.size()));
197 }
198 [[nodiscard]] static Tcl_Obj* newObj(const char* s) {
199 return Tcl_NewStringObj(s, int(strlen(s)));
200 }
201 [[nodiscard]] static Tcl_Obj* newObj(bool b) {
202 return Tcl_NewBooleanObj(b);
203 }
204 [[nodiscard]] static Tcl_Obj* newObj(int i) {
205 return Tcl_NewIntObj(i);
206 }
207 [[nodiscard]] static Tcl_Obj* newObj(unsigned u) {
208 return Tcl_NewIntObj(u);
209 }
210 [[nodiscard]] static Tcl_Obj* newObj(float f) {
211 return Tcl_NewDoubleObj(double(f));
212 }
213 [[nodiscard]] static Tcl_Obj* newObj(double d) {
214 return Tcl_NewDoubleObj(d);
215 }
216 [[nodiscard]] static Tcl_Obj* newObj(std::span<const uint8_t> buf) {
217 return Tcl_NewByteArrayObj(buf.data(), int(buf.size()));
218 }
219 [[nodiscard]] static Tcl_Obj* newObj(const TclObject& o) {
220 return o.obj;
221 }
222 [[nodiscard]] static Tcl_Obj* newList(std::initializer_list<Tcl_Obj*> l) {
223 return Tcl_NewListObj(int(l.size()), l.begin());
224 }
225
226 void assign(std::string_view s) {
227 Tcl_SetStringObj(obj, s.data(), int(s.size()));
228 }
229 void assign(const char* s) {
230 Tcl_SetStringObj(obj, s, int(strlen(s)));
231 }
232 void assign(bool b) {
233 Tcl_SetBooleanObj(obj, b);
234 }
235 void assign(int i) {
236 Tcl_SetIntObj(obj, i);
237 }
238 void assign(unsigned u) {
239 Tcl_SetIntObj(obj, u);
240 }
241 void assign(float f) {
242 Tcl_SetDoubleObj(obj, double(f));
243 }
244 void assign(double d) {
245 Tcl_SetDoubleObj(obj, d);
246 }
247 void assign(std::span<const uint8_t> b) {
248 Tcl_SetByteArrayObj(obj, b.data(), int(b.size()));
249 }
250
251 template<typename ITER>
252 void addListElementsImpl(ITER first, ITER last, std::input_iterator_tag) {
253 for (ITER it = first; it != last; ++it) {
254 addListElement(*it);
255 }
256 }
257 template<typename ITER>
258 void addListElementsImpl(ITER first, ITER last, std::random_access_iterator_tag) {
259 auto objc = last - first;
260 if (objc == 0) return; // because 0-length VLAs are not allowed (but gcc/clang allow it as an extension)
261 VLA(Tcl_Obj*, objv, objc);
262 std::transform(first, last, objv, [](const auto& t) { return newObj(t); });
263 addListElementsImpl(objc, objv);
264 }
265
266 void addListElement(Tcl_Obj* element);
267 void addListElementsImpl(int objc, Tcl_Obj* const* objv);
268 void addListElementsImpl(std::initializer_list<Tcl_Obj*> l);
269 void addDictKeyValues(std::initializer_list<Tcl_Obj*> keyValuePairs);
270 [[nodiscard]] unsigned getListLengthUnchecked() const;
271 [[nodiscard]] TclObject getListIndexUnchecked(unsigned index) const;
272
273private:
274 Tcl_Obj* obj;
275};
276
277// We want to be able to reinterpret_cast a Tcl_Obj* as a TclObject.
278static_assert(sizeof(TclObject) == sizeof(Tcl_Obj*));
279
280template<typename... Args>
281[[nodiscard]] TclObject makeTclList(Args&&... args)
282{
283 return TclObject(TclObject::MakeListTag{}, std::forward<Args>(args)...);
284}
285
286template<typename... Args>
287[[nodiscard]] TclObject makeTclDict(Args&&... args)
288{
289 return TclObject(TclObject::MakeDictTag{}, std::forward<Args>(args)...);
290}
291
293 [[nodiscard]] uint32_t operator()(std::string_view str) const {
294 return xxhash(str);
295 }
296 [[nodiscard]] uint32_t operator()(const TclObject& obj) const {
297 return xxhash(obj.getString());
298 }
299};
300
301} // namespace openmsx
302
303#endif
TclObject t
bool getBoolean(Interpreter &interp) const
Definition: TclObject.cc:91
TclObject executeCommand(Interpreter &interp, bool compile=false)
Interpret this TclObject as a command and execute it.
Definition: TclObject.cc:181
TclObject & operator=(const TclObject &other)
Definition: TclObject.hh:92
iterator begin() const
Definition: TclObject.hh:167
unsigned getListLength(Interpreter &interp) const
Definition: TclObject.cc:125
friend bool operator==(const TclObject &x, const TclObject &y)
Definition: TclObject.hh:182
TclObject getListIndex(Interpreter &interp, unsigned index) const
Definition: TclObject.cc:143
bool evalBool(Interpreter &interp) const
Definition: TclObject.cc:171
bool empty() const
Definition: TclObject.hh:166
TclObject(const TclObject &o)
Definition: TclObject.hh:73
double getDouble(Interpreter &interp) const
Definition: TclObject.cc:101
TclObject(TclObject &&o) noexcept
Definition: TclObject.hh:74
void addListElement(Args &&... args)
Definition: TclObject.hh:135
TclObject & operator=(TclObject &other)
Definition: TclObject.hh:99
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:118
void addListElements(ITER first, ITER last)
Definition: TclObject.hh:128
TclObject getDictValue(Interpreter &interp, const Key &key) const
Definition: TclObject.hh:158
Tcl_Obj * getTclObjectNonConst() const
Definition: TclObject.hh:124
int getInt(Interpreter &interp) const
Definition: TclObject.cc:72
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:185
void addDictKeyValue(const Key &key, const Value &value)
Definition: TclObject.hh:141
void addDictKeyValues(Args &&... args)
Definition: TclObject.hh:144
TclObject getDictValue(Interpreter &interp, const TclObject &key) const
Definition: TclObject.cc:161
unsigned size() const
Definition: TclObject.hh:165
std::optional< int > getOptionalInt() const
Definition: TclObject.cc:82
zstring_view getString() const
Definition: TclObject.cc:111
iterator end() const
Definition: TclObject.hh:168
Tcl_Obj * getTclObject()
Definition: TclObject.hh:123
TclObject & operator=(TclObject &&other) noexcept
Definition: TclObject.hh:106
Like std::string_view, but with the extra guarantee that it refers to a zero-terminated string.
Definition: zstring_view.hh:22
This file implemented 3 utility functions:
Definition: Autofire.cc:9
constexpr KeyMatrixPosition x
Keyboard bindings.
Definition: Keyboard.cc:127
TclObject makeTclList(Args &&... args)
Definition: TclObject.hh:281
TclObject makeTclDict(Args &&... args)
Definition: TclObject.hh:287
auto transform(InputRange &&range, OutputIter out, UnaryOperation op)
Definition: ranges.hh:220
void swap(openmsx::MemBuffer< T > &l, openmsx::MemBuffer< T > &r) noexcept
Definition: MemBuffer.hh:202
uint32_t operator()(std::string_view str) const
Definition: TclObject.hh:293
uint32_t operator()(const TclObject &obj) const
Definition: TclObject.hh:296
constexpr uint128 operator*(const uint128 &a, const uint128 &b)
Definition: uint128.hh:189
#define VLA(TYPE, NAME, LENGTH)
Definition: vla.hh:10
uint32_t xxhash(std::string_view key)
Definition: xxhash.hh:139
constexpr auto begin(const zstring_view &x)
constexpr auto end(const zstring_view &x)