2

I want to be able to do something like this:

while (line = fgets(line, n, input)) {
    if (strlen(line) == 1)
        print_and_continue("Blank line.\n");
    printf("%s\n", line);
}

Ordinarily the way to do this and cover all syntactic intricacies is to employ a freakish do while(0) loop:

#define print_and_die(x) do {printf("%s\n", (x)); exit(1);} while (0)

However, I can't use continue (or break for that matter) in place of exit(1) cause it would apply to the do while(0) loop, not the loop I want it. And sure, I can just define the macro like:

#define print_and_continue(x) {printf("%s\n", (x)); continue;}

And expect the user not to write code like this:

if (something)
   print_and_continue(x);
else {...}

But I want to know, is there a way to cover all syntactic cases?


There has been a slight debate in comments as to whether construct this construct is useful enough that it warrants avoiding outright writing:

if (condition) {
   printf("Failure.\n");
   continue;
}

Just to illustrate the point, if there are a lot of conditions that make you want to skip processing data (or even partially process data before skipping it) then yes, it very much becomes useful.

Compare the readability of the two. The latter is rather repetitive and unreadable (and trickier to edit)

/* With print_and_continue() */
char buffer[BUFSIZ];
while (fgets(buffer, sizeof(buffer), input)) {
    if (strlen(buffer) < 10)
        print_and_continue("To small."); 
    if (strlen(buffer) > 100)
        print_and_continue("To large."); 
    if (strcmp(buffer, "Do not print this"))
        print_and_continue("Avoided printing a line.");
    fputs(buffer, output); 
}

/* Without print_and_continue() */
char buffer[BUFSIZ];
while (fgets(buffer, sizeof(buffer), input)) {
    if (strlen(buffer) < 10){
        printf("Too small.\n");
        continue; 
    }
    if (strlen(buffer) > 100) {
        printf("To large.\n");
        continue; 
    }
    if (strcmp(buffer, "Do not print this")) {
        printf("Avoided printing a line.\n");
        continue;
    }
    fputs(buffer, output); 
}

ihato
  • 73
  • 5
  • You need to learn what [`fgets`](https://en.cppreference.com/w/c/io/fgets) really puts into your string. Even for an empty line, the length will not be zero. – Some programmer dude Jul 11 '19 at 13:19
  • 2
    As for your problem, macros can often make code harder to read and understand. For such a simple case like "print and continue" have an explicit `printf` call and `continue` statement. That makes it very clear to readers what really happens. – Some programmer dude Jul 11 '19 at 13:21
  • @kabanus Why would you ever `continue` unconditionally? The only way `break` and `continue` can even can make sense **is** if they are inside an `if` that's inside a loop; and I just want to print a message along with continue, like there is usually a macro for it exit (die) and return. – ihato Jul 11 '19 at 13:25
  • @Someprogrammerdude I fixed the example (Yes, I know that fgets() puts '\n' at the end.). It's a simple construct **that's used a lot** and I want to have it in one line just like I can have it with exit (with die or assert) or return (with functions like g_return_if_fail(cond, value)) – ihato Jul 11 '19 at 13:32

1 Answers1

1

You shouldn't do this (as said in the comments). In case you still want to do it (you shouldn't), you can try it like this:

#include <stdio.h>

#define print_and_continue(x) if(printf("%s\n", (x))) continue; else do{} while(0)

int main(int argc, char* argv[]) {
    for(int i=0;i<argc;++i) {
        if(i%2==0)
            print_and_continue("Even arg");
        else
            printf("Uneven arg\n");
        printf("This is printed\n");
    }
}
tstenner
  • 10,080
  • 10
  • 57
  • 92
  • But why exactly *shouldn't* I do this? No comment has explained that so far yet. – ihato Jul 11 '19 at 13:36
  • 1
    It's about the same length as if you'd typed it out and it looks like a simple function call but does something unexpected – tstenner Jul 11 '19 at 13:44
  • @ihato: You should not do this because it obscures the code structure. C has expression statements (which do things), declarations (which give types to names), and a variety of control statements: compound statements, selection statements, iteration statements, and jump statements (which control the flow of program execution). If you make a macro substitute for **one** of these, you do not fundamentally change the language, just introduce a well understood convenience. If you make a macro which **combines** these, you introduce a new thing which may not be understood immediately by the reader. – Eric Postpischil Jul 11 '19 at 14:35