A T
A T

Reputation: 13846

Create a `FILE` from an internal function (not a file)?

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

Answers (0)

Related Questions