EmbeddedRelated.com
Forums

Using C preproc to uniquely identify structure members

Started by John Speth April 10, 2021
On 4/12/2021 6:51 PM, George Neuner wrote:
> On Mon, 12 Apr 2021 11:13:42 -0700, Don Y > <blockedofcourse@foo.invalid> wrote: > >> On 4/12/2021 10:06 AM, George Neuner wrote: >>> >> Yes. But, what you're ULTIMATELY doing with the results (creating a >> message) is uniform across all members. You're not doing one thing >> with magazineIDs and something different with printDates. > > Detecting changes AND doing something with them (the next part below) > can all be table driven.
The *hook* to invoke an "X_action" can be part of the table. But, the developer is still faced with writing that code; likely more involved than checking for changes. Note, also, that using a function leaves you with scoping decisions for variables; it's unlikely X_changed() will want to access the same stuff as Y_changed().
>> My point was the OP seems to be wanting to automate the "easy >> stuff"; the hard stuff is likely "member-dependent". I don't >> see how he's going to add that in a way that makes it resilient >> to developer errors (if he can't expect developers to get >> comparison operations correct!) >> >> So, consider how to focus on the *actions* that are associated >> with each "detected change" instead of just automating the >> detection of the change. > > Include in each table entry a (pointer to a) function to be called > with the value(s) if/when a change is detected.
See above.
>> But, this comes at the expense of developing a tool to do that work >> for you. > > Back to the maintenance issue. > > However, the OP himself raised the possibility of custom preprocessing > tools. If you really need to work in C, a tool that reads struct > declarations and generates code for walking them probably IS the best > generic solution.
Subject to all these caveats... :>
> However, if the input is limited to legal C syntax, associating > arbitrary struct members with arbitrary functions will be ... let's > call it "really, really hard" and leave it there. > >> I have no idea how you could do this in the C preprocessor. >> M4 may be able to lend a hand. But, in general, most (old) >> assemblers had more flexible "macro languages" where you could >> actually parse arguments, etc. > > The complexity will depend on whether structs/unions recursively can > contain other structs/unions. The more general, the more complex. > > Personally, I wouldn't want to try doing it with M4 - it would be > easier to use a real parser tool and grab/modify the struct handling > code from an already written C parser. There are a number of them > available.
M4 is... "disappointing" :>
>> And, you have to also consider how robust the tool/technique will >> be. What if the input isn't what you'd expected? Will you throw >> an (compile) error? Or, silently generate gobbledygook? > > The input should be legal C structs, so if a custom preprocessor > fails, so should the C compiler. Not a problem if fails silently.
I think you'd want to "expose" the struct early so the compiler can throw errors before the "tool" has to try to make sense of them. That way, the tool *knows* the input is, at least, syntactically correct.
> Similarly, if a preprocessor produces illegal C code, the C compiler > should catch that later. > > The worrisome case is that the preprocessor produces legal code that > is incorrect given the input. Only inspection will catch this. > >> In my world, the C compiler doesn't provide any effective type >> checking/enforcement. E.g., a foo_t and a bar_t are both the >> same underlying basic (C) type. So, no way of ensuring >> that you're actually invoking a member defined for a particular >> object type *on* an instance of that type! (i.e., you'd have >> to rely on run-time error detection instead of catching it >> at compile-time; rolling a tool gives you that added benefit) >> >> Again, I repeat the observation that you are now in the tools >> business once you head down this path. And, perpetually >> obligated to ensure your tools track the needs of the project >> AT ANY POINT IN ITS DEVELOPMENT *history*. You're now >> developing, testing and maintaining a tool instead of just >> "your code". > > Yup. Tools to make tools.
I'm finding it hard not to "evolve" the tools -- largely because I'm discovering new uses for them as I use them. But, the idea of being able to support earlier versions is more trouble than its worth; anything older than X is simply "unsupported". [As I have very few people using the tools, I can likely get away with this -- as long as I don't piss anyone off by making "radical" changes.]
On 12/04/2021 20:13, Don Y wrote:

> And, you have to also consider how robust the tool/technique will > be.&nbsp; What if the input isn't what you'd expected?&nbsp; Will you throw > an (compile) error?&nbsp; Or, silently generate gobbledygook? > > In my world, the C compiler doesn't provide any effective type > checking/enforcement.&nbsp; E.g., a foo_t and a bar_t are both the > same underlying basic (C) type.&nbsp; So, no way of ensuring > that you're actually invoking a member defined for a particular > object type *on* an instance of that type!&nbsp; (i.e., you'd have > to rely on run-time error detection instead of catching it > at compile-time; rolling a tool gives you that added benefit) >
That is an odd thing to say. C /does/ provide type checking and enforcement. If you choose to use it in particular ways, it can have very strong typing - but that comes at a cost in writing convenient and clear code. In particular, structs and unions introduce new types and you have to explicitly write messy pointer casts in order to break the type safety. The challenge with C is that it makes it quite easy to break type safety, and people often write code that does this. The way you ensure that you are dealing with a member of a particular struct type on an appropriate object of that type, is to avoid point casts except under very controlled and very necessary circumstances.