Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improvement proposal for json_object_object_foreach #475

Closed
fhaverkamp opened this issue Feb 5, 2019 · 5 comments
Closed

improvement proposal for json_object_object_foreach #475

fhaverkamp opened this issue Feb 5, 2019 · 5 comments

Comments

@fhaverkamp
Copy link

I tried to use the macro two times within one function and got warnings/errors due to double definitions of key and value. Looking at the current version:

# define json_object_object_foreach(obj,key,val) \
	char *key = NULL; \
	struct json_object *val __attribute__((__unused__)) = NULL; \
	for(struct lh_entry *entry ## key = json_object_get_object(obj)->head, *entry_next ## key = NULL; \
		({ if(entry ## key) { \
			key = (char*)lh_entry_k(entry ## key); \
			val = (struct json_object*)lh_entry_v(entry ## key); \
			entry_next ## key = entry ## key->next; \
		} ; entry ## key; }); \
		entry ## key = entry_next ## key )

it is obvious why this is. I think it would be good idea to wrap the block into a do { } while (0), which I think makes my problem go away:

# define json_object_object_foreach(obj,key,val) \
    do { \
	char *key = NULL; \
	struct json_object *val __attribute__((__unused__)) = NULL; \
	for(struct lh_entry *entry ## key = json_object_get_object(obj)->head, *entry_next ## key = NULL; \
		({ if(entry ## key) { \
			key = (char*)lh_entry_k(entry ## key); \
			val = (struct json_object*)lh_entry_v(entry ## key); \
			entry_next ## key = entry ## key->next; \
		} ; entry ## key; }); \
		entry ## key = entry_next ## key )
    } while (0)

What do you think?

@ploxiln
Copy link
Contributor

ploxiln commented Feb 5, 2019

That does not work the same, because the macro intends you to provide the body of the for loop right after it, like:

json_object_object_foreach(fruitstand, fruit, q) {
    printf("%s: %d\n", fruit, json_object_get_int(q));
}

The body appearing after a while (0) won't work.

I think this macro should work when nested, if you choose different names for key and val. Can you provide a code sample of your nested object-foreach loops and the compiler warnings/errors you get?

@fhaverkamp
Copy link
Author

fhaverkamp commented Feb 6, 2019

The loops are not even nested, they are right behind each other. I use the first one to see how many entries in a table I need. After I know that I allocate some memory and use the 2nd loop to fill my new table up. If I would use nested loops, I think the renaming makes totally sense.

The error messages look like this:

cmdserver.c:194:39: error: redefinition of 'key'
        json_object_object_foreach(json_obj, key, val) {
                                             ^
cmdserver.c:172:39: note: previous definition is here
        json_object_object_foreach(json_obj, key, val) {
                                             ^
cmdserver.c:194:44: error: redefinition of 'val'
        json_object_object_foreach(json_obj, key, val) {
                                                  ^
cmdserver.c:172:44: note: previous definition is here
        json_object_object_foreach(json_obj, key, val) {

You are right, I guess the do { } while (0) does not work in this case. I had added it around the foreach blocks such that it created its own block. That way the scope of the variable definitions was separated and the warnings disappeared. I agree that renaming helps to overcome the problem, but I thought that it is much better read and understandable (also copy-paste works better ;-), if multiple of those loops use the same variable names.

Now I tried this:

#define __json_object_object_foreach(obj,key,val)                      \
	do {                                                           \
		json_object_object_foreach(obj,key,val)

#define __json_object_object_foreach_end()                             \
	} while (0)

	/* Count required environment elements */
	nr = 0;
	__json_object_object_foreach(json_obj, key, val) {
		val_type = json_object_get_type(val);

		switch (val_type) {
		case json_type_double:
		case json_type_int:
		case json_type_string:
			nr++;
			break;
		default:
			break; /* Nothing */
		}
	}
	__json_object_object_foreach_end();

That works, but I wonder if I really improved it ;-). Maybe you have a nicer idea?

@ploxiln
Copy link
Contributor

ploxiln commented Feb 6, 2019

Ah, I see, same problem when not nested.

You can easily use different key and val names, they're macro args for this reason:

foo();

json_object_object_foreach(jobj, key1, val1) {
    bar(key1, bval1);
}
json_object_object_foreach(jobj, key2, val2) {
    bar(key2, val2);
}

If you really want to use the same key and val names, you can create a block scope without any control flow statements, like:

foo();

{
    json_object_object_foreach(json_obj, key, val) {
        bar(key, val);
    }
}

{
    json_object_object_foreach(json_obj, key, val) {
        bar(key, val);
    }
}

Subjectively, I think both these options are better than separate "start" and "end" macros.

@fhaverkamp
Copy link
Author

Thanks for the comments. I guess I get along so far. There is another thing I just found when carrying the code over to a Linux box with recent gcc. In my count loop I do not use the key variable.
The compiler complains here about defined and set but not used variable key. So the attribute((unused)) which you already have for the val, might also make sense for the key. I think I will use your macro as example and throw out all I do not need to get my objects counted instead of using the foreach directly.

@dota17
Copy link
Member

dota17 commented Feb 7, 2020

Problem has been solved and this issue could be closed @hawicz

@hawicz hawicz closed this as completed Feb 8, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants