More on macros/defines and Freebasic in general... plzplzplz

General discussion for topics related to the FreeBASIC project or its community.
agamemnus
Posts: 1842
Joined: Jun 02, 2005 4:48

More on macros/defines and Freebasic in general... plzplzplz

Post by agamemnus »

So, I believe that C++ has a way to create macros out of functions for speed. This functionality is sorely lacking in Freebasic...

Here's the situation. I have a uinteger pointer pointer that my program modifies all day long: "dim buildingAmountByPlayer as uinteger ptr ptr"

The use is:

Code: Select all

buildingAmountByPlayer[playerId][buildingId] = buildingAmount
------

I want to attach a way to modify my "building type count" every time that I modify the variable. (The "building type count" would be the amount of unique building types that are built... so, 5 schools would count as +1, and 3 bakeries as +1, but 0 stables would not count.)

This addition is fairly simple. Here are two cases, setting and adding/subtracting:

Code: Select all

'ORIGINAL (setting):
buildingAmountByPlayer[playerId][buildingId] = buildingAmount

'NEW (setting):
if buildingAmount > 0 then
 if buildingAmountByPlayer[playerId][buildingId] = 0 then buildingCountByBuildingType[playerId] += 1
else
 if buildingAmountByPlayer[playerId][buildingId] then buildingCountByBuildingType[playerId] -= 1
end if


'ORIGINAL (adding/subtracting):
buildingAmountByPlayer[playerId][buildingId] += buildingAmount

'NEW (adding/subtracting):
scope
 dim as uinteger buildingAmountOldTemp = buildingAmountByPlayer[playerId][buildingId]
 dim as uinteger buildingAmountNewTemp = buildingAmountOldTemp + buildingAmount


if buildingAmountNewTemp > 0 then
 if buildingAmountOldTemp = 0 then buildingCountByBuildingType[playerId] += 1
else
 if buildingAmountOldTemp then buildingCountByBuildingType[playerId] -= 1
end if

buildingAmountByPlayer[playerId][buildingId] += buildingAmount
end scope
The straightforward way to do that in Freebasic is to make buildingAmountByPlayer a private variable and add a getter and setter function. There problem with this is that there is a huge speed hit due to the amount of extra times I'd be jumping to the function and copying variables.

Another way, which I believe exists in C++ and partly in Freebasic, is to create inline functions, or macros.


I can create two macros for setting and adding/subtracting and a macro for the getter. Thus, I won't have the speed hit that is associated with small functions.

Since there's not really that much extra code here and the amount of instances in the code (not the amount of times I'll be calling, which is much greater) is small, there's a negligible memory hit.

There is a huge issue with Freebasic macros that make them fairly unwieldy to use and dirty, however.

THERE IS NO SCOPE

The buildingAmountByPlayer and buildingCountByBuildingType variables are contained in a UDT. I want my macro similarly contained, but the macro namespace is global.

(A) This forces me to have a huge function name instead of a huge one.

(B) it prevents me from having a clean getter. Theoretically, I need to be able to get these:

getbuildingAmountByPlayer(p) ' A pointer to the building list for that player.
getbuildingAmountByPlayer(p, buildingId) ' Just an amount.

I can't do that with the current macros since I'll get a duplicate definition!

(C) The programmer can't set getbuildingAmountByPlayer to PRIVATE (it should be private!!) because the macro can only work on public variables. (since it's just a macro) Again, theoretically, proper scoping (allowing macros to be put in UDTs) should fix this.
marcov
Posts: 3462
Joined: Jun 16, 2005 9:45
Location: Netherlands
Contact:

Re: More on macros/defines and Freebasic in general... plzpl

Post by marcov »

agamemnus wrote:So, I believe that C++ has a way to create macros out of functions for speed. This functionality is sorely lacking in Freebasic...
Macros are source editing, and have nothing to do with speed. In classic C development systems they are mostly already resolved before the actual compiler is executed.

It is the inline bit that performs the boost, and for that every non ackward language has the ability to inline functions/methods/property getters and setters. (if you have global properties)

macros are just poor man's workaround when that is lacking. They can't be scoped because they operate on text before the compiler analysed thus, and thus can't make use of information that the compiler yet has to discover.
agamemnus
Posts: 1842
Joined: Jun 02, 2005 4:48

Re: More on macros/defines and Freebasic in general... plzpl

Post by agamemnus »

marcov wrote:
agamemnus wrote:So, I believe that C++ has a way to create macros out of functions for speed. This functionality is sorely lacking in Freebasic...
Macros are source editing, and have nothing to do with speed. In classic C development systems they are mostly already resolved before the actual compiler is executed.

It is the inline bit that performs the boost, and for that every non ackward language has the ability to inline functions/methods/property getters and setters.
Semantics.... I include "inline functions" into the macro family. Changing the source changes the speed, so I don't know how you can say they have nothing to do with speed.
(if you have global properties)
???
macros are just poor man's workaround when that is lacking. They can't be scoped because they operate on text before the compiler analysed thus, and thus can't make use of information that the compiler yet has to discover.
Well... that's how it is in Freebasic now. C++'s inline functions are also just smart macros in that they replace the source code, but in a more sophisticated method than Freebasic macros.

I don't really care all that much whether the Freebasic #macro and #define are replaced with a new inline option (though obviously you need that for backwards-compatibility)... just want an inline function. =[

So yes, you re-hashed the problem well. :P

Now code me the solution! :P
cha0s
Site Admin
Posts: 5319
Joined: May 27, 2005 6:42
Location: USA
Contact:

Post by cha0s »

C++'s inline functions are also just smart macros in that they replace the source code, but in a more sophisticated method than Freebasic macros.
Do you just make up sentences before you post? :P Inline functions are unfortunately a bit more complex than just 'smart macros'.
agamemnus
Posts: 1842
Joined: Jun 02, 2005 4:48

Post by agamemnus »

How about .... really, really smart macros that need a ton of code and backbreaking labor to implement?
marcov
Posts: 3462
Joined: Jun 16, 2005 9:45
Location: Netherlands
Contact:

Re: More on macros/defines and Freebasic in general... plzpl

Post by marcov »

agamemnus wrote:

Semantics.... I include "inline functions" into the macro family.
Well, if you read my reply carefully then you'll see that semantics are exactly the problem. Type and block information are derived in the so called semantic pass of the compiler. Which only happens AFTER macro processing.
Changing the source changes the speed, so I don't know how you can say they have nothing to do with speed.
No. The two different implementations are the speed difference, one where you call a standalone function, and one where essentially the code is copied.

How that is achieved (inline functions, copy and paste or macros) doesn't matter for speed just for maintenance.

Then copy and pasteing of code is unmaintainable for the user,

Macros systems however are unmaintainable for the compiler builder, so you end up with inline functions and/or generics.

And that goes doubly for when you start expanding macro systems adhoc to workaround compiler deficiencies. Such hack always come with a set of limitations, corner cases and maintenance burden to later implement any new feature with a global scope.

The fact that inline functions and generics seem to be currently the best (though still imperfect) solutions is a bitter pill to swallow since they are both, as cha0s already said, not exactly the easiest bits. (and generics can be said to be the worst (common) feature in a compiler to implement)

But it remains a slippery slope when you start expanding temporary hacks (and never work on the real functionality anymore
(if you have global properties)
???
Properties are things that "work" like variables first order (e.g. you can simply use them in an expression) but call getters and setters under the hood.

Typically they are part of classes though. I don't know if it is a general term, but when they are not part of some class namespace then we call them "global properties". One of the key uses is in TLS (thread local storage) systems, to have a global variable that holds a value per thread.
Well... that's how it is in Freebasic now.
True. But now macros have no scope either :-)
C++'s inline functions are also just smart macros in that they replace the source code, but in a more sophisticated method than Freebasic macros.
No. Inline functions are different, they are processed in a different stage of the compilation process when there is way more info present. And typically the result is checkable much easier, and in case of an error it is way easier (for the compiler) to point out where the error is.
I don't really care all that much whether the Freebasic #macro and #define are replaced with a new inline option (though obviously you need that for backwards-compatibility)... just want an inline function. =[
Good. Now you get the point :-)
So yes, you re-hashed the problem well. :P

Now code me the solution! :P
To be honest I wouldn't know how to do that in FB even in theory. THe only experience I have with inline functions (and that is not from a compiler devel perspective) is within module systems.
Last edited by marcov on Jul 09, 2011 19:58, edited 1 time in total.
agamemnus
Posts: 1842
Joined: Jun 02, 2005 4:48

Post by agamemnus »

I wonder whether just using the C++ compiler can allow us to add inline functions, but probably once the FB code is sent, it's too low-level to allow such processing...?
marcov
Posts: 3462
Joined: Jun 16, 2005 9:45
Location: Netherlands
Contact:

Post by marcov »

agamemnus wrote:I wonder whether just using the C++ compiler can allow us to add inline functions, but probably once the FB code is sent, it's too low-level to allow such processing...?
It is possible, but afaik the problem with C++ is that the inline functionality is incomplete and the function must be lifted from the source to the header.

You can't simply take code as-is and simply say "now you must be inline" like in other language.
dodicat
Posts: 7983
Joined: Jan 10, 2006 20:30
Location: Scotland

Post by dodicat »

marcov wrote:
It is possible, but afaik the problem with C++ is that the inline functionality is incomplete and the function must be lifted from the source to the header.

You can't simply take code as-is and simply say "now you must be inline" like in other language.
Hi marcov
Are macros compiled inline in fb?
If you use the -pp switch once you have set up the macros and compile the ~pp.bas, is this not the same as compiling the original file?
Here's an example with macros for factorial, the ~pp.bas is about 30 lines longer, but it works at the same speed and both the exe files for original and ~pp are the same size.

Code: Select all


Function Factorial( n As String) As String
#macro plus(_num1,_num2)
Do
    _flag=0
    #macro finish()
    answer=Ltrim(answer,"0")
    If _flag=1 Then Swap _num2,_num1
    Exit Do
    #endmacro
    If Len(_num2)>Len(_num1) Then 
        Swap _num2,_num1
        _flag=1
    Endif
        
        diff=Len(_num1)-Len(_num2)
        answer="0"+_num1
        addcarry=0
        For n_=Len(_num1)-1 To diff Step -1 
            addup=_num2[n_-diff]+_num1[n_]-96
            answer[n_+1]=ADDQmod(addup+addcarry)
            addcarry=ADDbool(addup+addcarry)
        Next n_ 
        If addcarry=0 Then 
            finish()
        Endif
            If n_=-1 Then 
                answer[0]=addcarry+48
                finish()
            Endif
                For n_=n_ To 0 Step -1 
                    addup=_num1[n_]-48
                    answer[n_+1]=ADDQmod(addup+addcarry)
                    addcarry=ADDbool(addup+addcarry)
                Next n_
                answer[0]=addcarry+48
                finish()
                Exit Do
            Loop
            #endmacro
            
            #macro mult(num1,num2)
            flag=0
            If Len(num2)>Len(num1) Then
                flag=1
                Swap num2,num1
            End If
            three="0"&num1
            accum="0"
            For n1 As Integer=Len(num2)-1 To 0 Step -1
                multcarry=0
                For n2 As Integer=Len(num1)-1 To 0 Step -1 
                    multadd=multaddtable(num2[n1],num1[n2])
                    three[n2+1]= threetable(multadd,multcarry)
                    multcarry=multcarrytable(multadd,multcarry) 
                Next n2
                three[0]=multcarry+48
                plus(three,accum)
                accum=answer
                three=three+"0"
            Next n1
            If flag=1 Then Swap num2,num1
            #endmacro
            
Dim  As Ubyte multcarrytable( 81,9),threetable( 81,9),multaddtable(48 To 57,48 To 57)
Dim As Ubyte ADDQmod(0 To 19),ADDbool(0 To 19) 
                For x As Integer=48 To 57
                    For y As Integer=48 To 57
                        multaddtable(x,y)=(x-48)*(y-48)
                    Next y
                Next x
                For x As Integer=0 To 81
                    For y As Integer=0 To 9
                        threetable(x,y)=((x + y) Mod 10)+48
                        multcarrytable(x,y)=(x+y-(x+y) Mod 10)\10
                    Next y
                Next x 
                For z As Integer=0 To 19
                    ADDQmod(z)=Cubyte(z Mod 10+48)
                    ADDbool(z)=Cubyte(-(10<=z))
                Next z 
                
                Dim  As Ubyte addup,addcarry
                Dim As Integer multadd,multcarry,diff,n_
                Dim As Byte flag,_flag
                Dim As String accum,three,answer,z="0",fact="1",one=fact
                If Ltrim(n,"0")="" Then Return fact
                Do
                    plus(z,one)
                    z=answer
                    mult(fact,z)
                    fact = accum
                Loop Until z=n
                Return fact
            End Function
            
            Dim As Double t1,t2
            Dim As String ans
            t1=Timer
            ans=factorial("2011")
            t2=Timer
            Print ans
            Print "Time ";t2-t1
            Sleep
            
TJF
Posts: 3809
Joined: Dec 06, 2009 22:27
Location: N47°, E15°
Contact:

Post by TJF »

@dodicat

fbc does the preprocessing before the compiling. Your .bas file is checked for '#' statements and they get expanded in to an intermediate version of your .bas file, called ~pp.bas. The option -pp writes this intermediate version to the disk (causes that the intermediate version gets not removed).

So since ~pp.bas is created from your .bas it must be the same as compiling the original.
marcov
Posts: 3462
Joined: Jun 16, 2005 9:45
Location: Netherlands
Contact:

Post by marcov »

dodicat wrote:
Are macros compiled inline in fb?
Depends on how you defined "compiled". Macros are inlined in the text on a textual level (generating the ~pp.bas as TJF explained), inline functions work on the code level, copying a part of the compile tree from one place to the other.

The second part allows much more control (since there is way more information about typing etc available) , but is also a lot more difficult to do.

Much depends on the state of the compiler code. FPC had to rewrite much of its own homegrown 1.0 version into something more maintainable before taking this step.

It is possibly we have to do this again for proper generics. Currently generics are based on token replay (which is macro like functionality, albeit one step deeper), just like C++.

Other languages (like e.g. C#, and more important for us, Delphi) process generics at a deeper level, which is easier long term, and has slightly more possibilities.


C++ is a bit ackward in these things because it has to workaround the interface-implementation separation (which e.g. C# or Java don't have), but no grownup ways to manage that (which e.g. Pascal does with a welldefined module system. Moreover the severe backwards compatibility requirement with C imposed many limitations in C++ design.
agamemnus
Posts: 1842
Joined: Jun 02, 2005 4:48

Post by agamemnus »

Another thing that this would allow me to do (and I've had this issue for a long time) is to partially resolve this issue:

I want accessing the variable to be public, but modifying it to be private. The reasoning here is that the setter method can change (with the addition of related commands to the setting of the variable), while the getter almost always stays the same.

That's impossible in the current scheme... only way is via a getter and setter, which are slow.
marcov
Posts: 3462
Joined: Jun 16, 2005 9:45
Location: Netherlands
Contact:

Post by marcov »

agamemnus wrote:Another thing that this would allow me to do (and I've had this issue for a long time) is to partially resolve this issue:

I want accessing the variable to be public, but modifying it to be private. The reasoning here is that the setter method can change (with the addition of related commands to the setting of the variable), while the getter almost always stays the same.

That's impossible in the current scheme... only way is via a getter and setter, which are slow.
Keep in mind that information hiding is only a first order solution. The first level protects the most. If more information hiding would be really better, we would have ACLs per keyword now :-)
agamemnus
Posts: 1842
Joined: Jun 02, 2005 4:48

Post by agamemnus »

marcov wrote:
agamemnus wrote:Another thing that this would allow me to do (and I've had this issue for a long time) is to partially resolve this issue:

I want accessing the variable to be public, but modifying it to be private. The reasoning here is that the setter method can change (with the addition of related commands to the setting of the variable), while the getter almost always stays the same.

That's impossible in the current scheme... only way is via a getter and setter, which are slow.
Keep in mind that information hiding is only a first order solution. The first level protects the most. If more information hiding would be really better, we would have ACLs per keyword now :-)
It is better. It helps avoid mistakes... (/runs)
marcov
Posts: 3462
Joined: Jun 16, 2005 9:45
Location: Netherlands
Contact:

Post by marcov »

agamemnus wrote:
Keep in mind that information hiding is only a first order solution. The first level protects the most. If more information hiding would be really better, we would have ACLs per keyword now :-)
It is better. It helps avoid mistakes... (/runs)
WAAAYP
Post Reply