iostream equivalent to snprintf(NULL, 0, format_string, args...) - c++

I want to find the number of characters that a stream formatting operation would produce without allocating memory from the heap. In C, it can be done with
int nchars = snprintf(NULL, 0, format_string, args...);
How can it be done within the ostream framework in C++?
An implementation with std::ostringstream may allocate memory from the heap:
template <class T>
int find_nchar(const T& value) {
std::ostringstream os; // may allocate memory from the heap
os << value;
return os.str().size(); // may allocate memory from the heap
}
I think I need to make a custom ostream class to achieve this. The custom ostream should respect all the formatting flags one can set for the normal std::ostream.
I am searching for a solution that only uses the C++ standard library, not boost::iostreams, for example.

Rather than a custom std::ostream it might be easier -- and perhaps more flexible -- to implement a custom std::streambuf that can then be used with any std::ostream.
#include <streambuf>
template <class CharT, class Traits = std::char_traits<CharT>>
struct counting_streambuf: std::basic_streambuf<CharT, Traits> {
using base_t = std::basic_streambuf<CharT, Traits>;
using typename base_t::char_type;
using typename base_t::int_type;
std::streamsize count = 0;
std::streamsize xsputn(const char_type* /* unused */, std::streamsize n)
override
{
count += n;
return n;
}
int_type overflow(int_type ch)
override
{
++count;
return ch;
}
};
Then use as...
#include <iostream>
int
main (int argc, char **argv)
{
using char_type = decltype(std::cout)::char_type;
counting_streambuf<char_type> csb;
/*
* Associate the counting_streambuf with std::cout whilst
* retaining a pointer to the original std::streambuf.
*/
auto *oldbuf = std::cout.rdbuf(&csb);
std::cout << "Some text goes here...\n";
/*
* Restore the original std::streambuf.
*/
std::cout.rdbuf(oldbuf);
std::cout << "output length is " << csb.count << " characters\n";
}
Running the above results in...
output length is 23 characters
Edit: The original solution didn't overload overflow. This works on Linux but not on Windows. Thanks go to Peter Dimov from Boost, who found the solution.

Related

convert std::array of bytes to hex std::string

I'd like a way to take an arbitrary-size array of bytes and return a hex string. Specifically for logging packets sent over the net, but I use the equivalent function that takes a std::vector a lot. Something like this, probably a template?
std::string hex_str(const std::array<uint8_t,???> array);
I've searched but the solutions all say "treat it as a C-style array" and I am specifically asking whether there's a way not to do that. I assume the reason this isn't in every single C++ FAQ is that it's impossible and if so can someone outline why?
I already have these overloads and the second one can be used for std::array by decaying into a C-style array, so please don't tell me how to do that.
std::string hex_str(const std::vector<uint8_t> &data);
std::string hex_str(const uint8_t *data, const size_t size);
(edit: vector is a reference in my code)
You should consider writing the function to work with iterators, like standard algorithms do. Then you can use it with both std::vector and std::array inputs, eg:
template<typename Iter>
std::string hex_str(Iter begin, Iter end)
{
std::ostringstream output;
output << std::hex << std::setw(2) << std::setfill('0');
while(begin != end)
output << static_cast<unsigned>(*begin++);
return output.str();
}
Online Demo
If you really want to avoid having to call begin()/end() on whatever container you pass in, you can define a helper to handle that for you, eg:
template<typename C>
std::string hex_str(const C &data) {
return hex_str(data.begin(), data.end());
}
Online Demo
Or, if you really wanted to, you can just flatten this all down into a single function, eg:
template <typename C>
std::string hex_str(const C& data)
{
std::ostringstream output;
output << std::hex << std::setw(2) << std::setfill('0');
for(const auto &elem : data)
output << static_cast<unsigned>(elem);
return output.str();
}
Online Demo
If you know the size of the std::array at compile time you can use a non type template parameter.
template<std::size_t N>
std::string hex_str( const std::array<std::uint8_t, N>& buffer )
{ /* Implementation */ }
int main( )
{
// Usage.
std::array<std::uint8_t, 5> bytes = { 1, 2, 3, 4, 5 };
const auto value{ hex_str( bytes ) };
}
Or you can just template the entire container (cut down on your overloads).
template<typename Container>
std::string hex_str( const Container& buffer )
{ /* Implementaion */ }
int main( )
{
// Usage.
std::array<std::uint8_t, 5> bytes = { 1, 2, 3, 4, 5 };
const auto value{ hex_str( bytes ) };
}

How can i create a istream from a uint8_t vector?

I am in the process of adding the ability to get data over the network to code that used to only read local files. The network library I am using sends and receives data in the form of a vector<uint8_t>. I'd like to be able to reuse the code that processes the data after reading the file, but that code expects a std::istream, is there a way to have an istream read the vector data? It's the same data so I feel like there should be a way, but i haven't been able to find or figure out code for how to do it.
current code:
std::ifstream stream("data.img", std::ios::in | std::ios::binary | std::ios::ate);
if (!stream.is_open())
{
throw std::invalid_argument("Could not open file.");
}
// the arg for processData is std::istream
processData(stream);
network framework:
vector<uint8_t> data = networkMessage.data;
// need some way to create istream from data
std::istream stream = ?
processData(stream);
stream.close();
Is there a way to do this, or am I barking up the wrong tree?
std::basic_istream gets its data from an associated std::basic_streambuf derived class. The STL provides such classes for file I/O and string I/O, but not for memory I/O or network I/O.
You could easily write (or find a 3rd party) memory-based streambuf class that uses the std::vector as its underlying buffer, and then you can construct an std::istream that uses that memory streambuf. For example (using the imemstream class from
this answer):
std::vector<uint8_t> &data = networkMessage.data;
imemstream stream(reinterpret_cast<const char*>(data.data()), data.size());
processData(stream);
Well C++ does actually have a class for this - istrstream, and you could use it like this:
vector<uint8_t> data = ...;
// need some way to create istream from data
std::istrstream stream(reinterpret_cast<const char*>(data.data()), data.size());
processData(stream);
As far as I can tell this doesn't copy the data, unlike the other answers. However it was also deprecated in C++98 because it's hard to know who is responsible for freeing the buffer, so you may want to write your own equivalent.
The istream is a reference of raw data. It doesn't hold the data, and just a visitor, by keeping some char* pointers of the begin and end of data memory address.
The storage in vector<> is continuous, but by using push_back(), the storage address may changed, (copied inner vector)
So it's possible to make an istream to const vector
The reference
https://en.cppreference.com/w/cpp/io/basic_istream
https://www.cplusplus.com/reference/streambuf/streambuf/
The shortest example
class vectorbuf : public std::streambuf {
public:
vectorbuf(std::vector<uint8_t> &v){
setg((char*)v.data(), (char*)v.data(), (char*)(v.data() + v.size()));
}
~vectorbuf() {}
};
//Usage:
vector<uint8_t> arr{11,12,13,14,15,16};
vectorbuf vbuf(arr);
std::istream is(&vbuf);
The full WRONG sample code
#include <streambuf>
#include <iostream>
#include <iomanip>
#include <vector>
using namespace std;
template<typename T>
class vectorbuf : public std::streambuf {
public:
vectorbuf(std::vector<T> &v) : _value(v) {
char *bptr = (char*)_value.data();
char *eptr = (char*)(_value.data() + _value.size());
setg(bptr, bptr, eptr);
cout<<"Setg: "<<(void*)bptr<<" "<<(void*)eptr<<endl;
}
~vectorbuf() {}
//Zone start ---
//Note: this zone of code can be commented since the virtual function in base class do same
protected:
virtual int underflow() {
char *bptr = (char*)_value.data();
char *new_eptr = (char*)(_value.data() + _value.size());
cout<<"[underflow() when gptr()="<<(void*)gptr()
<<", now_bptr="<<(void*)bptr<<" now_eptr="<<(void*)new_eptr<<"]";
return traits_type::eof();
//since the vector& must not modified, the code below is unnecessary.
if (new_eptr == egptr())
return traits_type::eof();
setg(bptr, gptr(), new_eptr);
return *gptr();
}
//Zone end ---
private:
std::vector<T> &_value;
};
int main() {
vector<int> arr{'a',12,13,14,15};
cout<<"The array: ";
for (int i=0; i<arr.size(); i++)
cout<<arr[i]<<" ";
cout<<endl;
cout<<" storage: ";
for (int i=0; i<arr.size()*sizeof(int); i++) {
char *ptr = (char*)arr.data();
cout<<static_cast<int>(ptr[i])<<" ";
}
cout<<endl;
vectorbuf<int> vbuf(arr);
std::istream is(&vbuf);
arr.push_back(16); //!!! wrong code here !!!
//the size of arr is 6*4 == 24, with sizeof(int)==4
for (int i=0; i<26; i++) {
cout<<"good?"<<is.good()
<<", fail?"<<is.fail()
<<", bad?"<<is.bad()
<<", eof?"<<is.eof()
<<", tellg="<<is.tellg();
//Note there must be char
//'int a' would not accepted and make is.fail() to true
//and std::noskipws is also importanted
char a;
is>>std::noskipws>>a;
int out = a;
cout<<", Read from arr: "<<out<<endl;
}
return 0;
}
You can do this via assigning the data to a std::string and use a std::istringstream bound to that (leaving aside the unsigned char to signed char conversion issues):
std::string s((char*)networkMessage.data(),networkMessage.size());
std::istringstream iss(s);
std::istream& stream = iss;
// ^ Note the reference here.
processData(stream);
stream.close();
This will work with vector of any type, not just uint8_t:
std
template <class T>
auto make_istringstream_std_1(const std::vector<T>& v) -> std::istringstream
{
using namespace std::string_literals;
std::string str;
for (auto& e : v)
{
str += std::to_string(e) + " "s;
}
// the trailing space is not an issue
return std::istringstream{str};
}
std algorithm
template <class T>
auto make_istringstream_std_2(const std::vector<T>& v) -> std::istringstream
{
std::stringstream ss;
std::copy(v.begin(), v.end(), std::ostream_iterator<int>(ss, " "));
// the trailing space is not an issue
return std::istringstream{ss.str()};
}
boost
template <class T>
auto make_istringstream_boost(const std::vector<T>& v) -> std::istringstream
{
using boost::adaptors::transformed;
using boost::algorithm::join;
return std::istringstream{
join(v | transformed([](int a) { return std::to_string(a); }), " ")};
}
attribution:
How to transform a vector<int> into a string?
A good example for boost::algorithm::join

C++ string formatting like Python "{}".format

I am looking for a quick and neat way to print in a nice table format with cells being aligned properly.
Is there a convenient way in c++ to create strings of substrings with certain length like python format
"{:10}".format("some_string")
In C++20 you'll be able to use std::format which brings Python-like formatting to C++:
auto s = std::format("{:10}", "some_string");
Until then you can use the open-source {fmt} formatting library, std::format is based on.
Disclaimer: I'm the author of {fmt} and C++20 std::format.
Try this https://github.com/fmtlib/fmt
fmt::printf("Hello, %s!", "world"); // uses printf format string syntax
std::string s = fmt::format("{0}{1}{0}", "abra", "cad");
You have many options here. For instance using streams.
source.cpp
std::ostringstream stream;
stream << "substring";
std::string new_string = stream.str();
#mattn was correct, the fmt library at https://github.com/fmtlib/fmt provides exactly this functionality.
The exciting news is this has been accepted into the C++20 standard.
You can use the fmt library knowing that it will be std::fmt in C++20
https://www.zverovich.net/2019/07/23/std-format-cpp20.html
https://en.cppreference.com/w/cpp/utility/format/format
you can quickly write a simple function to return a fixed length string.
We consider the str string is terminated by null, buf is already defined before calling the function.
void format_string(char * str, char * buf, int size)
{
for (int i=0; i<size; i++)
buf[i] = ' '; // initialize the string with spaces
int x = 0;
while (str[x])
{
if (x >= size) break;
buf[x] = str[x]; // fill up the string
}
buf[size-1] = 0; // termination char
}
Used as
char buf[100];
char str[] = "Hello";
format_string(str, buf, sizeof(buf));
printf(buf);
If you cannot use fmt as mentioned above the best way would be to use a wrapper class for formatting. Here is what I have done once:
#include <iomanip>
#include <iostream>
class format_guard {
std::ostream& _os;
std::ios::fmtflags _f;
public:
format_guard(std::ostream& os = std::cout) : _os(os), _f(os.flags()) {}
~format_guard() { _os.flags(_f); }
};
template <typename T>
struct table_entry {
const T& entry;
int width;
table_entry(const T& entry_, int width_)
: entry(entry_), width(static_cast<int>(width_)) {}
};
template <typename T>
std::ostream& operator<<(std::ostream& os, const table_entry<T>& e) {
format_guard fg(os);
return os << std::setw(e.width) << std::right << e.entry;
}
And then you would use it as std::cout << table_entry("some_string", 10). You can adapt table_entry to your needs. If you don't have class template argument deduction you could implement a make_table_entry function for template type deduction.
The format_guard is needed since some formatting options on std::ostream are sticky.

How to construct a std::string with embedded values, i.e. "string interpolation"?

I want to create a string with embedded information. One way (not the only way) of achieving what I want is called string interpolation or variable substitution, wherein placeholders in a string are replaced with actual values.
In C, I would do something like this:
printf("error! value was %d but I expected %d",actualValue,expectedValue)
whereas if I were programming in python, I would do something like this:
"error! value was {0} but I expected {1}".format(actualValue,expectedValue)
both of these are examples of string interpolation.
How can I do this in C++?
Important Caveats:
I know that I can use std::cout if I want to print such a message to standard output (not string interpolation, but prints out the kind of string I want):
cout << "error! value was " << actualValue << " but I expected "
<< expectedValue;
I don't want to print a string to stdout. I want to pass a std::string as an argument to a function (e.g. the constructor of an exception object).
I am using C++11, but portability is potentially an issue, so knowing which methods work and don't work in which versions of C++ would be a plus.
Edit
For my immediate usage, I'm not concerned about performance (I'm raising an exception for cryin' out loud!). However, knowing the relative performance of the various methods would be very very useful in general.
Why not just use printf itself (C++ is a superset of C after all...)? This answer discusses some reasons why not. As far as I can understand, type safety is a big reason: if you put %d, the variable you put in there had better really be convertible to an integer, as that's how the function figures out what type it is. It would be much safer to have a method which uses compile-time knowledge of the actual type of the variables to be inserted.
In C++20 you will be able to use std::format.
This will support python style formatting:
string s = std::format("{1} to {0}", "a", "b");
There is already an implementation available: https://github.com/fmtlib/fmt.
Method 1: Using a string stream
It looks like std::stringstream gives a quick solution:
std::stringstream ss;
ss << "error! value was " << actualValue << " but I expected " << expectedValue << endl;
//example usage
throw MyException(ss.str())
Positive
no external dependencies
I believe this works in C++ 03 as well as c++ 11.
Negative
reportedly quite slow
a bit more messy: you must create a stream, write to it, and then get the string out of it.
Method 2: Boost Format
The Boost Format library is also a possibility. Using this, you would do:
throw MyException(boost::format("error! value was %1% but I expected %2%") % actualValue % expectedValue);
Positive
pretty clean compared to stringstream method: one compact construct
Negative
reportedly quite slow: uses the stream method internally
it's an external dependency
Edit:
Method 3: variadic template parameters
It seems that a type-safe version of printf can be created by using variadic template parameters (the techincal term for a template that takes an indefinite number of template parameters). I have seen a number of possibilities in this vein:
This question gives a compact example and discusses performance problems with that example.
This answer to that question, whose implementation is also quite compact, but reportedly still suffers from performance issues.
The fmt library, discussed in this answer, is reportedly quite fast and seems to be as clean as printf itself, but is an external dependency
Positive
usage is clean: just call a printf-like function
The fmt library is reportedly quite fast
The other options seem quite compact (no external dependency required)
Negative
the fmt library, while fast, is an external dependency
the other options apparently have some performance issues
In C++11 you can use std::to_string:
"error! value was " + std::to_string(actualValue) + " but I expected " + std::to_string(expectedValue)
It's not pretty, but it's straightforward, and you can use a macro to shrink it a bit. Performance is not great, since you do not reserve() space beforehand. Variadic templates would probably be faster and look nicer.
This kind of string construction (instead of interpolation) is also bad for localization, but you'd probably use a library if you needed that.
Use whatever you like:
1) std::stringstream
#include <sstream>
std::stringstream ss;
ss << "Hello world!" << std::endl;
throw std::runtime_error(ss.str());
2) libfmt : https://github.com/fmtlib/fmt
#include <stdexcept>
throw std::runtime_error(
fmt::format("Error has been detected with code {} while {}",
0x42, "copying"));
C++17 solution that works both for std::string & for std::wstring (Tested on VS2019 & VS2022):
#include <string>
#include <stdexcept>
#include <cwchar>
#include <cstdio>
#include <type_traits>
template<typename T, typename ... Args>
std::basic_string<T> string_format(T const* const format, Args ... args)
{
int size_signed{ 0 };
// 1) Determine size with error handling:
if constexpr (std::is_same_v<T, char>) { // C++17
size_signed = std::snprintf(nullptr, 0, format, args ...);
}
else {
size_signed = std::swprintf(nullptr, 0, format, args ...);
}
if (size_signed <= 0) {
throw std::runtime_error("error during formatting.");
}
const auto size = static_cast<size_t>(size_signed);
// 2) Prepare formatted string:
std::basic_string<T> formatted(size, T{});
if constexpr (std::is_same_v<T, char>) { // C++17
std::snprintf(formatted.data(), size + 1, format, args ...); // +1 for the '\0' (it will not be part of formatted).
}
else {
std::swprintf(formatted.data(), size + 1, format, args ...); // +1 for the '\0' (it will not be part of formatted).
}
return formatted; // Named Return Value Optimization (NRVO), avoids an unnecessary copy.
}
// USE EXAMPLE: //
int main()
{
int i{ 0 };
const std::string example1 = string_format("string. number %d.", ++i); // => "string. number 1."
const std::wstring example2 = string_format(L"wstring. number %d.", ++i); // => L"wstring. number 2."
}
DISCLAIMER:
The subsequent code is based on an article I read 2 years ago. I will find the source and put it here ASAP.
This is what I use in my C++17 project. Should work with any C++ compiler supporting variadic templates though.
Usage:
std::string const word = "Beautiful";
std::string const message = CString::format("%0 is a %1 word with %2 characters.\n%0 %2 %0 %1 %2", word, "beautiful", word.size());
// Prints:
// Beautiful is a beautiful word with 9 characters.
// Beautiful 9 Beautiful beautiful 9.
The class implementation:
/**
* The CString class provides helpers to convert 8 and 16-bit
* strings to each other or format a string with a variadic number
* of arguments.
*/
class CString
{
public:
/**
* Format a string based on 'aFormat' with a variadic number of arbitrarily typed arguments.
*
* #param aFormat
* #param aArguments
* #return
*/
template <typename... TArgs>
static std::string format(
std::string const&aFormat,
TArgs &&...aArguments);
/**
* Accept an arbitrarily typed argument and convert it to it's proper
* string representation.
*
* #tparam TArg
* #tparam TEnable
* #param aArg
* #return
*/
template <
typename TArg,
typename TEnable = void
>
static std::string toString(TArg const &aArg);
/**
* Accept a float argument and convert it to it's proper string representation.
*
* #tparam TArg
* #param arg
* #return
*/
template <
typename TArg,
typename std::enable_if<std::is_floating_point<TArg>::value, TArg>::type
>
static std::string toString(const float& arg);
/**
* Convert a string into an arbitrarily typed representation.
*
* #param aString
* #return
*/
template <
typename TData,
typename TEnable = void
>
static TData const fromString(std::string const &aString);
template <
typename TData,
typename std::enable_if
<
std::is_integral<TData>::value || std::is_floating_point<TData>::value,
TData
>::type
>
static TData fromString(std::string const &aString);
private:
/**
* Format a list of arguments. In this case zero arguments as the abort-condition
* of the recursive expansion of the parameter pack.
*
* #param aArguments
*/
template <std::size_t NArgs>
static void formatArguments(std::array<std::string, NArgs> const &aArguments);
/**
* Format a list of arguments of arbitrary type and expand recursively.
*
* #param outFormatted
* #param inArg
* #param inArgs
*/
template <
std::size_t NArgs,
typename TArg,
typename... TArgs
>
static void formatArguments(
std::array<std::string, NArgs> &aOutFormatted,
TArg &&aInArg,
TArgs &&...aInArgs);
};
//<-----------------------------------------------------------------------------
//<-----------------------------------------------------------------------------
//<
//<-----------------------------------------------------------------------------
template <typename... TArgs>
std::string CString::format(
const std::string &aFormat,
TArgs &&...aArgs)
{
std::array<std::string, sizeof...(aArgs)> formattedArguments{};
formatArguments(formattedArguments, std::forward<TArgs>(aArgs)...);
if constexpr (sizeof...(aArgs) == 0)
{
return aFormat;
}
else {
uint32_t number = 0;
bool readNumber = false;
std::ostringstream stream;
for(std::size_t k = 0; k < aFormat.size(); ++k)
{
switch(aFormat[k])
{
case '%':
readNumber = true;
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
// Desired behaviour to enable reading numbers in text w/o preceding %
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
if(readNumber)
{
number *= 10;
number += static_cast<uint32_t>(aFormat[k] - '0');
break;
}
default:
if(readNumber)
{
stream << formattedArguments[std::size_t(number)];
readNumber = false;
number = 0;
}
stream << aFormat[k];
break;
#pragma GCC diagnostic warning "-Wimplicit-fallthrough"
}
}
if(readNumber)
{
stream << formattedArguments[std::size_t(number)];
readNumber = false;
number = 0;
}
return stream.str();
}
}
//<-----------------------------------------------------------------------------
//<-----------------------------------------------------------------------------
//<
//<-----------------------------------------------------------------------------
template <typename TArg, typename enable>
std::string CString::toString(TArg const &aArg)
{
std::ostringstream stream;
stream << aArg;
return stream.str();
}
//<-----------------------------------------------------------------------------
//<-----------------------------------------------------------------------------
//<
//<-----------------------------------------------------------------------------
template <
typename TArg,
typename std::enable_if<std::is_floating_point<TArg>::value, TArg>::type
>
std::string CString::toString(const float& arg)
{
std::ostringstream stream;
stream << std::setprecision(12) << arg;
return stream.str();
}
//<-----------------------------------------------------------------------------
//<-----------------------------------------------------------------------------
//<
//<-----------------------------------------------------------------------------
template <std::size_t argCount>
void CString::formatArguments(std::array<std::string, argCount> const&aArgs)
{
// Unused: aArgs
}
//<-----------------------------------------------------------------------------
//<-----------------------------------------------------------------------------
//<
//<-----------------------------------------------------------------------------
template <std::size_t argCount, typename TArg, typename... TArgs>
void CString::formatArguments(
std::array<std::string, argCount> &outFormatted,
TArg &&inArg,
TArgs &&...inArgs)
{
// Executed for each, recursively until there's no param left.
uint32_t const index = (argCount - 1 - sizeof...(TArgs));
outFormatted[index] = toString(inArg);
formatArguments(outFormatted, std::forward<TArgs>(inArgs)...);
}
//<-----------------------------------------------------------------------------
//<-----------------------------------------------------------------------------
//<
//<-----------------------------------------------------------------------------
template <
typename TData,
typename std::enable_if
<
std::is_integral<TData>::value || std::is_floating_point<TData>::value,
TData
>::type
>
TData CString::fromString(std::string const &aString)
{
TData const result{};
std::stringstream ss(aString);
ss >> result;
return result;
}
//<-----------------------------------------------------------------------------
If you don't mind using a preprocessor script, here is a more easy but handy solution: https://github.com/crazybie/cpp_str_interpolation. Then you can write the code like this:
string s1 = "world", s2 = "!";
cout << _F("hello, {s1+s2}") << endl;
it also support using like a template engine:
int a = 1;
float b = 2.3f;
cout << _F(R"(
`for (int i=0; i<2; i++) {`
a is {a}, i is {i}.
a+i is {a+i}.
`}`
b is {b}.
cout << "123" << endl;`
)") << endl;
I've grown very fond of this solution, std::format notwithstanding. I dislike it on several counts (use of macros, and the whole concept of operator << overloading). But the ease of use truly makes up for it.
#ifndef SS_HPP
#define SS_HPP
#include <sstream>
#include <iostream>
// usage: SS("xyz" << 123 << 45.6) returning a std::string rvalue.
#define SS(x) ( ((std::stringstream&)(std::stringstream() << x )).str())
#endif

Printing chars as Integers

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?
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.
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?
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;
}
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.