c++exceptionconcatenationstdstringconst-char

custom exception class, using a base class, with multiple arguments


So I'm setting up custom exception classes for a program I am writing. I'm creating a catch-all base class, that I will use primarily as a generic exception. This base class will be inherited by several other custom exceptions. Here is the base class and one of the additional exception classes, there will be 10+ child classes that inherit from the parent class.

#include <exception>
#include <string>
#include <string.h>

class AgentException : public std::exception
{

protected:
  char *msg;

public:
  AgentException() : msg("AgentException"){};
  AgentException(char *m) : msg(m){};
  AgentException(char *m, std::string d)
  {

    strcat(m, d.c_str()); //aware this fails. its a segmentation fault
  };
  ~AgentException() = default;

  const char *what() const throw()
  {

    return (const char *)msg;
  }
};

class ConnectionFailed : public AgentException
{

private:
  std::string eType = "ConnectionFailed";

public:
  ConnectionFailed() : AgentException("ConnectionFailed"){};
  ConnectionFailed(std::string d) : AgentException("ConnectionFailed: ", d){};
  ~ConnectionFailed() = default;
};

I'm aware with the above code that what() will not currently return anything as the member variable is not being assigned. I left that out, because I'm getting a segmentation fault from the strcat() call.

I've created multiple constructors for the parent class, as there will be times that I want the default value, a single value, or even two arguments passed. In the case of the child class, it will always be passing at least the class ID to the parent, where in some cases I may need to pass a string variable along with the class id. The string variable, std::string, is a must. Those were the directives I was given to use.

Originally I set up all message variables as std::string within the classes, but I ultimately ran into a problem related to the what() function. I couldn't figure out how to convert the std::string to return as a const char*. After doing some research, I found out that it was a bad idea to utilize strings within an exception class, as what's going to catch any exceptions that may occur within

So I converted everything back to const char*, but now I can't seem to get a return from what(). The problems all stem from my inability to figure out the concatenation of the different types.

I can get something working passably with this change to the AgentException class.

protected:
  char msg[100];

public:
  // AgentException() : msg("AgentException"){};
  // AgentException(char *m) : msg(m){};
  AgentException(const char *m, std::string d)
  {

    strcpy(msg, m);
    strcat(msg, d.c_str());
    
  };

I can make this change work overall, but it doesn't feel like it's the correct way of doing this. Could someone provide me some insight into the changes they would make to this setup?

I'm currently testing by throwing either the AgentException or ConnectionFailed exception and catching with the Base AgentException. I've been rotating to see if there are any different reactions.

try
  {
    throw ConnectionFailed("test");
  }
  catch (const AgentException &e)
  {

    std::cout << "----------------" << std::endl;
    std::cerr << e.what() << '\n';
    std::cout << "_________________" << std::endl;
  }

Solution

  • As Silvio mentioned in another comment, you can't use strcat if the buffer can't contain the resulting string. In this case, you also have the problem that you're passing a const char * to strcat which is a no no (literal strings are const).

    I suggest that you change your msg variable to a std::string. It will then be able to grow to accommodate the concatenated message, and manage the memory for you.

    class AgentException : public std::exception
    {
    
    protected:
      std::string msg;
    
    public:
      AgentException() : msg("AgentException") {};
      AgentException(const char *m) : msg(m){};
      AgentException(const char *m, const std::string& d)
      {
          msg = m + d;
      };
      ~AgentException() = default;
    
      const char *what() const throw()
      {
          return msg.c_str();
      }
    };
    
    class ConnectionFailed : public AgentException
    {
    public:
      ConnectionFailed() : AgentException("ConnectionFailed"){};
      ConnectionFailed(const std::string& d) : AgentException("ConnectionFailed: ", d){};
      ~ConnectionFailed() = default;
    };