How can I throw errors with custom error messages? - c++

I have a function that allocates a text font on the heap from a filename. It looks like this:
std::unique_ptr<sf::Font> newFont(std::string&& fileName)
{
auto font = std::make_unique<sf::Font>();
if (!font->loadFromFile(fileName))
{
exit(0);
}
return font;
}
Someone on Stack Exchange - Code Review told me that I needed better error messages, since my program just silently exits if the file is not found. But since I'm writing a game, I'm not using a console window for output. I was thinking that maybe I could throw some kind of custom compiler error that triggers when the file is not found. Something like "Unable to allocate font: <font_path>". Is there a way to do this, or should I solve this some other way?

Exceptions are what you are looking for:
std::unique_ptr<sf::Font> newFont(std::string&& fileName) noexcept(false)
{
auto font = std::make_unique<sf::Font>();
if (!font->loadFromFile(fileName))
{
throw std::runtime_error("Unable to allocate font: " + fileName);
}
return font;
}
Now we can print error messages using a try-catch block:
int main() {
std::unique_ptr<sf::Font> font;
try {
font = newFont("myfont.ttf");
} catch ( const std::exception & ex ) {
PRINT_ERROR(ex.what());
return 1;
}
// do something with font ...
}
Normally you could implement PRINT_ERROR by just printing to std::cerr, but since you don't have a console to print to, you will need to do something else with the error message. Options include:
creating a log file and printing errors to the log using std::ofstream
displaying the error messages directly in your game

Related

Google protocol buffer always throw bad allocation exception

I'm newbie in google protocol buffer. And now I have a issue as below:
I have created a simple message in testMessage.proto file:
option optimize_for = SPEED;
message TestMessage
{
optional string foo = 1;
optional string bar = 2;
}
Then I generated this message to testMessage.pb.h and testMessage.pb.cc files and included these files, also added libprotobuf libs to my test project.
Then I wrote a simple code to use this class:
TestMessage testMsg;
testMsg.set_foo("1234"); // set ok
testMsg.set_bar("abcd"); // set ok
string output;
try {
std::cout << testMsg.foo() << std::endl; // output foo ok
testMsg.PrintDebugString(); // throw bad allocation exception here
// testMsg.SerializeToString(&output); // also throw bad allocation exception here
} catch (std::exception ex) {
std::cout << ex.what() << std::endl;
}
This code is very simple, but I cannot understand why it cannot run correctly.
I googled for my issue but there is nowhere mention about it.
Everyone please help me with this issue.

unspecified iostream_category error while piping to /dev/null (ios::badbit issue?)

I have code that parses a configuration file, which may send output to stdout or stderr in case of errors.
Unfortunately, when I pipe the output of my program to /dev/null, I get an exception: ios_base::clear: unspecified iostream_category error with stderror Inappropriate ioctl for device.
Here is the relevant part of my code:
try {
file.exceptions(std::ios::failbit | std::ios::badbit);
file.open(config_file);
// file.exceptions(std::ios::failbit);
}
catch (std::ios_base::failure& e) {
throw std::invalid_argument(std::string("Can't open config file ") + config_file + ": " + strerror(errno));
}
try {
errno = 0; // reset errno before I/O operation.
// ...
while (std::getline(file, line)) {
if ( [... unknown line ...] ) {
std::cerr << "Invalid line in config " << config_file << ": " << line << std::endl;
continue;
}
// debug info to STDOUT:
std::cout << config_file << ": " << line << std::endl;
}
} catch (std::ios_base::failure& err) {
std::cout << "Caught exception " << err.what() << std::endl;
if (errno != 0) {
char* theerror = strerror(errno);
file.close();
throw std::invalid_argument(std::string("Can't read config file ") + config_file + ": " + theerror);
}
}
try {
file.close();
}
catch (std::ios_base::failure& e) {
throw std::invalid_argument(std::string("Can't close config file ") + config_file + ": " + strerror(errno));
}
Here is an example of an exception:
~> ./Application test1.conf > /dev/null
test1.conf: # this is a line in the config file
Caught exception ios_base::clear: unspecified iostream_category error
When I don't pipe to /dev/null (but to stdout or a regular file), all is fine. I first suspected that the cout and cerr where causing problems, but I'm not sure.
I finally found that I could resolve this by enabling this line after opening the file, so that the badbit-type of exceptions are ignored.
file.exceptions(std::ios::failbit);
Frankly, I'm too novice in C++ to understand what is going on here.
My questions: what is causing the unspecified iostream_category exception? How can I avoid it? Is setting file.exceptions(std::ios::failbit); indeed a proper solution, or does that give other pitfalls? (A pointer to a good source detailing best practices for opening files in C++, which does included all proper exception handling, or some background explained, is highly appreciated!)
I would recommend the following approach. This is based on my own experience as well as on some of the links that have been provided above. In short I would suggest the following:
Don't turn on the exceptions when using C++ streams. They are just so hard to get right I find they make my code less readable, which sort of defeats the purpose of exceptions. (IMHO it would be better if the C++ streams used exceptions by default and in a more reasonable manner. But since it isn't built that way it is better not to force the issue and just follow the pattern that the designers seem to have had in mind.)
Rely on the fact that getline will handle the various stream bits properly. You don't need to check after each call if the bad or fail bits are set. The stream returned by getline will be implicitly cast to false when these occur.
Restructure your code to follow the RAII pattern so that you don't need to call open() or close() manually. This not only simplifies your code, but ensures that you don't forget to close it. If you are not familiar with the RAII pattern, see https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization.
Don't repeatedly put things like errno or your filename into the exceptions you generate. Keep them minimal to avoid repetition and use a catch block at the bottom to handle the errors, possibly throwing a new exception that adds the details you wish to report.
With that said, I would recommend rewriting your code to look something like the following:
using namespace std;
try {
errno = 0;
// Construct the stream here (part of RAII) then you don't need to call
// open() or close() manually.
ifstream file(config_file);
if (!file.is_open()) {
throw invalid_argument("Could not open the file");
}
while (getline(file, line)) {
// do all your processing as if no errors will occur (getline will
// be explicitly cast to a false when an error occurs). If there is
// something wrong with a line (bad format, etc.) then throw an
// exception without echoing it to cerr.
}
if (file.bad()) {
throw invalid_argument("Problem while reading file");
}
}
catch (const invalid_argument& e) {
// Whatever your error handling needs to be. config_file should still
// be valid so you can add it here, you don't need to add it to each
// individual exception. Also you can echo to cerr here, you don't need
// to do it inside the loop. If you want to use errno it should still
// be set properly at this point. If you want to pass the exception to
// the next level you can either rethrow it or generate a new one that
// adds additional details (like strerror and the filename).
}
I've improved on my earlier answer by writing a couple of functions that handle the stream checks using lambdas to actually process the file. This has the following advantages:
You don't forget where to put the stream checks.
Your code concentrates on what you want to do (the file processing) and not on the system boilerplate items.
I've created two versions. With the first your lambda is given the stream and you can process it how you like. With the second your lambda is given one line at a time. In both cases if an I/O problem occurs it will throw a system_error exception. You can also throw your own exceptions in your lambda and they will be passed on properly.
namespace {
inline void throwProcessingError(const string& filename, const string& what_arg) {
throw system_error(errno, system_category(), what_arg + " " + filename);
}
}
void process_file(const string& filename, function<void (ifstream &)> fn) {
errno = 0;
ifstream strm(filename);
if (!strm.is_open()) throwProcessingError(filename, "Failed to open");
fn(strm);
if (strm.bad()) throwProcessingError(filename, "Failed while processing");
}
void process_file_line_by_line(const string& filename, function<void (const string &)> fn)
{
errno = 0;
ifstream strm(filename);
if (!strm.is_open()) throwProcessingError(filename, "Failed to open");
string line;
while (getline(strm, line)) {
fn(line);
}
if (strm.bad()) throwProcessingError(filename, "Failed while processing");
}
To use them, you would call them as follows...
process_file("myfile.txt", [](ifstream& stream) {
... my processing on stream ...
});
or
process_file_line_by_line("myfile.txt", [](const string& line) {
... process the line ...
});

Visual C++ System::Windows::Forms::SaveFileDialog() throws System.AccessViolationException and quietly kills application if started by drag/drop

In a simple application (a skeleton put together with Visual Studio designer) I recently worked around an error that only manifests if I use drag/drop to execute the program.
Problem: I drag and drop a file onto my program. I later use File->SaveAs to save the contents of a rich text box. If I attempt to type a filename or drop down the hierarchy of directory paths, the application instantly quits. If I select a file displayed in the SaveAs dialog, this works fine (after accepting the overwrite).
Workaround: Add a try/catch block above the ShowDialog call in the saveAsToolStripMenuItem_Click function containing an attempt to open and write to a file. A simple fputs will cause the exception, which I then ignore and delete any file created in the process.
If someone knows a simpler (or cleaner!) solution, thanks in advance. Otherwise, here is my ugly solution:
private: System::Void saveAsToolStripMenuItem_Click(System::Object^ sender, System::EventArgs^ e)
{
static bool fFirstTime = true;
static char *sBogusFileName = "c:\\__$$TrashFileIMustCreateThenDelete";
SaveFileDialog^ dialog = gcnew SaveFileDialog();
{
System::Windows::Forms::DialogResult DR;
dialog->Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*";
dialog->FilterIndex = 2;
dialog->RestoreDirectory = true;
// dialog->InitialDirectory = "c:\\";
if (fFirstTime)
{
FILE *fp;
// This is a strange problem that came up on my Windows XP machine.
// The first time through this logic, I would get a System.AccessViolationException
// If I do it with this simple fputs, the error is recoverable.
// If it happens during the dialog->ShowDialog that follows, the application quietly exits.
// No event logs, nothing. This solved it for me.
try {
// The fopen that follows make the MS compiler unhappy. The filename is hard-coded, so I don't care.
#pragma warning( suppress: 4996 )
fp=fopen(sBogusFileName, "w");
fputs("A string that might never be actually written - this causes an exception on some machines\n", fp);
}
catch(Exception ^e)
{
(void)e; // Suppress another compiler error (unused variable)
// Debug code that I later removed. This logging is not required to fix the problem.
// RRCLOGB1("\nError attempting to write to a file:\n%s\n\n", e->ToString());
}
finally
{
fclose(fp);
}
remove(sBogusFileName);
fFirstTime = false; // Only cause the exception once...but why is it necessary?!?
}
DR = dialog->ShowDialog(this);
if ( DR == System::Windows::Forms::DialogResult::OK )
{
o
o
o

Reading XML: How to catch exceptions from ticpp::LoadFile()?

I am trying to read a XML file using ticpp::LoadFile(); It was not successfull and I did not know why because I have no idea how to catch the exceptions. Here is what I did:
try
{
// Load a document
ticpp::Document doc( pFilename );
doc.LoadFile();
// Get an element by chaining calls - no return values to check, no TiXmlHandle
ticpp::Element* pElem = doc.FirstChildElement()->NextSibling();
// do something useful here
}
catch( ticpp::Exception& ex )
{
// If any function has an error, execution will enter here.
// Report the error
std::cout << ex.what();
}
But I the ex actually shows grayed as if it is never declared. I am wondering how I can catch the exceptions in this case?
It turned out it was the file path that is messed up.
The code itself is fine.

How to handle JPEG #41 error

I have a project in C++ Builder6. There's an OpenDialog where I upload images to the project. I'd like my project to be safe and because it only accepts .jpg or .bmp images I decided to make a restriction. As far as I'm concerned I can recognize a .jpg file by setting my stream reader to the 4th position. If I find "JFIF" here, It'll be .jpeg file. And so on.
Here's my code
if(OpenDialog1->Execute())
{
TFileStream *stream = new TFileStream(OpenDialog1->FileName, fmOpenRead);
if(stream != NULL)
{
if(stream->Size < 10)
{
delete stream;
return;
}
char str[10];
stream->Read(str, 10);
if(AnsiString(str + 6).SetLength(4)=="JFIF")
{
ShowMessage("It's jpeg");
}
else if ( AnsiString(str).SetLength(2)=="BM") {
ShowMessage("It's bmp");
}
else
{
ShowMessage("It can not be downloaded");
return;
}
}
delete stream;
}
But unfortunately that code raises an exception about JPEG error #41 when I put here a text file with renamed extension.
So my idea doesn't work. The whole question is:
Can I make my program return my error messages without using try-catch method?
By the way, I understand why the exception is being raised, because my jpeg file is empty. But I'd like to handle it using my own system, not the standart exception.