#include <iostream>
#include <iomanip>
#include <string>
#include <sstream>
#include <climits>
#include <type_traits>
#include <cstdint>

// -----

// First option, with `<< "0x"`.
namespace ZeroX {
	namespace detail {
	    constexpr int HEX_DIGIT_BITS = 4;
	
	    template<typename T> struct is_char
	      : std::integral_constant<bool,
	                               std::is_same<T, char>::value ||
	                               std::is_same<T, signed char>::value ||
	                               std::is_same<T, unsigned char>::value> {};
	}
	
	template<typename T>
	std::string hex_out_s(T val) {
	    using namespace detail;
	
	    std::stringstream sformatter;
	    sformatter << std::hex
	               << std::internal
	               << "0x"
	               << std::setfill('0')
	               << std::setw(sizeof(T) * CHAR_BIT / HEX_DIGIT_BITS)
	               << (is_char<T>::value ? static_cast<int>(val) : val);
	
	    return sformatter.str();
	}
} // ZeroX

// -----

// Second option, with `<< std::showbase`.
namespace ShowBase {
	namespace detail {
	    constexpr int HEX_DIGIT_BITS = 4;
	    constexpr int HEX_BASE_CHARS = 2;
	
	    template<typename T> struct is_char
	      : std::integral_constant<bool,
	                               std::is_same<T, char>::value ||
	                               std::is_same<T, signed char>::value ||
	                               std::is_same<T, unsigned char>::value> {};
	}
	
	template<typename T>
	std::string hex_out_s(T val) {
	    using namespace detail;
	
	    std::stringstream sformatter;
	    sformatter << std::hex
	               << std::internal
	               << std::showbase
	               << std::setfill('0')
	               << std::setw((sizeof(T) * CHAR_BIT / HEX_DIGIT_BITS) + HEX_BASE_CHARS)
	               << (is_char<T>::value ? static_cast<int>(val) : val);
	
	    return sformatter.str();
	}
} // ShowBase

// -----

int main() {
	uint32_t       hexU32 = 0x0f;
	int            hexI   = 0x3c;
	unsigned short hexUS  = 0x12;
	char           hexC   = 0xf;
	unsigned char  hexUC  = 0xa;
	signed char    hexSC  = 0x3;

	std::cout << std::left
			  << std::setw(22) << "uint32_t:"
			     << std::setw(18) << "0x version: "       << ZeroX::hex_out_s(hexU32)
			  << std::setw(23) << '\n'
			     << std::setw(18) << "showbase version: " << ShowBase::hex_out_s(hexU32)

			  << std::setw(24) << "\n\nint:"
			     << std::setw(18) << "0x version: "       << ZeroX::hex_out_s(hexI)
			  << std::setw(23) << '\n'
			     << std::setw(18) << "showbase version: " << ShowBase::hex_out_s(hexI)

			  << std::setw(24) << "\n\nunsigned short:"
			     << std::setw(18) << "0x version: "       << ZeroX::hex_out_s(hexUS)
			  << std::setw(23) << '\n'
			     << std::setw(18) << "showbase version: " << ShowBase::hex_out_s(hexUS)

			  << std::setw(24) << "\n\nchar:"
			     << std::setw(18) << "0x version: "       << ZeroX::hex_out_s(hexC)
			  << std::setw(23) << '\n'
			     << std::setw(18) << "showbase version: " << ShowBase::hex_out_s(hexC)

			  << std::setw(24) << "\n\nunsigned char:"
			     << std::setw(18) << "0x version: "       << ZeroX::hex_out_s(hexUC)
			  << std::setw(23) << '\n'
			     << std::setw(18) << "showbase version: " << ShowBase::hex_out_s(hexUC)

			  << std::setw(24) << "\n\nsigned char:"
			     << std::setw(18) << "0x version: "       << ZeroX::hex_out_s(hexSC)
			  << std::setw(23) << '\n'
			     << std::setw(18) << "showbase version: " << ShowBase::hex_out_s(hexSC)

			  << std::setw(24) << "\n\nDecimal rvalues, too:"
			     << std::setw(18) << "0x version: "       << ZeroX::hex_out_s(29)
			  << std::setw(23) << '\n'
			     << std::setw(18) << "showbase version: " << ShowBase::hex_out_s(29)

			  << std::setw(24) << "\n\nA long long:"
			     << std::setw(18) << "0x version: "       << ZeroX::hex_out_s(LLONG_MAX)
			  << std::setw(23) << '\n'
			     << std::setw(18) << "showbase version: " << ShowBase::hex_out_s(LLONG_MAX)

			  << std::setw(24) << "\n\nAnd zero:"
			     << std::setw(18) << "0x version: "       << ZeroX::hex_out_s(0)
			  << std::setw(23) << '\n'
			     << std::setw(18) << "showbase version: " << ShowBase::hex_out_s(0)
			  << "\n\n" << std::endl;
}