Autore Topic: Come arrestare i "costruttori con parametri a consumo"  (Letto 679 volte)

Offline vuott

  • Moderatore globale
  • Senatore Gambero
  • *****
  • Post: 11.723
  • Ne mors quidem nos iunget
    • Mostra profilo
Come arrestare i "costruttori con parametri a consumo"
« il: 21 Aprile 2013, 01:26:38 »
Vorrei riportare questa discussione apparsa nella M.L.I.:


« I have a deep hierarchy of classes, seven levels of specialisation so
far and likely to grow.  There are 64 classes in the hierarchy, which is
very strongly designed.

When instantiating any of these objects, the exact type to be created is
unknown, so the root class has a static "factory method" to decide and
create the correct object in the heirarchy.

All leaf classes (obviously) represent a real object in it's own right.
However, some of the intermediate classes are also real objects. All of
the classes have constructors that have two parameters, a string array
("Source") and a string ("Name") i.e.
»
       
Codice: gambas [Seleziona]
Public Sub _new(Source as String[], Name as String)

The intent of the constructor is to parse the Source array and set the
object property values accordingly.

This is my problem.  Each of the constructors "consumes" some of the
parameters passed according to their specific signature.  So to create
an object three levels down the hierarchy, I need to call the
constructor with 4 dummy and the two real parameters, i.e.
»
       
Codice: gambas [Seleziona]
hObj = New Level3Class(Null,Null,Null,Null,Source,Name)

so the two real parameters are available to the "real" constructor in Level3Class.
At level 4, 6 dummy and the 2 real parameters, i.e.
»
       
Codice: gambas [Seleziona]
hObj = New
        Level4Class(Null,Null,Null,Null,Null,Null,Source,Name)


This is becoming very painful.

Is there any way to "turn off" the way constructors consume parameters?

Bruce
»


It's a core language design decision, so I doubt it.
Personally instead of using the "official" constructor _new I often
have a separate
Init( ) function which is called immediately after instantiation


Codice: gambas [Seleziona]
foo = New MyClass()
foo.Init(someParameter)


Messy, but because .Init( ) is an "ordinary" function in the eyes of
the the Gambas compiler, ancestor
classes to not "consume" parameters in the same way, plus it is only
overridden in the hierarchy when required.

Unfortunately Gambas doesn't allow any change in function signatures,
so if you wanted an extra parameter in a descendant
constructor you would have to have an "Init2" method which then calls
"Me.Init()"

Ian
»


« >
> Unfortunately Gambas doesn't allow any change in function signatures,
> so if you wanted an extra parameter in a descendant
> constructor you would have to have an "Init2" method which then calls
> "Me.Init()"
>

(Sshhh! There is a way around this, a 5 line change to gbx_class.c can
remove/disable the signature checking.  We have been running it for
quite some time (August, 2011) with none of the reported "failures" in
over 30 client sites. I can send you the diff privately if you want it,
but remember, it is very unsupported by Benoit.  It all started here:
http://code.google.com/p/gambas/issues/detail?id=78

cheers
Bruce
»


« The check is not done for the "_new" special method, as the inheritance of
_new is special (see "inheritance and constructor" in the "Gambas object
model" documentation on the wiki).

Fabien Bodard
»


« Yep, I know that, but Ian's comment was about overrides for "normal"
methods. Which still for the sake of sanity I could never understand the
need for a total ban on signature changes.

I can appreciate that the return type should to some extent be
"prevented" from overrides, although even that is questionable in cases
like abstract factories.  As I said at the time, I could not see, nor
have experienced a situation where gambas has failed where a method is
overridden in a child class that has a requirement for a different
signature.

In particular, where an ancestral non-instantiable root class has no
implementation of some method "XXXX" and the root class is abstract, it
is fairly easy to specify a method in that class that must be
overridden. In other words a stub that looks something like this


Codice: gambas [Seleziona]
  Public Sub XXXX(...)
    Error.Raise(Subst("Incomplete override &1", Object.Class(Me).Name))
  End


So now I have declared a method in the root class, which MUST be
overridden in the instantiable child classes, by some method which may
require {0,1,2,... many} parameters.  As of the fix for Issue 78 this
doesn't work "nicely", because every override method in a child class
MUST use the signature "Public Sub XXXX(...)"

The solution I "suggested" to Ian is based on optionally compiling out
the signature check in gbx-class.c which as I said has not failed for
about 2 years. I would really love to see an example of where this
approach could fail because I am still running and promulgating a
modified version of Gambas locally and to all the clients that has this
patch in it.

If this sounds like a bit of a rave then I apologise, but "for the sake
of sanity"...

Anyway, so much for the coffee break, back to rewriting the 64 classes
to use Ian's "_new()/Init()" approach. Grrrr!

cheers
Bruce
»
« Chiunque, non ricorrendo lo stato di necessità, nel proprio progetto Gambas fa uso delle istruzioni Shell o Exec, è punito con la sanzione pecuniaria da euro 20,00 a euro 60,00. »

Offline vuott

  • Moderatore globale
  • Senatore Gambero
  • *****
  • Post: 11.723
  • Ne mors quidem nos iunget
    • Mostra profilo
Re: Come arrestare i "costruttori con parametri a consumo"
« Risposta #1 il: 29 Aprile 2013, 17:38:07 »
...continua...

« The signature check is actually a bug fix.

Let's suppose that class B inherits class A, and that A has a method
foo() which is reimplemented in B with a different signature.

Then, calling X.foo() may lead to a crash if X is declared as an A and
is actually a B, because of an internal optimization that replaces an
expensive table symbol lookup by a direct method access.

If *all* of the classes of your hierarchy wants a 'Source' and 'Name'
argument, then these arguments must be defined in the root class
constructor, not in each one!

If theses twwo arguments must be handled differently between classes,
then add a public method to handle them. Prefix its name with an
underscore, to indicate that it must not be called outside of the
implementation.

' Root class

Codice: gambas [Seleziona]
Public Sub _new(Source as String[], Name as String)
   _Init(Source, Name)
End


' Any child class
Codice: gambas [Seleziona]
Public Sub _Init(Source as String[], Name as String)
   ...
End


Does it fit your needs?

--
Benoît Minisini
»


« This is rule around underscores enforced by the compiler (a la Python)
or only a convention?

would you consider a Protected access type (like C++) instead?

Ian
»


« No, I always explained that in a previous mail (don't remember which one).

I prefer using a convention, to prevent confusion on what really happens
in the background (and keep simplicity).

"protected" in C++ is a convention enforced by the compiler, but it's
still a public method, i.e. a method that can be called outside of a
class (even if it is a child class).

I often saw C++ programmers that do not understand how all that klingon
object-oriented syntax is implemented in the background. In Gambas, the
syntax is far more... basic, but what happens is more clear (you just
can be "public" or "private").

Regards,

--
Benoît Minisini
»
« Ultima modifica: 30 Aprile 2013, 11:40:41 da vuott »
« Chiunque, non ricorrendo lo stato di necessità, nel proprio progetto Gambas fa uso delle istruzioni Shell o Exec, è punito con la sanzione pecuniaria da euro 20,00 a euro 60,00. »