Tuesday, June 16, 2009

Google Mock Internals: Converting Matchers Safely

After spending the last 4 weeks on writing code, I'm finally ready to get back to blogging.  In my last post, I introduced the basic typing rules for Google Mock's matchers.  At the end of the post, the reader is left with this question:

When we use a composite matcher Not(m) in a context where a Matcher<T> is expected, what type should we require m to have?

As we have seen, it is too restrictive to demand m being a Matcher<T>, as it prevents you from using Not(an_int_matcher) as a Matcher<short>, even if you can convert the short argument to int before giving it to an_int_matcher.

To design the typing rule here in a non-restrictive yet safe way, we need to distinguish between two kinds of conversions between monomorphic matchers: safe conversions and unsafe conversions:

As an example of a safe conversion, you can use a Matcher<int> (let's call it m) as a Matcher<const int&>: you just pass the argument (typed as const int&) to m, which takes an int.  No information will be lost in the process.  The converse is not true: if you use a Matcher<const int&> as a Matcher<int>, you may get a wrong result as the Matcher<const int&> may be interested in the address of the argument (as in the example of Ref(x)).

But what dos it exactly mean for a conversion to be safe?

In general, if type A can be implicitly converted to type B, we can safely convert a Matcher<B> to a Matcher<A> (i.e. Matcher is contravariant): just keep a copy of the original Matcher<B>, convert the argument from type A to B, and then pass it to the underlying Matcher<B>.  There are two exceptions to this rule:
  1. When B is a reference and A is not, the conversion is unsafe as the underlying Matcher<B> may be interested in the argument's address, which is not preserved in the conversion from A to B.
  2. Lossy conversions between built-in numeric types are implicit in C++.  That is, as far as the compiler is concerned, you can cast a double to an int implicitly.  Therefore, for conversions between built-in numeric types, we impose the additional constraint that they must not be lossy (e.g. converting from int to long is fine, but vice versa is not).
If it's still unclear to you why #2 is necessary, consider this example:

Matcher<int> equals_5 = Eq(5);
Matcher<double> double_equals_5 = SafeMatcherCast<double>(equals_5);

If you use double_equals_5 to match 5.2, you'll get "yes", even though 5.2 isn't equal to 5.

Before we end this post, let recap the basic typing rules for matchers:
  • Matchers are statically typed.
  • A monomorphic matcher that matches an int value has type Matcher<int>.
  • Matcher<int> and Matcher<const int&> are different types: a matcher of the latter type can see the address of its argument, while a matcher of the former type can't.
  • A polymorphic matcher m has a type that can be implicitly converted to Matcher<T> if m can be used to match a T value.
  • A Matcher<B> can be safely converted to a Matcher<A> as long as A can be implicitly converted to B, the conversion is not from a non-reference to a reference, and it's not a lossy numeric conversion.

1 comment:

  1. Thank you for this post i had also read all your related to matchers. Its really good i got so many ideas.
    Also i got some articles from www.macrotesting.com it also helped me in this as your thank you....


Note: Only a member of this blog may post a comment.