Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Add more examples to 500 |
---|---|
Downloads: | Tarball | ZIP archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA3-256: |
ca2e14f744ca56b23991be42b2e509c5 |
User & Date: | dkf 2018-05-25 09:44:19.466 |
Context
2018-05-25
| ||
21:51 | TIP #509: Initial draft complete check-in: 61ea8fa199 user: fbonnet tags: trunk | |
09:44 | Add more examples to 500 check-in: ca2e14f744 user: dkf tags: trunk | |
2018-05-24
| ||
16:22 | Initiated TIP #509: Implement reentrant mutexes on all platforms check-in: f14eed0570 user: fbonnet tags: trunk | |
Changes
Changes to tip/500.md.
︙ | ︙ | |||
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | In particular: oo::class create Top { method Foo {} { puts "This is Top::Foo for [self]" } } oo::class create Super { superclass Top private method Foo {} { puts "This is Super::Foo for [self]" } method bar {other} { puts "This is Super::bar for [self]" my Foo [self] Foo $other Foo } } oo::class create Sub { superclass Super method Foo {} { puts "This is Sub::Foo for [self]" next } private method bar {other} { error "This should be unreachable" } method grill {} { puts "This is Sub::grill for [self]" my Foo } } Sub create obj1 Sub create obj2 obj1 bar obj2 obj1 grill is expected to print out: | > > > > > > > > | 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 | In particular: oo::class create Top { method Foo {} { puts "This is Top::Foo for [self]" } } oo::class create Super { superclass Top private method Foo {} { puts "This is Super::Foo for [self]" } method bar {other} { puts "This is Super::bar for [self]" my Foo [self] Foo $other Foo } } oo::class create Sub { superclass Super method Foo {} { puts "This is Sub::Foo for [self]" next } private method bar {other} { error "This should be unreachable" } method grill {} { puts "This is Sub::grill for [self]" my Foo } } Sub create obj1 Sub create obj2 obj1 bar obj2 obj1 grill is expected to print out: |
︙ | ︙ | |||
172 173 174 175 176 177 178 179 180 181 182 183 184 185 | Private methods are created calling **oo::define** (and **oo::objdefine**, and via the constructor of **oo::class**) like this: oo::define TheClass { private method foo {...} { ... } private forward bar SomeOtherCommand } At the C API level, private methods can be directly created by setting the *flags* parameter (called *isPublic* in older versions of the API) of `Tcl_NewMethod` and `Tcl_NewInstanceMethod` to the constant `TCL_OO_METHOD_PRIVATE`. This lets custom types of methods be declared private | > | 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 | Private methods are created calling **oo::define** (and **oo::objdefine**, and via the constructor of **oo::class**) like this: oo::define TheClass { private method foo {...} { ... } private forward bar SomeOtherCommand } At the C API level, private methods can be directly created by setting the *flags* parameter (called *isPublic* in older versions of the API) of `Tcl_NewMethod` and `Tcl_NewInstanceMethod` to the constant `TCL_OO_METHOD_PRIVATE`. This lets custom types of methods be declared private |
︙ | ︙ | |||
200 201 202 203 204 205 206 | <dd>Reports the truly private methods only.</dd></dl> The **-scope** option causes the **-all** and **-private** options to be ignored. # Detailed Proposal: Variables | | | > > > | 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 | <dd>Reports the truly private methods only.</dd></dl> The **-scope** option causes the **-all** and **-private** options to be ignored. # Detailed Proposal: Variables Variables are more complex, conceptually, as they necessarily can be named by other commands (e.g., so that they can be **vwait**ed upon or used with Tk widgets; the key underlying operation in both of these cases is that they must support traces) and so cannot be completely separated from the rest of Tcl. This requires them to be named by compounding the user-supplied name (to reduce confusion) and a unique identifier that identifies the particular context of the name. Now, one of the trickier things about TclOO is that it does not provide fixed names for objects; the **rename** command is supported. Because of that, the fully-qualified name of an object or class, while unique, is not a stable identifier. The full name of the backing namespace *is* stably-named (as namespaces cannot be renamed), but only has a unique name when the |
︙ | ︙ | |||
236 237 238 239 240 241 242 | created by declaration; that's up to an appropriate method to actually do (and the variable can be created as normal scalar, array or link). When a method runs in the relevant class, the class's variable resolver will map the private variable in with the user-expected name. The **variable** and **varname** methods of **oo::object** will be aware of the mechanism, respecting what names are visible in the context that invokes | > > > > > > > | > > > > > > > > > > > > > > > | > | 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 | created by declaration; that's up to an appropriate method to actually do (and the variable can be created as normal scalar, array or link). When a method runs in the relevant class, the class's variable resolver will map the private variable in with the user-expected name. The **variable** and **varname** methods of **oo::object** will be aware of the mechanism, respecting what names are visible in the context that invokes them. Thus, if **varname** is called from a context that would be able to see the private variable, `foobar`, it will return the fully qualified name of the Tcl variable variable in the target object's namespace that acts as the storage of that variable; when the declaring class has creation ID `123` and the instance of that class has namespace `::oo::Obj456`, the result of `my varname foobar` will be `::oo::Obj456::123 : foobar`; the parts of that name are: * “`::oo::Obj456`” — Fully-qualified instance namespace name; private variables are still ultimately variables that exist on object instances. * “`::`” — Namespace separator. * “`123`” — Declaring context ID, from the class that holds the definition of the private variable. * “` : `” — Private variable separator; a space, a colon, and another space. This is chosen to be very unlikely to be in user scripts by accident (as variable names with spaces and single colons are traditionally known to be awkward to use explicitly). * “`foobar`” — User-supplied variable name fragment. Similarly, the **variable** method can be used to bring the private variable into scope; this would not normally be necessary, but is useful when the usual resolution rules for the variable are suspended (such as when a variable with that name is given as a formal argument to a constructor). ## Creation and Introspection A private variable mapping is created like this: oo::define Foo { private variable x y method foo {} { set x 1 set y(z) 2 } } That is, the **private** declaration command described above (for methods) |
︙ | ︙ | |||
272 273 274 275 276 277 278 | A supporting introspector is also added, **info object creationid**, which returns the creation ID of any existing object. It also applies to classes. Again, note that creation IDs are _always_ system-allocated and are _never_ guaranteed to be unique between interpreters, either in multiple processes or in the same thread or process; they are only ever locally unique. | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | > > | > | > > | > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 | A supporting introspector is also added, **info object creationid**, which returns the creation ID of any existing object. It also applies to classes. Again, note that creation IDs are _always_ system-allocated and are _never_ guaranteed to be unique between interpreters, either in multiple processes or in the same thread or process; they are only ever locally unique. # Examples This example shows a private variable linked to a Tk entry: oo::class create Editable { variable w; # Allow subclasses to see this private variable val; # Hide this from subclasses constructor {widgetName} { set val {} set w [entry $widgetName -textvariable [my varname val] } method value {args} { if {[llength $args] == 0} { return $val } elseif {[llength $args] == 1} { lassign $args val return } return -code error "wrong # args: ..." } method trace {callbackLambda} { trace add variable val write [list apply $callbackLambda] } } Editable create field .e field trace {args { puts "field is now [field value]" }} field value "Set the contents" Ths is an example of private methods. It shows how private methods can be used to manage the complexity of a method without making its API (as exposed to either subclasses or the rest of Tcl) overly complex. oo::class create Modulator { # The exported interface to the class method modulate {args} { try { foreach value $args { my modulateOne $value } } on error {msg opts} { tailcall return {*}[my reworkError $msg $opts] } } private { # Do something with the single value; practical code would do more method modulateOne {value} { puts "this would modulate with $value" if {$value > 5} { error "this is bad" } } # Simple way to hide the internal structure of the class from errors method reworkError {msg opts} { dict set opts -errorinfo "problem with modulating: $msg" list -options $opts $msg } } } set m [Modulator new] catch {$m modulate 4 5 6 7 8} -> opts puts [dict get $opts -errorinfo] # Prints something like: # this would modulate with 4 # this would modulate with 5 # this would modulate with 6 # problem with modulating: this is bad # invoked from within # "::oo::Obj15 modulate 5 6 7 8" catch {$m no.such.method} msg puts $msg # Prints something like: # unknown method "no.such.method": must be destroy or modulate This is a combined example of private methods and variables. oo::class create LabelEqual { constructor {label} { set [my varname label] $label } private { # Two private declarations in the same block variable label method getLabel {} { return $label } } method equals {other} { # Note that this can call the private method of an # object other than itself. Private methods can make # class-internal protocols. expr {$label eq [$other getLabel]} } } oo::class create Evaluated { superclass LabelEqual # Poorly chosen variable name! Happens too easily in real life variable label constructor {expression} { next $expression set label [expr $expression] } method value {} { return $label } } set expr1 [Evaluated new {1 + 2 + 3}] set expr2 [Evaluated new {3 + 2 + 1}] puts "one is two? [$expr1 equals $expr2]" # Prints: # one is two? 0 puts "one=[$expr1 value] two=[$expr2 value]" # Prints: # one=6 two=6 puts [info vars [info object namespace $expr1]::*] # Prints something like: # {::oo::Obj13::11 : label} ::oo::Obj13::label This example highlights the behaviour of private variables and `info object creationid`: # A simple introspection procedure for classes proc dumpinfo cls { puts "class ID of $cls is [info object creationid $cls]" } # A class with a private variable oo::class create Foo { private variable x constructor {} { # Remember, [self class] says what the class that declared # the current method is, i.e., ::Foo in this case. dumpinfo [self class] set x 1 } method getPrivateX {} { # Returns the fully qualified name for 'x' return [my varname x] } } # A subclass with a normal variable with a potentially clashing name oo::class create Bar { superclass Foo constructor {} { # Ensure we call the superclass constructor first next dumpinfo [self class] set x 1 } method getPublicX {} { return [my varname x] } } puts [Bar create abc] # Prints, for example: # class ID of ::Foo is 11 # class ID of ::Bar is 12 # ::abc abc getPrivateX # Prints, for example: # ::oo::Obj13::11 : x abc getPublicX # Prints, for example: # ::oo::Obj13::x # Implementation See the [`tip-500` branch](https://core.tcl.tk/tcl/timeline?r=tip-500). # Copyright This document has been placed in the public domain. |