// bsltf_testvaluesarray.h -*-C++-*- #ifndef INCLUDED_BSLTF_TESTVALUESARRAY #define INCLUDED_BSLTF_TESTVALUESARRAY #include <bsls_ident.h> BSLS_IDENT("$Id: $") //@PURPOSE: Provide a container for values used for testing. // //@CLASSES: // bsltf::TestValuesArray: container for values used for testing // bsltf::TestValuesArrayIterator: iterator for the container // //@SEE_ALSO: bsltf_templatetestfacility // //@DESCRIPTION: This component defines a class 'bsltf::TestValuesArray' // providing a uniform interface for creating and accessing a sequence of test // values of a type that has a copy constructor, and may or may not have a // default constructor. // // This component also defines an iterator class // 'bsltf::TestValuesArrayIterator' providing access to elements in a // 'TestValuesArray' object. 'TestValuesArrayIterator' is designed to // satisfies the minimal requirement of an input iterator as defined by the // C++11 standard [24.2.3]. It uses the 'BSLS_ASSERT' macro to detect // undefined behavior. // // The sequence described by this container is an input-range, that may be // traversed exactly once. Once an iterator is incremented, any other iterator // at the same position in the sequence is invalidated. The 'TestValuesArray' // object provides a 'resetIterators' method that restores the ability to // iterate the container. // ///Iterator ///-------- // The requirements of the input iterators as defined by the C++11 standard may // not be as tight as the users of the input iterators expected. Incorrect // assumptions about the properties of the input iterator may result in // undefined behavior. 'TestValuesArrayIterator' is designed to detect // possible incorrect usages. Specifically, 'TestValuesArrayIterator' put // restriction on when it can be dereferenced or compared. A // 'TestValuesArrayIterator' is considered to be *dereferenceable* if it // satisfies all of the following: // //: 1 The iterator refers to a valid element (not 'end'). //: //: 2 The iterator has not been dereferenced. (*) //: //: 3 The iterator is not a copy of another iterator of which 'operator++' //: have been invoked. (see [table 107] of the C++11 standard) // // *note: An input iterator may not be dereferenced more than once is a common // requirement of a container method that takes input iterators as arguments. // Other standard algorithms may allow the iterator to be dereferenced more // than once, in which case, 'TestValuesArrayIterator' is not suitable to be // used to with those algorithms. // // 'TestValuesArrayIterator' is comparable if the iterator is not a copy of // another iterator of which 'operator++' have been invoked. // ///Thread Safety ///------------- // This component is *not* thread-safe, by any definition of the term, and // should not be used in test scenarios concerned with concurrent code. // ///Usage ///----- // This section illustrates intended use of this component. // ///Example 1: Testing a Simple Template Function ///- - - - - - - - - - - - - - - - - - - - - - - // Suppose that we have a function that we would like to test. This function // take in a range defined by two input iterators and returns the largest value // in that range. // // First, we define the function we would like to test: //.. // template <class VALUE, class INPUT_ITERATOR> // VALUE myMaxValue(INPUT_ITERATOR first, INPUT_ITERATOR last) // // Return the largest value referred to by the iterators in the range // // beginning at the specified 'first' and up to, but not including, the // // specified 'last'. The behavior is undefined unless [first, last) // // specifies a valid range and 'first != last'. // { // assert(first != last); // // VALUE largestValue(*first); // ++first; // for(;first != last; ++first) { // // Store in temporary variable to avoid dereferencing twice. // // const VALUE& temp = *first; // if (largestValue < temp) { // largestValue = temp; // } // } // return largestValue; // } //.. // Next, we implement a test function 'runTest' that allows the function to be // tested with different types: //.. // template <class VALUE> // void runTest() // // Test driver. // { //.. // Then, we define a set of test values and expected results: //.. // struct { // const char *d_spec; // const char d_result; // } DATA[] = { // { "A", 'A' }, // { "ABC", 'C' }, // { "ADCB", 'D' }, // { "EDCBA", 'E' } // }; // const size_t NUM_DATA = sizeof DATA / sizeof *DATA; //.. // Now, for each set of test values, verify that the function return the // expected result. //.. // for (size_t i = 0; i < NUM_DATA; ++i) { // const char *const SPEC = DATA[i].d_spec; // const VALUE EXP = // bsltf::TemplateTestFacility::create<VALUE>(DATA[i].d_result); // // bsltf::TestValuesArray<VALUE> values(SPEC); // assert(EXP == myMaxValue<VALUE>(values.begin(), values.end())); // } // } //.. // Finally, we invoke the test function to verify our function is implemented // correctly. The test function to run without triggering the 'assert' // statement: //.. // runTest<char>(); //.. #include <bslscm_version.h> #include <bsltf_templatetestfacility.h> #include <bslma_stdallocator.h> #include <bsls_alignmentutil.h> #include <iterator> #include <stddef.h> #include <string.h> #ifndef BDE_DONT_ALLOW_TRANSITIVE_INCLUDES #include <bsls_nativestd.h> #endif // BDE_DONT_ALLOW_TRANSITIVE_INCLUDES namespace BloombergLP { namespace bsltf { template <class VALUE, class ALLOCATOR> struct TestValuesArray_DefaultConverter; template <class VALUE> class TestValuesArray_PostIncrementPtr; // ============================= // class TestValuesArrayIterator // ============================= template <class VALUE> class TestValuesArrayIterator { // This class provide a STL-conforming input iterator over values used for // testing (see section [24.2.3 input.iterators] of the C++11 standard. A // 'TestValuesArrayIterator' provide access to elements of parameterized // type 'VALUE'. An iterator is considered dereferenceable all of the // following are satisfied: //: 1 The iterator refers to a valid element (not 'end'). //: //: 2 The iterator has not been dereferenced. //: //: 3 The iterator is not a copy of another iterator of which 'operator++' //: have been invoked. // An iterator is comparable if the iterator is not a copy of another // iterator of which 'operator++' have been invoked. // // This class is *not* thread-safe: different iterator objects manipulate // shared state without synchronization. This is rarely a concern for the // test scenarios supported by this component. // DATA const VALUE *d_data_p; // pointer to array of values (held, // not owned) const VALUE *d_end_p; // end pointer (held, not owned) bool *d_dereferenceable_p; // indicate if dereferenceable (held, // not owned) bool *d_isValid_p; // indicate not yet invalidated (held, // not owned) private: // FRIENDS template <class OTHER_VALUE> friend bool operator==(const TestValuesArrayIterator<OTHER_VALUE>&, const TestValuesArrayIterator<OTHER_VALUE>&); template <class OTHER_VALUE> friend bool operator!=(const TestValuesArrayIterator<OTHER_VALUE>&, const TestValuesArrayIterator<OTHER_VALUE>&); public: // TYPES typedef std::input_iterator_tag iterator_category; typedef VALUE value_type; typedef ptrdiff_t difference_type; typedef const VALUE *pointer; typedef const VALUE& reference; // Standard iterator defined types [24.4.2]. public: // CREATORS TestValuesArrayIterator(const VALUE *object, const VALUE *end, bool *dereferenceable, bool *isValid); // Create an iterator referring to the specified 'object' for a // container with the specified 'end', with two arrays of boolean // referred to by the specified 'dereferenceable' and 'isValid' to // indicate whether this iterator and its subsequent values until // 'end' is allowed to be dereferenced and is not yet invalidated // respectively. TestValuesArrayIterator(const TestValuesArrayIterator& original); // Create an iterator having the same value as the specified 'original' // object. The behavior is undefined unless 'original' is valid. // MANIPULATORS TestValuesArrayIterator& operator=(const TestValuesArrayIterator& other); // Assign to this object the value of the specified 'other' object. // The behavior is undefined unless 'other' is valid. TestValuesArrayIterator& operator++(); // Move this iterator to the next element in the container. Any copies // of this iterator are no longer dereferenceable or comparable. The // behavior is undefined unless this iterator refers to a valid value // in the container. TestValuesArray_PostIncrementPtr<VALUE> operator++(int); // Move this iterator to the next element in the container, and return // an object that can be dereferenced to refer to the same object that // this iterator initially points to. Any copies of this iterator are // no longer dereferenceable or comparable. The behavior is undefined // unless this iterator refers to a valid value in the container. // ACCESSORS const VALUE& operator *() const; // Return the value referred to by this object. This object is no // longer dereferenceable after a call to this function. The behavior // is undefined unless this iterator is dereferenceable. const VALUE *operator->() const; // Return the address of the element (of the template parameter // 'VALUE') at which this iterator is positioned. The behavior is // undefined unless this iterator dereferenceable. }; template <class VALUE> bool operator==(const TestValuesArrayIterator<VALUE>& lhs, const TestValuesArrayIterator<VALUE>& rhs); // Return 'true' if the specified 'lhs' and the specified 'rhs' refer to // the same element, and 'false' otherwise. The behavior is undefined // unless 'lhs' and 'rhs' are comparable. template <class VALUE> bool operator!=(const TestValuesArrayIterator<VALUE>& lhs, const TestValuesArrayIterator<VALUE>& rhs); // Return 'true' if the specified 'lhs' and the specified 'rhs' do *not* // refer to the same element, and 'false' otherwise. The behavior is // undefined unless 'lhs' and 'rhs' are comparable. // ===================== // class TestValuesArray // ===================== template <class VALUE, class ALLOCATOR = bsl::allocator<VALUE>, class CONVERTER = TestValuesArray_DefaultConverter<VALUE, ALLOCATOR> > class TestValuesArray { // This class provides a container to store values of the (template // parameter) type 'VALUE', and also provides the iterators to access the // values. The iterators are designed to conform to a standard input // iterator, and report any misuse of the iterator. private: // PRIVATE TYPES typedef typename bsl::allocator_traits<ALLOCATOR>::template rebind_traits<bsls::AlignmentUtil::MaxAlignedType> AllocatorTraits; typedef typename AllocatorTraits::allocator_type AllocatorType; typedef typename AllocatorTraits::size_type size_type; // DATA ALLOCATOR d_allocator; // allocator (held, not owned) VALUE *d_data_p; // pointer to memory storing the values // (owned) size_t d_size; // number of elements in this container bool *d_dereferenceable_p; // pointer to an array to indicate if // value is dereferenceable (owned) bool *d_validIterator_p; // pointer to an array to indicate if // value is comparable (owned) // NOT IMPLEMENTED TestValuesArray(const TestValuesArray&); // = delete TestValuesArray& operator=(const TestValuesArray&); // = delete // PRIVATE MANIPULATOR void initialize(const char *spec); // Initialize this container, using the specified 'spec' to populate // container with test values. public: // TYPES typedef TestValuesArrayIterator<VALUE> iterator; // Iterator for this container. public: // CREATORS explicit TestValuesArray(); explicit TestValuesArray(ALLOCATOR basicAllocator); explicit TestValuesArray(const char *spec); explicit TestValuesArray(const char *spec, ALLOCATOR basicAllocator); // Create a 'TestValuesArray' object. Optionally, specify 'spec' to // indicate the values this object should contain, where the values are // created by invoking the 'bsltf::TemplateTestFacility::create' method // on each character of 'spec'. If no 'spec' is supplied, the object // will contain 52 distinct values of the (template parameter) type // 'VALUE'. Optionally, specify 'basicAllocator' used to supply // memory. If no allocator is supplied, a 'bslma::MallocFree' // allocator is used to supply memory. ~TestValuesArray(); // Destroy this container and all contained elements. // MANIPULATORS iterator begin(); // Return an iterator providing non-modifiable access to the first // 'VALUE' object in the sequence of 'VALUE' objects maintained by this // container, or the 'end' iterator if this container is empty. iterator end(); // Return an iterator providing access to the past-the-end position in // the sequence of 'VALUE' objects maintained by this container. iterator index(size_t position); // Return an iterator to the element at the specified 'position'. The // behavior is undefined unless 'position <= size()'. void resetIterators(); // Make all iterators dereferenceable and comparable again. // ACCESSORS const VALUE *data() const; // Return the address of the non-modifiable first element in this // container. const VALUE& operator[](size_t index) const; // Return a reference providing non-modifiable access to the element at // the specified 'index'. The behavior is undefined unless // '0 < size() && index < size()'. size_t size() const; // Return number of elements in this container. }; // ====================================== // class TestValuesArray_DefaultConverter // ====================================== template <class VALUE, class ALLOCATOR> struct TestValuesArray_DefaultConverter // This 'struct' provides a namespace for an utility function, // 'createInplace', that creates an object of the (template parameter) type // 'VALUE' from a character identifier. { // CLASS METHODS static void createInplace(VALUE *objPtr, char value, ALLOCATOR allocator); // Create an object of the (template parameter) type 'VALUE' at the // specified 'objPtr' address whose state is unique for the specified // 'value'. Use the specified 'allocator' to supply memory. The // behavior is undefined unless '0 <= value && value < 128' and 'VALUE' // is contained in the macro // 'BSLTF_TEMPLATETESTFACILITY_TEST_TYPES_ALL'. }; // ====================================== // class TestValuesArray_PostIncrementPtr // ====================================== template <class VALUE> class TestValuesArray_PostIncrementPtr // This class is a wrapper that encapsulates a reference, providing // non-modifiable access to the element of 'TestValuesArray' container. // Object of this class is returned by post increment operator of // TestValuesArray' container. { private: // DATA const VALUE *d_data_p; // pointer to the value (not owned) public: // CREATORS explicit TestValuesArray_PostIncrementPtr(const VALUE* ptr); // Create a 'TestValuesArray_PostIncrementPtr' object having the value // of the specified 'ptr'. // ACCESSORS const VALUE& operator*() const; // Return a reference providing non-modifiable access to the object // referred to by this wrapper. }; // ============================================================================ // INLINE DEFINITIONS // ============================================================================ // ----------------------------- // class TestValuesArrayIterator // ----------------------------- // CREATORS template <class VALUE> inline TestValuesArrayIterator<VALUE>::TestValuesArrayIterator( const VALUE *object, const VALUE *end, bool *dereferenceable, bool *isValid) : d_data_p(object) , d_end_p(end) , d_dereferenceable_p(dereferenceable) , d_isValid_p(isValid) { BSLS_ASSERT_SAFE(object); BSLS_ASSERT_SAFE(end); BSLS_ASSERT_SAFE(dereferenceable); BSLS_ASSERT_SAFE(isValid); BSLS_ASSERT_SAFE(*isValid); } template <class VALUE> inline TestValuesArrayIterator<VALUE>::TestValuesArrayIterator( const TestValuesArrayIterator& original) : d_data_p(original.d_data_p) , d_end_p(original.d_end_p) , d_dereferenceable_p(original.d_dereferenceable_p) , d_isValid_p(original.d_isValid_p) { BSLS_ASSERT_OPT(*original.d_isValid_p); } // MANIPULATORS template <class VALUE> TestValuesArrayIterator<VALUE>& TestValuesArrayIterator<VALUE>::operator=(const TestValuesArrayIterator& other) { BSLS_ASSERT_OPT(*other.d_isValid_p); d_data_p = other.d_data_p; d_end_p = other.d_end_p; d_dereferenceable_p = other.d_dereferenceable_p; d_isValid_p = other.d_isValid_p; return *this; } template <class VALUE> TestValuesArrayIterator<VALUE>& TestValuesArrayIterator<VALUE>::operator++() { BSLS_ASSERT_OPT(d_data_p != d_end_p); BSLS_ASSERT_OPT(*d_isValid_p); *d_dereferenceable_p = false; *d_isValid_p = false; ++d_data_p; ++d_dereferenceable_p; ++d_isValid_p; return *this; } template <class VALUE> TestValuesArray_PostIncrementPtr<VALUE> TestValuesArrayIterator<VALUE>::operator++(int) { BSLS_ASSERT_OPT(*d_isValid_p); BSLS_ASSERT_OPT(d_data_p != d_end_p); TestValuesArray_PostIncrementPtr<VALUE> result(d_data_p); this->operator++(); return result; } // ACCESSORS template <class VALUE> inline const VALUE& TestValuesArrayIterator<VALUE>::operator *() const { BSLS_ASSERT_OPT(*d_isValid_p); BSLS_ASSERT_OPT(*d_dereferenceable_p); *d_dereferenceable_p = false; return *d_data_p; } template <class VALUE> inline const VALUE *TestValuesArrayIterator<VALUE>::operator->() const { BSLS_ASSERT_OPT(*d_isValid_p); BSLS_ASSERT_OPT(*d_dereferenceable_p); *d_dereferenceable_p = false; return d_data_p; } } // close package namespace // FREE OPERATORS template <class VALUE> inline bool bsltf::operator==(const bsltf::TestValuesArrayIterator<VALUE>& lhs, const bsltf::TestValuesArrayIterator<VALUE>& rhs) { BSLS_ASSERT_OPT(*lhs.d_isValid_p); BSLS_ASSERT_OPT(*rhs.d_isValid_p); return lhs.d_data_p == rhs.d_data_p; } template <class VALUE> inline bool bsltf::operator!=(const bsltf::TestValuesArrayIterator<VALUE>& lhs, const bsltf::TestValuesArrayIterator<VALUE>& rhs) { BSLS_ASSERT_OPT(*lhs.d_isValid_p); BSLS_ASSERT_OPT(*rhs.d_isValid_p); return !(lhs == rhs); } namespace bsltf { // --------------------- // class TestValuesArray // --------------------- // CREATORS template <class VALUE, class ALLOCATOR, class CONVERTER> TestValuesArray<VALUE, ALLOCATOR, CONVERTER>::TestValuesArray() : d_allocator(&bslma::MallocFreeAllocator::singleton()) { static const char DEFAULT_SPEC[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; initialize(DEFAULT_SPEC); } template <class VALUE, class ALLOCATOR, class CONVERTER> TestValuesArray<VALUE, ALLOCATOR, CONVERTER>::TestValuesArray( ALLOCATOR basicAllocator) : d_allocator(basicAllocator) { static const char DEFAULT_SPEC[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; initialize(DEFAULT_SPEC); } template <class VALUE, class ALLOCATOR, class CONVERTER> inline TestValuesArray<VALUE, ALLOCATOR, CONVERTER>::TestValuesArray(const char *spec) : d_allocator(&bslma::MallocFreeAllocator::singleton()) { initialize(spec); } template <class VALUE, class ALLOCATOR, class CONVERTER> inline TestValuesArray<VALUE, ALLOCATOR, CONVERTER>::TestValuesArray( const char *spec, ALLOCATOR basicAllocator) : d_allocator(basicAllocator) { initialize(spec); } template <class VALUE, class ALLOCATOR, class CONVERTER> TestValuesArray<VALUE, ALLOCATOR, CONVERTER>::~TestValuesArray() { for (size_t i = 0; i < d_size; ++i) { bsl::allocator_traits<ALLOCATOR>::destroy(d_allocator, d_data_p + i); } size_type numBytes = static_cast<size_type>( d_size * sizeof(VALUE) + 2 * (d_size + 1) * sizeof(bool)); size_type numMaxAlignedType = (numBytes + bsls::AlignmentUtil::BSLS_MAX_ALIGNMENT - 1) / bsls::AlignmentUtil::BSLS_MAX_ALIGNMENT; AllocatorType alignAlloc(d_allocator); AllocatorTraits::deallocate( alignAlloc, reinterpret_cast<bsls::AlignmentUtil::MaxAlignedType *>( reinterpret_cast<void *>(d_data_p)), numMaxAlignedType); // The redundant cast to 'void *' persuades gcc/Solaris that there are // no alignment issues to warn about. } // PRIVATE MANIPULATORS template <class VALUE, class ALLOCATOR, class CONVERTER> void TestValuesArray<VALUE, ALLOCATOR, CONVERTER>::initialize(const char *spec) { BSLS_ASSERT_SAFE(spec); d_size = strlen(spec); // Allocate all memory in one go. size_type numBytes = static_cast<size_type>( d_size * sizeof(VALUE) + 2 * (d_size + 1) * sizeof(bool)); size_type numMaxAlignedType = (numBytes + bsls::AlignmentUtil::BSLS_MAX_ALIGNMENT - 1) / bsls::AlignmentUtil::BSLS_MAX_ALIGNMENT; AllocatorType alignAlloc(d_allocator); d_data_p = reinterpret_cast<VALUE *>(AllocatorTraits::allocate( alignAlloc, numMaxAlignedType)); d_dereferenceable_p = reinterpret_cast<bool *>(d_data_p + d_size); d_validIterator_p = d_dereferenceable_p + d_size + 1; for (int i = 0; '\0' != spec[i]; ++i) { CONVERTER::createInplace(d_data_p + i, spec[i], d_allocator); } memset(d_dereferenceable_p, true, d_size * sizeof(bool)); d_dereferenceable_p[d_size] = false; // 'end' is never dereferenceable memset(d_validIterator_p, true, (d_size + 1) * sizeof(bool)); } // MANIPULATORS template <class VALUE, class ALLOCATOR, class CONVERTER> inline typename TestValuesArray<VALUE, ALLOCATOR, CONVERTER>::iterator TestValuesArray<VALUE, ALLOCATOR, CONVERTER>::begin() { return iterator(data(), data() + d_size, d_dereferenceable_p, d_validIterator_p); } template <class VALUE, class ALLOCATOR, class CONVERTER> inline typename TestValuesArray<VALUE, ALLOCATOR, CONVERTER>::iterator TestValuesArray<VALUE, ALLOCATOR, CONVERTER>::end() { return iterator(data() + d_size, data() + d_size, d_dereferenceable_p + d_size, d_validIterator_p + d_size); } template <class VALUE, class ALLOCATOR, class CONVERTER> inline typename TestValuesArray<VALUE, ALLOCATOR, CONVERTER>::iterator TestValuesArray<VALUE, ALLOCATOR, CONVERTER>::index(size_t position) { BSLS_ASSERT_SAFE(position <= size()); return iterator(data() + position, data() + d_size, d_dereferenceable_p + position, d_validIterator_p + position); } template <class VALUE, class ALLOCATOR, class CONVERTER> void TestValuesArray<VALUE, ALLOCATOR, CONVERTER>::resetIterators() { memset(d_dereferenceable_p, 1, d_size * sizeof(bool)); d_dereferenceable_p[d_size] = false; memset(d_validIterator_p, 1, (d_size + 1) * sizeof(bool)); } // ACCESSORS template <class VALUE, class ALLOCATOR, class CONVERTER> inline const VALUE *TestValuesArray<VALUE, ALLOCATOR, CONVERTER>::data() const { return d_data_p; } template <class VALUE, class ALLOCATOR, class CONVERTER> inline const VALUE& TestValuesArray<VALUE, ALLOCATOR, CONVERTER>:: operator[](size_t index) const { BSLS_ASSERT_SAFE(0 < size() && index < size()); return data()[index]; } template <class VALUE, class ALLOCATOR, class CONVERTER> inline size_t TestValuesArray<VALUE, ALLOCATOR, CONVERTER>::size() const { return d_size; } // -------------------------------------- // class TestValuesArray_DefaultConverter // -------------------------------------- template <class VALUE, class ALLOCATOR> inline void TestValuesArray_DefaultConverter<VALUE, ALLOCATOR>::createInplace( VALUE *objPtr, char value, ALLOCATOR allocator) { bsltf::TemplateTestFacility::emplace(objPtr, value, allocator); } // -------------------------------------- // class TestValuesArray_PostIncrementPtr // -------------------------------------- template <class VALUE> inline TestValuesArray_PostIncrementPtr<VALUE>:: TestValuesArray_PostIncrementPtr(const VALUE* ptr) : d_data_p(ptr) { BSLS_ASSERT_SAFE(ptr); } template <class VALUE> inline const VALUE& TestValuesArray_PostIncrementPtr<VALUE>::operator*() const { return *d_data_p; } } // close package namespace } // close enterprise namespace #endif // ---------------------------------------------------------------------------- // Copyright 2013 Bloomberg Finance L.P. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ----------------------------- END-OF-FILE ----------------------------------