Giving an alias to an object (udt/variable/function)

Post your FreeBASIC tips and tricks here. Please don’t post your code without including an explanation.
Tourist Trap
Posts: 2762
Joined: Jun 02, 2015 16:24

Giving an alias to an object (udt/variable/function)

Postby Tourist Trap » Nov 09, 2016 2:21

While thinking of this second version, new common situations have come into my sight. I'm just listing them, altogether with the previous stuff, and with a word of the why and how.
I don't claim here again showing the best pratices ever, but the topic as a whole is interesting. It could hardly be left untouched at all.
If one knows about best pratices anyway, he can just leave a useful remark around here.
(Thanks to fxm for the first recommendations that helped me reformulating the things)


Renaming - A showcase

I - Resolving conflicts
One of the interest of renaming symbols is to fix conflict when they meet together under a same identifiers.
  • A - Resolving conflict with the help of namespaces blocks
  • B - Resolving conflict with the help of #UNDEF

A - Resolving conflict with the help of namespaces blocks
Take two source files. A main current source is modifiable. And a source file imported, via the #include instruction, has its content that must keep untouched.

case 1
we may embed the imported stuff into a namespace to distinguish conveniently its declarations from the current declarations in use.
Remark: works only if the imported source contains nothing but declarations, or functions/subroutines blocks.
Performance overhead: none known to me

Code: Select all

'file to be saved as "importedfile.bas" in the same dir as the *main file*

'we declare a variable we will conflict with:
   const as integer xxx => 99

'(eof)


Code: Select all

'main code file - where the user is supposed to work currently on

'we can do this as below only if the imported file contains only declarations
'(generally the imported filename would take a terminal .bi as extension)

nameSpace importedDeclarations
   #include "importedfile.bas"
end nameSpace

const as integer xxx => 44

? importedDeclarations.xxx
? xxx

getKey()
'(eof) tested on XPwin32 fb1.05


case 2
we may embed the stuff of ours, currently at work, into one or many namespaces.
Remark: A namespace can only hold declarations - in the version of FB in use here (1.05) at least - but, surprisingly enough, a same namespace can be open and closed as many times as wanted to collect declarations into it here and there many times.
Performance overhead: none known to me

Code: Select all

'main code file - where the user is supposed to work currently on

#include "importedfile.bas"

nameSpace currentDeclarations
   const as integer xxx => 44
end nameSpace

? currentDeclarations.xxx
? xxx

getKey()
'(eof) tested on XPwin32 fb1.05

Code: Select all

'main code file - where the user is supposed to work currently on

#include "importedfile.bas"

nameSpace currentDeclarations
   const as integer xxx => 44
end nameSpace

? currentDeclarations.xxx
? xxx

nameSpace currentDeclarations 'again!
   dim as integer yyy => 88
end nameSpace

? currentDeclarations.xxx
? currentDeclarations.yyy
? xxx

getKey()
'(eof) tested on XPwin32 fb1.05


B - Resolving conflict with the help of #UNDEF
Let take again the story of our 2 source files, one 'include file' and the current work.

case 1
The idea here is to copy every duplicate into brand new variables with non-conflicting names, then, wash out the old names by an #undef, and finally, reuse the unbound names in our current work file.
Remark: the old variables will stand somewhere in memory without any way to reach them, unless we destroy them before undef, but, in the case of a constant, I don't see what this could mean... Otherwise we could also keep track of the variable content with lost identifier via a pointer to its data address in memory. But here again when the variable is a constant like in the example, what is a Const Ptr in FB?
Performance possible overhead: At least a new declaration with a copy by value initialization for each duplicate to solve out.

Code: Select all

'main work file
#include "importedfile.bas"

const xxx_ => xxx 'transfer
'note:
'I'm not sure that the datatype should not be specified in the const

#undef xxx 'undefine

const as integer xxx=> 44 'reuse

? "current xxx, xxx="; xxx
? "imported xxx, now renamed as 'xxx_', xxx_="; xxx_

'(eof) fb1.05


case 2
This is something that seems just not possible to do: undefine a user defined type identifier; even if we have taken care of renaming it before thanks to the TYPE... AS... instruction. This however only true with imported material, otherwise undef seems to work.
And unfortunately I can't see the reason for this...

Code: Select all

'file to be saved as "importedfile.bas" in the same dir as the 'main file'

'declaring a UDT we will conflict with:
type CONFLICT
   as string _stringvariable => "hello"
end type

'(eof)

Code: Select all

'main work file
#include "importedfile.bas"

type CONFLICT_ as CONFLICT 'transfer

#undef CONFLICT 'error 176: This symbol cannot be undefined

type CONFLICT 'error 4: Duplicated definition, CONFLICT
   as string _stringvariable => "goodbye"
end type

? "current CONFLICT udt, CONFLICT._stringvariable="; CONFLICT._stringvariable
? "imported CONFLICT, now renamed as 'CONFLICT_', CONFLICT_._stringvariable="; CONFLICT_._stringvariable

'(eof) fb1.05



II - Using an alias for reducing identifier's length
The focus here is set on renaming again, but in order to reuse symbols under shorter names than those they have been given at their creation.
Doing so, some long named stuff can have their names exchanged for small things and then can not be said to be prone to any reading/writting trouble.


  • A - Reducing identifier's size with VAR BYREF <varshortname> = <varlongname>
  • B - Reducing identifier's size with VAR <procshortname> = @<proclongname>
  • C - Reducing identifier's size with TYPE <shortname> AS <longname>
  • D - Reducing identifier's size with #DEFINE <shortname> <longname>

A - Reducing identifier's size with VAR BYREF <varshortname> = <varlongname>
Consider some variable of your main source file. We'll want to add a name alias shorter than the initial name to work with very exactly as we would do with the original.
Remark: we'll have to distinguish the case of CONST.
Performance possible overhead: A declaration with a copy by reference initialization for each duplicate we create. This is about pointers so equivalent to the copy of the integer value holding the variable address independently of the datatype value size.

case 1
We will start with the case of CONST that seems not able to work as we wish without a special declination of the syntax that fortunately will make everything work equivalently.

Code: Select all

'VAR BYREF for identifier change - case of CONSTANT
const as integer veryLongName = 333 'Not OK for us

var byref shorty = veryLongName 'error 24: Invalid data types
dim byref as integer shorty = veryLongName 'error 24: Invalid data types

'(eof) fb1.05

We must use DIM AS CONST <DATATYPE> rather than CONST.
Unfortunately we have nothing as VAR AS CONST, so a classical CONST statement not mentionning explicitly a datatype, has no direct workaround visible to me.

Code: Select all

'var byref for identifier reuse - case of CONSTANT
dim as const integer veryLongName = 333 'OK
'dim as const (typeOf(333)) veryLongName = 333 'NOK -> error 14: Expected identifier

var byref shorty = veryLongName 'OK
'dim byref as integer shorty = veryLongName 'NOK -> error 24: Invalid data types
'dim byref as integer const shorty = veryLongName 'NOK -> error 272: Expected 'PTR' or 'POINTER'
? veryLongName
? shorty

'(eof) fb1.05


case 2
A more common issue addressed here is something like in the next example where names are even more inflated due to the cascading of the dot notation.

Code: Select all

type MYVERYBELOVEDUSERDEFINEDTYPE
   as integer   _myAbsolutlyCherishedIntegerField
end type
type MVBUDT as MYVERYBELOVEDUSERDEFINEDTYPE

dim as MVBUDT someInstance
someInstance._myAbsolutlyCherishedIntegerField => 44

'(eof) fb1.05

If we are to access _myAbsolutlyCherishedIntegerField each time like this, this will be undoubtly a pain. That's a nice improvement to be able to do this:

Code: Select all

type MYVERYBELOVEDUSERDEFINEDTYPE
   as integer_myAbsolutlyCherishedIntegerField
end type
type MVBUDT as MYVERYBELOVEDUSERDEFINEDTYPE

dim as MVBUDT someInstance

var byref i => someInstance._myAbsolutlyCherishedIntegerField
i = 44 'now i stands as an alias for the full longname above

? someInstance._myAbsolutlyCherishedIntegerField

'(eof) fb1.05


case 3
Worth some attention here: the problem with static functions.
I don't know if the compiler should not catch this in order to trigger some warning? (I love the warnings!)

Code: Select all

type X
   as integer i = 999
end type

function XXX() as integer static ''static not good here!
   dim as X xInstance
   var byref j = xInstance.i
   return j
end function

? XXX()
'(eof) fb1.05

B - Reducing identifier's size with VAR <procshortname> = @<proclongname>
If the purpose is to rename a function, here is a very nice FB feature, of a rare frugal and efficent syntax.
Remark: none.
Performance possible overhead: A declaration with a copy by reference initialization for each alias created this way.

Code: Select all

function XXX(byval x as integer) as double
   XXX = 33
end function

var YYY => @XXX

? YYY(88)

'(eof) fb1.05

C - Reducing identifier's size with TYPE <shortname> AS <longname>
It's how to give an alias to a user defined type.
Remark: there is also the same syntax to name a UDT pointer, like for type forwarding and many other interesting bindings that unfortunately I don't master.
Performance possible overhead: I don't know what is the category of this, re-assignement, text-replacement? So I can not judge.

Code: Select all

'declare first a type
type EXAMPLEUDT
   as integer   _integerUdtField
end type

'trigger a type alias
type EU   as   EXAMPLEUDT

'now use the alias or the original name anywhere
'with only 1 condition, the type declaration must have preceded the usage

dim as EU   euInstance


D - Reducing identifier's size with #DEFINE <shortname> <longname>
Indeed text replacement is the most immediate tool one can think of to handle renaming/aliasing issues.
However, in this topic I would object that the conventions I use myself to distinguish "defines" from variables or functions tend to give them a long name, all uppercase, and with a underscore as prefix (one of the goals is to make them unique so that they won't conflict if the code has to import new defines; they are prone to conflict because they are global by nature). So that this is not much suitable for code size reduction!
But #DEFINE is a great tool. And I'll give two examples.
A single "novelty" maybe: the scope emulation. Defines, at least as far as I know, aren't undefined according to common destruction rules related to local objects. So to forbid a define meant to keep localized from propagating, one must think of undefining it before the concerned scope ends.
Now from a performance point of view, it's all code preprocessing so it can not interfer by itself with the program performances. The text replacement is done before the code is compiled. Look at option -pp of the compiler to generate a snapshot of the resulting source code after the replacement is done.

Code: Select all

type MYVERYBELOVEDUSERDEFINEDTYPE
   as integer    _myAbsolutlyCherishedIntegerField
end type
type MVBUDT as MYVERYBELOVEDUSERDEFINEDTYPE

dim as MVBUDT someInstanceOfMvbUdt

#define _FIELD someInstanceOfMvbUdt._myAbsolutlyCherishedIntegerField
   _FIELD => 44
    ? _FIELD
    ? someInstanceOfMvbUdt._myAbsolutlyCherishedIntegerField
#undef _FIELD

'(eof) fb1.05

Code: Select all

type UDTEXAMPLE
   declare constructor(ArrayExtension as integer=10)
   redim as integer   _arrayOfInteger(0)
end type
type UE as UDTEXAMPLE

constructor UE(ArrayExtension as integer=10)
   #define _A(Ax) THIS._arrayOfInteger(uBound(THIS._arrayOfInteger) + Ax)
      redim preserve _A(ArrayExtension)
      redim preserve _A(-ArrayExtension)
      ? uBound(THIS._arrayOfInteger)
   #undef _A
end constructor

dim as UE ueInstance

getKey()
'(eof) fb1.05



That will be all of for today!
(...)
Last edited by Tourist Trap on Nov 10, 2016 21:56, edited 13 times in total.
fxm
Posts: 9126
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Giving an alias to an object (udt/variable/function)

Postby fxm » Nov 09, 2016 9:24

At first glance, one user might think that a reference to a variable (even declared with the same type) is a perfect variable alias (as for the type aliases), but this is not true in the compiler implementation:
- For any declaration of reference, the compiler allocates physical memory for an internal pointer of the specified type, and which points to that variable.
- When the user uses this reference, the compiler replaces it by the dereferencing of the internal pointer.
Therefore, this adds one indirection compared to direct access to the variable by using its original name.

This is why:
- We can reassign a reference to refer to another variable.
- We can also define a reference of type different from the one of the variable to refer (but compatible).
Tourist Trap
Posts: 2762
Joined: Jun 02, 2015 16:24

Re: Giving an alias to an object (udt/variable/function)

Postby Tourist Trap » Nov 09, 2016 16:15

Thanks fxm for the information. I'll update the first post as soon as I can.

So all of the technics above require a pointer under the hood, so an integer to be assigned? Even TYPE ... AS ?

Then am I right to conclude that if performance is important, the only good way for renaming is to use a DEFINE, with a possible UNDEF for scope emulation ?
fxm
Posts: 9126
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Giving an alias to an object (udt/variable/function)

Postby fxm » Nov 09, 2016 17:38

Type alias is just an alternative type-name which remains at compilation level, without any impact on the generated code.
Tourist Trap
Posts: 2762
Joined: Jun 02, 2015 16:24

Re: Giving an alias to an object (udt/variable/function)

Postby Tourist Trap » Nov 10, 2016 19:27

fxm wrote:Type alias is just an alternative type-name which remains at compilation level, without any impact on the generated code.

Which means that it''s equivalent to a kind of text replacement I guess? Anyway, I've noticed that it can not be undefined ---> when imported (otherwise, if not coming from an include, types are prone to undef without question in my test).
fxm
Posts: 9126
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Giving an alias to an object (udt/variable/function)

Postby fxm » Nov 10, 2016 20:54

In first post:

- II / A / case2:
Correct in the two codes:
as integer _myAbsolutlyCherishedIntegerField '' space added after integer


[edit]
Corrected now in the first post.
Last edited by fxm on Nov 10, 2016 21:41, edited 1 time in total.
Tourist Trap
Posts: 2762
Joined: Jun 02, 2015 16:24

Re: Giving an alias to an object (udt/variable/function)

Postby Tourist Trap » Nov 10, 2016 21:32

Done, thanks. Tabulations had been eaten somehow.

Also added the missing example of II.C, though very easy and not crucial.
fxm
Posts: 9126
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Giving an alias to an object (udt/variable/function)

Postby fxm » Nov 10, 2016 21:34

Tourist Trap wrote:case 3
Worth some attention here: the problem with static functions.
I don't know if the compiler should not catch this in order to trigger some warning? (I love the warnings!)

Code: Select all

type X
   as integer i = 999
end type

function XXX() as integer static ''static not good here!
   dim as X xInstance
   var byref j = xInstance.i
   return j
end function

? XXX()
'(eof) fb1.05


@dkl and @Tourist Trap

Compiler should output an error on:
var byref j = xInstance.i
because j is declared as static reference, but @xInstance.i is not a constant (field address in a type depending on memory alignment).

In the similar code using a pointer instead of a reference, the error is well detected:

Code: Select all

type X
   as integer i = 999
end type

static as X xInstance
static as integer ptr pj = @xInstance.i
error 11: Expected constant in 'static as integer ptr pj = @xInstance.i'

Workaround to also avoid a future compiler error (after bug fixing):
In all, 3 code lines modified:

Code: Select all

type X
   as integer i = 999
end type

function XXX() byref as integer static  ''<<< function should better return by reference
   dim as X xInstance
   var byref j = *cast(integer ptr, 0)  ''<<< to avoid eventual compiler error
   @j = @xInstance.i                    ''<<< to assign the reference to refer to xInstance.i
   return j
end function

? XXX()
'(eof) fb1.05
Last edited by fxm on Nov 10, 2016 21:50, edited 1 time in total.
fxm
Posts: 9126
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: Giving an alias to an object (udt/variable/function)

Postby fxm » Nov 10, 2016 21:40

Tourist Trap wrote:Done, thanks. Tabulations had been eaten somehow.
Same mistake in the penultimate code.


[edit]
Corrected now in the first post.
Last edited by fxm on Nov 10, 2016 22:02, edited 1 time in total.
Tourist Trap
Posts: 2762
Joined: Jun 02, 2015 16:24

Re: Giving an alias to an object (udt/variable/function)

Postby Tourist Trap » Nov 10, 2016 21:53

(typo corrected Thanks)
fxm wrote:

Code: Select all

function XXX() byref as integer static
   dim as X xInstance
   var byref j = *cast(integer ptr, 0)
   @j = @xInstance.i                   
   return j
end function
It makes more sense like this even if it's frightening ! I'll mention it next time I will edit.

It reminds me that I've left aside another little detail (presented in the 1st version), maybe ◆dkl◆ could have an answer, or yourself. It's about USING <namespaceid>:

Code: Select all

dim as integer   xxx = 99

namespace myNameSpace
   dim as integer    xxx = 44
end nameSpace

using myNameSpace

'next 2 lines shouldn't usually give the same result?
print xxx
print myNameSpace.xxx

Return to “Tips and Tricks”

Who is online

Users browsing this forum: No registered users and 3 guests