3

I've recently been writing some basic command-line programs (I want to keep my skills sharp over the summer), but printf and scanf have been starting to annoy me. I'm not a wonderful C programmer, and having to get into printf/scanf and their instabilities (or even worse, fgets and their ilk) isn't exactly putting me in a comforting setting (for this reason exactly, I love NSLog, with its comforting default namespace and its automatic NSString and NSObject parsing).

Much to my disappointment, though, NSLog doesn't have a counterpart function, and prints a lot of extra 'junk' (time, function name, etc., along with a newline at the end), which defeats a lot of the purpose in my using it. So I decided to sit down for a different kind of programming exercise and write functions to replace printf and scanf that would meet my needs.

And voila, I came up with my own NSInput.h file, containing two functions: NSPrint(), and NSScan(). These two functions are modeled much after printf and scanf, but also handle NSString's. I know I'm treading on sacred namespace here, but I couldn't resist (IFPrint and IFScan just sound terrible!).

Now, while I'm really happy that I have working code (for which you can find the source here), I know that it's not efficient (much to my surprise, though, NSPrint is several times more efficient than printf under LLDB in Xcode 4, but that's beside the point). I need some advice on how to make the functions better, and more efficient. NSScan, for example, converts the va_list it recieves into an NSPointerArray, and uses NSScanner's to scan through the format and input strings, so I know there's a lot of room for improvement.

Basically, what I want to know is, are there any glaring mistakes I made that could and should be fixed? Is there anything huge that I missed? Should I just be called spoiled and go back to using printf and scanf? Please tell me, I'm looking for input here (pun not intended!)...

Thanks in advance!

Itai Ferber
  • 28,308
  • 5
  • 77
  • 83

1 Answers1

3

My thoughts:

  • Don't call them NSxxxxx, NS is reserved for Cocoa and Foundation.
  • Both functions should be modified to accept a FILE* i.e. you should be modelling the interface to fprintf() and fscanf() for more flexibility.
  • Your printf function would probably be better if you used fputs()

e.g.

void NSFPrint (FILE* fp, NSString *format, ...) 
{
    // Create the variable argument list.
    va_list args;
    va_start(args, format);

    // Using NSString, parse the argument list and convert it to a C string.
    fputs([[[[NSString alloc] initWithFormat:format arguments:args] autorelease] UTF8String], fp);
     va_end(args);
}
  • Consider adding support for input and output in encodings other than UTF-8.
  • Your scanf replacement mixes C buffered IO and Unix unbuffered IO on stdin. This might be bad.
  • Your scanf replacement reads up to the end of the line even when it doesn't need to. I haven't checked carefully, but if the scan format does not consume the entire line, it looks like you are discarding input. This might be bad.
JeremyP
  • 84,577
  • 15
  • 123
  • 161
  • Thanks for the suggestions! I'll be implementing them as soon as possible. Just one question, though. What do you mean by my using both the C buffered IO and Unix unbuffered IO, or at least, how can I avoid this problem? – Itai Ferber Aug 09 '10 at 09:21
  • I've updated my code a little (http://snipt.org/XmL) to reflect your suggestions (I've renamed the functions `IFPrint` and `IFScan`, they now take file pointers as arguments, `IFPrint` now uses `fputs`, and I've replaced `read(0, NULL, 1)` with `fgetc` - is that what you meant with the buffered/unbuffered IO?). Hopefully, I'm heading in the right direction. – Itai Ferber Aug 09 '10 at 11:22
  • 2
    Yes. The `read()` function call is a Unix system call and it reads direct from the file descriptor using nothing but Unix IO calls. `fgets()` is a C library function which operates on a `FILE*`. file pointer. The file pointer maintains a buffer and fgets only reads from the physical file when this buffer is exhausted. And when it does read, it reads in a large block. That way, doing `fgetc()` is not as inefficient as you might think because most of the time it gets the character from an internal buffer. – JeremyP Aug 09 '10 at 13:24
  • Now, how about this: http://pastie.org/1081717? I realized that if I ever had to use `IFPrint` to write to a file, I'd be much more likely to work with an `NSFileHandle` than a `FILE *`, so I worked accordingly. `IFPrint` will keep the same efficient code as before (printing to `stdout`), while `IFFPrint` will print using an NSFileHandle. I split up `IFScan` in the same way, letting the user scan through an `NSFileHandle` with a given offset. Do you think this was worth the effort, or am I just wasting my time? – Itai Ferber Aug 09 '10 at 13:46