All,
I have a program that prints to a stream. I need to buffer this stream in memory, and then print each line as necessary to an actual file later.
Since t
No. malloc()
just gives you a block of (probably uninitialized) memory. There's no "magical" casting going on; when you do int * buf = malloc(10*sizeof(int);
you are pointing buf
at, effectively, 10 uninitialized ints.
The corresponding thing with a FILE would be FILE * f = malloc(10*sizeof(FILE));
which points f
at 10 uninitialized FILE structures, which doesn't make any sense. Furthermore, writing to an uninitialized FILE is likely to result in a crash if you're lucky.
It's easier to help if you tell us what platforms you're targeting and what you actually want to achieve. On POSIX, you can use shm_open()
to get a file descriptor pointing to "shared memory" and fdopen()
to turn the file descriptor to a FILE*
. Yes, it might run out of space.
For those who come after me, have hope! There is a solution. As noted in my question, I was using open_memstream()
, which is unsupported on windows.
Since I have a File *
pointer (this cannot be changed to char *
), I needed to redirect it to memory until later. Since I'm dealing with a file in memory, I looked into mmap()
. It handily solves the problem, but again, it is linux only.
But, windows includes a corollary to mmap()
called MapViewOfFile()
. Through the magic of #ifdef
I've got it using whichever is necessary:
#ifdef WIN32
#include <windows.h>
#else
#include <sys/mman.h>
#endif
Later on, in the main method, I call tmpfile()
which is supported on both platforms. This opens a stream to a guaranteed unique temporary file for me. Now that I have my FILE *
pointer, I need to mmap()
the space. But mmap()
needs a file descriptor, not a stream, so I used the fileno()
function to get the new file descriptor.
/* create tmp file and get file descriptor */
int fd;
yyout = tmpfile();
fd = fileno(yyout);
Now I have some more #ifdef
code to determine which memory mapping codeset needs to be used. Note the difference in the mapped space between the two versions. Windows maps 16384 bytes
and linux maps 4096 bytes
. This is because the smaller value segfaults on windows, as noted in my question here.
#ifdef WIN32
HANDLE fm;
HANDLE h = (HANDLE) _get_osfhandle (fd);
fm = CreateFileMapping(
h,
NULL,
PAGE_READWRITE|SEC_RESERVE,
0,
16384,
NULL);
if (fm == NULL) {
fprintf (stderr, "%s: Couldn't access memory space! %s\n", argv[0], strerror (GetLastError()));
exit(GetLastError());
}
bp = (char*)MapViewOfFile(
fm,
FILE_MAP_ALL_ACCESS,
0,
0,
0);
if (bp == NULL) {
fprintf (stderr, "%s: Couldn't fill memory space! %s\n", argv[0], strerror (GetLastError()));
exit(GetLastError());
}
#else
bp = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_FILE|MAP_PRIVATE, fd, 0);
if (bp == MAP_FAILED) {
fprintf (stderr, "%s: Couldn't access memory space! %s\n", argv[0], FileName, strerror (errno));
exit(errno);
}
#endif
A bunch of work now happens, wherin data is sent to the yyout
stream. Eventually the flushData()
method gets called. It finalizes the stream with a null terminated character, flushes it, and then rewinds it. Then the pointer to the memory space is passed through a function pointer, along with the proper stream to print to.
void flushData(void) {
/* write out data in the stream and reset */
while (currFields < headerFields) { fprintf(yyout, ",\"\""); currFields++; }
currFields = 0;
fprintf(yyout, "%c%c%c", 13, 10, '\0');
fflush(yyout);
rewind(yyout);
if (faqLine == 1) {
faqLine = 0; /* don't print faq's to the data file */
}
else {
(*printString)(outfile, bp);
fflush(outfile);
}
fflush(yyout);
rewind(yyout);
}
This is one of the functions that could be pointed to for printing. It walks the memory space and prints each char until it hits the null printed earlier.
int printAnsi( FILE *outstream, char *string) {
/* loop over the chars in string and print them to the outputstream as ansi */
char * ps = string;
while (*ps != '\0') {
fprintf(outstream, "%c", *ps);
ps++;
}
return 0;
}
The end result of all this being that I have a stream to memory space just like open_memstream()
, up to also having a char pointer I can use to walk through the memory space if necessary. It is cross platform, and (seemingly) fully functional.
If anyone wants more details or have notes about problems I should fix, please add a comment.