Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Object Copying

2 views
Skip to first unread message

Fenrir

unread,
Oct 2, 2008, 6:33:26 PM10/2/08
to
I'm working on making items stack in my player inventory, and I'm
having some trouble allowing the player to drop a specified amount of
items at once. The player's inventory is a vector of pointers to Item
objects. When the player drops an item, I want to reduce the quantity
of that item in the player's inventory, and create that many items
(or, rather, one item with it's amount variable set to the appropriate
quantity) where the player is standing. The problem is that I have a
Potion class derived from the Item class, which is abstract. I plan on
having still more classes derived from Item. How can I copy an object
if I don't know what type it is?

corremn

unread,
Oct 2, 2008, 8:11:04 PM10/2/08
to

Sounds like a difficult way of doing it. Each item should have an
item_type flag, therefore you know what type it is.

Thats how I did it originally but now my inventory has a list of
reference numbers, referencing a global object list.
The inventory then sorts the list to put all numbers of the same type
together. Any referece numbers the reference stackable objects then
stack in the inventory like below.

3 -sword
4 -axe
34 35 56 - 3 arrows
45 45 - 2 potions
4 - stone

Then when I drop the sword, reference number 3 is put on the ground
(added to list on the ground). When I drop the 2 arrows, reference
number 34 and 35 are put on the ground and 56 stays in my inventory.
The only trick is getting the inventory to show stacked item and
allowing the user to reference them correctly.
No copying, no pointers = much better.
Saving is better as you just save a reference number and the global
list.

Nate879

unread,
Oct 2, 2008, 8:11:45 PM10/2/08
to

awhite

unread,
Oct 2, 2008, 8:34:45 PM10/2/08
to

Have your base Item class require child classes to implement clone()
methods. Each leaf class will of course know what type it is, so can copy
itself easily.

Are you sure that subclassing is the right way to go? Data driven via
composition might be better than inheritance. Otherwise, how can you
easily implement polymorph effects?

Adam

Gelatinous Mutant Coconut

unread,
Oct 2, 2008, 9:15:23 PM10/2/08
to
Well, there are two kinds of "copies", basically: Shallow Copies and
Deep Copies. Shallow Copies point back to the same data (your Item, in
this case), while Deep Copies make a copy of the data itself, so that
each set of data can be altered independently.

When you are just splitting a pile of identical items into different
piles, you may want to perform that as a Shallow Copy, with some extra
data stored with the pointer to the item to denote how many items
there are at each location. When you want to take an item from a pile
and make it different, you want to instead perform a Deep Copy of the
object, decrement the quantity in the original pile by 1, and then
make the modifications to the new copy's Item class. (For instance, if
you want just one from a stack of 15 iron longswords to become a rusty
iron longsword.)
---
Making Shallow Copies:

One idea would be to replace your pointers to items with a struct (or
whatever your language's equivalent is) like this:

typedef struct
{
Item *item_pointer;
int item_quantity;
}
item_clones;

Both the player's inventory and squares where items may be would then
have vectors of item_clones, rather than pointers to the item. If make
sure to delete item_clones when their quantity reaches zero, if you
have automatic garbage collection, the item pointed to by your
item_clones will get cleaned up once you delete the last item_clones
that points to the that item. If you don't have garbage collection,
you'll need to implement some kind of reference counting in your Item
class.
---
Making Deep Copies:

The basic idea here is to use virtual functions to make an exact copy
of all of the data in the original object into a new object of the
same type. (Possibly with modification to a 'quantity' variable, if
you aren't going to pull that out into a struct like in my previous
section; you can probably get away with only doing Deep Copies as long
as you aren't on a memory-restricted device.) So your Potion class,
for example, will have a ".copy()" method that first calls Item's
".copy()" (which copies all of the Item object data into an new Item
object (which in this case is a Potion)), and then copies it's data
that is specific to Potion type objects into the new object; the
method then returns this new Potion item.

I'm not so experienced with this sort of thing, so I'm probably not
describing Deep Copying in particular in a manner that makes complete
sense, but if you can't find the information you need quickly on
Google, I'm sure there are people here with more experience in C++ (I
assume that's what you're using?) that could explain it better.
---
Good luck!

Gelatinous Mutant Coconut

unread,
Oct 2, 2008, 9:27:53 PM10/2/08
to
Nate879 is correct that you want a copy constructor; you specifically
want a DEEP copy constructer, rather than a shallow one. I believe
that the deep copy constructor is generally referred to as ".clone()"
in C++. A shallow copy constructor only copies the reference to the
object, instead of returning a reference that points to a new copy of
the data itself.

Fenrir

unread,
Oct 2, 2008, 10:18:40 PM10/2/08
to
Your explanation of shallow and deep copies was sufficient, and I am
using C++. (I just accidentally punctuated that sentence with a
semicolon. :) )

I gather from corremn's post that there is an easier way to do this
that doesn't involve all the bloody pointers, but I don't think I
would be able to do what was described, as I've not the programming
aptitude to even know where to start implementing such a system. I'll
probably stick with what I've got, since I've already gotten most of
it working except dropping things.

On Oct 2, 8:34 pm, awhite <spud...@iinet.net.au> wrote:
> Data driven via composition might be better than inheritance.

I don't know what that means.

> Otherwise, how can you easily implement polymorph effects?

It's far too early in my roguelike-development career to be concerned
about that.

awhite

unread,
Oct 2, 2008, 10:50:32 PM10/2/08
to
On Thu, 02 Oct 2008 19:18:40 -0700, Fenrir wrote:

Please don't top post.

> On Oct 2, 8:34�pm, awhite <spud...@iinet.net.au> wrote:
>> Data driven via composition might be better than inheritance.
>
> I don't know what that means.

Rather than have class Silver_Sword() inherit from Sword() inherit from
Weapon() inherit from Item(), have a base Item() or even (Weapon :
public Item) class with slots for each datum.

>> Otherwise, how can you easily implement polymorph effects?
>
> It's far too early in my roguelike-development career to be concerned
> about that.

Cool - it's better to actually Write A Game than mess around worrying
about things that May Never Actually Happen.

Adam

tyrec...@yahoo.com

unread,
Oct 3, 2008, 2:05:48 AM10/3/08
to
On Oct 3, 4:18 am, Fenrir <siliconvik...@gmail.com> wrote:
> Your explanation of shallow and deep copies was sufficient, and I am
> using C++. (I just accidentally punctuated that sentence with a
> semicolon. :) )

Gelatinous is correct about the need to distinguish between deep and
shallow copies. Unfortunately, Gelatinous didn't properly explain what
clone() is as opposed to copy construction and what that has to do
with you.

Copy construction is non-polymorphic. Whenever you create a new object
using copy construction, you are explicitly naming its type up front.
There is no dynamic lookup to determine the type of the new object.
Copy construction is really useful in a lot of contexts, but if you
are using polymorphism, you cannot take advantage of polymorphism and
normal assignment of objects.

A clone() function is a function which returns a new type whose type
is only determined at runtime by the actual objects. In order to
accomplish this, you first make a virtual function in the base class:

class Base
{
public:
virtual Base * clone(void)=0;
};

Then you can override it in your derived classes to return the
appropriate type. How you implement it in your derived classes
determines whether it will be a deep copy or a shallow copy. If you
don't know which you want, always err on the side of making a deep
copy.

Then you can call clone() on an object even if you just have a pointer
to the base class. And it will return a correct copy of whatever you
wanted.

Next lession, we'll cover auto_ptr<>. :)

> I gather from corremn's post that there is an easier way to do this
> that doesn't involve all the bloody pointers, but I don't think I
> would be able to do what was described, as I've not the programming
> aptitude to even know where to start implementing such a system.  I'll
> probably stick with what I've got, since I've already gotten most of
> it working except dropping things.

Corremn is advocating that you don't handle things polymorphically. In
this case, there might be a field in your generic 'Item' class which
distinguishes what kind of item it is. Sometimes polymorphism is
better, sometimes adding a distinguishing field is better. It depends
on your overall architecture.

-D

Gelatinous Mutant Coconut

unread,
Oct 3, 2008, 3:41:34 AM10/3/08
to
Thank you for addressing my misinformation! I don't think I ever
really properly understood how .clone() differed from .copy(); I
learned something today. Yay!

zai...@zaimoni.com

unread,
Oct 3, 2008, 11:19:12 PM10/3/08
to

Adapting the way my math AI handles this:
* give Item an abstract method: virtual void CopyInto(Item*& dest)
const = 0;
* Make all terminal subclasses of Item implement CopyInto(Item*& dest)
const; loosely as follows:
** virtual void CopyInto(Item*& Target) const
{::CopyInto(*this,Target);};

where the ::CopyInto references a template function

template<typename T,typename U>
void
CopyInto(const T& src, U*& dest)
{ //! \todo should not be considered if T* is not convertible to U*
if (NULL==dest)
dest = new T(src);
else if (typeid(*dest)==typeid(src))
{
*static_cast<T*>(dest) = src;
}
else{
delete dest;
dest = new T(src);
}
}

(Math AI uses Boost library to get conditional definition of the
template, some namespaces stripped; code example is Boost-licensed
like everything else I write).

This does assume you have working public copy constructors and
assignment operators for all final types. Your Item class only needs
them to be protected members (they should get used by derived classes).

zai...@zaimoni.com

unread,
Oct 3, 2008, 11:22:22 PM10/3/08
to

NO. That didn't work for my math AI in 1997, and it still doesn't
work now.
* The Item class is intentionally abstract, so its copy constructor is
useless (can't be invoked directly, only as part of another
constructor).
* Even if the Item class were not abstract, using Item(const Item&
src) on a Potion will only get an Item, not a Potion: "constructor
slicing"

zai...@zaimoni.com

unread,
Oct 4, 2008, 12:55:58 AM10/4/08
to
On Oct 2, 8:27 pm, Gelatinous Mutant Coconut

<GelatinousMutantCoco...@gmail.com> wrote:
> Nate879 is correct that you want a copy constructor; you specifically
> want a DEEP copy constructer, rather than a shallow one. I believe
> that the deep copy constructor is generally referred to as ".clone()"
> in C++.

While that is the standard name (borrowed from Java), it's not
something you'll find in the C++ STL. It's certainly not a C++ copy
constructor in any sense, as the intent is to fully copy a derived
class into something that only claims to the base class; pointers are
required to make this work.

Google currently refuses to admit sending a post with a pre-Java
implementation of a similar concept that is rendered typesafe by
template functions.

> A shallow copy constructor only copies the reference to the
> object, instead of returning a reference that points to a new copy of
> the data itself.

A shallowly implemented copy constructor needs something like
boost::shared_ptr to make it easy to avoid leaking memory during
execution.

C++ references are the ultimate shallow copy, but have the slight
problem that if the data they're referencing goes out of scope before
they do, the result of further use is as undefined as a dangling
pointer dereference. They also make assignment operators difficult to
implement.

zai...@zaimoni.com

unread,
Oct 5, 2008, 11:22:47 AM10/5/08
to
On Oct 2, 5:33 pm, Fenrir <siliconvik...@gmail.com> wrote:

[Resending, Google appears to have eaten the post]
The copy constructor for Item wouldn't work even for a concrete class,
of course (it slices off the information in Potion proper).

Implementing Java-standard clone() method works, but isn't maximally
memory efficient. What my math AI does is something like:

template<typename T,typename U>
void
CopyInto(const T& src, U*& dest)
{

if (NULL==dest)
dest = new T(src);
else if (typeid(*dest)==typeid(src))
{
*static_cast<T*>(dest) = src;
}
else{
delete dest;
dest = new T(src);
}
}

Then the following member functions. For Item:

virtual void CopyInto(Item*& dest) const = 0;

For terminal subclasses such as Potion:
virtual void CopyInto(Item*& dest) const {::CopyInto(*this,dest);};

Assuming copyright even holds for such a small code snippet, it's
Boost-licensed.

ave

unread,
Oct 7, 2008, 10:36:02 AM10/7/08
to
> Rather than have class Silver_Sword() inherit from Sword() inherit from
> Weapon() inherit from Item(), have a base Item() or even (Weapon :
> public Item) class with slots for each datum.

Composition via containment rather than multiple inheritance is far more
elegant. And your described hierarchy makes no sense in an oop world :)
Though I understand it was just an example.

ave


Timofei Shatrov

unread,
Oct 9, 2008, 9:42:20 AM10/9/08
to
On Fri, 03 Oct 2008 08:34:45 +0800, awhite <spu...@iinet.net.au> tried to
confuse everyone with this message:

I don't know if you can do it in C++, but in Common Lisp it is very easy
to make an object become of another class.

For example in The Rougelike the following code is used to promote "user" to
"admin"

(defmethod do-action (monster (action promote))
(declare (ignore monster))
(when (typep (monster action) 'user)
(change-class (monster action) 'admin)
(out-char (monster action))))

(defmethod update-instance-for-different-class ((prev user) (new admin) &key)
(setf (max-hp new) 10 (hp new) 10 (speed new) 1
(karma new) (if (> (karma prev) 0) (karma prev) 0)
(name new) "admin"
(pro-userbox new) (if (zerop (random 8)) t nil))
(push (make-instance 'banhammer) (inventory new))
(setf (weapon new) (car (inventory new))))


--
|Don't believe this - you're not worthless ,gr---------.ru
|It's us against millions and we can't take them all... | ue il |
|But we can take them on! | @ma |
| (A Wilhelm Scream - The Rip) |______________|

zai...@zaimoni.com

unread,
Oct 9, 2008, 9:54:28 PM10/9/08
to
On Oct 9, 8:42 am, g...@mail.ru (Timofei Shatrov) wrote:
> On Fri, 03 Oct 2008 08:34:45 +0800, awhite <spud...@iinet.net.au> tried to

> >Are you sure that subclassing is the right way to go? Data driven via
> >composition might be better than inheritance. Otherwise, how can you
> >easily implement polymorph effects?
>
> I don't know if you can do it in C++, but in Common Lisp it is very easy
> to make an object become of another class.

You have to set it up carefully, but this can be emulated (and is
*very* convenient when implementing a frame-based AI in C++).

But it's still about ten times as hard in C++ as in LISP, and you'll
*pay* for your design decision the instant you try to write to and
read from a savefile.

Ray Dillinger

unread,
Oct 9, 2008, 11:08:43 PM10/9/08
to
zai...@zaimoni.com wrote:

> On Oct 9, 8:42 am, g...@mail.ru (Timofei Shatrov) wrote:

>> I don't know if you can do it in C++, but in Common Lisp it is very easy
>> to make an object become of another class.

> You have to set it up carefully, but this can be emulated (and is
> *very* convenient when implementing a frame-based AI in C++).

Yeah, I'm doing this in C. Very handy.

Of course, OO is not directly supported by the language, but C
has the very cool property that you can do anything you want,
including implementing your own OO that is better than the C++
version.

C has the formlessness of water. The only high-level language
sharing this virtue (or most of it anyway) is Lisp. It seems
that most language implementors decide that there is a "Right"
way to do everything and then make all other ways hard, or even
impossible. Lisp gives you no choice about garbage collection,
but otherwise C and Lisp pretty much leave the structure of your
code up to you. You can do OO if you want, or ADT, or data-
directed, or anything else that fits your problem. Of course, with
a formless language you have no defense except practice and
experience against your own poor design choices and bad taste,
and there are no interchangeable parts between programs that use
different methodologies.

I implemented an "actor" type to which one can attach arbitrary
attributes (name/value pairs) via a hash table. The "names" are
members of an enum, used to index into the hash table. Open
hashing means there can be multiple, ordered values for any
attribute. Some values are partial closures (OO types would say
"methods") which contain code pointers and parameters. The
"invoke a closure" code looks for the appropriate attribute in
the local object. If found, it gets called, with the partial-closure
parameters and the call parameters. If not, the code looks for an
attribute that identifies a "fallback" actor (OO types would say
"parent class") and looks for the appropriate attribute in that
actor, recursively.

So each class is also an object, and can be dynamically modified
as necessary. Also, each object can be a member of multiple classes.
It is necessary to guard against circular fallback/inheritance
patterns, but that's true of all OO.

If I want something to "change classes" all I have to do is reset
the fallback attribute. The object keeps its individual attributes,
but for anything not attached to the individual object, of course
any access or call to it goes to the new fallback.

Bear

awhite

unread,
Oct 10, 2008, 12:15:11 AM10/10/08
to
On Thu, 09 Oct 2008 20:08:43 -0700, Ray Dillinger wrote:

> Of course, OO is not directly supported by the language, but C has the
> very cool property that you can do anything you want, including
> implementing your own OO that is better than the C++ version.

You can.... but you have to forego type safety, by relying on the "struct
hack" or void * pointers everywhere. That's what made me abandon my C
design for C++ (and then again for the purity of LISP).

[ similarities between the "formlessness" of C and LISP ]

> You can do OO if you want, or ADT, or data- directed, or anything else
> that fits your problem. Of course, with a formless language you have no
> defense except practice and experience against your own poor design
> choices and bad taste, and there are no interchangeable parts between
> programs that use different methodologies.

But a few choice (defmacro)s or (define-symbol-macro)s and you can paper
over the more egregious deficiencies in your design. I can't see how you
can do that quite as easily in plain C.

[ cool hashtable based property design ]

> If I want something to "change classes" all I have to do is reset the
> fallback attribute. The object keeps its individual attributes, but for
> anything not attached to the individual object, of course any access or
> call to it goes to the new fallback.

Sounds very much like PG's OO re-design in the book "ANSI Common Lisp"; he
used hashtables indexed by :keyword attributes, implemented in less than
20 lines of code all up.

Adam

PS. Bear, what happened with your new language design? A few years ago you
announced here that you would be absent for a long while, designing a
new-ish language feature atop Scheme. Did it ever eventuate?

A

awhite

unread,
Oct 10, 2008, 12:41:31 AM10/10/08
to
On Thu, 09 Oct 2008 13:42:20 +0000, Timofei Shatrov wrote:

>
> I don't know if you can do it in C++, but in Common Lisp it is very easy
> to make an object become of another class.
>
> For example in The Rougelike the following code is used to promote "user" to
> "admin"

[ snip MOP juiciness ]

Well, in C++ you can't do it portably. If you're prepared to sell your
soul to the portability devil, you can do it for heap-based objects. It
requires a bit of set-up... last member of the old class has to use the
struct hack, and you need to create objects by malloc()ing them using the
placement constructor, instead of the more common "new()" syntax. Then
explicitly invoke the destructor, realloc() the size, and use the
placement constructor of the new class.

It's very brittle, and relies on the C++ implementation using vtables for
dynamic dispatch, as well as realloc() not moving the memory block.

Plus it's dubious if you can even discover if an object was allocated on
the heap or the stack!

In short, Don't Do That. If you need to redefine classes, use a different
language (plug: Common Lisp's CLOS is the most powerful OO system around),
or redesign.

Adam

Ray Dillinger

unread,
Oct 10, 2008, 4:06:01 AM10/10/08
to
awhite wrote:


> PS. Bear, what happened with your new language design? A few years ago you
> announced here that you would be absent for a long while, designing a
> new-ish language feature atop Scheme. Did it ever eventuate?

It did. It runs and it's very cool, but it's slow and a memory hog.
There are years of work, I can see, in making it faster and making
it "complete" enough (system libraries, etc) for Real Programmers
to work with.

It's not "atop Scheme" anymore, the interpreter is in C. If I ever
get around to adding some libraries and things it may be releasable.

Briefly; it's a Lisp-1 like Scheme, except that functions are both
first-class (like traditional Lisp functions) and first-order (like
traditional lisp macros). This was done by messing with its argument
passing discipline. Each call is annotated with a set of environments (
up to one per argument plus one for the call site) and each argument
"value" is replaced with a struct of expression pointer (a pointer
into the data representation of the argument expression) plus
indicator of what environment to use in evaluating the argument,
like in a lazy language. The exception of course is that arguments
which are already of the expression-environment pair type don't get
a new environment.

This is enough information for the called routine to evaluate the
arguments under its own control -- zero times, once, or multiple
times. Or take them apart and access the expressions unevaluated
or access environments and do side effects, etc.

The result is that macrology and syntactic closures are completely
unnecessary. When each routine has complete control over how its
subexpressions are evaluated and access to their environments, you
can do as a function anything that normal lisps handle as syntax
or a macro. So my new lisp unifies syntactic and functional
abstraction into a single construct. Functional abstractions
like "apply" are now able to handle things normally implemented
as syntax, like "lambda" and "and". And functions are first-
class entities. You can store them in structures or variables,
pass them as arguments, return them as function results, etc -
all the things you "just can't do" with macros in Lisps.

The first thing I did was to implement a "defmacro" function
that duplicates the defmacro in Common Lisp, to show that the
new system has (at least) the power of defmacro. The difference
is that the entities returned from it, while they do the same kind
of syntactic abstraction as the macros they replace, are functions,
and therefore first-class entities, not macros.

Bear


awhite

unread,
Oct 10, 2008, 4:32:54 AM10/10/08
to
On Fri, 10 Oct 2008 01:06:01 -0700, Ray Dillinger wrote:

[ Ray's new PTL (Pet Toy Language) ]

> Briefly; it's a Lisp-1 like Scheme, except that functions are both
> first-class (like traditional Lisp functions) and first-order (like
> traditional lisp macros). This was done by messing with its argument
> passing discipline. Each call is annotated with a set of environments (
> up to one per argument plus one for the call site) and each argument
> "value" is replaced with a struct of expression pointer (a pointer
> into the data representation of the argument expression) plus
> indicator of what environment to use in evaluating the argument,
> like in a lazy language. The exception of course is that arguments
> which are already of the expression-environment pair type don't get
> a new environment.

Cheers! Whoah, it went over my head when you announced it, and it's gone
over my head now. I vaguely remember at the time you saying that it would
collapse the difference between read-time, compile-time, and run-time into
one abstraction, but I can't see the necessary point.

If defmacro and friends (macrolet, define-symbol-macro, symbol-macrolet,
lambda, let*, and so forth) give you that power, why collapse them into
just the one - lambda (the ultimate)? I'm not a very good programmer, so
it's probably beyond me.

Thanks for the update - when you reappeared a while ago, I had wondered
how you got on.

Adam


Radomir Dopieralski

unread,
Oct 10, 2008, 4:51:47 AM10/10/08
to
At Fri, 10 Oct 2008 01:06:01 -0700, Ray Dillinger wrote:

> Briefly; it's a Lisp-1 like Scheme, except that functions are both
> first-class (like traditional Lisp functions) and first-order (like
> traditional lisp macros). This was done by messing with its argument
> passing discipline. Each call is annotated with a set of environments (
> up to one per argument plus one for the call site) and each argument
> "value" is replaced with a struct of expression pointer (a pointer
> into the data representation of the argument expression) plus
> indicator of what environment to use in evaluating the argument,
> like in a lazy language. The exception of course is that arguments
> which are already of the expression-environment pair type don't get
> a new environment.

Somehow it vaguely resembles what TCL is doing in a much more primitive
way...

--
Radomir Dopieralski, http://sheep.art.pl

Ray Dillinger

unread,
Oct 10, 2008, 3:06:13 PM10/10/08
to
awhite wrote:

> Cheers! Whoah, it went over my head when you announced it, and it's gone
> over my head now. I vaguely remember at the time you saying that it would
> collapse the difference between read-time, compile-time, and run-time into
> one abstraction, but I can't see the necessary point.

Well, principally it happens because macros are traditionally
static - ie, they can't change during the program run. This
introduces a separation of phases, where the code that runs at
readtime figures out what the program text (after macroexpansion)
is. That no longer makes sense in a universe where there are no
such thing as static macros; there are first-order functions
that can do the same sort of thing, but they are also first-
class entities, and therefore mutable during runtime.

So. What's normally done at readtime is to macroexpand the
program into a static, macro-less form - a step unnecessary
because I have no static macros as such.

What's normally done at compile time is to generate code for
the macroexpanded program - a step impossible because the
first-order processing inside functions - the stuff that in
macros would be rearranging your static code - can be changed
on the fly by substituting an altered function definition
during the program run.

So - semantically speaking, everything happens at runtime.
It may be possible to do compilation as such, but I don't
at this time, and if I did the mathematics just doesn't allow
me to hang any semantic distinctions on it.

> If defmacro and friends (macrolet, define-symbol-macro, symbol-macrolet,
> lambda, let*, and so forth) give you that power, why collapse them into
> just the one - lambda (the ultimate)? I'm not a very good programmer, so
> it's probably beyond me.

Heh. I (re)define these things in terms of lambda in order
to prove a point about the power of the new "lambda" construct.
For actual use, I want them separate, for the same reason Scheme
already uses such syntactic sugar as "let" etc, instead of just
using lambda for everything.

Also, practically speaking, there's power they don't give me.
The macros defined by these traditional constructs are not
first-class entities. You can't store them in structures and
variables, nor give them as arguments to functions that expect
functional arguments, nor create them on the fly, nor mutate
their values once defined, etc.... and in the new toylisp, you
can do all of those things.

Finally, the point of collapsing everything onto "lambda" is,
sort-of, about the math. The toylisp is a sort of experiment -
an extreme case of semantic reduction of *everything* in a lisp
to lambda calculus, where first-order constructs like macros
were one of the big chunks that had never been amenable to such
treatment before.

Bear

zai...@zaimoni.com

unread,
Oct 10, 2008, 10:01:12 PM10/10/08
to
On Oct 9, 11:41 pm, awhite <spud...@iinet.net.au> wrote:

> Well, in C++ you can't do it portably. If you're prepared to sell your
> soul to the portability devil, you can do it for heap-based objects. It
> requires a bit of set-up... last member of the old class has to use the
> struct hack, and you need to create objects by malloc()ing them using the
> placement constructor, instead of the more common "new()" syntax. Then
> explicitly invoke the destructor, realloc() the size, and use the
> placement constructor of the new class.
>
> It's very brittle, and relies on the C++ implementation using vtables for
> dynamic dispatch, as well as realloc() not moving the memory block.

Why not just use placement new/delete on malloc'ed memory to both get
rid of the struct hack, and insulate the whole strategy from whether
vtables are used (and whether realloc relocates the memory block)?

> Plus it's dubious if you can even discover if an object was allocated on
> the heap or the stack!

True, but not a problem in practice.

Adam White

unread,
Oct 10, 2008, 11:48:11 PM10/10/08
to
On Fri, 10 Oct 2008 12:06:13 -0700, Ray Dillinger wrote:

[Ray's PTL]

> So - semantically speaking, everything happens at runtime. It may be
> possible to do compilation as such, but I don't at this time, and if I
> did the mathematics just doesn't allow me to hang any semantic
> distinctions on it.

Ah. Well, since I know you're a big fan of C - can you give up the raw
speed advantage of compilation? I know it's a PTL, but for most
programmers it's hard to *stop* thinking about efficiency!


> Finally, the point of collapsing everything onto "lambda" is, sort-of,
> about the math. The toylisp is a sort of experiment - an extreme case
> of semantic reduction of *everything* in a lisp to lambda calculus,
> where first-order constructs like macros were one of the big chunks that
> had never been amenable to such treatment before.

I remember hearing that McCarthy originally had more work to do on lisp
(M-expressions, and the like); it sounds like you're going back to some
of Church's basics.

Actually that's pretty cool. Scary in scope, but cool.

Having such power (argument to a function could potentially affect the
order-of-evaluation of that function) sounds like an awful lot to keep in
your head at once when programming. Of course you Don't Need To Abuse It,
but it sounds a bit like Perl-ish exercise in write-only.

Way too hardcore for me! I really wish you well in your endeavour.

Adam

Adam White

unread,
Oct 10, 2008, 11:53:36 PM10/10/08
to
On Fri, 10 Oct 2008 19:01:12 -0700, zaimoni wrote:

[on nasty tricks to redefine objects]

> Why not just use placement new/delete on malloc'ed memory to both get
> rid of the struct hack, and insulate the whole strategy from whether
> vtables are used (and whether realloc relocates the memory block)?

Placement new/delete is what I was thinking of. realloc() changing the
memory address seems to be an insurmountable problem. If it's not a
problem, it's probably because you only have a single pointer to that
object. If that's the case, no trickery is required - just delete() and
new() again.

Vtables are the only currently-used method (IIRC), but they're required
for this nasty trick to work. Otherwise there's no way around it if the
C++ implementation theoretically stored memory addresses for look-ups in
some user-inaccessible place.

Adam

Ray Dillinger

unread,
Oct 11, 2008, 12:45:35 PM10/11/08
to
Adam White wrote:

> Actually that's pretty cool. Scary in scope, but cool.

You're getting it. I was going for "scary in scope..." It's what
I do on my mad-science weekends these days.


> Having such power (argument to a function could potentially affect the
> order-of-evaluation of that function) sounds like an awful lot to keep in
> your head at once when programming. Of course you Don't Need To Abuse It,
> but it sounds a bit like Perl-ish exercise in write-only.

Heh. "You don't need to abuse it" may need to become the catchphrase
for this language. Being almost *completely* formless, it allows you
to do things regardless of whether or not they are bad ideas. I like
that, but then I'm crazy.

But I do have a specific application in mind. It's a very easy
automatic-translation target, ideally suited to being a "computer
interlanguage" - an environment in which code originally written
in many different languages can interoperate successfully.

Bear

0 new messages