Heres an example of what I call “macro templates” in PureBasic. A template encapsulates certain functionality, allowing you to dynamically generate the code in a flexible manner.

This example implements a bare-bones n-vector library using a structure and a static array:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
Macro Vector_Register( _n, _type ) Structure VECTOR#_n#_type vector._type[_n] EndStructure Procedure._type Vector#_n#_type#_Add( *r.VECTOR#_n#_type, *a.VECTOR#_n#_type, *b.VECTOR#_n#_type ) Define.i i For i=0 To _n - 1 *r\vector[i] = *a\vector[i] + *b\vector[i] Next EndProcedure Procedure._type Vector#_n#_type#_Subtract( *r.VECTOR#_n#_type, *a.VECTOR#_n#_type, *b.VECTOR#_n#_type ) Define.i i For i=0 To _n - 1 *r\vector[i] = *a\vector[i] - *b\vector[i] Next EndProcedure Procedure._type Vector#_n#_type#_Divide( *r.VECTOR#_n#_type, *a.VECTOR#_n#_type, *b.VECTOR#_n#_type ) Define.i i For i=0 To _n - 1 If *b\vector[i] <> 0.0 And *a\vector[i] <> 0.0 *r\vector[i] = *a\vector[i] / *b\vector[i] EndIf Next EndProcedure Procedure._type Vector#_n#_type#_Multiply( *r.VECTOR#_n#_type, *a.VECTOR#_n#_type, *b.VECTOR#_n#_type ) Define.i i For i=0 To _n - 1 *r\vector[i] = *a\vector[i] * *b\vector[i] Next EndProcedure Procedure._type Vector#_n#_type#_DotProduct( *a.VECTOR#_n#_type, *b.VECTOR#_n#_type ) Define.i i Define._type result For i=0 To _n - 1 result + ( *a\vector[i] * *b\vector[i] ) Next ProcedureReturn result EndProcedure Procedure._type Vector#_n#_type#_Length( *v.VECTOR#_n#_type ) ProcedureReturn Sqr( Vector#_n#_type#_DotProduct( *v, *v ) ) EndProcedure Procedure._type Vector#_n#_type#_Distance( *a.VECTOR#_n#_type, *b.VECTOR#_n#_type ) Define.VECTOR#_n#_type temp Vector#_n#_type#_Subtract( temp, *a, *b ) ProcedureReturn Vector#_n#_type#_Length( temp ) EndProcedure Procedure.s Vector#_n#_type#_Debug( *v.VECTOR#_n#_type, Decimals.i=#PB_Default ) Define.i i Define.s tmp = "[" For i=0 To _n-1 tmp + StrF( *v\vector[i], Decimals.i ) If i <> _n-1 tmp + ", " EndIf Next tmp + "]" Debug tmp ProcedureReturn tmp EndProcedure EndMacro |

It might look strange/complicated at first, but once you read it you’ll realize it’s fairly simple.

Let’s see the usage of this particular template:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
Vector_Register(3, f ) ; Register a float "vec3". Define.VECTOR3f a,b,c ; Define a few vectors with the new structure. a\vector[0] = 10.0 a\vector[1] = 20.0 a\vector[2] = 30.0 b\vector[0] = 100.0 b\vector[1] = 200.0 b\vector[2] = 300.0 Vector3f_Add(c, a, b) ; c = a + b Vector3f_Debug(c) ; show each element using the debug output. Debug Vector3f_DotProduct( a, b ) Debug Vector3f_Length( a ) Debug Vector3f_Distance( a, b ) |

Cool, huh?. And you can define any amount of elements with any basic type.

Of course we sacrificed speed for flexibility. In those cases where we have to define n-vectors, this would be an ideal solution. For everything else, I suggest a specific library, such as my vec3 macro lib.

Using this principle you can abstract almost anything, within reason. One good example is my object factory template. Ideally one would have arrays, lists, etc. Implemented in this very same way, in such case the possibilities would be endless and you’d be able to define dynamic lists/arrays inside structures, etc.

I strongly advice you to implement at least one of those templates, even if it’s just for an exercise.

Having the extra tools can’t hurt!

Cheers.