openMSX
TclObject.hh
Go to the documentation of this file.
1 #ifndef TCLOBJECT_HH
2 #define TCLOBJECT_HH
3 
4 #include "string_view.hh"
5 #include "span.hh"
6 #include "vla.hh"
7 #include "xxhash.hh"
8 #include <tcl.h>
9 #include <algorithm>
10 #include <climits>
11 #include <initializer_list>
12 #include <iterator>
13 #include <cassert>
14 #include <cstdint>
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 = string_view;
27  using reference = string_view;
28  using pointer = string_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  bool operator==(const iterator& other) const {
36  assert(obj == other.obj);
37  return i == other.i;
38  }
39  bool operator!=(const iterator& other) const {
40  return !(*this == other);
41  }
42 
43  string_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  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  ~TclObject() { Tcl_DecrRefCount(obj); }
78 
79  // assignment operators
80  TclObject& operator=(const TclObject& other) {
81  if (&other != this) {
82  Tcl_DecrRefCount(obj);
83  init(other.obj);
84  }
85  return *this;
86  }
87  TclObject& operator=(TclObject&& other) noexcept {
88  std::swap(obj, other.obj);
89  return *this;
90  }
91  template<typename T>
93  if (Tcl_IsShared(obj)) {
94  Tcl_DecrRefCount(obj);
95  obj = newObj(t);
96  Tcl_IncrRefCount(obj);
97  } else {
98  assign(t);
99  }
100  return *this;
101  }
102 
103  // get underlying Tcl_Obj
104  Tcl_Obj* getTclObject() { return obj; }
105  Tcl_Obj* getTclObjectNonConst() const { return const_cast<Tcl_Obj*>(obj); }
106 
107  // add elements to a Tcl list
108  template<typename T> void addListElement(T t) { addListElement(newObj(t)); }
109  template<typename ITER> void addListElements(ITER first, ITER last) {
110  addListElementsImpl(first, last,
111  typename std::iterator_traits<ITER>::iterator_category());
112  }
113  template<typename Range> void addListElements(Range&& range) {
114  addListElements(std::begin(range), std::end(range));
115  }
116  template<typename... Args> void addListElement(Args&&... args) {
117  addListElementsImpl({newObj(std::forward<Args>(args))...});
118  }
119 
120  // add key-value pair(s) to a Tcl dict
121  template<typename Key, typename Value>
122  void addDictKeyValue(const Key& key, const Value& value) {
123  addDictKeyValues({newObj(key), newObj(value)});
124  }
125  template<typename... Args> void addDictKeyValues(Args&&... args) {
126  addDictKeyValues({newObj(std::forward<Args>(args))...});
127  }
128 
129  // value getters
130  string_view getString() const;
131  int getInt (Interpreter& interp) const;
132  bool getBoolean (Interpreter& interp) const;
133  double getDouble(Interpreter& interp) const;
135  unsigned getListLength(Interpreter& interp) const;
136  TclObject getListIndex(Interpreter& interp, unsigned index) const;
137  TclObject getDictValue(Interpreter& interp, const TclObject& key) const;
138  template<typename Key>
139  TclObject getDictValue(Interpreter& interp, const Key& key) const {
140  return getDictValue(interp, TclObject(key));
141  }
142 
143  // STL-like interface when interpreting this TclObject as a list of
144  // strings. Invalid Tcl lists are silently interpreted as empty lists.
145  unsigned size() const { return getListLengthUnchecked(); }
146  bool empty() const { return size() == 0; }
147  auto begin() const { return iterator(*this, 0); }
148  auto end() const { return iterator(*this, size()); }
149 
150  // expressions
151  bool evalBool(Interpreter& interp) const;
152 
160  TclObject executeCommand(Interpreter& interp, bool compile = false);
161 
162  friend bool operator==(const TclObject& x, const TclObject& y) {
163  return x.getString() == y.getString();
164  }
165  friend bool operator==(const TclObject& x, string_view y) {
166  return x.getString() == y;
167  }
168  friend bool operator==(string_view x, const TclObject& y) {
169  return x == y.getString();
170  }
171 
172  friend bool operator!=(const TclObject& x, const TclObject& y) { return !(x == y); }
173  friend bool operator!=(const TclObject& x, string_view y) { return !(x == y); }
174  friend bool operator!=(string_view x, const TclObject& y) { return !(x == y); }
175 
176 private:
177  void init(Tcl_Obj* obj_) noexcept {
178  obj = obj_;
179  Tcl_IncrRefCount(obj);
180  }
181 
182  static Tcl_Obj* newObj(string_view s) {
183  return Tcl_NewStringObj(s.data(), int(s.size()));
184  }
185  static Tcl_Obj* newObj(const char* s) {
186  return Tcl_NewStringObj(s, int(strlen(s)));
187  }
188  static Tcl_Obj* newObj(bool b) {
189  return Tcl_NewBooleanObj(b);
190  }
191  static Tcl_Obj* newObj(int i) {
192  return Tcl_NewIntObj(i);
193  }
194  static Tcl_Obj* newObj(unsigned u) {
195  return Tcl_NewIntObj(u);
196  }
197  static Tcl_Obj* newObj(float f) {
198  return Tcl_NewDoubleObj(double(f));
199  }
200  static Tcl_Obj* newObj(double d) {
201  return Tcl_NewDoubleObj(d);
202  }
203  static Tcl_Obj* newObj(span<const uint8_t> buf) {
204  return Tcl_NewByteArrayObj(buf.data(), int(buf.size()));
205  }
206  static Tcl_Obj* newObj(const TclObject& o) {
207  return o.obj;
208  }
209 
210  void assign(string_view s) {
211  Tcl_SetStringObj(obj, s.data(), int(s.size()));
212  }
213  void assign(const char* s) {
214  Tcl_SetStringObj(obj, s, int(strlen(s)));
215  }
216  void assign(bool b) {
217  Tcl_SetBooleanObj(obj, b);
218  }
219  void assign(int i) {
220  Tcl_SetIntObj(obj, i);
221  }
222  void assign(unsigned u) {
223  Tcl_SetIntObj(obj, u);
224  }
225  void assign(float f) {
226  Tcl_SetDoubleObj(obj, double(f));
227  }
228  void assign(double d) {
229  Tcl_SetDoubleObj(obj, d);
230  }
231  void assign(span<const uint8_t> b) {
232  Tcl_SetByteArrayObj(obj, b.data(), int(b.size()));
233  }
234 
235  template<typename ITER>
236  void addListElementsImpl(ITER first, ITER last, std::input_iterator_tag) {
237  for (ITER it = first; it != last; ++it) {
238  addListElement(*it);
239  }
240  }
241  template<typename ITER>
242  void addListElementsImpl(ITER first, ITER last, std::random_access_iterator_tag) {
243  auto objc = last - first;
244  VLA(Tcl_Obj*, objv, objc);
245  std::transform(first, last, objv, [](const auto& t) { return newObj(t); });
246  addListElementsImpl(objc, objv);
247  }
248 
249  void addListElement(Tcl_Obj* element);
250  void addListElementsImpl(int objc, Tcl_Obj* const* objv);
251  void addListElementsImpl(std::initializer_list<Tcl_Obj*> l);
252  void addDictKeyValues(std::initializer_list<Tcl_Obj*> l);
253  unsigned getListLengthUnchecked() const;
254  TclObject getListIndexUnchecked(unsigned index) const;
255 
256 private:
257  Tcl_Obj* obj;
258 };
259 
260 // We want to be able to reinterpret_cast a Tcl_Obj* as a TclObject.
261 static_assert(sizeof(TclObject) == sizeof(Tcl_Obj*), "");
262 
263 
264 struct XXTclHasher {
265  uint32_t operator()(string_view str) const {
266  return xxhash(str);
267  }
268  uint32_t operator()(const TclObject& obj) const {
269  return xxhash(obj.getString());
270  }
271 };
272 
273 } // namespace openmsx
274 
275 #endif
const char * data() const
Definition: string_view.hh:65
void addListElements(Range &&range)
Definition: TclObject.hh:113
TclObject & operator=(const TclObject &other)
Definition: TclObject.hh:80
TclObject(Tcl_Obj *o)
Definition: TclObject.hh:72
void swap(optional< T > &x, optional< T > &y) noexcept(noexcept(x.swap(y)))
Definition: optional.hh:816
TclObject & operator=(T t)
Definition: TclObject.hh:92
friend bool operator==(const TclObject &x, const TclObject &y)
Definition: TclObject.hh:162
auto begin() const
Definition: TclObject.hh:147
Definition: span.hh:34
string_view getString() const
Definition: TclObject.cc:102
unsigned getListLength(Interpreter &interp) const
Definition: TclObject.cc:116
uint32_t xxhash(string_view key)
Definition: xxhash.hh:143
friend bool operator!=(string_view x, const TclObject &y)
Definition: TclObject.hh:174
bool evalBool(Interpreter &interp) const
Definition: TclObject.cc:162
void addListElement(Args &&... args)
Definition: TclObject.hh:116
bool empty() const
Definition: TclObject.hh:146
Tcl_Obj * getTclObject()
Definition: TclObject.hh:104
friend bool operator!=(const TclObject &x, const TclObject &y)
Definition: TclObject.hh:172
auto begin(const string_view &x)
Definition: string_view.hh:152
Tcl_Obj * getTclObjectNonConst() const
Definition: TclObject.hh:105
uint32_t operator()(const TclObject &obj) const
Definition: TclObject.hh:268
unsigned size() const
Definition: TclObject.hh:145
uint128 operator*(const uint128 &a, const uint128 &b)
Definition: uint128.hh:164
constexpr size_t strlen(const char *s) noexcept
Definition: cstd.hh:135
span< const uint8_t > getBinary() const
Definition: TclObject.cc:109
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
TclObject getListIndex(Interpreter &interp, unsigned index) const
Definition: TclObject.cc:134
TclObject getDictValue(Interpreter &interp, const TclObject &key) const
Definition: TclObject.cc:152
friend bool operator!=(const TclObject &x, string_view y)
Definition: TclObject.hh:173
TclObject & operator=(TclObject &&other) noexcept
Definition: TclObject.hh:87
bool getBoolean(Interpreter &interp) const
Definition: TclObject.cc:82
TclObject(const TclObject &o)
Definition: TclObject.hh:74
double getDouble(Interpreter &interp) const
Definition: TclObject.cc:92
uint32_t operator()(string_view str) const
Definition: TclObject.hh:265
This class implements a (close approximation) of the std::string_view class.
Definition: string_view.hh:15
int getInt(Interpreter &interp) const
Definition: TclObject.cc:72
void addListElement(T t)
Definition: TclObject.hh:108
friend bool operator==(string_view x, const TclObject &y)
Definition: TclObject.hh:168
void addDictKeyValues(Args &&... args)
Definition: TclObject.hh:125
TclObject getDictValue(Interpreter &interp, const Key &key) const
Definition: TclObject.hh:139
auto transform(InputRange &&range, OutputIter out, UnaryOperation op)
Definition: ranges.hh:161
size_type size() const
Definition: string_view.hh:52
void addDictKeyValue(const Key &key, const Value &value)
Definition: TclObject.hh:122
auto end() const
Definition: TclObject.hh:148
void addListElements(ITER first, ITER last)
Definition: TclObject.hh:109
TclObject(TclObject &&o) noexcept
Definition: TclObject.hh:75
TclObject executeCommand(Interpreter &interp, bool compile=false)
Interpret this TclObject as a command and execute it.
Definition: TclObject.cc:172
#define VLA(TYPE, NAME, LENGTH)
Definition: vla.hh:10
TclObject t
auto end(const string_view &x)
Definition: string_view.hh:153
friend bool operator==(const TclObject &x, string_view y)
Definition: TclObject.hh:165