ОБНОВЛЕНИЕ: смотрите @Jettatura, что я думаю, что это лучший ответ https://stackoverflow.com/a/33612982/225186 (только для Linux?).
ОРИГИНАЛ:
(возможно, не кроссплатформенный, а простой)
Упрощение взлома в http://www.ginac.de/~kreckel/fileno/ (ответ дворака) и просмотр этого расширения gcc http://gcc.gnu.org/onlinedocs/gcc-4.6.2/libstdc++/api/a00069.html#a59f78806603c619eafcd4537c920f859,
У меня есть это решение, которое работает на GCC
(минимум 4,8) и clang
(минимум 3,3)
#include<fstream>
#include<ext/stdio_filebuf.h>
typedef std::basic_ofstream<char>::__filebuf_type buffer_t;
typedef __gnu_cxx::stdio_filebuf<char> io_buffer_t;
FILE* cfile_impl(buffer_t* const fb){
return (static_cast<io_buffer_t* const>(fb))->file(); //type std::__c_file
}
FILE* cfile(std::ofstream const& ofs){return cfile_impl(ofs.rdbuf());}
FILE* cfile(std::ifstream const& ifs){return cfile_impl(ifs.rdbuf());}
и может быть использовано это,
int main(){
std::ofstream ofs("file.txt");
fprintf(cfile(ofs), "sample1");
fflush(cfile(ofs)); // ofs << std::flush; doesn't help
ofs << "sample2\n";
}
Ограничения: (комментарии приветствуются)
Я считаю, что важно fflush
после fprintf
печати до std::ofstream
, в противном случае «sample2» появляется перед «sample1» в примере выше. Я не знаю, есть ли лучший обходной путь для этого, чем использование fflush
. В частности ofs << flush
не помогает.
Невозможно извлечь FILE * из std::stringstream
, я даже не знаю, возможно ли это. (см. ниже обновление).
Я до сих пор не знаю, как извлечь C stderr
из std::cerr
и т. Д., Например, использовать в fprintf(stderr, "sample")
, в гипотетическом коде, подобном этому fprintf(cfile(std::cerr), "sample")
.
Что касается последнего ограничения, единственный найденный мной обходной путь - добавить следующие перегрузки:
FILE* cfile(std::ostream const& os){
if(std::ofstream const* ofsP = dynamic_cast<std::ofstream const*>(&os)) return cfile(*ofsP);
if(&os == &std::cerr) return stderr;
if(&os == &std::cout) return stdout;
if(&os == &std::clog) return stderr;
if(dynamic_cast<std::ostringstream const*>(&os) != 0){
throw std::runtime_error("don't know cannot extract FILE pointer from std::ostringstream");
}
return 0; // stream not recognized
}
FILE* cfile(std::istream const& is){
if(std::ifstream const* ifsP = dynamic_cast<std::ifstream const*>(&is)) return cfile(*ifsP);
if(&is == &std::cin) return stdin;
if(dynamic_cast<std::ostringstream const*>(&is) != 0){
throw std::runtime_error("don't know how to extract FILE pointer from std::istringstream");
}
return 0; // stream not recognized
}
Попытка обработать iostringstream
Можно читать с fscanf
из istream
, используя fmemopen
, но это требует большого учета и обновления позиции ввода потока после каждого чтения, если кто-то хочет объединить C-чтения и C ++ - читает. Я не смог преобразовать это в cfile
функцию, как описано выше. (Может быть, cfile
класс , который продолжает обновляться после каждого чтения - это путь).
// hack to access the protected member of istreambuf that know the current position
char* access_gptr(std::basic_streambuf<char, std::char_traits<char>>& bs){
struct access_class : std::basic_streambuf<char, std::char_traits<char>>{
char* access_gptr() const{return this->gptr();}
};
return ((access_class*)(&bs))->access_gptr();
}
int main(){
std::istringstream iss("11 22 33");
// read the C++ way
int j1; iss >> j1;
std::cout << j1 << std::endl;
// read the C way
float j2;
char* buf = access_gptr(*iss.rdbuf()); // get current position
size_t buf_size = iss.rdbuf()->in_avail(); // get remaining characters
FILE* file = fmemopen(buf, buf_size, "r"); // open buffer memory as FILE*
fscanf(file, "%f", &j2); // finally!
iss.rdbuf()->pubseekoff(ftell(file), iss.cur, iss.in); // update input stream position from current FILE position.
std::cout << "j2 = " << j2 << std::endl;
// read again the C++ way
int j3; iss >> j3;
std::cout << "j3 = " << j3 << std::endl;
}