Created
August 2, 2021 23:00
-
-
Save dwcullop/aabb2007ba0dcd8c7e80a761545e6ca3 to your computer and use it in GitHub Desktop.
Easy, Old School Printf-Style Formatting for std::string and std::wstring
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |
// FormatString.h : Standard String Formatting Support | |
// | |
// Author Name : | |
// Darrin W. Cullop ([email protected]) | |
// | |
// License : | |
// CC-BY 4.0 (https://creativecommons.org/licenses/by/4.0/) | |
// | |
// Abstract : | |
// Create or append to a std::string/std::wstring with simple "PrintF" functionality | |
// (Simliar to C++20's std::format but using the old-school printf style formatting) | |
// | |
// Example Usages: | |
// std::string str = dwc::FormatString( "Hello, World! The number is %d!", 37 ); | |
// std::wstring strw = dwc::FormatString( L"Hello, World! The number is %d!", 37 ); | |
// | |
// auto str1 = std::string{"Hello"}; | |
// dwc::AppendFormatString( str1, " %s!", "World" ); | |
// std::cout << str1 << std::endl; // Prints "Hello World!" | |
// | |
// auto wstr1 = std::wstring{L"Hello"}; | |
// dwc::AppendFormatString( wstr1, L" %s!", "Nurse" ); | |
// std::wcout << dwc::AppendFormatString(wstr1, L"\r\n").c_str(); // wstr1 becomes L"Hello Nurse!\r\n" | |
// | |
// auto mixWidths = std::string{"Q: Can we mix in a Wide String?"}; | |
// dwc::AppendFormatString(mixWidths, "\r\nA: "); | |
// dwc::AppendFormatString(mixWidths, "[%S]", L"Yup but why would we?"); | |
// dwc::AppendFormatString(mixWidths, "\r\n"); | |
// dwc::AppendFormatString(mixWidths, "(Ya never know what you're gonna need)"); | |
// | |
// std::string GetDictionaryDisplayString( const std::map<std::string, std::string>& dictionary ) | |
// { | |
// auto display = dwc::FormatString( "Dictionary contains %u Entries\r\n", dictionary.size() ); | |
// for ( const auto& entry : dictionary ) | |
// { | |
// const auto& [key, value] = *entry; | |
// dwc::AppendFormatString( display, "\t[%s] = %s\r\n", key.c_str(), value.c_str() ); | |
// } | |
// return display; | |
// } | |
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |
#pragma once | |
#include <string> | |
#include <cstdio> | |
#include <cwchar> | |
#include <cassert> | |
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |
// dwc Namespace | |
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |
namespace dwc | |
{ | |
namespace Details | |
{ | |
// Formatting Overloads for both types of strings | |
template<typename... Args> | |
inline auto Format( char* const buffer, std::size_t size, const char* const format, Args&&... args ) | |
{ | |
return std::snprintf( buffer, size, format, std::forward<Args>( args )... ); | |
} | |
template<typename... Args> | |
inline auto Format( wchar_t* const buffer, std::size_t size, const wchar_t* const format, Args&&... args ) | |
{ | |
return std::swprintf( buffer, size, format, std::forward<Args>( args )... ); | |
} | |
} | |
////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |
template<typename CharType, typename... Args> | |
[[nodiscard]] auto FormatString( const CharType* const format, Args&&... args ) -> std::basic_string<CharType> | |
{ | |
using String = std::basic_string<CharType>; | |
// Get the number of characters needed (without the null) | |
const auto need = Details::Format( nullptr, 0, format, std::forward<Args>( args )... ); | |
assert( ( need > 0 ) || ( format[0] == CharType{ '\0' } ) ); | |
// Allocate the right amount (but include the null) | |
auto result = String( need + std::size_t{ 1 }, CharType{ '\0' } ); | |
// Write the characters into the string data | |
const auto used = Details::Format( result.data(), result.capacity(), format, std::forward<Args>( args )... ); | |
assert( ( used > 0 ) || ( format[0] == CharType{ '\0' } ) ); | |
// Shrink the string down to the perfect size (leaving the null at the end for safety) | |
result.resize( used ); | |
// Return the result | |
return result; | |
} | |
////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |
template<typename StrType, typename... Args> | |
auto AppendFormatString( StrType& target, | |
const typename StrType::value_type* const format, | |
Args&&... args ) -> StrType& | |
{ | |
using CharType = typename StrType::value_type; | |
// Find the spot to insert, either at the null, or at the end | |
auto offset = target.find_last_of( CharType{ '\0' } ); | |
if ( offset == StrType::npos ) | |
{ | |
offset = target.size(); | |
} | |
// Determine the length of the string to be appended | |
const auto need = Details::Format( nullptr, 0, format, std::forward<Args>( args )... ); | |
assert( ( need > 0 ) || ( format[0] == CharType{ '\0' } ) ); | |
// Resize the string to fit the old, the new, and the null | |
target.resize( offset + need + std::size_t{ 1 }, CharType{ '\0' } ); | |
// Write the string to be appended into the string's buffer | |
const auto used = Details::Format( target.data() + offset, | |
target.capacity() - offset, | |
format, | |
std::forward<Args>( args )... ); | |
assert( ( used > 0 ) || ( format[0] == CharType{ '\0' } ) ); | |
// Shrink the string down to the perfect size (leaving the null at the end for safety) | |
target.resize( used + offset ); | |
// Return the string object | |
return target; | |
} | |
////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |
} // namespace dwc | |
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// | |
// End of FormatString.h | |
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment