使用C++ c11标准的std::fstream的跨平台注意事项

问题根因

微软的visual studio 2015的VC编译器及以上版本都是支持大部分c11语法标准的,但在具体实现和工作原理上与GCC/G++依然有不少出入,导致即便写的代码是一致的,得到的结果却天差地别。甚至有时根本在另一平台上无法运行。

案例展示

#ifdef _WIN32
#define PRV_DPT_LOGI(fmt, ...) fprintf(stdout, "PRV_DPT I: " fmt "\n", __VA_ARGS__)
#define PRV_DPT_LOGW(fmt, ...) fprintf(stdout, "PRV_DPT W: " fmt "\n", __VA_ARGS__)
#define PRV_DPT_LOGE(fmt, ...) fprintf(stdout, "PRV_DPT E: " fmt "\n", __VA_ARGS__)
#else
#define PRV_DPT_LOGI(...) __android_log_print(ANDROID_LOG_INFO, "PRV_DPT", __VA_ARGS__)
#define PRV_DPT_LOGW(...) __android_log_print(ANDROID_LOG_WARN, "PRV_DPT", __VA_ARGS__)
#define PRV_DPT_LOGE(...) __android_log_print(ANDROID_LOG_ERROR, "PRV_DPT", __VA_ARGS__)
#endif


#ifdef _WIN32
#define FAIL_EXIT(code) {system("pause");exit(code);}
#else
#define FAIL_EXIT(code) {exit(code);}
#endif


// the incorrect version
// Ok on Linux/Android, but failed on Windows: incomplete bytes are read.
void read_yuv_file(uint8_t * y_plane,
                   uint8_t * uv_plane,
                   uint64_t y_size,
                   uint64_t uv_size,
                   const char * file){
    std::fstream fs(file, std::ios::in);
    if (!fs.is_open()) {
	PRV_DPT_LOGI("failed to open file [%s]!", file);
	FAIL_EXIT(-1);
    }
    fs.read((char*)y_plane, y_size);
    uint64_t n_read = fs.gcount();
    if (n_read < y_size) {
	PRV_DPT_LOGE("read_yuv_file failed: expected %lu bits, got %lu!", y_size, n_read);
	FAIL_EXIT(-1);
    }
    fs.read((char*)uv_plane, uv_size);
    n_read = fs.gcount();
    if (n_read < uv_size) {
	PRV_DPT_LOGE("read_yuv_file failed: expected %lu bits, got %lu!", uv_size, n_read);
	FAIL_EXIT(-1);
    }
    fs.close();
}

上述代码在Linux/Android平台上是可以正确工作的,可以完整的将YUV格式的数据读取到对应的指针所指向的内存中;
但当移植至windows平台上时,在visual studio 2015上却报错:

PRV_DPT E: read_yuv_file failed: expected 691200 bits, got 0!
program exit with code 0xffffff.

显示文件读取不正确,读取到的字节数量为0。
这是因为默认情况下,GCC默认读取格式是binary二进制流,而visual studio的vc则为text文本字符格式。
因此,将缺省的默认读取格式指定清楚就可以跨平台无区别运行了。
更正后的代码如下:

#ifdef _WIN32
#define PRV_DPT_LOGI(fmt, ...) fprintf(stdout, "PRV_DPT I: " fmt "\n", __VA_ARGS__)
#define PRV_DPT_LOGW(fmt, ...) fprintf(stdout, "PRV_DPT W: " fmt "\n", __VA_ARGS__)
#define PRV_DPT_LOGE(fmt, ...) fprintf(stdout, "PRV_DPT E: " fmt "\n", __VA_ARGS__)
#else
#define PRV_DPT_LOGI(...) __android_log_print(ANDROID_LOG_INFO, "PRV_DPT", __VA_ARGS__)
#define PRV_DPT_LOGW(...) __android_log_print(ANDROID_LOG_WARN, "PRV_DPT", __VA_ARGS__)
#define PRV_DPT_LOGE(...) __android_log_print(ANDROID_LOG_ERROR, "PRV_DPT", __VA_ARGS__)
#endif


#ifdef _WIN32
#define FAIL_EXIT(code) {system("pause");exit(code);}
#else
#define FAIL_EXIT(code) {exit(code);}
#endif


// the correct version
// Working great on both Linux/Android and Windows.
void read_yuv_file(uint8_t * y_plane,
                   uint8_t * uv_plane,
                   uint64_t y_size,
                   uint64_t uv_size,
                   const char * file){
    std::fstream fs(file, std::ios::in|std::ios::binary);
    if (!fs.is_open()) {
	PRV_DPT_LOGI("failed to open file [%s]!", file);
	FAIL_EXIT(-1);
    }
    fs.read((char*)y_plane, y_size);
    uint64_t n_read = fs.gcount();
    if (n_read < y_size) {
	PRV_DPT_LOGE("read_yuv_file failed: expected %lu bits, got %lu!", y_size, n_read);
	FAIL_EXIT(-1);
    }
    fs.read((char*)uv_plane, uv_size);
    n_read = fs.gcount();
    if (n_read < uv_size) {
	PRV_DPT_LOGE("read_yuv_file failed: expected %lu bits, got %lu!", uv_size, n_read);
	FAIL_EXIT(-1);
    }
    fs.close();
}
posted @ 2020-07-07 14:00  xchk138  阅读(686)  评论(0)    收藏  举报