The 2 reasons to use this aproach were enhanced readability and
better support for internationalisation:
Simply take the example from the handbook:
Person pers("Bob", 6);
String format = "%4 says: %3 plus %2 gives %1";
string one = "one";
string two = "two";
string three = "three";
format << three // %1
<< two // %2
<< one // %3
<< pers; // %4
std::cout << format << std::endl;
The format string already gives you a good idea about the result, you only
have to substitute the % by the actual values.
Please also understand that I intentionally reverted the order of the
%-substitutes to show this possibility. More about the need for this below.
On the contrary it is far more difficult to understand the outcome with
standard streaming operators:
cout << pers.toString()
<< " says: "
<< one
<< " plus "
<< two
<< " give "
<< three;
I am pretty sure you will forget the one or the other space which seperates
for example the literal "plus" from variable "two".
The other important point is: you can't translate your solution reasonably.
Translating "says:" and "plus" seperately and concatenating them like above often
results in the nonsense you get when you read manuals for devices from
the far east. Often also the order of the substituted words change in the
translated version. In this case the translator simply would swap %1 and %2.
Maybe you don't find it worth the effort but english is not the only language
on earth, so why not prepare a nice way to translate the lib?
Someone interested in a translated version simply uses "xgettext"
to collect the words, translates them with a simple editor and replaces macro
mockpp_i18n() with the according call from the standard intl package. I haven't
done myself because I assume most people don't need it. But there alredy is
a "make messages" as a starting point :-)
And overloading operator<< is simply the *same* as you would do with standard
iomanip:
I think one thing to making unit testing and mock testing more attractive to developers under heavy pressure (aren't most of us?) , is to make it as easy as possible to get your tests / mocks up and running in as little time as possible. I think mockpp has a steep learning curve already ... not because the framework is bad, but because the inherent limitations of C++ like missing introspection probably required every trick in the book to implement mockpp such as the extended macros.
Having to worry about custom formatters for your object is just another detail that gets in the way of making mocks quickly. I guess I really can't speak for other developers here, but often , for debugiing purposes, I find myself overloading ostream& operator<< (ostream&, type) already, and having to do it again to pamper mockpp puts a bigger burden on the developer. Most people just want to get there class in a test harness as quick as possible, and getting compiler errors because we haven't overloaded the string format thingy is an obstracle to the newbie, and an inconvenience for the rest. At least, there should be a macro like MOCK_USE_STD_OSTREAM which
reverts to the standard stream stuff.
Also, as I somewhat alluded to this already, using standard streams allows your << operator to operator in other contexts, like debugging. Overloading the custom mockpp string formatter is only useful in mockpp.
If many people are already doing operator<<(ostream& , type) for debuggin purposes, the added "readability" of the custom string thing is moot, because "No code" is the best and most readable code of all, and just having to overload this custom operator in the first place subtracts from readability.
The argument for localization and i18n is the most substantiative, so I can see the utility of custom formatters ...
I guess it would be nice if those of use that already overloaded the standard ostream& operator << could just reuse those.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
you are certainly right when you mention the difficulties to learn mocks in general and mockpp in special. And from time to time I am still a bit surprised that it finally worked at all to replace introspection by a set of macros and templates :)
Introducing a MOCK_USE_STD_OSTREAM to "revert" to something like ostream seems no trivial task. And it would probably make it necessary to choose the one or the other but not both at the same time.
So there are two possibilites from my point of view. Either create a toString() member that does the actual job. This is a cleaner solution anyway than writing everything into operator<<() and make it a "friend" for full access. This toString() could then be used by both stream operators:
class T
{
std::string toString ()
{
return data;
}
};
ostream &operator<<(ostream &os, T x)
{
os << x.toString();
return os;
}
String &operator<<(String &s, T x)
{
s << x.toString();
return s;
}
The other and more convenient solution were an adapter to re-use ostream<<() in String<<(). But for this to work I have to think a bit more :-)
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I finally found a solution for the use of std::ostream. It is not as elegant as I originally hoped but it should be usable. The macro MOCKPP_OSTREAMABLE bridges the gap between std::ostream and mockpp::String for a given class T:
MOCKPP_OSTREAMABLE(T)
As already suggested please download from anon cvs to try yourself and to give me feedback about your opinion. You need Formatter.h and Formatter_test.cpp (test case is at the end).
If you can't use cvs just drop me a personal mail and I will send you a tar ball.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I have been using the following adapter for a while :
template< typename T >
mockpp::String& operator<<( mockpp::String& formatter, const T& adaptee )
{
std::stringstream adapter;
adapter << adaptee;
return formatter << adapter.str();
}
Typically it's defined after including mockpp in my test program.
The trick is if a class defines :
friend mockpp::String& operator<<( mockpp::String& formatter, const MyCLass& myClass );
it will be used by mockpp
If the class defines :
friend std::ostream& operator<<( std::ostream& stream, const MyClass& myClass );
mockpp will wrap it with the adapter.
I have no idea if this is portable as I've only been using mockpp with msvc71 though.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
mockpp implements the trick a bit differently with MOCKPP_OWSTREAMABLE(T). But I was not sure about the idea to offer this function as global template so you have to instantiate each needed type explicitly.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
why does not the library use the std::stringstream instead of the propriatary formatter-functions?
<iomanip> would provide all kinds of formatting functions.
moreover it would be easy to overload the operator<< for other types.
greetz, chris
The 2 reasons to use this aproach were enhanced readability and
better support for internationalisation:
Simply take the example from the handbook:
Person pers("Bob", 6);
String format = "%4 says: %3 plus %2 gives %1";
string one = "one";
string two = "two";
string three = "three";
format << three // %1
<< two // %2
<< one // %3
<< pers; // %4
std::cout << format << std::endl;
The format string already gives you a good idea about the result, you only
have to substitute the % by the actual values.
Please also understand that I intentionally reverted the order of the
%-substitutes to show this possibility. More about the need for this below.
On the contrary it is far more difficult to understand the outcome with
standard streaming operators:
cout << pers.toString()
<< " says: "
<< one
<< " plus "
<< two
<< " give "
<< three;
I am pretty sure you will forget the one or the other space which seperates
for example the literal "plus" from variable "two".
The other important point is: you can't translate your solution reasonably.
Translating "says:" and "plus" seperately and concatenating them like above often
results in the nonsense you get when you read manuals for devices from
the far east. Often also the order of the substituted words change in the
translated version. In this case the translator simply would swap %1 and %2.
Maybe you don't find it worth the effort but english is not the only language
on earth, so why not prepare a nice way to translate the lib?
Someone interested in a translated version simply uses "xgettext"
to collect the words, translates them with a simple editor and replaces macro
mockpp_i18n() with the according call from the standard intl package. I haven't
done myself because I assume most people don't need it. But there alredy is
a "make messages" as a starting point :-)
And overloading operator<< is simply the *same* as you would do with standard
iomanip:
class CA
{
String toString()
{
...
}
...
};
String & operator << (String &formatter, const CA &o)
{
return ::operator<< (formatter, o.toString());
}
Additionally you can use MOCKPP_ENABLE_DEFAULT_FORMATTER to generate
default operator which outputs the class name which is often enough.
Do you still think std::stringstream is the better solution?
I think one thing to making unit testing and mock testing more attractive to developers under heavy pressure (aren't most of us?) , is to make it as easy as possible to get your tests / mocks up and running in as little time as possible. I think mockpp has a steep learning curve already ... not because the framework is bad, but because the inherent limitations of C++ like missing introspection probably required every trick in the book to implement mockpp such as the extended macros.
Having to worry about custom formatters for your object is just another detail that gets in the way of making mocks quickly. I guess I really can't speak for other developers here, but often , for debugiing purposes, I find myself overloading ostream& operator<< (ostream&, type) already, and having to do it again to pamper mockpp puts a bigger burden on the developer. Most people just want to get there class in a test harness as quick as possible, and getting compiler errors because we haven't overloaded the string format thingy is an obstracle to the newbie, and an inconvenience for the rest. At least, there should be a macro like MOCK_USE_STD_OSTREAM which
reverts to the standard stream stuff.
Also, as I somewhat alluded to this already, using standard streams allows your << operator to operator in other contexts, like debugging. Overloading the custom mockpp string formatter is only useful in mockpp.
If many people are already doing operator<<(ostream& , type) for debuggin purposes, the added "readability" of the custom string thing is moot, because "No code" is the best and most readable code of all, and just having to overload this custom operator in the first place subtracts from readability.
The argument for localization and i18n is the most substantiative, so I can see the utility of custom formatters ...
I guess it would be nice if those of use that already overloaded the standard ostream& operator << could just reuse those.
you are certainly right when you mention the difficulties to learn mocks in general and mockpp in special. And from time to time I am still a bit surprised that it finally worked at all to replace introspection by a set of macros and templates :)
Introducing a MOCK_USE_STD_OSTREAM to "revert" to something like ostream seems no trivial task. And it would probably make it necessary to choose the one or the other but not both at the same time.
So there are two possibilites from my point of view. Either create a toString() member that does the actual job. This is a cleaner solution anyway than writing everything into operator<<() and make it a "friend" for full access. This toString() could then be used by both stream operators:
class T
{
std::string toString ()
{
return data;
}
};
ostream &operator<<(ostream &os, T x)
{
os << x.toString();
return os;
}
String &operator<<(String &s, T x)
{
s << x.toString();
return s;
}
The other and more convenient solution were an adapter to re-use ostream<<() in String<<(). But for this to work I have to think a bit more :-)
I finally found a solution for the use of std::ostream. It is not as elegant as I originally hoped but it should be usable. The macro MOCKPP_OSTREAMABLE bridges the gap between std::ostream and mockpp::String for a given class T:
MOCKPP_OSTREAMABLE(T)
As already suggested please download from anon cvs to try yourself and to give me feedback about your opinion. You need Formatter.h and Formatter_test.cpp (test case is at the end).
If you can't use cvs just drop me a personal mail and I will send you a tar ball.
I have been using the following adapter for a while :
template< typename T >
mockpp::String& operator<<( mockpp::String& formatter, const T& adaptee )
{
std::stringstream adapter;
adapter << adaptee;
return formatter << adapter.str();
}
Typically it's defined after including mockpp in my test program.
The trick is if a class defines :
friend mockpp::String& operator<<( mockpp::String& formatter, const MyCLass& myClass );
it will be used by mockpp
If the class defines :
friend std::ostream& operator<<( std::ostream& stream, const MyClass& myClass );
mockpp will wrap it with the adapter.
I have no idea if this is portable as I've only been using mockpp with msvc71 though.
mockpp implements the trick a bit differently with MOCKPP_OWSTREAMABLE(T). But I was not sure about the idea to offer this function as global template so you have to instantiate each needed type explicitly.