Lisp HUG Maillist Archive

How to provide extra initargs for interfaces?

Hello all,

Let's say I'm working on an interface foo which has
a pane bar-pane which is of type bar. Then I would have
use a definition something like:

(capi:define-interface foo () ()
  (:panes
    (bar bar-pane)
    ...)
  ...)

Now, the bar-pane class accepts some initargs
with its make-instance; e.g.,

(make-instance 'bar-pane :option-1 'baz)

Is there a way I can setup an initarg for the
interface such that I could

(make-instance 'foo :bar-option-1 'baz)

If interfaces were regular classes, I think I'd use
an initialize-instance or shared-initialize for this
(but please correct me if that's not the way one should
get this behavior.) However, the define-interface macro
expands to

(DSPEC:DEF (CAPI:DEFINE-INTERFACE FOO)
  (DEFCLASS FOO (CAPI:INTERFACE) ((BAR)) (:METACLASS CAPI::CAPI-CLASS))
  NIL
  NIL
  (DEFMETHOD CAPI::INITIALIZE-INTERFACE
    :AFTER
    ((#:SELF3826 FOO) &REST #:ARGS3827 &KEY &ALLOW-OTHER-KEYS)
    (WITH-SLOTS (BAR) #:SELF3826
      (DECLARE (IGNORABLE BAR))
      (LET ((CAPI::SELF #:SELF3826) (CAPI:INTERFACE #:SELF3826))
        (DECLARE (IGNORABLE CAPI::SELF CAPI:INTERFACE))
        (SETF (SLOT-VALUE #:SELF3826 'BAR)
              (MAKE-INSTANCE 'BAR-PANE :NAME 'BAR))
        (SETF (SLOT-VALUE #:SELF3826 'CAPI::PANES)
              (LIST* (SLOT-VALUE #:SELF3826 'BAR)
                     (SLOT-VALUE #:SELF3826 'CAPI::PANES))))))
  'FOO)

and capi::initialize-interface seems to set the bar slot
unconditionally. So, is there a way to do what I'm trying
to do? I'd like to make only one bar-pane, and the initarg
that I'd like to get to it does have some important behavior
in the initialization of bar, so I'd like to make one bar-pane,
and to make it right the first time.

Now, I am aware that I can do this by adding a slot to the
interface and providing an initarg for that slot, e.g.,

(capi:define-interface foo ()
  ((bar-option
     :initarg :bar-option))
  (:panes
    (bar bar-pane
          :option-1 bar-option)))

but this is rather unappealing as it adds an otherwise unnecessary
slot, and I can no longer omit an :option-1 initarg from my instance
of bar.

Thoughts?


-- 
=====================
Joshua Taylor
tayloj@rpi.edu

"A lot of good things went down one time,
     back in the goodle days."
               John Hartford


Re: How to provide extra initargs for interfaces?

On Sat, 6 Jan 2007 00:13:05 -0500, "Taylor, Joshua" <tayloj@rpi.edu> wrote:

> Let's say I'm working on an interface foo which has a pane bar-pane
> which is of type bar. Then I would have use a definition something
> like:
>
> (capi:define-interface foo () ()
>   (:panes
>     (bar bar-pane)
>     ...)
>   ...)
>
> Now, the bar-pane class accepts some initargs with its
> make-instance; e.g.,
>
> (make-instance 'bar-pane :option-1 'baz)
>
> Is there a way I can setup an initarg for the interface such that I
> could
>
> (make-instance 'foo :bar-option-1 'baz)

Have you considered doing something like (untested)

  (define-interface foo ()
    ((option :initarg :option))
    (:panes
     (bar-pane :option-1 option)
     ...

i.e. stuffing the additional slots into the interface itself?


Re: How to provide extra initargs for interfaces?

On 1/6/07, Edi Weitz <edi@agharta.de> wrote:
> On Sat, 6 Jan 2007 00:13:05 -0500, "Taylor, Joshua" <tayloj@rpi.edu> wrote:
>
> > Let's say I'm working on an interface foo which has a pane bar-pane
> > which is of type bar. Then I would have use a definition something
> > like:
> >
> > (capi:define-interface foo () ()
> >   (:panes
> >     (bar bar-pane)
> >     ...)
> >   ...)
> >
> > Now, the bar-pane class accepts some initargs with its
> > make-instance; e.g.,
> >
> > (make-instance 'bar-pane :option-1 'baz)
> >
> > Is there a way I can setup an initarg for the interface such that I
> > could
> >
> > (make-instance 'foo :bar-option-1 'baz)
>
> Have you considered doing something like (untested)
>
>   (define-interface foo ()
>     ((option :initarg :option))
>     (:panes
>      (bar-pane :option-1 option)
>      ...
>
> i.e. stuffing the additional slots into the interface itself?
>

Actually, I mentioned the same thing in my first email. :)
This does work, and I've used it in the past. I'm wondering
if there is a way to do this that doesn't involve using an
extra slot. Also, probably more importantly, I don't want to
be forced to provide the initarg. I want to be able to do

(make-instance 'foo) ; no initarg provided

and have the bar end up being

(make-instance 'bar-pane) ; no initarg provided

but that

(make-instance 'foo :option 3)

would set bar to

(make-instance 'bar-pane :option-1 3)


-- 
=====================
Joshua Taylor
tayloj@rpi.edu

"A lot of good things went down one time,
     back in the goodle days."
               John Hartford


Re: How to provide extra initargs for interfaces?

On 1/6/07, Mika Kuuskankare <mkuuskan@siba.fi> wrote:
> ... or
> (defmethod initialize-instance :after ((self foo) &rest args &key
> option-1)
>         (with-slots (bar) self
>                 blah blah))

This allows me to catch the initarg, and perform some code based
on it, but the slot will have already been filled at this point.

I'm going to make my examples a little more concrete now, and maybe
what I need will become a little more clear.

(defclass array-pane (capi:output-pane)
  ((array))
  (:default-initargs
    :rows 5
    :cols 5))

(defmethod initialize-instance :after
    ((i array-pane) &rest initargs &key rows cols)
  "Set the array, and set hints of the pane based on the
dimensions of the array."
  (setf (slot-value i 'array) (make-array (list rows cols)))
  (capi:set-hint-table
   i (list :visible-min-width (* 10 rows)
           :visible-max-width (* 10 rows))))

Array-pane has an array which is initialized via
initialize-instance, and there are capi properties
dependent on the size of the array.

(capi:define-interface array-interface () ()
    (:panes
     ((ap array-pane))))

Here's an interface which contains an array-pane.
I'd like to be able to write

(make-instance 'array-interface :rows r :cols c)

and have ap be

(make-instance 'array-pane :rows r :cols c)

Ideally I won't have to destructively modify any
arrays, nor will I need to explicitly set the hints
table again, nor will I need to add any extra slots
to the interface. Thoughts?

-- 
=====================
Joshua Taylor
tayloj@rpi.edu

"A lot of good things went down one time,
     back in the goodle days."
               John Hartford


Re: How to provide extra initargs for interfaces?

Well, I've been working on this a bit more, and I wonder if I've found
a workaround. Hopefully someone with more CAPI knowhow can
tell me whether this is OK:


;; a class in which the slot array is an array whose size
;; is determined by initargs. The visible-size of the pane
;; also depends on the initargs
(defclass array-pane (capi:output-pane)
 ((array))
 (:default-initargs
   :rows 5
   :cols 5))

(defmethod initialize-instance :after
   ((i array-pane) &rest initargs &key rows cols)
 "Set the array, and set hints of the pane based on the
dimensions of the array."
 (setf (slot-value i 'array) (make-array (list rows cols)))
 (capi:set-hint-table
  i (list :visible-min-width (* 10 rows)
          :visible-max-width (* 10 rows))))

;; Here's an interface which contains a single
;; pane, an array pane, but in order to grab the
;; initargs, the array pane is created in an
;; initialize-instance
(capi:define-interface array-interface ()
  ((ap))
  (:layouts
   (main-layout capi:column-layout '(ap)))
  (:default-initargs
    :rows 5
    :cols 8))

;; This method creates the array pane. Note that the pane
;; is given a name, just as it would be with the
;; initialize-interface that define-interface would define.
(defmethod initialize-instance :before
    ((ai array-interface) &rest initargs &key rows cols)
  (with-slots (ap) ai
    (setf ap (make-instance 'array-pane
			    :rows rows
			    :cols cols
			    :name 'ap))))

;; initialize-interface also sets the capi::panes slot
;; of the class, but capi::panes isn't bound at the
;; time that the :before method is evaluated, but
;; it is bound when the :after method comes about.
(defmethod initialize-instance :after
    ((ai array-interface) &rest initargs)
  (with-slots (capi::panes ap) ai
    (setf capi::panes (list* ap capi::panes))))


I admit that this seems a little hackish, and it depends
on knowing to what define-interface expands, so it's
not going to be guaranteed across versions. Nonetheless,
can any CAPI guru tell me if this will work correctly
(My tests make it seem like it does, but it would be nice
to have some reassuring) or if there are other subtleties
that I'm missing. Thanks!

-- 
=====================
Joshua Taylor
tayloj@rpi.edu

"A lot of good things went down one time,
     back in the goodle days."
               John Hartford


Re: How to provide extra initargs for interfaces?

On Sat, 6 Jan 2007 08:01:24 -0500, "Taylor, Joshua" <tayloj@rpi.edu> wrote:

> Actually, I mentioned the same thing in my first email. :)

Right, I should have read until the end... :)


Re: How to provide extra initargs for interfaces?

Unable to parse email body. Email id is 6330

Re: How to provide extra initargs for interfaces?

On 1/8/07, Martin Simmons <martin@lispworks.com> wrote:
>
> >>>>> On Sat, 6 Jan 2007 00:13:05 -0500, Taylor, Joshua said:
> >
> > Hello all,
> >
> > Let's say I'm working on an interface foo which has
> > a pane bar-pane which is of type bar. Then I would have
> > use a definition something like:
> >
> > (capi:define-interface foo () ()
> >   (:panes
> >     (bar bar-pane)
> >     ...)
> >   ...)
> >
> > Now, the bar-pane class accepts some initargs
> > with its make-instance; e.g.,
> >
> > (make-instance 'bar-pane :option-1 'baz)
> >
> > Is there a way I can setup an initarg for the
> > interface such that I could
> >
> > (make-instance 'foo :bar-option-1 'baz)
>
> Yes, this can be done by specifying (:initarg initarg-spec) as a value for the
> initarg of the pane.  The initarg-spec can be keyword, e.g.
>
> (capi:define-interface foo () ()
>   (:panes
>     (bar bar-pane :bar-option-1 (:initarg :bar-option-1))
>     ...)
>   ...)
>
> or something that can follow &key in a lambda-list, e.g.
>
> (capi:define-interface foo () ()
>   (:panes
>     (bar bar-pane :bar-option-1 (:initarg (bar-option-1 22)))
>     ...)
>   ...)
>
> There is an example of the first case in
> examples/capi/graphics/pinboard-test.lisp and we will add it to the
> documentation for define-interface.
>
> --
> Martin Simmons
> LispWorks Ltd
> http://www.lispworks.com/
>
>
This is great! It gets me *almost* everything I want. However,
there's one bit that it's still missing for me. If the initarg isn't
given, I'd like to see the :initform be used. Consider the following
example:

(defclass foo (capi:output-pane)
  ((slot1 :initarg :slot1
	  :initform 3)))

(capi:define-interface
    foo-interface () ()
    (:panes
     (f foo
	:slot1 (:initarg :slot1))))

(let ((fi (make-instance 'foo-interface :slot1 5)))
  (with-slots (f) fi
    (slot-value f 'slot1)))
; => 5

(let ((fi (make-instance 'foo-interface)))
  (with-slots (f) fi
    (slot-value f 'slot1)))
; => NIL
; I was hoping to see 3

In the second instance of foo-interface, slot1 is set to NIL
rather than 5 because the define-interface expands to a
make-instance where the value is placed unconditionally.
Is there a mechanism for getting something like
... &key (arg value arg-p) .. ?

(And if there's not, it's not *all* that bad, but it means that defaults
have to be kept in the pane definition as initforms *and* in the
interface definition as default initargs; I'd like to have the defaults
in just one place. Nonetheless, this is nice to know about.)

Thanks!

-- 
=====================
Joshua Taylor
tayloj@rpi.edu

"A lot of good things went down one time,
     back in the goodle days."
               John Hartford


Re: How to provide extra initargs for interfaces?

Unable to parse email body. Email id is 6335

Re: How to provide extra initargs for interfaces?

On 1/9/07, Martin Simmons <martin@lispworks.com> wrote:
>
> >>>>> On Mon, 8 Jan 2007 15:50:08 -0500, Taylor, Joshua said:
> >
> > On 1/8/07, Martin Simmons <martin@lispworks.com> wrote:
> > >
> > > >>>>> On Sat, 6 Jan 2007 00:13:05 -0500, Taylor, Joshua said:
> > > >
> > > > Hello all,
> > > >
> > > > Let's say I'm working on an interface foo which has
> > > > a pane bar-pane which is of type bar. Then I would have
> > > > use a definition something like:
> > > >
> > > > (capi:define-interface foo () ()
> > > >   (:panes
> > > >     (bar bar-pane)
> > > >     ...)
> > > >   ...)
> > > >
> > > > Now, the bar-pane class accepts some initargs
> > > > with its make-instance; e.g.,
> > > >
> > > > (make-instance 'bar-pane :option-1 'baz)
> > > >
> > > > Is there a way I can setup an initarg for the
> > > > interface such that I could
> > > >
> > > > (make-instance 'foo :bar-option-1 'baz)
> > >
> > > Yes, this can be done by specifying (:initarg initarg-spec) as a value for the
> > > initarg of the pane.  The initarg-spec can be keyword, e.g.
> > >
> > > (capi:define-interface foo () ()
> > >   (:panes
> > >     (bar bar-pane :bar-option-1 (:initarg :bar-option-1))
> > >     ...)
> > >   ...)
> > >
> > > or something that can follow &key in a lambda-list, e.g.
> > >
> > > (capi:define-interface foo () ()
> > >   (:panes
> > >     (bar bar-pane :bar-option-1 (:initarg (bar-option-1 22)))
> > >     ...)
> > >   ...)
> > >
> > > There is an example of the first case in
> > > examples/capi/graphics/pinboard-test.lisp and we will add it to the
> > > documentation for define-interface.
> > >
> > > --
> > > Martin Simmons
> > > LispWorks Ltd
> > > http://www.lispworks.com/
> > >
> > >
> > This is great! It gets me *almost* everything I want. However,
> > there's one bit that it's still missing for me. If the initarg isn't
> > given, I'd like to see the :initform be used. Consider the following
> > example:
> >
> > (defclass foo (capi:output-pane)
> >   ((slot1 :initarg :slot1
> >         :initform 3)))
> >
> > (capi:define-interface
> >     foo-interface () ()
> >     (:panes
> >      (f foo
> >       :slot1 (:initarg :slot1))))
> >
> > (let ((fi (make-instance 'foo-interface :slot1 5)))
> >   (with-slots (f) fi
> >     (slot-value f 'slot1)))
> > ; => 5
> >
> > (let ((fi (make-instance 'foo-interface)))
> >   (with-slots (f) fi
> >     (slot-value f 'slot1)))
> > ; => NIL
> > ; I was hoping to see 3
> >
> > In the second instance of foo-interface, slot1 is set to NIL
> > rather than 5 because the define-interface expands to a
> > make-instance where the value is placed unconditionally.
> > Is there a mechanism for getting something like
> > .. &key (arg value arg-p) .. ?
> >
> > (And if there's not, it's not *all* that bad, but it means that defaults
> > have to be kept in the pane definition as initforms *and* in the
> > interface definition as default initargs; I'd like to have the defaults
> > in just one place. Nonetheless, this is nice to know about.)
>
> There is no direct way to omit the initarg, but see the define-interface
> documentation on the :make-instance-extra-apply-args keyword argument for
> another way to add complete lists of initargs to a pane.
>

Ah, I'd missed that in the docs. This is a useful one to have too. I'll probably
end up using the (:initarg name-of-initarg) option, and just provide a
default-initarg for this application, but it's good to have the flexibility
for future reference. Thanks!

-- 
=====================
Joshua Taylor
tayloj@rpi.edu

"A lot of good things went down one time,
     back in the goodle days."
               John Hartford


Updated at: 2020-12-10 08:46 UTC