Commits

Matt Oswald  committed 68c4bd2

adding a new xUnitpp::ToString that defaults to printing an object's type, if to_string(<object type>) doesn't exist

  • Participants
  • Parent commits 4b70968

Comments (0)

Files changed (6)

File xUnit++/src/xUnitAssert.cpp

     return none;
 }
 
-xUnitAssert::xUnitAssert(const std::string &call, const xUnitpp::LineInfo &lineInfo)
+xUnitAssert::xUnitAssert(std::string &&call, const xUnitpp::LineInfo &lineInfo)
     : lineInfo(lineInfo)
-    , call(call)
+    , call(std::move(call))
 {
 }
 
     auto found = actualString.find(value);
     if (found != std::string::npos)
     {
-        return OnFailure(xUnitAssert(callPrefix + "DoesNotContain", lineInfo).CustomMessage("Found: \"" + value + "\" at position " + std::to_string(found) + "."));
+        return OnFailure(xUnitAssert(callPrefix + "DoesNotContain", lineInfo).CustomMessage("Found: \"" + value + "\" at position " + ToString(found) + "."));
     }
 
     return OnSuccess();

File xUnit++/src/xUnitTestRunner.cpp

                     if (threadStarted->wait_for(gate, std::chrono::duration_cast<std::chrono::nanoseconds>(testTimeLimit)) == std::cv_status::timeout)
                     {
                         attachedOutput->Detach();
-                        sharedOutput.ReportEvent(test->TestDetails(), TestEvent(EventLevel::Fatal, "Test failed to complete within " + std::to_string(Time::ToMilliseconds(testTimeLimit).count()) + " milliseconds."));
+                        sharedOutput.ReportEvent(test->TestDetails(), TestEvent(EventLevel::Fatal, "Test failed to complete within " + ToString(Time::ToMilliseconds(testTimeLimit).count()) + " milliseconds."));
                         sharedOutput.ReportFinish(test->TestDetails(), testTimeLimit);
                         ++failedTests;
                     }

File xUnit++/xUnit++.vcxproj

     <ClInclude Include="xUnit++/TestEvent.h" />
     <ClInclude Include="xUnit++/xUnitCheck.h" />
     <ClInclude Include="xUnit++\xUnitLog.h" />
+    <ClInclude Include="xUnit++\xUnitToString.h" />
     <ClInclude Include="xUnit++\xUnitWarn.h" />
   </ItemGroup>
   <PropertyGroup Label="Globals">
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
     <ClCompile>
       <AdditionalIncludeDirectories>xUnit++</AdditionalIncludeDirectories>
-        <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
     </ClCompile>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
     <ClCompile>
       <AdditionalIncludeDirectories>xUnit++</AdditionalIncludeDirectories>
-        <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
     </ClCompile>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
     <ClCompile>
       <AdditionalIncludeDirectories>xUnit++</AdditionalIncludeDirectories>
-        <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+      <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
     </ClCompile>
   </ItemDefinitionGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
   </ImportGroup>
-</Project>
+</Project>

File xUnit++/xUnit++.vcxproj.filters

     <ClInclude Include="xUnit++\xUnitWarn.h" />
     <ClInclude Include="xUnit++\TestEventRecorder.h" />
     <ClInclude Include="xUnit++\xUnitLog.h" />
+    <ClInclude Include="xUnit++\xUnitToString.h" />
   </ItemGroup>
-</Project>
+</Project>

File xUnit++/xUnit++/xUnitAssert.h

 #include <type_traits>
 #include <vector>
 #include "LineInfo.h"
+#include "xUnitToString.h"
 
 namespace xUnitpp
 {
     typedef std::exception base;
 
 public:
-    xUnitAssert(const std::string &call, const LineInfo &lineInfo);
+    xUnitAssert(std::string &&call, const LineInfo &lineInfo);
 
     xUnitAssert &CustomMessage(const std::string &message);
     xUnitAssert &Expected(const std::string &expected);
     static double round(double value, size_t precision);
 
     template<typename T>
-    static std::string RangeToString(const T &begin, const T &end)
+    static std::string RangeToString(T &&begin, T &&end)
     {
-        using std::to_string;
+        typedef decltype(*begin) val_type;
+
         std::string result = "[ ";
 
-        std::for_each(begin, end, [&result](decltype(*begin) val) { result += to_string(val) + ", "; });
+        std::for_each(std::forward<T>(begin), std::forward<T>(end), [&result](val_type &&val) { result += ToString(std::forward<val_type>(val)) + ", "; });
 
         result[result.size() - 2] = ' ';
         result[result.size() - 1] = ']';
     template<typename TExpected, typename TActual, typename TComparer>
     xUnitFailure Equal(TExpected expected, TActual actual, TComparer comparer, const LineInfo &lineInfo = LineInfo::empty()) const
     {
-        using std::to_string;
-
         if (!comparer(expected, actual))
         {
-            return OnFailure(xUnitAssert(callPrefix + "Equal", lineInfo).Expected(to_string(expected)).Actual(to_string(actual)));
+            return OnFailure(xUnitAssert(callPrefix + "Equal", lineInfo).Expected(ToString(expected)).Actual(ToString(actual)));
         }
 
         return OnSuccess();
     xUnitFailure Equal(double expected, double actual, int precision, const LineInfo &lineInfo = LineInfo::empty()) const;
 
     template<typename TExpected, typename TActual, typename TComparer>
-    xUnitFailure Equal(const TExpected &expectedBegin, const TExpected &expectedEnd, const TActual &actualBegin, const TActual &actualEnd, TComparer comparer, const LineInfo &lineInfo = LineInfo::empty()) const
+    xUnitFailure Equal(TExpected &&expectedBegin, TExpected &&expectedEnd, TActual &&actualBegin, TActual &&actualEnd, TComparer &&comparer, const LineInfo &lineInfo = LineInfo::empty()) const
     {
         auto expected = expectedBegin;
         auto actual = actualBegin;
         if (expected != expectedEnd || actual != actualEnd)
         {
             return OnFailure(xUnitAssert(callPrefix + "Equal", lineInfo)
-                .CustomMessage("Sequence unequal at location " + std::to_string(index) + ".")
-                .Expected(RangeToString(expectedBegin, expectedEnd))
-                .Actual(RangeToString(actualBegin, actualEnd)));
+                .CustomMessage("Sequence unequal at location " + ToString(index) + ".")
+                .Expected(RangeToString(std::forward<TExpected>(expectedBegin), std::forward<TExpected>(expectedEnd)))
+                .Actual(RangeToString(std::forward<TActual>(actualBegin), std::forward<TActual>(actualEnd))));
         }
 
         return OnSuccess();
     template<typename TSequence, typename TPredicate>
     xUnitFailure DoesNotContainPred(const TSequence &sequence, TPredicate &&predicate, const LineInfo &lineInfo = LineInfo::empty()) const
     {
-        using std::to_string;
-
         auto found = std::find_if(std::begin(sequence), std::end(sequence), std::forward<TPredicate>(predicate));
         if (found != std::end(sequence))
         {
             return OnFailure(xUnitAssert(callPrefix + "DoesNotContain", lineInfo)
-                .CustomMessage("Found: matching value at position " + to_string(std::distance(std::begin(sequence), found)) + "."));
+                .CustomMessage("Found: matching value at position " + ToString(std::distance(std::begin(sequence), found)) + "."));
         }
 
         return OnSuccess();
     template<typename TActual, typename TRange>
     xUnitFailure InRange(TActual actual, TRange min, TRange max, const LineInfo &lineInfo = LineInfo::empty()) const
     {
-        using std::to_string;
-
         if (min >= max)
         {
-            throw std::invalid_argument("Assert.InRange argument error: min (" + to_string(min) + ") must be strictly less than max (" + to_string(max) + ").");
+            throw std::invalid_argument("Assert.InRange argument error: min (" + ToString(min) + ") must be strictly less than max (" + ToString(max) + ").");
         }
 
         if (actual < min || actual >= max)
         {
             return OnFailure(xUnitAssert(callPrefix + "InRange", lineInfo)
-                .Expected("[" + to_string(min) + " - " + to_string(max) + ")")
-                .Actual(to_string(actual)));
+                .Expected("[" + ToString(min) + " - " + ToString(max) + ")")
+                .Actual(ToString(actual)));
         }
 
         return OnSuccess();
     template<typename TActual, typename TRange>
     xUnitFailure NotInRange(TActual actual, TRange min, TRange max, const LineInfo &lineInfo = LineInfo::empty()) const
     {
-        using std::to_string;
-
         if (min >= max)
         {
-            throw std::invalid_argument("Assert.NotInRange argument error: min (" + to_string(min) + ") must be strictly less than max (" + to_string(max) + ").");
+            throw std::invalid_argument("Assert.NotInRange argument error: min (" + ToString(min) + ") must be strictly less than max (" + ToString(max) + ").");
         }
 
         if (actual >= min && actual < max)
         {
             return OnFailure(xUnitAssert(callPrefix + "NotInRange", lineInfo)
-                .Expected("[" + to_string(min) + " - " + to_string(max) + ")")
-                .Actual(to_string(actual)));
+                .Expected("[" + ToString(min) + " - " + ToString(max) + ")")
+                .Actual(ToString(actual)));
         }
 
         return OnSuccess();

File xUnit++/xUnit++/xUnitToString.h

+#ifndef XUNITTOSTRING_H_
+#define XUNITTOSTRING_H_
+
+// from:
+// http://stackoverflow.com/questions/1386183/how-to-call-a-templated-function-if-it-exists-and-something-else-otherwise
+// modified to fix Visual Studio warning C4913
+
+#include <string>
+
+namespace xUnitpp
+{
+
+namespace ToStringImpl
+{
+    namespace fallback
+    {
+        struct flag { };
+
+        flag to_string(...);
+
+        // char would normally be treated like an int, which is probably not what we want
+        inline std::string to_string(char c)
+        {
+            return std::string() + c;
+        }
+
+        // you can't normally call std::to_string with a std::string
+        inline std::string to_string(std::string s)
+        {
+            return s;
+        }
+
+        long operator,(flag, flag);
+
+        template<typename T>
+        flag operator,(flag, T &&);
+
+        char operator,(long, flag);
+    }
+
+    template<bool>
+    struct to_string_impl;
+
+    template<>
+    struct to_string_impl<true>
+    {
+        template<typename T>
+        static std::string to_string(T &&t)
+        {
+            using fallback::to_string;
+            using std::to_string;
+
+            return to_string(std::forward<T>(t));
+        }
+    };
+
+    template<>
+    struct to_string_impl<false>
+    {
+        template<typename T>
+        static std::string to_string(T &&)
+        {
+            return typeid(T).name();
+        }
+    };
+}
+
+template <typename T>
+std::string ToString(T &&t)
+{
+    using ToStringImpl::fallback::to_string;
+    using std::to_string;
+
+    return ToStringImpl::to_string_impl<(sizeof (ToStringImpl::fallback::flag(), to_string(t), ToStringImpl::fallback::flag()) != sizeof(char))>::to_string(std::forward<T>(t));
+}
+
+template<typename T>
+bool has_to_string(T &&t)
+{
+    (void)t;
+    return has_to_string<typename std::remove_reference<T>::type>();
+}
+
+// !!!VS this could be a constexpr
+// if it's a constexpr, then the repeated code in ToString could be simplified
+template<typename T>
+bool has_to_string()
+{
+    using ToStringImpl::fallback::to_string;
+    using std::to_string;
+
+    return (sizeof (ToStringImpl::fallback::flag(), to_string((T &)*(T *)nullptr), ToStringImpl::fallback::flag()) != sizeof(char));
+}
+
+}
+
+#endif