Adam Lee
Adam Lee

Reputation: 25748

what's the best practice to print out(print debugging information) for a class?

I am wondering what's the best practice to print out a class (say classA), I have several method

classA ca;

1) Define a debug method and inside this method , print out all its members.

2) Define a str() method, and use cout<< ca.str()

3) Define something like string conversion(I am not sure how yet), and just use cout << ca

Upvotes: 0

Views: 441

Answers (2)

Ali Kazmi
Ali Kazmi

Reputation: 1458

clog can be an option if you want to log your steps of a process in some file and review the file later to see what goes wrong. Also you can check state of data members at certain interval by logging them externally in file.

Upvotes: 0

Wintermute
Wintermute

Reputation: 44043

The usual way is to overload operator<< roughly like this:

std::ostream &operator<<(std::ostream &out, classA const &a) {
  // precise formatting depends on your use case. Do the same thing you would
  // if you wanted to print a to cout or a file, because that is what this code
  // will end up doing.
  out << a.some_data() << ", " << a.some_more_data();
  return out;
}

If classA has a limited interface (does not expose all relevant data members), it may be necessary to make the operator<< a friend of classA, such as

class A {
public:
  // stuff

  friend operator<<(std::ostream &out, classA const &a);
};

// private members can then be accessed
std::ostream &operator<<(std::ostream &out, classA const &a) {
  out << a.some_private_member << ", " << a.some_other_private_member;
  return out;
}

There is not usually a good reason to prevent read access to private members that you then enable the user to dump out per operator<<, though, since it would be rather leaky access control.

This then enables you to write

classA a;
std::cout << a;

std::ofstream file("foo.txt");
file << a;

std::ostringstream fmt;
fmt << a;
std::string s = fmt.str();

and so forth.

As a style note: It is possible to write

std::ostream &operator<<(std::ostream &out, classA const &a) {
  // precise formatting depends on your use case
  return out << a.some_data() << ", " << a.some_more_data();
}

This achieves the same thing as the split return because operator<< (by convention) returns the same stream object that was passed into it (to enable the chaining of << as in std::cout << i << j << k;).

Style note 2: If there is nothing in classA that makes it difficult, an upgrade to this technique is to write

template<typename char_type>
std::basic_ostream<char_type> &operator<<(std::basic_ostream<char_type> &out, classA const &a) {
  // rest as before. Put it in a header because it is a function template now.
}

This enables you to write classA objects not only to cout, cerr, clog, ofstream, ostringstream etc., but also to their wchar_t counterparts wcout, wcerr, wclog, wofstream, and wostringstream. These are rarely used in practice, but often it will cost you nothing to implement this feature. The trick is that std::ostream and std::wostream -- base classes of all these output streams -- are aliases for std::basic_ostream<char> and std::basic_ostream<wchar_t>, respectively. That gives us this nice way to handle both (and potentially other) character classes without code duplication.

Upvotes: 3

Related Questions