0

There is a C structure

struct a
{
int val1,val2;
}

I have made changes to the code like

struct b
{ 
int val2;
}
struct a
{
int val1;
struct b b_obj;
}

Now, usage of val2 in the other C files is like a_obj->val2;.
I want to replace its declaration usage and there are a lot of them, so I have defined a macro in the header file where the struct a is defined as follows:

#define a_obj->val2 (a_obj->b_obj.val2)

It's not working. Is -> illegal in the identifier part of a macro definition #define?
Could someone please tell me where am I wrong?

Edit as suggested by @Basile -
It's a legacy source code, a very huge project. Not sure of LOC.
I want to make such changes because I want to make it more modular.
For example I want to group similar fields of the structure under a same name and that's the reason I want to create another struct B with fields which are related to B feature and also common to A.
I can't use Find Replace feature of other text editors, I am using VIM.

Yunnosch
  • 26,130
  • 9
  • 42
  • 54
ugola
  • 300
  • 3
  • 18
  • 1
    The macro name must be a single token – M.M Jul 09 '18 at 04:57
  • Try this horrible macro magic: `int val2m;` instead of `int val2;` and `#define val2 b_obj.val2m`. – Yunnosch Jul 09 '18 at 05:02
  • Thanks @M.M So what should I do now to achieve the replacement of a_obj->val2 with a_obj->b_obj.val2 . Using macros it's impossible you mean? – ugola Jul 09 '18 at 05:02
  • 3
    Don't use macro magic for that. Use the editor (or some editing script with `sed` or `ed`) to replace explicitly every occurrence of `a_obj->val2` with `a_obj->b_obj.val2` or replace *interactively* (and with caution) the relevant occurrences of `->val2` with `->b_obj.val2`. BTW, if you miss some, the compiler would complain. Of course, use some [version control](https://en.wikipedia.org/wiki/Version_control) system: I recommend [git](http://git-scm.com/) – Basile Starynkevitch Jul 09 '18 at 05:07
  • @BasileStarynkevitch perfect answer (+1). The best way is to use some script to search-replace rather than macro magic which will ultimately come to bite you at wrong time. – Ketan Mukadam Jul 09 '18 at 05:21
  • @BasileStarynkevitch I would like to upvote an answer with that comments content. And refer to it as the clean way when it is possible, from my answer. – Yunnosch Jul 09 '18 at 05:21
  • Consider adding the tag [tag:c-preprocessor] – Yunnosch Jul 09 '18 at 05:39
  • You mean macro? I am trying to use that only. – ugola Jul 09 '18 at 05:41
  • Also, **edit your question** to improve it. If you are working a huge source code base, explain why you can't replace the field in some editor. Give more details: what kind of application, what is the actual `struct` and field name, what size of source code. – Basile Starynkevitch Jul 09 '18 at 05:42
  • 1
    Oh, _please_ do not do this kind of thing! Source code is as much about communication with future developers (possibly including your future self) as it is about telling the computer what to do. Nobody wants to read `a->b` and find out later that it actually means `a->b.c`, or whatever it is you're trying to do. Your code would be far more comprehensible if you simply replace all your letters and numbers with Chinese characters and all your punctuation marks with emojis. – Dawood ibn Kareem Jul 09 '18 at 06:53
  • @DawoodibnKareem I got your point, thanks! :) – ugola Jul 09 '18 at 08:27
  • Your question smells badly as some [XY problem](http://xyproblem.info/)... **What is the actual problem you want to solve?** (and what does "huge project" means to you? give actual project sizes, even approximate ones; I won't believe that your project is huge if you don't know its size) Please edit your question again to motivate it (in particular, explain why scripting cleverly `vim` is in practice not enough; in a lot of cases, it should fit, and your C or C++ compiler would also help by finding mistakes or typos) – Basile Starynkevitch Jul 09 '18 at 13:55
  • I really think the question should be downvoted, since it lacks so much motivation and context – Basile Starynkevitch Jul 10 '18 at 05:22

4 Answers4

5

This kind of macro magic will get you into trouble soon,
because it is making your source code unreadable and brittle (credits Basile for the phrasing).
But this should work for what you describe.

struct b
{ 
int val2m;
}
struct a
{
int val1;
struct b b_obj;
}

#define val2 b_obj.val2m

The trick is to give the actual identifier inside the struct declaration a new name (val2m), so that the name all the other code uses can be turned into a magic alias,
which then can contain the modified access to take a detour via the additionally introduced inner struct.

This is only a kind of band-aid for the problematic situation of having to change something backstage in existing code with many references. Only use it if there is no chance of refactoring the code cleanly. ("band-aid", appropriate image by StoryTeller, credits).

I explicitly recommend looking at Basiles answer, for a cleaner more "future-proof" way. It is the way to go to avoid the trouble I predict with using this macro magic. Use it if you are not forced by very good reasons.

Yunnosch
  • 26,130
  • 9
  • 42
  • 54
  • It Worked, but technically speaking in what ways is it hazardous? – ugola Jul 09 '18 at 05:09
  • @BasileStarynkevitch Good way of phrasing it. Let me know if you mind me using that phrase in my answer. – Yunnosch Jul 09 '18 at 05:10
  • No, of course you can improve the phrasing of your answer using words from my comments. That is why comments are useful for – Basile Starynkevitch Jul 09 '18 at 05:11
  • 1
    It's worth mentioning that this is only a band-aid to the OP's problem. – StoryTeller - Unslander Monica Jul 09 '18 at 05:11
  • 2
    BTW, there is no need to use another name `val2m`. In principle `#define val2 b_obj.val2` would work (since recursive macros are not expanded indefinitely) but that is still very ugly – Basile Starynkevitch Jul 09 '18 at 05:15
  • @BasileStarynkevitch I actually did not know that. On the other hand, for readability purposes (the little that remains), I will forget it as soon as possible. ;-) Thanks for mentioning however. – Yunnosch Jul 09 '18 at 05:16
  • @BasileStarynkevitch I think it will make havoc if the define is ever seen before the type declaration... I shudder and stay with the `val2m`. Still valuable input by you. – Yunnosch Jul 09 '18 at 05:18
4

As other explained, the preprocessor works only on tokens, and you can only #define a name. Read the documentation of cpp and the C11 standard n1570.

What you want to do is very ugly (and there are few occasions where it is worthwhile). It makes your code messy, unreadable, and brittle.

Learn to use better your source code editor (you probably have some interactive replace, or interactive replace with regexp-s; if you don't, switch to a better editor like GNU emacs or vim - and study the documentation of your editor). You could also use scripting tools like ed, sed, grep, awk etc... to help you in doing those replacements.

In a small project, replacing relevant occurrences of ->val2 (or .val2) with ->b_obj.val2 (or .b_obj.val2) is really easy, even if you have a hundred of them. And that keeps your code readable. Don't forget to use some version control system (to keep both old and new versions of your code).

In a large project of at least a million of lines of source code, you might ask how to find every occurrence of field usage of val2 for a given type (but you should probably name val2 well enough to have most occurrences of it be relevant; in other words, take care of the naming of your fields). That is a very different question (e.g. you could write some GCC plugin to find such occurrences and help you in replacing the relevant ones).

If you are refactoring an old and large legacy code, you need to be sure to keep it readable, and you don't want fancy macro tricks. For example, you might add some static inline function to access that field. And it could be then worthwhile to use some better tools (e.g. a compiler plugin, some kind of C parser, etc...) to help you in that refactoring.

Keep the source code readable by human developers. Otherwise, you are shooting yourself in the foot. What you want to do is unreasonable, it decreases the readability of the code base.

I can't use Find Replace feature of other text editors, I am using VIM.

vim is scriptable (e.g. in lua) and accepts plugins (so if interactive replace is not enough, consider writing some vim plugin or script to help you), and has powerful find-replace-regexp facilities. You might also use some combination of scripts to help you. In many cases they are enough. If they are not, you should explain why.

Also, you could temporarily replace the val2 field of struct a with a unique name like val2_3TYRxW1PuK7 (or whatever is appropriate, making some unique "random-looking" name is easy). Then you run your full build (e.g. after some make clean). The compiler would emit error messages for every place where you need to replace val2 used as a field of struct a (but won't mind for any other occurrence of the val2 name used for some other purpose). That could help you a lot -once you have corrected your code to get rid of all errors- (especially when combined with some editor scripting) because then you just need to replace val2_3TYRxW1PuK7 with b_obj.val2 everywhere.

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
  • It's a very large project and I making these kind of changes to make the legacy code more modular and understandable by grouping fields based on some properties which reside under a struct with an understandable name. Since you said writing a GCC plugin to replace the occurences would be the solution, I haven't really written one but I can try if that's the correct way. – ugola Jul 09 '18 at 05:28
  • How large is that project? How many millions of source code lines? Such details should go into your question, because they matter! – Basile Starynkevitch Jul 09 '18 at 05:29
  • 1
    I do not know about a gcc plugin, but using some auto-text-editor, e.g. written in awk, perl, sed is a good alternative way to follow this good recommendation. I also recommend the other part of the similar comment, using a version control system. – Yunnosch Jul 09 '18 at 05:33
  • I am using VIM. – ugola Jul 09 '18 at 05:34
  • but I also want the changes to be reflected upstream. So running the command on my system to change the occurrences wouldn't do this :S – ugola Jul 09 '18 at 05:36
  • So update properly the source code, and commit it, and propose the patch upstream. That is a social issue (how to get your improvement accepted upstream for a free software project) not a technical one. And surely, the community of a free software project would reject a patch (since it is unreadable) doing macro tricks like you dream of (or at least you'll need to discuss it) – Basile Starynkevitch Jul 09 '18 at 05:37
2

Is -> illegal in #define?

Yes.

#define identifier can only be letter, number or underscore.

1

Macros definitions must be regular identifiers, so you can't use any special character like - or >. I've thinked that may be you can use an union, like this:

struct b
{
    int val2;
}
struct a
{
    int val1;
    union {
        struct b b_obj;
        int val2;
    }
} 

so you can still using a_obj->val2.

Eduardo Pascual Aseff
  • 1,149
  • 2
  • 13
  • 26
  • 2
    You should add that this will only work if first member of `struct b` remains same as val2 in size. Also this works only for one member element. It is a hackish way and not very maintainable code. – Ketan Mukadam Jul 09 '18 at 05:18
  • Thanks :) but if there are many fields in my B struct, wouldn't this approach make my code unnecessarily long? – ugola Jul 09 '18 at 05:24
  • For multi entry struct even more than for same size structs read https://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule – Yunnosch Jul 09 '18 at 05:28