Transport Tycoon Forums

The place to talk about Transport Tycoon
It is currently Mon Nov 12, 2018 7:05 pm

All times are UTC




Post new topic  Reply to topic  [ 6 posts ] 
Author Message
PostPosted: Sat Apr 01, 2017 2:32 pm 
Offline
Tycoon
Tycoon

Joined: Tue Feb 22, 2011 6:34 pm
Posts: 2654
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:
#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


Top
   
PostPosted: Sat Apr 01, 2017 3:31 pm 
Offline
Transport Coordinator
Transport Coordinator

Joined: Mon Nov 07, 2011 7:32 pm
Posts: 330
Location: PL
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:
#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:
#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:
#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


Top
   
PostPosted: Sat Apr 01, 2017 6:40 pm 
Offline
Tycoon
Tycoon

Joined: Tue Feb 22, 2011 6:34 pm
Posts: 2654
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


Top
   
PostPosted: Sun Apr 02, 2017 8:56 am 
Offline
OpenTTD Developer
OpenTTD Developer

Joined: Sun Sep 09, 2007 5:03 am
Posts: 4677
Location: home
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:
#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:
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 OpenTTD developer does not mean I know what I am doing.
Also, other OpenTTD developers may have different opinions.


Top
   
PostPosted: Sun Apr 02, 2017 9:46 am 
Offline
Tycoon
Tycoon
User avatar

Joined: Sat Mar 31, 2007 2:23 pm
Posts: 4705
Location: Lost in Music
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.

_________________
FIRS Industry Replacement Set (Released) | HEQS Heavy Equipment Set (trucks, industrial trams and more) (Finished)
Squid Ate FISH (ships) (Released) | CHIPS Has Improved Players' Stations (Finished)
Iron Horse (trains, released) | Termite (tracks for Iron Horse, released) | Busy Bee (game script, released)

Road Hog (road vehicles, released)


Top
   
PostPosted: Sun Apr 02, 2017 11:29 am 
Offline
Tycoon
Tycoon

Joined: Tue Feb 22, 2011 6:34 pm
Posts: 2654
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:
#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.

Quote:
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


Top
   
Display posts from previous:  Sort by  
Post new topic  Reply to topic  [ 6 posts ] 

All times are UTC


Who is online

Users browsing this forum: No registered users and 3 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000-2018 phpBB Limited

Copyright © Owen Rudge/The Transport Tycoon Forums 2001-2018.
Hosted by Zernebok Hosting.