Post by pozzPost by David BrownYou don't need a very fancy pre-processor to handle this yourself, if
you are happy to make a few changes to the code. Have your code use
#define DisplayPrintf(id, desc, args...) \
display_printf(strings[language][string_ ## id], ## x)
DisplayPrintf(event_type_on, "Event on", ev->idx);
A little Python preprocessor script can chew through all your C files
and identify each call to "DisplayPrintf".
Little... yes, it would be little, but not simple, at least for me. How
to write a correct C preprocessor in Python?
You don't write a C preprocessor - that's the point.
Tools like gettext have to handle any C code. That means they need to
deal with situations with complicated macros, include files, etc.
You don't need to do that when you make your own tools. You make the
rules - /you/ decide what limitations you will accept in order to
simplify the pre-processing script.
So you would typically decide you only put these DisplayPrintf calls in
C files, not headers, that you ignore all normal C preprocessor stuff,
and that you keep each call entirely on one line, and that you'll never
use the sequence "DisplayPrintf" for anything else. Then your Python
preprocessor becomes :
for this_line in open(filename).readlines() :
if "DisplayPrintf" in line :
handle(line)
This is /vastly/ simpler than dealing with more general C code, without
significant restrictions to you as the programmer using the system.
If you /really/ want to handle include files, conditional compilation
and all rest of it, get the C compiler to handle that - use "gcc -E" and
use the output of that. Trying to duplicate that in your own Python
code would be insane.
Post by pozzThis preprocessor should ingest a C source file after it is preprocessed
by the standard C preprocessor for the specific build you are doing.
#if BUILD == BUILD_FULL
DisplayPrintf(msg, "Press (1) for simple process, (2) for advanced
process");
x = wait_keypress();
if (x == '1') do_simple();
if (x == '2') do_adv();
#elif BUILD == BUILD_LIGHT
do_simple();
#endif
The really simple answer is, don't do that.
Post by pozzIf I'm building the project as BUILD_FULL, there's at least one
additional string to translate.
The slightly more complex answer is that you end up with an extra string
in one build or the other. Almost certainly, this is not worth
bothering about. And if it is - say you have a large number of extra
strings in a debug test build - then I'm sure you can find convenient
ways to handle that. At a minimum, you'd probably not bother having
translated versions but fall back to English.
Post by pozzAnother big problem is the Python preprocessor should understand C
syntax; it shouldn't simply search for DisplayPrintf occurrences.
Why not?
Post by pozz/* DisplayPrintf(old_string, "This is an old message"); */
DisplayPrintf(new_string, "This is a new message");
Of course, only one string is present in the source file, but it's not
simple to extract it.
It's extremely simple to extract it. Remember - /you/ make the rules.
If you don't want to bother skipping such commented-out lines, /you/
pick a convenient way to do so. For example, you would decide that the
opening comment token must be at the start of the white-space stripped
line :
if line.strip().startswith("/*") :
return False
if line.strip().startswith("//") :
return False
(I've been talking about Python here, because that's the language I use
for such tools, and it's a very common choice. If you are not familiar
with Python then you can obviously use any other language you like.)
Or alternatively, have :
#define XDisplayPrintf(...)
And now your commenting system becomes :
XDisplayPrintf(old_string, "This is an old message");
DisplayPrintf(new_string, "This is a new message");
The "XDisplayPrintf" can be inside comments or conditionally uncompiled
code if you like. (You do have to filter out XDisplayPrintf bits from
the earlier check for DisplayPrintf.)
Post by pozzThanks for the suggestion, the idea is great. However I'm not able to
write a Python preprocessor that works well.
Sure you can. You just have to redefine what you mean by "works well"
to suit what you can write :-)
For my own use, I probably wouldn't even bother handling commented-out
strings. I have used this kind of technique for message translation and
a variety of other situations.
For more fun, you could switch to modern C++ and use user-defined
literals combined with constexpr template variables to put together a
system that is all within the one source language and is fully checked
at compile-time. I'm not sure it would be clearer, however!