参数化测试or数据驱动可以利用大量的数据跑一个具体的Case,有利于发现问题。至于Google test如何写数据驱动,请参考google的文档。
先介绍求Prime素数的类:
// The prime table interface. class PrimeTable { public: virtual ~PrimeTable() {} // Returns true iff n is a prime number. virtual bool IsPrime(int n) const = 0; // Returns the smallest prime number greater than p; or returns -1 // if the next prime is beyond the capacity of the table. virtual int GetNextPrime(int p) const = 0; }; // Implementation #1 calculates the primes on-the-fly. class OnTheFlyPrimeTable : public PrimeTable { public: virtual bool IsPrime(int n) const { if (n <= 1) return false; for (int i = 2; i*i <= n; i++) { // n is divisible by an integer other than 1 and itself. if ((n % i) == 0) return false; } return true; } virtual int GetNextPrime(int p) const { for (int n = p + 1; n > 0; n++) { if (IsPrime(n)) return n; } return -1; } }; // Implementation #2 pre-calculates the primes and stores the result // in an array. class PreCalculatedPrimeTable : public PrimeTable { public: // 'max' specifies the maximum number the prime table holds. explicit PreCalculatedPrimeTable(int max) : is_prime_size_(max + 1), is_prime_(new bool[max + 1]) { CalculatePrimesUpTo(max); } virtual ~PreCalculatedPrimeTable() { delete[] is_prime_; } virtual bool IsPrime(int n) const { return 0 <= n && n < is_prime_size_ && is_prime_[n]; } virtual int GetNextPrime(int p) const { for (int n = p + 1; n < is_prime_size_; n++) { if (is_prime_[n]) return n; } return -1; } private: void CalculatePrimesUpTo(int max) { ::std::fill(is_prime_, is_prime_ + is_prime_size_, true); is_prime_[0] = is_prime_[1] = false; for (int i = 2; i <= max; i++) { if (!is_prime_[i]) continue; // Marks all multiples of i (except i itself) as non-prime. for (int j = 2*i; j <= max; j += i) { is_prime_[j] = false; } } } const int is_prime_size_; bool* const is_prime_; // Disables compiler warning "assignment operator could not be generated." void operator=(const PreCalculatedPrimeTable& rhs); };
下面开始写具体的Case:
typedef PrimeTable* CreatePrimeTableFunc(); PrimeTable* CreateOnTheFlyPrimeTable() { return new OnTheFlyPrimeTable(); } template <size_t max_precalculated> PrimeTable* CreatePreCalculatedPrimeTable() { return new PreCalculatedPrimeTable(max_precalculated); } class PrimeTableTest : public TestWithParam<CreatePrimeTableFunc*> { public: virtual ~PrimeTableTest() { delete table_; } virtual void SetUp() { table_ = (*GetParam())(); } virtual void TearDown() { delete table_; table_ = NULL; } protected: PrimeTable* table_;
测试的Case如下所示:
TEST_P(PrimeTableTest, ReturnsFalseForNonPrimes) { EXPECT_FALSE(table_->IsPrime(-5)); } TEST_P(PrimeTableTest, ReturnsTrueForPrimes) { EXPECT_TRUE(table_->IsPrime(2)); } TEST_P(PrimeTableTest, CanGetNextPrime) { EXPECT_EQ(2, table_->GetNextPrime(0)); } INSTANTIATE_TEST_CASE_P( OnTheFlyAndPreCalculated, PrimeTableTest, Values(&CreateOnTheFlyPrimeTable, &CreatePreCalculatedPrimeTable<1000>));
按照上一篇文档的方法进行宏扩展,扩展后Case的具体实现如下:
class PrimeTableTest_ReturnsFalseForNonPrimes_Test : public PrimeTableTest { public: PrimeTableTest_ReturnsFalseForNonPrimes_Test() {} virtual void TestBody(); private: static int AddToRegistry() { ::testing::UnitTest::GetInstance()->parameterized_test_registry(). GetTestCasePatternHolder<PrimeTableTest>( "PrimeTableTest", "d:\\gtest\\gtest_all\\gtest_all\\sample7_unittest.cc", 50)->AddTestPattern( "PrimeTableTest", "ReturnsFalseForNonPrimes", new ::testing::internal::TestMetaFactory< PrimeTableTest_ReturnsFalseForNonPrimes_Test>() ); return 0; } static int gtest_registering_dummy_; PrimeTableTest_ReturnsFalseForNonPrimes_Test(PrimeTableTest_ReturnsFalseForNonPrimes_Test const &); void operator=(PrimeTableTest_ReturnsFalseForNonPrimes_Test const &); }; int PrimeTableTest_ReturnsFalseForNonPrimes_Test::gtest_registering_dummy_ = PrimeTableTest_ReturnsFalseForNonPrimes_Test::AddToRegistry(); void PrimeTableTest_ReturnsFalseForNonPrimes_Test::TestBody() { switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(!(table_->IsPrime(-5)))) ; else ::testing::internal::AssertHelper( ::testing::TestPartResult::kNonFatalFailure, "d:\\gtest\\gtest_all\\gtest_all\\sample7_unittest.cc", 51, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_, "table_->IsPrime(-5)", "true", "false" ).c_str() ) = ::testing::Message(); } // // class PrimeTableTest_ReturnsTrueForPrimes_Test : public PrimeTableTest { public: PrimeTableTest_ReturnsTrueForPrimes_Test() {} virtual void TestBody(); private: static int AddToRegistry() { ::testing::UnitTest::GetInstance()->parameterized_test_registry(). GetTestCasePatternHolder<PrimeTableTest>( "PrimeTableTest", "d:\\gtest\\gtest_all\\gtest_all\\sample7_unittest.cc", 59)->AddTestPattern( "PrimeTableTest", "ReturnsTrueForPrimes", new ::testing::internal::TestMetaFactory< PrimeTableTest_ReturnsTrueForPrimes_Test>()); return 0; } static int gtest_registering_dummy_; PrimeTableTest_ReturnsTrueForPrimes_Test(PrimeTableTest_ReturnsTrueForPrimes_Test const &); void operator=(PrimeTableTest_ReturnsTrueForPrimes_Test const &); }; int PrimeTableTest_ReturnsTrueForPrimes_Test::gtest_registering_dummy_ = PrimeTableTest_ReturnsTrueForPrimes_Test::AddToRegistry(); void PrimeTableTest_ReturnsTrueForPrimes_Test::TestBody() { switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(table_->IsPrime(2))) ; else ::testing::internal::AssertHelper( ::testing::TestPartResult::kNonFatalFailure, "d:\\gtest\\gtest_all\\gtest_all\\sample7_unittest.cc", 60, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_, "table_->IsPrime(2)", "false", "true" ).c_str() ) = ::testing::Message(); } // // class PrimeTableTest_CanGetNextPrime_Test : public PrimeTableTest { public: PrimeTableTest_CanGetNextPrime_Test() {} virtual void TestBody(); private: static int AddToRegistry() { ::testing::UnitTest::GetInstance()->parameterized_test_registry(). GetTestCasePatternHolder<PrimeTableTest>( "PrimeTableTest", "d:\\gtest\\gtest_all\\gtest_all\\sample7_unittest.cc", 68)->AddTestPattern( "PrimeTableTest", "CanGetNextPrime", new ::testing::internal::TestMetaFactory< PrimeTableTest_CanGetNextPrime_Test>()); return 0; } static int gtest_registering_dummy_; PrimeTableTest_CanGetNextPrime_Test(PrimeTableTest_CanGetNextPrime_Test const &); void operator=(PrimeTableTest_CanGetNextPrime_Test const &); }; int PrimeTableTest_CanGetNextPrime_Test::gtest_registering_dummy_ = PrimeTableTest_CanGetNextPrime_Test::AddToRegistry(); void PrimeTableTest_CanGetNextPrime_Test::TestBody() { switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar = (::testing::internal:: EqHelper<(sizeof(::testing::internal::IsNullLiteralHelper(2)) == 1)>::Compare( "2", "table_->GetNextPrime(0)", 2, table_->GetNextPrime(0) ) ) ) ; else ::testing::internal::AssertHelper( ::testing::TestPartResult::kNonFatalFailure, "d:\\gtest\\gtest_all\\gtest_all\\sample7_unittest.cc", 69, gtest_ar.failure_message() ) = ::testing::Message(); } // // ::testing::internal::ParamGenerator<PrimeTableTest::ParamType> gtest_OnTheFlyAndPreCalculatedPrimeTableTest_EvalGenerator_() { return Values(&CreateOnTheFlyPrimeTable, &CreatePreCalculatedPrimeTable<1000>); } int gtest_OnTheFlyAndPreCalculatedPrimeTableTest_dummy_ = ::testing::UnitTest::GetInstance()->parameterized_test_registry(). GetTestCasePatternHolder<PrimeTableTest>( "PrimeTableTest", "d:\\gtest\\gtest_all\\gtest_all\\sample7_unittest.cc", 87)->AddTestCaseInstantiation( "OnTheFlyAndPreCalculated", >est_OnTheFlyAndPreCalculatedPrimeTableTest_EvalGenerator_, "d:\\gtest\\gtest_all\\gtest_all\\sample7_unittest.cc", 87);
(一)在主线程进入main函数之前首先初始化类中的静态变量。因此首先会执行:
int PrimeTableTest_ReturnsFalseForNonPrimes_Test::gtest_registering_dummy_ = PrimeTableTest_ReturnsFalseForNonPrimes_Test::AddToRegistry();
AddToRegistry的对应代码如下:
static int AddToRegistry() { ::testing::UnitTest::GetInstance()->parameterized_test_registry(). GetTestCasePatternHolder<PrimeTableTest>( "PrimeTableTest", "d:\\gtest\\gtest_all\\gtest_all\\sample7_unittest.cc", 59)->AddTestPattern( "PrimeTableTest", "ReturnsTrueForPrimes", new ::testing::internal::TestMetaFactory< PrimeTableTest_ReturnsTrueForPrimes_Test>()); return 0; }
GetTestCasePatternHolder和AddTestPattern方法如下:
class ParameterizedTestCaseRegistry { … … // Looks up or creates and returns a structure containing information about // tests and instantiations of a particular test case. template <class TestCase> ParameterizedTestCaseInfo<TestCase>* GetTestCasePatternHolder( const char* test_case_name, const char* file, int line) { ParameterizedTestCaseInfo<TestCase>* typed_test_info = NULL; for (TestCaseInfoContainer::iterator it = test_case_infos_.begin(); it != test_case_infos_.end(); ++it) { if ((*it)->GetTestCaseName() == test_case_name) { if ((*it)->GetTestCaseTypeId() != GetTypeId<TestCase>()) { // Complain about incorrect usage of Google Test facilities // and terminate the program since we cannot guaranty correct // test case setup and tear-down in this case. ReportInvalidTestCaseType(test_case_name, file, line); posix::Abort(); } else { // At this point we are sure that the object we found is of the same // type we are looking for, so we downcast it to that type // without further checks. typed_test_info = CheckedDowncastToActualType< ParameterizedTestCaseInfo<TestCase> >(*it); } break; } } if (typed_test_info == NULL) { typed_test_info = new ParameterizedTestCaseInfo<TestCase>(test_case_name); test_case_infos_.push_back(typed_test_info); } return typed_test_info; } … … }
template <class TestCase> class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase { … void AddTestPattern(const char* test_case_name, const char* test_base_name, TestMetaFactoryBase<ParamType>* meta_factory) { tests_.push_back(linked_ptr<TestInfo>(new TestInfo(test_case_name, test_base_name, meta_factory))); } … }
(二)在进入main函数之前,在初始化上面的类中静态变量之后,将初始化全局变量:
int gtest_OnTheFlyAndPreCalculatedPrimeTableTest_dummy_ = ::testing::UnitTest::GetInstance()->parameterized_test_registry(). GetTestCasePatternHolder<PrimeTableTest>( "PrimeTableTest", "d:\\gtest\\gtest_all\\gtest_all\\sample7_unittest.cc", 87)->AddTestCaseInstantiation( "OnTheFlyAndPreCalculated", >est_OnTheFlyAndPreCalculatedPrimeTableTest_EvalGenerator_, "d:\\gtest\\gtest_all\\gtest_all\\sample7_unittest.cc", 87);
AddTestCaseInstantiation的方法实现如下:
template <class TestCase> class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase { … // INSTANTIATE_TEST_CASE_P macro uses AddGenerator() to record information // about a generator. int AddTestCaseInstantiation(const string& instantiation_name, GeneratorCreationFunc* func, const char* /* file */, int /* line */) { instantiations_.push_back(::std::make_pair(instantiation_name, func)); return 0; // Return value used only to run this method in namespace scope. } … }
(三)进入main函数之后的执行流程如下:
int main(int argc, char **argv) { //testing::FLAGS_gtest_filter = "OnTheFlyAndPreCalculated*"; testing::InitGoogleTest(&argc, argv); return (::testing::UnitTest::GetInstance()->Run()); }
void InitGoogleTest(int* argc, char** argv) { internal::InitGoogleTestImpl(argc, argv); }
// The internal implementation of InitGoogleTest(). // // The type parameter CharType can be instantiated to either char or // wchar_t. template <typename CharType> void InitGoogleTestImpl(int* argc, CharType** argv) { … GetUnitTestImpl()->PostFlagParsingInit(); } // Performs initialization dependent upon flag values obtained in // ParseGoogleTestFlagsOnly. Is called from InitGoogleTest after the call to // ParseGoogleTestFlagsOnly. In case a user neglects to call InitGoogleTest // this function is also called from RunAllTests. Since this function can be // called more than once, it has to be idempotent. void UnitTestImpl::PostFlagParsingInit() { … // Registers parameterized tests. This makes parameterized tests // available to the UnitTest reflection API without running // RUN_ALL_TESTS. RegisterParameterizedTests(); … }
// This method expands all parameterized tests registered with macros TEST_P // and INSTANTIATE_TEST_CASE_P into regular tests and registers those. // This will be done just once during the program runtime. void UnitTestImpl::RegisterParameterizedTests() { #if GTEST_HAS_PARAM_TEST if (!parameterized_tests_registered_) { parameterized_test_registry_.RegisterTests(); parameterized_tests_registered_ = true; } #endif }
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. // // ParameterizedTestCaseRegistry contains a map of ParameterizedTestCaseInfoBase // classes accessed by test case names. TEST_P and INSTANTIATE_TEST_CASE_P // macros use it to locate their corresponding ParameterizedTestCaseInfo // descriptors. class ParameterizedTestCaseRegistry { … void RegisterTests() { for (TestCaseInfoContainer::iterator it = test_case_infos_.begin(); it != test_case_infos_.end(); ++it) { (*it)->RegisterTests(); } }//注意对照上面的ParameterizedTestCaseRegistry::GetTestCasePatternHolder方法 … };
注意:下面的RegisterTests方法和上面已经介绍的AddTestPattern,AddTestCaseInstantiation同属于类ParameterizedTestCaseInfo。
template <class TestCase> class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase { public: … // TEST_P macro uses AddTestPattern() to record information // about a single test in a LocalTestInfo structure. // test_case_name is the base name of the test case (without invocation // prefix). test_base_name is the name of an individual test without // parameter index. For the test SequenceA/FooTest.DoBar/1 FooTest is // test case base name and DoBar is test base name. void AddTestPattern(const char* test_case_name, const char* test_base_name, TestMetaFactoryBase<ParamType>* meta_factory) { tests_.push_back(linked_ptr<TestInfo>(new TestInfo(test_case_name, test_base_name, meta_factory))); } // INSTANTIATE_TEST_CASE_P macro uses AddGenerator() to record information // about a generator. int AddTestCaseInstantiation(const string& instantiation_name, GeneratorCreationFunc* func, const char* /* file */, int /* line */) { instantiations_.push_back(::std::make_pair(instantiation_name, func)); return 0; // Return value used only to run this method in namespace scope. } // UnitTest class invokes this method to register tests in this test case // test cases right before running tests in RUN_ALL_TESTS macro. // This method should not be called more then once on any single // instance of a ParameterizedTestCaseInfoBase derived class. // UnitTest has a guard to prevent from calling this method more then once. virtual void RegisterTests() { for (typename TestInfoContainer::iterator test_it = tests_.begin(); test_it != tests_.end(); ++test_it) {////很显然,tests_是在AddTestPattern方法中增加元素的。 linked_ptr<TestInfo> test_info = *test_it; for (typename InstantiationContainer::iterator gen_it = instantiations_.begin(); gen_it != instantiations_.end(); ++gen_it) { //很显然,instantiations_是在AddTestCaseInstantiation方法中增加元素的。 const string& instantiation_name = gen_it->first; ParamGenerator<ParamType> generator((*gen_it->second)()); Message test_case_name_stream; if ( !instantiation_name.empty() ) test_case_name_stream << instantiation_name << "/"; test_case_name_stream << test_info->test_case_base_name; int i = 0; for (typename ParamGenerator<ParamType>::iterator param_it = generator.begin(); param_it != generator.end(); ++param_it, ++i) { Message test_name_stream; test_name_stream << test_info->test_base_name << "/" << i; MakeAndRegisterTestInfo( test_case_name_stream.GetString().c_str(), test_name_stream.GetString().c_str(), NULL, // No type parameter. PrintToString(*param_it).c_str(), GetTestCaseTypeId(), TestCase::SetUpTestCase, TestCase::TearDownTestCase, test_info->test_meta_factory->CreateTestFactory(*param_it)); } // for param_it } // for gen_it } // for test_it } // RegisterTests private: // LocalTestInfo structure keeps information about a single test registered // with TEST_P macro. struct TestInfo { TestInfo(const char* a_test_case_base_name, const char* a_test_base_name, TestMetaFactoryBase<ParamType>* a_test_meta_factory) : test_case_base_name(a_test_case_base_name), test_base_name(a_test_base_name), test_meta_factory(a_test_meta_factory) {} const string test_case_base_name; const string test_base_name; const scoped_ptr<TestMetaFactoryBase<ParamType> > test_meta_factory; }; typedef ::std::vector<linked_ptr<TestInfo> > TestInfoContainer; // Keeps pairs of <Instantiation name, Sequence generator creation function> // received from INSTANTIATE_TEST_CASE_P macros. typedef ::std::vector<std::pair<string, GeneratorCreationFunc*> > InstantiationContainer; InstantiationContainer instantiations_; … }; // class ParameterizedTestCaseInfo
MakeAndRegisterTestInfo方法正是我们在上一篇分析中介绍的方法。
TestInfo* MakeAndRegisterTestInfo( const char* test_case_name, const char* name, const char* type_param, const char* value_param, TypeId fixture_class_id, SetUpTestCaseFunc set_up_tc, TearDownTestCaseFunc tear_down_tc, TestFactoryBase* factory) { TestInfo* const test_info = new TestInfo(test_case_name, name, type_param, value_param, fixture_class_id, factory); GetUnitTestImpl()->AddTestInfo(set_up_tc, tear_down_tc, test_info); return test_info; }
在结束本文之前, 我们对ParameterizedTestCaseInfo::RegisterTests方法中的一段代码感兴趣。
ParamGenerator<ParamType> generator((*gen_it->second)()); 首先看刚开始的一段代码:
int gtest_OnTheFlyAndPreCalculatedPrimeTableTest_dummy_ = ::testing::UnitTest::GetInstance()->parameterized_test_registry(). GetTestCasePatternHolder<PrimeTableTest>( "PrimeTableTest", "d:\\gtest\\gtest_all\\gtest_all\\sample7_unittest.cc", 87)->AddTestCaseInstantiation( "OnTheFlyAndPreCalculated", >est_OnTheFlyAndPreCalculatedPrimeTableTest_EvalGenerator_, "d:\\gtest\\gtest_all\\gtest_all\\sample7_unittest.cc", 87);
查看一下AddTestCaseInstantiation方法。我们知道,(*gen_it->second)()实际上就是执行gtest_OnTheFlyAndPreCalculatedPrimeTableTest_EvalGenerator_方法。
::testing::internal::ParamGenerator<PrimeTableTest::ParamType> gtest_OnTheFlyAndPreCalculatedPrimeTableTest_EvalGenerator_() { return Values(&CreateOnTheFlyPrimeTable,&reatePreCalculatedPrimeTable<1000>); }
template <typename T1, typename T2> internal::ValueArray2<T1, T2> Values(T1 v1, T2 v2) { return internal::ValueArray2<T1, T2>(v1, v2); }
template <typename T1, typename T2> class ValueArray2 { public: ValueArray2(T1 v1, T2 v2) : v1_(v1), v2_(v2) {} template <typename T> operator ParamGenerator<T>() const { const T array[] = {v1_, v2_}; return ValuesIn(array); } private: // No implementation - assignment is unsupported. void operator=(const ValueArray2& other); const T1 v1_; const T2 v2_; };
template <typename T, size_t N> internal::ParamGenerator<T> ValuesIn(const T (&array)[N]) { return ValuesIn(array, array + N); }
template <typename ForwardIterator> internal::ParamGenerator< typename ::testing::internal::IteratorTraits<ForwardIterator>::value_type> ValuesIn(ForwardIterator begin, ForwardIterator end) { typedef typename ::testing::internal::IteratorTraits<ForwardIterator> ::value_type ParamType; return internal::ParamGenerator<ParamType>( new internal::ValuesInIteratorRangeGenerator<ParamType>(begin, end)); }
// Generates values from a pair of STL-style iterators. Used in the // ValuesIn() function. The elements are copied from the source range // since the source can be located on the stack, and the generator // is likely to persist beyond that stack frame. template <typename T> class ValuesInIteratorRangeGenerator : public ParamGeneratorInterface<T> { public: template <typename ForwardIterator> ValuesInIteratorRangeGenerator(ForwardIterator begin, ForwardIterator end) : container_(begin, end) {} virtual ~ValuesInIteratorRangeGenerator() {} virtual ParamIteratorInterface<T>* Begin() const { return new Iterator(this, container_.begin()); } virtual ParamIteratorInterface<T>* End() const { return new Iterator(this, container_.end()); } private: typedef typename ::std::vector<T> ContainerType; //内部类,并且是一个迭代器的实现 class Iterator : public ParamIteratorInterface<T> { public: Iterator(const ParamGeneratorInterface<T>* base, typename ContainerType::const_iterator iterator) : base_(base), iterator_(iterator) {} virtual ~Iterator() {} virtual const ParamGeneratorInterface<T>* BaseGenerator() const { return base_; } virtual void Advance() { ++iterator_; value_.reset(); } virtual ParamIteratorInterface<T>* Clone() const { return new Iterator(*this); } // We need to use cached value referenced by iterator_ because *iterator_ // can return a temporary object (and of type other then T), so just // having "return &*iterator_;" doesn't work. // value_ is updated here and not in Advance() because Advance() // can advance iterator_ beyond the end of the range, and we cannot // detect that fact. The client code, on the other hand, is // responsible for not calling Current() on an out-of-range iterator. virtual const T* Current() const { if (value_.get() == NULL) value_.reset(new T(*iterator_)); return value_.get(); } virtual bool Equals(const ParamIteratorInterface<T>& other) const { // Having the same base generator guarantees that the other // iterator is of the same type and we can downcast. GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) << "The program attempted to compare iterators " << "from different generators." << std::endl; return iterator_ == CheckedDowncastToActualType<const Iterator>(&other)->iterator_; } private: Iterator(const Iterator& other) // The explicit constructor call suppresses a false warning // emitted by gcc when supplied with the -Wextra option. : ParamIteratorInterface<T>(), base_(other.base_), iterator_(other.iterator_) {} const ParamGeneratorInterface<T>* const base_; typename ContainerType::const_iterator iterator_; // A cached value of *iterator_. We keep it here to allow access by // pointer in the wrapping iterator's operator->(). // value_ needs to be mutable to be accessed in Current(). // Use of scoped_ptr helps manage cached value's lifetime, // which is bound by the lifespan of the iterator itself. mutable scoped_ptr<const T> value_; }; // class ValuesInIteratorRangeGenerator::Iterator // No implementation - assignment is unsupported. void operator=(const ValuesInIteratorRangeGenerator& other); const ContainerType container_; }; // class ValuesInIteratorRangeGenerator
// Wraps ParamGeneratorInterface<T> and provides general generator syntax // compatible with the STL Container concept. // This class implements copy initialization semantics and the contained // ParamGeneratorInterface<T> instance is shared among all copies // of the original object. This is possible because that instance is immutable. template<typename T> class ParamGenerator { public: typedef ParamIterator<T> iterator; explicit ParamGenerator(ParamGeneratorInterface<T>* impl) : impl_(impl) {} ParamGenerator(const ParamGenerator& other) : impl_(other.impl_) {} ParamGenerator& operator=(const ParamGenerator& other) { impl_ = other.impl_; return *this; } iterator begin() const { return iterator(impl_->Begin()); } iterator end() const { return iterator(impl_->End()); } private: linked_ptr<const ParamGeneratorInterface<T> > impl_; };