3
\$\begingroup\$

Currently I have a wrapper function for the read/write system calls, the purpose of which is to eliminate partial transfer scenarios, by retrying until either all data is transferred or the system call fails in trying to do so.

Includes:

#include <errno.h>
#include <unistd.h>

Code:

int force(ssize_t(*const func)(int, void *, size_t), const int file, void *ptr, size_t size) {
    for (ssize_t r;; ptr = (char *)ptr+r) {
        if ((r = func(file, ptr, size)) == -1) {
            if (errno == EINTR)
                continue;
            return -1;
        }
        if (size -= r)
            continue;
        return 0;
    }
}

This function would be used as such:

extern struct Conf conf;
int file = open("file", O_RDONLY);
if (force(read, file, &conf, sizeof(struct Conf)))
    handle_error();

If write is passed, ptr may point to const data because write does not modify the data pointed to. But const casts are considered improper. Should I keep this wrapper, or should I write separate and mostly duplicated, but 'proper' wrappers?

int force_read(const int file, void *ptr, size_t size) {
    for (ssize_t r;; ptr = (char *)ptr+r) {
        if ((r = read(file, ptr, size)) == -1) {
            if (errno == EINTR)
                continue;
            return -1;
        }
        if (size -= r)
            continue;
        return 0;
    }
}
int force_write(const int file, const void *ptr, size_t size) {
    for (ssize_t r;; ptr = (const char *)ptr+r) {
        if ((r = write(file, ptr, size)) == -1) {
            if (errno == EINTR)
                continue;
            return -1;
        }
        if (size -= r)
            continue;
        return 0;
    }
}
\$\endgroup\$
10
  • \$\begingroup\$ Is this even working? Your indentation and your lack of if braces { } suggest that the intention and implementation are at odds. \$\endgroup\$
    – Reinderien
    Mar 2 at 17:56
  • 1
    \$\begingroup\$ you didnt say that was the case \$\endgroup\$
    – pm100
    Mar 3 at 1:11
  • 2
    \$\begingroup\$ Much of the code is not standard C nor compiles without various includes. Add compiler specific tag and add relevant includes to forestall closure. \$\endgroup\$ Mar 3 at 3:30
  • 1
    \$\begingroup\$ In addition to read, write, EINTR you identified as non-standard, ssize_t, O_RDONLY, handle_error() are also non-standard C. Recommend that you add the Posix tag (and since the limit is 5, drop one). Further, even with a tag change, posted code does not compile. \$\endgroup\$ Mar 3 at 16:31
  • 2
    \$\begingroup\$ Please try compiling the posted code (which lacks headers) and then reconsider to post compliable code. \$\endgroup\$ Mar 4 at 16:58

2 Answers 2

3
\$\begingroup\$

I don't think either of the choices presented are a good option without seeing the code that actually needs to use them.

The read() and write() functions are low level I/O, I rarely use them and when I do it is generally to read a binary file. If I need to use these low level functions for I/O then it requires a lot more control over the input then is demonstrated by these options.

For general input I prefer fgets() or getline(), however, I will sometimes use one of the scanf() functions. For general output I use the printf() family of functions.

\$\endgroup\$
4
  • 1
    \$\begingroup\$ What if I am dealing with file descriptors as opposed to FILE * \$\endgroup\$
    – user256024
    Mar 5 at 2:47
  • \$\begingroup\$ 'man fdopen' should help. \$\endgroup\$ Mar 5 at 8:09
  • \$\begingroup\$ My programs almost invariably interact with only binary files as opposed to text files \$\endgroup\$
    – user256024
    Mar 5 at 15:57
  • 1
    \$\begingroup\$ When I find myself reaching for these low-level functions, then memory mapping is often a better alternative. \$\endgroup\$ Mar 7 at 13:35
2
\$\begingroup\$

The second choice should be used for a separate reason, an asymmetry between read and write. In the event that read is called via the above function, and EOF is reached before the requested bytes are read, read will return 0, resulting in a forever loop. write does not need to handle a 0 return, unless size is 0, which can be handled by checking size before the first iteration instead after. The read wrapper may return a different value such as 1 signaling an end of file. Local variable names should also be lowercase.

int Read(const int file, void *ptr, size_t size) {
    for (ssize_t r; size; size -= r, ptr = (char *)ptr+r) {
        if ((r = read(file, ptr, size))) {
            if (r == -1)
                return -1;
            continue;
        }
        return memset(ptr, 0, size), 1;
    }
    return 0;
}
int Write(const int file, const void *ptr, size_t size) {
    for (ssize_t r; size; size -= r, ptr = (const char *)ptr+r) {
        if ((r = write(file, ptr, size)) == -1)
            return -1;
    }
    return 0;
}
\$\endgroup\$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy