Home   Help Search Login Register  

Author Topic: Macros  (Read 3262 times)

0 Members and 1 Guest are viewing this topic.

Offline Rommel92

  • Members
  • *
Macros
« on: 21 Apr 2009, 13:49:59 »
Code: [Select]
sleep 1;
RMM_F_HINTC(["Test1","Test2"])

Code: [Select]
#define RMM_F_HINTC(ARRAY) hint format ["%1",ARRAY]Displays nothing. (ie macros is not launched)

Code: [Select]
#define RMM_F_HINTC(ARRAY,TEST) hint format ["%1 and %2",ARRAY,TEST]Displays "["Test1","Test2"] and  "

With:

Code: [Select]
sleep 1;
RMM_F_HINTC(["Test1","Test2","Test3"])

Code: [Select]
#define RMM_F_HINTC(ARRAY) hint format ["%1",ARRAY]Displays nothing. (ie macro is not launched)

Code: [Select]
#define RMM_F_HINTC(ARRAY,TEST) hint format ["%1 and %2",ARRAY,TEST]Displays nothing. (ie macro is not launched)


Code: [Select]
#define RMM_F_HINTC(ARRAY,TEST,TEST2) hint format ["%1 and %2 and %3",ARRAY,TEST,TEST2]Displays "["Test1","Test2","Test3"] and  and"


EDIT: With:

Code: [Select]
RMM_F_HINTC(str([name, player]))And
Code: [Select]
#define RMM_F_HINTC(ARRAY) hint format ["%1",ARRAY]Displays "Rommel"
It should display similiar to this... right?
Code: [Select]
hint format ["%1", "[name, player]"]

EDIT2: With:

Code: [Select]
_msg = ["1","2","3","4"];
RMM_F_HINTC(_msg)

Code: [Select]
#define RMM_F_HINTC(ARRAY) hint format ["%1",ARRAY]Displays ""1","2","3","4"", as intended. But I still fail to see why the parser would screw up on all the other ones...
« Last Edit: 21 Apr 2009, 15:10:30 by Rommel92 »

Offline Worldeater

  • Former Staff
  • ****
  • Suum cuique
Re: Macros
« Reply #1 on: 21 Apr 2009, 14:21:58 »
Download mcpp from sourceforge.net and preprocess you code. Then take a look at what you've done.

Please stop abusing macros. If you want a function, write one.
try { return true; } finally { return false; }

Offline Rommel92

  • Members
  • *
Re: Macros
« Reply #2 on: 21 Apr 2009, 15:05:14 »
Thanks for the program, however I am not abusing macros, I am merely experimenting. May I earn that right or is that against moral code too?

 :good:

#EDIT: Do not quote the entire previous post you're replying to..  h-
« Last Edit: 21 Apr 2009, 17:40:51 by h- »

Offline i0n0s

  • Former Staff
  • ****
Re: Macros
« Reply #3 on: 21 Apr 2009, 16:31:59 »
Code: [Select]
#define RMM_F_HINTC(ARRAY,TEST) hint format ["%1 and %2",ARRAY,TEST]will be
Code: [Select]
hint format ["%1 and %2", [""Test1", "Test2"]]
A , is a new argument for the preprocessor. As Worldeater said: Use a preprocessor and take a look at the output.

Btw:
This code doesn't work as a function ;)
Code: [Select]
#define DEBUG(Level, Msg, ...)\
[__FILE__, STR(__LINE__), Level, format[localize #Msg, ##__VA_ARGS__]] call ION_RTE_pDebug;
(but it won't work with ArmA too  :whistle:)

Offline Worldeater

  • Former Staff
  • ****
  • Suum cuique
Re: Macros
« Reply #4 on: 21 Apr 2009, 18:27:18 »
As you might have noticed I'm somewhat allergic to macros. I consider them being the nukes in the programmers arsenal:
- very powerful, require lots of insight and responsibility
- can ease your task but the result will always be kinda messy
- avoid a "I use them because I can" attitude
- the more you use and the bigger those you use are the more others will hate you
- they are nice as long as you're the only one using them
- you might curse youself for having used them in the past
- in a perfect world we would live without them (because there would be no need to use them)

Well, enough of that...


The first thing one should know about macros is that they are not part of the programming language. All they do is replacing strings in the source code.
The next thing is that this replacement takes place before the code is parsed and run (in a process called preprocessing). So the parser never sees the macros nor are they ever "launched" either.

Since the preprocessor is not part of the language, it does not know anything about variables, keywords, types, etc.

A macro like
    #define RMM_F_HINTC(ARRAY) hint format ["%1",ARRAY]
requires exactly one argument.

Using it like this
    RMM_F_HINTC(["Test1","Test2"])
won't work because here you pass two arguments: ["Test1" and "Test2"].

Again, the preprocessor does not know what an array is. It just sees two strings separated by a comma.
« Last Edit: 21 Apr 2009, 18:33:26 by Worldeater »
try { return true; } finally { return false; }

Offline Rommel92

  • Members
  • *
Re: Macros
« Reply #5 on: 21 Apr 2009, 23:37:45 »
Thanks for the insight. I'd only used preprocessed content before for constants that were being used over and over again; so I was aware they replaced the 'compression' of themselves.

Quote from: Worldeater
...this replacement takes place before the code is parsed and run (in a process called preprocessing). So the parser never sees the macros...

...Since the preprocessor is not part of the language, it does not know anything about variables, keywords, types, etc...

This was very interesting, and thank you so much for clearing that up for me. I knew that it had simply been problems somewhere in translation, however I was unsure where.

 :P
« Last Edit: 21 Apr 2009, 23:40:53 by Rommel92 »

Offline Spooner

  • Members
  • *
  • Mostly useless
    • Community Base Addons
Re: Macros
« Reply #6 on: 17 May 2009, 00:30:43 »
Code: [Select]
Just to spoil things, the "shortcut" for
[code]
hint format ["%1", array]
is
Code: [Select]
hint str array
If you want to hint several things, the easiest way (without typing very much) is to:
Code: [Select]
hint str [frog, cheese, peas]

On the other hand, what you could do is this (and yes, you'd need HINT1(), HINT2() and HINT3(), because macros are dumb):
Code: [Select]
#define HINT3(a,b,c) hint format ["%1 = %2\n%3 = %4\n%5 = %6", #a, a, #b, b, #c, c];
Or as a function:
Code: [Select]
hintList =
{
  private ["_hintListStr", "_hintListVars"];

  _hintListStr = _this select 0;
  _hintListVars = _this select 1;

  { _hintListStr = _hintListStr + format ["\n%1: %2" _x, call compile _x]} forEach _hintListVars;
};

["Prices of Fish:", ["frog", "fish", "_cheese"]] call hintList;
These last two examples give examples of how you can show both the names and values of variables you want to view (I got bored of typing format ["frog: %1\nfish: %2,_cheese: %3", frog,fish,cheese] a long time ago). Not tested any of this, but if there are errors, I hope they are obvious to fix.

Anyway, my point was both to agree with worldeater regarding the evil of macros and to suggest you can do more with them than you might think...and that, in a lot of cases, such as this one, you can do better with a function (or a simpler macro that uses a function or whatever).

The thing with preprocessor macros is that in their day, they were invaluable to programmers (in C), whereas with the advent of C++ in the 90s and even more advanced languages after that, significantly better alternatives were provided so that we could consign the preprocessor to the same cupboard as we keep the dinosaurs in. I don't think any developer in the current age would seriously suggest the use of a preprocessor the like of which were are using now.

If we had better systems, I would say that no-one should ever use macros (either for replacement or pseudo-functions), but the thing is that we are living in a dark age so we get to use dark-age tools. I use macros extensively in generating dialogs, and frankly, couldn't live without them in that context (which is admittedly not the same as using them in SQF). The key issue is that the preprocessor is a very sharp tool. You need to really understand how it works before you use it, because it will bite you in the arse if you don't give the dinosaur the due respect it deserves![/code]
« Last Edit: 21 May 2009, 18:12:32 by Spooner »
[Arma 2] CBA: Community Base Addons
[Arma 1] SPON Core (including links to my other scripts)

Offline Rommel92

  • Members
  • *
Re: Macros
« Reply #7 on: 21 May 2009, 11:17:11 »
These are all true, and I do use macro's a lot simply for cutting down code in a way that allows to do things that originally I couldn't; ie here is my macro file:

Code: [Select]
/* Last revision 21-May-2009 by rommel */

//---------------------------------------------//
//----------------COMMANDS---------------------//
//---------------------------------------------//
#define isArray2(X) (typeName X == 'Array')
#define isBool(X) (typeName X == 'Bool')
#define isCode(X) (typeName X == 'Code')
#define isConfig(X) (typeName X == 'Config')
#define isControl(X) (typeName X == 'Control')
#define isDisplay(X) (typeName X == 'Display')
#define isGroup(X) (typeName X == 'Group')
#define isInteger(X) (isScalar(X) and (Round(X) == X))
#define isObject(X) (typeName X == 'Object')
#define isScalar(X) (typeName X == 'Scalar')
#define isScript(X) (typeName X == 'Script')
#define isSide(X) (typeName X == 'Side')
#define isString(X) (typeName X == 'String')
#define isText(X) (typeName X == 'Text')

#define isAlive(X) (not isnull X and alive X)

#define isDediServer (isServer and isnull player)
#define isDediClient (Not isServer)

#define isVehicle(X) (Vehicle X != X)

#define isDriver(X) (isVehicle(X) and (Driver Vehicle X == X))
#define isGunner(X) (isVehicle(X) and (Gunner Vehicle X == X))
#define isCommander(X) (isVehicle(X) and (Commander Vehicle X == X))
#define isCargo(X) (isVehicle(X) and not isDriver(X) and not isGunner(X) and not isCommander(X))

#define EmptyDriver(X) (isVehicle(X) and X EmptyPositions 'Driver' >= 1)
#define EmptyGunner(X) (isVehicle(X) and X EmptyPositions 'Gunner' >= 1)
#define EmptyCommander(X) (isVehicle(X) and X EmptyPositions 'Commander' >= 1)
#define EmptyCargo(X) (isVehicle(X) and X EmptyPositions 'Cargo' >= 1)

#define Cache(File2Cache) compile preprocessFileLineNumbers File2Cache

#define Arg(X) _this select X
#define First(X) X select 0
#define Stack(X,Y) X resize (count X + 1); X set [(count X) - 1, Y]
#define Concat(X,Y) X resize (count X + 1); X = X + [Y]

#define playerNumber (playersnumber east + playersnumber west + playersnumber resistance + playersnumber civilian)

#define Broadcast(X, Y, Z) \
if (isObject(X) and isScalar(Y) and isString(Z)) then { \
switch ( Y ) do { \
case 0: {X setVehicleinit format['if (isServer) then {%1}', Z]}; \
case 1: {X setVehicleinit format['if (local this) then {%1}', Z]}; \
case 2: {X setVehicleinit format['%1', Z]}; \
}; \
processinitcommands \
}
//---------------------------------------------//
//----------------FUNCTIONS--------------------//
//---------------------------------------------//

#define vehStats(X,I) \
[ \
typeof X, \
switch(I)do{ case 0 : { (leader X) worldToModel getpos X }; case 1 : { getpos X } }, \
magazines X, \
weapons X, \
damage X, \
fuel X, \
velocity X, \
vectorup X, \
vectordir X \
]

#define unitsAlive(Array, Index) \
{ \
private '_return'; _return = []; \
if isArray2(Array) then { \
if isScalar(Index) then { \
{ \
if ( Index == -1 ) then { \
if ( Not isnull _x and alive _x ) then { \
Array set [count Array, _x] \
} \
} else { \
if ( Not isnull ( _x select Index ) and alive ( _x select Index ) ) then { \
_return set [count _return, ( _x select Index )] \
} \
} \
} foreach Array \
} \
}; \
_return \
}
//--//
#define getDirFromTo(From, To) \
{ \
private ['PosA','PosB','_return']; \
if isObject(From) then { \
PosA = getpos From \
} else { \
if isArray2(From) then { \
if ((count From) > 2 and (count From) < 4) then { \
if (isScalar(From select 0) and isScalar(From select 1) and isScalar(From select 2)) then { \
PosA = From \
} \
} \
} \
}; \
if isObject(To) then { \
PosB = getpos To \
} else { \
if isArray2(To) then { \
if ((count To) >= 2 and (count To) <= 3) then { \
if (isScalar(To select 0) and isScalar(To select 1) and isScalar(To select 2)) then { \
PosB = To \
} \
} \
} \
}; \
_return = ((PosA select 0) - (PosB Select 1)) atan2 ((PosA Select 0) - (PosB Select 0)); \
if (_return < 0) then {_return = _return + 360}; \
_return \
}
//--//

Offline Spooner

  • Members
  • *
  • Mostly useless
    • Community Base Addons
Re: Macros
« Reply #8 on: 21 May 2009, 18:26:36 »
I'm with WorldEater that the last few macros should, in fact, just be implemented as SQF functions. I'm less convinced that short macros, should be converted to functions though.

You should get in the habit of wrapping expanded macro parameters in brackets (there are specific situations where you wouldn't want to do this, of course). This prevents unexpected effects caused by the use of complex parameters and operator precedence. e.g.
Code: [Select]
#define isVehicle(X) (Vehicle X != X)
should be
Code: [Select]
#define isVehicle(X) (Vehicle (X) != (X))

Code: [Select]
#define Stack(X,Y) X resize (count X + 1); X set [(count X) - 1, Y]
could be:
Code: [Select]
#define Stack(X,Y)  (X) set [count (X), (Y)]
Though "Stack" doesn't really make sense, since a stack is the data structure you are using, not the operation. I would rename this as Push(X,Y).

Code: [Select]
#define Concat(X,Y) X resize (count X + 1); X = X + [Y]
Don't seem like it would work. All it will do is the equivalant of:
Code: [Select]
#define Concat(X,Y) X = X + [nil, Y]
Which doesn't seem very useful. Perhaps you meant to do something else?
[Arma 2] CBA: Community Base Addons
[Arma 1] SPON Core (including links to my other scripts)

Offline Rommel92

  • Members
  • *
Re: Macros
« Reply #9 on: 23 May 2009, 08:29:20 »
I'm inclined to agree with both of you that the last few should be functions, and they will be, I was just inquisitive to see how I could go.
The rest however seem to be fine, and by the way thanks for the tip about (X), I have had a few issues with this and simply bracketed the variable, not the macro variable... if that makes sense.

edit:

I don't use some of those macro's any more as well, simply because X set [X, Y] has the same amount of characters as Push(X,Y) anyway, and saves time and memory. Many of the others however allow great simplification of code.

Offline Spooner

  • Members
  • *
  • Mostly useless
    • Community Base Addons
Re: Macros
« Reply #10 on: 23 May 2009, 14:49:17 »
If X is SPON_Map_ListOfJellies then the macro version is a lot quicker to type and less prone to the two versions of X being inconsistent ;)

I would go back and cull some of my own macros that are probably too large, but the problem is changing all the code that uses it. In a "proper" language, I could replace macros with functions and functions with macros without having to change every usage...
[Arma 2] CBA: Community Base Addons
[Arma 1] SPON Core (including links to my other scripts)

Offline kju

  • Members
  • *
    • PvPScene - The ArmA II multiplayer community
Re: Macros
« Reply #11 on: 23 May 2009, 18:44:59 »
I don't get the typing argument.
Almost every basic code editors supports code templates.
If we are to use an IDE, code completion is on the table as well.

Offline Denisko-Redisko

  • Members
  • *
Re: Macros
« Reply #12 on: 25 May 2009, 08:41:06 »
Quote from: Spooner
The thing with preprocessor macros is that in their day, they were invaluable to programmers (in C), whereas with the advent of C++ in the 90s and even more advanced languages after that, significantly better alternatives were provided so that we could consign the preprocessor to the same cupboard as we keep the dinosaurs in.
Dinosaurs? But what about templates in C++? Because they work through the preprocessor.  ;)

I, perhaps, too, will share their favorite macro:
Code: [Select]
//
// Arguments macro
//

#define arg(X)            (_this select (X))
#define argIf(X)          if(count _this > (X))
#define argIfType(X,T)    if(if(count _this > (X))then{typeName arg(X) == (T)}else{false})
#define argSafe(X)        argIf(X)then{arg(X)}
#define argSafeType(X,T)  argIfType(X,T)then{arg(X)}
#define argOr(X,V)        (argSafe(X)else{V})

//
// Array macro
//

#define item(A,X)   ((A)select(((X) min (count(A)-1)) max 0))
#define itemr(A,X)  (item((A), if((X) < 0)then{count(A)+(X)}else{X}))
#define push(A,V)   (A)set[count(A),(V)]
#define top(A)      ((A)select((count(A) - 1) max 0))
#define pop(A)      (0 call { _this = top(A); A resize ((count(A) - 1) max 0); _this })

//
// Position macro (for only expressive)
//

#define x(A)        ((A) select 0)
#define y(A)        ((A) select 1)
#define z(A)        ((A) select 2)

//
// Other macro
//

#define xor(A,B)    (!(A && B) && (A || B))
#define inc(N)      (call { N = N + 1; N })
#define dec(N)      (call { N = N - 1; N })

//
// And, hackneyed:
// Type of expression
//

#define isCodeType(V)    (typeName(V) == "CODE")
#define isScalarType(V)  (typeName(V) == "SCALAR")
#define isScriptType(V)  (typeName(V) == "SCRIPT")
#define isSideType(V)    (typeName(V) == "SIDE")
#define isTextType(V)    (typeName(V) == "TEXT")

#define isNumberType(V)   isScalarType(V)
#define isArrayType(V)    (typeName(V) == "ARRAY")
#define isBooleanType(V)  (typeName(V) == "BOOL")
#define isConfigType(V)   (typeName(V) == "CONFIG")
#define isControlType(V)  (typeName(V) == "CONTROL")
#define isDisplayType(V)  (typeName(V) == "DISPLAY")
#define isGroupType(V)    (typeName(V) == "GROUP")
#define isObjectType(V)   (typeName(V) == "OBJECT")
#define isStringType(V)   (typeName(V) == "STRING")

For example:
Code: [Select]
_someScalar = argOr(1, "default value");But:
Code: [Select]
_someArray = argSafe(1) else {["default", "list", "values"]};
« Last Edit: 25 May 2009, 08:45:14 by Denisko-Redisko »
sorry for my english

Offline Denisko-Redisko

  • Members
  • *
Re: Macros
« Reply #13 on: 13 Aug 2009, 04:11:38 »
I don't know, maybe this is naive  :), but recently I found a fun way to simulate the operation assignments such as +=, -=, *=, etc:
Code: [Select]
#define _(var) _##var = _##varnow:
Code: [Select]
_myVar = 123;
_(myVar) - 23;
_(myVar) * 5;
:whistle:
--------------
PS.
And, not so long ago, i have solved a problem with a push:
Code: [Select]
#define pushTo(array) call { array set [count array, _this] }
[1,2,3,4,5] pushTo(_myArr)
« Last Edit: 20 Sep 2009, 09:52:39 by Denisko-Redisko »
sorry for my english