Good looking definition?

BlitzMax Forums/BlitzMax Beginners Area/Good looking definition?

Casaber(Posted 2016) [#1]
So I really want to define something with style in BMax but I´m failing miserable.
With primitive types you may say something like:

Local name:int = 1

I would like to define new types with methods and functions and be able to define them like so:
Local name:sometype = someparameter


So far I 've only succeeded with this:

Local name:sometype = New sometype ; name = sometype.Create(someparameter)


As you can see It gets very ugly (too verbose and repetive. e.g. nonmeanful words like New And Create, and type is said 3 times instead of 1, the name is said 2 times instead of 1.
Are there any OO wizardry that could explain this problem from a nice angle how to do this?


I would like to be able to create something similiar to this:


' Create
Local name:owntype = parametershere ' < the idea I´m striving for (something according to those lines)

' ... and Use (using works fine already)
name.strawberryfield = 100
name.add(10,20,"plus")


Wiebo(Posted 2016) [#2]
Bmax doesn't work that way.


Derron(Posted 2016) [#3]
Function CreateMyObj:myobj(param:int)
  Local o:myobj = new myobj
  o.strawberryfield = param
  Return o
End Function

Local myO:myobj = CreateMyObj(15)


Bye
Ron


Henri(Posted 2016) [#4]
Hi,

alternatively you can hide the function inside your type:
Local ts:Tsome = Tsome.Create(12, 20)

ts.ShowAll()

Type Tsome
	
	Field x:Int, y:Int
	
	Function Create:Tsome(x:Int, y:Int)
		Local ts:Tsome = New Tsome
		
		ts.x = x
		ts.y = y
		
		Return ts
	EndFunction
	
	Method ShowAll()
		Print x + " | " + y
	EndMethod
EndType


-Henri


Brucey(Posted 2016) [#5]
And alternatively, for better polymorphism, you can use a method :
Local ts:Tsome = New Tsome.Create(12, 20)

ts.ShowAll()

Type Tsome
	
	Field x:Int, y:Int
	
	Method Create:Tsome(x:Int, y:Int)
		Self.x = x
		Self.y = y

		Return Self
	End Method
	
	Method ShowAll()
		Print x + " | " + y
	EndMethod
EndType

which facilitates subclass instantiation.


Casaber(Posted 2016) [#6]
@Derron

Thanks, that worked If I changed it and added a type before the create function.

like so

Local name:type = type.create(parameters)

That's my best shot so far, any other ideas?
Still there are clones of the type, there's the keyword create (or any other keyword I name it but I seem to
need a keyword)

Are there no Constructors available to get away with create? Or similiar?


dw817(Posted 2016) [#7]
Now Casaber, don't forget, you can just make everything strings and sort it out from there in future code.

"@fruit=nnnn" where the "@" in the front defined the variable TYPE, whichever you want to build and interpret, then stuff the value after the equals. Make that an array if you so choose.

I've done this many times to create unique variable types.


Casaber(Posted 2016) [#8]
@Henri Thanks, that gives the exact same creation syntax as Derron.
It seemed that my code already was able to do that I discovered I just did not knew about that syntax.

You also added inside it Local ts:Tsome = New Tsome I got lost there what that means. It defines a type of itself within itself?

@Brucey that will add a "new" keyword, what kind of flexibility does that give in simple terms?


Casaber(Posted 2016) [#9]
@dw817

This is not strings though, I guess I explained abit poorly I tried my sleepy best. I´m trying to learn how Bmax does it's OO.

Wanna see the mess?
It's a bit hefty for most eyes I know I just need something up and running this evening hopefully.

The important bit is the Create method, and the two top lines which are how everything will look when you actually use it.
It should look good.

It's for my Shader VBO. And I thought I'll try to code a proper library. So I wanted a nice interface you know?


Local shader:TShader = New TShader ; shader=TShader.Create(VERTEX,PIXEL) '  Old way

Local shader:Tshader = Tshader.Create(VERTEX,PIXEL) ' New way (best so far)

Type TShader
	Field PO:Int
	
	Function Create:TShader(VC:String,PC:String) ; Local p:TShader = New TShader ; p.PO = glCreateProgramObjectARB()
	    VS = glCreateShader(GL_VERTEX_SHADER) ; PS = glCreateShader(GL_FRAGMENT_SHADER)
    	Local SC:Byte Ptr ; Local SL:Int
    	SC = VC.ToCString() ; SL = VC.length ; glShaderSourceARB VS,1,Varptr SC,Varptr SL ; MemFree SC ; glCompileShaderARB VS
    	SC = PC.ToCString() ; SL = PC.length ; glShaderSourceARB PS,1,Varptr SC,Varptr SL ; MemFree SC ; glCompileShaderARB PS
		glAttachObjectARB p.PO,VS ; glLinkProgramARB p.PO ; glAttachObjectARB p.PO,PS ; glLinkProgramARB p.PO ; glDeleteObjectARB VS ; glDeleteObjectARB PS
		Return p
	End Function
	
	Method bind() ; glUseProgramObjectARB PO ; End Method
	Method unbind() ; glUseProgramObjectARB 0 ; End Method
	Method UI1(name:String,v:Int) ; Local SC:Byte Ptr = name.ToCString() ; Local l:Int = glGetUniformLocationARB(PO,SC) ; glUniform1iARB l,v ; MemFree SC ; End Method
	Method UF1(name:String,v:Float) ; Local SC:Byte Ptr = name.ToCString() ; Local l:Int = glGetUniformLocationARB(PO,SC) ; glUniform1fARB l,v ; MemFree SC ; End Method
    Method UF2(name:String,v1:Float,v2:Float) ; Local SC:Byte Ptr = name.ToCString() ; Local l:Int = glGetUniformLocationARB(PO,SC) ; glUniform2fARB l,v1,v2 ; MemFree SC ; End Method
End Type




dw817(Posted 2016) [#10]
Nono Casaber. Strings can carry, well, just about anything ! Remember when we talked about using "#" as the first character in a string to let you know it was a number ?

This is quite similar. You look for an "@" as the first character to a string, and that tells you what KIND of variable you want to make it. Then get the data after the "=" sign, and you're all set !
global vartyp$[5]
vartyp$[0]="@box=1,2,3,4"
vartyp$[1]="@triangle=1,2,3"
vartyp$[2]="@desc=For more information press [F1]"
vartyp$[3]="#863474"
vartyp$[4]="Frederick"
See ? It's easy ! :)

@box expects to have 4 comma delimited numbers
@triangle expects 3 of them
@desc is a special variable you can set and have appear anywhere (object-oriented)
# is of course just a number that you can add, subtract to, or what have you
and the last is not denoted as special, so - it's just treated as a normal string.


Derron(Posted 2016) [#11]
My code is of course _not_ what I would suggest to do, but created the "shortest" part after the "=".

Each "Type" has an inbuilt method named "new", this is calles as soon as you create an instance of a type.
If you do a "mytype = new TMyType" then a new instance of "TMyType" is created, and the "new()" method is called.

In Blitzmax it is not possible to adjust the "new()" to accept parameters.


Difference between "Function Create:TMyObject(params...)" and "Method Create:TMyObject(params...)" is, that the Method can only be called by an already existing instance - this is why Brucey did a "new TSome.Create(...)".
A Function is callable by the type but therefore needs to create an instance on its own (this is what Henri posted).

Other differences? I cannot come up with some, so I think there are none.


Small hint: While BlitzMax does not allow overloading, so you cannot do the following:
Method Create:TMyObject(param:int)...
Method Create:TMyObject(param:int, param2:string)...

But what you are able to do is, to override the returned object type (in extended types).

SuperStrict

Type TTypeA
  Field a:int

  Method Init:TTypeA(param:int)
    a = param
    return self
  End Method

  Method SetA:TTypeA(param:int)
    a = param
    return self
  End Method
End Type

Type TTypeB extends TTypeA
  Field b:int

  Method Init:TTypeB(param:int)
    Super.Init(param * 4) 'this delegates some work to TTypeA
    b = param * 2
    return self
  End Method

  'override to allow chaining
  Method SetA:TTypeB(param:int)
    Super.SetA(param * 4)
    return self
  End Method

  Method SetB:TTypeB(param:int)
    b = param
    return self
  End Method
End Type

local myA:TMyTypeA = new TMyTypeA.Init(5).SetA(10)
local myB:TMyTypeB = new TMyTypeB.Init(5).SetA(10).SetB(5)



bye
Ron


Casaber(Posted 2016) [#12]
@dw817 I think I will try use that somehow somewhere maybe even in this code but here my problem is that BMax has verbose syntax.

It seem that I need to do this:

Local shader:Tshader = Tshader.Create("Depresso")

When I would like to be able to do this:

Local shader:Tshader("all I need is a cup of Coffee")


dw817(Posted 2016) [#13]
You can create your own mini-programming language or interpreter if you want, Casaber. Many times in big code I've written, I encapsulate an interpreter to make up for what the programming language cannot do.

Let's see ... I really don't know anything about that shader definition, but I know you can do this.
Local shader:TShader,var$

var$="@ts:tshader=all I need is a cup of coffee"

If Left$(svar$,3)="@ts" Then shader=tshader.Create(Mid$(svar$,Instr(svar$,"=")+1))
That may not be a perfect example, but I think you can see how to work this in.

. . .

Will be gone for a few hours, BBL.


Casaber(Posted 2016) [#14]
@Derron That was a bumpy ride, Okay'l think I will have to take it slow. I´m okayish with this new syntax I got now.

I think I'll keep it on this level for while now.


Casaber(Posted 2016) [#15]
I just want to fill you in quickly what I ended up that evening becuase I thought it was, but a very powerful sugar hack. At times when you really want it. And now i did.


Here it is:

Okay the goal was to find something easy on the eyes for own types, similiar to inbuiilt primitivetypes such aa
' Local number:int = 0

This was my 1st attempt:
Local shader:TShader = New TShader ; shader=TShader.Create(VERTEX,PIXEL)

Which could be simplified into this:
Local shader:Tshader = Tshader.Create(VERTEX,PIXEL)

But then I got the idea to add this line in the code
Function CreateTShader:TShader(VC:String,PC:String) ; Return TShader.Create(VC,PC) ; End Function

Now I'm able to use this final version:
Local shader:Tshader = CreateTShader(VERTEX,PIXEL)

I guess I's also a way to overload inbuilt Bmax commands into own Types aswell.

I´m not saying it's a good hack if you use OO in all kinds of way - but it might.


Brucey(Posted 2016) [#16]
But then I got the idea to add this line in the code...

Which is basically what Derron posted in #3.

btw, I don't think your code examples are at all user-friendly or readable.
Having as many statements as possible on a single line (separated by semi-colons) is neither clear nor easily maintainable.


Casaber(Posted 2016) [#17]
I only code because I find it fun, and the style that you see goes along with that fun.

It's actually very maintainable for the right eyes, even if it's not "mainstream".

I''m basically offering you to see something very personal when I show any code.
I don´t have the strength right now to convert back and forth between "mainstream" and my own.

Hope I won´t be neeeding to here that kind of critics in the future on the style becuase you're gonna see plenty of it.

If that's not okay I will not use this forum.


Derron(Posted 2016) [#18]
Even if you do not go d'accord with it - I have to support Brucey's argument.

Function CreateTShader:TShader(VC:String,PC:String) ; Return TShader.Create(VC,PC) ; End Function


Is not very readable. Imagine you add 2 other options
Function CreateMyObject:TMyObject(a:string,b:TMySimpleType,c:TAnotherType=null,d:int var) ; Return TMyObject.Create(a,b,c,d); End Function


While it saves on "lines of code"-count, you will more likely miss things in so long lines. This is the reason, many editors have this vertical line at about 72 characters. It stresses the eyes if you have to read long lines.

I try to avoid long lines if possible, especially if there is no need (";" forcefully puts things on one line). Only real exceptions for me, are longer formulas (or parts just being 3-4 chars longer than the "virtual limit"). This is because Blitzmax needs those ".." to connect multiple lines belonging together - which makes manual formatting less easier then automatic recognition (brackets, or "then" - or recognition of "or" or "and")...
... so in your example, I would not "multi-line" a big function header just because of so many params, but the "type.Create(a,b..)" would be done on another line, same for "End Function".

As long as you keep the code for yourself long lines might be something like a "personal taste", but when working with teams (or publishing your code) it is a good idea to try to follow a certain "guideline".
So nobody "forbids" to use the style you like, they (Brucey and Myself in this case) just give hints / personal thoughts on what you posted.


@CreateTShader()
For me such helper functions are known in BlitzMax to add "convenience" for non-oop-coders.
There is "LoadStream()" which calls "stream.load()", "LoadImage()" which calls "image.Load()" ... and other things.


Nonetheless I also would like to see custom "New()" methods (local shader:TShader = new TShader(param,param2)) but we did not get it with vanilla, so we wont see it added there (backwards compatibility?).
A "local shader := TShader(bla)" would be nice too (auto type setting - like it is allowed in Monkey).
But hey, it works well without these convenience functions and you code things once instead of retyping it for every execution.


BTW:
Local shader:TShader = New TShader ; shader=TShader.Create(VERTEX,PIXEL)
could be done as
Local shader:TShader = New TShader.Create(VERTEX,PIXEL)
(the Create() is then a method, returning "self", this is what Brucey described in #5)

At the end it is all a matter of taste - and once you publish your taste, other cooks will come and drop a line about that :-)

bye
Ron


dw817(Posted 2016) [#19]
Not to bite the already bit butt but I did mention this one earlier time, Casaber.

I only use ";" to define values if they'll fit within the first 50-characters, in fact I try very hard not to have any single line greater than 50-characters if at all possible.

a=deskrect[0] ; b=deskrect[1] ; c=deskrect[2] ; d=deskrect[3]

which for me is pretty extreme. Also

If grid ' operate on grid ?
x=bound(x,4,maxx) ; y=bound(y,4,maxy) ' it's all on a tight
h=h/4*4 ; v=v/4*4 ; x=x/4*4 ; y=y/4*4 ' grid
endif

But as you can see once again these are just like-minded variables being set so it's not too hard to follow, and you don't have to scroll the page to see any of the commands or functions.

No, it's when you mix actual BlitzMAX commands on a single line does it get a little obfuscated.

Function erasepixels(img:Timage,h,v,x,y) ; local i,j,p:tpixmap ; for i=0 until y ; for j=0 until x ; writepixel p,h+j,v+i,0 ; next ; next ; endfunction

This does bring up an interesting point. GFA had the ability of 'folding' functions. That is, I could press [F11] at the beginning of a function I created and it would change it to:

Function erasepixels(img:Timage,h,v,x,y) and not show the contents of the function until I pressed [F11] again, keeping your source code easy to read, especially when you were satisfied with the contents of any of your functions.

Perhaps, Casaber, this is what you want to keep your coding readable ?

Guys, is this ability available in MaxIDE ?