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