0

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);
}
A T
  • 13,008
  • 21
  • 97
  • 158
  • Hmmm - https://git.musl-libc.org/cgit/musl/tree/src/stdio/fopencookie.c - https://github.com/eblot/newlib/blob/master/newlib/libc/stdio/fopencookie.c – A T May 24 '21 at 08:57
  • If you're on a POSIX system (like Linux or macOS) you can just open the device file and write to it. For example you could open the file `/dev/lp` (or whatever the printer device file might be called on your system) and write to it. – Some programmer dude May 24 '21 at 08:58
  • I need to support Linux, macOS, and Windows (unfortunately). EDIT: Oh and I don't know how it's implemented internally, it's a printer function that I call from a library that I don't have the source code of – A T May 24 '21 at 09:02
  • What do you mean by a *function* being complaint with `fopen` etc.? Do you mean a *type* (e.g. `SomeType*`)? – Konrad Rudolph May 24 '21 at 09:32
  • Yes I do mean that. – A T May 24 '21 at 10:08

0 Answers0