diff --git a/Library/collections/collections.h b/Library/collections/collections.h index fbf7a1aa..14378251 100644 --- a/Library/collections/collections.h +++ b/Library/collections/collections.h @@ -20,6 +20,7 @@ #include "gmath.h" #include "hashcode.h" #include "random.h" +#include "typeproperties.h" // begin global namespace string read/writing functions from strlib.h @@ -81,6 +82,7 @@ bool stringNeedsQuoting(const std::string& str); */ template std::ostream& writeGenericValue(std::ostream& os, const ValueType& value, bool) { + ASSERT_STREAM_INSERTABLE(ValueType); return os << std::boolalpha << value; } @@ -116,6 +118,7 @@ inline std::string genericValueToString(const std::string& value, */ template bool readGenericValue(std::istream& is, ValueType& value) { + ASSERT_STREAM_EXTRACTABLE(ValueType); return (bool) (is >> value); } @@ -147,6 +150,7 @@ void checkVersion(const CollectionType& coll, const IteratorType& itr, } } + /* * Performs a comparison for ordering between the given two collections * by comparing their elements pairwise to each other. @@ -157,6 +161,8 @@ void checkVersion(const CollectionType& coll, const IteratorType& itr, */ template int compare(const CollectionType& coll1, const CollectionType& coll2) { + ASSERT_IS_COMPARABLE(decltype(*coll1.begin())); + // optimization: if they are the same object, then they are equal if (&coll1 == &coll2) { return 0; @@ -171,15 +177,6 @@ int compare(const CollectionType& coll1, const CollectionType& coll2) { ++itr1, ++itr2) { // compare each pair of elements from iterators - // TO STUDENT: - // If the line below is failing to compile in your program, it probably - // means that you are trying to make a nested collection - // (e.g. Set>) for some element type T that does not have a - // less-than < operator. That operator is *required* in order to make - // a Set or Map of Vectors, so that the set/map knows how to sort the - // elements into their ascending order. - // You should either add a < operator to your class, or consider a - // different nested collection solution. Good luck! if (*itr1 < *itr2) { return -1; } else if (*itr2 < *itr1) { @@ -210,6 +207,10 @@ int compare(const CollectionType& coll1, const CollectionType& coll2) { */ template int compareMaps(const MapType& map1, const MapType& map2) { + /* Keys and values must be comparable. */ + ASSERT_IS_COMPARABLE(decltype(*map1.begin())); + ASSERT_IS_COMPARABLE(decltype(map1[*map1.begin()])); + // optimization: if they are the same object, then they are equal if (&map1 == &map2) { return 0; @@ -272,17 +273,35 @@ inline int compareTo() { } template int compareTo(const T& first, const T& second, const Rest&... rest) { + ASSERT_IS_COMPARABLE(T); + if (first < second) return -1; if (second < first) return +1; return compareTo(rest...); } +/* + * Template functions to compare two interleaved sequences of values, returning + * whether they're equal. The types must support the == operator. + */ +inline int equalTo() { + return 0; +} +template +int equalTo(const T& first, const T& second, const Rest&... rest) { + ASSERT_HAS_EQUALITY(T); + + return first == second && compareTo(rest...); +} + /* * Returns true if the two collections contain the same elements in the same order. * The element type must have an operator ==. */ template bool equals(const CollectionType& coll1, const CollectionType& coll2) { + ASSERT_HAS_EQUALITY(decltype(*coll1.begin())); + // optimization: if literally same collection, stop if (&coll1 == &coll2) { return true; @@ -345,6 +364,10 @@ bool equalsDouble(const CollectionType& coll1, const CollectionType& coll2) { */ template bool equalsMap(const MapType& map1, const MapType& map2) { + /* Both keys and values must be equality-comparable. */ + ASSERT_HAS_EQUALITY(decltype(*map1.begin())); + ASSERT_HAS_EQUALITY(decltype(map1[*map1.begin()])); + // optimization: if literally same map, stop if (&map1 == &map2) { return true; @@ -372,6 +395,8 @@ bool equalsMap(const MapType& map1, const MapType& map2) { */ template int hashCodeIterable(IteratorType begin, IteratorType end, bool orderMatters = true) { + ASSERT_IS_HASHABLE(decltype(*begin)); + int code = hashSeed(); while (begin != end) { if (orderMatters) { @@ -398,6 +423,10 @@ int hashCodeCollection(const CollectionType& collection, bool orderMatters = tru */ template int hashCodeMap(const MapType& map, bool orderMatters = true) { + /* Both keys and values must be hashable. */ + ASSERT_IS_HASHABLE(decltype(*map.begin())); + ASSERT_IS_HASHABLE(decltype(map[*map.begin()])); + int code = hashSeed(); auto begin = map.begin(); auto end = map.end(); @@ -456,6 +485,7 @@ template std::istream& readCollection(std::istream& input, CollectionType& collection, ElementType& element, std::string /* descriptor */, void (*fn)(CollectionType&, const ElementType&) = readOne) { + ASSERT_STREAM_EXTRACTABLE(ElementType); char ch = '\0'; input >> ch; @@ -500,6 +530,9 @@ template std::istream& readPairedCollection(std::istream& input, CollectionType& collection, KeyType& key, ValueType& value, std::string /* descriptor */, void (*fn)(CollectionType&, const KeyType&, const ValueType&) = readOne) { + ASSERT_STREAM_EXTRACTABLE(KeyType); + ASSERT_STREAM_EXTRACTABLE(ValueType); + char ch = '\0'; input >> ch; if (ch != '{') { @@ -542,6 +575,8 @@ std::istream& readPairedCollection(std::istream& input, CollectionType& collecti */ template std::ostream& writeIterable(std::ostream& out, IteratorType begin, IteratorType end) { + ASSERT_STREAM_INSERTABLE(decltype(*begin)); + out << "{"; bool first = true; while (begin != end) { @@ -571,6 +606,8 @@ std::ostream& writeCollection(std::ostream& out, CollectionType collection) { */ template std::ostream& writeIterableOfPointers(std::ostream& out, IteratorType begin, IteratorType end) { + ASSERT_STREAM_INSERTABLE(decltype(**begin)); + out << "{"; bool first = true; while (begin != end) { @@ -603,6 +640,9 @@ std::ostream& writeCollectionOfPointers(std::ostream& out, CollectionType collec */ template std::ostream& writeMap(std::ostream& out, const MapType& map) { + ASSERT_STREAM_INSERTABLE(decltype(*map.begin())); + ASSERT_STREAM_INSERTABLE(decltype(map[*map.begin()])); + out << "{"; auto begin = map.begin(); auto end = map.end(); @@ -1678,55 +1718,6 @@ std::istream& operator >>(std::istream& is, GenericSet& set) { } -/* - * Types used to automatically check whether a type is comparable using - * the < operator and whether a type supports operator== and hashCode. - * - * This is used to provide better compiler diagnostics to students when - * they try to instantiate our times incorrectly. - * - * Later on, when C++20 concepts are rolled out, we should consider - * replacing this code with concepts. - */ -template -struct IsLessThanComparable { -private: - /* Use SFNIAE overloading to detect which of these two options to pick. */ - struct Yes{}; - struct No {}; - - template - static Yes check(int, - decltype(std::declval() < std::declval()) = 0); - template static No check(...); - -public: - static constexpr bool value = - std::conditional(0)), Yes>::value, - std::true_type, - std::false_type>::type::value; -}; - -template -struct IsHashable { -private: - /* Use SFNIAE overloading to detect which of these two options to pick. */ - struct Yes{}; - struct No {}; - - template - static Yes check(int, - decltype(hashCode(std::declval())) = 0, - decltype(std::declval() == std::declval()) = 0); - template static No check(...); - -public: - static constexpr bool value = - std::conditional(0)), Yes>::value, - std::true_type, - std::false_type>::type::value; -}; - /* * Returns std::less, except with a nice static assertion wrapped around it to * make sure that in the event that T isn't comparable via <, the error message is diff --git a/Library/collections/deque.h b/Library/collections/deque.h index 01aa5eae..41df2071 100644 --- a/Library/collections/deque.h +++ b/Library/collections/deque.h @@ -222,6 +222,7 @@ void Deque::enqueueFront(const ValueType& value) { template bool Deque::equals(const Deque& deque2) const { + ASSERT_HAS_EQUALITY(ValueType); return _elements == deque2._elements; } diff --git a/Library/collections/linkedlist.h b/Library/collections/linkedlist.h index e2abbca0..f3bdbb13 100644 --- a/Library/collections/linkedlist.h +++ b/Library/collections/linkedlist.h @@ -539,32 +539,38 @@ LinkedList::operator +=(const ValueType& value) { */ template bool LinkedList::operator ==(const LinkedList& list2) const { + ASSERT_HAS_EQUALITY(ValueType); return _elements == list2._elements; } template bool LinkedList::operator !=(const LinkedList& list2) const { - return _elements != list2._elements; + ASSERT_HAS_EQUALITY(ValueType); + return !(*this == list2); } template bool LinkedList::operator <(const LinkedList& list2) const { + ASSERT_IS_COMPARABLE(ValueType); return _elements < list2._elements; } template bool LinkedList::operator <=(const LinkedList& list2) const { + ASSERT_IS_COMPARABLE(ValueType); return _elements <= list2._elements; } template bool LinkedList::operator >(const LinkedList& list2) const { + ASSERT_IS_COMPARABLE(ValueType); return _elements > list2._elements; } template bool LinkedList::operator >=(const LinkedList& list2) const { - return this->_elements >= list2._elements; + ASSERT_IS_COMPARABLE(ValueType); + return _elements >= list2._elements; } template diff --git a/Library/collections/priorityqueue.h b/Library/collections/priorityqueue.h index 89913aa6..05514cdc 100644 --- a/Library/collections/priorityqueue.h +++ b/Library/collections/priorityqueue.h @@ -281,6 +281,8 @@ void PriorityQueue::enqueue(const ValueType& value, double priority) template bool PriorityQueue::equals(const PriorityQueue& pq2) const { + ASSERT_HAS_EQUALITY(ValueType); + // optimization: if literally same pq, stop if (this == &pq2) { return true; @@ -294,7 +296,7 @@ bool PriorityQueue::equals(const PriorityQueue& pq2) const if (!floatingPointEqual(backup1.peekPriority(), backup2.peekPriority())) { return false; } - if (backup1.dequeue() != backup2.dequeue()) { + if (!(backup1.dequeue() == backup2.dequeue())) { return false; } } @@ -366,6 +368,8 @@ bool PriorityQueue::operator !=(const PriorityQueue& pq2) const { */ template int hashCode(const PriorityQueue& pq) { + ASSERT_IS_HASHABLE(T); + // (slow, memory-inefficient) implementation: copy pq, dequeue all, and hash together PriorityQueue backup = pq; int code = hashSeed(); @@ -380,6 +384,8 @@ int hashCode(const PriorityQueue& pq) { template std::ostream& operator <<(std::ostream& os, const PriorityQueue& pq) { + ASSERT_STREAM_INSERTABLE(ValueType); + os << "{"; // faster implementation: print in heap order diff --git a/Library/collections/typeproperties.h b/Library/collections/typeproperties.h new file mode 100644 index 00000000..170ae672 --- /dev/null +++ b/Library/collections/typeproperties.h @@ -0,0 +1,345 @@ +/* + * File: typeproperties.h + * ------------------- + * Internal helper functions used to check whether various types + * have various properties + */ +#pragma once + +#include "hashcode.h" +#include +#include + +/* + * Checks that various operations are defined, giving nice error messages if + * they aren't. + */ +#define ASSERT_IS_COMPARABLE(...) \ + static_assert(::stanfordcpplib::collections::IsLessThanComparable<__VA_ARGS__>::value,\ + "Oops! You tried comparing two objects that aren't comparable. Click this error for more details.") + /* + * Hello students! If you got directed to this line of code in a compiler error, + * it probably means that you wrote code that requires comparing two objects + * with the < operator that aren't comparable. This could be from trying to use + * the < operator directory, from trying to sort a list of objects, from using + * your type as the key in a Map, or from storing your object in a Set. By + * default, objects in C++ aren't comparable by <, hence the error. + * + * There are two ways to fix this. The first option would simply be to not use + * the object in a way that requires the comparison. + * + * The second way to fix this is to explicitly define an operator< function for your custom + * type. Here's the syntax for doing that: + * + * bool operator< (const YourCustomType& lhs, const YourCustomType& rhs) { + * using namespace stanfordcpplib::collections; + * return compareTo(lhs.data1, rhs.data1, + * lhs.data2, rhs.data2, + * ... + * lhs.dataN, rhs.dataN) == -1; // -1 signals less than + * } + * + * where data1, data2, ... dataN are the data members of your type. For example, if you had + * a custom type + * + * struct MyType { + * int myInt; + * string myString; + * }; + * + * you would define the function + * + * bool operator< (const MyType& lhs, const MyType& rhs) { + * using namespace stanfordcpplib::collections; + * return compareTo(lhs.myInt, rhs.myInt, + * lhs.myString, rhs.myString) == -1; + * } + * + * Hope this helps! + */ + +#define ASSERT_HAS_EQUALITY(...) \ + static_assert(::stanfordcpplib::collections::IsEqualityComparable<__VA_ARGS__>::value, \ + "Oops! You tried comparing two objects that aren't comparable. Click this error for more details.") + + /* + * Hello students! If you got directed to this line of code in a compiler error, + * it probably means that you wrote code that requires comparing two objects + * with the == operator that aren't comparable. This could be from trying to use + * the == operator directory, from using EXPECT_EQUAL or EXPECT_NOT_EQUAL on + * custom types, etc. + * + * There are two ways to fix this. The first option would simply be to not use + * the object in a way that requires the comparison. + * + * The second way to fix this is to explicitly define an operator< function for your custom + * type. Here's the syntax for doing that: + * + * bool operator== (const YourCustomType& lhs, const YourCustomType& rhs) { + * using namespace stanfordcpplib::collections; + * return equalTo(lhs.data1, rhs.data1, + * lhs.data2, rhs.data2, + * ... + * lhs.dataN, rhs.dataN); + * } + * + * where data1, data2, ... dataN are the data members of your type. For example, if you had + * a custom type + * + * struct MyType { + * int myInt; + * string myString; + * }; + * + * you would define the function + * + * bool operator== (const MyType& lhs, const MyType& rhs) { + * using namespace stanfordcpplib::collections; + * return equalTo(lhs.myInt, rhs.myInt, + * lhs.myString, rhs.myString); + * } + * + * Hope this helps! + */ + +#define ASSERT_IS_HASHABLE(...) \ + static_assert(::stanfordcpplib::collections::IsHashable<__VA_ARGS__>::value, \ + "Oops! You tried hashing an object that isn't hashable. Click this error for more details.") + /* + * Hello CS106 students! If you got directed to this line of code in a compiler error, + * it means your code needs to hash something that isn't hashable. + * + * In order to compute hash codes - either because you're storing a custom type in + * a HashMap or HashSet, or because you explicitly needed to compute a hash code - + * you need to define a hashCode() and operator== function for your custom type. + * + * There are two ways to fix this. The first option would simply be to not to do whatever + * operation requires hashing. This is probably the easiest option. + * + * The second way to fix this is to explicitly define a hashCode() and operator== function + * for your type. To do so, first define hashCode as follows: + * + * int hashCode(const YourCustomType& obj) { + * return hashCode(obj.data1, obj.data2, ..., obj.dataN); + * } + * + * where data1, data2, ... dataN are the data members of your type. For example, if you had + * a custom type + * + * struct MyType { + * int myInt; + * string myString; + * }; + * + * you would define the function + * + * int hashCode(const MyType& obj) { + * return hashCode(obj.myInt, obj.myString); + * } + * + * Second, define operator== as follows: + * + * bool operator== (const YourCustomType& lhs, const YourCustomType& rhs) { + * using namespace stanfordcpplib::collections; + * return equalTo(lhs.data1, rhs.data1, + * lhs.data2, rhs.data2, + * ... + * lhs.dataN, rhs.dataN); + * } + * + * where data1, data2, ... dataN are the data members of your type. For example, if you had + * a custom type + * + * struct MyType { + * int myInt; + * string myString; + * }; + * + * you would define the function + * + * bool operator== (const MyType& lhs, const MyType& rhs) { + * using namespace stanfordcpplib::collections; + * return equalTo(lhs.myInt, rhs.myInt, + * lhs.myString, rhs.myString); + * } + * + * Hope this helps! + */ + +#define ASSERT_STREAM_INSERTABLE(...) \ + static_assert(::stanfordcpplib::collections::IsStreamInsertable<__VA_ARGS__>::value,\ + "Oops! You tried printing an object that isn't printable. Click this error for more details.") + + /* + * Hello CS106 students! If you got directed to this line of code in a compiler error, + * it means your code needs to print something that isn't printable. + * + * In order to print a value to cout, or to use SimpleTest on a custom value, that + * value needs to have a function called operator<< defined for it. If this doesn't + * exist, then C++ will cause an error. + * + * There are two ways to fix this. The first option would simply be to not to do whatever + * operation requires printing the value. This is probably the easiest option. + * + * The second way to fix this is to explicitly define the operator<< function for your + * type. To do this, write a function that looks like this: + * + * std::ostream& operator<< (std::ostream& out, const YourCustomType& obj) { + * // ... write code here to print out the contents of your custom + * // ... type. Instead of printing to cout, though, print to out. + * + * return out; + * } + * + * For example, if you had a custom type + * + * struct MyType { + * int myInt; + * string myString; + * }; + * + * you might define the function + * + * std::ostream& operator<< (std::ostream& out, const MyType& obj) { + * out << "{" << obj.myInt << ", " << obj.myString << "}"; + * return out; + * } + * + * Hope this helps! + */ + +#define ASSERT_STREAM_EXTRACTABLE(...) \ + static_assert(::stanfordcpplib::collections::IsStreamExtractable<__VA_ARGS__>::value,\ + "Oops! You tried reading an object that isn't readable. Click this error for more details.") + /* + * Hello CS106 students! If you got directed to this line of code in a compiler error, + * it means your code needs to read something that isn't readable. + * + * In order to print a value to cout, or to use SimpleTest on a custom value, that + * value needs to have a function called operator>> defined for it. If this doesn't + * exist, then C++ will cause an error. + * + * There are two ways to fix this. The first option would simply be to not to do whatever + * operation requires printing the value. This is probably the easiest option. + * + * The second way to fix this is to explicitly define the operator>> function for your + * type. To do this, write a function that looks like this: + * + * std::istream& operator>> (std::istream& in, YourCustomType& obj) { + * // ... write code here to read a YourCustomType. Instead of using + * // ... cin here, though, use in. + * + * return in; + * } + * + * Hope this helps! + */ + +namespace stanfordcpplib { + namespace collections { + /* + * The types below are used to check whether a type has various properties + * (comparable via <, hashable, comparable via ==, capable of being output + * to a stream, capable of being read from a stream, etc. + * + * This is used to provide better compiler diagnostics to students when + * they try to instantiate our types incorrectly. + * + * Later on, when C++20 concepts are rolled out, we should consider + * replacing this code with concepts. + */ + template + struct IsLessThanComparable { + private: + /* Use SFNIAE overloading to detect which of these two options to pick. */ + struct Yes{}; + struct No {}; + + template + static Yes check(int, + decltype(std::declval() < std::declval()) = false); + template static No check(...); + + public: + static constexpr bool value = + std::conditional(0)), Yes>::value, + std::true_type, + std::false_type>::type::value; + }; + + template + struct IsEqualityComparable { + private: + /* Use SFNIAE overloading to detect which of these two options to pick. */ + struct Yes{}; + struct No {}; + + template + static Yes check(int, + decltype(std::declval() == std::declval()) = false); + template static No check(...); + + public: + static constexpr bool value = + std::conditional(0)), Yes>::value, + std::true_type, + std::false_type>::type::value; + }; + + template + struct IsStreamInsertable { + private: + /* Use SFNIAE overloading to detect which of these two options to pick. */ + struct Yes{}; + struct No {}; + + template + static Yes check(typename std::remove_reference())>*); + template static No check(...); + + public: + static constexpr bool value = + std::conditional(nullptr)), Yes>::value, + std::true_type, + std::false_type>::type::value; + }; + + template + struct IsStreamExtractable { + private: + /* Use SFNIAE overloading to detect which of these two options to pick. */ + struct Yes{}; + struct No {}; + + template + static Yes check(typename std::remove_reference> std::declval())>*); + template static No check(...); + + public: + static constexpr bool value = + std::conditional(nullptr)), Yes>::value, + std::true_type, + std::false_type>::type::value; + }; + + template + struct IsHashable { + private: + /* Use SFNIAE overloading to detect which of these two options to pick. */ + struct Yes{}; + struct No {}; + + template + static Yes check(int, + decltype(hashCode(std::declval())) = 0, + decltype(std::declval() == std::declval()) = false); + template static No check(...); + + public: + static constexpr bool value = + std::conditional(0)), Yes>::value, + std::true_type, + std::false_type>::type::value; + }; + } +} diff --git a/SPL-unit-tests/test-deque.cpp b/SPL-unit-tests/test-deque.cpp index 34152760..660bdd44 100644 --- a/SPL-unit-tests/test-deque.cpp +++ b/SPL-unit-tests/test-deque.cpp @@ -86,3 +86,14 @@ PROVIDED_TEST("Deque, error on modify during iterate") { EXPECT_ERROR(removeDuring(deque)); } +PROVIDED_TEST("Sensible compiler error messages.") { + struct Bad {}; + + Deque v1; + Deque v2; + //(void) (v1 < v2); // Should trigger a static assertion rather than a long chain of sorrows + //(void) (v1 == v2); // Should trigger a static assertion rather than a long chain of sorrows + //(void) hashCode(v1); // Should trigger a static assertion rather than a long chain of sorrows + //(void) (std::cout << v1); // Should trigger a static assertion rather than a long chain of sorrows + //(void) (std::cin >> v1); // Should trigger a static assertion rather than a long chain of sorrows +} diff --git a/SPL-unit-tests/test-grid.cpp b/SPL-unit-tests/test-grid.cpp index e09dd042..e5161589 100644 --- a/SPL-unit-tests/test-grid.cpp +++ b/SPL-unit-tests/test-grid.cpp @@ -139,3 +139,14 @@ PROVIDED_TEST("Grid, randomElement") { EXPECT(counts[s] > 0); } } + +PROVIDED_TEST("Sensible compiler error messages.") { + struct Bad {}; + + Grid v1; + Grid v2; + //(void) (v1 < v2); // Should trigger a static assertion rather than a long chain of sorrows + //(void) hashCode(v1); // Should trigger a static assertion rather than a long chain of sorrows + //(void) (std::cout << v1); // Should trigger a static assertion rather than a long chain of sorrows + //(void) (std::cin >> v1); // Should trigger a static assertion rather than a long chain of sorrows +} diff --git a/SPL-unit-tests/test-hashmap.cpp b/SPL-unit-tests/test-hashmap.cpp index 269f25da..e46ccc89 100644 --- a/SPL-unit-tests/test-hashmap.cpp +++ b/SPL-unit-tests/test-hashmap.cpp @@ -167,3 +167,14 @@ PROVIDED_TEST("HashMap, streamExtract") { bool result = bool(hmstreambad >> hm); EXPECT(! result); } + +PROVIDED_TEST("Sensible compiler error messages.") { + struct Bad {}; + + HashMap v1; + HashMap v2; + //(void) (v1 == v2); // Should trigger a static assertion rather than a long chain of sorrows + //(void) hashCode(v1); // Should trigger a static assertion rather than a long chain of sorrows + //(void) (std::cout << v1); // Should trigger a static assertion rather than a long chain of sorrows + //(void) (std::cin >> v1); // Should trigger a static assertion rather than a long chain of sorrows +} diff --git a/SPL-unit-tests/test-linkedlist.cpp b/SPL-unit-tests/test-linkedlist.cpp index 383c0b0b..32c64a07 100644 --- a/SPL-unit-tests/test-linkedlist.cpp +++ b/SPL-unit-tests/test-linkedlist.cpp @@ -115,3 +115,15 @@ PROVIDED_TEST("LinkedList, sort") { LinkedList exp { 0, 10, 10, 20, 30, 40, 50, 50, 60, 70, 90}; EXPECT_EQUAL( exp.toString(), vec.toString()); } + +PROVIDED_TEST("Sensible compiler error messages.") { + struct Bad {}; + + LinkedList v1; + LinkedList v2; + //(void) (v1 < v2); // Should trigger a static assertion rather than a long chain of sorrows + //(void) (v1 == v2); // Should trigger a static assertion rather than a long chain of sorrows + //(void) hashCode(v1); // Should trigger a static assertion rather than a long chain of sorrows + //(void) (std::cout << v1); // Should trigger a static assertion rather than a long chain of sorrows + //(void) (std::cin >> v1); // Should trigger a static assertion rather than a long chain of sorrows +} diff --git a/SPL-unit-tests/test-map.cpp b/SPL-unit-tests/test-map.cpp index 9f788c5f..9088c23f 100644 --- a/SPL-unit-tests/test-map.cpp +++ b/SPL-unit-tests/test-map.cpp @@ -176,3 +176,15 @@ PROVIDED_TEST("Map, streamExtract") { stream >> map; EXPECT_EQUAL( "{1:10, 2:20, 3:30, 4:40}", map.toString()); } + +PROVIDED_TEST("Sensible compiler error messages.") { + struct Bad {}; + + Map v1; + Map v2; + //(void) (v1 < v2); // Should trigger a static assertion rather than a long chain of sorrows + //(void) (v1 == v2); // Should trigger a static assertion rather than a long chain of sorrows + //(void) hashCode(v1); // Should trigger a static assertion rather than a long chain of sorrows + //(void) (std::cout << v1); // Should trigger a static assertion rather than a long chain of sorrows + //(void) (std::cin >> v1); // Should trigger a static assertion rather than a long chain of sorrows +} diff --git a/SPL-unit-tests/test-pqueue.cpp b/SPL-unit-tests/test-pqueue.cpp index 2279ce85..89867d80 100644 --- a/SPL-unit-tests/test-pqueue.cpp +++ b/SPL-unit-tests/test-pqueue.cpp @@ -130,3 +130,14 @@ PROVIDED_TEST("PQueue, initializerList") { EXPECT_EQUAL( exp, act); } } + +PROVIDED_TEST("Sensible compiler error messages.") { + struct Bad {}; + + PriorityQueue v1; + PriorityQueue v2; + //(void) (v1 == v2); // Should trigger a static assertion rather than a long chain of sorrows + //(void) hashCode(v1); // Should trigger a static assertion rather than a long chain of sorrows + //(void) (std::cout << v1); // Should trigger a static assertion rather than a long chain of sorrows + //(void) (std::cin >> v1); // Should trigger a static assertion rather than a long chain of sorrows +} diff --git a/SPL-unit-tests/test-queue.cpp b/SPL-unit-tests/test-queue.cpp index 10f67a9f..5906964e 100644 --- a/SPL-unit-tests/test-queue.cpp +++ b/SPL-unit-tests/test-queue.cpp @@ -95,3 +95,15 @@ PROVIDED_TEST("Queue, peekEnqueueBug") { EXPECT_EQUAL( "{10, 20, 30, 40, 50, 60, 70, 80, 90, 10}", queue.toString()); } } + +PROVIDED_TEST("Sensible compiler error messages.") { + struct Bad {}; + + Queue v1; + Queue v2; + //(void) (v1 < v2); // Should trigger a static assertion rather than a long chain of sorrows + //(void) (v1 == v2); // Should trigger a static assertion rather than a long chain of sorrows + //(void) hashCode(v1); // Should trigger a static assertion rather than a long chain of sorrows + //(void) (std::cout << v1); // Should trigger a static assertion rather than a long chain of sorrows + //(void) (std::cin >> v1); // Should trigger a static assertion rather than a long chain of sorrows +} diff --git a/SPL-unit-tests/test-set.cpp b/SPL-unit-tests/test-set.cpp index aa19ae1a..41254be9 100644 --- a/SPL-unit-tests/test-set.cpp +++ b/SPL-unit-tests/test-set.cpp @@ -227,3 +227,17 @@ PROVIDED_TEST("Set, removeAndRetain") { all.intersect(primes); EXPECT_EQUAL(all, expected); } + +PROVIDED_TEST("Sensible compiler error messages.") { + struct Bad { + bool operator< (const Bad&) const { + return true; + } + }; + + Set v1; + Set v2; + //(void) hashCode(v1); // Should trigger a static assertion rather than a long chain of sorrows + //(void) (std::cout << v1); // Should trigger a static assertion rather than a long chain of sorrows + //(void) (std::cin >> v1); // Should trigger a static assertion rather than a long chain of sorrows +} diff --git a/SPL-unit-tests/test-stack.cpp b/SPL-unit-tests/test-stack.cpp index 94a49a98..ca003613 100644 --- a/SPL-unit-tests/test-stack.cpp +++ b/SPL-unit-tests/test-stack.cpp @@ -78,3 +78,14 @@ PROVIDED_TEST("Stack initializer_list") { EXPECT_EQUAL( "{10, 20, 30}", stack.toString()); } +PROVIDED_TEST("Sensible compiler error messages.") { + struct Bad {}; + + Stack v1; + Stack v2; + //(void) (v1 < v2); // Should trigger a static assertion rather than a long chain of sorrows + //(void) (v1 == v2); // Should trigger a static assertion rather than a long chain of sorrows + //(void) hashCode(v1); // Should trigger a static assertion rather than a long chain of sorrows + //(void) (std::cout << v1); // Should trigger a static assertion rather than a long chain of sorrows + //(void) (std::cin >> v1); // Should trigger a static assertion rather than a long chain of sorrows +} diff --git a/SPL-unit-tests/test-vector.cpp b/SPL-unit-tests/test-vector.cpp index 492d03cf..d73ab0e5 100644 --- a/SPL-unit-tests/test-vector.cpp +++ b/SPL-unit-tests/test-vector.cpp @@ -335,3 +335,19 @@ PROVIDED_TEST("Vector sublist") { } } } + +/* + * Uncomment this code to test that we get sensible error messages for + * a lack of operator support on the underlying type. + */ +PROVIDED_TEST("Sensible compiler error messages.") { + struct Bad {}; + + Vector v1; + Vector v2; + //(void) (v1 < v2); // Should trigger a static assertion rather than a long chain of sorrows + //(void) (v1 == v2); // Should trigger a static assertion rather than a long chain of sorrows + //(void) hashCode(v1); // Should trigger a static assertion rather than a long chain of sorrows + //(void) (std::cout << v1); // Should trigger a static assertion rather than a long chain of sorrows + //(void) (std::cin >> v1); // Should trigger a static assertion rather than a long chain of sorrows +}