-1

I would like to modify PierreBdR's exception example of how to make a custom exception so that it is visible to other classes. His example works for me but the exception is visible only inside that class.

I'm using MS Visual Studio 2013 and I'm a C++ newbie encumbered by a Java mentality.

Here is what is in the .hpp file:


struct IllegalArgumentException : public std::exception
{
public: IllegalArgumentException(std::string ss);
public: ~IllegalArgumentException();

const char* IllegalArgumentException::what()  { return s.c_str(); }

private: std::string s;

};

Here is part of the .cpp file:


IllegalArgumentException::IllegalArgumentException(std::string ss) : s(ss) {}

IllegalArgumentException::~IllegalArgumentException() {}

const char * IllegalArgumentException::what()  { return s.c_str(); }

I'm getting:

  • error C2011: 'IllegalArgumentExcpetion': 'struct' type redefinition (.hpp file)
Community
  • 1
  • 1
ssimm
  • 1,830
  • 3
  • 14
  • 30

4 Answers4

2

Point 1

const char* IllegalArgumentException::what()  { return s.c_str(); }

is declared incorrectly if declared inside a class or struct. Since the declaration is being made inside the IllegalArgumentException class, IllegalArgumentException:: is implied and messes with the compiler because the compiler now thinks you are declaring something else. You want

const char* what()  { return s.c_str(); }

In addition, the { return s.c_str(); } portion implements the function, so there is no need to implement it in the cpp file.

Point 2

Everything in a struct is public unless declared following the private keyword. This is the opposite of a class where everything is private unless stated otherwise. A class and a struct are pretty much identical other than the difference on default access.

Point 3

In C++ you can declare access level of members in blocks. There is no need to declare the access level of members one at a time.

struct IllegalArgumentException : public std::exception 
{ 
    // these are all public by default in a struct
    IllegalArgumentException(std::string ss); 
   ~IllegalArgumentException(); 
    const char* IllegalArgumentException::what() { return s.c_str(); } 
private: // everything after this is private
    std::string s; 
    int example; 
}; 

or

class IllegalArgumentException : public std::exception 
{ 
public: // these are all private by default in a class and need to be public
    IllegalArgumentException(std::string ss); 
   ~IllegalArgumentException(); 
    const char* IllegalArgumentException::what() { return s.c_str(); }
private: // switch back to private
    std::string s; 
    int example; 
}; 

or

class IllegalArgumentException : public std::exception 
{ 
    // these are all private by default in a class
    std::string s; 
    int example; 
public: // everything after this is public
    IllegalArgumentException(std::string ss); 
   ~IllegalArgumentException(); 
    const char* IllegalArgumentException::what() { return s.c_str(); }
}; 

Point 4

IllegalArgumentException::~IllegalArgumentException() {}

doesn't do anything. It doesn't need to do anything so the Rule of Zero recommends against having a destructor at all. The compiler will create it for you. If you don't have to write it, don't write it because code that doesn't exist has no bugs.

class IllegalArgumentException : public std::exception 
{ 
    // these are all private by default 
    std::string s; 
    int example; 
public: // everything after this is public
    IllegalArgumentException(std::string ss); 
    const char* IllegalArgumentException::what() { return s.c_str(); }
}; 

Point 5

Stealing from KerrekSB here because it's a point that OP had another question on. Use Include Guards

Include guards prevent a header from being included multiple times in the same translation unit. This is a problem because of bloat and the possibility of the same thing being defined or declared more than once leading to confusion about which is the real one.

A simple header guard:

#ifndef ILLEGALARGUMENTEXCEPTION_H // if we've never seen ILLEGALARGUMENTEXCEPTION_H 
                                   // before, do the following
#define ILLEGALARGUMENTEXCEPTION_H // OK we've seen it now!

// all subsequent includes of IllegalArgumentException.h will have seen 
// ILLEGALARGUMENTEXCEPTION_H and fail the ifndef, skipping everything 
// until it finds the closing #endif

#include <string>
#include <exception>
class IllegalArgumentException : public std::exception 
{ 
    // these are all private by default 
    std::string s; 
    int example; 
public: // everything after this is public
    IllegalArgumentException(std::string ss); 
    const char* IllegalArgumentException::what() { return s.c_str(); }
}; 

#endif // end of Include Guard

You may also use #pragma once, but be warned that #pragma means non-standard compiler extension. once may not exist for your compiler and if it doesn't, the compiler is allowed to skip the instruction without telling you!

There are many reasons why once is not in the standard, most important is it has unresolved fail cases. Use it with caution.

Community
  • 1
  • 1
user4581301
  • 29,019
  • 5
  • 26
  • 45
  • It's very kind of you to have given me all these good tips (I've reread it several times). But I must reward the answer to czimbortibor because his answer better fit the ideals of minimalist and verifiable. – ssimm Jan 19 '17 at 17:59
  • @ssimm Not a problem. The only point immediately relevant to the question is point 1 and czimbortibor explains it in a bit more detail. – user4581301 Jan 19 '17 at 18:14
1

You mixed up the what() function's declaration and definition. You tried refer to the function with the scope resolution operator (i.e ::). In this case it should be done in the definition (i.e in the .cpp file)

Also the what() function was implemented as an inline function in your .hpp file, which means that you don't need to define it again in the .cpp file. Therefore you got a redefinition of ‘const char* IllegalArgumentException::what() error.

P.S: In C++ class declarations one does not need to specify every attribute/method's access modifier, you can just group them under a single modifier.

.hpp file:

struct IllegalArgumentException : public std::exception
{
public: 
 IllegalArgumentException(std::string ss);
~IllegalArgumentException();

const char* what()  { return s.c_str(); }

private: 
 std::string s;

};

.cpp file:

IllegalArgumentException::IllegalArgumentException(std::string ss) : s(ss) {}

IllegalArgumentException::~IllegalArgumentException() {}
0

I am not sure, but I can comment so...

I think your "what" function is two times implemented: First in your hpp file and then in the cpp file.

César HM
  • 90
  • 2
  • 16
0

It seems you are possibly, perhaps indirectly, including the .hpp file more than once?

#include is a fairly primitive way to share code among several .cpp files: it tells the compiler (the preprocessor, specifically) to import the contents of the .hpp right there at that spot in the code. Then in a later pass, the compiler will compile the entire thing as if it is all one file. (There's a whole crazy turing-complete language for that preprocessor step; IMO you should avoid it as much as possible, mostly only using it for the stuff outlined in this note.)

One problem with this approach is that if you end up importing one .hpp file more than once – a very common thing to do, since .hpp files typically include other .hpp files – you will have that code repeated. It's illegal to define a struct or class more than once, so you get the error about "type redefinition".

The solution is to use include guards in every single .hpp file. At the very top, use lines like:

#ifndef SOME_STRING_THAT_UNIQUELY_IDENTIFIES_THIS_HEADER
#define SOME_STRING_THAT_UNIQUELY_IDENTIFIES_THIS_HEADER

Then at the very bottom of the .hpp file:

#endif  // SOME_STRING_THAT_UNIQUELY_IDENTIFIES_THIS_HEADER

Those preprocessor directives, #ifndef, #define, etc will remove any text in the middle if the big unique string is already defined. Usually your codebase will establish conventions for that big string, such as encoding the path into it. My company even uses a lint tool to make sure you encoded it correctly.

This ensures that if you #include a .hpp twice, or #include another .hpp that includes an already-included .hpp, you will get the contents of the .hpp file exactly once in the final preprocessor output.

This, and the preprocessor in general, is one of the less charming parts of C++ that really shows its age. Soon the language will be getting a much more modern and pleasant system for sharing interfaces called "modules". In the meantime, each .hpp needs these 3 lines of boilerplate.

ech
  • 118
  • 1
  • 5