1

I'm learning about IO in the context of image processing and its the first time ive had to use the streams for binary files rather than formatted/text files. I'm trying to read the first 14 characters of a bitmap into a struct using an overloaded stream insertion operator with template programming.

#include <cstdint>
#include <iostream>
#include <fstream>
#include <string>

// a struct for the first 14 bytes
struct bmp_header {
  // ordered by size (not their order in the file) for efficient packing
  uint32_t size_, pixel_data_offset_;
  uint16_t reserved_1_, reserved_2_;
  char header_field_[2];
};

// overload stream extraction 
template<typename... Args, typename istream_type = std::basic_istream<Args...>>
istream_type &operator>>(istream_type &is, bmp_header &hdr) {
  return is >> hdr.header_field_[0] >> hdr.header_field_[1] >> hdr.size_ >> hdr.reserved_1_ >> hdr.reserved_2_ >> hdr.pixel_data_offset_;         
} 

int main(){
  // open the stream
  std::string ifname {"images/cameraman.bmp"};
  std::ifstream ist {ifname, std::ios::in | std::ios::binary}; 

  // instantiate the struct
  bmp_header myHeader;

  // read into the struct
  ist >> myHeader; // error

  ist.close();
  return 0;
} 

My compiler (g++) gives error:

new.cpp: In instantiation of ‘istream_type& operator>>(istream_type&, bmp_header&) [with Args = {}; istream_type = std::basic_ifstream<char>]’:
new.cpp:29:10:   required from here
new.cpp:17:119: error: invalid initialization of reference of type ‘std::basic_ifstream<char>&’ from expression of type ‘std::basic_istream<char>::__istream_type’ {aka ‘std::basic_istream<char>’}
   return is >> hdr.header_field_[0] >> hdr.header_field_[1] >> hdr.size_ >> hdr.reserved_1_ >> hdr.reserved_2_ >> hdr.pixel_data_offset_;
                                                                                                                       ^~~~~~~~~~~~~~~~~~

I'm not sure exactly how to interpret this error message. I know that std::fstream and std::iostream aren't compatible see this related question , but I don't understand if its telling me that the operator is expecting std::basic_ifstream<char>& and been given a std::basic_istream<char> or vice-versa.

I'm also not sure how to fix this. I've tried changing the template from

template<typename... Args, typename istream_type = std::basic_istream<Args...>>

to

template<typename... Args, typename istream_type = std::basic_ifstream<Args...>>

but it makes no change to the error message.

If anyone could point me in the right direction I'd appreciate it. I'm not sure whether i should be trying to read the data from a different stream type, or if I should modify the specification or template for my stream operator to make it compatible with my current stream type.

Thanks in advance!

1 Answers1

0

The problem is that, in your code, the type of this expression:

is >> hdr.header_field_[0] >> ...

is std::istream&. Then, inside return statement, you are trying to initialize the return value of type std::ifstream& (derived) by the above expression of type std::istream& (base), which is not possible. Try this example to produce the exact same error:

std::ifstream f;
std::istream& ref1 = f; // OK
std::ifstream& ref2 = ref1; // ERROR

A simple remedy is to split the code into 2 lines:

is >> hdr.header_field_[0] >> ...;
return is;

Another option would be to downcast to istream_type&, however, the syntax would be too complicated here (IMO).

Daniel Langr
  • 22,196
  • 3
  • 50
  • 93