1

I'm confused about char * and const char *. In my example I'm not sure how to put them together. I have several const char * strings I would like to concatenate to a final const char * string.

struct MyException : public std::exception
{
  const char *source;
  int number;
  const char *cause;

  MyException(const char *s, int n)
      : source(s), number(n) {}
  MyException(const char *s, const char *c)
      : source(s), number(0), cause(c) {}

  const char *what() const throw()
  {
    if (number != 0) {
      char buffer[1024];
      // why does this not work?
      cause = strerror_r(number, buffer, 1024);
    }

    // how to concatenate the strings?
    return source + ": " + cause;
  }
};
multiholle
  • 3,050
  • 8
  • 41
  • 60

6 Answers6

5

You can store a std::string and still return a const char * from your what function.

struct MyException : public std::exception
{
private:
  std::string message;  

public:    
  MyException(const char *s, int n) {
    char buffer[1024];
    strerror_r(n, buffer, 1024);
    message.reserve(strlen(s) + 2 + strlen(buffer));
    message = s;
    message += ": ";
    message += buffer;
  }

  MyException(const char *s, const char *c) {
    message.reserve(strlen(s) + 2 + strlen(c));
    message = s;
    message += ": ";
    message += c;
  }

  const char *what() const throw()
  {
    return message.c_str();
  }
};
Benjamin Lindley
  • 101,917
  • 9
  • 204
  • 274
2

Just use strcat() and strcpy() function from string.h.

http://www.cplusplus.com/reference/clibrary/cstring/strcat/ http://www.cplusplus.com/reference/clibrary/cstring/strcpy/

Also, since you don't have to modify original strings, the difference between const char* and char* doesn't matter.

Also don't forget to malloc() (reserve the space for) the required size of destination string.

Rok Kralj
  • 46,826
  • 10
  • 71
  • 80
  • 1
    +1 for suggesting strcat instead of std:string. I'm old school. – Rafael Baptista Jul 26 '12 at 20:05
  • 2
    You would leave the catcher with the responsibility of freeing the memory? – Benjamin Lindley Jul 26 '12 at 20:13
  • Many times malloc is imminent. – Rok Kralj Jul 26 '12 at 20:15
  • What if you lose the original strings before getting to `what()`? Or can it just be used with literals and global variables? This creates an interface that is just terrible. Using a std::string solves most of this. – Bo Persson Jul 26 '12 at 20:58
  • If I'm not mistaken, the OP asked explicitly about the char* strings. – Rok Kralj Jul 26 '12 at 21:14
  • He did. But I think as long as the input (the arguments to the constructor) is the same (two `char*`, or a `char*` and an `int`), and the output (the return value of what()) is a `const char*`, it fits the requirements. If a `std::string` is used as an intermediary, that should be okay, and preferred if it makes the code's interface 14 times better. – Benjamin Lindley Jul 27 '12 at 05:33
2

This is how I'd implement this:

struct MyException : public std::exception
{
public:
  const char *source;
  int number;
  const char *cause;
private:
  char buffer[1024]; // #1
  std::string message; // #2

  std::string build_message() {
    if (number != 0) {
      cause = strerror_r(number, buffer, 1024); // use the member buffer
    }
    std::string s; // #3
    s.reserve(strlen(source) + 2 + strlen(cause));
    return s + source + ": " + cause;
  }

public:
  MyException(const char *s, int n)
      : source(s), number(n), cause(), message(build_message()) {}
  MyException(const char *s, const char *c)
      : source(s), number(0), cause(c), message(build_message()) {}

  const char *what() const throw()
  {
    return message.c_str(); // #4
  }
};

Things to note:

  1. The original code was using a local variable for a buffer. That is a bad idea, as the pointer stored in cause would be invalid the moment the scope ends.

  2. For the concatenated message, dynamic allocation would be required. And that also means that cleanup of that storage would be required. I grabbed an existing tool that does that and provides string-like operations: std::string.

  3. With std::string concatenation can be done with the + operator. Note how I asked it to reserve memory for the expected size. This is memory an optimization, and is not required: the string would allocate enough memory either way.

  4. what cannot throw an exception, otherwise a call std::unexpected would arise. So the string cannot be allocated here.

Community
  • 1
  • 1
R. Martinho Fernandes
  • 228,013
  • 71
  • 433
  • 510
1

If you must work with char* pointers, you will want to use strcat. strcat takes two arguments a char* and a const char* and appends the string pointed to by the const char* onto the char*. This means you first need to copy your first string over.

You'll want to do something like this:

char* Concatenate(const char* first, const char* second)
{
  char* mixed = new char[strlen(first) + strlen(second) + 2 /* for the ': ' */ + 1 /* for the NULL */];
  strcpy(mixed, first);
  strcat(mixed, ": ");
  strcat(mixed, second);

  return mixed;
}

Isn't that just ugly? And, remember, because you've dynamically allocated the char* returned by that function the caller must remember to delete[] it. This ugliness and the need to ensure the caller cleans up in the right way is why you're better off using a string implementation such as std::string.

Jack Aidley
  • 19,439
  • 7
  • 43
  • 70
0

Allocate a buffer of size strlen(source) + strlen(cause) + 3 and use sprintf to create your message. Actually you can move this code to constructor so that what becomes simple getter.

tumdum
  • 1,981
  • 14
  • 19
0

If you really must use c-strings, you should look at strcat() to concatenate them together. However, since you are creating a custom exception, it would be reasonable to consider using std::string instead because it is more friendly to use in C++.

Code-Apprentice
  • 81,660
  • 23
  • 145
  • 268