Added ToString, ReplaceAll, and Format function

This commit is contained in:
Walter Julius Hennecke 2017-10-02 23:30:12 +02:00
parent 37f1a0edd9
commit 181f85ad1d
6 changed files with 241 additions and 11 deletions

View file

@ -1,5 +1,6 @@
#include "String.h"
#include "../doctest/parts/doctest_fwd.h"
#include "Warnings.h"
namespace Common
{
@ -364,5 +365,87 @@ namespace Common
REQUIRE(CompareNoCase(Str3, Str5) == 1);
REQUIRE(CompareNoCase(Str4, Str5) == 1);
}
TEST_CASE("String ToString Ptr")
{
int32_t* Test = nullptr;
WARNINGS_OFF(4127);
if(sizeof(void*) == 4)
REQUIRE(ToString(Test) == _T("00000000"));
else
// ReSharper disable once CppUnreachableCode
REQUIRE(ToString(Test) == _T("0000000000000000"));
WARNINGS_ON;
}
TEST_CASE("String ToString String")
{
const String Test = _T("Test");
REQUIRE(ToString(Test) == Test);
}
TEST_CASE("String ToString Bool")
{
REQUIRE(ToString(true) == _T("1"));
REQUIRE(ToString(false) == _T("0"));
REQUIRE(ToString(true, true) == _T("true"));
REQUIRE(ToString(false, true) == _T("false"));
REQUIRE(ToString(1 == 1) == _T("1"));
REQUIRE(ToString(1 == 42) == _T("0"));
REQUIRE(ToString(1 == 1, true) == _T("true"));
REQUIRE(ToString(1 == 42, true) == _T("false"));
}
TEST_CASE("String ToString Floating Point")
{
float f = 42.0f;
ToString(4.02, 5);
REQUIRE(ToString(f, 5, true) == _T("42.00000"));
REQUIRE(ToString(2.04, 5) == _T("2.04"));
REQUIRE(ToString(2.04, 1) == _T("2"));
auto Temp = ToString(f, 10, true, true, 20);
REQUIRE(Temp.length() == 20);
}
TEST_CASE("String ToString Integral")
{
int32_t I1 = 42;
uint32_t I2 = 42;
int16_t I3 = -42;
REQUIRE(ToString(I1) == _T("42"));
REQUIRE(ToString(I2) == _T("42"));
REQUIRE(ToString(I3) == _T("-42"));
REQUIRE(ToString(I1, EIntegralFormat::Hexadecimal) == _T("2a"));
REQUIRE(ToString(I2, EIntegralFormat::Hexadecimal) == _T("2a"));
REQUIRE(ToString(I3, EIntegralFormat::Hexadecimal) == _T("ffd6"));
REQUIRE(ToString(I1, EIntegralFormat::Octal) == _T("52"));
REQUIRE(ToString(I2, EIntegralFormat::Octal) == _T("52"));
REQUIRE(ToString(I3, EIntegralFormat::Octal) == _T("177726"));
REQUIRE(ToString(I1, EIntegralFormat::Decimal, 5).length() == 5);
REQUIRE(ToString(I2, EIntegralFormat::Decimal, 5).length() == 5);
REQUIRE(ToString(I3, EIntegralFormat::Decimal, 5).length() == 5);
REQUIRE(ToString(I1, EIntegralFormat::Decimal, 5, _T('.')) == _T("...42"));
REQUIRE(ToString(I2, EIntegralFormat::Decimal, 5, _T('.')) == _T("...42"));
REQUIRE(ToString(I3, EIntegralFormat::Decimal, 5, _T('.')) == _T("..-42"));
}
TEST_CASE("String Format")
{
REQUIRE(Format(_T("%0 %1 %2"), 42, _T("test"), true) == _T("42 test 1"));
REQUIRE(Format(_T("%1%0%1"), 1, 0) == _T("010"));
}
}

View file

@ -1,8 +1,13 @@
#pragma once
#include <string>
#include <locale>
#include <algorithm>
#include <iostream>
#include <sstream>
#include <array>
#include <initializer_list>
#include <iterator>
#include <iomanip>
#include <cctype>
#include <optional>
#if defined (UNICODE)
#define _T(X) L##X
@ -47,4 +52,135 @@ namespace Common
bool EqualNoCase(const String& A, const String& B);
int32_t CompareNoCase(const String& A, const String& B);
template<typename T, typename = std::enable_if_t<std::is_pointer<T>::value && !std::is_convertible<T, const char*>::value && !std::is_convertible<T, const wchar_t*>::value>>
String ToString(T Value)
{
StringStream Stringstream;
Stringstream << static_cast<const void*>(Value);
return Stringstream.str();
}
inline String ToString(String Value) { return Value; }
template<typename T, typename = std::enable_if_t<std::is_floating_point<T>::value>>
String ToString(T Value, std::optional<int32_t> Precision = std::nullopt, bool Fixed = false, bool Scientific = false, std::optional<int32_t>Width = std::nullopt)
{
StringStream Stringstream;
if(Precision)
Stringstream << std::setprecision(*Precision);
if(Fixed)
Stringstream << std::fixed;
if(Scientific)
Stringstream << std::scientific;
if(Width)
Stringstream << std::setw(*Width);
Stringstream << Value;
return Stringstream.str();
}
template <typename T, typename = std::enable_if_t<std::is_same<T, bool>::value>>
String ToString(T Value, bool Alpha = false)
{
StringStream Stringstream;
if(Alpha)
Stringstream << std::boolalpha;
Stringstream << Value;
return Stringstream.str();
}
enum class EIntegralFormat
{
Decimal,
Hexadecimal,
Octal
};
template<typename T, typename = std::enable_if_t<std::is_integral<T>::value && !std::is_same<T, bool>::value>>
String ToString(T Value, EIntegralFormat Format = EIntegralFormat::Decimal, std::optional<int32_t> Width = std::nullopt, std::optional<String::value_type> Fill = std::nullopt)
{
StringStream StringStream;
switch(Format)
{
case EIntegralFormat::Hexadecimal:
{
StringStream << std::hex;
break;
}
case EIntegralFormat::Octal:
{
StringStream << std::oct;
break;
}
case EIntegralFormat::Decimal:
default:
break;
}
if(Width)
StringStream << std::setw(*Width);
if(Fill)
StringStream << std::setfill(*Fill);
StringStream << Value;
return StringStream.str();
}
template<typename T, typename = std::enable_if_t<!std::is_integral<T>::value && !std::is_floating_point<T>::value && !std::is_pointer<T>::value>, typename = void>
String ToString(T Value)
{
StringStream StringStream;
StringStream << Value;
return StringStream.str();
}
template<typename MatchType, typename RepType, typename = std::enable_if_t<std::is_convertible<MatchType, String>::value && std::is_convertible<RepType, String>::value>>
String& ReplaceAll(String& Str, MatchType&& Match, RepType&& Rep)
{
String M{ Match };
if(M.empty())
return Str;
String R{ Rep };
String::size_type Index = 0;
while(true)
{
Index = Str.find(M, Index);
if(Index == String::npos)
break;
Str.replace(Index, M.size(), R);
Index += R.size();
}
return Str;
}
template<typename MatchType, typename RepType, typename = std::enable_if_t<std::is_convertible<MatchType, String>::value && std::is_convertible<RepType, String>::value>>
String ReplaceAll(const String& Str, MatchType&& Match, RepType&& Rep)
{
auto Copy(Str);
return ReplaceAll(Copy, std::forward<MatchType>(Match), std::forward<RepType>(Rep));
}
template<typename... ArgsT>
String Format(const String& Format, ArgsT const & ... Args)
{
auto Copy(Format);
std::array<String, sizeof...(Args)> StringArgs = { {ToString(Args)...} };
for(auto i = 0; i < sizeof...(Args); i++)
{
ReplaceAll(Copy, _T("%") + ToString(i), StringArgs[i]);
}
return Copy;
}
}

View file

@ -0,0 +1,10 @@
#pragma once
#ifdef _MSC_VER
#define WARNINGS_OFF(...) __pragma (warning(push))\
__pragma (warning(disable: __VA_ARGS__))
#define WARNINGS_ON __pragma (warning(pop))
#endif

View file

@ -129,8 +129,9 @@
<WarningLevel>Level4</WarningLevel>
<SuppressStartupBanner>true</SuppressStartupBanner>
<CompileAs>CompileAsCpp</CompileAs>
<LanguageStandard>stdcpp17</LanguageStandard>
<LanguageStandard>stdcpplatest</LanguageStandard>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<RuntimeTypeInfo>true</RuntimeTypeInfo>
</ClCompile>
<ResourceCompile>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>

View file

@ -6,12 +6,9 @@
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="C:\Users\Walter\Source\Repos\rpgxEF\misc\doctest\..\..\code\doctest\parts\doctest_impl.h">
<Filter>parts</Filter>
</ClInclude>
<ClInclude Include="C:\Users\Walter\Source\Repos\rpgxEF\misc\doctest\..\..\code\doctest\parts\doctest_fwd.h">
<Filter>parts</Filter>
</ClInclude>
<ClInclude Include="$(MSBuildThisFileDirectory)..\..\code\doctest\runner.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)..\..\code\doctest\doctest.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)..\..\code\doctest\parts\doctest_fwd.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)..\..\code\doctest\parts\doctest_impl.h" />
</ItemGroup>
</Project>

View file

@ -16,8 +16,11 @@
<ItemGroup>
<ClInclude Include="$(MSBuildThisFileDirectory)..\..\..\code\common_game\Position2D.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)..\..\..\code\common_game\String.h" />
<ClInclude Include="$(MSBuildThisFileDirectory)..\..\..\code\common_game\Warnings.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="$(MSBuildThisFileDirectory)..\..\..\code\common_game\String.cpp" />
<ClCompile Include="$(MSBuildThisFileDirectory)..\..\..\code\common_game\String.cpp">
<EnforceTypeConversionRules>true</EnforceTypeConversionRules>
</ClCompile>
</ItemGroup>
</Project>