pointers-to-members, (custom) array implementation

Wolfgang Draxinger wdraxinger at darkstargames.de
Wed Feb 7 17:25:18 PST 2007


I'm currently in a discussion D vs. C++ and in some points I must
admit, that the other side has some point, I must admit, too:

Pointers to members are a feature that D currently lacks, but
sometimes they are quite usefull and can't be replaced by
delegates.

E.g. say you got a class "foo" which has several member functions
of the same prototype and several member variables. Now you want
to write some function that performs some action on the class,
but is not fixed on certain members. That function would accept
the instance of a class, and pointers-to-members as parameters.
The whole thing could be used in a foreach loop.

For the member variables one could use the .offset property to
supply their offset address relative to the class instance
address, but for member functions emulating such functionality
would be quite a hack. I think that pointers-to-members are as
important as delegates.

Another point they claim is, that having the full implementation
for dynamic and associative arrays within the _language itself_
is not clean. Instead the language should define a universal
interface for dynamic array classes.

I'm thinking of the following ways, how to specify a custom array
class implementation:

type[] foo = new!(My_Array) type[];

i.e. new is a template creating a instance of My_Array, calling
that's constructor with the apropriate values (I come to that
later). Normally new defaults to the builtin class Array (just
like Object is a builtin class). I don't have a clear idea yet
how this should interface with class allocators, but one
solution would be, that a custom array class should use that
allocator for memory allocation instead of the array class
default's. But an array class should be allowed to override that
request.

Since pragmatic programmers are lazy programmers it should also
be possible to define a array class for a whole scope. A pragma
that affects the current scope and all subscope would be fine,
e.g.

pragma([], My_Array);

if a custom array class is desired only for specific types

pragma(type[], My_TypeArray);

And of course multiple pragmas should be possible, as long the
override is not ambigous. However within one scope only one
pragma per Type is allowed, but a subscope may override again. 

This is a point where pointers-to-members would come in handy:
Since a resizing of the array possibly requires constructors to
be called the C++ approach on dynamic arrays is templates.
But actually managing an array one requires the size of each
element, stride, length and amount of allocated memory. Only
constructor and destructor calling is a type specific. But here
can pointers-to-members help. For each class a pointer-to-member
to the constructor function is created. A Array class then can
use that pointer to member to call the constructor for newly
allocated classes. Of course only the (eventually not present)
default constructor would be called, after the elements contents
would have been initialized.

A custom Array class constructor must accept the following
parameters then: The beginning length of the array, the total
amount of memory to allocate (this is to allow the compiler to
tell the Array class, that more memory is needed for elements
appended in the following code), a pointer to TypeInfo, a
pointer to ClassInfo; the ClassInfo has the pointer-to-member of
the constructor and the initializer data, the TypeInfo contains
information about size and alignment for one element.

To designate pointer-to-members the following might be
apropriate:

class foo
{
        int bar;
        char*[] spam;
        void eggs(int);
};

foo.*int a; // a is a pointer to a int member of foo
foo.*char*[] b; // b is a pointer to an array of pointers to char
member of foo
foo.*void function(int) c; // c is a pointer to a member function
taking an int

To get a pointer-to-member the following syntax might be usable

a = foo.&bar;
b = foo.&spam;
c = foo.&eggs;

Using the pointers-to-members is consistent with normal pointer
syntax:
Normal pointers are designated by '*' and dereferenced by '*',
too. The address is retrieved by '&'. By prepending the member
operator '.' ".*" can be read right to left (which is the way
types get defined) as "pointer (to) member", and also
as "dereference member". Similairily ".&" can be read as "get
address of member". So dereferencing would be written as:

foo TheFoo = new foo;
TheFoo.*a = 5;
printf(TheFoo.*b[3]);
TheFoo.*c(10);

which is consistent with C++ syntax.

Wolfgang Draxinger
-- 
E-Mail address works, Jabber: hexarith at jabber.org, ICQ: 134682867




More information about the Digitalmars-d mailing list