[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.c

dynamic pointer indexing and base passing

Skybuck Flying

6/30/2011 4:52:00 PM

Hello,

I need the following code to work:

var
Memory : pointer;
Block : dynamic pointer;
Element : dynamic pointer;
BlockIndex : integer;
ElementIndex : integer;
begin
ElementCount := 8000;
ElementSize := 4;

BlockCount := 1000;
BlockSize := ElementCount * ElementSize;

GetMem( Memory, BlockCount * BlockSize );

Block( Memory, BlockSize );

Element( nil, ElementSize );

Block[ BlockIndex ] : Element[ ElementIndex ] := 1000;

Block[ 500 ] : Element[ 1000 ] := Block[ 300 ] : Element[ 2000 ];
end;

What the following code does is:

Block( Memory, BlockSize );

initializes the block dynamic pointer to have a base address of memory and a
stride of block size.


What the following code does is:

Element( nil, ElementSize );

Initialize the element dynamic pointer to have a base address of nil and a
stride of element size.

What the following code does is:

Block[ BlockIndex ] : Element[ ElementIndex ] := 1000;

BlockPointer := BlockBase + BlockIndex * BlockSize;

: indicates the block pointer is to be passed on as the base for element:
ElementBase := BlockPointer;

ElementPointer := ElementBase + ElementIndex * ElementSize;

ElementPointer^ := 1000;

Additional the compiler is capable of recgonizing different indexing pairs:

Block[ 500 ] : Element[ 1000 ] := Block[ 300 ] : Element[ 2000 ];

Invokes the code above twice for the first and the second pair.

And does:

ElementPointer1^ := ElementPointer2^;

I need this language feature today !

In other words as soon as possible so get to it !

It would also be beneficial if C/C++ had this and if CUDA C/C++ can
integrate this as soon as possible.

C syntax:

int main()
{
void *Memory;
dynamic void *Block;
dynamic void *Element;
int BlockSize;
int ElementSize;
int BlockIndex;
int ElementIndex;

... etc ...;

Memory = Malloc( etc );

Block( Memory, BlockSize );

Element( NULL, ElementSize );


Block[ BlockIndex ] : Element[ ElementIndex ] = 1000;

// However perhaps this might present a little issue since : is already used
in C.

// A solution could be to use a double colon like so:

Block[ BlockIndex ] :: Element[ ElementIndex ] = 1000;

}

Bye,
Skybuck.

13 Answers

Skybuck Flying

6/30/2011 5:04:00 PM

0

This basic concept could be expanded to allow a short hand form of multi
dimensional dynamic pointers, example:

var
BlockElement : dynamic pointer of dynamic pointer;
begin
// memory address, stride for first dimension, stride for second
dimension.
BlockElement( Base, BlockSize, ElementSize );

// the user can then write the short hand form:
BlockElement[ BlockIndex, ElementIndex ] =

// and/or
BlockElement[ BlockIndex ] [ ElementIndex ] =

// which would be shorter than:
Block[ BlockIndex ] : Element[ ElementIndex ] =

// other shorter example:
BlockElement[ 10, 20 ]

// versus
Block[ 10 ] : Element[ 20 ];

end;

// example in C:

int main
(
void *Memory;
dynamic void **BlockElement;

// or more pascal like:
dynamic void * dynamic void * BlockElement;

BlockElement( Memory, BlockSize, ElementSize );

// however perhaps this is a bit odd since c/c++ is not used to dynamic
multi dimensional arrays but would be a nice feature to start adding
// anyway ;)

BlockElement[ 10 ] [ 20 ] = 100;
);

However this short hand form is not necessary for now, focus on the first
version first... once that is done perhaps a short hand form can be added,
though it's not really that much shorter ;) But some might like it ;)

Bye,
Skybuck.

Skybuck Flying

6/30/2011 5:54:00 PM

0

Proof of concept demonstration program, showing some issues with current
programming language design/limitations and how to work around that, further
paths for investigation provided, and further syntax ideas provided.

Current program demonstration is limited to Delphi only for now, since C/C++
does not even have properties so C/C++ is more limited already.

The goal is ofcourse to make programming as easy as possible.

The solution provided below is on it's way to become easy usuable, as
mentioned further path for investigation is using a different operator to be
used as operator overloading to try and automate the passing of the base
pointer to the next indirection/record.

Perhaps

Block[ 1000 ] * Element[ 2000] = 1000;

Might work... the * operator could be overloaded (perhaps delphi has a colon
operator overloader but I don't think so)

^ This idea might work if indeed PDynamicPointers are passed to the operator
overloader, then access to base pointers and strides should be
allowed/possible and thus this code can be automated to pass base pointer
from block to element for element access.

However there is a problem... element[2000] is probably evaluated before the
multiple operator is invoked and thus it seems not possible to use the
operator * overloader unless the index/pointer calculation can be re-done
after the fact/invokation of the * operator overloader.

The ultimate goal of the * operator overloader would be to provide an
address which can then be written to be the assignment operator.

So in a way it could be possible if dynamic pointers or in this case
element stores the index that it wants to write to.

Then * operator overloader could be used to return a pointer.

Finally the user could override the pointer with a typecast as follows:

PInteger( Block[1000] * Element[2000] )^ := 1000;

So this short examination above does seem to provide a solution to mimic
this new language behaviour.

This idea above has not yet been explored/implemented in the example above,
but it's pretty interesting so I will try in a next example.

For now here is the first example/version, showing compiler/language
limitations and manual work arounds:

To be precise the language limitation is in pointers:

Pointers are in a way static, their strides are static, they depend on the
type to which the pointer points (static "typed pointers").

General programming languages need to be enhanced to prevent "dynamic
pointers".

Where their strides can be specified at runtime to allow flexibility for
multi-dimensional programming which will probably become more important in
the future as processors/transistor run into switching speed limitations and
the only way to add more speed is to increase the ammount of transistors and
chips leading to dimensionality... be it 1d or multi-d.

Some flexibility for programming solutions is then required, since these
chips will have different sizes/dimensionalities, and so do memory chips and
thus computational problems which need to be flexible solved to what the
capabilities of the systems are, thus flexible software required. Assuming
that 1 GB arrays will fit on all is bad assumption, these arrays need to be
flexible, and thus flexible pointers required.

Perhaps hardware will make things easy for as programmers by providing
simple multi dimensional to single dimension translations/mappings and vice
versa, or perhaps not, in the latter case we programmers need better
languagees to support these multi dimensional situations ;)
Even just 1D already requires this to be able to program it easy.

Think of a single core as 0D ;) (<- this era seems over)

// *** Begin of Test Program ***

program TestProgram;

{$APPTYPE CONSOLE}

{

Test dynamic pointer indexing and base passing idea

version 0.01 created on 30 june 2011 by Skybuck Flying.

Dynamic pointer type record provided below.

It consumes no memory in the final memory output.

It does use local variables/record fields to store it's base/field but these
are not stored in the memory block which is a good thing.

The pointers returned are all directly into the memory block.

Yet because they are of type PDynamicPointer it is allowed to invoke further
indirection/array index operator which is also a good thing.

In this first test I see no delphi/pascal language facility yet to
automatically
pass the calculated base pointer to the next indirection.

Thus this is done manually in code section (*).

This is were compiler help would be usefull, or a new language feature for
example
the suggested : colon operator to pass values to the next indirection/next
record

Perhaps a different existing operator could be used to mimic/implement this
behaviour
via some form of operator overloading. So further investigations could
deliver interesting results.

Which leaves the issue of having to typecast the dynamic pointer to the
final type to use...
in this example the integer type.

A further suggestion is to allow the following syntax declaration to
automate these typecasts by the compiler:
var
Element : dynamic pointer to integer;

This could automatically setup up the stride and the type so no typecasting
required.

}

uses
SysUtils;

type
PDynamicPointer = ^TDynamicPointer;
TDynamicPointer = record
private
mBase : pointer;
mStride : integer;
function PerformArrayIndexOperator( ParaIndex : integer ) :
PDynamicPointer;
// procedure PerformArrayIndexOperatorWrite( ParaIndex : integer;
ParaDynamicPointer : PDynamicPointer );

public
property Base : pointer read mBase write mBase;
constructor Create( Base : pointer; Stride : integer );
property ArrayIndexOperator[ ParaIndex : integer ] : PDynamicPointer read
PerformArrayIndexOperator; default;
end;

constructor TDynamicPointer.Create( Base : pointer; Stride : integer );
begin
mBase := Base;
mStride := Stride;
end;

function TDynamicPointer.PerformArrayIndexOperator( ParaIndex : integer ) :
PDynamicPointer;
begin
result := PDynamicPointer( longword(mBase) + longword(ParaIndex) *
longword(mStride) );
end;

(*
procedure TDynamicPointer.PerformArrayIndexOperatorWrite( ParaIndex :
integer; ParaDynamicPointer : PDynamicPointer );
begin
result := PDynamicPointer( longword(mBase) + longword(ParaIndex) *
longword(mStride) );
end;

procedure PerformArrayIndexOperatorWrite( ParaIndex : integer;
ParaDynamicPointer : PDynamicPointer );
*)

procedure Main;
var
Memory : pointer;
Block : TDynamicPointer;
Element : TDynamicPointer;
ElementCount : integer;
ElementSize : integer;

BlockCount : integer;
BlockSize : integer;

BlockIndex : integer;
ElementIndex : integer;

Check : Pinteger;

begin
ElementCount := 8000;
ElementSize := 4;

BlockCount := 2000;
BlockSize := ElementCount * ElementSize;

GetMem( Memory, BlockCount * BlockSize );

Block := TDynamicPointer.Create( Memory, BlockSize );
Element := TDynamicPointer.Create( nil, ElementSize );

// Block[ 1000 ].Element[ 2000 ] := 4;
// ^ cannot pass base pointer. (current language limitation )

// cannot assign pointer to element, perhaps operator overloading might
help.
// Element := Block[ 1000 ];

// for now we do passing the base manually:
Element.Base := Block[ 1000 ]; // (*)

// another problem we face, cannot overload the pointer to be a pointer to a
type
// so typecast needed, or we could introduce a special property but this
would be a property per type less ideal.
PInteger(Element[ 2000 ])^ := 666;

// Now let's test if the element went were it should be:
BlockIndex := 1000;
ElementIndex := 2000;

Check := Pinteger( longword(Memory) + longword(BlockIndex) *
longword(BlockSize) + longword(ElementIndex) * longword(ElementSize) );

writeln( Check^ ); // correct it displays 666.

// Conclusion:
// A General dynamic pointer type is already possible, this allows to embed
base and stride into a record
// which can then be a record to a record to a record for easy/automatic
multi dimensional support/extensibility/flexibility.
// What is needed is a little bit of compiler help to automate the above
code in a more nicely to use syntax sugar
// so that it can automaticaly pass the base as in code section (*);
// some automatic type casting would also be nice.
// which could be done last like so:
// then the stride would already be known... or at least the type be known !
;)
{
Block : dynamic pointer;
Element : dynamic pointer to integer;
}


FreeMem( Memory, BlockCount * BlockSize );
end;


begin
try
Main;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
ReadLn;
end.

// *** End of Test Program ***

Bye,
Skybuck.

Skybuck Flying

6/30/2011 6:25:00 PM

0

Operator overloading idea tried, some kind of weird read access problem for
"self".

Could be another Delphi bug with operator overloading... I am not sure...

According to the help Delphi XE now should have operator overloading for
classes as well... I think Delphi 2007 didn't have this yet... so this
offers more possibilities ;)

Second version (has problems):

// *** Begin of Test Program ***

program TestProgram;

{$APPTYPE CONSOLE}

{

Test dynamic pointer indexing and base passing idea

version 0.01 created on 30 june 2011 by Skybuck Flying.

Dynamic pointer type record provided below.

It consumes no memory in the final memory output.

It does use local variables/record fields to store it's base/field but these
are not stored in the memory block which is a good thing.

The pointers returned are all directly into the memory block.

Yet because they are of type PDynamicPointer it is allowed to invoke further
indirection/array index operator which is also a good thing.

In this first test I see no delphi/pascal language facility yet to
automatically
pass the calculated base pointer to the next indirection.

Thus this is done manually in code section (*).

This is were compiler help would be usefull, or a new language feature for
example
the suggested : colon operator to pass values to the next indirection/next
record

Perhaps a different existing operator could be used to mimic/implement this
behaviour
via some form of operator overloading. So further investigations could
deliver interesting results.

Which leaves the issue of having to typecast the dynamic pointer to the
final type to use...
in this example the integer type.

A further suggestion is to allow the following syntax declaration to
automate these typecasts by the compiler:
var
Element : dynamic pointer to integer;

This could automatically setup up the stride and the type so no typecasting
required.

}

{

version 0.02 created on 30 june 2011 by Skybuck Flying.

Use operator overloading to automate.

This version apperently has pointer problems.

Perhaps using/returning pointers is not such a good idea because of bug
creation.

However it was a good idea for speed.

Unfortunately it doesn't seem possible.

So will have to fall back to passing copies around... perhaps that will
work.

And then some more properties to actually get a pointer.

Or perhaps it can work in a slightly different form or so.

Return TDynamicPointer instead of PDynamicPointer for indexing operators...
it would be much slower... but perhaps more robust for now...

Then somehow this needs to be overriden to allow pointers as well.

Code is now a bit messy... I wish it worked for pointers...

I wish pointer types could have operator overloading as well...

That would probably make things a whole lot easier ?!?

As far as I know pointers and there operators cannot be overloaded...

However perhaps a special pointer type can be created which can do certain
things... not sure if it would be usefull for this...

But it's more or less the same idea but then slightly different...

instead of trying to apply an array index operator directly to the type...

Perhaps we need to take a step back and first work on the basic concept
of a "dynamic pointer type" without the array specifier.

So the dynamic pointer type should then simply store:

base pointer
stride (could also be called element size or so or simply size or so, but
stride
is probably best for now).

which is pretty much what we are doing right now... but then instead leave
the array
specifier out of it... and try to include that in other types...

like array of dynamic pointer or so...

or dynamic pointer to array of integers or something.

So that's another whacky idea to try ;)

Or perhaps this current code can be fixed somehow ;) and needs some more
work/tries ;) :)

But I doubt it will work... it seems to have access violations with the
array thingy
One last idea could be to immediatly store "self" in a field...
// perhaps that's what is causing the problem so I will give that a last
try.

Nope it still causes a problem...

So time to give up on this idea... could be a delphi bug though.

}

uses
SysUtils;

type
PDynamicPointer = ^TDynamicPointer;
TDynamicPointer = record
private
mBase : pointer;
mStride : integer;
mIndex : integer;
mSelf : PDynamicPointer;
function PerformArrayIndexOperator( ParaIndex : integer ) :
PDynamicPointer;

function GetSelf : PDynamicPointer;
function GetCopy : TDynamicPointer;
public
property Base : pointer read mBase write mBase;
constructor Create( Base : pointer; Stride : integer );
property ArrayIndexOperator[ ParaIndex : integer ] : PDynamicPointer read
PerformArrayIndexOperator; default;

class operator Multiply( ParaA, ParaB : TDynamicPointer): PDynamicPointer;

property ItSelf : PDynamicPointer read GetSelf;
property Copy : TDynamicPointer read Getcopy;
end;

constructor TDynamicPointer.Create( Base : pointer; Stride : integer );
begin
mBase := Base;
mStride := Stride;
mSelf := @Self;
end;

function TDynamicPointer.PerformArrayIndexOperator( ParaIndex : integer ) :
PDynamicPointer;
begin
mIndex := ParaIndex;
result := PDynamicPointer( longword(mBase) + longword(ParaIndex) *
longword(mStride) );
end;

// cannot create, to bad :(:
// class operator TDynamicPointer.Multiply( ParaA, ParaB : PDynamicPointer):
PDynamicPointer;

class operator TDynamicPointer.Multiply( ParaA, ParaB : TDynamicPointer):
PDynamicPointer;
begin
ParaB.mBase := Pointer( longword(ParaA.mBase) + longword(ParaA.mIndex) *
longword(ParaA.mStride) );
result := PDynamicPointer( ParaB[ ParaB.mIndex ] );
end;

function TDynamicPointer.GetSelf : PDynamicPointer;
begin
result := mSelf; // read access problem.
end;

// still does not work, creates a problem ? Maybe another bug in Delphi ?!?
// or perhaps it's because of the pointer... a bit strange...
function TDynamicPointer.GetCopy : TDynamicPointer;
begin
result.mBase := mBase;
result.mStride := mStride;
result.mIndex := mIndex;
end;

procedure Main;
var
Memory : pointer;
Block : TDynamicPointer;
Element : TDynamicPointer;
ElementCount : integer;
ElementSize : integer;

BlockCount : integer;
BlockSize : integer;

BlockIndex : integer;
ElementIndex : integer;

Check : Pinteger;

begin
ElementCount := 8000;
ElementSize := 4;

BlockCount := 2000;
BlockSize := ElementCount * ElementSize;

GetMem( Memory, BlockCount * BlockSize );

Block := TDynamicPointer.Create( Memory, BlockSize );
Element := TDynamicPointer.Create( nil, ElementSize );

// use new trick:
// this cannot be used since this would not point to self/record but to
memory
// PInteger( Block[ 1000 ]^ * Element[ 2000 ]^ )^ := 666;

// need pointer to self/type.
// doesn't work, leads to problems, which is a little bit strange.
PInteger( Block[ 1000 ].ItSelf^ * Element[ 2000 ].ItSelf^ )^ := 666;

// so must/try a copy instead.
// PInteger( Block[ 1000 ].Copy * Element[ 2000 ].Copy )^ := 666;

// Now let's test if the element went were it should be:
BlockIndex := 1000;
ElementIndex := 2000;

Check := Pinteger( longword(Memory) + longword(BlockIndex) *
longword(BlockSize) + longword(ElementIndex) * longword(ElementSize) );

writeln( Check^ ); // correct it displays 666.

// Conclusion:
// A General dynamic pointer type is already possible, this allows to embed
base and stride into a record
// which can then be a record to a record to a record for easy/automatic
multi dimensional support/extensibility/flexibility.
// What is needed is a little bit of compiler help to automate the above
code in a more nicely to use syntax sugar
// so that it can automaticaly pass the base as in code section (*);
// some automatic type casting would also be nice.
// which could be done last like so:
// then the stride would already be known... or at least the type be known !
;)
{
Block : dynamic pointer;
Element : dynamic pointer to integer;
}


FreeMem( Memory, BlockCount * BlockSize );
end;


begin
try
Main;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
ReadLn;
end.

// *** End of Test Program ***

Bye,
Skybuck.

Skybuck Flying

6/30/2011 6:40:00 PM

0

Seeing this first operator overloading/second program attempt at creating
the final pointer it becomes apperent how an easy way can be created to
allow this to work, at least for two dimensions maybe even three/multiple
dimensions. Though maybe less obvious now that I look at it again:

The trick is in the following code:

Block[2000] * Element[ 1000 ]

What could be done is the following:

ParameterA is used to return the block index * block stride + base

ParameterB is used to return the element index * element stride and is added
to the above.

This should circumvent the pointer problem.

So the index operator should return as a first try the record so that the
operator overloader can receive that.

However that does create some overhead... which I would like to get rid of.

So instead Block[2000] could return ParameterA and Element[1000] could
return ParameterB

These would then be integers which would fail to call the operator
overloading, but if these were special typed integers then operator
overloading should/would be able to be called.

All the multiply operator then would need to do is:

ParameterA + ParameterB which would then return a valid final pointer.

This could probably work... now the question is can this be scaled to
infinite dimensions:

A[10] * B[20] * C[30];

The question would now be: how are these operators called ? In what order ?

Also what about temporarely values... for example:
(A * B) * C[30];
^
Special integer type.

Perhaps the special integer type could store the separate values and could
then be used to repeat the trick above with the base... so the base don't
get screwed... however perhaps this isnâ??t needed since + C[30] is the only
thing that needs to happen.

So another possibility to overload the special integer multiple operator.

However this might create a problem since C[30] already returns such a
special integer.

So what happens if:
special integer * special integer is done...

That is more or less what happened in the first place...

So perhaps the same could still happen as long as special integer contains
base and stride.

Then it can probably use that and perform further calculations.

So this idea could scale to infinite dimensions....

All in all not to bad...

It's probably also a nice example of how special integer is actually a
TDynamicPointer and how the array operator/indexed property simply returns a
TDynamicPointer which should be easy and fast... though maybe not as fast as
I would like..

Then all that needs to be done is:

TDynamicPointer * TDynamicPointer * TDynamicPointer

^ This calculation would finally return a usuable final pointer (which can
then be used to access individual memory elements using a typecast or
perhaps even automatic typecasting for the final type by providing even more
default overloaded operators)

All in all this could be a very nice and simple idea how to do multi
dimension/scalable dynamic pointer idea's ! ;) =D

Bye,
Skybuck.

Skybuck Flying

6/30/2011 7:03:00 PM

0

So to clearify this nice/neat perhaps even brilliant idea I shall explain it
a little bit further:

To be able to use "dynamic pointers" in a scalable fashion the following
code underneath would do the following:

TDynamicPointer * TDynamicPointer * TDynamicPointer

Thanks to operator overloading and storage inside TDynamicPointer the
following happens:

(not so good formula, better formula below ! but was nice to examine this
anyway:)

Base + (Index * Stride) * Size + (Index * Stride) * Size + (Index *
Stride);

The trick is with the Size.

Size changes and depends on the other pointers.

Each dynamic pointer could therefore have it's own DimensionSize, plus it
could also have a "carry-over" size, which can also be tought of as
"accumilative" size.

It accumilates the sizes/volumes/dimensions so far. If this is possible
remains to be seen and depends on the order in which the operator
overloading is called.

If the operator overloading goes from right to left then that would be ideal
which would mean z,y,x. However if it was opposite then it's still usuable
but programmer would need to write x,y,z.

So perhaps the AccumilativeSize of each dynamic pointer could be used to
store the progress of the multiplication progression:

For example:

operator overloader *( A, B )
AccumilativeSize := A.Index * B.DimensionSize;

// notice inconsistent code... perhaps it can be solved.

// then on the next call:
AccumilativeSize := AccumilativeSize + A.Index * B.DimensionSize;

There is however a problem:

Somehow the AccumlitiveSize must be reset to zero ?!?

A first idea which comes to mind is a class variable which might track how
many multiplications need to be done... but then the same problem remains...
how to reset it ? Perhaps the reset could occur when the pointer is used or
when an assignment is done... however this doesn't necessarily always happen
so could introduce bugs... alternatively... user could reset itself but that
requires more code which is kinda nasty and unwanted.

Perhaps there is a different solution thinkable... where the DimensionSize
or simply Stride is always used at the multiplicative.

Then the formula simply becomes:

Base + (Index * Stride) + (Index * Stride) + (Index * Stride);

Which would be the correct formula anyway based on the initial idea.

This does however require the programmer to specify the "full stride" for
each dynamic pointer.

This would mean and limit the pointer to being used in a certain way, in a
certain dimension, so some freedom is lost.

It's also less generic. But at least it would work easy:

z = DynamicPointer( optional base (should be set), Stride ); // dimension z
y = DynamicPointer( optional base, Stride ); // dimension y
x =DynamicPointer( optional base, Stride ); // dimenzion x


Then at least these dimension specific pointers can be used as follows:

z[1000] * y[500] * x[300] = 10;

^ This would offer some flexibility and easy programming !

However it would always need to be used like that. Or maybe not... maybe
it's also possible to simply write:

x[300] * y[500] * z[1000] = 10;

According to the formula above this should also work.

However there is still a relation ship between these 3 pointers, their
relation ship is encoded in the stride setup.

Therefore unrelated dynamic pointers cannot be used, example of a wrong
usage:

a[10] * x[300] = 100;

^ This would be a wrong usage unless A's stride is somehow related to x.

Since x was lowest dimension this might still make some sense, but what
about:

a[10] * y[500] = 100;

This would also still make some sense, since Y has it's own stride as
well... however what does a[10] mean could be anything...

What this would ultimately be is a pointer towards y's (and a's ) plane of
memory... so it would at least represent the start of their memory block...
not necessarily individual elements, unless the plane is consider an entire
individual element.

The point is that the assignment = 100 is then a bit strange and probably
invalid... the final pointer is not really an ^integer type it could be
treated as such but that wasn't the original goal of the code.

It would be kinda funny if programming language would allow entire arrays to
be assigned as well... and copies performed... other meaning could also be
given like 100 is initializer for entire array, with this code finally does
remains up to the freedom of the programmer, here it's just examined to see
if it would present any problems and it doesn't really.

The point is that each dynamic pointer has it's own stride and thus
multiplieing dimensions like this has a somewhat different meaning then y
ranges from 0 to 499. In this case it's more like y ranges from 0 * y's
stride to 499 * y's stride. So it's like multiplieing with a plane's area or
a volume's size or so... that should be pretty straight forward to
understand ! ;) :) However what is not immediately clear is what y's stride
is...

The code y[500] doesn't show this... to learn what the stride is the code
needs to be examined, which is ok. There could even be a stride property in
case the programmer wants to programmatically or by debugging inspect it
directly/immediately.

All in all not a too bad design... and I think this could be usefull in
practice with existing Delphi language features.

However the prrooooff is in the pudding ! ;) =D

So now I still need to go make this pudding which will probably be not that
difficult and be a whole lot cleaner then previous whacky examples ! ;) =D

Now me go eat, and then me go make pudding ! ; ) =D

Bye,
Skybuck.

Skybuck Flying

6/30/2011 7:49:00 PM

0

Here is the fourth idea/iteration of this idea:

So let's recap a little bit what is wanted is the following code to work:

World[ 5 ] * Block[ 200 ] * Element[ 10 ];

The new idea/addition idea for the fourth idea is to set the
"AccumilatedStride" to zero each time the index operator is used.

So the index operator is simply used to store the index inside of the
dynamic pointer record and meanwhile it's accumilated stride is reset to
zero.

The strifing here is ofcourse more flexibility. It would be nice if each
dynamic pointer can be specified in such a way that only it's own
dimensionality needs to be given/specified so that different dynamic
pointers of different dimensionality can be used and mixed together. (Idea
three did not yet allow this).

So what is done is the following:

index operator( ParaIndex );
mAccumilatedStride = 0;
mIndex := ParaIndex

Now comes the cool trick:

To understand the trick first requires to understand how the index operators
are called, the index operators are called first, which is really cool, this
make sure/assures that the accumilated strides are all zero, so this means
that it doesn't matter in what order the multiplicator operator is done, any
accumilated stride field can be used to do the processing, perhaps they can
even be combined to solve the problem which is correct index/offset
calculations giving any possible dimensionality.

So what is further required to be able to do calculations properly is the
dimensionality of each (dynamic) pointer.

So the dynamic pointer needs a new field which records this information and
specifies it.

mDimensionSize = 8000; // for example

So each dynamic pointer now also has a mDimensionSize field.

Now when the multiplication operator is called done the following can
happen:

(One could think of the mDimensionSize field to replace the mStride field
which is now probably replaced by mAccumulatedStride)
(However this is still a bit weird to think of an element size in terms of
"dimension size".)

(IndexA * DimensionA) + (IndexB * DimensionB);

Yet this seems correct so far up to a certain point, the plus point, it
needs some more to be fully correct.

DimensionB could for example by SizeOf(integer) and IndexB could be
ElementIndex.

IndexX could be BlockIndex and DimensionA the number of blocks.

What is still needed is a BlockSize...

Thus BlockSize = ElementCount * ElementSize;

BlockSize is what should have been DimensionA in a weird way... so there are
now conflicting definitions.

So let's keep the definition of a stride, it might come in handy.

The DimensionSize is renamed to simply mDimension or perhaps mDimensionCount
to prevent confusion with "byte size" it is not a byte-based-size.

The dimension should only specify N, it specifies the range of the
dimension. So perhaps mDimensionRange is a good name... though range can
also have a start and an end... so not completely statisfactory. Though this
could be an interesting extension for pascal to allow dimensions to be
[10..20] or so.

So for such an extension mDimensionRangeBegin and mDimensionRangeEnd could
be used... as well as mDimensionCount

(though sometimes count is used as a byte-sized specifier as well in some
software codes so could still present somewhat of confusion but for now it
seems ok.)

So to make things even more cool:

TDynamicPointer could now contain a mDimensionRangeBegin and
mDimensionRangeEnd this would allow automatical bounds checking to be done
on pascal like ranges.

Furthermore it contains mDimensionCount which specifies how many indexing
possibilities there are.

The mDimensionCount is needed for the formula so let's proceed with that.

The formula in the operator overloading has to be something like:


(IndexA * DimensionCountB * StrideB) + (IndexB * StrideB)

This can also be tought of as:

1.
// VolumeB = DimensionCountB * StrideB;
(IndexA * VolumeB) + (IndexB * StrideB);

Therefore to simply the formula even further the TDynamicPointer could also
have yet another field:

mVolume which simply tracks the total ammount of bytes inside the dimension.

mVolume = mDimensionCount * mStride;

This makes the formula above in 1. a little bit shorter and simpler and
maybe even quicker too.

Therefore the conclusion can be:

The multiply operator should multiple the index of A with the volume of B
and add the index B multiplied with the stride of B.

The question is now how to proceed with this information for the next
multiplication.

I am thinking right now that the volume of A needs to be transferred to the
next multiplication.

So instead of having a cumulative stride... there could simply be a
CumulativeVolume which tracks the volume to multiple with... however perhaps
each index needs it's own volume so perhaps that idea of cumulative volumes
is flawed and needs to be seperated into separate volumes... however
the multiplications only need to happen once... at least they should be...
so here is a possibilty for a coding problem, example:

Block[200] * Block[200] * Block[200] // could be a problem. but easier to
detect.

Another example:

Block[200] * A[ 20 ] * Block[200]; /// another problem, but harder to
detect.


What is the ultimate formula to do it ?

Let's see it's probably something like:

(IndexZ * VolumeY) + (IndexY * VolumeX) + (IndexX * VolumeW) + (IndexW *
VolumeV) + (IndexV * VolumeU);

And so forth all down the alphabet... z to a for a total of 26 dimensions in
this formula example.

So conclusion is: the higher dimension always has to be multiplied with the
volume of the lower dimensions.

The volume of the lower dimensions can progressively be determined if the
multiple operator overloading happens from the lower dimension (right side)
to the higher dimension (left side).

In that case the volume calculation can be passed on the left side for
automatic volume calculation which would look conceptually something like:

VolumeOfEvenLowerDimension needs to be passed in somehow if possible or used
from whatever it was set to (the initial stride).

VolumeLowerDimension is more or less the same but can be calculated for next
multiplication.

(IndexHigherDimension * VolumeLowerDimension) + (IndexLowerDimension *
VolumeOfEvenLowerDimension);

So it probably becomes something like this:

The volume for the next multiplication should be stored in A.

which can also be though of as VolumeHigherDimension

So

VolumeA or VolumeHigherDimension = DimensionA * VolumeLowerDimension;

^ This should be the correct formula to pass the volumes on to the next one.

So with carefull programming the user programmer should be able to
automatically scale up the pointer calculations to any combination of
pointer dimensions which would be a really nice and flexible future to have
!

All of this functionality can probably already be implemented with existing
delphi language features.

The only potential problem which remains is:

Block[200] * Block[200]

^ The same dynamic pointer may not be used twice in a single calculation...
this would probably cause problems, unless perhaps the records are copied
during multiply operator then it might still be possible which would be a
really insane and cool thing I guess... so that the following code could be
written as well:

Block[300] * Block[200] * Block[400] * Block and so forth...

Ofcourse this would only work for a cube... since the dimension is fixed...
the single dynamic pointer could be used for this.

However this is what it was not designed for... multiple dynamic pointers
should be used each with their own dimension.

In case this multiple usage of the same dynamic pointer might be unsolvealbe
and lead to problems then this is where the compiler could step in and solve
it.

However I can also think of another solution which does not need any
compiler extensions and can still be solved with today's delphi language
features.

What could be done is the following: when the index operator is set it
resets a "usage count" to zero.

This will cause multiple index operators on the same dynamic pointer to
reset the usage counter multiple times to zero.

Now every time the multiplication operator kicks in the count is incremented
with one.

The multiplication operator can now add checking code/asserts to make sure
that no counts exceed the value of one.

It it does exceed a programming error is detected and the programmer can be
warned at runtime that there is a programming error.

So this offers both possibilities to be implemented, tested and used !

Quite a cool idea ! ;) =D

Bye,
Skybuck.


Skybuck Flying

7/1/2011

0

Right now I am implementing this fourth idea and I come across a little
issue,

The volume "passing" formula works splendidly though it needs to go from
right to left but that's not the issue.

The issue is with the theory up to this point.

Only the volume formula/theory has been explored and described in general
scaleble form.

The same needs to happen for the indexing formula so it needs to be made of
a somewhat more general/scalable form.

I have a feeling that we might no longer need the stride field which can
probably be replaced by the volume field.

The volume of a single element/the lowest dimension for example an integer
would exactly be 4 which would be a nice initial value for starting

the general/scalable formula. First I am now going to disable windows live
spelling checking because it's quite annoying, I canâ??t use that right now ;)
:)

There we go this should get rid of this fokking distraction. Yeah..
distractions are bad when trying to play einstein damn you Microsoft ! ;) =D

Anyway, here I was with the original example formula which must now be
described in a more general/scalable form:

(IndexA * VolumeB) + (IndexB * StrideB);

This pretty much represents the following idea/general form:


FinalIndex = FinalIndex + (IndexHigherDimension * VolumeLowerDimension);

So in other words:

(IndexZ * VolumeY) + (IndexY * VolumeX) + (IndexX * VolumeW) + (IndexW *
VolumeV) + (IndexV * VolumeU);

And so forth all the way down the alphabet.

Hmmm this is actually what I already wrote before... apperently it wasn't
clear to me what this ment... I was under the impression that this was the
higher volume calculation but this is not the case, this is the actual index
calculation.

So I shall write it a little bit better hopefully, which can probably
written as follows for the multiply operator

FinalIndex = FinalIndex + (HigherDimension.Index * LowerDimension.Volume);

Now we need a place to store FinalIndex, which can also be considered the
final pointer eventually.

Perhaps this could be stored in base for now, since it's not used when going
from right to left, but I am also hoping to get a general formula going
which can also work in any order.

Back to the volume formula which was a bit confusing because of the push to
higher.

The volume formula is:

ResultingVolume := HigherDimensionCount * LowerDimensionVolume (which can
also be tought of as previous volume calculation);

This is where the problems are coming from when going in random order.

The problem is that the higher volumes are not yet known and are set to
zero.

This can probably be prevented by setting all volumes to a default of 1
which should be correct.

This will then automatically produce the correct final volume calculations.

However there is still a problem with calculating the correct final index
calculations. These depend on the correct volume to be already calculated.

Of these volumes are not yet correctly calculated then the intermediate
result will be wrong.

However since the final index calculation depends simply on another volume
multiplication these intermediate results could be corrected when going from
left to right by multiplieing by the smaller volume.

So there is still hope that these index calculations can also be done in
random or opposite order.

Bye,
Skybuck.

Skybuck Flying

7/1/2011 12:14:00 AM

0

However I suspect that I have now entered into a region where I can no
longer solve the problem by just using delphi's language features.

Example:

A * B * C * D;

These multiplications can be executed in any order.

The user programmer could be a bit stubern and add parenthesis like so:

( A * B ) * ( C * D);

Therefore any order I assume will be incorrect without further data
provided.

My code could assume left right:
(((A * B) * C) * D) // which is what delphi seems to do

Or I could assume the opposite:
(A * (B * ( C * D) ));

However I don't see how I could possibly know and detect:

(A * B) * (C * D);

So this represent a real problem for me now, something which might be easily
solved by the compiler, but maybe less easily to maybe not at all by me.

I can solve the either two orders quite easily though:

Assuming left to right:

HighestIndex * Volume * Volume * Volume // as it progresses from left to
right

Assuming right to left:

HighestIndex * AccumulatedVolume; // as it progresses from right to left

The mixed order remains a problem.

But I can already vaguely see a solution perhaps...

This does remind me of huffman encoding and tree construction which gave a 1
(or some frequency) to all the end nodes and as the nodes are combined their
frequencies are summed together to form a higher frequency.

Perhaps such combining strategy might also be used here to indicate which
kinda of volume or indexing calculation needs to be done.

So left to right these indexes would be incremented somehow or so:

(( (1+1) + 1) + 1) which would give ((2)3)4)

So right to left these indexes would be incremented somehow or so:

(1 + (1 + (1 + 1) ) which would give (4(3(2))

Now the mixed case:

(1+1) + (1+1) which would give (2) 4 (2)

So perhaps these numbering could give some idea/indication how to perform
the calculations.

However I fear that access to individual fields which might be necessary to
do so correctly might no longer be available.

So perhaps in this case the calculations cannot be done correctly.

I am not yet sure though... but I fear it though ;)

Bye,
Skybuck.

Skybuck Flying

7/1/2011 1:02:00 AM

0

Here is a little demonstration program to inspect/demonstrate Delphi's
multiply operator order, plus output:

Delphi's multiply order is from left to right, which makes me wonder a bit
what free pascal's order and C's order and C++'s order and perhaps Java's
order is ?! ;)

For now left to right seems the order which I should target for user
friendlyness.

However first I want one more question answered:

What happens for the following code:

A * B * C * D.Field;

Especially in relation to

A * B * C * D[20].Field;

Maybe guess would be that .Field is recovered/requested/acquired before the
multiplication happens, so this field trick can probably not be used to
specify that a pointer to the field type should be returned, but that's a
somewhat hasty conclusion, since a special operator could be written which
would do:

Multiply( DummyType, FieldType )

Then this operator can detect the field type and perform pointer
calculations and then return a pointer.

It would be fed the following:

(A*B*C) Parameter A
(D[20].Field) Parameter B

So there is a problem, D[20] is lost and cannot be accessed anymore, unless
somehow field would point back to D but that's already been tried and for
some unexplained reason failed.

So this idea of using the field dot operator/qualifier to try and return a
pointer to integer type is unusuable.

So that idea can be scratched for now, instead a typecast seems the most
likely solution:

Typecast( A * B * C * D)

An impliciet type cast operator overloader could be used probably for the
assignment operator, I think that's probably the same as the assignment
operator, I am pretty sure about that.

This would be usuable for

result := (A*B*C*D);

But having to store the result into a result variable kinda sucks and is not
what I want.

I want to be able to immediatly use the pointer value that should be
returned so I can write:

if (A*B*C*D).AsInteger = 100 then
begin
(A*B*C*D).AsInteger := 200;
end;

However now that I see this code it does have some performance implications.

None the less I want easy of code writing.

Thus an explicit overload is probably required:

Integer( A * B * C * D ) := 100;

or

PInteger( A * B * C * D ) ^ := 100;

These options/ideas will be examined/explored in next postings ;) :)

For now here is the test program for multiple operator order:

// *** Begin of Test Program ***

program TestProgram;

{$APPTYPE CONSOLE}

{

Test (Delphi XE) multiply overloaded operator order

version 0.01 created on 1 juli 2011 by Skybuck Flying

}

uses
SysUtils;

type
TDummyType = record
private
vString : string;
public
constructor Create( ParaNumber : integer );

class operator Multiply( ParaA, ParaB : TDummyType ) : TDummyType;
end;

constructor TDummyType.Create( ParaNumber : integer );
begin
vString := IntToStr(ParaNumber);
end;

class operator TDummyType.Multiply( ParaA, ParaB : TDummyType ) :
TDummyType;
begin
result.vString := '(' + ParaA.vString + '*' + ParaB.vString + ')';
end;

procedure Main;
var
vDummy1 : TDummyType;
vDummy2 : TDummyType;
vDummy3 : TDummyType;
vDummy4 : TDummyType;
vResult : TDummyType;
begin
vDummy1 := TDummytype.Create( 1 );
vDummy2 := TDummytype.Create( 2 );
vDummy3 := TDummytype.Create( 3 );
vDummy4 := TDummytype.Create( 4 );

writeln('vResult := vDummy1 * vDummy2 * vDummy3 * vDummy4;');
vResult := vDummy1 * vDummy2 * vDummy3 * vDummy4;
writeln( vResult.vString );
writeln;

writeln('vResult := (vDummy1 * (vDummy2 * (vDummy3 * vDummy4)));');
vResult := (vDummy1 * (vDummy2 * (vDummy3 * vDummy4)));
writeln( vResult.vString );
writeln;

writeln( 'vResult := (vDummy1 * vDummy2) * (vDummy3 * vDummy4);');
vResult := (vDummy1 * vDummy2) * (vDummy3 * vDummy4);
writeln( vResult.vString );
writeln;

end;

begin
try
Main;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
ReadLn;
end.

(*

// this proves left to right order for the first one:

Output:

vResult := vDummy1 * vDummy2 * vDummy3 * vDummy4;
(((1*2)*3)*4)

vResult := (vDummy1 * (vDummy2 * (vDummy3 * vDummy4)));
(1*(2*(3*4)))

vResult := (vDummy1 * vDummy2) * (vDummy3 * vDummy4);
((1*2)*(3*4))

*)

// *** End of Test Program ***

Bye,
Skybuck.

John Gordon

7/1/2011 2:24:00 AM

0

In <ebc69$4e0d1c98$5419acc3$20363@cache2.tilbu1.nb.home.nl> "Skybuck Flying" <Windows7IsOK@DreamPC2006.com> writes:

> Here is a little demonstration program to inspect/demonstrate Delphi's
> multiply operator order, plus output:

> Delphi's multiply order is from left to right, which makes me wonder a bit
> what free pascal's order and C's order and C++'s order and perhaps Java's
> order is ?! ;)

Since multiplication is associative, I would think the order of
multiplication is immaterial.

Did you mean to say order of *evaluation* ?

--
John Gordon A is for Amy, who fell down the stairs
gordon@panix.com B is for Basil, assaulted by bears
-- Edward Gorey, "The Gashlycrumb Tinies"