Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backwards compatibility for oo.tcl #293

Open
prpr19xx opened this issue Feb 2, 2024 · 3 comments
Open

Backwards compatibility for oo.tcl #293

prpr19xx opened this issue Feb 2, 2024 · 3 comments

Comments

@prpr19xx
Copy link
Contributor

prpr19xx commented Feb 2, 2024

Following on from #248...

Before 5af9d6a, this worked without complaint:

. package require oo
1.0
. class t {v1 0 v2 0}
t
. set v [t new {v1 1 v3 3}]
::<reference.<t______>.00000000000000000002>
. $v get v1
1
. $v get v2
0

Since then, it generates an error:
t defaultconstructor, v3 is not a class variable

I don't care about v3, I just don't want it to cause any errors (this is a much simplified test-case whereas in reality there are many variables I'm not interested in, but it's huge work to sort in our existing code).
As a workaround, I have been manually patching out the "return -code error..." statement that causes it to fail, restoring previous behaviour.
But I'd rather not have to patch at all, and it would seem that this is fairly easily fixed by adding something like the following change to allow selectable behaviour:

diff --git a/oo.tcl b/oo.tcl
index 4fefa9c..96fca14 100644
--- a/oo.tcl
+++ b/oo.tcl
@@ -1,4 +1,5 @@
 # OO support for Jim Tcl, with multiple inheritance
+set ::oo::ignore_invalid_vars false

 # Create a new class $classname, with the given
 # dictionary as class variables. These are the initial
@@ -81,11 +82,14 @@ proc class {classname {baseclasses {}} classvars} {
        $classname method defaultconstructor {{__vars {}}} {
                set __classvars [$self classvars]
                foreach __v [dict keys $__vars] {
-                       if {![dict exists $__classvars $__v]} {
-                               # level 3 because defaultconstructor is called by new
-                               return -code error -level 3 "[lindex [info level 0] 0], $__v is not a class variable"
+                       if {[dict exists $__classvars $__v]} {
+                               set $__v [dict get $__vars $__v]
+                       } else {
+                               if {!$::oo::ignore_invalid_vars} {
+                                       # level 3 because defaultconstructor is called by new
+                                       return -code error -level 3 "[lindex [info level 0] 0], $__v is not a class variable"
+                               }
                        }
-                       set $__v [dict get $__vars $__v]
                }
        }
        alias "$classname constructor" "$classname defaultconstructor"

This maintains the current behaviour, but allows regression to the previous e.g.

. package require oo
1.0
. puts $::oo::ignore_invalid_vars
false
. class t {v1 0 v2 0}
t
. set v [t new {v1 1 v3 3}]
t defaultconstructor, v3 is not a class variable
[error] . 
. set ::oo::ignore_invalid_vars true
true
. set v [t new {v1 1 v3 3}]
::<reference.<t______>.00000000000000000002>
. $v get v1
1
. $v get v2
0

There may be a better way, but this works for me.

@msteveb
Copy link
Owner

msteveb commented Feb 14, 2024

I'll have a think about this. I'm probably not inclined to bake in backward compatibility, but I'll consider an approach that will make it easy for you to do this in your own code without modifying oo.tcl

@msteveb
Copy link
Owner

msteveb commented Sep 24, 2024

So can't you simply create your own constructor that behaves as you want rather than using the default constructor?

e.g.

t method constructor {arglist} {
    foreach {k v} $arglist {
        set $k $v
    }
}

Just need to do this for each class you create where you want to use the previous behaviour.

@The-Markitecht
Copy link
Contributor

... and that also is easily automated for sugar:

# untested example:
proc makeCompatible {className} {
    $className method constructor {arglist} {
    foreach {k v} $arglist {
        set $k $v
    }
}

class t {v1 0 v2 0}
makeCompatible t

You could even sugar it further than that, pretty easily, by rolling that functionality into your own wrapper of the class command, call it class, after rename'ing the original one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants