8

I want to control whether my ostream outputting of chars and unsigned char's via << writes them as characters or integers. I can't find such an option in the standard library. For now I have reverted to using multiple overloads on a set of alternative print functions

ostream& show(ostream& os, char s) { return os << static_cast<int>(s); }
ostream& show(ostream& os, unsigned char s) { return os << static_cast<int>(s); }

Is there a better way?

James
  • 24,676
  • 13
  • 84
  • 130
Nordlöw
  • 11,838
  • 10
  • 52
  • 99
  • 1
    Do you want always to print chars as integers or depending on a condition? – Andriy Jun 08 '12 at 14:13
  • I want it to depend on a condition (state) similar to the `ios` state flags. – Nordlöw Jun 08 '12 at 14:17
  • 4
    I don't understand the necessity to distinguish signed and unsigned char. If you want to output it as a number, cast it as an int first. Otherwise, just print it to os. – Neil Jun 08 '12 at 14:18
  • Maybe it is possible to write your own io manipulator for this... – PlasmaHH Jun 08 '12 at 14:26
  • 1
    `cout << static_cast(some_char_val);`? – John Dibling Jun 08 '12 at 14:38
  • @JohnDibling: static_cast should be used when type and precision is known in advance but not in generic template algorithms, which is what I want this for. We could type cast everything to `uint64_t` to minimize risk of overflow but what if argument is an arbitrary precision integer or some other class no convertible to `uintxx_t`? – Nordlöw Jun 08 '12 at 14:50
  • You said this was for `char`s and `unsigned char`s. – John Dibling Jun 08 '12 at 14:52
  • @JohnDibling: Ohh, sorry about that. – Nordlöw Jun 08 '12 at 14:53

4 Answers4

1

No, there isn't a better way. A better way would take the form of a custom stream manipulator, like std::hex. Then you could turn your integer printing off and on without having to specify it for each number. But custom manipulators operate on the stream itself, and there aren't any format flags to do what you want. I suppose you could write your own stream, but that's way more work than you're doing now.

Honestly, your best bet is to see if your text editor has functions for making static_cast<int> easier to type. I assume you'd otherwise type it a lot or you wouldn't be asking. That way someone who reads your code knows exactly what you mean (i.e., printing a char as an integer) without having to look up the definition of a custom function.

Michael Kristofik
  • 34,290
  • 15
  • 75
  • 125
1

Just an update to an old post. The actual trick is using '+'. Eg:

template <typename T>
void my_super_function(T x)
{
  // ...
  std::cout << +x << '\n';  // promotes x to a type printable as a number, regardless of type
  // ...
}

In C++11 you could do:

template <typename T>
auto promote_to_printable_integer_type(T i) -> decltype(+i)
{
  return +i;
}

Credit: How can I print a char as a number? How can I print a char* so the output shows the pointer’s numeric value?

malat
  • 12,152
  • 13
  • 89
  • 158
0

I have a suggestion based on the technique used in how do I print an unsigned char as hex in c++ using ostream?.

template <typename Char>
struct Formatter
  {
  Char c;
  Formatter(Char _c) : c(_c) { }

  bool PrintAsNumber() const
    {
    // implement your condition here
    }
  };

template <typename Char> 
std::ostream& operator<<(std::ostream& o, const Formatter<Char>& _fmt)
  {
  if (_fmt.PrintAsNumber())
    return (o << static_cast<int>(_fmt.c));
  else
    return (o << _fmt.c);
  }

template <typename Char> 
Formatter<Char> fmt(Char _c)
  {
  return Formatter<Char>(_c);
  }

void Test()
  {
  char a = 66;
  std::cout << fmt(a) << std::endl;
  }
Community
  • 1
  • 1
Andriy
  • 8,486
  • 3
  • 27
  • 51
  • You realize of course that you wrote 23 lines of code (excluding tester) in order to be able to print a character as a number, right? Isn't this a bit overkill? – Neil Jun 08 '12 at 14:52
0

In C++20 you'll be able to use std::format to do this:

unsigned char uc = 42;
std::cout << std::format("{:d}", uc); // format uc as integer 42 (the default)
std::cout << std::format("{:c}", uc); // format uc as char '*' (assuming ASCII)

In the meantime you can use the {fmt} library, std::format is based on.

Disclaimer: I'm the author of {fmt} and C++20 std::format.

vitaut
  • 49,672
  • 25
  • 199
  • 336