Can char * or char ** masquerade as FILE *?

In C, I often want to process data read from a file and data read from an array of strings the same way. Typically, reading from a file is done for production, and from lines for testing. I finish writing a lot of code as follows:

void handle_line(char *line, Things *things) { ... } Things *read_from_chars(char *lines[]) { Things *things = Things_new(); for (int i = 0; lines[i] != NULL; i++) { handle_line(lines[i], things); } return things; } Things *read_from_input(FILE *input) { char *line = NULL; size_t linelen = 0; Things *things = Things_new(); while (getline(&line, &linelen, input) > 0) { handle_line(line, things); } return things; } 

This is a duplication of effort.

Is there a way to make an array of strings masquerade as a FILE * pointer? Or vice versa? Or is there a better model to solve this problem?

For bonus points: the solution is to make char * or char ** suitable for use with standard file functions such as fgets and getline .

+6
source share
7 answers

There is a non-standard function fmemopen , which allows you to open char [] for reading or writing. It is available in most versions of GNU libc, I think, and in most versions of Linux.

(This allows you to read or write only one line, not the array of lines you requested.)

+5
source

You can use a discriminated union containing FILE* and a pointer to an array, and then write a get_next function that does something with it.

 typedef struct { enum { is_file, is_array } type; union { FILE *file; struct { int index; int size; char **lines; } array; } data; } file_or_array; char *get_next(file_or_array foa) { if (foa.type == is_file) { char *line = NULL; size_t linelen = 0; getline(&line, &linelen, foa.data.file); return line; } else { if (foa.data.array.index < foa.data.array.size) { return strdup(foa.data.array.lines[foa.data.array.index++]); } else { return NULL; } } } 

To do this, you need to call strdup() . Since getline() returns the newly allocated row that the caller must free, it also does the same when returning a row from an array. Then the caller can safely release him in both cases.

+6
source

One of the most powerful ways to handle this is through streams. I use them to hide files / lines / serial ports, etc.

I have deployed my own thread library, which I mainly use for embedded systems

general idea: -

 typedef struct stream_s stream_t; struct stream_s { BOOL (*write_n)(stream_t* stream, char* s, WORD n); BOOL (*write_byte)(stream_t* stream, BYTE b); BOOL (*can_write)(stream_t* stream); BOOL (*can_read)(stream_t* stream); BYTE (*read_byte)(stream_t* stream); void* context; }; 

then you will create a whole bunch of functions

 BOOL stream_create(stream_t* stream); BOOL stream_write_n(stream_t* stream, char* s, WORD n); BOOL stream_can_read(stream_t* stream); BYTE stream_read_byte(stream_t* stream); 

etc.

which use these base function callbacks.

the context in the stream structure that you use to indicate the structure for a sequential, string, file, or whatever you want. Then you have things like file_create_stream(stream_t* stream, char* filename) that will populate stream callbacks with file-related functions. Then for the strings you have something similar, but it processes the strings

+2
source

Is there a better sample to solve this problem?

My proposed solution is to overload functions.

Indicate all possible parameters:

 Things* readThings(FILE *f, char *l[]) { char *line = NULL; size_t linelen = 0; Things *things = Things_new(); if (f) { while(getline(&line, &linelen, input) > 0) handle_line(line, things); } else { for(int i = 0; lines[i] != NULL; i++) handle_line(lines[i], things); } return things; } Things* readThingsChar(char *l[]){ return readThings(0, l); } Things* readThingsFile(FILE *f){ return readThings(f, 0); } 

How to use

 FILE *f; char *l[100]; .. Things *a = readThings(f,0); // or readThingsFile(f) Things *b = readThings(0,l); // or readThingsChar(l) 

You can insert it into the data:

 Things* readThings(char *l[]) { char *line = NULL; size_t linelen = 0; Things *things = Things_new(); FILE *f = NULL; if (l[0][0]==UNIQUE_IDENTIFIER) { f = fopen(l[0]+1); while(getline(&line, &linelen, input) > 0) handle_line(line, things); fclose(f); } else { for(int i = 0; lines[i] != NULL; i++) handle_line(lines[i], things); } return things; } 

How to use

 char *f[1] = { "_file.txt" }; char *l[100] = { "first line", .. "last line" }; f[0][0] = UNIQUE_IDENTIFIER; Things *a = readThings(f); Things *b = readThings(l); 
+1
source

There is more than one way to trick this particular cat, but in general, the solution to this problem hides the implementation of the public interface behind indirectness, which allows you to introduce separate "implementations".

(This embodiment of your problem is also closely related to a slightly different problem of ensuring ABI compatibility between code versions.)

To solve this problem in C, you can do it similarly to pimpl with-inheritance in C ++ (protected instead of a private d-pointer with overridden protected constructors):

You create an opaque object 'reader' / 'stream' (a pointer to redirecting the declared w / typedef structure to C) and the corresponding constructor functions to create an instance of an opaque object that introduces the desired implementation.

Let me sketch out sample header files to give you an idea of ​​how functions fit together. Let's start with the guts, the definition of d-pointer / p-impl objects (NB: I omit some patterns, such as guards):

reader-private.h :

 /* probably should be in its proper C file, but here for clarification */ struct FileReaderPrivateData { FILE * fp; }; /* probably should be in its proper C file, but here for clarification */ struct StringReaderPrivateData { size_t nlines; size_t cursor; char ** lines; }; /* in C we don't have inheritance, but we can 'fix' it using callbacks */ struct ReaderPrivate { int (* close)(void* pData); /* impl callback */ ssize_t (* readLine)(void* pData, char** into); /* impl callback */ /* impl-specific data object, callbacks can type cast safely */ void * data; }; /* works like a plain p-impl/d-pointer, delegates to the callbacks */ struct Reader { struct ReaderPrivate * dPtr; } 

reader.h

 typedef struct Reader* Reader; /* NB: buf would be a pointer to set to a newly allocated line buffer. */ ssize_t readLine(Reader r, char ** buf); int close(Reader r); 

file-reader.h

 #include "reader.h" Reader createFileReader(FILE * fp); Reader createFileReader(const char* path); 

string-reader.h

 #include "reader.h" Reader createStringReader(const char**, size_t nlines); 

This is a common template for executing a pimpl / d pointer with C inheritance, so you can ignore the implementation context behind the open interface that is accessed through opaque pointers. This mechanism is usually useful for ensuring API and ABI compatibility between different implementations of an open interface and for implementing a simple inheritance pattern.

+1
source

Here's an implementation using fcookieopen [IIRC, BSD has something similar]:

 // control for string list struct cookie { char **cook_list; // list of strings int cook_maxcount; // maximum number of strings int cook_curidx; // current index into cook_list int cook_curoff; // current offset within item }; int cookie_close(void *vp); ssize_t cookie_read(void *vp,char *buf,size_t size); cookie_io_functions_t cook_funcs = { .read = cookie_open; .close = cookie_close; }; // cookie_open -- open stream FILE * cookie_open(char **strlist,int count,const char *mode) // strlist -- list of strings // count -- number of elements in strlist // mode -- file open mode { cookie *cook; FILE *stream; cook = calloc(1,sizeof(cookie)); cook->cook_list = strlist; cook->cook_maxcount = count; stream = fopencookie(cook,mode,&cook_funcs); return stream; } // cookie_close -- close stream int cookie_close(void *vp) { free(vp); return 0; } // cookie_read -- read stream ssize_t cookie_read(void *vp,char *buf,size_t size) { cookie *cook = vp; char *base; ssize_t totcnt; totcnt = 0; while (size > 0) { // bug out if all strings exhausted if (cook->cook_curidx >= cook->cook_maxcount) break; base = cook->cook_list[cook->cook_curidx]; base += cook->cook_curoff; // if at end of current string, start on the next one if (*base == 0) { cook->cook_curidx += 1; cook->cook_curoff = 0; continue; } // store character and bump buffer and count *buf++ = *base; size -= 1; totcnt += 1; cook->cook_curoff += 1; } return totcnt; } 
+1
source

If you need this function for debugging only, write the function fopen_strings(char *list[]) to:

  • create temporary file
  • open it with fopen using "r+" mode
  • write all your lines.
  • delete the file (FILE * can still work on it until it is closed explicitly or implicitly at the end of the program. You may need to skip this step on some operating systems that prevent the deletion of open files.
  • rewind stream
  • return the stream and let your program use it like a regular file.
+1
source

All Articles