4

I am fairly new to C++ and wanted to convert a *FILE (such as returned by popen()) to a iostream to be used with functions such as getline etc. I found the following code http://fw-geekycoder.blogspot.co.za/2011/06/how-to-convert-c-file-to-c-iostream.html, as well as similar code from a bunch of places, but the compiler moans about boost::iostreams::stream_buffer<boost::iostreams::file_descriptor_sink> bis(fd); and boost::iostreams::stream_buffer<boost::iostreams::file_descriptor_sink> bis(fd);

#include <iostream>
#include <cstdio>
#include <unistd.h>
#include <boost/iostreams/device/file_descriptor.hpp>
#include <boost/iostreams/stream.hpp>

void write() {
    FILE* fp = fopen("whatever.txt", "w");
    if (fp == NULL) {
        perror("fopen error");
    }
    int fd = fileno(fp);
    boost::iostreams::stream_buffer<boost::iostreams::file_descriptor_sink> bis(fd);
    std::ostream os(&bis);
    os << "Hello World!" << std::endl;

    fclose(fp);
}

void read() {
    FILE* fp = fopen("whatever.txt", "r");
    if (fp == NULL) {
        perror("fopen error");
    }
    int fd = fileno(fp);
    boost::iostreams::stream_buffer<boost::iostreams::file_descriptor_source> bis(fd);
    std::istream is(&bis);
    while (is) {
        std::string line;
        std::getline(is, line);
        std::cout << line << std::endl;
    }
    fclose(fp);
}

int main() {
    write();
    read();

    return 0;
}

It seems like my system finds boost, but as if the API or something changed. What is the problem, here is my output from eclipse:

make all 
Building file: ../src/boostPopenHandler.cpp
Invoking: GCC C++ Compiler
g++ -D__GXX_EXPERIMENTAL_CXX0X__ -I../../emdw/src -I../../patrecII/src -I../../ -O0 -g3 -Wall -c -fmessage-length=0 -std=c++0x -MMD -MP -MF"src/boostPopenHandler.d" -MT"src/boostPopenHandler.d" -o"src/boostPopenHandler.o" "../src/boostPopenHandler.cpp"
In file included from ../src/boostPopenHandler.cpp:4:0:
/usr/include/boost/iostreams/device/file_descriptor.hpp: In instantiation of ‘boost::iostreams::file_descriptor_sink::file_descriptor_sink(const Path&, std::ios_base::openmode) [with Path = char; std::ios_base::openmode = std::_Ios_Openmode]’:
/usr/include/boost/iostreams/stream_buffer.hpp:94:5:   required from ‘boost::iostreams::stream_buffer<T, Tr, Alloc, Mode>::stream_buffer(U100&, typename boost::disable_if<boost::is_same<U0, T> >::type*) [with U100 = char; T = boost::iostreams::file_descriptor_sink; Tr = std::char_traits<char>; Alloc = std::allocator<char>; Mode = boost::iostreams::output_seekable; typename boost::disable_if<boost::is_same<U0, T> >::type = void]’
../src/boostPopenHandler.cpp:13:83:   required from here
/usr/include/boost/iostreams/device/file_descriptor.hpp:276:36: error: invalid conversion from ‘char’ to ‘const char*’ [-fpermissive]
     { open(detail::path(path), mode); }
                                    ^
In file included from /usr/include/boost/iostreams/device/file_descriptor.hpp:26:0,
                 from ../src/boostPopenHandler.cpp:4:
/usr/include/boost/iostreams/detail/path.hpp:52:5: error:   initializing argument 1 of ‘boost::iostreams::detail::path::path(const char*)’ [-fpermissive]
     path(const char* p) : narrow_(p), wide_(), is_wide_(false) { }
     ^
In file included from ../src/boostPopenHandler.cpp:4:0:
/usr/include/boost/iostreams/device/file_descriptor.hpp: In instantiation of ‘boost::iostreams::file_descriptor_source::file_descriptor_source(const Path&, std::ios_base::openmode) [with Path = char; std::ios_base::openmode = std::_Ios_Openmode]’:
/usr/include/boost/iostreams/stream_buffer.hpp:94:5:   required from ‘boost::iostreams::stream_buffer<T, Tr, Alloc, Mode>::stream_buffer(U100&, typename boost::disable_if<boost::is_same<U0, T> >::type*) [with U100 = char; T = boost::iostreams::file_descriptor_source; Tr = std::char_traits<char>; Alloc = std::allocator<char>; Mode = boost::iostreams::input_seekable; typename boost::disable_if<boost::is_same<U0, T> >::type = void]’
../src/boostPopenHandler.cpp:26:85:   required from here
/usr/include/boost/iostreams/device/file_descriptor.hpp:194:36: error: invalid conversion from ‘char’ to ‘const char*’ [-fpermissive]
     { open(detail::path(path), mode); }
                                    ^
In file included from /usr/include/boost/iostreams/device/file_descriptor.hpp:26:0,
                 from ../src/boostPopenHandler.cpp:4:
/usr/include/boost/iostreams/detail/path.hpp:52:5: error:   initializing argument 1 of ‘boost::iostreams::detail::path::path(const char*)’ [-fpermissive]
     path(const char* p) : narrow_(p), wide_(), is_wide_(false) { }
     ^
make: *** [src/boostPopenHandler.o] Error 1

Edit:
Following the answer from Selçuk Cihan, I changed the relevant code to:

boost::iostreams::file_descriptor_source fds(fd);
boost::iostreams::stream_buffer<boost::iostreams::file_descriptor_sink> 
...
boost::iostreams::file_descriptor_source fds(fd);
boost::iostreams::stream_buffer<boost::iostreams::file_descriptor_source> 

But I still get a list of compile errors:

**** Build of configuration Debug for project boostPopenHandler ****

make all 
Building file: ../src/boostPopenHandler.cpp
Invoking: GCC C++ Compiler
g++ -I../../emdw/src -I../../patrecII/src -I../../ -O0 -g3 -Wall -c -fmessage-length=0 -std=c++0x -MMD -MP -MF"src/boostPopenHandler.d" -MT"src/boostPopenHandler.d" -o"src/boostPopenHandler.o" "../src/boostPopenHandler.cpp"
In file included from ../src/boostPopenHandler.cpp:4:0:
/usr/include/boost/iostreams/device/file_descriptor.hpp: In instantiation of ‘boost::iostreams::file_descriptor_source::file_descriptor_source(const Path&, std::ios_base::openmode) [with Path = int; std::ios_base::openmode = std::_Ios_Openmode]’:
../src/boostPopenHandler.cpp:13:52:   required from here
/usr/include/boost/iostreams/device/file_descriptor.hpp:194:36: error: invalid conversion from ‘int’ to ‘const char*’ [-fpermissive]
     { open(detail::path(path), mode); }
                                    ^
In file included from /usr/include/boost/iostreams/device/file_descriptor.hpp:26:0,
                 from ../src/boostPopenHandler.cpp:4:
/usr/include/boost/iostreams/detail/path.hpp:52:5: error:   initializing argument 1 of ‘boost::iostreams::detail::path::path(const char*)’ [-fpermissive]
     path(const char* p) : narrow_(p), wide_(), is_wide_(false) { }
     ^
In file included from ../src/boostPopenHandler.cpp:4:0:
/usr/include/boost/iostreams/device/file_descriptor.hpp: In instantiation of ‘boost::iostreams::file_descriptor_sink::file_descriptor_sink(const Path&, std::ios_base::openmode) [with Path = boost::iostreams::file_descriptor_source; std::ios_base::openmode = std::_Ios_Openmode]’:
/usr/include/boost/iostreams/stream_buffer.hpp:94:5:   required from ‘boost::iostreams::stream_buffer<T, Tr, Alloc, Mode>::stream_buffer(U100&, typename boost::disable_if<boost::is_same<U0, T> >::type*) [with U100 = boost::iostreams::file_descriptor_source; T = boost::iostreams::file_descriptor_sink; Tr = std::char_traits<char>; Alloc = std::allocator<char>; Mode = boost::iostreams::output_seekable; typename boost::disable_if<boost::is_same<U0, T> >::type = void]’
../src/boostPopenHandler.cpp:14:84:   required from here
/usr/include/boost/iostreams/device/file_descriptor.hpp:276:36: error: no matching function for call to ‘boost::iostreams::detail::path::path(const boost::iostreams::file_descriptor_source&)’
     { open(detail::path(path), mode); }
                                    ^
/usr/include/boost/iostreams/device/file_descriptor.hpp:276:36: note: candidates are:
In file included from /usr/include/boost/iostreams/device/file_descriptor.hpp:26:0,
                 from ../src/boostPopenHandler.cpp:4:
/usr/include/boost/iostreams/detail/path.hpp:138:5: note: boost::iostreams::detail::path::path(const wstring&)
     path(const std::wstring&);
     ^
/usr/include/boost/iostreams/detail/path.hpp:138:5: note:   no known conversion for argument 1 from ‘const boost::iostreams::file_descriptor_source’ to ‘const wstring& {aka const std::basic_string<wchar_t>&}’
/usr/include/boost/iostreams/detail/path.hpp:70:5: note: boost::iostreams::detail::path::path(const boost::iostreams::detail::path&)
     path(const path& p) 
     ^
/usr/include/boost/iostreams/detail/path.hpp:70:5: note:   no known conversion for argument 1 from ‘const boost::iostreams::file_descriptor_source’ to ‘const boost::iostreams::detail::path&’
/usr/include/boost/iostreams/detail/path.hpp:64:14: note: template<class Path> boost::iostreams::detail::path::path(const Path&, typename Path::codecvt_type*)
     explicit path(const Path& p, typename Path::codecvt_type* = 0)
              ^
/usr/include/boost/iostreams/detail/path.hpp:64:14: note:   template argument deduction/substitution failed:
/usr/include/boost/iostreams/detail/path.hpp: In substitution of ‘template<class Path> boost::iostreams::detail::path::path(const Path&, typename Path::codecvt_type*) [with Path = boost::iostreams::file_descriptor_source]’:
/usr/include/boost/iostreams/device/file_descriptor.hpp:276:36:   required from ‘boost::iostreams::file_descriptor_sink::file_descriptor_sink(const Path&, std::ios_base::openmode) [with Path = boost::iostreams::file_descriptor_source; std::ios_base::openmode = std::_Ios_Openmode]’
/usr/include/boost/iostreams/stream_buffer.hpp:94:5:   required from ‘boost::iostreams::stream_buffer<T, Tr, Alloc, Mode>::stream_buffer(U100&, typename boost::disable_if<boost::is_same<U0, T> >::type*) [with U100 = boost::iostreams::file_descriptor_source; T = boost::iostreams::file_descriptor_sink; Tr = std::char_traits<char>; Alloc = std::allocator<char>; Mode = boost::iostreams::output_seekable; typename boost::disable_if<boost::is_same<U0, T> >::type = void]’
../src/boostPopenHandler.cpp:14:84:   required from here
/usr/include/boost/iostreams/detail/path.hpp:64:14: error: no type named ‘codecvt_type’ in ‘class boost::iostreams::file_descriptor_source’
/usr/include/boost/iostreams/device/file_descriptor.hpp: In instantiation of ‘boost::iostreams::file_descriptor_sink::file_descriptor_sink(const Path&, std::ios_base::openmode) [with Path = boost::iostreams::file_descriptor_source; std::ios_base::openmode = std::_Ios_Openmode]’:
/usr/include/boost/iostreams/stream_buffer.hpp:94:5:   required from ‘boost::iostreams::stream_buffer<T, Tr, Alloc, Mode>::stream_buffer(U100&, typename boost::disable_if<boost::is_same<U0, T> >::type*) [with U100 = boost::iostreams::file_descriptor_source; T = boost::iostreams::file_descriptor_sink; Tr = std::char_traits<char>; Alloc = std::allocator<char>; Mode = boost::iostreams::output_seekable; typename boost::disable_if<boost::is_same<U0, T> >::type = void]’
../src/boostPopenHandler.cpp:14:84:   required from here
/usr/include/boost/iostreams/detail/path.hpp:57:14: note: template<class Path> boost::iostreams::detail::path::path(const Path&, typename Path::external_string_type*)
     explicit path(const Path& p, typename Path::external_string_type* = 0)
              ^
/usr/include/boost/iostreams/detail/path.hpp:57:14: note:   template argument deduction/substitution failed:
/usr/include/boost/iostreams/detail/path.hpp: In substitution of ‘template<class Path> boost::iostreams::detail::path::path(const Path&, typename Path::external_string_type*) [with Path = boost::iostreams::file_descriptor_source]’:
/usr/include/boost/iostreams/device/file_descriptor.hpp:276:36:   required from ‘boost::iostreams::file_descriptor_sink::file_descriptor_sink(const Path&, std::ios_base::openmode) [with Path = boost::iostreams::file_descriptor_source; std::ios_base::openmode = std::_Ios_Openmode]’
/usr/include/boost/iostreams/stream_buffer.hpp:94:5:   required from ‘boost::iostreams::stream_buffer<T, Tr, Alloc, Mode>::stream_buffer(U100&, typename boost::disable_if<boost::is_same<U0, T> >::type*) [with U100 = boost::iostreams::file_descriptor_source; T = boost::iostreams::file_descriptor_sink; Tr = std::char_traits<char>; Alloc = std::allocator<char>; Mode = boost::iostreams::output_seekable; typename boost::disable_if<boost::is_same<U0, T> >::type = void]’
../src/boostPopenHandler.cpp:14:84:   required from here
/usr/include/boost/iostreams/detail/path.hpp:57:14: error: no type named ‘external_string_type’ in ‘class boost::iostreams::file_descriptor_source’
/usr/include/boost/iostreams/device/file_descriptor.hpp: In instantiation of ‘boost::iostreams::file_descriptor_sink::file_descriptor_sink(const Path&, std::ios_base::openmode) [with Path = boost::iostreams::file_descriptor_source; std::ios_base::openmode = std::_Ios_Openmode]’:
/usr/include/boost/iostreams/stream_buffer.hpp:94:5:   required from ‘boost::iostreams::stream_buffer<T, Tr, Alloc, Mode>::stream_buffer(U100&, typename boost::disable_if<boost::is_same<U0, T> >::type*) [with U100 = boost::iostreams::file_descriptor_source; T = boost::iostreams::file_descriptor_sink; Tr = std::char_traits<char>; Alloc = std::allocator<char>; Mode = boost::iostreams::output_seekable; typename boost::disable_if<boost::is_same<U0, T> >::type = void]’
../src/boostPopenHandler.cpp:14:84:   required from here
/usr/include/boost/iostreams/detail/path.hpp:52:5: note: boost::iostreams::detail::path::path(const char*)
     path(const char* p) : narrow_(p), wide_(), is_wide_(false) { }
     ^
/usr/include/boost/iostreams/detail/path.hpp:52:5: note:   no known conversion for argument 1 from ‘const boost::iostreams::file_descriptor_source’ to ‘const char*’
/usr/include/boost/iostreams/detail/path.hpp:49:5: note: boost::iostreams::detail::path::path(const string&)
     path(const std::string& p) : narrow_(p), wide_(), is_wide_(false) { }
     ^
/usr/include/boost/iostreams/detail/path.hpp:49:5: note:   no known conversion for argument 1 from ‘const boost::iostreams::file_descriptor_source’ to ‘const string& {aka const std::basic_string<char>&}’
/usr/include/boost/iostreams/detail/path.hpp:46:5: note: boost::iostreams::detail::path::path()
     path() : narrow_(), wide_(), is_wide_(false) { }
     ^
/usr/include/boost/iostreams/detail/path.hpp:46:5: note:   candidate expects 0 arguments, 1 provided
make: *** [src/boostPopenHandler.o] Error 1
Simon Streicher
  • 2,638
  • 1
  • 26
  • 30
  • I couldn't see why the compiler tries `In instantiation of ‘boost::iostreams::file_descriptor_source::file_descriptor_source(const Path&, std::ios_base::openmode)` that constructor even though you supplied an int fd. But that is the problem. – Selçuk Cihan Feb 04 '16 at 12:09
  • Why are you doing `-D__GXX_EXPERIMENTAL_CXX0X__`? That is undefined behaviour. It's set by the compiler automatically when you use `-std=c++0x` or similar and would cause errors without a suitable `-std` option. So don't define it yourself it. Ever. – Jonathan Wakely Feb 04 '16 at 12:25
  • @Jonathan Wakely, I found it on a guide explaining how to setup eclipse properly for c++ 11. I don't know why the guide specified that, so I will follow your advice and remove it. – Simon Streicher Feb 04 '16 at 13:19
  • Ugh, maybe eclipse is not smart enough to know that `-std=c++0x` means you're using C++11. Maybe that was true once, but I hope eclipse has been fixed now. – Jonathan Wakely Feb 04 '16 at 13:35
  • For interest, I can confirm that without `-D__GXX_EXPERIMENTAL_CXX0X__` eclipse has trouble compiling some of my code – Simon Streicher Feb 08 '16 at 12:21

2 Answers2

5

Oh, you should be first declaring a file_descriptor_source as in

boost::iostreams::file_descriptor_source fds(fd);

and then comes your stream buffer

boost::iostreams::stream_buffer<boost::iostreams::file_descriptor_source> bis(fds);

Edit:

Sorry for the confusion, that ctor with an int parameter is deprecated and it seems that you do not have that ctor because otherwise your code would compile perfectly. That is why the above code i supplied needs a second mandatory parameter of either boost::iostreams::never_close_handle or boost::iostreams::close_handle

So it should read

boost::iostreams::file_descriptor_source fds(fd, boost::iostreams::close_handle);

otherwise you would still get that error. And same fix goes with the file_descriptor_sink also.

Now as for how you should read the errors:

/usr/include/boost/iostreams/device/file_descriptor.hpp: In instantiation of ‘boost::iostreams::file_descriptor_source::file_descriptor_source(const Path&, std::ios_base::openmode) [with Path = char; std::ios_base::openmode = std::_Ios_Openmode]’:

Says that it tried to instantiate a file_descriptor_source with a template parameter choice of

[with Path = char; std::ios_base::openmode = std::_Ios_Openmode]

Since you only supplied an integer, the compiler tried to match that version of the ctor and failed to make the conversion.

Selçuk Cihan
  • 1,979
  • 2
  • 17
  • 30
  • I still get compile errors after I tried your suggestion, I have documented the errors in the original question (since the output is quite long). – Simon Streicher Feb 04 '16 at 13:39
  • @SimonStreicher have you checked my edit, my bad sorry for the confusion. – Selçuk Cihan Feb 04 '16 at 13:44
  • Thanks for the explanation about the specific error. I tried the latest suggestion about `boost::iostreams::close_handle`, but now the compiler says: undefined reference to `boost::iostreams::file_descriptor_source::file_descriptor_source(int, boost::iostreams::file_descriptor_flags)`. – Simon Streicher Feb 04 '16 at 14:14
  • You should tell the linker about boost lib, like `-lboost_iostreams`. – Selçuk Cihan Feb 04 '16 at 15:14
  • @ Selçuk Cihan. Oh thanks, I though boost_system included everything. It's working now! – Simon Streicher Feb 04 '16 at 15:21
  • You want to use `boos::iostreams::never_close_handle` because `fclose` will close the file handle anyway. – Brice M. Dempsey Sep 30 '18 at 08:16
0

You don't need Boost for this.

wanted to convert a *FILE (such as returned by popen()) to a iostream

Use pstreams instead (disclaimer, I wrote it, if that matters).

Some compilers provide extensions to create a streambuf from a C file without needing Boost, e.g. with GCC:

#include <ext/stdio_filebuf.h>
...
__gnu_cxx::stdio_filebuf<char> fb(fp, std::ios::out);
std::ostream os(&fb);
Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521
  • 3
    That's not only not a proper answer to this question, but it's also publicising your own project without disclaiming your involvement! – Lightness Races in Orbit Feb 04 '16 at 12:19
  • 2
    So what? It's not like I make money from pstreams. I *am* paid to work on GCC though, should I not promote that either? ;) – Jonathan Wakely Feb 04 '16 at 12:22
  • @Jonathan Wakely, thanks I did try it, but couldn't easily find a way to output to stdin and stream from stdout on a single child process. My final goal with all of this is to wrap a very simple input-output cli program. – Simon Streicher Feb 04 '16 at 13:25
  • Pstreams supports that (but `popen` doesn't, at least not on most operating systems, it's either read-only or -write-only). A `redi::pstream` object will be opened for stdin and stdout by default, you just read and write from it like you would with `std::iostream` – Jonathan Wakely Feb 04 '16 at 13:36
  • @Johnathan Wakely, well if that is the case, I will quickly try again and report my findings. Thanks – Simon Streicher Feb 04 '16 at 13:42
  • @Johnathan Wakely, hmm, I seem not to quite grasp how to let pstream communicate with a program. Will you please have a look at this separate question?: http://stackoverflow.com/questions/35204550/wrapping-a-commandline-program-with-pstream – Simon Streicher Feb 04 '16 at 15:09
  • @Jonathan Wakely. Also, `__gnu_cxx::stdio_filebuf` did work on my compiler, thanks. – Simon Streicher Feb 04 '16 at 21:30
  • 1
    As a plus, @JonathanWakely's implementation supports move construction and assignment, unlike boost's implementation as of 1.81.0_1. – Louis Langholtz Apr 25 '23 at 08:57