Raised This Month: $ Target: $400
 0% 

Why are define macro function thingies bad?


Post New Thread Reply   
 
Thread Tools Display Modes
Leonardo
Veteran Member
Join Date: Feb 2010
Location: 90's
Old 10-23-2014 , 04:34   Re: Why are define macro function thingies bad?
Reply With Quote #11

Quote:
Originally Posted by Chdata View Post
0 < i <= MaxClients

true <= MaxClients

??
not ( 0 < i ) <= MaxClients, it checks if value is in between the given values.

wait, just tested:
Code:
new a = 0, b = 1, c = 2, d = 4, e = 8;
if( a < b < c < d < e )
    PrintToServer( "hi" );
else
    PrintToServer( "bye" );
and it'll work. that's weird.
__________________
Leonardo is offline
Chdata
Veteran Member
Join Date: Aug 2012
Location: Computer Chair, Illinois
Old 11-25-2014 , 09:30   Re: Why are define macro function thingies bad?
Reply With Quote #12

Okay so I've resolved to using macros for things I just wanna type faster or shorten like...

PHP Code:
#define PLYR                    MAXPLAYERS+1       // lol hi nergal
#define PATHX                   PLATFORM_MAX_PATH

#define IsPlayerOwner(%1)       GetAdminFlag(GetUserAdmin(%1), Admin_Root)
#define IsPlayerAdmin(%1)       GetAdminFlag(GetUserAdmin(%1), Admin_Slay) 
But using functions for things that look more 'lengthy' to reduce file size, like so:

PHP Code:
#define IsMediUber(%1)          TF2_IsPlayerInCondition(%1, TFCond_Ubercharged)
#define IsQuickUber(%1)         TF2_IsPlayerInCondition(%1, TFCond_MegaHeal)

#define IsBlastUber(%1)         TF2_IsPlayerInCondition(%1, TFCond_UberBlastResist)
#define IsBulletUber(%1)        TF2_IsPlayerInCondition(%1, TFCond_UberBulletResist)
#define IsFireUber(%1)          TF2_IsPlayerInCondition(%1, TFCond_UberFireResist)

#define IsVacUbered(%1)         (IsBlastUber(%1) || IsBulletUber(%1) || IsFireUber(%1))
#define IsUbered(%1)            (IsMediUber(%1) || IsQuickUber(%1) || IsVacUber(%1))

stock bool:IsVacUber(iClient)
{
    return 
IsVacUbered(iClient);
}

stock bool:IsAnyUber(iClient)
{
    return 
IsUbered(iClient);

Not length as in 'boy TF2_IsPlayerInCondition is a lot of characters' but rather 'let's imprint just 1 function instead of 3 everywhere.
__________________

Last edited by Chdata; 11-25-2014 at 09:31.
Chdata is offline
Powerlord
AlliedModders Donor
Join Date: Jun 2008
Location: Seduce Me!
Old 11-25-2014 , 10:14   Re: Why are define macro function thingies bad?
Reply With Quote #13

Quote:
Originally Posted by Chdata View Post
Okay so I've resolved to using macros for things I just wanna type faster or shorten like...

PHP Code:
#define PLYR                    MAXPLAYERS+1       // lol hi nergal
#define PATHX                   PLATFORM_MAX_PATH 
#define IsPlayerOwner(%1) GetAdminFlag(GetUserAdmin(%1), Admin_Root)
#define IsPlayerAdmin(%1) GetAdminFlag(GetUserAdmin(%1), Admin_Slay)[/php]

But using functions for things that look more 'lengthy' to reduce file size, like so:

PHP Code:
#define IsMediUber(%1)          TF2_IsPlayerInCondition(%1, TFCond_Ubercharged)
#define IsQuickUber(%1)         TF2_IsPlayerInCondition(%1, TFCond_MegaHeal)

#define IsBlastUber(%1)         TF2_IsPlayerInCondition(%1, TFCond_UberBlastResist)
#define IsBulletUber(%1)        TF2_IsPlayerInCondition(%1, TFCond_UberBulletResist)
#define IsFireUber(%1)          TF2_IsPlayerInCondition(%1, TFCond_UberFireResist)

#define IsVacUbered(%1)         (IsBlastUber(%1) || IsBulletUber(%1) || IsFireUber(%1))
#define IsUbered(%1)            (IsMediUber(%1) || IsQuickUber(%1) || IsVacUber(%1))

stock bool:IsVacUber(iClient)
{
    return 
IsVacUbered(iClient);
}

stock bool:IsAnyUber(iClient)
{
    return 
IsUbered(iClient);

Not length as in 'boy TF2_IsPlayerInCondition is a lot of characters' but rather 'let's imprint just 1 function instead of 3 everywhere.
Pretty sure I mentioned this already, but define are processed by the compiler's preprocessor. In other words, the literal values of whatever you're doing in the defines replace the usage of the DEFINE.

As an example, this code:

PHP Code:
new String:path[PATHX]; 
becomes
PHP Code:
new String:path[255]; 
during the preprocessor phase (255 being the current value of PLATFORM_MAX_PATH).

Likewise,
PHP Code:
stock bool:IsAnyUber(iClient)
{
    return 
IsUbered(iClient);

becomes
Stretches screen so hidden by default

(iirc stock is resolved after the preprocessor)

In other words, you reduce the source file size while doing nothing to the size of the compiled plugin (or slightly increasing it) while at the same time increasing compile time (due to multiple preprocessor passes) and making the code harder to read in general (I'm not sure I'd call it obfuscation).

Incidentally, if you're only using a macro once, you're actually increasing the size of the source file too. As a side note, I'm not sure about Pawn, but in C this won't work: IsUbered(GetClientOfUserId(userid)) because you can't use functions as an argument to a macro.

Macros biggest advantage are that they're faster than function calls.
__________________
Not currently working on SourceMod plugin development.
Powerlord is offline
Chdata
Veteran Member
Join Date: Aug 2012
Location: Computer Chair, Illinois
Old 11-25-2014 , 11:13   Re: Why are define macro function thingies bad?
Reply With Quote #14

Quote:
Originally Posted by Powerlord View Post
In other words, you reduce the source file size while doing nothing to the size of the compiled plugin (or slightly increasing it) while at the same time increasing compile time (due to multiple preprocessor passes) and making the code harder to read in general (I'm not sure I'd call it obfuscation).
I don't undrastand.

PHP Code:
stock bool:IsAnyUber(iClient

    return (
TF2_IsPlayerInCondition(%1TFCond_Ubercharged) || TF2_IsPlayerInCondition(%1TFCond_MegaHeal) || (TF2_IsPlayerInCondition(%1TFCond_UberBlastResist) || TF2_IsPlayerInCondition(%1TFCond_UberBulletResist) || TF2_IsPlayerInCondition(%1TFCond_UberFireResist))); 
}

public 
Action:tSomeTimer(Handle:hTimerUserId)
{
    new 
iClient GetClientOfUserId(UserId);
    if (
IsValidClient(iClient) && IsAnyUber(iClient)){}
}

func2(iClient)
{
    if (
IsAnyUber(iClient)){}
}

OnRaged(iClient)
{
    if (
IsAnyUber(iClient)){return;}
}

func4(iClient)
{
    if (
IsAnyUber(iClient)){}
}

func5(iClient// etc
{
    if (
IsAnyUber(iClient)){}
}

// Compared to 

// More source file size
// More compiled file size

public Action:tSomeTimer(Handle:hTimerUserId)
{
    new 
iClient GetClientOfUserId(UserId);
    if (
IsValidClient(iClient) && (TF2_IsPlayerInCondition(iClientTFCond_Ubercharged) || TF2_IsPlayerInCondition(iClientTFCond_MegaHeal) || (TF2_IsPlayerInCondition(iClientTFCond_UberBlastResist) || TF2_IsPlayerInCondition(iClientTFCond_UberBulletResist) || TF2_IsPlayerInCondition(iClientTFCond_UberFireResist)))){}
}

func2(iClient)
{
    if ((
TF2_IsPlayerInCondition(iClientTFCond_Ubercharged) || TF2_IsPlayerInCondition(iClientTFCond_MegaHeal) || (TF2_IsPlayerInCondition(iClientTFCond_UberBlastResist) || TF2_IsPlayerInCondition(iClientTFCond_UberBulletResist) || TF2_IsPlayerInCondition(iClientTFCond_UberFireResist)))){}
}

OnRaged(iClient)
{
    if ((
TF2_IsPlayerInCondition(iClientTFCond_Ubercharged) || TF2_IsPlayerInCondition(iClientTFCond_MegaHeal) || (TF2_IsPlayerInCondition(iClientTFCond_UberBlastResist) || TF2_IsPlayerInCondition(iClientTFCond_UberBulletResist) || TF2_IsPlayerInCondition(iClientTFCond_UberFireResist)))){return;}
}

func4(iClient)
{
    if ((
TF2_IsPlayerInCondition(iClientTFCond_Ubercharged) || TF2_IsPlayerInCondition(iClientTFCond_MegaHeal) || (TF2_IsPlayerInCondition(iClientTFCond_UberBlastResist) || TF2_IsPlayerInCondition(iClientTFCond_UberBulletResist) || TF2_IsPlayerInCondition(iClientTFCond_UberFireResist)))){}
}

func5(iClient// etc
{
    if ((
TF2_IsPlayerInCondition(iClientTFCond_Ubercharged) || TF2_IsPlayerInCondition(iClientTFCond_MegaHeal) || (TF2_IsPlayerInCondition(iClientTFCond_UberBlastResist) || TF2_IsPlayerInCondition(iClientTFCond_UberBulletResist) || TF2_IsPlayerInCondition(iClientTFCond_UberFireResist)))){}

Also... compile time is a problem? You compile once, but isn't runtime what matters?

Also...

Maybe comparing IsAnyUber to

PHP Code:
stock bool:IsValidClient(iClient)
{
    return (
iClient && iClient <= MaxClients
        
&& IsClientInGame(iClient)
        && !
IsClientReplay(iClient)
        && !
IsClientSourceTV(iClient)
        && !
GetEntProp(iClientProp_Send"m_bIsCoaching")
    );

you lose readability, but for

PHP Code:
#define IsBlastUber(%1)         TF2_IsPlayerInCondition(%1, TFCond_UberBlastResist)
#define IsBulletUber(%1)        TF2_IsPlayerInCondition(%1, TFCond_UberBulletResist)
#define IsFireUber(%1)          TF2_IsPlayerInCondition(%1, TFCond_UberFireResist)
#define IsPlayerOwner(%1)       GetAdminFlag(GetUserAdmin(%1), Admin_Root) 
?

Also I'm pretty sure in pawn, %1 gets replaced with the entirety of whatever's put there.

If it's %1,%2,%3 it explodes based on the commas.

These are all macros I use that seem to work just fine.
__________________

Last edited by Chdata; 11-25-2014 at 11:17.
Chdata is offline
rswallen
SourceMod Donor
Join Date: Jun 2013
Location: 127.0.0.1
Old 11-25-2014 , 14:23   Re: Why are define macro function thingies bad?
Reply With Quote #15

You seem to under the impression that "stock" is synonymous to "inline" of C++ (on compile, replace function prototype with body). It is not.
When you declare a function "stock", it tells the compiler to only include the function if it is used (no replacing of anything).

Spoiler


As for that IsValidClient stock, you should consider splitting it into two separate stocks:
Spoiler


Reasoning:
  1. Easier to read without significantly hampering execution time
  2. Fits description better (coaching clients are valid clients, just not valid boss clients) [VSH]
  3. Optimised (We shouldn't care if a client is coaching when they are opening a menu [and replay clients don't call custom commands]) [VSH]
So many people use bloated IsValidClient stocks, when it makes more sense to make a second (if not third) client validation function that is more selective.
__________________
rswallen is offline
Chdata
Veteran Member
Join Date: Aug 2012
Location: Computer Chair, Illinois
Old 11-25-2014 , 17:41   Re: Why are define macro function thingies bad?
Reply With Quote #16

Yeah, I was talking to flaminsarge about that and considering putting the replaychecks in its own thing.

How are coaching clients valid clients? Aren't coachers like spectators?
__________________
Chdata is offline
ddhoward
Veteran Member
Join Date: May 2012
Location: California
Old 11-25-2014 , 17:42   Re: Why are define macro function thingies bad?
Reply With Quote #17

Quote:
Originally Posted by Chdata View Post
Aren't coachers like spectators?
No.
__________________
ddhoward is offline
BAILOPAN
Join Date: Jan 2004
Old 11-25-2014 , 17:43   Re: Why are define macro function thingies bad?
Reply With Quote #18

FYI: Macro-functions are slated for removal. Probably in SM 1.9. In 1.8 will give you a deprecation warning (I figured 1.7 has enough syntax changes as it is.)

Basically #define is a nightmare since it can materially change the token stream of the source file, and that greatly complicates the parsing process. When I tried to implement a cleaner pipeline for this, probably 2,000 lines of code were ultimately dedicated to handling token-expansion.

It makes it harder for any tools that want to do source-level analysis or rewriting. It makes backtraces weird. It's easy to run into bugs where tokens don't expand in the way you'd expect (for example, passing anything with side effects into a macro is very risky since the macro could expand its input twice). Stylistically, they're a little gross as others have pointed out.

And macros don't create linkage. For example, if plugin A uses a #define for its API, and plugin B is compiled against it, plugin B must be recompiled if plugin A fixes a bug. Functions create linkage, avoiding this problem.

Macros do allow primitive, albeit often unsafe generic behavior - and they can very slightly reduce overhead that would be introduced by a call frame or condition check. But the benefit is so small for all the complexity and problems they introduce. I'd rather find other solutions to the problems they solve.
__________________
egg

Last edited by BAILOPAN; 11-25-2014 at 17:56.
BAILOPAN is offline
Chdata
Veteran Member
Join Date: Aug 2012
Location: Computer Chair, Illinois
Old 11-25-2014 , 17:45   Re: Why are define macro function thingies bad?
Reply With Quote #19

Quote:
Originally Posted by ddhoward View Post
No.
You sure?

https://www.youtube.com/watch?v=73dwKjPvK7w

Quote:
Originally Posted by rswallen View Post
You seem to under the impression that "stock" is synonymous to "inline" of C++ (on compile, replace function prototype with body). It is not.
When you declare a function "stock", it tells the compiler to only include the function if it is used (no replacing of anything).
Nope. I understood that about stocks already. Not sure how you came up with me having that impression.
__________________

Last edited by Chdata; 11-25-2014 at 17:50.
Chdata is offline
friagram
Veteran Member
Join Date: Sep 2012
Location: Silicon Valley
Old 11-25-2014 , 21:18   Re: Why are define macro function thingies bad?
Reply With Quote #20

Don't think I've ever had need to make a macro in sp.
__________________
Profile - Plugins
Add me on steam if you are seeking sp/map/model commissions.
friagram is offline
Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -4. The time now is 07:44.


Powered by vBulletin®
Copyright ©2000 - 2024, vBulletin Solutions, Inc.
Theme made by Freecode