gcc/make problem with define

Discussions about the technical aspects of graphics development, including NewGRF tools and utilities.

Moderator: Graphics Moderators

Post Reply
Transportman
Tycoon
Tycoon
Posts: 2781
Joined: 22 Feb 2011 18:34

gcc/make problem with define

Post by Transportman »

Hi all,

For a recode of the 2cc TrainsInNML I'm working on, I want to ease the reuse of some code that is coming back regularly. One of the things I'm working on, are capacity switches for several passenger vehicles. I'm using the following definition:

Code: Select all

#define ENGINECAPACITYSWITCH(VEHICLE_NAME, TYPE) \
switch(FEAT_TRAINS, SELF, switch_VEHICLEIDENTIFIER_capacity_engine, cargo_classes) { \
	bitmask(CC_MAIL): HEAD_CAPACITY/2; \
	bitmask(CC_ARMOURED): HEAD_CAPACITY/4; \
	HEAD_CAPACITY; \
}
The VEHICLE_NAME and TYPE input are not used, I'm aware of that, but that is not the problem. Both VEHICLEIDENTIFIER and HEAD_CAPACITY are defined before this ENGINECAPACITYSWITCH is included for a vehicle, but the problem that I'm having is that VEHICLEIDENTIFIER is not replaced, with the defined value, while HEAD_CAPACITY is.

As I also have other switches for which I want to do something like this, I would really like to have this working.
Coder of the Dutch Trackset | Development support for the Dutch Trainset | Coder of the 2cc TrainsInNML
TadeuszD
Transport Coordinator
Transport Coordinator
Posts: 329
Joined: 07 Nov 2011 19:32
Location: PL

Re: gcc/make problem with define

Post by TadeuszD »

Transportman wrote:Both VEHICLEIDENTIFIER and HEAD_CAPACITY are defined before this ENGINECAPACITYSWITCH is included for a vehicle, but the problem that I'm having is that VEHICLEIDENTIFIER is not replaced, with the defined value, while HEAD_CAPACITY is.
To concatenate defined strings you should use '##'. For example:

Code: Select all

#define ENGINE_RUNNING_COST_SWITCH(Engine) \
switch (FEAT_TRAINS, PARENT, switch_##Engine##_running_cost, current_speed ) { \
	0: return HALTED_COST_##Engine; \
	return RUNNING_COST_##Engine; \
}
But it works only in the scope of current #define directive. You can not use other definitions. The example below will throw the error, ##ANY_STRING will not be replaced:

Code: Select all

#define ANY_STRING 123
...
#define ENGINE_RUNNING_COST_SWITCH(Engine) \
switch (FEAT_TRAINS, PARENT, switch_##Engine##_running_cost, current_speed ) { \
	0: return HALTED_COST_##Engine; \
	return RUNNING_COST_##ANY_STRING; \
}
You should pass the ANY_STRING value as new argument in your definition:

Code: Select all

#define ANY_STRING 123
...
#define ENGINE_RUNNING_COST_SWITCH(Engine, AnyString) \
switch (FEAT_TRAINS, PARENT, switch_##Engine##_running_cost, current_speed ) { \
	0: return HALTED_COST_##Engine; \
	return RUNNING_COST_##AnyString; \
}
...
ENGINE_RUNNING_COST_SWITCH(MY_ENGINE, ANY_STRING)
Image
Transportman
Tycoon
Tycoon
Posts: 2781
Joined: 22 Feb 2011 18:34

Re: gcc/make problem with define

Post by Transportman »

Thanks for the response, but I still cannot get it to work, it replaces the argument with the name of the #define instead of the value of the define. And I can't understand why HEAD_CAPACITY is replaced just fine.

Could I otherwise send you the source so that you can take a look at it? It might be something really small that is making it not working for me, but I just can't see it.
Coder of the Dutch Trackset | Development support for the Dutch Trainset | Coder of the 2cc TrainsInNML
Alberth
OpenTTD Developer
OpenTTD Developer
Posts: 4763
Joined: 09 Sep 2007 05:03
Location: home

Re: gcc/make problem with define

Post by Alberth »

Transportman wrote:Thanks for the response, but I still cannot get it to work, it replaces the argument with the name of the #define instead of the value of the define. And I can't understand why HEAD_CAPACITY is replaced just fine.
"cpp" is a C pre-processor, and it applies C conventions on names in replacements. In particular, it does not break names to replace parts of it with #define content.

It finds "switch_VEHICLEIDENTIFIER_capacity_engine" as name, and compares that with "VEHICLEIDENTIFIER" #define name, and concludes the names are not the same, and does not do replacement.
If finds "HEAD_CAPACITY" as name, and compares that with "HEAD_CAPACITY" #define name, and concludes it's the same, and replace the HEAD_CAPACITY with the content of the #define.

To get VEHICLEIDENTIFIER replaced, the ## magic comes in ( https://gcc.gnu.org/onlinedocs/cpp/Conc ... catenation ). Basically, it allows parameters from the #define to get replaced before joining them together to a legal C name. The documentation doesn't say anything about replacing definitions first, it doesn't seem designed to do this at all. Your best bet imho is not to try this, and instead specify the value of VEHICLEIDENTIFIER as parameter

Code: Select all

#define HEAD_CAPACITY infinite
#define ENGINECAPACITYSWITCH(VEHICLEIDENTIFIER, VEHICLE_NAME, TYPE) \
switch(FEAT_TRAINS, SELF, switch_ ## VEHICLEIDENTIFIER ## _capacity_engine, cargo_classes) { \
   bitmask(CC_MAIL): HEAD_CAPACITY/2; \
   bitmask(CC_ARMOURED): HEAD_CAPACITY/4; \
   HEAD_CAPACITY; \
}

ENGINECAPACITYSWITCH(vi, abc, nonetype)
results in

Code: Select all

switch(FEAT_TRAINS, SELF, switch_vi_capacity_engine, cargo_classes) { bitmask(CC_MAIL): infinite/2; bitmask(CC_ARMOURED): infinite/4; infinite; }

Far from ideal, I agree. Basically, you need something better than cpp. m4 would work, but it comes with its own quirks. Andythenorth basically dropped cpp, and uses Python with templating instead. Perhaps someone needs to make a better processing language on top of nml (like cpp, but better), or nml could be extended.
The latter doesn't work nicely with the parser though in an experiment I once tried, so it involves much more work than it seems at first sight.
Being a retired OpenTTD developer does not mean I know what I am doing.
User avatar
andythenorth
Tycoon
Tycoon
Posts: 5656
Joined: 31 Mar 2007 14:23
Location: Lost in Music

Re: gcc/make problem with define

Post by andythenorth »

For this case (concatenating switch names) you need variadic macros with the C pre-processor.

I found it better to switch to a more appropriate templating language at this point. I use Chameleon because I'm familiar with it, but there are other choices.

The case of templating switch names is so common that ideally nml would provide for this, but it's not trivial to do that.
Transportman
Tycoon
Tycoon
Posts: 2781
Joined: 22 Feb 2011 18:34

Re: gcc/make problem with define

Post by Transportman »

Thanks for the help and suggestions.

I found a workaround using CPP, while still passing the #define to it. Instead of directly calling the define that defines the switch, I call a define that calls the define that defines the switch.

Code: Select all

#define ENGINECAPACITYSWITCHNAME(VEHIDCODE) EXPANDEDENGINECAPACITYSWITCHNAME(VEHIDCODE)
#define ENGINECAPACITYSWITCHCALL(VEHIDCODE) EXPANDEDENGINECAPACITYSWITCHCALL(VEHIDCODE)
#define ENGINECAPACITYSWITCH(VEHIDCODE) EXPANDEDENGINECAPACITYSWITCH(VEHIDCODE)


#define EXPANDEDENGINECAPACITYSWITCHNAME(VEHIDCODE) switch_ ## VEHIDCODE ## _capacity_engine
#define EXPANDEDENGINECAPACITYSWITCHCALL(VEHIDCODE) cargo_capacity: switch_ ## VEHIDCODE ## _capacity_engine;

#define EXPANDEDENGINECAPACITYSWITCH(VEHIDCODE) \
switch(FEAT_TRAINS, SELF, switch_ ## VEHIDCODE ## _capacity_engine, cargo_classes) { \
	bitmask(CC_MAIL): HEAD_CAPACITY/2; \
	bitmask(CC_ARMOURED): HEAD_CAPACITY/4; \
	HEAD_CAPACITY; \
}
andythenorth wrote:For this case (concatenating switch names) you need variadic macros with the C pre-processor.

I found it better to switch to a more appropriate templating language at this point. I use Chameleon because I'm familiar with it, but there are other choices.
For me there is not much use to switch to something else, although I do have to admit that it is not the nicest code I now have.
The case of templating switch names is so common that ideally nml would provide for this, but it's not trivial to do that.
Not only that, but a way to pass along values as arguments to the switch would be nice. Then I could just use those arguments in a simple switch that is the same for all vehicles, and be done with it. A bit like templates for graphics, just give in some arguments and the rest is done automatically.
Coder of the Dutch Trackset | Development support for the Dutch Trainset | Coder of the 2cc TrainsInNML
Post Reply

Return to “NewGRF Technical Discussions”

Who is online

Users browsing this forum: No registered users and 4 guests