Note that the first one the data is actually not in a data file but in a .h template file, but there are cases where it is and the .h file for the preprocessor is just a link to get the name of the function compiled in.

I was going to ask which of these is better, but on looking at them I think the second one clearly wins - which is a shame, since there's only one of those and lots of the other.

Am I right? Have I missed something? Is there another method (not involving lua, python or whatever)?

__________________
One Ring to rule them all, One Ring to find them,
One Ring to bring them all and in the darkness bind them.

If I understand the question correctly, you're trying to figure out how to identify a) what function needs to be invoked, and b) what parameters to pass to the function, at runtime? If so, then yes, absolutely, the explicitly coupled version is superior to using preprocessor hacks, if only for readability -- the problem I have with the preprocessor directives is that functions get defined/invoked "magically" -- there's not an obvious path in the code to get from point A to point B because the code as written isn't complete.

The value_bases array in the second example is much more like what I'd aim for. It's effectively a manually-created dispatch table, where you use a string to look up the function you want to invoke. Then someone reading the code can say "ah, this function is going to invoke one of these other functions depending on the value of this string"; nice and straightforward to comprehend.

Neither is better. When you want an enum, you must use a .h file, at which point it's better to put all associated info in the same place. Same holds for generated code.
When you want pure data, of course a data file is better

Neither is better. When you want an enum, you must use a .h file, at which point it's better to put all associated info in the same place. Same holds for generated code.
When you want pure data, of course a data file is better

Why can't you get an enum out of the data file? It would be something like this:

Read through file once and catalog the number of functions of different types
Make the enumerated list
Read through again and get the data

Write a little code generator which pre-processes the data files into something usable for the C compiler, be it struct definitions or enums or whatever. An added bonus is that the generated definitions/code are easily greppable contrary to the "X macro" pattern.

(Unless I'm misunderstanding the goals here. As long as the values in the data files actually don't change at run-time, what I'm suggesting should work pretty nicely.)

The goal is to make the game as player modifiable - without compiling - as possible. Obviously there are limits, but I want to get close to them. And there is quite a variety of situations to deal with, so one solution won't fit all of them.

__________________
One Ring to rule them all, One Ring to find them,
One Ring to bring them all and in the darkness bind them.

The goal is to make the game as player modifiable - without compiling - as possible. Obviously there are limits, but I want to get close to them. And there is quite a variety of situations to deal with, so one solution won't fit all of them.

Oh, right. Forgot the "without compiling" bit...

In that case you're going to eventually run into Greenspun's tenth rule. The only real way "out" of that is to bite the bullet and embed an interpreter/compiler rather than ending up implementing one's own (inevitably awful) variant of a LISP. However, personally, I don't think doing so is a good idea unless it's a language with LISP-level support for DSLs and meta-programs. Not mention that it's already been tried to limited success in both Angband and ToME2 -- though pretty half-heartedly AFAIUI in both cases[1]. If it were up to me, I'd rather focus on just making it easier to make changes to the C code and making it as easy as possible to compile.

[1] In the ToME2 case the interface between the C bits and the Lua bits were an awful mix of high-level and low-level -- all the C structs were basically just exported completely as-is to the Lua bits. It was incredibly error prone and the dynamic type checking of Lua exacerbated that whenever one had to interface with the C data/code; so we got basically none of the benefits of dynamic type checking, but all of the drawbacks. Not a happy place.

Note that the solution I came up with for Pyrel for this problem did not involve embedding code into the data files, even though I could have trivially done that as Python has an exec() function that allows it to execute strings as programs*. Instead, if I wanted e.g. a wand that fired three elemental balls in succession, then the wand's object file entry would look something like:

The code that parsed these files would hand off the "use_effect" section to a separate parser, which would examine the "proc_type" entry to determine which function to call, and then hand that function the rest of the entry (with the radius, target info, element, and damage) which it would use as parameters to decide what to actually do. So somewhere in the code was a mapping of strings to functions that looked like this:

All of those functions had the same signature: they took a "record" parameter, which is one of the mappings loaded from the data file. They were then responsible for extracting data from the record, providing default values for anything that wasn't present, etc.

Really the only fancy part of this is the equation evaluator.

* You should never, ever call this function as it is a security nightmare, but it does exist.