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

namespace hex_out_helper {
	constexpr int HEX_DIGIT_BITS = 4; // One hex digit = 4 bits.
	constexpr int HEX_BASE_CHARS = 2; // For the "0x".

    template<typename T> struct CharCheck {
        using type = T;
    };
    
    template<> struct CharCheck<signed char> {
        using type = char;
    };
    
    template<> struct CharCheck<unsigned char> {
        using type = char;
    };
    
    template<typename T> using CharChecker = typename CharCheck<T>::type;
} // namespace hex_out_helper


template<typename T> std::string hex_out_s(T val) {
    using namespace hex_out_helper;

	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)
			   << (std::is_same<CharChecker<T>, char>{} ? static_cast<int>(val) : val);
	return sformatter.str();
}

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 << "uint32_t: " << hex_out_s(hexU32) << std::endl;
	std::cout << "int: " << hex_out_s(hexI) << std::endl;
	std::cout << "unsigned short: " << hex_out_s(hexUS) << std::endl;
	std::cout << "char: " << hex_out_s(hexC) << std::endl;
	std::cout << "unsigned char: " << hex_out_s(hexUC) << std::endl;
	std::cout << "signed char: " << hex_out_s(hexSC) << std::endl;
	std::cout << "Decimal rvalues, too: " << hex_out_s(29) << std::endl;
	std::cout << "And a long long: " << hex_out_s(LLONG_MAX) << std::endl;
}