3

I currently use C++ to do some graph related computation using boost::graph. boost::graph can output its graph as a dot file and I use a std::stringstream to capture the output dot file. Thus the contents of the dot file resides in memory.

I want to use the dot file to visualize the graph (as fast as possible). Thus I want to generate the dot file, generate an svg file and print it onto some canvas. I want to avoid using temporary files for this, as the graphs shall be small and memory is available anyway.

However graphviz libgraph has only the function extern Agraph_t *agread(FILE *); the only way I can imagine to make this working is to hack around in the filehandle struct __FILE which is really not portable.

How would you let a library read you memory contents as a file in Unix/linux?

I just found out that libcgraph from GraphViz allows to enter a overloaded version here, but so far the documentation doesn't point me to some usefull place.

Alexander Oh
  • 24,223
  • 14
  • 73
  • 76

4 Answers4

4

Well, it is arguably a bug in the API, but here's an idea. This is assuming that the agread() function would read the file in as binary data.

Note that I am not familiar with the API you're using, but I hope this may be useful anyway.

  1. Map a file into memory using mmap().
  2. Use that memory region to do your graph construction.
  3. When it comes time to call agread(), open that file descriptor into a FILE * struct (fopen() or fdopen() if you didn't close the descriptor).
  4. Pass the FILE * struct.

Edit: Or, ignore my answer and use the fmemopen() call. It probably is exactly what you need. I didn't want to delete my answer though, in case someone is currently writing a response :-).

tdenniston
  • 3,389
  • 2
  • 21
  • 29
  • this doesn't avoid writing the file to disk, which I really try to do here. – Alexander Oh Oct 07 '11 at 15:43
  • Assuming you're speaking to my original answer, it may very well avoid writing to disk. You mentioned the graphs were small, which greatly increases the chance that the OS will simply cache the writes and not actually write out to disk. You'd have to call `msync()` to flush to disk. – tdenniston Oct 07 '11 at 15:46
  • ah yes might be true. but what happens if the program is terminated. I'd have to delete those files again, or place them in the tmp folder. `fmemopen()` is a glibc function. Unfortunately the code needs to run on freebsd as well. – Alexander Oh Oct 07 '11 at 15:53
  • Good point. You could either use `tmpfile()` like Ilmari Karonen mentioned in another answer, or use `fmemopen()` like I mentioned in my amended answer, which does not need a file at all. Edit: Ah, I see the problem with fmemopen. – tdenniston Oct 07 '11 at 15:54
3

You could create a pipe with pipe(), write the data into the input end and use fdopen() to turn the output file descriptor into a filehandle suitable for passing into agread().

However, this will only work if you're sure that the data is less than PIPE_BUF bytes; otherwise the write might block forever, since there's nothing reading from the other end.

In general, using temporary files is much easier and more reliable. Just use tmpfile() to get a file handle, write the data into it, rewind and pass it to agread():

fh = tmpfile();
fputs( data, fh );
rewind( fh );
graph = agread( fh );
fclose( fh );

(Of course, you should check for errors, which I didn't for the sake of brevity.)

Ilmari Karonen
  • 49,047
  • 9
  • 93
  • 153
1

If you are willing to use a GNU libc extension, you can open a C string as a FILE*; the documentation is at http://www.gnu.org/s/libc/manual/html_node/String-Streams.html.

Jeremiah Willcock
  • 30,161
  • 7
  • 76
  • 78
0

On Windows, you can open a named pipe with fopen.

FILE* f = fopen("\\\\.\\Pipe\\<pipe name>", "rb");

So you can create a pipe in a separate thread where you push the data on it, and agread will read from it without a need for a temporary file.

sashoalm
  • 75,001
  • 122
  • 434
  • 781