Quando scrivi una libreria, l'aggiunta di wrapper di convenienza è spesso un servizio per i tuoi utenti.
Ad esempio, supponiamo di aver scritto una libreria di compressione del flusso in C. Il mio punto di ingresso altamente versatile potrebbe essere:
Stream *compress_stream(Stream *input, StreamOptions *opts);
Tuttavia, molti utenti vorranno semplicemente una funzione come questa:
void *compress(const void *data, size_t length, size_t *out_length);
compress_stream
è più versatile: può sia consumare input sia produrre output pigramente e l'utente può fornire parametri aggiuntivi (ad esempio algoritmo, qualità, ecc.)
Tuttavia, se un utente ha semplicemente un blob di dati che desidera rendere più piccoli, la mia funzione compress_stream
sarà estremamente ingombrante di per sé. L'utente dovrà conoscere il mio oggetto Stream
:
typedef struct Stream Stream;
struct Stream
{
size_t (*read)(Stream *s, void *buffer, size_t len);
void free(Stream *s);
};
Quindi dovranno implementare compress
nei loro termini:
typedef struct
{
Stream stream;
const char *data;
size_t remaining;
} SimpleStream;
size_t SimpleStream_read(Stream *s, void *buffer, size_t len)
{
SimpleStream *ss = container_of(s, Stream, stream);
if (len > ss->remaining)
len = ss->remaining;
memcpy(buffer, ss->data, len);
ss->data += len;
ss->remaining -= len;
return len;
}
void SimpleStream_free(Stream *s)
{
free(container_of(s, Stream, stream));
}
Stream *SimpleStream_new(const void *data, size_t length)
{
SimpleStream *ss = stream_new(sizeof(SimpleStream),
SimpleStream_read,
SimpleStream_free);
if (ss == NULL)
return NULL;
ss->data = data;
ss->remaining = length;
return &ss->stream;
}
void *consume(Stream *s, size_t *out_length)
{
char *buffer;
size_t buffer_length = 0;
size_t buffer_alloc = 16;
buffer = malloc(buffer_alloc + 1);
if (buffer == NULL)
return NULL;
for (;;) {
size_t readlen;
readlen = s->read(s, buffer + buffer_length, buffer_alloc - buffer_length);
if (readlen == 0)
break;
buffer_length += readlen;
assert(buffer_length <= buffer_alloc);
if (buffer_alloc - buffer_length < 16) {
char *tmp;
buffer_alloc *= 2;
tmp = realloc(buffer, buffer_alloc + 1);
if (tmp == NULL) {
free(buffer);
return NULL;
}
buffer = tmp;
}
}
buffer[buffer_length] = 0;
*out_length = buffer_length;
return buffer;
}
void *compress(const void *data, size_t length, size_t *out_length)
{
Stream *input;
Stream *output;
void *ret;
input = SimpleStream_new(data, length);
output = compress_stream(input, NULL);
input->free(input);
ret = consume(output, out_length);
output->free(output);
return ret;
}
Questo può essere un esempio forzato, ma ho visto le librerie trascurare di includere wrapper semplici in più occasioni. Per gli utenti che implementano questi wrapper nel loro codice, dovranno imparare di più sulla tua libreria di quanto non volessero realmente.