Reputation: 13846
POSIX/C defines a number of nice functions to deal with file objects, like fputs
and fprintf
.
/*
`FILE*` rather than `SomeType*` wanted?
*/
void f(const char *text) {
SomeType* printer_object = \
allocate_printer_if_not_defined_statically_else_return_that();
printer_object.error_out(text); /* equiv. semantics to stderr */
// printer_object.info_out(text); /* equiv. semantics to stdout */
cleanup_printer_object(&printer_object);
}
How do I enable a random function (like f
above) to be compliant with fopen
, fclose
, &etc.?
Partial solution (thanks atk on Freenode's ##C):
#include <assert.h>
#include <errno.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#define container_of(ptr, type, member) ((type *)((char *)(1 ? (ptr) : &((type *)0)->member) - offsetof(type, member)))
struct io_ops;
typedef size_t io_ops_read_func(struct io_ops **ops, void *buf, size_t size);
typedef size_t io_ops_write_func(struct io_ops **ops, const void *buf, size_t size);
typedef void io_ops_close_func(struct io_ops **ops);
struct io_ops {
io_ops_read_func *read;
io_ops_write_func *write;
io_ops_close_func *close;
/* You also probably want seek */
};
size_t io_read(struct io_ops **ops, void *buf, size_t size)
{
if (!ops || !*ops) {
errno = EINVAL;
return 0;
}
if (!(*ops)->read) {
errno = ENOSYS;
return 0;
}
return (*ops)->read(ops, buf, size);
}
size_t io_write(struct io_ops **ops, const void *buf, size_t size)
{
if (!ops || !*ops) {
errno = EINVAL;
return 0;
}
if (!(*ops)->write) {
errno = ENOSYS;
return 0;
}
return (*ops)->write(ops, buf, size);
}
void io_close(struct io_ops **ops)
{
if (!ops || !*ops) {
errno = EINVAL;
return;
}
if (!(*ops)->close) {
errno = ENOSYS;
return;
}
(*ops)->close(ops);
}
struct buffer_io {
void *buffer;
size_t size;
size_t pos;
struct io_ops *ops;
};
size_t buffer_io_ops_read(struct io_ops **ops, void *buf, size_t size)
{
struct buffer_io *bio = container_of(ops, struct buffer_io, ops);
assert(bio->pos <= bio->size);
if (size > bio->size - bio->pos)
size = bio->size - bio->pos;
memcpy(buf, (unsigned char *)bio->buffer + bio->pos, size);
bio->pos += size;
return size;
}
size_t buffer_io_ops_write(struct io_ops **ops, const void *buf, size_t size)
{
struct buffer_io *bio = container_of(ops, struct buffer_io, ops);
assert(bio->pos <= bio->size);
if (size > bio->size - bio->pos)
size = bio->size - bio->pos;
memcpy((unsigned char *)bio->buffer + bio->pos, buf, size);
bio->pos += size;
return size;
}
void buffer_io_ops_close(struct io_ops **ops)
{
struct buffer_io *bio = container_of(ops, struct buffer_io, ops);
free(bio);
}
struct io_ops buffer_io_ops = {
.read = buffer_io_ops_read,
.write = buffer_io_ops_write,
.close = buffer_io_ops_close,
};
struct io_ops **new_buffer_io(void *buf, size_t size)
{
struct buffer_io *bio;
bio = malloc(sizeof *bio);
if (bio == NULL)
return NULL;
bio->buffer = buf;
bio->size = size;
bio->pos = 0;
bio->ops = &buffer_io_ops;
return &bio->ops;
}
int main(void)
{
char buf[1024], *test = "foo bar baz", readbuf[1024];
struct io_ops **bio = new_buffer_io(buf, sizeof buf);
io_write(bio, test, sizeof *test);
io_close(bio);
bio = new_buffer_io(buf, sizeof buf);
io_read(bio, readbuf, sizeof *test);
assert(memcmp(test, readbuf, sizeof *test) == 0);
}
Upvotes: 0
Views: 48