openMSX
TclObject.cc
Go to the documentation of this file.
1#include "TclObject.hh"
2#include "Interpreter.hh"
3#include "CommandException.hh"
4#include "narrow.hh"
5
6namespace openmsx {
7
8[[noreturn]] static void throwException(Tcl_Interp* interp)
9{
10 std::string_view message = interp ? Tcl_GetStringResult(interp)
11 : "TclObject error";
12 throw CommandException(message);
13}
14
15static void unshare(Tcl_Obj*& obj)
16{
17 if (Tcl_IsShared(obj)) {
18 Tcl_DecrRefCount(obj);
19 obj = Tcl_DuplicateObj(obj);
20 Tcl_IncrRefCount(obj);
21 }
22}
23
24void TclObject::addListElement(Tcl_Obj* element)
25{
26 // Although it's theoretically possible that Tcl_ListObjAppendElement()
27 // returns an error (e.g. adding an element to a string containing
28 // unbalanced quotes), this rarely occurs in our context. So we don't
29 // require passing an Interpreter parameter in all addListElement()
30 // functions. And in the very unlikely case that it does happen the
31 // only problem is that the error message is less descriptive than it
32 // could be.
33 unshare(obj);
34 Tcl_Interp* interp = nullptr;
35 if (Tcl_ListObjAppendElement(interp, obj, element) != TCL_OK) {
36 throwException(interp);
37 }
38}
39
40void TclObject::addListElementsImpl(std::initializer_list<Tcl_Obj*> l)
41{
42 addListElementsImpl(int(l.size()), l.begin());
43}
44
45void TclObject::addListElementsImpl(int objc, Tcl_Obj* const* objv)
46{
47 unshare(obj);
48 Tcl_Interp* interp = nullptr; // see comment in addListElement
49 if (Tcl_ListObjReplace(interp, obj, INT_MAX, 0, objc, objv) != TCL_OK) {
50 throwException(interp);
51 }
52}
53
54void TclObject::addDictKeyValues(std::initializer_list<Tcl_Obj*> keyValuePairs)
55{
56 assert((keyValuePairs.size() % 2) == 0);
57 unshare(obj);
58 Tcl_Interp* interp = nullptr; // see comment in addListElement
59 auto it = keyValuePairs.begin(), et = keyValuePairs.end();
60 while (it != et) {
61 Tcl_Obj* key = *it++;
62 Tcl_Obj* value = *it++;
63 if (Tcl_DictObjPut(interp, obj, key, value) != TCL_OK) {
64 throwException(interp);
65 }
66 }
67}
68
69int TclObject::getInt(Interpreter& interp_) const
70{
71 auto* interp = interp_.interp;
72 int result;
73 if (Tcl_GetIntFromObj(interp, obj, &result) != TCL_OK) {
74 throwException(interp);
75 }
76 return result;
77}
78
79std::optional<int> TclObject::getOptionalInt() const
80{
81 int result;
82 if (Tcl_GetIntFromObj(nullptr, obj, &result) != TCL_OK) {
83 return {};
84 }
85 return result;
86}
87
89{
90 auto* interp = interp_.interp;
91 int result;
92 if (Tcl_GetBooleanFromObj(interp, obj, &result) != TCL_OK) {
93 throwException(interp);
94 }
95 return result != 0;
96}
97
98std::optional<bool> TclObject::getOptionalBool() const
99{
100 int result;
101 if (Tcl_GetBooleanFromObj(nullptr, obj, &result) != TCL_OK) {
102 return {};
103 }
104 return result != 0;
105}
106
107float TclObject::getFloat(Interpreter& interp_) const
108{
109 // Tcl doesn't directly support 'float', only 'double', so use that.
110 // But we hide this from the rest from the code, and we do the
111 // narrowing conversion in only this single location.
112 return narrow_cast<float>(getDouble(interp_));
113}
114std::optional<float> TclObject::getOptionalFloat() const
115{
116 if (auto d = getOptionalDouble()) {
117 return narrow_cast<float>(*d);
118 }
119 return {};
120}
121
122double TclObject::getDouble(Interpreter& interp_) const
123{
124 auto* interp = interp_.interp;
125 double result;
126 if (Tcl_GetDoubleFromObj(interp, obj, &result) != TCL_OK) {
127 throwException(interp);
128 }
129 return result;
130}
131
132std::optional<double> TclObject::getOptionalDouble() const
133{
134 double result;
135 if (Tcl_GetDoubleFromObj(nullptr, obj, &result) != TCL_OK) {
136 return {};
137 }
138 return result;
139}
140
142{
143 int length;
144 char* buf = Tcl_GetStringFromObj(obj, &length);
145 return {buf, size_t(length)};
146}
147
148std::span<const uint8_t> TclObject::getBinary() const
149{
150 int length;
151 auto* buf = Tcl_GetByteArrayFromObj(obj, &length);
152 return {buf, size_t(length)};
153}
154
155unsigned TclObject::getListLength(Interpreter& interp_) const
156{
157 auto* interp = interp_.interp;
158 int result;
159 if (Tcl_ListObjLength(interp, obj, &result) != TCL_OK) {
160 throwException(interp);
161 }
162 return result;
163}
164unsigned TclObject::getListLengthUnchecked() const
165{
166 int result;
167 if (Tcl_ListObjLength(nullptr, obj, &result) != TCL_OK) {
168 return 0; // error
169 }
170 return result;
171}
172
173TclObject TclObject::getListIndex(Interpreter& interp_, unsigned index) const
174{
175 auto* interp = interp_.interp;
176 Tcl_Obj* element;
177 if (Tcl_ListObjIndex(interp, obj, narrow<int>(index), &element) != TCL_OK) {
178 throwException(interp);
179 }
180 return element ? TclObject(element) : TclObject();
181}
183{
184 Tcl_Obj* element;
185 if (Tcl_ListObjIndex(nullptr, obj, narrow<int>(index), &element) != TCL_OK) {
186 return {};
187 }
188 return element ? TclObject(element) : TclObject();
189}
190
191void TclObject::removeListIndex(Interpreter& interp_, unsigned index)
192{
193 unshare(obj);
194 auto* interp = interp_.interp;
195 if (Tcl_ListObjReplace(interp, obj, narrow<int>(index), 1, 0, nullptr) != TCL_OK) {
196 throwException(interp);
197 }
198}
199
200void TclObject::setDictValue(Interpreter& interp_, const TclObject& key, const TclObject& value)
201{
202 unshare(obj);
203 auto* interp = interp_.interp;
204 if (Tcl_DictObjPut(interp, obj, key.obj, value.obj) != TCL_OK) {
205 throwException(interp);
206 }
207}
208
210{
211 auto* interp = interp_.interp;
212 Tcl_Obj* value;
213 if (Tcl_DictObjGet(interp, obj, key.obj, &value) != TCL_OK) {
214 throwException(interp);
215 }
216 return value ? TclObject(value) : TclObject();
217}
218
219std::optional<TclObject> TclObject::getOptionalDictValue(const TclObject& key) const
220{
221 Tcl_Obj* value;
222 if ((Tcl_DictObjGet(nullptr, obj, key.obj, &value) != TCL_OK) || !value) {
223 return {};
224 }
225 return TclObject(value);
226}
227
229{
230 auto* interp = interp_.interp;
231 int result;
232 if (Tcl_ExprBooleanObj(interp, obj, &result) != TCL_OK) {
233 throwException(interp);
234 }
235 return result != 0;
236}
237
239{
240 auto* interp = interp_.interp;
241 Tcl_Obj* result;
242 if (Tcl_ExprObj(interp, obj, &result) != TCL_OK) {
243 throwException(interp);
244 }
245 return TclObject(result);
246}
247
249{
250 auto* interp = interp_.interp;
251 int flags = compile ? 0 : TCL_EVAL_DIRECT;
252 if (Tcl_EvalObjEx(interp, obj, flags) != TCL_OK) {
253 throw CommandException(Tcl_GetStringResult(interp));
254 }
255 return TclObject(Tcl_GetObjResult(interp));
256}
257
258} // namespace openmsx
bool getBoolean(Interpreter &interp) const
Definition TclObject.cc:88
TclObject executeCommand(Interpreter &interp, bool compile=false)
Interpret this TclObject as a command and execute it.
Definition TclObject.cc:248
unsigned getListLength(Interpreter &interp) const
Definition TclObject.cc:155
TclObject getListIndexUnchecked(unsigned index) const
Definition TclObject.cc:182
TclObject getListIndex(Interpreter &interp, unsigned index) const
Definition TclObject.cc:173
bool evalBool(Interpreter &interp) const
Definition TclObject.cc:228
std::optional< TclObject > getOptionalDictValue(const TclObject &key) const
Definition TclObject.cc:219
void removeListIndex(Interpreter &interp, unsigned index)
Definition TclObject.cc:191
double getDouble(Interpreter &interp) const
Definition TclObject.cc:122
float getFloat(Interpreter &interp) const
Definition TclObject.cc:107
TclObject eval(Interpreter &interp) const
Definition TclObject.cc:238
void addListElement(const T &t)
Definition TclObject.hh:131
std::span< const uint8_t > getBinary() const
Definition TclObject.cc:148
std::optional< float > getOptionalFloat() const
Definition TclObject.cc:114
int getInt(Interpreter &interp) const
Definition TclObject.cc:69
void addDictKeyValues(Args &&... args)
Definition TclObject.hh:148
std::optional< bool > getOptionalBool() const
Definition TclObject.cc:98
TclObject getDictValue(Interpreter &interp, const TclObject &key) const
Definition TclObject.cc:209
std::optional< int > getOptionalInt() const
Definition TclObject.cc:79
zstring_view getString() const
Definition TclObject.cc:141
std::optional< double > getOptionalDouble() const
Definition TclObject.cc:132
void setDictValue(Interpreter &interp, const TclObject &key, const TclObject &value)
Definition TclObject.cc:200
Like std::string_view, but with the extra guarantee that it refers to a zero-terminated string.
This file implemented 3 utility functions:
Definition Autofire.cc:11