Tak jak zapowiadałem w moim poprzednim wpisie, rvalue_converter operujący na streamach jest też, a jakże. Krótkie wprowadzenie: mamy sobie wyeksportowane api, które m.in. prowadzi zapis danych do pliku, ale aby odciążyć sama metodę od żmudnych operacji na strumieniach, a jednocześnie dać jej dużą elastyczność dot. zapisu danych, dostaje ona gotową referencję do strumienia jako parametr, przykładowy kod może wyglądać tak:
class MyClass {
public:
writeToStream( std::ifstream& out ){}
}
No i teraz, fajnie by było mieć możliwość otwarcia pliku po stronie pythona i tylko przesłania samego uchwytu do naszej metody c++’owej. Otóż Ci którzy czytali poprzedni wpis o konwersji stringów już pewnie wiedzą, że to jest możliwe, może troche karkołomne, bo synchronizacja streamów to sprawa niebalna, no ale w moich testach wszystko działa poprawnie.
A oto i kod:
namespace tulips {
namespace converters {
namespace fstream {
class custom_fstream
{
public:
custom_fstream(
const char* path,
FILE* file,
std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out
) :
m_fstream(path,mode),
m_file(file)
{
fflush(m_file);
m_fstream.seekg(ftell(m_file));
}
std::fstream& get_stream() { return m_fstream; }
~custom_fstream(){ fseek( m_file, m_fstream.tellg(), SEEK_SET );}
private:
std::fstream m_fstream;
FILE* m_file;
};
struct custom_fstream_from_python_file
{
custom_fstream_from_python_file()
{
bp::converter::registry::push_back(
&convertible,
&construct,
bp::type_id<custom_fstream>());
}
static void* convertible(PyObject* obj_ptr)
{
if (!PyFile_Check(obj_ptr) || obj_ptr == Py_None) return 0;
return obj_ptr;
}
static void construct(PyObject* obj_ptr, bp::converter::rvalue_from_python_stage1_data* data)
{
FILE* file = PyFile_AsFile(obj_ptr);
void* storage = ((bp::converter::rvalue_from_python_storage<custom_fstream>*)data)->storage.bytes;
new (storage) custom_fstream(PyString_AsString(PyFile_Name(obj_ptr)), file, std::ios::in|std::ios::out);
data->convertible = storage;
}
};
void init_converter()
{
custom_fstream_from_python_file();
}
}}} // namespace tulips::converters::stream
No i teraz już możemy sobie przesyłać uchwyt do strumienia otwarty po stronie pythona