## digitalmars.D - class design question (inner classes)

- coxalan <coxalan web.de> Sep 11 2007
- Regan Heath <regan netmail.co.nz> Sep 11 2007
- coxalan <coxalan web.de> Sep 11 2007
- Regan Heath <regan netmail.co.nz> Sep 11 2007
- coxalan <coxalan web.de> Sep 11 2007
- Regan Heath <regan netmail.co.nz> Sep 11 2007
- Regan Heath <regan netmail.co.nz> Sep 11 2007
- Downs <default_357-line yahoo.de> Sep 11 2007
- Downs <default_357-line yahoo.de> Sep 11 2007
- coxalan <coxalan web.de> Sep 11 2007
- Bruno Medeiros <brunodomedeiros+spam com.gmail> Sep 12 2007
- Christian Kamm <kamm.incasoftware shift-at-left-and-remove-this.de> Sep 11 2007
- Roberto Mariottini <rmariottini mail.com> Sep 11 2007
- coxalan <coxalan web.de> Sep 11 2007

Hello, I come from mathematics and I want to create some classes for _fast_ computations in certain algebraic structures (groups, fields, rings, ...). My hope is that specialization at compile time will give me an advantage over universal computer algebra systems. I want to have: 1) Objects for the algebraic base structures 2) Some type (class, struct, whatever) for the elements of this structure For example: 1) a class SymmetricGroup and 2) a class Permutation. Each instance of type 2) "belongs" to exactly one object of class 1). But this link should not be stored for each instance of 2). Instead, It should be given statically by the type. Furthermore, for convenience I really want to have overloaded operators for the objects of 2). Up to now I tried these two examples: *** 1 *** import std.stdio; class SymmetricGroup { const uint degree; this(int degreeIn) { degree = degreeIn; } struct Permutation { uint data[]; } Permutation createPermutation(uint[] dataIn) { Permutation result; result.data = dataIn; return result; } Permutation mul(Permutation a, Permutation b) { Permutation result; result.data.length = degree; foreach(int i, uint val; b.data) { result.data[i] = a.data[val]; } return result; } } void main() { SymmetricGroup s = new SymmetricGroup(3); s.Permutation a = s.createPermutation([0,2,1]); s.Permutation b = s.createPermutation([1,2,0]); s.Permutation c = s.mul(a,b); writefln("%d",c.data); } ******** The problem with this is that I don't know how to overload the *-Operator such that I can write s.Permutation c = a * b; instead of s.Permutation c = s.mul(a,b); I cannot add a method "opMul" within the struct "Permutation", because to call the method "mul" I would need a pointer to the outer class "SymmetricGroup", which I don't have. [Comment: Yes, I could write Permutation opMul(Permutation x) { Permutation result; result.data.length = data.length; foreach(int i, uint val; x.data) { result.data[i] = data[val]; } return result; } in this particular example, but in general I need information stored in the outer class 1).] *** 2 *** import std.stdio; class SymmetricGroup { const uint degree; this(int degreeIn) { degree = degreeIn; } class Permutation { uint data[]; this() { data.length = degree; } this(uint[] dataIn) { data = dataIn; } Permutation opMul(Permutation x) { return mul(this, x); } } Permutation mul(Permutation a, Permutation b) { Permutation result = new Permutation(); foreach(int i, uint val; b.data) { result.data[i] = a.data[val]; } return result; } } void main() { SymmetricGroup s = new SymmetricGroup(3); s.Permutation a = s.new Permutation([0,2,1]); s.Permutation b = s.new Permutation([1,2,0]); s.Permutation c = a * b; writefln("%d",c.data); } ******** This is definitely more elegant than the first example. But here, every object of type "s.Permutation" stores a reference to the object "s", which I want to avoid. If it was possible to instantiate the object "s" at compile time (something like static const SymmetricGroup s = new SymmetricGroup(3); ), the above mentioned references would not be needed. But unfortunately, It is not possible to instantiate objects at compile time (what's actually the reason for this?). [A final comment: I know that I could go without the class SymmetricGroup, and have only objects of type Permutation. But I really want these outer classes, to do constructions like direct products of groups, etc.] I believe that within the D template toolkit, there must be a solution for my problem, which I simply don't see. I will appreciate any hint or suggestion.

Sep 11 2007

coxalan wrote:Hello, I come from mathematics and I want to create some classes for _fast_ computations in certain algebraic structures (groups, fields, rings, ...). My hope is that specialization at compile time will give me an advantage over universal computer algebra systems. I want to have: 1) Objects for the algebraic base structures 2) Some type (class, struct, whatever) for the elements of this structure For example: 1) a class SymmetricGroup and 2) a class Permutation. Each instance of type 2) "belongs" to exactly one object of class 1). But this link should not be stored for each instance of 2). Instead, It should be given statically by the type. Furthermore, for convenience I really want to have overloaded operators for the objects of 2). Up to now I tried these two examples: *** 1 *** import std.stdio; class SymmetricGroup { const uint degree; this(int degreeIn) { degree = degreeIn; } struct Permutation { uint data[]; } Permutation createPermutation(uint[] dataIn) { Permutation result; result.data = dataIn; return result; } Permutation mul(Permutation a, Permutation b) { Permutation result; result.data.length = degree; foreach(int i, uint val; b.data) { result.data[i] = a.data[val]; } return result; } } void main() { SymmetricGroup s = new SymmetricGroup(3); s.Permutation a = s.createPermutation([0,2,1]); s.Permutation b = s.createPermutation([1,2,0]); s.Permutation c = s.mul(a,b); writefln("%d",c.data); } ******** The problem with this is that I don't know how to overload the *-Operator such that I can write s.Permutation c = a * b; instead of s.Permutation c = s.mul(a,b); I cannot add a method "opMul" within the struct "Permutation", because to call the method "mul" I would need a pointer to the outer class "SymmetricGroup", which I don't have. [Comment: Yes, I could write Permutation opMul(Permutation x) { Permutation result; result.data.length = data.length; foreach(int i, uint val; x.data) { result.data[i] = data[val]; } return result; } in this particular example, but in general I need information stored in the outer class 1).] *** 2 *** import std.stdio; class SymmetricGroup { const uint degree; this(int degreeIn) { degree = degreeIn; } class Permutation { uint data[]; this() { data.length = degree; } this(uint[] dataIn) { data = dataIn; } Permutation opMul(Permutation x) { return mul(this, x); } } Permutation mul(Permutation a, Permutation b) { Permutation result = new Permutation(); foreach(int i, uint val; b.data) { result.data[i] = a.data[val]; } return result; } } void main() { SymmetricGroup s = new SymmetricGroup(3); s.Permutation a = s.new Permutation([0,2,1]); s.Permutation b = s.new Permutation([1,2,0]); s.Permutation c = a * b; writefln("%d",c.data); } ******** This is definitely more elegant than the first example. But here, every object of type "s.Permutation" stores a reference to the object "s", which I want to avoid. If it was possible to instantiate the object "s" at compile time (something like static const SymmetricGroup s = new SymmetricGroup(3); ), the above mentioned references would not be needed. But unfortunately, It is not possible to instantiate objects at compile time (what's actually the reason for this?). [A final comment: I know that I could go without the class SymmetricGroup, and have only objects of type Permutation. But I really want these outer classes, to do constructions like direct products of groups, etc.] I believe that within the D template toolkit, there must be a solution for my problem, which I simply don't see. I will appreciate any hint or suggestion.

Maybe template bolt-ins? (I'm not 100% sure I know what you want to achieve so this may be waaaay off base) class SymmetricGroup { uint degree; this(int degreeIn) { degree = degreeIn; } } class Permutation(T, int D) : T { uint data[]; this() { super(D); data.length = degree; } this(uint[] dataIn) { super(D); data = dataIn; } Permutation opMul(Permutation b) { auto result = new Permutation; foreach(int i, uint val; b.data) { result.data[i] = data[val] * b.data[val]; } return result; } } alias Permutation!(SymmetricGroup, 3) SGP; void main() { SGP a = new SGP(); SGP b = new SGP(); SGP c = a * b; } In the above you don't actually need SymmetricGroup at all, your template could just take "int D" and use that for degree, however I suspect you want to add more to SymmetricGroup? Regan

Sep 11 2007

Regan Heath Wrote:Maybe template bolt-ins? (I'm not 100% sure I know what you want to achieve so this may be waaaay off base) class SymmetricGroup { uint degree; this(int degreeIn) { degree = degreeIn; } } class Permutation(T, int D) : T { uint data[]; this() { super(D); data.length = degree; } this(uint[] dataIn) { super(D); data = dataIn; } Permutation opMul(Permutation b) { auto result = new Permutation; foreach(int i, uint val; b.data) { result.data[i] = data[val] * b.data[val]; } return result; } } alias Permutation!(SymmetricGroup, 3) SGP; void main() { SGP a = new SGP(); SGP b = new SGP(); SGP c = a * b; }

With your code, the uint-value "degree" gets stored once for every instance of SGP. This is not what I want. The class SymmetricGroup should collect everything that all of its elements (the object of the class Permutation) have in common. Just imagine the same for a class TableGroup, where the multiplication-values are stored in an huge array. This table definitely should not be stored for each single element of the group.In the above you don't actually need SymmetricGroup at all, your template could just take "int D" and use that for degree, however I suspect you want to add more to SymmetricGroup?

Yes, of course this is only an example which I made as short as possible. There are two reasons for this class [these are the classes of kind 1) in my first posting]: * I want to implement more kinds of groups, for example also matrix groups. All the groups should have a common interface which will be used in templated group algorithms. * There are constructions which combine two or more groups into a new one. For the implementation of such combined groups, It will be very convenient to have classes of this kind 1). coxalan

Sep 11 2007

With your code, the uint-value "degree" gets stored once for every instance of SGP. This is not what I want.

Make it "static" or, make it a template parameter and just use that, eg. class SymmetricGroup { } class Permutation(T, int D) : T { uint data[]; this() { data.length = D; } this(uint[] dataIn) { data = dataIn; } Permutation opMul(Permutation b) { auto result = new Permutation; foreach(int i, uint val; b.data) { result.data[i] = data[val] * b.data[val]; } return result; } } alias Permutation!(SymmetricGroup, 3) SGP; void main() { SGP a = new SGP(); SGP b = new SGP(); SGP c = a * b; }The class SymmetricGroup should collect everything that all of its elements (the object of the class Permutation) have in common.

Like .. (list please). Are they all data members or methods as well. Have you considered using mixins to mix a SymmetricGroup template containing methods/data members into Permutation.In the above you don't actually need SymmetricGroup at all, your template could just take "int D" and use that for degree, however I suspect you want to add more to SymmetricGroup?

Yes, of course this is only an example which I made as short as possible.

Sure, but a short example can make it hard (for us/me) to see the whole picture. :) Especially when my formal maths education stopped at high school level.There are two reasons for this class [these are the classes of kind 1) in my first posting]: * I want to implement more kinds of groups, for example also matrix groups. All the groups should have a common interface which will be used in templated group algorithms. * There are constructions which combine two or more groups into a new one. For the implementation of such combined groups, It will be very convenient to have classes of this kind 1).

Ok. I think I have reached the limit of my usefulness here :) Someone else is bound to have some ideas. Regan

Sep 11 2007

Regan Heath Wrote:> The class SymmetricGroupshould collect everything that all of its elements (the object of the class Permutation) have in common.

Like .. (list please). Are they all data members or methods as well.

Definitely methods, like uint order() which returns the number of the elements of the group. For the SymmetricGroup there are no other data members, but for other Groups there could be arbitrary data members (like a big array where the multiplication results are stored).[...] Ok. I think I have reached the limit of my usefulness here :) Someone else is bound to have some ideas.

It was my fear that I put too much technical mathematics stuff into my posting. I already put this question (in C++ form) into a mathematical internet forum, but then the OOP-design part of the discussion got stuck at a very basic level... :-) Now I rethought my question and how I can put it on a more abstract level to get out the math part. I guess my problem comes down to this point: class Outer { class Inner { [...] } [...] } main() { A a = new A(); [... initialize many a.Inner objects here ...] } Once that "a" is initialized, te reference "a" stays constant all the time. So all further references/pointers to "a" are redundant. Especially, the references to "a" stored in "a.Inner" inner objects are redundant. So if there was a way to tell the compiler that the reference a will never change, the compiler could do the optimization and skip all these inner-class-references. Now the question is: Is there currently a way to achieve a satisfying, equivalent result? If not: Should I make this a feature request? coxalan

Sep 11 2007

coxalan wrote:Regan Heath Wrote:The class SymmetricGroup should collect everything that all of its elements (the object of the class Permutation) have in common.

well.

Definitely methods, like uint order() which returns the number of the elements of the group. For the SymmetricGroup there are no other data members, but for other Groups there could be arbitrary data members (like a big array where the multiplication results are stored).[...] Ok. I think I have reached the limit of my usefulness here :) Someone else is bound to have some ideas.

It was my fear that I put too much technical mathematics stuff into my posting. I already put this question (in C++ form) into a mathematical internet forum, but then the OOP-design part of the discussion got stuck at a very basic level... :-) Now I rethought my question and how I can put it on a more abstract level to get out the math part. I guess my problem comes down to this point: class Outer { class Inner { [...] } [...] } main() { A a = new A(); [... initialize many a.Inner objects here ...] } Once that "a" is initialized, te reference "a" stays constant all the time. So all further references/pointers to "a" are redundant. Especially, the references to "a" stored in "a.Inner" inner objects are redundant. So if there was a way to tell the compiler that the reference a will never change, the compiler could do the optimization and skip all these inner-class-references. Now the question is: Is there currently a way to achieve a satisfying, equivalent result? If not: Should I make this a feature request? coxalan

What about doing it manually/explicitly... import std.stdio; import std.string; import std.c.stdlib; class Outer { Inner[] m_Data; uint m_Degree; this(uint d) { m_Degree = d; } Inner allocate(uint[] data) { m_Data ~= new Inner(this, data); return m_Data[$-1]; } new(uint size) { writefln("Outer new %d", size); return malloc(size); } delete(void *p) { free(p); } } class Inner { static Outer m_Outer; uint m_Data[]; this(Outer o, uint[] data = null) { m_Outer = o; m_Data.length = m_Outer.m_Degree; if (data !is null) { m_Data[0..$] = data[0..m_Data.length]; } } Inner opMul(Inner b) { Inner res = new Inner(m_Outer); foreach(int i, uint val; b.m_Data) { res.m_Data[i] = m_Data[i] * b.m_Data[i]; } return res; } new(uint size) { writefln("Inner new %d", size); return malloc(size); } delete(void *p) { free(p); } string toString() { return format("%s", m_Data); } } void main() { Outer o = new Outer(3); Inner a = o.allocate([0,1,2]); Inner b = o.allocate([1,2,3]); Inner c = a * b; writefln(c); } Of course, if you use Inner with another Outer class it will overwrite the static m_Outer and fail horribly... Regan

Sep 11 2007

Regan Heath wrote:Of course, if you use Inner with another Outer class it will overwrite the static m_Outer and fail horribly...

But maybe we can get round this by using templates... class Inner(T) { static T m_Outer; } That way, if you use say.. Inner!(Outer) a; Inner!(Outer) b; Inner!(Outer) c; Inner!(Bob) d; Inner!(Bob) e; Inner!(Fred) f; You will get a static m_Outer for each combination, thus 3 for the code above. Regan

Sep 11 2007

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 coxalan wrote:Regan Heath Wrote:> The class SymmetricGroupshould collect everything that all of its elements (the object of the class Permutation) have in common.

Definitely methods, like uint order() which returns the number of the elements of the group. For the SymmetricGroup there are no other data members, but for other Groups there could be arbitrary data members (like a big array where the multiplication results are stored).[...] Ok. I think I have reached the limit of my usefulness here :) Someone else is bound to have some ideas.

It was my fear that I put too much technical mathematics stuff into my posting. I already put this question (in C++ form) into a mathematical internet forum, but then the OOP-design part of the discussion got stuck at a very basic level... :-) Now I rethought my question and how I can put it on a more abstract level to get out the math part. I guess my problem comes down to this point: class Outer { class Inner { [...] } [...] } main() { A a = new A(); [... initialize many a.Inner objects here ...] } Once that "a" is initialized, te reference "a" stays constant all the time. So all further references/pointers to "a" are redundant. Especially, the references to "a" stored in "a.Inner" inner objects are redundant. So if there was a way to tell the compiler that the reference a will never change, the compiler could do the optimization and skip all these inner-class-references. Now the question is: Is there currently a way to achieve a satisfying, equivalent result? If not: Should I make this a feature request? coxalan

I'm surprised nobody brought this up before. Yes, of course you can do this. class Outer { class Inner { [...] } } void main() { Outer foo=new Outer; auto bar=foo.new Inner; auto baz=foo.new Inner; } That what you want? :) --downs -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.7 (GNU/Linux) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org iD8DBQFG5vsZpEPJRr05fBERAtpiAJ47qu9tffXEr47OsNR3+v89i4JLEwCfU15R rUPiTTNTUCH/o/lv52Cae2M= =UmNC -----END PGP SIGNATURE-----

Sep 11 2007

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 I'm sorry. I didn't read/understand your requirements precisely. Of course, this _will_ entail a reference to the outer class. As they say, "oops". Sorry again. --downs -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.7 (GNU/Linux) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org iD8DBQFG5vvDpEPJRr05fBERApk6AJ4mQwqMcVCiUNAp4q8bSY3O8vTK4wCgnGSD fAzOol+zuD48RTxx2aRFH+E= =5Sm8 -----END PGP SIGNATURE-----

Sep 11 2007

Downs Wrote:I'm sorry. I didn't read/understand your requirements precisely. Of course, this _will_ entail a reference to the outer class. As they say, "oops". Sorry again.

No problem and thanks for your answer. As you can see in my postings, it took me some time to see what really the matter is, too: I know that in my case the reference to the outer class object will never change. So I know that all references stored in the inner class objects will be the same and are not necessary. coxalan

Sep 11 2007

coxalan wrote:Regan Heath Wrote:

It was my fear that I put too much technical mathematics stuff into my posting. I already put this question (in C++ form) into a mathematical internet forum, but then the OOP-design part of the discussion got stuck at a very basic level... :-) Now I rethought my question and how I can put it on a more abstract level to get out the math part. I guess my problem comes down to this point: class Outer { class Inner { [...] } [...] } main() { A a = new A(); [... initialize many a.Inner objects here ...] } Once that "a" is initialized, te reference "a" stays constant all the time. So all further references/pointers to "a" are redundant. Especially, the references to "a" stored in "a.Inner" inner objects are redundant. So if there was a way to tell the compiler that the reference a will never change, the compiler could do the optimization and skip all these inner-class-references. Now the question is: Is there currently a way to achieve a satisfying, equivalent result? If not: Should I make this a feature request? coxalan

I assume correctly that A == Outer in that example above? Some questions: is the total number of instances of Outer constant and known at compile time? If not, I think it's impossible to do what you want. -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D

Sep 12 2007

If it was possible to instantiate the object "s" at compile time (something like static const SymmetricGroup s = new SymmetricGroup(3); ), the above mentioned references would not be needed. But unfortunately, It is not possible to instantiate objects at compile time (what's actually the reason for this?).

If SymmetricGroup contains only compile time constants, you could do: struct SymmetricGroup(int degreeIn) { const int degree = degreeIn; } struct Permutation(Group) { alias Permutation!(Group) thistype; thistype opMul(ref thistype a) { // do something with Group.degree } } void main() { alias SymmetricGroup!(3) s; Permutation!(s) a, b, c; c = a * b; }

Sep 11 2007

coxalan wrote: [...]This is definitely more elegant than the first example. But here, every object of

to avoid. You can simply declare the inner class as static. This way it won't store a reference to its container class. Ciao

Sep 11 2007

Roberto Mariottini Wrote:coxalan wrote: [...]This is definitely more elegant than the first example. But here, every object of

to avoid. You can simply declare the inner class as static. This way it won't store a reference to its container class. Ciao

A static outer class will not work, because within the inner class I need access to the class members of the outer class. But since the outer class will stay constant, there is no need to store this reference again and again for each inner object.

Sep 11 2007