Putting together variable names with macros in cc65

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems.

Moderator: Moderators

User avatar
DRW
Posts: 1913
Joined: Sat Sep 07, 2013 2:59 pm

Putting together variable names with macros in cc65

Post by DRW » Fri Nov 08, 2019 10:22 pm

I have some C source code (in cc65) where I connect variable names by macros:

Code: Select all

#define DECLARE_NUMBER_WITH_POSTFIX()\
	int Number##CurrentPostfix

#define CurrentPostfix A
DECLARE_NUMBER_WITH_POSTFIX();

void Function(void)
{
	NumberA = 1;
}
The problem is: In this case, the variable is still called NumberCurrentPostfix and not NumberA.

I need to do this for it to work:

Code: Select all

#define DECLARE_NUMBER_WITH_POSTFIX__(postfix)\
	int Number##postfix

#define DECLARE_NUMBER_WITH_POSTFIX_(postfix)\
	DECLARE_NUMBER_WITH_POSTFIX__(postfix)

#define DECLARE_NUMBER_WITH_POSTFIX()\
	DECLARE_NUMBER_WITH_POSTFIX_(CurrentPostfix)

#define CurrentPostfix A
DECLARE_NUMBER_WITH_POSTFIX();

void Function(void)
{
	NumberA = 1;
}
Now, I have a whole bunch of those macros. And everytime, I have to declare a group of three that call each other.

Is there a way to shorten this?

Either if Number##CurrentPostfix could directly be converted to NumberA somehow.

Or if I declare one singular global macro that needs to be declared in this group of three, but all other macros can be declared once and they simply call this global macro.

For example, if this would work: Number##MACRO_VALUE(CurrentPostfix) and only MACRO_VALUE would have been declared in a group.


By the way, the connection of names can be completely different each time.
So, just declaring a macro VALUE(baseName, postfix) and then to call int VALUE(Number, CurrentPostfix); in my other macros wouldn't be enough.

Because sometimes I also have stuff similar to this: CurrentType##Data_b##CurrentBankName##CurrentBankNumber which would then be turned to CharacterData_bChr1.
Available now: My game "City Trouble".
Sales website: https://megacatstudios.com/products/city-trouble
Gameplay: https://youtu.be/Eee0yurkIW4
Download website: http://www.denny-r-walter.de/city.htm

User avatar
wonder
Posts: 23
Joined: Sat Aug 31, 2019 2:12 pm

Re: Putting together variable names with macros in cc65

Post by wonder » Thu Nov 14, 2019 4:10 pm

I'm a little bit confused by your post, but isn't this enough?

Code: Select all

#define DECLARE_NUMBER_WITH_POSTFIX(postfix) \
    int Number##postfix

DECLARE_NUMBER_WITH_POSTFIX(A);
Live: https://godbolt.org/z/3qXXQY

I've been doing a lot of similar stuff with macros lately, maybe this can help:

sprite.h

Code: Select all

#define DECLARE_SPRITE(sprite_name, num_animations, total_num_frames) \
  extern const byte sprite_##sprite_name##_animations[num_animations][3]; \
  extern const byte sprite_##sprite_name##_frame_data[total_num_frames][2]; \
  extern const Sprite sprite_##sprite_name; \
  enum { sprite_##sprite_name##_##n_animations = num_animations }; \
  enum { sprite_##sprite_name##_##n_frames = total_num_frames }

#define CREATE_SPRITE(sprite_name, info, data) \
  const byte sprite_##sprite_name##_animations[sprite_##sprite_name##_##n_animations][3] = info; \
  const byte sprite_##sprite_name##_frame_data[sprite_##sprite_name##_##n_frames][2] = data; \
  const Sprite sprite_##sprite_name = { sprite_##sprite_name##_animations, sprite_##sprite_name##_frame_data }

#define SET_ANIMATION_NAME(sprite_name, animation_name, index) \
  enum { sprite_##sprite_name##_##animation_name = index }
data.h

Code: Select all

#ifndef _DATA_H
#define _DATA_H

#include "actor.h"
#include "sprite.h"

// =====================================
// *** SPRITES ***
// =====================================

// Player Sprite
DECLARE_SPRITE(player, 4, 6 + 3 + 1 + 6);
SET_ANIMATION_NAME(player, idle, 0);
SET_ANIMATION_NAME(player, walk, 1);
SET_ANIMATION_NAME(player, duck, 2);
SET_ANIMATION_NAME(player, dead, 3);

...
data.c

Code: Select all

#include "data.h"


/* * * * * * * * * * * * * * * * * * * * * * * * * * *
typedef struct Sprite {
  void *info;
  void *data;
} Sprite;
* * * * * * * * * * * * * * * * * * * * * * * * * * */


// Player Sprite
// - - - - - - - - - - - - - - - - - - - - - - - - - -

#define _SPRITE_INFO { \
  {6, 0, 0,}, {3, 6, 0}, {1, 9, 0}, {6, 10, 1} \
}
#define _SPRITE_DATA { \
  {0xb5, 240}, {0xb6,   4}, {0xb7,   4}, \
  {0xb8, 120}, {0xb9,   4}, {0xb4,   6}, \
                                         \
  {0xb1,   3}, {0xb2,   3}, {0xb3,   3}, \
                                         \
  {0xba,   1},                           \
                                         \
  {0xc0,   2}, {0xc1,   2}, {0xc2,   2}, \
  {0xc3,   2}, {0xc4,   2}, {0xc5,   2}  \
}
CREATE_SPRITE(player, _SPRITE_INFO, _SPRITE_DATA);
#undef _SPRITE_DATA
#undef _SPRITE_INFO

...
Image

User avatar
DRW
Posts: 1913
Joined: Sat Sep 07, 2013 2:59 pm

Re: Putting together variable names with macros in cc65

Post by DRW » Thu Nov 14, 2019 7:13 pm

wonder wrote:I'm a little bit confused by your post, but isn't this enough?

Code: Select all

#define DECLARE_NUMBER_WITH_POSTFIX(postfix) \
    int Number##postfix

DECLARE_NUMBER_WITH_POSTFIX(A);
The idea is that you don't simply write the postfix, but that the postfix is the value inside another macro.

This is what I want:

Code: Select all

#define DECLARE_NUMBER_WITH_POSTFIX() \
    int Number##CurrentPostfix

#define CurrentPostfix A

DECLARE_NUMBER_WITH_POSTFIX();
And the number shall be called NumberA, not NumberCurrentPostfix.

Until now, the only way that I found to do this is like this:

Code: Select all

#define DECLARE_NUMBER_WITH_POSTFIX__(postfix) \
    int Number##postfix

#define DECLARE_NUMBER_WITH_POSTFIX_(postfix) \
    DECLARE_NUMBER_WITH_POSTFIX__(postfix)

#define DECLARE_NUMBER_WITH_POSTFIX() \
    DECLARE_NUMBER_WITH_POSTFIX_(CurrentPostfix)

#define CurrentPostfix A

DECLARE_NUMBER_WITH_POSTFIX();
But I'm looking for somthing where I don't have to declare three macros every time.
Available now: My game "City Trouble".
Sales website: https://megacatstudios.com/products/city-trouble
Gameplay: https://youtu.be/Eee0yurkIW4
Download website: http://www.denny-r-walter.de/city.htm

User avatar
rainwarrior
Posts: 7669
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Putting together variable names with macros in cc65

Post by rainwarrior » Thu Nov 14, 2019 7:27 pm

Trying to use C macros that interact with each other, or recursively, can be a bit tricky. The order in which they apply is not always very intuitive. cc65's preprocessor isn't really documented, though I think it's a fairly "normal" C preprocessor implementation. GCC has macro documentation but it's really hard to read. Wikipedia strangely has a concise description of this topic that might help a little.

If you want some insight as to how the preprocessor works, you can investigate the result of the preprocessor with the -E flag. Something like this will output a file that has had all the macro preprocessing applied:

Code: Select all

cc65 -E -o thing.pre thing.c
Otherwise I can't offer much advice on the situation you asked about. To me it looks like it would take more time to understand than it would to find a different way to accomplish the same thing.

User avatar
wonder
Posts: 23
Joined: Sat Aug 31, 2019 2:12 pm

Re: Putting together variable names with macros in cc65

Post by wonder » Fri Nov 15, 2019 1:05 am

rainwarrior wrote:If you want some insight as to how the preprocessor works, you can investigate the result of the preprocessor with the -E flag. Something like this will output a file that has had all the macro preprocessing applied:

Code: Select all

cc65 -E -o thing.pre thing.c
Exactly! :)
Furthermore, you can try the pre-processor live on this website: https://godbolt.org/ (lang: C, compiler: CC65, args: -E)
For your convenience, here's the direct link generated with the Share button: https://godbolt.org/z/3qXXQY

Removing the -E argument lets you see the generated assembly code in real time. Very useful for prototyping! :)
Image

User avatar
DRW
Posts: 1913
Joined: Sat Sep 07, 2013 2:59 pm

Re: Putting together variable names with macros in cc65

Post by DRW » Sun Nov 17, 2019 4:20 am

Thanks for your information.

I did it like this now:

Code: Select all

#define DefNameX_(name1, name2, name3, name4, name5, name6)\
	name1##name2##name3##name4##name5##name6

#define DefName6(name1, name2, name3, name4, name5, name6)\
	DefNameX_(name1, name2, name3, name4, name5, name6)

...

#define DefName2(name1, name2)\
	DefNameX_(name1, name2, , , , )
This way, I can do this:

Code: Select all

#define DECLARE_NUMBER_WITH_POSTFIX()\
    int DefName2(Number, CurrentPostfix)

#define CurrentPostfix A

DECLARE_NUMBER_WITH_POSTFIX();
/* --> Generates int NumberA; */
And it has enough overloaded versions for all kinds of complicated names, for example:
CurrentArea##name##_b##whatBankType##CurrentBankNumber
Available now: My game "City Trouble".
Sales website: https://megacatstudios.com/products/city-trouble
Gameplay: https://youtu.be/Eee0yurkIW4
Download website: http://www.denny-r-walter.de/city.htm

Oziphantom
Posts: 774
Joined: Tue Feb 07, 2017 2:03 am

Re: Putting together variable names with macros in cc65

Post by Oziphantom » Fri Nov 22, 2019 3:01 am

What you want is basically illegal. You can not redefine a preprocessor value again, it should give you an error. So you would technically need to UNDEF thing, then DEF thing again. Baring in mind that a preprocessor should happen before anything else and is free to happen in any order it wants across files. And if you have incremental linking, might not happen in the order you think it does..

BUT this is CC65 which from what I've been told, doesn't even preprocess the preprocessor for the "true one pass" and it does it "in place". Which probably helps, in your case, but having something like ggc -P -E as a first step might come in handy.

The real question is, why do you want a macro for this? It seems more effort than just typing NAMEThing each time.

User avatar
DRW
Posts: 1913
Joined: Sat Sep 07, 2013 2:59 pm

Re: Putting together variable names with macros in cc65

Post by DRW » Fri Nov 22, 2019 3:58 am

Oziphantom wrote:
Fri Nov 22, 2019 3:01 am
What you want is basically illegal. You can not redefine a preprocessor value again, it should give you an error.
I don't really want to redefine a preprocessor value, I want the macro to put together the correct name.
And the thing that I do works with every compiler I tried out, including that web-based preprocessor that was linked here.

Oziphantom wrote:
Fri Nov 22, 2019 3:01 am
The real question is, why do you want a macro for this? It seems more effort than just typing NAMEThing each time.
Here's a practical example:

The NES can only display a limited number of tiles. Hence, a single screen should only use the meta tiles that the graphics can actually display in the moment.
So, if you're in a town and your current screen uses a meta tile from the beach, this wouldn't be so good because it would show garbled graphics. Therefore, a town should only ever use town-related meta tiles.

Hence, I first prefix every meta tile with its area name:
TownHouse
TownWall
TownPavement
BeachSand
BeachPalmTree

Then, when I design a screen, it looks like this:

Code: Select all

#undef CurrentArea
#define CurrentArea Town

const byte ScreenTownEntry[] =
{
    INIT_SCREEN(),
    META_TILE(5, 7, House),
    META_TILE(0, 0, Wall),
    ...
}
Now, my meta tile macro would look something like this:

Code: Select all

#define META_TILE(x, y, name)\
    x, y, CurrentArea##name
This way, if I ever try to place a palm tree from the beach into a town screen, the compiler would immediately give me an error.

This makes programming much safer than simply relying on the hope that you just never assign the wrong meta tiles to the wrong areas.

Also, the extra effort of typing NAME(thing) everytime only refers to the macros themselves. I do stuff like

Code: Select all

#define META_TILE(x, y, name)\
    x, y, NAME(CurrentArea, name)
maybe a dozen times in the code, but from then on, the 10000 times when these macros are used, I can omit, for example, the reduntant area name :

Code: Select all

META_TILE(5, 7, House),
META_TILE(0, 0, Wall),
instead of

Code: Select all

META_TILE(5, 7, TownHouse),
META_TILE(0, 0, TownWall),


If you think that my attempt is illegal C code, I'd need you to point me exactly to the relevant error.
The following is a self-contained standalone code. Is there anything invalid in it?

Code: Select all

#define DefNameX_(name1, name2, name3, name4, name5, name6)\
	name1##name2##name3##name4##name5##name6

#define DefName2(name1, name2)\
	DefNameX_(name1, name2, , , , )

#define DECLARE_NUMBER_WITH_POSTFIX()\
    int DefName2(Number, CurrentPostfix)

#undef CurrentPostfix
#define CurrentPostfix A
DECLARE_NUMBER_WITH_POSTFIX();

#undef CurrentPostfix
#define CurrentPostfix B
DECLARE_NUMBER_WITH_POSTFIX();

void Test(void)
{
    NumberA = NumberB;
}
Available now: My game "City Trouble".
Sales website: https://megacatstudios.com/products/city-trouble
Gameplay: https://youtu.be/Eee0yurkIW4
Download website: http://www.denny-r-walter.de/city.htm

User avatar
rainwarrior
Posts: 7669
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Putting together variable names with macros in cc65

Post by rainwarrior » Fri Nov 22, 2019 8:56 pm

Oziphantom wrote:
Fri Nov 22, 2019 3:01 am
BUT this is CC65 which from what I've been told, doesn't even preprocess the preprocessor for the "true one pass" and it does it "in place". Which probably helps, in your case, but having something like ggc -P -E as a first step might come in handy.
No, that's ca65's macro system. cc65's preprocessor is a normal C preprocessor. ca65 doesn't have a preprocessor, even those its macros have some similar looking functionality.

Oziphantom
Posts: 774
Joined: Tue Feb 07, 2017 2:03 am

Re: Putting together variable names with macros in cc65

Post by Oziphantom » Sat Nov 23, 2019 12:17 am

DRW wrote:
Fri Nov 08, 2019 10:22 pm
I have some C source code (in cc65) where I connect variable names by macros:

Code: Select all

#define DECLARE_NUMBER_WITH_POSTFIX()\
	int Number##CurrentPostfix

#define CurrentPostfix A
DECLARE_NUMBER_WITH_POSTFIX();

void Function(void)
{
	NumberA = 1;
}
Your new example does have undefine which as noted in my post is needed. But going by the example as posted this is technically illigal. as you can't
#define CurrentPostfix A

#define CurrentPostfix B
without an undefine in between.

However technically if one did this

Code: Select all

#define CurrentPostfix A
#define DECLARE_NUMBER_WITH_POSTFIX()\
	int Number##CurrentPostfix
DECLARE_NUMBER_WITH_POSTFIX()
#undefine CurrentPostfix 
#define CurrentPostfix B
DECLARE_NUMBER_WITH_POSTFIX()
you could get
int NumberA
int NumberA
this is because when the first define is processed CurrentPostfix is A and hence it set DECLARE_NUMBER_WITH_POSTIFIX to int NumberA, and even though you redefine the postfix after, doesn't mean it will automatically update the previous preprocessor define. If you are lucky the compiler might remember the dependency, or store and keep the whole string and parse it each instance, but it doesn't have to, making it "implementation specific".
Now spread this across multiple files, so the initial define is in a different file which then gets resolved to a single value for it to be put into future files which it may or may not do, it may then decided to process them in a different order depending upon the dependency graph it makes. Illegal might be too strong, maybe 'not legal' C is better.

However for your use case, really just spend the few hours it takes to knock up a level editor in something and let it handle the tile sets for you, you will save more time by making the tool than it will take you to make it.

User avatar
DRW
Posts: 1913
Joined: Sat Sep 07, 2013 2:59 pm

Re: Putting together variable names with macros in cc65

Post by DRW » Sat Nov 23, 2019 4:52 am

Oziphantom wrote:
Sat Nov 23, 2019 12:17 am
Your new example does have undefine which as noted in my post is needed. But going by the example as posted this is technically illigal. as you can't
#define CurrentPostfix A

#define CurrentPostfix B
without an undefine in between.
Well, yes in this context I do have an undefine.
However: This undefine was never an issue to begin with. I never asked: "I want to redefine an existing macro name to another meaning, but the compiler didn't let me." So, why did you even bring it up in the first place?

My question was about something completely different. My problem was about concatenation of names via a macro and that number##CurrentPostfix doesn't get turned into NumberA and stays at NumberCurrentPostfix unless I create three macros that call each other.

I did not say: "The compiler doesn't let me redefine CurrentPostfix to B when it was A before."

Yet you told me that what I want to do is illegal because I cannot redefine macros.
But the redefinition of macros is not the reason why I had a problem because the whole concatenation with ## in macros and the non-transformation of macro name to macro value has nothing to do with defining and redefining existing macros.

I knew about #undef. If I didn't, I wouldn't even have come as far as to create an three-macro-workaround.

Oziphantom wrote:
Sat Nov 23, 2019 12:17 am
However technically if one did this

Code: Select all

#define CurrentPostfix A
#define DECLARE_NUMBER_WITH_POSTFIX()\
	int Number##CurrentPostfix
DECLARE_NUMBER_WITH_POSTFIX()
#undefine CurrentPostfix 
#define CurrentPostfix B
DECLARE_NUMBER_WITH_POSTFIX()
you could get
int NumberA
int NumberA
The code above outputs

Code: Select all

int NumberCurrentPostfix
int NumberCurrentPostfix
And even if I use the version where CurrentPostfix is converted:

Code: Select all

#define CurrentPostfix A

#define DefNameX_(name1, name2, name3, name4, name5, name6)\
	name1##name2##name3##name4##name5##name6

#define DefName2(name1, name2)\
	DefNameX_(name1, name2, , , , )

#define DECLARE_NUMBER_WITH_POSTFIX()\
    int DefName2(Number, CurrentPostfix)

DECLARE_NUMBER_WITH_POSTFIX();

#undef CurrentPostfix
#define CurrentPostfix B

DECLARE_NUMBER_WITH_POSTFIX();
it's still

Code: Select all

int NumberA;
int NumberB;
If you do:

Code: Select all

#define MACRO1 X
#define MACRO2 MACRO1

#undef MACRO1
#define MACRO1 Y

MACRO2
then the value of MACRO1 inside MACRO2 is not already interpreted in line 2, only in line 7. The output is Y, not X.

Oziphantom wrote:
Sat Nov 23, 2019 12:17 am
If you are lucky the compiler might remember the dependency, or store and keep the whole string and parse it each instance, but it doesn't have to, making it "implementation specific".
Can you point me to a source that confirms what you just said? Because as far as I know, macros are only interpreted whenever they are called, not when they are defined. And I have yet to find a compiler that does the above thing differently from any other compiler.
So, if you say this is one of the implementation specific details that a compiler doesn't have to adhere to and can choose a different approach, then I'd need a source where this is stated.

Oziphantom wrote:
Sat Nov 23, 2019 12:17 am
However for your use case, really just spend the few hours it takes to knock up a level editor in something and let it handle the tile sets for you, you will save more time by making the tool than it will take you to make it.
I never had the need for a level editor. Why should I build one because of some quirk in the code that I even had a solution for and I just wanted a more elegant solution?

Besides, my levels/screens aren't just put together with 16 x 16 meta tiles, so that they work like a puzzle and therefore a level editor is easy to program. My screens are object-based, i.e. a meta tile can have an arbitrary size. This size and what the meta tile looks like is defined in a totally different place.

Furthermore, my screen definitions can have conditional setup of screen objects (showing or hiding background elements and characters depending on the game status), destroyable objects, random opponents, fixed characters with or without assigned dialog boxes, geographical regions that trigger cutscenes, dialogs that trigger cutscenes, teleport points that move you to another screen, enemy spawn points and so on.
I will not waste weeks and weeks on some tool for that, just to avoid using a certain macro.

This is apart from the fact that I fail to see how a simple level editor would prevent me from including meta tiles from an incorrect area into another area's screen. For this, I'd have to make it even more complicated by letting it handle the area definitions and the currently loaded graphics and stuff.

Also, this was merely one of several uses for this kind of macro.
I also have the screens in different banks and I want the postfix of the screen name to represent that, so that when I put the screen in another bank and the screen is used in a hardcoded way somewhere, the program immediately tells me the name is wrong:

Code: Select all

#define DEFINE_SCREEN(name)\
    const byte Screen##name##_bScr##CurrentScreenBank [] =

#undef CurrentScreenBank
#define CurrentScreenBank 1

DEFINE_SCREEN(Start)
{
    ...
};

DEFINE_SCREEN(TownEntrance)
{
    ...
};

#undef CurrentScreenBank
#define CurrentScreenBank 2

DEFINE_SCREEN(DungeonEntrance)
{
    ...
};
Available now: My game "City Trouble".
Sales website: https://megacatstudios.com/products/city-trouble
Gameplay: https://youtu.be/Eee0yurkIW4
Download website: http://www.denny-r-walter.de/city.htm

Oziphantom
Posts: 774
Joined: Tue Feb 07, 2017 2:03 am

Re: Putting together variable names with macros in cc65

Post by Oziphantom » Sat Nov 23, 2019 8:25 am

http://www2.cs.uregina.ca/~hilder/cs833 ... nguage.pdf Page 189 and then 190.
the argument tokens are examined for macro calls, and expanded as necessary, just before insertion
This is what you are seeing and what you would expect.. however the bit of text before it
Unless the parameter in there placement sequence is preceded by #, or preceded or followed by ##,
which is what puts this case into "open for interpretation". note the
The effect is undefined if invalid tokens are produced, or if the result depends on the order of processing of the ## operators
then also note the
Some of the new rules, especially those involving concatenation, are bizarre.
So it neither says what I say, and neither says what you say which is my point. It's not guaranteed to work, and it's not guaranteed to fail. ## is kind of a special case, and I've seen it break in the past,
I've never seen ## on any operator precedence tables, and I could not find one in the standard above, if one does know of one, please tell.
define is fine, undef with define and multiple instances is also something that can get a bit undetermined sadly, normally solved by a clean rebuild.. normally.

A classic case where macros don't always do what you think they should do is the __FILE__ and __LINE__ "extensions", you want them to show you the file and line where you call the macro, but unless you actually put them in on said line, you end up with the file and line the macro was originally defined on and not the line it was called on.
some file #define LOG_THIS(x) LOG(X,__FILE__,__LINE__)
in another file
LOG_THIS("Something happened")
gives you MACROS.H:120 Something happened
LOG_THIS("Something happened",__FILE__,__LINE__)
Myfile.m:325 Something happened
Of cause given the preprocessor fully expands the macro on invocation this shouldn't be a problem and should "just worktm" luckily there is usually a magic sequence, order, to make it work in your compiler of choice though thankfully.

User avatar
DRW
Posts: 1913
Joined: Sat Sep 07, 2013 2:59 pm

Re: Putting together variable names with macros in cc65

Post by DRW » Sat Nov 23, 2019 11:56 am

Oziphantom wrote:
Sat Nov 23, 2019 8:25 am
the argument tokens are examined for macro calls, and expanded as necessary, just before insertion
This is what you are seeing and what you would expect.. however the bit of text before it
Unless the parameter in there placement sequence is preceded by #, or preceded or followed by ##,
which is what puts this case into "open for interpretation".
As far as I see this, this isn't open to interpretation. Yes, the text says "unless [it's] preceded or followed by ##".
However, the "unless" doesn't mean: "The parameters are expanded before insertion, unless they have ##, in which case they can also be expanded during the macro declaration instead of the macro call."

This is not what they're saying. They say "unless" because # and ## have additional rules, which is explained in the next paragraph:
Second, if the definition token sequence for either kind of macro contains a ## operator, then just after replacement of the parameters, each ## is deleted, together with any white space on either side, so as to concatenate the adjacent tokens and form a new token.
So, it merely says: "Macro parameters are expanded before insertion, unless they have # or ##. In this case, they have additional stuff done with them."
But it doesn't mean: "Macro parameters are expanded before insertion, unless they have # or ##. In this case, the parameters are already parsed and interpreted in the line where the macro gets defined."

Especially since we're not even talking about a parameter here: CurrentPostfix in my examples isn't a macro parameter that's passed in the parentheses, it's a global part of the macro itself, so it refers even less to the described exceptions.

Oziphantom wrote:
Sat Nov 23, 2019 8:25 am
note the
The effect is undefined if invalid tokens are produced, or if the result depends on the order of processing of the ## operators
Sure, there might be undefined behavior in there if you create some bizarre stuff. For example, if you call a macro and one of the parameters is a macro itself:
After
#define cat(x, y) x ## y
the call cat(var, 123) yields var123. However, the call cat(cat(1,2),3) is undefined: the presence of ## prevents the arguments of the outer call from being expanded. Thus it produces the token string
cat ( 1 , 2 )3
and )3 (the catenation of the last token of the first argument with the first token of the second) is not a legal token.
Again, the undefined behavior refers to some really strange stuff that you might try to do during a macro call.

But at no point ever in this whole paragraph does the text even slightly allude to the idea that the macro tokens might be interpreted at definition time instead of usage time. Especially since, as I said, my CurrentPostfix isn't even a macro parameter, but just something that's used inside the macro globally.

So, no, the paragraph doesn't say anything about the idea that the preprocessor might turn this:

Code: Select all

#define CurrentArea A

#define MACRO(name) name##CurrentArea
into this:

Code: Select all

#define CurrentArea A

#define MACRO(name) name##A
There's nothing that even hints at the idea that macro contents are expanded in the line where the #define resides.
The undefined behavior examples all refer to strange situations when you call the macro.

Oziphantom wrote:
Sat Nov 23, 2019 8:25 am
So it neither says what I say, and neither says what you say which is my point.
Well, it does say that macro parameters are expanded at call time, but never says that they are expanded at #define time.
And this counts even more for stuff that's in the macro but that's not in the parameter list.
Hence, CurrentArea will, if at all, only be replaced whenever the macro is called.
So, you can define and redefine CurrentArea multiple times and the macro calls will always use the current value.
The idea that the macro definition itself transforms CurrentArea immediately, so every macro call always has the replacement of what CurrentArea happened to be at the moment of the macro definition, this is totally not what the paragraph in that book was talking about.

Oziphantom wrote:
Sat Nov 23, 2019 8:25 am
A classic case where macros don't always do what you think they should do is the __FILE__ and __LINE__ "extensions", you want them to show you the file and line where you call the macro, but unless you actually put them in on said line, you end up with the file and line the macro was originally defined on and not the line it was called on.
some file #define LOG_THIS(x) LOG(X,__FILE__,__LINE__)
in another file
LOG_THIS("Something happened")
gives you MACROS.H:120 Something happened
LOG_THIS("Something happened",__FILE__,__LINE__)
Myfile.m:325 Something happened
Sorry, but that's wrong.

Unfortunately, I cannot try out your example myself since you never gave me the LOG macro.
But this program:

Code: Select all

#include <stdio.h>
#include <conio.h>

#define LOG(text, formatParameter) printf(text, formatParameter)
#define LOG_THIS() LOG("Current line: %i\n", __LINE__)

int main(void)
{
    LOG_THIS();
    LOG_THIS();

    getch();

    return 0;
}
outputs
Current line: 8
Current line: 9
and not
Current line: 5
Current line: 5

Same when I put the LOG macro into a header file and use the __FILE__ macro: It gives me the file name of my C file. Even if the LOG macro in the header file uses the __FILE__ macro itself:

Code: Select all

Macros.h:
#define LOG(text) printf(text, __FILE__)

Main.c:
#define LOG_THIS() LOG("Current file: %s\n")
Output:
Main.c

Such an example is even used on websites:

https://www.cprogramming.com/reference/ ... INE__.html

Code: Select all

int logError (int line, const std::string& message)
{
    cerr << "[" << line << "]" << message << endl;
}
#define LOG( message ) logError( __LINE__, message )

// LOG can now be used to produce a log message that includes the line on which the log occurrred
I don't know where you got your interpretations from.

So, if you can create a minimalistic compilable example where __FILE__ and __LINE__ behave the way you say and tell me which compiler you used, then I can check it out.
Otherwise, I cannot just take your word for it if actual compilers produce a different result from what you're claiming.
Available now: My game "City Trouble".
Sales website: https://megacatstudios.com/products/city-trouble
Gameplay: https://youtu.be/Eee0yurkIW4
Download website: http://www.denny-r-walter.de/city.htm

Oziphantom
Posts: 774
Joined: Tue Feb 07, 2017 2:03 am

Re: Putting together variable names with macros in cc65

Post by Oziphantom » Sun Nov 24, 2019 3:10 am

DRW wrote:
Sat Nov 23, 2019 11:56 am
Oziphantom wrote:
Sat Nov 23, 2019 8:25 am
the argument tokens are examined for macro calls, and expanded as necessary, just before insertion
This is what you are seeing and what you would expect.. however the bit of text before it
Unless the parameter in there placement sequence is preceded by #, or preceded or followed by ##,
which is what puts this case into "open for interpretation".
As far as I see this, this isn't open to interpretation. Yes, the text says "unless [it's] preceded or followed by ##".
However, the "unless" doesn't mean: "The parameters are expanded before insertion, unless they have ##, in which case they can also be expanded during the macro declaration instead of the macro call."

This is not what they're saying. They say "unless" because # and ## have additional rules, which is explained in the next paragraph:
Second, if the definition token sequence for either kind of macro contains a ## operator, then just after replacement of the parameters, each ## is deleted, together with any white space on either side, so as to concatenate the adjacent tokens and form a new token.
So, it merely says: "Macro parameters are expanded before insertion, unless they have # or ##. In this case, they have additional stuff done with them."
But it doesn't mean: "Macro parameters are expanded before insertion, unless they have # or ##. In this case, the parameters are already parsed and interpreted in the line where the macro gets defined."

Especially since we're not even talking about a parameter here: CurrentPostfix in my examples isn't a macro parameter that's passed in the parentheses, it's a global part of the macro itself, so it refers even less to the described exceptions.

Oziphantom wrote:
Sat Nov 23, 2019 8:25 am
note the
The effect is undefined if invalid tokens are produced, or if the result depends on the order of processing of the ## operators
Sure, there might be undefined behavior in there if you create some bizarre stuff. For example, if you call a macro and one of the parameters is a macro itself:
After
#define cat(x, y) x ## y
the call cat(var, 123) yields var123. However, the call cat(cat(1,2),3) is undefined: the presence of ## prevents the arguments of the outer call from being expanded. Thus it produces the token string
cat ( 1 , 2 )3
and )3 (the catenation of the last token of the first argument with the first token of the second) is not a legal token.
Again, the undefined behavior refers to some really strange stuff that you might try to do during a macro call.

But at no point ever in this whole paragraph does the text even slightly allude to the idea that the macro tokens might be interpreted at definition time instead of usage time. Especially since, as I said, my CurrentPostfix isn't even a macro parameter, but just something that's used inside the macro globally.

So, no, the paragraph doesn't say anything about the idea that the preprocessor might turn this:

Code: Select all

#define CurrentArea A

#define MACRO(name) name##CurrentArea
into this:

Code: Select all

#define CurrentArea A

#define MACRO(name) name##A
There's nothing that even hints at the idea that macro contents are expanded in the line where the #define resides.
The undefined behavior examples all refer to strange situations when you call the macro.

Oziphantom wrote:
Sat Nov 23, 2019 8:25 am
So it neither says what I say, and neither says what you say which is my point.
Well, it does say that macro parameters are expanded at call time, but never says that they are expanded at #define time.
And this counts even more for stuff that's in the macro but that's not in the parameter list.
Hence, CurrentArea will, if at all, only be replaced whenever the macro is called.
So, you can define and redefine CurrentArea multiple times and the macro calls will always use the current value.
The idea that the macro definition itself transforms CurrentArea immediately, so every macro call always has the replacement of what CurrentArea happened to be at the moment of the macro definition, this is totally not what the paragraph in that book was talking about.
Ok you are right. They will all expand at the same time and the same way.
DRW wrote:
Sat Nov 23, 2019 11:56 am
Oziphantom wrote:
Sat Nov 23, 2019 8:25 am
A classic case where macros don't always do what you think they should do is the __FILE__ and __LINE__ "extensions", you want them to show you the file and line where you call the macro, but unless you actually put them in on said line, you end up with the file and line the macro was originally defined on and not the line it was called on.
some file #define LOG_THIS(x) LOG(X,__FILE__,__LINE__)
in another file
LOG_THIS("Something happened")
gives you MACROS.H:120 Something happened
LOG_THIS("Something happened",__FILE__,__LINE__)
Myfile.m:325 Something happened
Sorry, but that's wrong.

Unfortunately, I cannot try out your example myself since you never gave me the LOG macro.
But this program:

Code: Select all

#include <stdio.h>
#include <conio.h>

#define LOG(text, formatParameter) printf(text, formatParameter)
#define LOG_THIS() LOG("Current line: %i\n", __LINE__)

int main(void)
{
    LOG_THIS();
    LOG_THIS();

    getch();

    return 0;
}
outputs
Current line: 8
Current line: 9
and not
Current line: 5
Current line: 5

Same when I put the LOG macro into a header file and use the __FILE__ macro: It gives me the file name of my C file. Even if the LOG macro in the header file uses the __FILE__ macro itself:

Code: Select all

Macros.h:
#define LOG(text) printf(text, __FILE__)

Main.c:
#define LOG_THIS() LOG("Current file: %s\n")
Output:
Main.c

Such an example is even used on websites:

https://www.cprogramming.com/reference/ ... INE__.html

Code: Select all

int logError (int line, const std::string& message)
{
    cerr << "[" << line << "]" << message << endl;
}
#define LOG( message ) logError( __LINE__, message )

// LOG can now be used to produce a log message that includes the line on which the log occurrred
I don't know where you got your interpretations from.

So, if you can create a minimalistic compilable example where __FILE__ and __LINE__ behave the way you say and tell me which compiler you used, then I can check it out.
Otherwise, I cannot just take your word for it if actual compilers produce a different result from what you're claiming.
It seems this is a globally solved problem these days. For example I made the trivial example on VS2019 which due to it being VS and it being 2019 becomes

Code: Select all

#define DO_LOG(x,l,f) {WCHAR buffer[512]; \
	swprintf_s(buffer, 512, L"%s:%d %s", WFILE, __LINE__, L"did the thing"); \
	OutputDebugString(buffer); }

#define LOG_LINE(x) DO_LOG(x,__LINE__,__FILE__)

#define WIDE2(x) L##x
#define WIDE1(x) WIDE2(x)
#define WFILE WIDE1(__FILE__)

I remember spending 3 days or so getting it to work on Visual Studio 6.0, I remember when we upgraded the GCC version on the PS2 it broke them, and one of the programmers eventually worked out that using variable length arguments to pass things made it work again.
However as an example of how compilers do different things when expanding I was able to find this https://stackoverflow.com/questions/129 ... -expansion
I've checked, it still compiles fine on VS2019. Using the handy link above by wonder, its fails to compile on everything in the list except for the MSVC compilers of which it works just fine.

User avatar
DRW
Posts: 1913
Joined: Sat Sep 07, 2013 2:59 pm

Re: Putting together variable names with macros in cc65

Post by DRW » Sun Nov 24, 2019 6:51 am

Oziphantom wrote:
Sun Nov 24, 2019 3:10 am
Ok you are right. They will all expand at the same time and the same way.
Not sure if this is supposed to be sarcasm, but in case it is, I'll repeat the gist of it one last time:

No, macros won't always expand in the same way.
But they will always expand at the same time: Always when they are called, never when they are defined. (The order of expanding them if macros are called as another macro's parameter or if parameters are put together with ## might then be different again. But in terms of define time vs. call time, it's always at call time, never at define time.)

This:

Code: Select all

#define CONSTANT 1

#define MACRO() int i = CONSTANT

#undef CONSTANT
#define CONSTANT 2

MACRO();
will always expand to int i = 2;. Always! This is not implementation-specific or up to debate in any case, not even if there are a few ## in there. It never matters what a constant's value is in the line where a macro is defined. If the macro uses the constant, the constant won't get expanded until the macro is actually called. The above code will never turn into int i = 1;

And that's the last I have to say about it until someone gives me an actual proof of the contrary.

Oziphantom wrote:
Sun Nov 24, 2019 3:10 am
For example I made the trivial example on VS2019 which due to it being VS and it being 2019 becomes

Code: Select all

#define DO_LOG(x,l,f) {WCHAR buffer[512]; \
	swprintf_s(buffer, 512, L"%s:%d %s", WFILE, __LINE__, L"did the thing"); \
	OutputDebugString(buffer); }

#define LOG_LINE(x) DO_LOG(x,__LINE__,__FILE__)
I have literally no idea what you're trying to accomplish here. You define a macro with three parameters: x, l and f. And none of them are used by you.

If you want me to understand your code, give me a complete and reproducable example, tell me your expected result and the compiler's actual result and tell me the compiler.
This thing above might compile, but as a demonstration it makes no sense because you posted it in a state where the macro has parameters, but where the macro's body uses fixed values and the parameters are never referenced.
So, I have no idea whether this was actually the intention or whether you just made a typing mistake.

Or is it just about the use of the WFILE macro? In this case, what's the big deal about LOG_LINE calling DO_LOG, putting the result in a buffer variable, having those unused parameters etc.?
Just tell me that you tried to put an L in front of a string that's represented as a macro (in this case __FILE__) and go from there. Post a clean, minimalistic example. Don't copy & paste unnecessary code from your original project where you never removed unused parameters and then let me guess where you encountered a problem.

Oziphantom wrote:
Sun Nov 24, 2019 3:10 am

Code: Select all

#define WIDE2(x) L##x
#define WIDE1(x) WIDE2(x)
#define WFILE WIDE1(__FILE__)
Hey look! If this isn't exactly the problem that I described in my very first post, the only reason this thread even exists, and the one concept that you advised me against using, so that I should rather program a level editor instead.

Isn't it ironic that you questioned my use of such a construct, yet you needed it yourself long before I did?

Oziphantom wrote:
Sun Nov 24, 2019 3:10 am
I remember spending 3 days or so getting it to work on Visual Studio 6.0
VS6 was never a standard-compliant compiler to begin with. So, yeah, nothing that this compiler is doing should be used as a reference for anything.

Oziphantom wrote:
Sun Nov 24, 2019 3:10 am
I remember when we upgraded the GCC version on the PS2 it broke them, and one of the programmers eventually worked out that using variable length arguments to pass things made it work again.
Again, I have no idea what you're talking about:

What worked in VS6 and broke in gcc? The WFILE parameter? Or the way __FILE__ and __LINE__ are resolved (which you brought up in your previous post)?

Where are you using "variable length arguments"? What do you mean by it? I'm not aware that variable length parameters exist for macros, only for functions.
If you mean the parameters in swprintf_s, why do you complicate the matter by introducing this new topic? We were talking about macros, not about functions. So, the fact that you did a workaround specifically for swprintf_s which cannot be recreated in arbitrary situations has nothing to do with anything we were talking about and only makes the thread more complicated.

What did the code that worked in VS6, but broke in gcc look like? Or if the above is that code: What does the code that works in gcc look like in contrast?


Also, you found examples of undefined compiler behavior on Stackoverflow. So? I never doubted that this exists.
And again, this still doesn't prove that macros are already expanded at define time. Even the example you linked is an issue that happens, when a macro gets called. So, what exactly are you trying to prove with this example?


Alright, I think that's all from my side in this thread until someone can prove to me that my solution to my original problem relies on undefined behavior. Because other than that, I don't really care about any random different situations where people encountered undefined behavior in C code.
Available now: My game "City Trouble".
Sales website: https://megacatstudios.com/products/city-trouble
Gameplay: https://youtu.be/Eee0yurkIW4
Download website: http://www.denny-r-walter.de/city.htm

Post Reply