openMSX
TclObject.hh
Go to the documentation of this file.
1 #ifndef TCLOBJECT_HH
2 #define TCLOBJECT_HH
3 
4 #include "span.hh"
5 #include "vla.hh"
6 #include "xxhash.hh"
7 #include "zstring_view.hh"
8 #include <tcl.h>
9 #include <cassert>
10 #include <cstdint>
11 #include <initializer_list>
12 #include <iterator>
13 #include <optional>
14 #include <string_view>
15 
16 struct Tcl_Obj;
17 
18 namespace openmsx {
19 
20 class Interpreter;
21 
22 class TclObject
23 {
24  // For STL interface, see below
25  struct iterator {
26  using value_type = zstring_view;
27  using reference = zstring_view;
28  using pointer = zstring_view*;
29  using difference_type = ptrdiff_t;
30  using iterator_category = std::bidirectional_iterator_tag;
31 
32  iterator(const TclObject& obj_, unsigned i_)
33  : obj(&obj_), i(i_) {}
34 
35  [[nodiscard]] bool operator==(const iterator& other) const {
36  assert(obj == other.obj);
37  return i == other.i;
38  }
39  [[nodiscard]] bool operator!=(const iterator& other) const {
40  return !(*this == other);
41  }
42 
43  [[nodiscard]] zstring_view operator*() const {
44  return obj->getListIndexUnchecked(i).getString();
45  }
46 
47  iterator& operator++() {
48  ++i;
49  return *this;
50  }
51  iterator operator++(int) {
52  iterator result = *this;
53  ++result;
54  return result;
55  }
56  iterator& operator--() {
57  --i;
58  return *this;
59  }
60  iterator operator--(int) {
61  iterator result = *this;
62  --result;
63  return result;
64  }
65  private:
66  const TclObject* obj;
67  unsigned i;
68  };
69 
70 public:
71 
72  TclObject() { init(Tcl_NewObj()); }
73  explicit TclObject(Tcl_Obj* o) { init(o); }
74  template<typename T> explicit TclObject(T t) { init(newObj(t)); }
75  TclObject(const TclObject& o) { init(newObj(o)); }
76  TclObject( TclObject&& o) noexcept { init(newObj(o)); }
77 
78  struct MakeListTag {};
79  template<typename... Args>
80  TclObject(MakeListTag, Args&&... args) {
81  init(newList({newObj(std::forward<Args>(args))...}));
82  }
83 
84  struct MakeDictTag {};
85  template<typename... Args>
86  TclObject(MakeDictTag, Args&&... args) {
87  init(Tcl_NewDictObj());
88  addDictKeyValues(std::forward<Args>(args)...);
89  }
90 
91  ~TclObject() { Tcl_DecrRefCount(obj); }
92 
93  // assignment operators
94  TclObject& operator=(const TclObject& other) {
95  if (&other != this) {
96  Tcl_DecrRefCount(obj);
97  init(other.obj);
98  }
99  return *this;
100  }
102  if (&other != this) {
103  Tcl_DecrRefCount(obj);
104  init(other.obj);
105  }
106  return *this;
107  }
108  TclObject& operator=(TclObject&& other) noexcept {
109  std::swap(obj, other.obj);
110  return *this;
111  }
112  template<typename T>
114  if (Tcl_IsShared(obj)) {
115  Tcl_DecrRefCount(obj);
116  obj = newObj(std::forward<T>(t));
117  Tcl_IncrRefCount(obj);
118  } else {
119  assign(std::forward<T>(t));
120  }
121  return *this;
122  }
123 
124  // get underlying Tcl_Obj
125  [[nodiscard]] Tcl_Obj* getTclObject() { return obj; }
126  [[nodiscard]] Tcl_Obj* getTclObjectNonConst() const { return const_cast<Tcl_Obj*>(obj); }
127 
128  // add elements to a Tcl list
129  template<typename T> void addListElement(const T& t) { addListElement(newObj(t)); }
130  template<typename ITER> void addListElements(ITER first, ITER last) {
131  addListElementsImpl(first, last,
132  typename std::iterator_traits<ITER>::iterator_category());
133  }
134  template<typename Range> void addListElements(Range&& range) {
135  addListElements(std::begin(range), std::end(range));
136  }
137  template<typename... Args> void addListElement(Args&&... args) {
138  addListElementsImpl({newObj(std::forward<Args>(args))...});
139  }
140 
141  // add key-value pair(s) to a Tcl dict
142  template<typename Key, typename Value>
143  void addDictKeyValue(const Key& key, const Value& value) {
144  addDictKeyValues({newObj(key), newObj(value)});
145  }
146  template<typename... Args> void addDictKeyValues(Args&&... args) {
147  addDictKeyValues({newObj(std::forward<Args>(args))...});
148  }
149 
150  // value getters
151  [[nodiscard]] zstring_view getString() const;
152  [[nodiscard]] int getInt (Interpreter& interp) const;
153  [[nodiscard]] bool getBoolean (Interpreter& interp) const;
154  [[nodiscard]] double getDouble(Interpreter& interp) const;
155  [[nodiscard]] 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]] auto begin() const { return iterator(*this, 0); }
170  [[nodiscard]] auto end() const { return iterator(*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  [[nodiscard]] friend bool operator==(std::string_view x, const TclObject& y) {
191  return x == y.getString();
192  }
193 
194  [[nodiscard]] friend bool operator!=(const TclObject& x, const TclObject& y) { return !(x == y); }
195  [[nodiscard]] friend bool operator!=(const TclObject& x, std::string_view y) { return !(x == y); }
196  [[nodiscard]] friend bool operator!=(std::string_view x, const TclObject& y) { return !(x == y); }
197 
198 private:
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(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(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, 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(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, [](const auto& t) { return newObj(t); });
272  addListElementsImpl(objc, objv);
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  [[nodiscard]] TclObject getListIndexUnchecked(unsigned index) const;
281 
282 private:
283  Tcl_Obj* obj;
284 };
285 
286 // We want to be able to reinterpret_cast a Tcl_Obj* as a TclObject.
287 static_assert(sizeof(TclObject) == sizeof(Tcl_Obj*));
288 
289 template<typename... Args>
290 [[nodiscard]] TclObject makeTclList(Args&&... args)
291 {
292  return TclObject(TclObject::MakeListTag{}, std::forward<Args>(args)...);
293 }
294 
295 template<typename... Args>
296 [[nodiscard]] TclObject makeTclDict(Args&&... args)
297 {
298  return TclObject(TclObject::MakeDictTag{}, std::forward<Args>(args)...);
299 }
300 
301 struct XXTclHasher {
302  [[nodiscard]] uint32_t operator()(std::string_view str) const {
303  return xxhash(str);
304  }
305  [[nodiscard]] uint32_t operator()(const TclObject& obj) const {
306  return xxhash(obj.getString());
307  }
308 };
309 
310 } // namespace openmsx
311 
312 #endif
TclObject t
friend bool operator==(std::string_view x, const TclObject &y)
Definition: TclObject.hh:190
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
unsigned getListLength(Interpreter &interp) const
Definition: TclObject.cc:125
friend bool operator==(const TclObject &x, const TclObject &y)
Definition: TclObject.hh:184
Tcl_Obj * getTclObjectNonConst() const
Definition: TclObject.hh:126
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:168
TclObject(const TclObject &o)
Definition: TclObject.hh:75
double getDouble(Interpreter &interp) const
Definition: TclObject.cc:101
TclObject(TclObject &&o) noexcept
Definition: TclObject.hh:76
void addListElement(Args &&... args)
Definition: TclObject.hh:137
void addListElements(Range &&range)
Definition: TclObject.hh:134
TclObject(MakeListTag, Args &&... args)
Definition: TclObject.hh:80
void addListElement(const T &t)
Definition: TclObject.hh:129
auto begin() const
Definition: TclObject.hh:169
TclObject & operator=(const TclObject &other)
Definition: TclObject.hh:94
void addListElements(ITER first, ITER last)
Definition: TclObject.hh:130
TclObject & operator=(TclObject &&other) noexcept
Definition: TclObject.hh:108
friend bool operator!=(const TclObject &x, const TclObject &y)
Definition: TclObject.hh:194
auto end() const
Definition: TclObject.hh:170
TclObject getDictValue(Interpreter &interp, const Key &key) const
Definition: TclObject.hh:160
int getInt(Interpreter &interp) const
Definition: TclObject.cc:72
TclObject(Tcl_Obj *o)
Definition: TclObject.hh:73
TclObject(MakeDictTag, Args &&... args)
Definition: TclObject.hh:86
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:143
void addDictKeyValues(Args &&... args)
Definition: TclObject.hh:146
TclObject & operator=(TclObject &other)
Definition: TclObject.hh:101
span< const uint8_t > getBinary() const
Definition: TclObject.cc:118
friend bool operator!=(std::string_view x, const TclObject &y)
Definition: TclObject.hh:196
Tcl_Obj * getTclObject()
Definition: TclObject.hh:125
friend bool operator!=(const TclObject &x, std::string_view y)
Definition: TclObject.hh:195
TclObject getDictValue(Interpreter &interp, const TclObject &key) const
Definition: TclObject.cc:161
unsigned size() const
Definition: TclObject.hh:167
TclObject & operator=(T &&t)
Definition: TclObject.hh:113
std::optional< int > getOptionalInt() const
Definition: TclObject.cc:82
zstring_view getString() const
Definition: TclObject.cc:111
constexpr pointer data() const noexcept
Definition: span.hh:323
constexpr index_type size() const noexcept
Definition: span.hh:296
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:89
TclObject makeTclList(Args &&... args)
Definition: TclObject.hh:290
TclObject makeTclDict(Args &&... args)
Definition: TclObject.hh:296
auto transform(InputRange &&range, OutputIter out, UnaryOperation op)
Definition: ranges.hh:190
uint32_t operator()(std::string_view str) const
Definition: TclObject.hh:302
uint32_t operator()(const TclObject &obj) const
Definition: TclObject.hh:305
constexpr uint128 operator*(const uint128 &a, const uint128 &b)
Definition: uint128.hh:187
#define VLA(TYPE, NAME, LENGTH)
Definition: vla.hh:10
uint32_t xxhash(std::string_view key)
Definition: xxhash.hh:144
constexpr auto begin(const zstring_view &x)
Definition: zstring_view.hh:83
constexpr auto end(const zstring_view &x)
Definition: zstring_view.hh:84