- Feature Articles
-
CodeSOD
- Most Recent Articles
- What a More And
- Hall of Mirrors
- Magical Bytes
- Contact Us
- Plugin Acrobatics
- Recursive Search
- Objectified
- Secondary Waits
- Error'd
- Forums
-
Other Articles
- Random Article
- Other Series
- Alex's Soapbox
- Announcements
- Best of…
- Best of Email
- Best of the Sidebar
- Bring Your Own Code
- Coded Smorgasbord
- Mandatory Fun Day
- Off Topic
- Representative Line
- News Roundup
- Editor's Soapbox
- Software on the Rocks
- Souvenir Potpourri
- Sponsor Post
- Tales from the Interview
- The Daily WTF: Live
- Virtudyne
Admin
Frost (like frist but cooler)
Admin
I have seen worse #defines.
Admin
Use extra parentheses
#define GLYPH_DEFS
X(glyph0, ({'0', 0x0E, 0x11, 0x0E, 0}))
Admin
Only works if the extra parentheses are accepted in the place where the macro is invoked.
Admin
TRWTF (apart from C using a macro pre-processor of course) is languages which don't allow trailing commas in lists. Not only does this cause enormous complications for anything which has to generate a list automatically (as in this case), it also causes spurious diffs in source code control when manually edited code is diffed or merged.
Admin
This takes the cake. With a cherry on top. Also frosting, of course.
Admin
Slap
No. Bad Remy.
Admin
I write C and Assembly code for a living, and I am okay with this, but on a code review I would ask you to explain the hack in a comment instead of writing "sorry". That sorry is not going to save the next person from being confused, but a proper explanation will.
I would also consider using Python to generate the lookup table. TRWTF is not the C preprocessor. It's the people who keep trying to make it do things it was never intended to do.
Also, please stop writing "C/C++". These are two very different languages. I am very annoyed by C++ developers who think that their best practices also apply to C. They do not.
Admin
To be fair, C99 and C++11 explicitly allow trailing commas in initializer lists, and certain compilers (i.e. gcc) have supported them long before. Additionally, gcc has special handling when the token paste operator ## is placed after a comma, removing it when the following variable resolves to empty. In C++2a, there is also a new preprocessor construct that deals with trailing commas, at least when using variadic macros: VA_OPT
Admin
You can use variadic macros for a slightly less terrible hack, If you're compiling in C99 or later or C++11 or later (or a compiler that supports variadic macros as an extension):
#define UNWRAP(...) VA_ARGS #define GLYPH_DEFS X(glyph0, UNWRAP({'0', 0x0E, 0x11, 0x0E, 0}))
The parentheses for the argument to the UNWRAP macro solve the parsing problem, and the expansion of UNWRAP removes those parentheses.
Admin
Why not just use inline functions (in c++)? Or rely on compiler optimisation (in either language)?
There may be a case for macro implementation of what is essentially a list or a dictionary these days, but frankly I'd want a very convincing performance test before I let this past a code review. And I've been doing C/C++ for thirty years, so this isn't language bigotry.
Admin
Four spaces at the start of each line creates a code block!
Admin
(I'm sorry; I hit enter earlier than I intended to!)
If your compiler does support
__VA_ARGS__
, you can also do it like this:Admin
Haha! I ran into this exact problem yesterday! Squigly braces and all. Previously I've used the _ trick, but now I have VA_ARGS available :)
Admin
You cannot call functions in a static initializer, so using functions to build the table statically is not an option. If you are using functions to build the table at run-time, you cannot expect the compiler to convert that to static initialization. It's not only a difficult problem, but there are very few cases where this would be a legal optimization. This code likely runs on a microcontroller where a const array would be stored in flash and used from there directly, but a non-const array would consume RAM. Most microcontrollers have significantly more flash than RAM. A function with a large switch statement could work, but now you are validating different code for each case instead of validating a function to look up an entry in an array. If you actually care about testing and code coverage, this is a pretty big difference. (Though I would not be surprised if this approach actually had better performance than using the table. Depends on the processor and the optimizations used by the compiler of course.)
BTW, C also has inline functions.
Admin
Uncertain whether you’re talking about C or C++. If C++, then you can (albeit at the cost of dynamic initialization), and more to the point, you can even without the cost of dynamic initialization if the functions are constexpr. It’s super-convenient: make a constexpr function that generates the data you need, make a static-duration variable initialized to a call to that function, compile, and boom, the function runs at compile time and an image of the data lives in .rodata (aka Flash, if you’re doing embedded).
Admin
Never heard of constexpr functions, then?
https://en.cppreference.com/w/cpp/language/constexpr
Admin
In case the last 3 comments, which were held for moderation, say something about what C++ allows you to do: my comment was about C, not C++. I should have specified that. C requires that initializers for objects with static storage duration (like this example) contain only constant expressions or string literals. I am aware that C++ is more flexible on this, but that's just another reason why talking about these two languages like they are the same thing is a bad idea. Also, even though C++ allows the compiler to evaluate those functions at compile time (assuming that some requirements are met), there is no guarantee that compilers will do so.
Admin
For obvious reasons, the combination shall be known as "C-4".
Admin
I do this a lot - we have a large table of 'registers' in our firmware (in C) which can be set via the GUI (on PC) or serial port and many are backed to NVM. For instance, hot oven temperature is register 23, REG_HOT_TEMP. I have one file (reg_table.h) which has one line with all the parameters for each register - reg number, type, name, default value, RAM only or stored in NVM, value limits, etc... Then it gets included 3 separate times with different #defines as shown. Once to generate the enums for the names (REG_HOT_TEMP), once to make a const array in flash (so not eating RAM) for the constant parts of each register like type and limits, then once to make an array in RAM for the volatile bits like current value.
Furthermore, the C# GUI (on PC) also parses reg_table.h so it knows what the registers are when it's talking to the device.
Some people are initially horrified by the idea of including a .h file 3 (or 4) times with different invocations, but there really is no better way with .c so have so much information in several different places and keep it synchronized (the real problem). Imagine trying to keep 3 separate array/enum definitions consistent - it won't happen. This way it's self synchronizing.
Admin
The only real alternative to that is to have a step in your build rules that converts the definitive original form of the information into derived forms that you can use in each of the locations. Sometimes this is easier.
Admin
@Naomi ref
IMO a better way to do code blocks is to place 3 backticks on a line by themselves. Then the same again on the line below. Then write or paste whatever block of text you want treated as code between the two. with this technique if you're working with any code that's already got indention, you don't need to fussily inject 4 extra spaces into each line.
The example below has two lines of code bracketed by two lines of triple backtick.
And now we're back to regular text on the next line below the backticks.
Admin
Gaah!! I got the example right but screwed up the block quote. That second sentence is mine, not Naomi's. Apologies to all.
No edit plus no preview = foolishness & frustration.
Admin
Definitely stunt coding. But if you're talking about readable and maintainable code for bitmap fonts, I prefer to abuse macros in a different direction. Let's see if I can do this without Markdown turning it into a mess.
That's a bit more readable than a bunch of random hex constants. If you want to make indexing it easier, I you could add something like this and use strchr to index it.
Admin
Sorry, do you even read this site?
(I am intrigued how this will look as there seems to be no help or any indication how comment formatting works and which markup is used)
Admin
Wouldn't the line comment in the #define ruin everything?
Admin
Generating the code from a config seems indeed the preferable way, but then again the macro stunts are essentially just that. Give it proper documentation and it is fine I'd say.
Still potentially confusing, especially for code intelligence features, but so would be pretty much any solution. Though in principle the build process could place the preprocessed files, where the editor can find them, and add a header that says "generated file, don't edit directly".
Admin
No, the comments are stripped before macros are expanded. I am not sure when this was first defined, but C99 defines this in the "Translation Phases" section.
However, if this was a multi-line macro using
\
, that comment would cause issues. Line splicing happens before comments are stripped, so the rest of the multi-line macro would be part of the comment.