Skip to content

Instantly share code, notes, and snippets.

@martinmoene
Created December 24, 2020 09:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save martinmoene/58aacbc965ab79a521a6a9df782357c2 to your computer and use it in GitHub Desktop.
Save martinmoene/58aacbc965ab79a521a6a9df782357c2 to your computer and use it in GitHub Desktop.
/*
* ./format-base.h
*
* In the public domain
*
* Author: Martin Moene
*/
#ifndef FORMAT_BASE_H_INCLUDED
#define FORMAT_BASE_H_INCLUDED
#include <algorithm>
#include <cmath>
#include <cstdint>
#include <cstdlib>
#include <string>
namespace format {
// predefined number of symbols between separators (').
const int sep_none = 0;
const int sep_each2 = 2;
const int sep_each4 = 4;
const int sep_each8 = 8;
namespace detail {
// default number of symbols between separator, in to_string(), format().
const int sep_each_def = sep_each8;
// maximum number of symbols in given base to present given number of bits.
inline int positions( int const base, int const bits )
{
return static_cast<int>( 0.5 + bits / ( std::log( 1.0 * base ) / std::log( 2.0 ) ) );
}
// a left-stripped copy of given text; at default strips space and tab.
inline std::string stripped_left( std::string text, std::string const & set = " \t" )
{
return text.erase( 0, text.find_first_not_of( set ) );
}
// a copy of given textual number of 'base' and width with separators at 'each' number of symbols, .
inline std::string separated( std::string text, int const base, int const bits, int const each = sep_each_def )
{
if ( each == sep_none )
return text;
const auto sep_count = base==10 ? 0 : (std::max)( 0, (positions( base, bits ) - 1) / each );
for ( int i = 0; i < sep_count; ++i )
{
text.insert( text.length() - i - (i + 1) * each, 1, '\'' );
}
return text;
}
// a zero-padded or space-padded (base-10 only) copy of given textual number.
inline std::string padded( std::string const & text, int const base, int const bits )
{
const auto pad_count = (std::max)( 0, positions(base, bits) - int(text.length()) );
return pad_count > 0 ? std::string( pad_count, base==10 ? ' ':'0' ) + text : text;
}
} // namespace detail
/**
* number of bits in given type.
*/
template< typename T >
std::size_t bits()
{
return 8 * sizeof(T);
}
/**
* a textual number with sperators (') removed.
*/
inline std::string unseparated( std::string text )
{
text.erase(
std::remove( text.begin(), text.end(), '\'' ),
text.end()
);
return text;
}
/**
* unsigned value in given 'base', with given 'bits' width and a separator 'sep_each' [8] symbols.
*/
inline std::string format( std::uint32_t const x, int const base, int const bits = 0, int const sep_each = detail::sep_each_def )
{
char buf[65];
return detail::separated( detail::padded( ::_ultoa( x, buf, base ), base, bits ), base, bits, sep_each );
}
/**
* unsigned value in base 2, at given 'width' and separator each 'sep_each' symbols.
*/
inline std::string format2( std::uint32_t const value, int const bits, int const sep_each )
{
return "0b" + format( value, 2, bits, sep_each );
}
/**
* unsigned value in base 16, at given 'width' and separator each 'sep_each' symbols.
*/
inline std::string format16( std::uint32_t const value, int const bits, int const sep_each )
{
return "0x" + format( value, 16, bits, sep_each );
}
/**
* unsigned value in base 10, width as given.
*/
inline std::string format10( std::uint32_t const value, int const bits )
{
return format( value, 10, bits );
}
/**
* report unsigned value in 'base' presentation; default left-aligned, separator each 8 symbols.
*/
inline std::string to_string( std::uint32_t const value, int const base, int const bits = 0, int const sep_each = detail::sep_each_def )
{
return base==2 ? format2 ( value, bits, sep_each ) :
base==16 ? format16( value, bits, sep_each ) : format10( value, bits );
}
/**
* report unsigned value in decimal presentation, like 1234; default left-aligned
*/
template< typename T >
inline std::string to_string10( T const value, int const width = 0 )
{
const int base10 = 10;
return to_string( value, base10, width, sep_none );
}
/**
* report unsigned value in hexadecimal presentation, like 0x1234'5678
*/
template< typename T >
inline std::string to_string16( T const value, int const sep_each = sep_each4 )
{
const int base16 = 16;
return to_string( value, base16, bits<T>(), sep_each );
}
/**
* report unsigned value in binary presentation, like 0b10101010'10101010.
*/
template< typename T >
inline std::string to_string2( T const value, int const sep_each = sep_each8 )
{
const int base2 = 2;
return to_string( value, base2, bits<T>(), sep_each );
}
/**
* report pattern in decimal, hexadecimal and binary type-specific fixed width presentation,
* like: 43690 - 0x007b - 0b10101010'10101010;
* width of decimal presentation can be specified: 0 is left-aligned.
*/
template< typename T >
std::string to_pattern_string( T const pattern, int const width10 = bits<T>() )
{
return to_string10( pattern, width10 ) + " - " +
to_string16( pattern ) + " - " +
to_string2 ( pattern );
}
} // namespace format
#endif // FORMAT_BASE_H_INCLUDED
// Exercise format::to_pattern_string(value):
// Output:
// 117 - 0x75 - 0b01110101
// 43981 - 0xabcd - 0b10101011'11001101
// 306752461 - 0x1248'abcd - 0b00010010'01001000'10101011'11001101
#include "format-base.h"
#include <iostream>
int main()
{
std::cout <<
format::to_pattern_string( static_cast<std::uint8_t>( 0x75) ) << '\n' <<
format::to_pattern_string( static_cast<std::uint16_t>(0xabcd) ) << '\n' <<
format::to_pattern_string( static_cast<std::uint32_t>(0x1248abcd) ) << '\n';
}
// g++ -std=c++11 -o main.exe main.cpp && main.exe
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment