They are a way of eliminating procedure call overhead because they are always expanded in-line. There is no alternative for this in FreeBASIC because it does not support inline procedures.
Using macros can be extremely unsafe and they hide a lot of pitfalls which are very hard to find.
Procedures give type checking and scoping, but macros just substitute the passed argument.
Another disadvantage of the macro is the size of the program. The reason is, the pre-processor will replace all the macros in the program by its real definition prior to the compilation process of the program.
Looking only at the source code file, the only way to find out what the problem is to look at the definition of the macro and try to understand what happened.
The most common error when using macros is the unbalanced open parentheses (inducing error at compile-time).
Another is to forget putting parentheses around arguments in macro definitions. That can cause some pretty nasty side effects because of operator precedence (inducing error at compile-time or bug at run-time).
When the compiler detects an error inside a macro (after expanding), it provides a rustic error message containing only:
- the line number where is the call of the macro,
- the error type,
- the text of the call (of the macro).
- Do
1. call fbc on the source file, but with the '-pp' compile option (fbc command emitting only the pre-processed input file, without compiling),
2. recover the pre-processed file,
3. edit and compile directly from this pre-processed file,
4. analyze the error, understand it, correct it, and compile again the pre-processed file thus modified,
5. postpone the equivalent correction in the concerned macro body of the original source file. - Loop
- Source file (*.bas):
Code: Select all
#Macro FIRST(_x_,_op_)
For index as Integer = LBound(_x_) to UBound(_x_) - 1
Print _x_(index) _op_ ", ";
Next index
Print _x_(Ubound(_x_))
#EndMacro
#Macro SECOND(_x_)
Print "Dump: " & #_x_
FIRST(_x_, +)
#EndMacro
Dim as String test1(0 to ... ) => {"First", "Second", "Third" }
Dim as Integer test2(0 to ...) => { 1, 2 ,3 }
SECOND(test1)
SECOND(test2)
Sleep
- Compiler output:
.....\FBIde0.4.6r4_fbc1.06.0\FBIDETEMP.bas(17) error 20: Type mismatch, found ';' in 'SECOND(test2)'
Code: Select all
Dim as String test1(0 to ... ) => {"First", "Second", "Third" }
Dim as Integer test2(0 to ...) => { 1, 2 ,3 }
Print "Dump: " & $"test1"
For index as Integer =LBound(test1) to UBound(test1) -1
Print test1(index) + ", ";
Next index
Print test1(Ubound(test1))
Print "Dump: " & $"test2"
For index as Integer =LBound(test2) to UBound(test2) -1
Print test2(index) + ", ";
Next index
Print test2(Ubound(test2))
Sleep
- Compiler output:
.....\FBIde0.4.6r4_fbc1.06.0\FBIDETEMP.bas(13) error 20: Type mismatch, found ';' in 'Print test2(index) + ", ";'
Code: Select all
#Macro FIRST(_x_,_op_)
For index as Integer = LBound(_x_) to UBound(_x_) - 1
Print _x_(index) _op_ ", ";
Next index
Print _x_(Ubound(_x_))
#EndMacro
#Macro SECOND(_x_)
Print "Dump: " & #_x_
FIRST(_x_, &) ''FIRST(_x_, +) ''corrected line
#EndMacro
Dim as String test1(0 to ... ) => {"First", "Second", "Third" }
Dim as Integer test2(0 to ...) => { 1, 2 ,3 }
SECOND(test1)
SECOND(test2)
Sleep
- Output:
Code: Select all
Dump: test1 First, Second, Third Dump: test2 1, 2, 3
Another solution could be a more detailed error message from compiler, relating to the call of the macro:
- For example it could be added, at the end of the error message relating to the line of macro call, the exact line number of error inside the macro definition body.
- In case of nested macros, adding successively the error line number for each macro definition body.
- Proposal of improved error message for the above example of erroneous code:
- Compiler output:
.....\FBIde0.4.6r4_fbc1.06.0\FBIDETEMP.bas(17) error 20: Type mismatch, found ';' in 'SECOND(test2)': from macro line 10, 3
- Compiler output:
#247 Compiler error message concerning #macro could be detailed