I am trying to port Google Test (gtest) code to VxWorks 5.5. The serious drawback is that development environment Tornado 2.2 uses ancient GCC compiler version 2.96.
While analyzing the code I've located part of the code in gtest.h
I do not understand! How this C++ template class is functioning?
// ImplicitlyConvertible<From, To>::value is a compile-time bool
// constant that's true iff type From can be implicitly converted to
// type To.
template <typename From, typename To>
class ImplicitlyConvertible {
private:
// We need the following helper functions only for their types.
// They have no implementations.
// MakeFrom() is an expression whose type is From. We cannot simply
// use From(), as the type From may not have a public default
// constructor.
static From MakeFrom();
// These two functions are overloaded. Given an expression
// Helper(x), the compiler will pick the first version if x can be
// implicitly converted to type To; otherwise it will pick the
// second version.
//
// The first version returns a value of size 1, and the second
// version returns a value of size 2. Therefore, by checking the
// size of Helper(x), which can be done at compile time, we can tell
// which version of Helper() is used, and hence whether x can be
// implicitly converted to type To.
static char Helper(To);
static char (&Helper(...))[2]; // NOLINT
// We have to put the 'public' section after the 'private' section,
// or MSVC refuses to compile the code.
public:
// MSVC warns about implicitly converting from double to int for
// possible loss of data, so we need to temporarily disable the
// warning.
#ifdef _MSC_VER
# pragma warning(push) // Saves the current warning state.
# pragma warning(disable:4244) // Temporarily disables warning 4244.
static const bool value =
sizeof(Helper(ImplicitlyConvertible::MakeFrom())) == 1;
# pragma warning(pop) // Restores the warning state.
#elif defined(__BORLANDC__)
// C++Builder cannot use member overload resolution during template
// instantiation. The simplest workaround is to use its C++0x type traits
// functions (C++Builder 2009 and above only).
static const bool value = __is_convertible(From, To);
#else
static const bool value =
sizeof(Helper(ImplicitlyConvertible::MakeFrom())) == 1;
#endif // _MSV_VER
};
When object of this class is created, boolean variable with the name value
should contain the answer if template type From
is implicitly convertible to template type To
. To get the answer, two private functions are used, MakeFrom()
and Helper()
. But these two functions are only declared here, and I cannot find definition for neither of them. If nothing else, this implementation should not link.
Neither do I understand the syntax of the following
static char (&Helper(...))[2];
Of course, this code compiles just fine (under Microsoft Visual C++ 7.1 or newer or GCC 3.4 or newer) and guys at Google know exactly what they are doing.
Please enlighten me! Not understanding this code will make me go crazy! :)
This is a standard trick with template programming.
Note that the comments say "by checking the size of Helper(x)": this underscores that the only thing the code does with Helper
is evaluate sizeof(Helper(x))
for some x
. The sizeof
operator does not actually evaluate its argument (it doesn't need to; it only needs to find out how large it is, which is possible using only information available at compile time) and this is why there is no linker error (Helper
is never really called).
The syntax that gives you trouble means that Helper
is a function that accepts any number and type of parameters and returns a reference to a char[2]
. To write a signature for this type of function (a variadic function) one needs to use ellipsis (...
) as the specification for the last argument.
Variadic functions are a feature inherited from C that should generally be avoided and that wreaks havoc when used with class types, but in this case it does not matter because -- as mentioned earlier -- Helper
will not be actually called.
The class ties this all together by allowing you to use the syntax
ImplicitlyConvertible<From, To>::value
To produce value
, the code "fakes" calling Helper
and passing it an instance of From
as an argument¹. It relies on the compiler's overload resolution to determine if the overload that takes a To
would be called in this scenario; if so, the return value of that overload is char
which has a guaranteed size of 1
and value
ends up being true
. Otherwise the variadic overload (which can take any type of argument) is selected, which returns a char[2]
. This has a size greater than 1
, so value
ends up false
.
¹ Note that here the "sizeof
does not actually evaluate the expression" trick is used again: how do you tell the compiler that the argument to Helper
is an instance of From
? You could use From()
, but then From
would need to have a default public constructor for the code to compile. So you just tell the compiler "I have a function MakeFrom
that returns a From
" -- the function will not be actually called.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With