Pointers to functions and complications using them

Pointers to functions and complications using them

Mate de Vita
Kelli

2008 Oct 4 • 2453
159 ₧
New exercise from K&R, a program that sorts input lines in lexicographical order. It also allows a couple of flags that affect the comparing and the sorting part of the program.
It's an exercise that supposedly shows the usefulness of function pointers.

C code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAXLINES 100
#define MAXLEN 1000

int main(argc, argv)
int argc;
char *argv[];
{
char *lineptr[MAXLINES];
int nlines;
int strcomp(), numcomp(), dictcomp(), foldcomp(), dfcomp(), swap(), linesort(), revsort();
int (*cmpfunc)() = strcomp, (*sortfunc)() = linesort;
char *flag;
int fold = 0, dict = 0;

while (--argc > 0 && (*++argv)[0] == '-')
for (flag = argv[0] + 1; *flag != '\0'; flag++)
switch (*flag){
case 'n':
cmpfunc = numcomp;
break;

case 'r':
sortfunc = revsort;
break;

case 'f':
fold = 1;
break;

case 'd':
dict = 1;
break;

default:
printf ("Illegal option: -%c.\n", *flag);
break;
}
if (dict || fold){
if (dict && fold) cmpfunc = dfcomp;
else if (dict) cmpfunc = dictcomp;
else cmpfunc = foldcomp;
}

if ((nlines = readlines (lineptr, MAXLINES)) >= 0){
(*sortfunc) (lineptr, nlines, cmpfunc, swap);
writelines (lineptr, nlines);
}

else if (nlines == -1) printf ("Input too big to sort: too many lines.\n");
else if (nlines == -2) printf ("Input too big to sort: not enough room in allocbuf.\n");
else printf ("Input too big to sort - line too long.\n");

return 0;
}


That's the main part of the program (the whole thing is quite a bit longer due to the number of functions required).



The flags are as follows:

-n : Program sorts lines in numerical order from the lowest number to the highest. This flag was added in the book already, the rest were incorporated by me as a part of the exercise.
-r : Program sorts lines in reverse. This flag has to work in conjunction with any of the other flags.
-f : Program folds upper and lower case together.
-d : Program sorts lines in dictionary order (makes comparisons only on letters, numbers and blanks). It has to work with the -f flag.



The functions are:


strcomp() compares two lines and returns 0 if they are the same, a positive int if the first one is lexicographically larger (ie. if it comes after the second one in the alphabet), and a negative int if the first one is lexicographically smaller.
This function's pointer is passed as an argument to sortfunc if no flags that would change the comparison process are in effect.

numcomp() compares two lines numerically and returns 0 if the two numbers are the same, 1 if the first number is larger, and -1 if the second number is larger.
This function's pointer is passed as an argument to sortfunc if -n flag is in effect.

dictcomp() compares two lines lexicographically, but based only on numbers, letters and blanks, and returns the same as strcomp().
This function's pointer is passed as an argument to sortfunc if -d flag is in effect.

foldcomp() compares two lines lexicographically, but it doesn't make distinctions between upper and lower case letters, so that a and A appear adjacent, returning the same as strcomp().
This function's pointer is passed as an argument to sortfunc if -f flag is in effect.

dfcomp() combines dictcomp() and foldcomp(), returning the same as strcomp().
This function's pointer is passed as an argument to sortfunc if -df (or -d -f) flags are in effect.



linesort() sorts lines by comparing two lines, using the comparison function, the pointer to which was passed to it as an argument, and sorting them with shellsort from lowest to highest.
This function is used as sortfunc if no flags that would change the sorting process are in effect.

revsort() sorts lines in the same way as linesort(), except that it sorts them from highest to lowest.
This function is used as sortfunc if -r flag is in effect.



readlines() records the lines into allocbuf, using alloc() (also a function from K&R) and returns the number of lines (or a negative int if there is an error).

writelines() outputs the lines, after they have been sorted with sortfunc.



Now the program as I made it, works as it should, but I think it's way too complicated. It has way too many functions, and the fact that I had to make a whole new function just to incorporate support for a combination of two already existing flags (-d and -f), bothers me a bit.

I'd rather just use a few external variables (dict, num, rev, fold), initialize them to 0, and set them to 1 if their respective flags were used. Then I'd just use one comparison function and one sorting function, and change the proper parts of them to behave appropriately if any of the above external variables were non-zero.
Or is there some reason why using so many functions and passing them via pointers is a good idea?
...and that's the bottom line because Mate de Vita said so.
 
 
2011 Jun 5 at 04:12 PDT — Ed. 2011 Jun 5 at 04:14 PDT
Down Rodeo
Cap'n Moth of the Firehouse

Find the Hole II Participation Medal
2007 Oct 19 • 5486
57,583 ₧
And that is why C can quite often scare me. Re: sorting, sort once then reverse if needed? I'd need to sit and have a look but the style of C you're using is slightly different to what I've seen so some things don't make sense to me so much :p
 
 
2011 Jun 5 at 07:33 PDT
Mate de Vita
Kelli

2008 Oct 4 • 2453
159 ₧
Down Rodeo said:
Re: sorting, sort once then reverse if needed?

That's how I first did it, but you need two functions for that as well, and the lines have to go through both if -r flag is used.

Anyway, the next exercise reads:

Exercise 5-14. Add a field-handling capability, so sorting may be done on fields within lines, each field according to an independent set of options. (The index for this book was sorted with -df for the index category and -n for the page numbers.)

I'm guessing this means that
code
./LineSort.out -df 4 -n 2 -r
would sort the entire file with the option -df, then sort the words (ie. sets of characters separated by spaces) in the 4th field of each line numerically and the words in the 2nd field of each line in reverse lexicographical order.
How would I define a field though?

In the book's index a line looks like this:
code
array, storage order of 104, 210

The first field is 'array, storage of', the second field is '104, 210'. So I guess a field is defined as a set of characters that's separated from the rest by more than a single space. Or maybe that's a tab in between.
...and that's the bottom line because Mate de Vita said so.
 
 
2011 Jun 6 at 08:04 PDT — Ed. 2011 Jun 6 at 08:04 PDT
SuperJer
Websiteman

2005 Mar 20 • 6299
You don't need all those functions, they are just trying to make a point.

You also don't even need your own sorting function at all in [modern] C since qsort is standard. But it's still a good learning exercise.

-----------

It seems to me there are multiple ways to interpret the sort-by-fields exercise. Usually, sorting by fields means sorting the lines using certain field(s) as keys, not sorting the fields themselves within the lines.

That would be an oddly specialized sorting program.

Also it seems much more reasonable to assume the fields in the lines are delimited by tabs or something similar, and not multiple spaces. Spaces would make your solution more complex without really teaching anything more.
 
 
2011 Jun 7 at 23:28 PDT — Ed. 2011 Jun 7 at 23:34 PDT
SuperJer
Websiteman

2005 Mar 20 • 6299
On second thought there is one important advantage to all these functions.

Efficiency.

You can put checks in the comp funcs to handle things differently, but if the func only does one thing, then you never have to execute any instructions to make decisions like that.

And for sorting in particular that could mean a pretty big win.
 
 
2011 Jun 7 at 23:51 PDT — Ed. 2011 Jun 8 at 01:16 PDT
SuperJer
Websiteman

2005 Mar 20 • 6299
FWIW here's how I would write the sorting program you described in the OP with just one compare function.

I think it works, too! It's intended to be simple, not that fast.

Also I just fixed the forams bug where [] tags inside the [code] tags were getting interpreted as special.

C code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define MAXLINES 100
#define MAXLEN 1000

static int numsort, reverse, folding, dicsort;


int compar(const void *_a, const void *_b)
{
const char *a=*(char * const *)_a;
const char *b=*(char * const *)_b;
int diff = 0;

if( numsort )
diff = atoi(a) - atoi(b);
else if( dicsort )
while( !diff && (*a || *b) ) {
while( *a && !isalnum(*a) && !isspace(*a) ) a++;
while( *b && !isalnum(*b) && !isspace(*b) ) b++;
diff = folding ? tolower(*a) - tolower(*b) : *a - *b;
if( a ) a++;
if( b ) b++;
}
else
diff = folding ? strcasecmp(a,b) : strcmp(a,b);

return reverse ? diff*-1 : diff;
}


int main(int argc, char *argv[])
{
char buf[MAXLEN+1] = {'\0'};
char *lines[MAXLINES];
int count;
int i;
char *p;

// read cmd line arguments
for( i=1; i<argc && argv[i][0]=='-'; i++ )
for( p=argv[i]+1; *p; p++ )
switch( *p ) {
case 'n': numsort = 1; break;
case 'r': reverse = 1; break;
case 'f': folding = 1; break;
case 'd': dicsort = 1; break;
default: goto bad_arg;
}

// read all input lines
for( count=0; fgets(buf, MAXLEN+1, stdin); count++ ) {
if( count>=MAXLINES ) goto too_many_lines;
if( buf[MAXLEN-2] ) goto too_long;
lines[count] = malloc(strlen(buf)+1);
strcpy(lines[count], buf);
}

// sort, print the results, deallocate
qsort(lines, count, sizeof *lines, compar);
for( i=0; i<count; i++ ) {
printf("%s", lines[i]);
free(lines[i]);
}

exit(EXIT_SUCCESS);

bad_arg: fprintf(stderr, "Bad arg: -%c\n", *p); exit(EXIT_FAILURE);
too_long: fprintf(stderr, "Line too long\n" ); exit(EXIT_FAILURE);
too_many_lines: fprintf(stderr, "Too many lines\n" ); exit(EXIT_FAILURE);
}
 
 
2011 Jun 8 at 01:06 PDT — Ed. 2011 Jun 8 at 01:53 PDT
Mate de Vita
Kelli

2008 Oct 4 • 2453
159 ₧
superjer said:
It seems to me there are multiple ways to interpret the sort-by-fields exercise. Usually, sorting by fields means sorting the lines using certain field(s) as keys, not sorting the fields themselves within the lines.

So the input would be for example
code
./LineSort.out 1 -df 3 -n
and the program would then sort the lines with the -df options by their first fields, and when two (or more) of them were the same, it would sort them by their numerically by their third fields?

Btw can you recursively call main?
...and that's the bottom line because Mate de Vita said so.
 
 
2011 Jun 9 at 10:43 PDT — Ed. 2011 Jun 9 at 10:43 PDT
Down Rodeo
Cap'n Moth of the Firehouse

Find the Hole II Participation Medal
2007 Oct 19 • 5486
57,583 ₧
Mate de Vita said:
Btw can you recursively call main?

Jesus Christ you are one of those tricky C fuckers, trying that stuff. Maybe. C++ says no, but I dunno, why not try? C is good for the insane stuff.
 
 
2011 Jun 9 at 12:48 PDT
SuperJer
Websiteman

2005 Mar 20 • 6299
Mate de Vita said:
superjer said:
It seems to me there are multiple ways to interpret the sort-by-fields exercise. Usually, sorting by fields means sorting the lines using certain field(s) as keys, not sorting the fields themselves within the lines.

So the input would be for example
code
./LineSort.out 1 -df 3 -n
and the program would then sort the lines with the -df options by their first fields, and when two (or more) of them were the same, it would sort them by their numerically by their third fields?

Yes. And I strongly recommend limiting it to 2 fields (or so). Allowing unlimited sort fields would be extremely tricky and frustrating.

Mate de Vita said:
Btw can you recursively call main?

You caaaaaaaaan... main is just a regular old function. But it really just feels wrong to call it recursively. It seems like that just can't be a good design.

C is a very simple and generic language, and allows you to do just about anything you want. But please be responsible. If there is a simple, straight-forward solution, then use it. Don't be clever.*

(*In real code. If you're just goofing around then go ahead!)
 
 
2011 Jun 10 at 01:19 PDT — Ed. 2011 Jun 10 at 01:23 PDT
Mate de Vita
Kelli

2008 Oct 4 • 2453
159 ₧
What are exit(EXIT_SUCCESS) and exit(EXIT_FAILURE)?
Are they equivalent to return 0 and return something else?

Also what's the difference between printf and fprintf?
...and that's the bottom line because Mate de Vita said so.
 
 
2011 Jun 10 at 10:11 PDT — Ed. 2011 Jun 10 at 10:13 PDT
buq25

2008 Jul 5 • 583
295 ₧
Mate de Vita said:
Also what's the difference between printf and fprintf?

...
Actually I have no idea.
Today's post brought to you by the letter: "heck".
 
 
2011 Jun 10 at 11:09 PDT
SuperJer
Websiteman

2005 Mar 20 • 6299
EXIT_SUCCESS and EXIT_FAILURE are guaranteed to be exit codes that indicate success and failure, respectively, on any platform.

As far as I know every platform uses 0 for success and anything else for failure, but I'd rather leave the decision up to the platform.

I like things that work 100% of the time.

printf always writes to stdout. fprintf lets you write to any FILE* you want. In this case, stderr.

You should always write error messages to stderr. For a whole lot of reasons.
 
 
2011 Jun 11 at 19:05 PDT — Ed. 2011 Jun 11 at 19:06 PDT
Mate de Vita
Kelli

2008 Oct 4 • 2453
159 ₧
superjer said:
Mate de Vita said:
superjer said:
It seems to me there are multiple ways to interpret the sort-by-fields exercise. Usually, sorting by fields means sorting the lines using certain field(s) as keys, not sorting the fields themselves within the lines.

So the input would be for example
code
./LineSort.out 1 -df 3 -n
and the program would then sort the lines with the -df options by their first fields, and when two (or more) of them were the same, it would sort them by their numerically by their third fields?

Yes. And I strongly recommend limiting it to 2 fields (or so). Allowing unlimited sort fields would be extremely tricky and frustrating.

Still, the index category mentioned in the exercise lists lines with more than one page number with page numbers in numerical order like this:
code
expression, assignment 15, 19, 47, 191
but there aren't any two lines with the exact same phrase.
So another possible interpretation would be that I'm supposed to fold lines with the same phrase together? I.e. the above line being a result of sorting with
code
./LineSort.out 1 -df 2 -n
the following lines:
code
expression, assignment 15
expression, assignment 19
expression, assignment 47
expression, assignment 191
...and that's the bottom line because Mate de Vita said so.
 
 
2011 Jul 1 at 09:26 PDT — Ed. 2011 Jul 1 at 10:47 PDT
SuperJer
Websiteman

2005 Mar 20 • 6299
Well that would certainly be useful, and it would be a good exercise. But strictly speaking, folding isn't sorting, so it would be strange to infer that a sorting program is supposed to fold, too.
 
 
2011 Jul 1 at 19:19 PDT
Down Rodeo
Cap'n Moth of the Firehouse

Find the Hole II Participation Medal
2007 Oct 19 • 5486
57,583 ₧
 
 
2011 Jul 3 at 12:12 PDT
SuperJer
Websiteman

2005 Mar 20 • 6299
Haskell looks like magic.
 
 
2011 Jul 3 at 21:58 PDT
Rockbomb
Dog fucker (but in a good way now)

2009 Nov 13 • 2045
superjer said:
Haskell looks like magic.

 
 
2011 Jul 3 at 22:33 PDT
sprinkles

Chrome Whore
2009 Sep 6 • 2547
10 ₧
This phone scrolls so well I read nothing above
 
 
2011 Jul 7 at 12:13 PDT — Ed. 2011 Jul 7 at 12:14 PDT
Page [1]