| $$ -*- mode: c++; -*- |
| $$ This is a Pump source file. Please use Pump to convert it to |
| $$ gmock-generated-variadic-actions.h. |
| $$ |
| $var n = 10 $$ The maximum arity we support. |
| // Copyright 2008, Google Inc. |
| // All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following disclaimer |
| // in the documentation and/or other materials provided with the |
| // distribution. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| // Google Mock - a framework for writing C++ mock classes. |
| // |
| // This file implements some commonly used variadic matchers. |
| |
| #ifndef GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_MATCHERS_H_ |
| #define GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_MATCHERS_H_ |
| |
| #include <sstream> |
| #include <string> |
| #include <vector> |
| #include <gmock/gmock-matchers.h> |
| #include <gmock/gmock-printers.h> |
| |
| namespace testing { |
| namespace internal { |
| |
| // Implements ElementsAre() and ElementsAreArray(). |
| template <typename Container> |
| class ElementsAreMatcherImpl : public MatcherInterface<Container> { |
| public: |
| typedef GMOCK_REMOVE_CONST_(GMOCK_REMOVE_REFERENCE_(Container)) RawContainer; |
| typedef typename RawContainer::value_type Element; |
| |
| // Constructs the matcher from a sequence of element values or |
| // element matchers. |
| template <typename InputIter> |
| ElementsAreMatcherImpl(InputIter first, size_t count) { |
| matchers_.reserve(count); |
| InputIter it = first; |
| for (size_t i = 0; i != count; ++i, ++it) { |
| matchers_.push_back(MatcherCast<const Element&>(*it)); |
| } |
| } |
| |
| // Returns true iff 'container' matches. |
| virtual bool Matches(Container container) const { |
| if (container.size() != count()) |
| return false; |
| |
| typename RawContainer::const_iterator container_iter = container.begin(); |
| for (size_t i = 0; i != count(); ++container_iter, ++i) { |
| if (!matchers_[i].Matches(*container_iter)) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| // Describes what this matcher does. |
| virtual void DescribeTo(::std::ostream* os) const { |
| if (count() == 0) { |
| *os << "is empty"; |
| } else if (count() == 1) { |
| *os << "has 1 element that "; |
| matchers_[0].DescribeTo(os); |
| } else { |
| *os << "has " << Elements(count()) << " where\n"; |
| for (size_t i = 0; i != count(); ++i) { |
| *os << "element " << i << " "; |
| matchers_[i].DescribeTo(os); |
| if (i + 1 < count()) { |
| *os << ",\n"; |
| } |
| } |
| } |
| } |
| |
| // Describes what the negation of this matcher does. |
| virtual void DescribeNegationTo(::std::ostream* os) const { |
| if (count() == 0) { |
| *os << "is not empty"; |
| return; |
| } |
| |
| *os << "does not have " << Elements(count()) << ", or\n"; |
| for (size_t i = 0; i != count(); ++i) { |
| *os << "element " << i << " "; |
| matchers_[i].DescribeNegationTo(os); |
| if (i + 1 < count()) { |
| *os << ", or\n"; |
| } |
| } |
| } |
| |
| // Explains why 'container' matches, or doesn't match, this matcher. |
| virtual void ExplainMatchResultTo(Container container, |
| ::std::ostream* os) const { |
| if (Matches(container)) { |
| // We need to explain why *each* element matches (the obvious |
| // ones can be skipped). |
| |
| bool reason_printed = false; |
| typename RawContainer::const_iterator container_iter = container.begin(); |
| for (size_t i = 0; i != count(); ++container_iter, ++i) { |
| ::std::stringstream ss; |
| matchers_[i].ExplainMatchResultTo(*container_iter, &ss); |
| |
| const string s = ss.str(); |
| if (!s.empty()) { |
| if (reason_printed) { |
| *os << ",\n"; |
| } |
| *os << "element " << i << " " << s; |
| reason_printed = true; |
| } |
| } |
| } else { |
| // We need to explain why the container doesn't match. |
| const size_t actual_count = container.size(); |
| if (actual_count != count()) { |
| // The element count doesn't match. If the container is |
| // empty, there's no need to explain anything as Google Mock |
| // already prints the empty container. Otherwise we just need |
| // to show how many elements there actually are. |
| if (actual_count != 0) { |
| *os << "has " << Elements(actual_count); |
| } |
| return; |
| } |
| |
| // The container has the right size but at least one element |
| // doesn't match expectation. We need to find this element and |
| // explain why it doesn't match. |
| typename RawContainer::const_iterator container_iter = container.begin(); |
| for (size_t i = 0; i != count(); ++container_iter, ++i) { |
| if (matchers_[i].Matches(*container_iter)) { |
| continue; |
| } |
| |
| *os << "element " << i << " doesn't match"; |
| |
| ::std::stringstream ss; |
| matchers_[i].ExplainMatchResultTo(*container_iter, &ss); |
| const string s = ss.str(); |
| if (!s.empty()) { |
| *os << " (" << s << ")"; |
| } |
| return; |
| } |
| } |
| } |
| |
| private: |
| static Message Elements(size_t count) { |
| return Message() << count << (count == 1 ? " element" : " elements"); |
| } |
| |
| size_t count() const { return matchers_.size(); } |
| std::vector<Matcher<const Element&> > matchers_; |
| }; |
| |
| // Implements ElementsAre() of 0-10 arguments. |
| |
| class ElementsAreMatcher0 { |
| public: |
| ElementsAreMatcher0() {} |
| |
| template <typename Container> |
| operator Matcher<Container>() const { |
| typedef GMOCK_REMOVE_CONST_(GMOCK_REMOVE_REFERENCE_(Container)) |
| RawContainer; |
| typedef typename RawContainer::value_type Element; |
| |
| const Matcher<const Element&>* const matchers = NULL; |
| return MakeMatcher(new ElementsAreMatcherImpl<Container>(matchers, 0)); |
| } |
| }; |
| |
| |
| $range i 1..n |
| $for i [[ |
| $range j 1..i |
| template <$for j, [[typename T$j]]> |
| class ElementsAreMatcher$i { |
| public: |
| $if i==1 [[explicit ]]ElementsAreMatcher$i($for j, [[const T$j& e$j]])$if i > 0 [[ : ]] |
| $for j, [[e$j[[]]_(e$j)]] {} |
| |
| template <typename Container> |
| operator Matcher<Container>() const { |
| typedef GMOCK_REMOVE_CONST_(GMOCK_REMOVE_REFERENCE_(Container)) |
| RawContainer; |
| typedef typename RawContainer::value_type Element; |
| |
| const Matcher<const Element&> matchers[] = { |
| |
| $for j [[ |
| MatcherCast<const Element&>(e$j[[]]_), |
| |
| ]] |
| }; |
| |
| return MakeMatcher(new ElementsAreMatcherImpl<Container>(matchers, $i)); |
| } |
| |
| private: |
| |
| $for j [[ |
| const T$j& e$j[[]]_; |
| |
| ]] |
| }; |
| |
| |
| ]] |
| // Implements ElementsAreArray(). |
| template <typename T> |
| class ElementsAreArrayMatcher { |
| public: |
| ElementsAreArrayMatcher(const T* first, size_t count) : |
| first_(first), count_(count) {} |
| |
| template <typename Container> |
| operator Matcher<Container>() const { |
| typedef GMOCK_REMOVE_CONST_(GMOCK_REMOVE_REFERENCE_(Container)) |
| RawContainer; |
| typedef typename RawContainer::value_type Element; |
| |
| return MakeMatcher(new ElementsAreMatcherImpl<Container>(first_, count_)); |
| } |
| |
| private: |
| const T* const first_; |
| const size_t count_; |
| }; |
| |
| } // namespace internal |
| |
| // ElementsAre(e0, e1, ..., e_n) matches an STL-style container with |
| // (n + 1) elements, where the i-th element in the container must |
| // match the i-th argument in the list. Each argument of |
| // ElementsAre() can be either a value or a matcher. We support up to |
| // $n arguments. |
| // |
| // NOTE: Since ElementsAre() cares about the order of the elements, it |
| // must not be used with containers whose elements's order is |
| // undefined (e.g. hash_map). |
| |
| inline internal::ElementsAreMatcher0 ElementsAre() { |
| return internal::ElementsAreMatcher0(); |
| } |
| |
| $for i [[ |
| $range j 1..i |
| |
| template <$for j, [[typename T$j]]> |
| inline internal::ElementsAreMatcher$i<$for j, [[T$j]]> ElementsAre($for j, [[const T$j& e$j]]) { |
| return internal::ElementsAreMatcher$i<$for j, [[T$j]]>($for j, [[e$j]]); |
| } |
| |
| ]] |
| |
| // ElementsAreArray(array) and ElementAreArray(array, count) are like |
| // ElementsAre(), except that they take an array of values or |
| // matchers. The former form infers the size of 'array', which must |
| // be a static C-style array. In the latter form, 'array' can either |
| // be a static array or a pointer to a dynamically created array. |
| |
| template <typename T> |
| inline internal::ElementsAreArrayMatcher<T> ElementsAreArray( |
| const T* first, size_t count) { |
| return internal::ElementsAreArrayMatcher<T>(first, count); |
| } |
| |
| template <typename T, size_t N> |
| inline internal::ElementsAreArrayMatcher<T> |
| ElementsAreArray(const T (&array)[N]) { |
| return internal::ElementsAreArrayMatcher<T>(array, N); |
| } |
| |
| } // namespace testing |
| $$ } // This Pump meta comment fixes auto-indentation in Emacs. It will not |
| $$ // show up in the generated code. |
| |
| |
| // The MATCHER* family of macros can be used in a namespace scope to |
| // define custom matchers easily. The syntax: |
| // |
| // MATCHER(name, description_string) { statements; } |
| // |
| // will define a matcher with the given name that executes the |
| // statements, which must return a bool to indicate if the match |
| // succeeds. Inside the statements, you can refer to the value being |
| // matched by 'arg', and refer to its type by 'arg_type'. |
| // |
| // The description string documents what the matcher does, and is used |
| // to generate the failure message when the match fails. Since a |
| // MATCHER() is usually defined in a header file shared by multiple |
| // C++ source files, we require the description to be a C-string |
| // literal to avoid possible side effects. It can be empty, in which |
| // case we'll use the sequence of words in the matcher name as the |
| // description. |
| // |
| // For example: |
| // |
| // MATCHER(IsEven, "") { return (arg % 2) == 0; } |
| // |
| // allows you to write |
| // |
| // // Expects mock_foo.Bar(n) to be called where n is even. |
| // EXPECT_CALL(mock_foo, Bar(IsEven())); |
| // |
| // or, |
| // |
| // // Verifies that the value of some_expression is even. |
| // EXPECT_THAT(some_expression, IsEven()); |
| // |
| // If the above assertion fails, it will print something like: |
| // |
| // Value of: some_expression |
| // Expected: is even |
| // Actual: 7 |
| // |
| // where the description "is even" is automatically calculated from the |
| // matcher name IsEven. |
| // |
| // Note that the type of the value being matched (arg_type) is |
| // determined by the context in which you use the matcher and is |
| // supplied to you by the compiler, so you don't need to worry about |
| // declaring it (nor can you). This allows the matcher to be |
| // polymorphic. For example, IsEven() can be used to match any type |
| // where the value of "(arg % 2) == 0" can be implicitly converted to |
| // a bool. In the "Bar(IsEven())" example above, if method Bar() |
| // takes an int, 'arg_type' will be int; if it takes an unsigned long, |
| // 'arg_type' will be unsigned long; and so on. |
| // |
| // Sometimes you'll want to parameterize the matcher. For that you |
| // can use another macro: |
| // |
| // MATCHER_P(name, param_name, description_string) { statements; } |
| // |
| // For example: |
| // |
| // MATCHER_P(HasAbsoluteValue, value, "") { return abs(arg) == value; } |
| // |
| // will allow you to write: |
| // |
| // EXPECT_THAT(Blah("a"), HasAbsoluteValue(n)); |
| // |
| // which may lead to this message (assuming n is 10): |
| // |
| // Value of: Blah("a") |
| // Expected: has absolute value 10 |
| // Actual: -9 |
| // |
| // Note that both the matcher description and its parameter are |
| // printed, making the message human-friendly. |
| // |
| // In the matcher definition body, you can write 'foo_type' to |
| // reference the type of a parameter named 'foo'. For example, in the |
| // body of MATCHER_P(HasAbsoluteValue, value) above, you can write |
| // 'value_type' to refer to the type of 'value'. |
| // |
| // We also provide MATCHER_P2, MATCHER_P3, ..., up to MATCHER_P$n to |
| // support multi-parameter matchers. |
| // |
| // When defining a parameterized matcher, you can use Python-style |
| // interpolations in the description string to refer to the parameter |
| // values. We support the following syntax currently: |
| // |
| // %% a single '%' character |
| // %(*)s all parameters of the matcher printed as a tuple |
| // %(foo)s value of the matcher parameter named 'foo' |
| // |
| // For example, |
| // |
| // MATCHER_P2(InClosedRange, low, hi, "is in range [%(low)s, %(hi)s]") { |
| // return low <= arg && arg <= hi; |
| // } |
| // ... |
| // EXPECT_THAT(3, InClosedRange(4, 6)); |
| // |
| // would generate a failure that contains the message: |
| // |
| // Expected: is in range [4, 6] |
| // |
| // If you specify "" as the description, the failure message will |
| // contain the sequence of words in the matcher name followed by the |
| // parameter values printed as a tuple. For example, |
| // |
| // MATCHER_P2(InClosedRange, low, hi, "") { ... } |
| // ... |
| // EXPECT_THAT(3, InClosedRange(4, 6)); |
| // |
| // would generate a failure that contains the text: |
| // |
| // Expected: in closed range (4, 6) |
| // |
| // For the purpose of typing, you can view |
| // |
| // MATCHER_Pk(Foo, p1, ..., pk, description_string) { ... } |
| // |
| // as shorthand for |
| // |
| // template <typename p1_type, ..., typename pk_type> |
| // FooMatcherPk<p1_type, ..., pk_type> |
| // Foo(p1_type p1, ..., pk_type pk) { ... } |
| // |
| // When you write Foo(v1, ..., vk), the compiler infers the types of |
| // the parameters v1, ..., and vk for you. If you are not happy with |
| // the result of the type inference, you can specify the types by |
| // explicitly instantiating the template, as in Foo<long, bool>(5, |
| // false). As said earlier, you don't get to (or need to) specify |
| // 'arg_type' as that's determined by the context in which the matcher |
| // is used. You can assign the result of expression Foo(p1, ..., pk) |
| // to a variable of type FooMatcherPk<p1_type, ..., pk_type>. This |
| // can be useful when composing matchers. |
| // |
| // While you can instantiate a matcher template with reference types, |
| // passing the parameters by pointer usually makes your code more |
| // readable. If, however, you still want to pass a parameter by |
| // reference, be aware that in the failure message generated by the |
| // matcher you will see the value of the referenced object but not its |
| // address. |
| // |
| // You can overload matchers with different numbers of parameters: |
| // |
| // MATCHER_P(Blah, a, description_string1) { ... } |
| // MATCHER_P2(Blah, a, b, description_string2) { ... } |
| // |
| // While it's tempting to always use the MATCHER* macros when defining |
| // a new matcher, you should also consider implementing |
| // MatcherInterface or using MakePolymorphicMatcher() instead, |
| // especially if you need to use the matcher a lot. While these |
| // approaches require more work, they give you more control on the |
| // types of the value being matched and the matcher parameters, which |
| // in general leads to better compiler error messages that pay off in |
| // the long run. They also allow overloading matchers based on |
| // parameter types (as opposed to just based on the number of |
| // parameters). |
| // |
| // CAVEAT: |
| // |
| // MATCHER*() can only be used in a namespace scope. The reason is |
| // that C++ doesn't yet allow function-local types to be used to |
| // instantiate templates. The up-coming C++0x standard will fix this. |
| // Once that's done, we'll consider supporting using MATCHER*() inside |
| // a function. |
| // |
| // MORE INFORMATION: |
| // |
| // To learn more about using these macros, please search for 'MATCHER' |
| // on http://code.google.com/p/googlemock/wiki/CookBook. |
| |
| namespace testing { |
| namespace internal { |
| |
| // Constants denoting interpolations in a matcher description string. |
| const int kTupleInterpolation = -1; // "%(*)s" |
| const int kPercentInterpolation = -2; // "%%" |
| const int kInvalidInterpolation = -3; // "%" followed by invalid text |
| |
| // Records the location and content of an interpolation. |
| struct Interpolation { |
| Interpolation(const char* start, const char* end, int param) |
| : start_pos(start), end_pos(end), param_index(param) {} |
| |
| // Points to the start of the interpolation (the '%' character). |
| const char* start_pos; |
| // Points to the first character after the interpolation. |
| const char* end_pos; |
| // 0-based index of the interpolated matcher parameter; |
| // kTupleInterpolation for "%(*)s"; kPercentInterpolation for "%%". |
| int param_index; |
| }; |
| |
| typedef ::std::vector<Interpolation> Interpolations; |
| |
| // Parses a matcher description string and returns a vector of |
| // interpolations that appear in the string; generates non-fatal |
| // failures iff 'description' is an invalid matcher description. |
| // 'param_names' is a NULL-terminated array of parameter names in the |
| // order they appear in the MATCHER_P*() parameter list. |
| Interpolations ValidateMatcherDescription( |
| const char* param_names[], const char* description); |
| |
| // Returns the actual matcher description, given the matcher name, |
| // user-supplied description template string, interpolations in the |
| // string, and the printed values of the matcher parameters. |
| string FormatMatcherDescription( |
| const char* matcher_name, const char* description, |
| const Interpolations& interp, const Strings& param_values); |
| |
| } // namespace internal |
| } // namespace testing |
| |
| $range i 0..n |
| $for i |
| |
| [[ |
| $var macro_name = [[$if i==0 [[MATCHER]] $elif i==1 [[MATCHER_P]] |
| $else [[MATCHER_P$i]]]] |
| $var class_name = [[name##Matcher[[$if i==0 [[]] $elif i==1 [[P]] |
| $else [[P$i]]]]]] |
| $range j 0..i-1 |
| $var template = [[$if i==0 [[]] $else [[ |
| |
| template <$for j, [[typename p$j##_type]]>\ |
| ]]]] |
| $var ctor_param_list = [[$for j, [[p$j##_type gmock_p$j]]]] |
| $var impl_ctor_param_list = [[$for j [[p$j##_type gmock_p$j, ]] |
| const ::testing::internal::Interpolations& gmock_interp]] |
| $var impl_inits = [[ : $for j [[p$j(gmock_p$j), ]]gmock_interp_(gmock_interp)]] |
| $var inits = [[$if i==0 [[]] $else [[ : $for j, [[p$j(gmock_p$j)]]]]]] |
| $var params_and_interp = [[$for j [[p$j, ]]gmock_interp_]] |
| $var params = [[$for j, [[p$j]]]] |
| $var param_types = [[$if i==0 [[]] $else [[<$for j, [[p$j##_type]]>]]]] |
| $var param_types_and_names = [[$for j, [[p$j##_type p$j]]]] |
| $var param_field_decls = [[$for j |
| [[ |
| |
| p$j##_type p$j;\ |
| ]]]] |
| $var param_field_decls2 = [[$for j |
| [[ |
| |
| p$j##_type p$j;\ |
| ]]]] |
| |
| #define $macro_name(name$for j [[, p$j]], description)\$template |
| class $class_name {\ |
| public:\ |
| template <typename arg_type>\ |
| class gmock_Impl : public ::testing::MatcherInterface<arg_type> {\ |
| public:\ |
| [[$if i==1 [[explicit ]]]]gmock_Impl($impl_ctor_param_list)\ |
| $impl_inits {}\ |
| virtual bool Matches(arg_type arg) const;\ |
| virtual void DescribeTo(::std::ostream* gmock_os) const {\ |
| const ::testing::internal::Strings& gmock_printed_params = \ |
| ::testing::internal::UniversalTersePrintTupleFieldsToStrings(\ |
| ::std::tr1::tuple<$for j, [[p$j##_type]]>($for j, [[p$j]]));\ |
| *gmock_os << ::testing::internal::FormatMatcherDescription(\ |
| #name, description, gmock_interp_, gmock_printed_params);\ |
| }\$param_field_decls |
| const ::testing::internal::Interpolations gmock_interp_;\ |
| };\ |
| template <typename arg_type>\ |
| operator ::testing::Matcher<arg_type>() const {\ |
| return ::testing::Matcher<arg_type>(\ |
| new gmock_Impl<arg_type>($params_and_interp));\ |
| }\ |
| $class_name($ctor_param_list)$inits {\ |
| const char* gmock_param_names[] = { $for j [[#p$j, ]]NULL };\ |
| gmock_interp_ = ::testing::internal::ValidateMatcherDescription(\ |
| gmock_param_names, ("" description ""));\ |
| }\$param_field_decls2 |
| ::testing::internal::Interpolations gmock_interp_;\ |
| };\$template |
| inline $class_name$param_types name($param_types_and_names) {\ |
| return $class_name$param_types($params);\ |
| }\$template |
| template <typename arg_type>\ |
| bool $class_name$param_types::\ |
| gmock_Impl<arg_type>::Matches(arg_type arg) const |
| ]] |
| |
| |
| namespace testing { |
| namespace internal { |
| |
| // Returns true iff element is in the STL-style container. |
| template <typename Container, typename Element> |
| inline bool Contains(const Container& container, const Element& element) { |
| return ::std::find(container.begin(), container.end(), element) != |
| container.end(); |
| } |
| |
| // Returns true iff element is in the C-style array. |
| template <typename ArrayElement, size_t N, typename Element> |
| inline bool Contains(const ArrayElement (&array)[N], const Element& element) { |
| return ::std::find(array, array + N, element) != array + N; |
| } |
| |
| } // namespace internal |
| |
| // Matches an STL-style container or a C-style array that contains the given |
| // element. |
| // |
| // Examples: |
| // ::std::set<int> page_ids; |
| // page_ids.insert(3); |
| // page_ids.insert(1); |
| // EXPECT_THAT(page_ids, Contains(1)); |
| // EXPECT_THAT(page_ids, Contains(3.0)); |
| // EXPECT_THAT(page_ids, Not(Contains(4))); |
| // |
| // ::std::map<int, size_t> page_lengths; |
| // page_lengths[1] = 100; |
| // EXPECT_THAT(map_int, Contains(::std::pair<const int, size_t>(1, 100))); |
| // |
| // const char* user_ids[] = { "joe", "mike", "tom" }; |
| // EXPECT_THAT(user_ids, Contains(::std::string("tom"))); |
| MATCHER_P(Contains, element, "") { |
| return internal::Contains(arg, element); |
| } |
| |
| } // namespace testing |
| |
| #endif // GMOCK_INCLUDE_GMOCK_GMOCK_GENERATED_MATCHERS_H_ |