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