How to read bytes from file using std::ifstream to std::array? - c++

The below program tries to open a rom file and loads it to std::array.
#include <array>
#include <fstream>
#include <iostream>
const std::string ROM_FILE = "cpu_instrs.gb";
int main()
{
std::array<uint8_t, 0x8000> m_Cartridge;
std::ifstream istream(ROM_FILE, std::ios::in | std::ios::binary);
istream.seekg(0, std::ios::end);
size_t length = istream.tellg();
istream.seekg(0, std::ios::beg);
if (length > m_Cartridge.size())
{
length = m_Cartridge.size();
}
istream.read(m_Cartridge.data(), length);
for (const uint8_t& b : m_Cartridge)
{
std::cout << b << std::endl;
}
return 0;
}
When I ran the program above using g++, I got the following error
test.cpp: In function 'int main()':
test.cpp:21:34: error: invalid conversion from 'std::array<unsigned char, 32768>::pointer' {aka 'unsigned char*'} to 'std::basic_istream<char>::char_type*' {aka 'char*'} [-fpermissive]
21 | istream.read(m_Cartridge.data(), length);
| ~~~~~~~~~~~~~~~~^~
| |
| std::array<unsigned char, 32768>::pointer {aka unsigned char*}
In file included from C:/TDM-GCC-64/lib/gcc/x86_64-w64-mingw32/9.2.0/include/c++/fstream:38,
from test.cpp:2:
C:/TDM-GCC-64/lib/gcc/x86_64-w64-mingw32/9.2.0/include/c++/istream:486:23: note: initializing argument 1 of 'std::basic_istream<_CharT, _Traits>& std::basic_istream<_CharT, _Traits>::read(std::basic_istream<_CharT, _Traits>::char_type*, std::streamsize) [with _CharT = char; _Traits = std::char_traits<char>; std::basic_istream<_CharT, _Traits>::char_type = char; std::streamsize = long long int]'
486 | read(char_type* __s, streamsize __n);
| ~~~~~~~~~~~^~~
It seems to me that std::istream only works with char not unsigned char. When I change type from uint8_t to char. The program compiles and ran without problems. Is there any way to make std::istream work with uint8_t?

std::istream is written in terms of char_type being simply char, and similarly, std::istream::read is the same.
The conventional approach would be to reinterpret_cast<char*> the pointer that you are reading to:
istream.read(reinterpret_cast<char*>(m_Cartridge.data()), length);
Although using reinterpret_cast is often frowned upon in many cases, this is one such case where it's both necessary and also safe to perform, since pointers to char are capable of aliasing any object with the same reachability as the parent object (in this case, the same reachability as the entire array object).
In fact, if you check the std::istream::read page on cppreference, even it uses a similar example to read the raw binary:
std::uint32_t n;
if(raw.read(reinterpret_cast<char*>(&n), sizeof n)) { ... }
That said, there is technically an alternative way you can do this -- though it's not recommended. The various stream classes are templated on the character type (basic_*stream<...>), of which std::ifstream is an alias of. Technically you could try to instantiate std::basic_ifstream<unsigned char> and read from this without a cast. However, in doing so, you may need to implement a custom std::char_traits -- as this is not required by the C++ standard to work with signed or unsigned character types (e.g. std::char_traits<unsigned char> is not guaranteed to be valid)

Related

-Wclass-memaccess warning with boost::endian and gcc

I'm getting a -Wclass-memaccess with gcc >= 8 and I would like to know if I can safely ignore the warning.
Test case:
#include <array>
#include <boost/endian/buffers.hpp>
int main()
{
static_assert(std::is_trivial<boost::endian::big_uint16_buf_t>::value);
static_assert(std::is_trivially_copyable<boost::endian::big_uint16_buf_t>::value);
// No warning when buffer is char*
{
std::array<char, 2> buffer{0x0, 0x1};
boost::endian::big_uint16_buf_t v;
std::memcpy(&v, buffer.data(), sizeof(v));
}
// -Wclass-memaccess when buffer is std::byte*
{
std::array<std::byte, 2> buffer{std::byte{0x0}, std::byte{0x1}};
boost::endian::big_uint16_buf_t v;
std::memcpy(&v, buffer.data(), sizeof(v));
}
}
Try it on wandbox
Background: This type of code is used to decode binary network traffic and memcpy is used to be safe with strict aliasing rule.
Before using that pattern more extensively on a bigger codebase, I would like to be sure it's not flawed.
I have the impression that it's safe to copy into a boost::endian buffer. Strangely there is no warning when the buffer is a pointer to char, but only when the buffer is a pointer to std::byte
Full warning message is:
prog.cc: In function 'int main()':
prog.cc:23:49: warning: 'void* memcpy(void*, const void*, size_t)' copying an object of type 'boost::endian::big_uint16_buf_t' {aka 'class boost::endian::endian_buffer<boost::endian::order::big, short unsigned int, 16>'} with 'protected' member 'boost::endian::endian_buffer<boost::endian::order::big, short unsigned int, 16>::m_value' from an array of 'std::array<std::byte, 6>::value_type' {aka 'enum class std::byte'}; use assignment or copy-initialization instead [-Wclass-memaccess]
23 | std::memcpy(&v, buffer.data(), sizeof(v));
| ^
In file included from prog.cc:2:
/opt/wandbox/boost-1.70.0/gcc-9.1.0/include/boost/endian/buffers.hpp:375:11: note: 'boost::endian::big_uint16_buf_t' {aka 'class boost::endian::endian_buffer<boost::endian::order::big, short unsigned int, 16>'} declared here
375 | class endian_buffer< order::big, T, n_bits, align::no >
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

C++ writing to file error in Linux/Ubuntu?

I have been trying to learn C++ recently, but I have stumbled across some errors. For example, when I try to run this code to ask the user what they want outputted to a file:
#include <iostream>
#include <cstdio>
using namespace std;
main() {
string output; //Declare variables before starting
FILE * file = fopen("newfile.txt","w"); //creates file
cout << "Entire something that you want to be written to the file: " << endl;
cin.getline(output, 256); //Asks what you want to put into file
fprintf(file, output); //Puts output into file
fclose(file); //closes file
return 0;
}
using
g++ -o main test.cpp
I get this error:
test.cpp: In function ‘int main()’:
test.cpp:10:25: error: no matching function for call to ‘std::basic_istream<char>::getline(std::string&, int)’
cin.getline(output, 256);
^
test.cpp:10:25: note: candidates are:
In file included from /usr/include/c++/4.8/iostream:40:0,
from test.cpp:1:
/usr/include/c++/4.8/istream:618:5: note: std::basic_istream<_CharT, _Traits>& std::basic_istream<_CharT, _Traits>::getline(std::basic_istream<_CharT, _Traits>::char_type*, std::streamsize, std::basic_istream<_CharT, _Traits>::char_type) [with _CharT = char; _Traits = std::char_traits<char>; std::basic_istream<_CharT, _Traits>::char_type = char; std::streamsize = long int]
basic_istream<char>::
^
/usr/include/c++/4.8/istream:618:5: note: candidate expects 3 arguments, 2 provided
/usr/include/c++/4.8/istream:427:7: note: std::basic_istream<_CharT, _Traits>::__istream_type& std::basic_istream<_CharT, _Traits>::getline(std::basic_istream<_CharT, _Traits>::char_type*, std::streamsize) [with _CharT = char; _Traits = std::char_traits<char>; std::basic_istream<_CharT, _Traits>::__istream_type = std::basic_istream<char>; std::basic_istream<_CharT, _Traits>::char_type = char; std::streamsize = long int]
getline(char_type* __s, streamsize __n)
^
/usr/include/c++/4.8/istream:427:7: note: no known conversion for argument 1 from ‘std::string {aka std::basic_string<char>}’ to ‘std::basic_istream<char>::char_type* {aka char*}’
test.cpp:11:22: error: cannot convert ‘std::string {aka std::basic_string<char>}’ to ‘const char*’ for argument ‘2’ to ‘int fprintf(FILE*, const char*, ...)’
fprintf(file, output);
^
Could someone please help me? And please forgive me if this is something that can be easily solved, I am fairly new to C++ and do not quite understand it yet.
The header for string is missing:
#include <string>
Without it, sring isn't defined, and everywhere you use output, you'll have errors
With the include you'll have a lot less errors. But this line has another issue (as πάντα ῥεῖ already pointed out):
cin.getline(output, 256);
because cin.getline() expects a char* and the length. If you want to use a string, you have to use the function getline(), without size (limited to strings maximume size) and on an istream:
getline(cin, output);
Last remark: you are of course free to mix c-style io and streams. But you could win from getting used to streams for all your file io.
The error occurs at the line
cin.getline(output, 256);
According to the documentation for std::istream::getline, the first argument for cin.getline() should be a char * and not a std::string as you have declared it.
Try changing the declaration of output to a char * like so
char[256] output;
Edit: Using std::getline as the others have said would be a better idea though.

Converting char to int in C++

I'm trying to convert a character from a c string to an int but I keep running into an error.
Here's my code
while(std::getline(file, line)){
if(std::isdigit(line[0]) && std::isspace(line[1]) && std::isdigit(line[2])){
SequenceArray.push_back(line);
if(std::stoi(line[2])== (SequenceArray.size() -1)){
std::cout<< "Success" << std::endl;
The error that I keep getting is as follows:
a1.cpp: In function ‘int main(int, char**)’:
a1.cpp:30:25: error: call of overloaded ‘stoi(char&)’ is ambiguous
if(std::stoi(line[2])== (SequenceArray.size() -1)){
^
a1.cpp:30:25: note: candidates are:
In file included from /usr/include/c++/4.8/string:52:0,
from /usr/include/c++/4.8/bits/locale_classes.h:40,
from /usr/include/c++/4.8/bits/ios_base.h:41,
from /usr/include/c++/4.8/ios:42,
from /usr/include/c++/4.8/ostream:38,
from /usr/include/c++/4.8/iostream:39,
from a1.cpp:1:
/usr/include/c++/4.8/bits/basic_string.h:2823:3: note: int std::stoi(const string&, std::size_t*, int) <near match>
stoi(const string& __str, size_t* __idx = 0, int __base = 10)
^
/usr/include/c++/4.8/bits/basic_string.h:2823:3: note: no known conversion for argument 1 from ‘char’ to ‘const string& {aka const std::basic_string<char>&}’
/usr/include/c++/4.8/bits/basic_string.h:2926:3: note: int std::stoi(const wstring&, std::size_t*, int) <near match>
stoi(const wstring& __str, size_t* __idx = 0, int __base = 10)
^
/usr/include/c++/4.8/bits/basic_string.h:2926:3: note: no known conversion for argument 1 from ‘char’ to ‘const wstring& {aka const std::basic_string<wchar_t>&}’
a1.cpp:35:6: warning: label ‘std’ defined but not used [-Wunused-label]
std:exit(EXIT_FAILURE);
A char implicit converts to a int, you don't need to use extra functions.
'a' = 97, 'b' = 98, 'c'=99, etc., following the ASCII table
So if you write,
char a_char = 'a';
int a_val = a_char;
cout << a_val << endl;
you have:
97
For std::stoi missing, try #include <string> (and enable C++11). However see also this thread - the Windows ports of g++ have had a long-standing issue with support of stoi and to_string.
The second error is that std:exit should be std::exit.
The third error is because of line[2].c_str(). You have not told us what line is but the error message suggests it is a std::string. So line[2] is a char and char does not have any member functions. If you explain what you are trying to do in the code std::atoi(line[2].c_str()) someone will be able to help. Maybe you meant line[2] - '0' which will give an integer between 0 and 9 if the third character in the line was a digit.
std::stoi() is C++11. Not all compilers enable C++11 by default.
The first error is because you haven't enabled C++11 support. GCC currently chooses C++03 by default, and stoi didn't exist in that version.
Add -std=c++11 to the compiler's arguments. If that doesn't work, try -std=c++0x, and think about getting a more up-to-date compiler. If you're stuck with an ancient compiler, then use atoi as in the code you originally posted (or perhaps something involving strtol, if you want to detect errors).
Also make sure you've included <string> for the declaration of that function.
The second error is because you wrote : instead of ::.

String c++ manipulation

Why the following code have compilation error?
#include<iostream>
#include<string>
using namespace std;
int main()
{
string str="abc";
string result=str[0];
cout<<result<<endl;
return 0;
}
However, the following code works fine:
#include<iostream>
#include<string>
using namespace std;
int main()
{
string str="abc";
str=str[0];
cout<<str<<endl;
return 0;
}
I works in unix and compilation command is: "g++ -g test.cpp -std=c++11 -o a", thenm ./a
The error for the first test.cpp after compile is:
test.cpp:9:21: error: invalid conversion from 'char' to 'const char*' [-fpermissive]
string result=str[0];
^
In file included from /usr/um/gcc-4.8.2/include/c++/4.8.2/string:52:0,
from /usr/um/gcc-4.8.2/include/c++/4.8.2/bits/locale_classes.h:40,
from /usr/um/gcc-4.8.2/include/c++/4.8.2/bits/ios_base.h:41,
from /usr/um/gcc-4.8.2/include/c++/4.8.2/ios:42,
from /usr/um/gcc-4.8.2/include/c++/4.8.2/ostream:38,
from /usr/um/gcc-4.8.2/include/c++/4.8.2/iostream:39,
from test.cpp:1:
/usr/um/gcc-4.8.2/include/c++/4.8.2/bits/basic_string.h:490:7: error: initializing argument 1 of 'std::basic_string<_CharT, _Traits, _Alloc>::basic_string(const _CharT*, const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]' [-fpermissive]
basic_string(const _CharT* __s, const _Alloc& __a = _Alloc());
^
std::basic_string doesn't have any constructor that takes a single CharT argument. This means that std::string (i.e. std::basic_string<char>) cannot be constructed from a single char.
The class does, however, have an assignment operator overload that takes a single CharT argument, which is why your second example compiles.
The difference between the two cases is because in the first you're performing copy initialization, which means technically you're first attempting to construct a temporary std::string instance from the char argument, and then copy it over to result. In the second you're performing assignment, which means assigning a new value to an existing std::string instance.
basic_string does have a constructor that takes a count followed by a character:
basic_string(size_type count, CharT ch, const Allocator& alloc = Allocator());
so your original example would compile if you changed the offending line to
string result = {1, str[0]};
The following code works fine as well:
string result;
result=str[0];
That means the difference is between initialization and simple assignment and, if you examine the error:
error: invalid conversion from ‘char’ to ‘const char*’
it should be clear that the initialization is not as "full-featured" as assignment - the is no string constructor that takes a char argument (there is an assignment that takes a char which is why your second example works).
You can fix it (in one way, there's no doubt others as well) by ensuring you initialize with a string rather than a character:
string result = str.substr(0,1);
str[0] returns a char&, but there is no conversion from char& to std::string
try thins instead
string result = string(1, str[0]);

g++ ifstream matching error

void FileIO :: openFile(const char* m_FileName,const char* m_FileMode);
I am getting error:
FileIO.cpp: In static member function ‘static void FileIO::openFile(const char*, const char*)’:
FileIO.cpp:12:45: error: no matching function for call to ‘std::basic_ifstream<char>::open(const char*&, const char*&)’
FileIO.cpp:12:45: note: candidate is:
In file included from FileIO.h:1:0:
/usr/include/c++/4.7/fstream:531:7: note: void std::basic_ifstream<_CharT, _Traits>::open(const char*, std::ios_base::openmode) [with _CharT = char; _Traits = std::char_traits<char>; std::ios_base::openmode = std::_Ios_Openmode]
/usr/include/c++/4.7/fstream:531:7: note: no known conversion for argument 2 from ‘const char*’ to ‘std::ios_base::openmode {aka std::_Ios_Openmode}’
std::basic_ofstream::open doesn't take two const char*s. (note: your subject says ofstream but from your comments it appears you're talking about ifstream).
http://en.cppreference.com/w/cpp/io/basic_ifstream/open
void open( const char *filename,
ios_base::openmode mode = ios_base::in );
void open( const std::string &filename,
ios_base::openmode mode = ios_base::in ); (since C++11)
The problem is the second, not the first argument.
ifstream ifs;
ifs.open("hello", "rb" /*<-- problem, this is a const char* not flags.*/);
Instead, you need to pass it std::ios_base flags
ifstream ifs("hello", std::ios_base::in | std::ios_base::binary);
or
ifstream ifs;
ifs.open("hello", std::ios_base::in | std::ios_base::binary);
--- EDIT ---
Looking at your comments following the post (why didn't you edit the post?) you are also trying to check for 'NULL'.
In C and C++ 'NULL' is a macro which is #defined as 0. So, checking for NULL can check for a null pointer, but it also can test for numeric values. If you want to check if the file opened, you will need to do:
m_FileInput.open("hello", std::ios_base::in | std::ios_base::binary);
if (!m_FileInput.good()) // checks if the file opened.
And you should try to use 'nullptr' instead of 'NULL' when possible.
You're trying to use C's FILE* syntax to call a C++ open function. The mode (read/write/append) arguments are NOT string literals in C++ but enumerated values possibly ORed together.