Thread overview | |||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
February 08, 2007 pointers-to-members, (custom) array implementation | ||||
---|---|---|---|---|
| ||||
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@jabber.org, ICQ: 134682867
|
February 08, 2007 Re: pointers-to-members, (custom) array implementation | ||||
---|---|---|---|---|
| ||||
Posted in reply to Wolfgang Draxinger | "Wolfgang Draxinger" <wdraxinger@darkstargames.de> wrote in message news:uoaq94-f0c.ln1@darkstargames.dnsalias.net... > 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. > It's not quite as elegant as pointers-to-members, but it's something: class A { static A members; static this() { members = new A(); } int fork() { return 1; } int knife() { return 2; } } void func(A a, int delegate()[] members...) { foreach(member; members) { member.ptr = a; writefln(member()); } } void main() { A a = new A(); func(a, &A.members.fork, &A.members.knife); } The ugly parts are (1) having to create an instance of A in order to get the offsets of the member functions (the static 'members' variable) and (2) having to set the delegate .ptr before calling it, rather than just calling something like "a.*member()". I think pointers-to-members have a bad reputation but I agree that they can be really useful in some cases. |
February 08, 2007 Re: pointers-to-members, (custom) array implementation | ||||
---|---|---|---|---|
| ||||
Posted in reply to Wolfgang Draxinger | Wolfgang Draxinger wrote:
> 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.
>
I'd have to check but I think this works
class C
{
int one(int i){...}
int two(int i){...}
int three(int i){...}
}
int Pt2Mem!(alias go)(C c, int i)
{
return c.go(i);
}
auto fn = &Pt2Mem!(one);
C c = new C;
c.fn(1);
The tail recursion should get optimized away and maybe even the first call in some cases.
|
February 08, 2007 Re: pointers-to-members, (custom) array implementation | ||||
---|---|---|---|---|
| ||||
Posted in reply to BCS | "BCS" <BCS@pathlink.com> wrote in message news:eqe3gf$159e$4@digitaldaemon.com... > I'd have to check but I think this works > > class C > { > int one(int i){...} > int two(int i){...} > int three(int i){...} > } > > int Pt2Mem!(alias go)(C c, int i) > { > return c.go(i); > } > > > auto fn = &Pt2Mem!(one); > > C c = new C; > > c.fn(1); Nope. Not only can you not use alias parameters in that way (they have to be a declared symbol, not just an arbitrary identifier), but you also can't call "obj.anything()" like "c.fn(1)". I was hoping it might be possible to use mixin() to create an arbitrary identifier, but unfortunately: c.mixin("one")(i); doesn't compile. |
February 08, 2007 Re: pointers-to-members, (custom) array implementation | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jarrett Billingsley | Jarrett Billingsley wrote:
> I was hoping it might be possible to use mixin() to create an arbitrary identifier, but unfortunately:
>
> c.mixin("one")(i);
>
> doesn't compile.
Try these (off the top of my head):
---
int Pt2Mem!(char[] go)(C c, int i)
{
return mixin("c." ~ go ~ "(i)");
}
---
or:
---
int Pt2Mem!(char[] go)(C c, int i)
{
mixin("return c." ~ go ~ "(i)");
}
---
IIRC you can only mixin complete expressions, statements or declarations[1]. Declarations aren't really useful here, but expressions and statements should work...
[1]: Well, and templates, but that doesn't work with strings.
|
February 08, 2007 Re: pointers-to-members, (custom) array implementation | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jarrett Billingsley | Jarrett Billingsley wrote: > It's not quite as elegant as pointers-to-members, but it's something: > > [some hack] I didn't neglegt the possibility of some hack to get the very same behaviour. The most ugly variant is possibly the following (and that works only in some ABI): class Foo { int bar(char[]); }; int function(Foo*, char[]) spam; spam = cast(int function(Foo*, char[])cast(void*)&Foo.bar; void eggs() { Foo foobar = new Foo; spam(&foobar, "spam'n'eggs"); } In some ABIs this actually works, and it't e.g. the way one uses DirectX from languages, that don't have interfaces/classes. But it's ugly and D is all about elegance IMHO. So I think as of one the next versions D should pointer to members. Those don't break anything, the language remains orthogonal (since PtM fill a gap, that one currently has to fill with a hack). There are also other things _I_ miss and that woudn't really hurt, to have in D. For example bitfields. Currently the best way to emulate them is a struct having some static array of required length and properties to get/set the values. Eventually a new stucture type "bitfield" may be introduced with some special rules. E.g. within a bitfield there can be declared no variables by basic types. One can only specify if a certain variable is to be treated signed or unsigned and the amount of bits it consumes. And there should be some way to define padding. Bitfields should align and the gap to the next alignment boundary should be consumed, too. And bitfields cannot stand for themself and must always be embedded into a struct or union. e.g. struct bar { bitfield { unsigned flags: 5; signed offset: 4; void: 5; // pad 5 bits. unsigned count: 15; }; // assume a 4 byte aligning, the bitfield // will consume another 3 bits to fill up }; Since nesting rules apply the members of the annonymous nested bitfield can be accessed like bar.flags = 1 | 2 | 8; bar.offset = -10; bar.count = 10000; There are some libraries, e.g. SDL, that use bitfields extensivly. Having them in the language would be very, very nice. I'm not about featureitis, but elegance. As soon as a language requires me to use some hack, to achieve my goal it doesn't seem elegant to me. Wolfgang Draxinger -- E-Mail address works, Jabber: hexarith@jabber.org, ICQ: 134682867 |
February 08, 2007 Re: pointers-to-members, (custom) array implementation | ||||
---|---|---|---|---|
| ||||
Posted in reply to Frits van Bommel | "Frits van Bommel" <fvbommel@REMwOVExCAPSs.nl> wrote in message news:eqess1$2ito$1@digitaldaemon.com... > > Try these (off the top of my head): > --- > int Pt2Mem!(char[] go)(C c, int i) > { > return mixin("c." ~ go ~ "(i)"); > } This one works just fine :) > or: > --- > int Pt2Mem!(char[] go)(C c, int i) > { > mixin("return c." ~ go ~ "(i)"); > } > --- This one has to have a semicolon at the end of the string, since the mixin statement strings have to be complete statements. But this works too. |
February 08, 2007 Re: pointers-to-members, (custom) array implementation | ||||
---|---|---|---|---|
| ||||
Posted in reply to BCS | BCS wrote: > I'd have to check but I think this works > > class C > { > int one(int i){...} > int two(int i){...} > int three(int i){...} > } > > int Pt2Mem!(alias go)(C c, int i) > { > return c.go(i); > } > > > auto fn = &Pt2Mem!(one); > > C c = new C; > > c.fn(1); Works but is IMHO not very elegant. Make PtM a language feature and it's get a lot more readable. I don't like it. Wolfgang Draxinger -- E-Mail address works, Jabber: hexarith@jabber.org, ICQ: 134682867 |
February 08, 2007 Re: pointers-to-members, (custom) array implementation | ||||
---|---|---|---|---|
| ||||
Posted in reply to Wolfgang Draxinger | Wolfgang Draxinger wrote:
> BCS wrote:
>
>> I'd have to check but I think this works
>>
>> class C
>> {
>> int one(int i){...}
>> int two(int i){...}
>> int three(int i){...}
>> }
>>
>> int Pt2Mem!(alias go)(C c, int i)
>> {
>> return c.go(i);
>> }
>>
>>
>> auto fn = &Pt2Mem!(one);
>>
>> C c = new C;
>>
>> c.fn(1);
>
> Works but is IMHO not very elegant. Make PtM a language feature
> and it's get a lot more readable. I don't like it.
No, it does not work. &Pt2Mem!(one) gives a 'what's "one"?' error.
And the c.fn part gives a 'no such member "fn"' error.
But if it did work I think it would be a pretty reasonably elegant way to do it. :-)
--bb
|
February 08, 2007 Re: pointers-to-members, (custom) array implementation | ||||
---|---|---|---|---|
| ||||
Posted in reply to Jarrett Billingsley | Jarrett Billingsley wrote: > "Frits van Bommel" <fvbommel@REMwOVExCAPSs.nl> wrote in message news:eqess1$2ito$1@digitaldaemon.com... >> Try these (off the top of my head): [snip first] > > This one works just fine :) > >> or: [snip second] > > This one has to have a semicolon at the end of the string, since the mixin statement strings have to be complete statements. But this works too. You read that first sentence, right? (especially the parenthesized part) That was a warning I didn't actually compile these :P. |
Copyright © 1999-2021 by the D Language Foundation