From: ······@ws1.yonsei.ac.kr
Subject: How can I translate C++ into CLOS?
Date: 
Message-ID: <6hi3mf$e4v$1@nnrp1.dejanews.com>
Hello,

I'm converting the C++ program into LISP and CLOS, but I don't know how can I
change the *this* keyword in C++ used in the member functions of the class.
Can I define the member function of the class without the instantiation of
that class? I'm appreciated with your examples.

-----== Posted via Deja News, The Leader in Internet Discussion ==-----
http://www.dejanews.com/   Now offering spam-free web-based newsreading

From: Pierre Mai
Subject: Re: How can I translate C++ into CLOS?
Date: 
Message-ID: <m3k98jusxh.fsf@torus.cs.tu-berlin.de>
>>>>> "k" == kramer  <······@ws1.yonsei.ac.kr> writes:

    k> Hello, I'm converting the C++ program into LISP and CLOS, but I
    k> don't know how can I change the *this* keyword in C++ used in
    k> the member functions of the class.  Can I define the member
    k> function of the class without the instantiation of that class?
    k> I'm appreciated with your examples.

Hmmm, I'm not really that clear on what your problem is, but generally 
one of the differences between CLOS (or for that matter Ada95) and C++ 
is, that CLOS passes around the references to objects _explicitly_
anyways, whereas C++ tries to hide it, and then needs to expose it in
member functions (i.e. methods) via `this' (Warning, quick and dirty
coding ahead):

class B;

class A
{
	void DoSomethingWithaB (B* reference);
};

// Global Instance of A here
A myA;

class B
{
	void Doit()
	{
		// We let Class A do something with us
		myA->DoSomethingWithaB(this);
	}
};

would be written in CLOS as:

(defclass B ())

(defclass A ())

(defmethod DoSomethingWithaB ((self A) (reference B))
  nil)

(defvar myA (make-instance 'A))

(defmethod Doit ((self B))
  (DoSomethingWithaB myA self))
		
Hope this helps.  Otherwise I'd recommend Sonya E. Keene's
`Object-Oriented Programming in Common Lisp', Addison-Wesley, ISBN
0-201-17589-4, 1989, which is _really_ helpful in getting in to the
Lisp-way of OOP.

Regs, Pierre.

-- 
Pierre Mai <····@cs.tu-berlin.de>	http://home.pages.de/~trillian/
  "Such is life." -- Fiona in "Four Weddings and a Funeral" (UK/1994)
From: Sunil Mishra
Subject: Re: How can I translate C++ into CLOS?
Date: 
Message-ID: <efyiuo36qyw.fsf@peachtree.cc.gatech.edu>
In article <············@nnrp1.dejanews.com> ······@ws1.yonsei.ac.kr writes:

   Hello,

   I'm converting the C++ program into LISP and CLOS, but I don't know how can I
   change the *this* keyword in C++ used in the member functions of the class.
   Can I define the member function of the class without the instantiation of
   that class? I'm appreciated with your examples.

There is no equivalent of *this* in clos.

The reason is a fundamental difference in philosophy. In C++ the messages
(more like messages than methods, at the very least) are all associated
with a particular object. The argument *this* therefore is treated as an
implicit argument, the first one in the function, which is always bound to
the object to which the message was sent.

In CLOS, the methods are never bound to an object. So, the variable *this*
has to be explicitly turned into an argument, more specifically, a typed
argument in a specialized lambda list.

So, a call like foo.set_value(23) in C++ would be written as
(set-value foo 23) in clos. (Actually, you would be much more likely to
write (setf (value foo) 23)).

Sunil
From: Bill Newman
Subject: Re: How can I translate C++ into CLOS?
Date: 
Message-ID: <wnewmanErs4yM.4xn@netcom.com>
Sunil Mishra (·······@peachtree.cc.gatech.edu) wrote:
: In article <············@nnrp1.dejanews.com> ······@ws1.yonsei.ac.kr writes:

:    Hello,

:    I'm converting the C++ program into LISP and CLOS, 
:    but I don't know how can I
:    change the *this* keyword in C++ used in the member functions 
:    of the class.
:    Can I define the member function of the class without the 
:    instantiation of
:    that class? I'm appreciated with your examples.

: There is no equivalent of *this* in clos.

: The reason is a fundamental difference in philosophy. In C++ the messages
: (more like messages than methods, at the very least) are all associated
: with a particular object. The argument *this* therefore is treated as an
: implicit argument, the first one in the function, which is always bound to
: the object to which the message was sent.

: In CLOS, the methods are never bound to an object. So, the variable *this*
: has to be explicitly turned into an argument, more specifically, a typed
: argument in a specialized lambda list.

I learned OO programming in C++ well before I ever used it in Lisp. As
Sunil says, a "this" pointer would be ambiguous in CLOS, and I don't
usually miss it. However, there's one circumstance where it wouldn't
be ambiguous: in initforms for slots of class or struct objects.  And
I do miss it there.

Sometimes it would be very handy for initforms to be able to refer to
a symbol which is bound to the object currently being initialized,
either so that they could define their value in terms of the value of
another slot or so that they could create an object which refers back
to the main object being initialized.

Always binding some reserved word like THIS doesn't seem very Lispy,
but it'd be just as good to have a :CTOR-THIS keyword argument to
DEFCLASS and DEFSTRUCT which specified a symbol which would be bound
to the object being initialized while the initforms were evaluated:

  (defclass (point (:ctor-this xyzzy)) ()
    ((x :reader x :initarg x)
     (y :reader y :initarg y)
     (r :reader r :initform (sqrt (+ (expt (x xyzzy) 2) (expt (y xyzzy) 2))))
     (point-table-entry :initform (make-point-table-entry xyzzy))
     (hash-memo :reader hash-memo :initform (sxhash (+ x (* 0.191022 y))))))

(The hypothetical initforms for R and HASH-MEMO would only work if
CLOS guarantees initialization order; I dunno whether it does.)

As it is, for CLOS, I just put the R and POINT-TABLE-ENTRY and HASH-MEMO
initialization code into INITIALIZE-INSTANCE, For structs, I write

  (defstruct (point (:constructor make-bare-point))
    x y r point-table-entry hash-memo)

and define MAKE-POINT in terms of MAKE-BARE-POINT. Both approaches
work, but it's a little tedious having to maintain the corresponding
slot information in two places (inside the class definition and inside
INITIALIZE-INSTANCE) even when class initialization is straightforward
enough that I could use initforms to express it all in one place if a
"this" pointer were available.  I could easily be overlooking some
nicer way to handle it, though. Common Lisp is a big language, and
CLOS by itself is pretty complicated and still a little alien to me.

  Bill Newman
From: Espen Vestre
Subject: Re: How can I translate C++ into CLOS?
Date: 
Message-ID: <w667k25ote.fsf@gromit.nextel.no>
·······@netcom.com (Bill Newman) writes:

> I learned OO programming in C++ well before I ever used it in Lisp. As
> Sunil says, a "this" pointer would be ambiguous in CLOS, and I don't
> usually miss it. However, there's one circumstance where it wouldn't
> be ambiguous: in initforms for slots of class or struct objects.  And
> I do miss it there.

I agree, I've also strongly missed reference to the object itself in
initforms, (but as you also point out later on) I've been a bit worried
about initialization order, so I ended up with a solution where you
can specify initialization functions which will be applied with the
object as the only parameter in an :after-method.  The code is a bit
messy right now, but my idea is that (some day) I would clean it up
and implement it as a new metaclass which supports an additional
initialization argument :init-something (:initfunction is already in
use...).  My idea is then that these functions would be called _after_
the initforms have all been evaluated.

--

  (espen vestre)
From: Sunil Mishra
Subject: Re: How can I translate C++ into CLOS?
Date: 
Message-ID: <efyogxuhs8a.fsf@cleon.cc.gatech.edu>
In article <··············@gromit.nextel.no> Espen Vestre <··@nextel.no> writes:

   I agree, I've also strongly missed reference to the object itself in
   initforms, (but as you also point out later on) I've been a bit worried
   about initialization order, so I ended up with a solution where you
   can specify initialization functions which will be applied with the
   object as the only parameter in an :after-method.  The code is a bit
   messy right now, but my idea is that (some day) I would clean it up
   and implement it as a new metaclass which supports an additional
   initialization argument :init-something (:initfunction is already in
   use...).  My idea is then that these functions would be called _after_
   the initforms have all been evaluated.

Maybe I'm missing something... Isn't an after method on shared-initialize
sufficient to do all the post processing needed on the object?

Sunil
From: Espen Vestre
Subject: Re: How can I translate C++ into CLOS?
Date: 
Message-ID: <w61zuo4zz1.fsf@gromit.nextel.no>
·······@cleon.cc.gatech.edu (Sunil Mishra) writes:

> Maybe I'm missing something... 

yes, I wasn't very clear I think...

> Isn't an after method on shared-initialize
> sufficient to do all the post processing needed on the object?

Obviously, yes.

BUT: If you do this a LOT, you ask yourself whether you could avoid
writing all these :after-methods for each class, and rather do
this through a (possibly new) slot-option.

But when you - as I do - generate the classes with ensure-class 
dynamically at run time, the shared-initialize-after-method solution 
gets even less attractive (because you would have to compile the 
method functions at run time).

I now think that the best solution will be to write a _general_
:after-method for shared-initialize that will instantiate any
slots that should be instantiated by calling a function on
the object to be instantiated.  The question then remains how
to mark them as such.  I don't quite like Barrys solution with
a double number of slots, so I've thought of at least two other
solutions:

- the meta object protocol solution:  Define a new metaclass that
  accepts a new kind of slot option with the desired behaviour.

- the "two-pass" initialization solution: Write an after-method for
  shared-initialize for the generic class at the top of my hierachy,
  and provide initforms that evaluate to objects that this after-
  method can use to provide the final initialization value.  The after-
  method could use several methods for regcognizing such slot-values:
  If you aren't interested in function slot-values, the obvious quick
  hack is to let the initform evaluate to a function, and let the after-
  method replace that function f with (funcall f myself).  If function slot-
  values are essential, you could let the values be some special kind
  of objects which the after-method could distinguish from ordinary
  slot values, and which contained (somehow) the function to be called.

--

regards,
  Espen Vestre
From: Joao Cachopo
Subject: Re: How can I translate C++ into CLOS?
Date: 
Message-ID: <wxg1j4h2kn.fsf@haiti.gia.ist.utl.pt>
>>>>> "Espen" == Espen Vestre <··@nextel.no> writes:

 Espen> - the "two-pass" initialization solution: Write an after-method for
 Espen>   shared-initialize for the generic class at the top of my
 Espen>   hierachy, and provide initforms that evaluate to objects
 Espen>   that this after- method can use to provide the final
 Espen>   initialization value.  The after- method could use several
 Espen>   methods for regcognizing such slot-values: If you aren't
 Espen>   interested in function slot-values, the obvious quick hack
 Espen>   is to let the initform evaluate to a function, and let the
 Espen>   after- method replace that function f with (funcall f
 Espen>   myself).  If function slot- values are essential, you could
 Espen>   let the values be some special kind of objects which the
 Espen>   after-method could distinguish from ordinary slot values,
 Espen>   and which contained (somehow) the function to be called.

[ Disclaimer: I have little experience with CLOS. For various reasons
              I've been using mainly ANSI Common Lisp without CLOS ]

You mean something like the following?

;;;;;;;;;;;;;;;;;;;; Start of code snippet ;;;;;;;;;;;;;;;;;;;;

(defstruct initform-wrapper
  initform)

(defmacro using-slots (slots &body forms)
  `(make-initform-wrapper
    :initform #'(lambda (this) 
		  (with-slots ,slots this
		    ,@forms))))

(defclass a ()
  ((x :initform 1 
      :initarg :x)
   (y :initform 2 
      :initarg :y)
   (z :initform (using-slots (x y) (+ x y))
      :initarg :z)))

(defun get-slot-names (inst snames)
  (if (listp snames) 
    snames
    (let ((class (class-of inst)))
      (unless (clos:class-finalized-p class)
	(clos:finalize-inheritance class))
      (mapcar #'clos:slot-definition-name 
	      (clos:class-slots class)))))

(defmethod shared-initialize :after ((inst a) slot-names &rest initargs)
  (declare (ignore initargs))
  (dolist (slot-name (get-slot-names inst slot-names))
    (let ((value (slot-value inst slot-name)))
    (when (initform-wrapper-p value)
      (setf (slot-value inst slot-name)
	(funcall (initform-wrapper-initform value) inst))))))

;;;;;;;;;;;;;;;;;;;; End of code snippet ;;;;;;;;;;;;;;;;;;;;


This seems to work with Franz Allegro CL 4.3 for Linux, but notice
that this is not portable Common Lisp as it uses the MOP.

My question is: can we do this without the MOP?

Substituting defclass with a macro of ours should do the trick, but I
wonder whether it is possible in any other way.

My first reading of shared-initialize led me to think that the second
argument should be a list with all the slot names, but after my first
try I re-read it and found that it can be the symbol T, instead.  I
wonder why?

-- 
Joao Cachopo   *   Homepage: http://www.gia.ist.utl.pt/~jcachopo

                 *** Murphy was an optimist ***
From: Sunil Mishra
Subject: Re: How can I translate C++ into CLOS?
Date: 
Message-ID: <efyvhs076ss.fsf@clairmont.cc.gatech.edu>
In article <··············@gromit.nextel.no> Espen Vestre <··@nextel.no> writes:

   ·······@cleon.cc.gatech.edu (Sunil Mishra) writes:

   I now think that the best solution will be to write a _general_
   :after-method for shared-initialize that will instantiate any
   slots that should be instantiated by calling a function on
   the object to be instantiated.  The question then remains how
   to mark them as such.  I don't quite like Barrys solution with
   a double number of slots, so I've thought of at least two other
   solutions:

Why not write a mixin class with a shared-initialize-after-method defined
on the mixin? This way you can have any class created using ensure-class
with this property call the right shared-initialize. This becomes easier
still if you have to do this over some standard set of slots. If not, I
imagine a shared slot (class allocation, NOT in the mixin!) that lists the
slots that must be treated this way for the class would suffice to announce
the slots on which specific types of initializations must be performed.

There might be other ways, but I would have to understand your problem
better.

Sunil
From: ···@franz.com
Subject: Re: How can I translate C++ into CLOS?
Date: 
Message-ID: <6houeh$jn2$1@news2.franz.com>
I also believe that having slot initializations that can refer to
other slot values of the instance is quite useful.
Of course there are ordering issues that must be well defined.
This can be done fairly easily by a user-written define-class macro
that extracts all the :initialize-form forms (not std :initform) and
executes them in an :after initialize-instance method
(or uses the #'initialize method as I define below ;)

I'm also of the opinion that the CLOS make-instance protocol
should be extended to call an additional function after the
slots are initialized:

(defmethod make-instance (class &rest initargs)
 (let ((instance (apply #'allocate-instance class initargs)))
   (apply #'initialize-instance instance initargs)
   ;; Add a call to INITIALIZE
   (initialize instance)
   instance)))

There are two key benefits to this
 1. It doesn't pass &rest args like initialize-instance that must
    be conses up and parsed over and over and #'applies
    for all the :after methods
 2. You can write useful :before methods on #'initialize,
    which are not possible with #'initialize-instance, since in
    a :before method all the slots are not yet bound.
    
I always define this myself by having a common superclass mixin
with an :after initialize-instance method that calls #'initialize
    
In article <···············@clairmont.cc.gatech.edu>, ·······@clairmont.cc.gatech.edu (Sunil Mishra) writes:
>>    I now think that the best solution will be to write a _general_
>>    :after-method for shared-initialize that will instantiate any
>>    slots that should be instantiated by calling a function on
>>    the object to be instantiated.  The question then remains how
>>    to mark them as such.  I don't quite like Barrys solution with
>>
>> Why not write a mixin class with a shared-initialize-after-method defined
>> on the mixin? This way you can have any class created using ensure-class

-Kelly Murray  ···@franz.com
From: Kjetil Valstadsve
Subject: Re: How can I translate C++ into CLOS?
Date: 
Message-ID: <jw4son69clg.fsf@ra.pvv.ntnu.no>
Espen Vestre <··@nextel.no> writes:

> My idea is then that these functions would be called _after_ the
> initforms have all been evaluated.

A bit off-subject: Are there any traditional ways to do lazy
initialisation instead? An access function would then be defined to a)
check for the slot's value; when not set, set it to some initial value
and b) return the slot's value. 

-- 
Kjetil
From: Erik Naggum
Subject: Re: How can I translate C++ into CLOS?
Date: 
Message-ID: <3102243586345101@naggum.no>
* Kjetil Valstadsve
| Are there any traditional ways to do lazy initialisation instead?  An
| access function would then be defined to a) check for the slot's value;
| when not set, set it to some initial value and b) return the slot's
| value.

  you could define methods on the generic function SLOT-UNBOUND to handle
  this case, but I have neither seen this done nor done this myself, so
  don't know how "traditional" it might be.  it would work, though.

#:Erik
-- 
  Abort, Retry, or Upgrade?
From: Barry Margolin
Subject: Re: How can I translate C++ into CLOS?
Date: 
Message-ID: <Mtr%.21$Qv4.381195@cam-news-reader1.bbnplanet.com>
In article <···············@ra.pvv.ntnu.no>,
Kjetil Valstadsve  <·····@pvv.ntnu.no> wrote:
>A bit off-subject: Are there any traditional ways to do lazy
>initialisation instead? An access function would then be defined to a)
>check for the slot's value; when not set, set it to some initial value
>and b) return the slot's value. 

You could write a :before method on the slot reader generic function.  E.g.

(defclass lazy-class
  (slot1 :accessor slot1)
  (slot1-init-function :accessor slot1-fun
                       :init-keyword :slot1-fun :initform 'nil))

(defmethod slot1 :before ((self lazy-class))
  (when (and (not (slot-boundp self 'slot1))
             (slot1-fun self))
    (setf (slot1 self) (funcall (slot1-fun self)))))

(make-instance 'lazy-class :slot1-fun (lambda () ...))

It could also be done using a SLOT-UNBOUND method, as another poster
suggested:

(defmethod slot-unbound (class (self lazy-class) (slot-name (eq slot1)))
  (setf (slot1 self) (funcall (slot1-fun self))))

-- 
Barry Margolin, ······@bbnplanet.com
GTE Internetworking, Powered by BBN, Cambridge, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.