Closed coombez closed 8 years ago
Thanks, did you try to build the tests project? It doesn't compile. I'll be happy to include your improvement after the test project pass compilation & all tests pass.
1>c:\users\eran\projects\fakeit\include\mockutils\formatter.hpp(20): error C2679: binary '<<' : no operator found which takes a right-hand operand of type 'const A' (or there is no acceptable conversion)
1> c:\program files (x86)\microsoft visual studio 12.0\vc\include\ostream(498): could be 'std::basic_ostream<char,std::char_traits
I'm not sure of the best way to submit a patch. I'm used to git, but not github, so I'm just pasting it right here... hopefully that will work ok. This passes all tests on GCC. My first implementation did a poor job of trying to use sfinae.
I changed the two specializations of Formatter, but really just defining an operator<< for class 'A' would probably be more convenient now. I don't know if you considered the Formatter part of your interface with the expectation that users would specialize. If so, I've broken this interface with this patch, but that could probably be massaged if necessary.
diff --git a/include/mockutils/Formatter.hpp b/include/mockutils/Formatter.hpp
index 7946712..a048413 100644
--- a/include/mockutils/Formatter.hpp
+++ b/include/mockutils/Formatter.hpp
@@ -10,92 +10,58 @@
#include <ostream>
#include <type_traits>
#include <string>
-#include "mockutils/to_string.hpp"
namespace fakeit {
- template<class T>
- struct Formatter {
- static std::string format(const T &val) {
- if (std::is_const<T>::value)
- return Formatter<typename std::remove_const<T>::type>::format(val);
- if (std::is_reference<T>::value)
- return Formatter<typename std::remove_reference<T>::type>::format(val);
- if (std::is_volatile<T>::value)
- return Formatter<typename std::remove_volatile<T>::type>::format(val);
+template <typename T>
+class is_ostreamable
+{
+ struct no {};
+ template <typename T1>
+ static auto test(std::ostream &s, const T1 &t) -> decltype(s << t);
+ static no test(...);
+public:
+ static const bool value = std::is_same<decltype(test(*(std::ostream *)nullptr,
+ std::declval<T>())),std::ostream &>::value;
+};
- return {"?"};
- }
- };
- template<>
- struct Formatter<bool> {
- static std::string format(const bool &val) {
- return val ? "true" : "false";
- }
- };
+template <typename T, bool streamable>
+struct FormatterImpl
+{
+ static std::string format(T const &)
+ {
+ return "?";
+ }
+};
- template<>
- struct Formatter<int> {
- static std::string format(const int &val) {
- return fakeit::to_string(val);
- }
- };
+template <typename T>
+struct FormatterImpl<T, true>
+{
+ static std::string format(T const &val)
+ {
+ std::ostringstream os;
+ os << val;
+ return os.str();
+ }
+};
- template<>
- struct Formatter<unsigned int> {
- static std::string format(const unsigned int &val) {
- return fakeit::to_string(val);
- }
- };
+template <>
+struct FormatterImpl<bool, true>
+{
+ static std::string format(bool const &val)
+ {
+ return val ? "true" : "false";
+ }
+};
- template<>
- struct Formatter<long> {
- static std::string format(const long &val) {
- return fakeit::to_string(val);
- }
- };
-
- template<>
- struct Formatter<unsigned long> {
- static std::string format(const unsigned long &val) {
- return fakeit::to_string(val);
- }
- };
-
- template<>
- struct Formatter<long long> {
- static std::string format(const long long &val) {
- return fakeit::to_string(val);
- }
- };
-
- template<>
- struct Formatter<unsigned long long> {
- static std::string format(const unsigned long long &val) {
- return fakeit::to_string(val);
- }
- };
-
- template<>
- struct Formatter<double> {
- static std::string format(const double &val) {
- return fakeit::to_string(val);
- }
- };
-
- template<>
- struct Formatter<long double> {
- static std::string format(const long double &val) {
- return fakeit::to_string(val);
- }
- };
-
- template<>
- struct Formatter<float> {
- static std::string format(const float &val) {
- return fakeit::to_string(val);
- }
- };
+template <typename T>
+using Formatter = FormatterImpl<
+ typename std::remove_volatile<
+ typename std::remove_const<
+ typename std::remove_reference<T>::type
+ >::type
+ >::type, is_ostreamable<T>::value
+ >;
}
diff --git a/tests/default_event_formatting_tests.cpp b/tests/default_event_formatting_tests.cpp
index 3ad2728..be55865 100644
--- a/tests/default_event_formatting_tests.cpp
+++ b/tests/default_event_formatting_tests.cpp
@@ -217,7 +217,7 @@ struct DefaultEventFormatting: tpunit::TestFixture {
expectedMsg += "Actual matches : 1\n";
expectedMsg += "Actual sequence : total of 1 actual invocations:\n";
//expectedMsg += " mock.all_types(?, true, 1, 1, 1, 1, 1, 1, 1.000000, 1.000000)";
- expectedMsg += " mock.all_types(?, true, 1, 1, 1, 1, 0, 0)";
+ expectedMsg += " mock.all_types(a, true, 1, 1, 1, 1, 0, 0)";
std::string actualMsg {to_string(e)};
std::cout << actualMsg;
@@ -240,7 +240,7 @@ struct DefaultEventFormatting: tpunit::TestFixture {
std::string expectedMsg{ formatLineNumner("test file", 1) };
expectedMsg += ": Verification error\n";
//expectedMsg += "Expected pattern: mock.all_types('a', true, 1, 1, 1, 1, 1.000000)\n";
- expectedMsg += "Expected pattern: mock.all_types(?, true, 1, 1, 1, 1, 0, 0)\n";
+ expectedMsg += "Expected pattern: mock.all_types(a, true, 1, 1, 1, 1, 0, 0)\n";
expectedMsg += "Expected matches: exactly 2\n";
expectedMsg += "Actual matches : 0\n";
expectedMsg += "Actual sequence : total of 0 actual invocations.";
diff --git a/tests/verification_errors_tests.cpp b/tests/verification_errors_tests.cpp
index 099898a..df328c5 100644
--- a/tests/verification_errors_tests.cpp
+++ b/tests/verification_errors_tests.cpp
@@ -26,7 +26,7 @@ struct A {
};
namespace fakeit {
-template<> struct Formatter<A> {
+template<> struct FormatterImpl<A, false> {
static std::string format(const A&) {
return {"a"};
}
diff --git a/tests/verification_tests.cpp b/tests/verification_tests.cpp
index a20d5fa..9773400 100644
--- a/tests/verification_tests.cpp
+++ b/tests/verification_tests.cpp
@@ -28,7 +28,7 @@ struct A {
};
namespace fakeit {
-template<> struct Formatter<A> {
+template<> struct FormatterImpl<A, false> {
static std::string format(const A&) {
return {"a"};
}
Thanks you for the suggestion and the solution. I used most of your code but changed it a little in order to keep the backwards compatibility. You can pull the latest version. Enjoy!
In my project I've replaced your formatter with the following, which allows any type with the standard
operator<<
defined to be formatted, including my custom types. Just sharing in case you like it. Feel free to use or discard as you please.