Reputation: 113
I'm refactoring an API I've made a couple of years ago bringing a polymorphic implementation of common data structures (lists, maps, sets, trees, graphs), available at http://www.data-types.com. For structures requiring values comparison (eg: hash table), I've made them use C comparators and everything goes well.
In next version, however, I'm trying to level comparators' behavior so that users won't be required to create one when it can be inferred already. Everything is going fine until I try to convert a method argument into a template argument. Example code reduced to bare bone:
#include <iostream>
#include <cstddef>
#include <stdexcept>
template<typename VALUE>
class HashTable {
public:
HashTable(int (*comparator)(const VALUE&,const VALUE&), std::size_t (*hasher)(const VALUE&)){
std::cout << "HashTable constructor called" << std::endl;
}
bool contains(const VALUE& value, int (*customCompare)(const VALUE&,const VALUE&)) const{
return false;
}
~HashTable(){
std::cout << "HashTable destructor called" << std::endl;
}
};
template<typename _KEY, typename _VALUE>
struct MapEntry {
_KEY key;
_VALUE value;
};
template<typename KEY, typename VALUE, int (*comparator)(const KEY&, const KEY&)>
static inline int compareMapKey(const MapEntry<KEY,VALUE>& left, const MapEntry<KEY,VALUE>& right) {
return comparator(left.key, right.key);
}
template<typename KEY, typename VALUE, int (*comparator)(const VALUE&, const VALUE&)>
static inline int compareMapValue(const MapEntry<KEY,VALUE>& left, const MapEntry<KEY,VALUE>& right) {
return comparator(left.value, right.value);
}
template<typename KEY, typename VALUE, std::size_t (*hash)(const KEY&)>
static inline std::size_t hashMapKey(const MapEntry<KEY, VALUE>& element) {
return hash(element.key);
}
template<typename KEY, typename VALUE, int (*compareByKey)(const KEY&, const KEY&), std::size_t (*hashByKey)(const KEY&)>
class HashMap {
public:
HashMap(){
hashTable = new HashTable<MapEntry<KEY,VALUE>>(compareMapKey<KEY, VALUE, compareByKey>, hashMapKey<KEY, VALUE, hashByKey>);
std::cout << "HashMap constructor called" << std::endl;
}
bool containsValue(const VALUE& value, int (*comparator)(const VALUE&, const VALUE&)) const{
MapEntry<KEY,VALUE> mapEntry;
mapEntry.value = value;
return hashTable->contains(mapEntry, compareMapValue<KEY, VALUE, comparator>);
}
~HashMap(){
delete hashTable;
std::cout << "HashMap destructor called" << std::endl;
}
private:
HashTable<MapEntry<KEY,VALUE>>* hashTable;
};
static inline int comparator(const long& left, const long& right) {
if(left<right) return -1;
else if (left>right) return 1;
else return 0;
}
static inline std::size_t hash(const long& item) {
return item;
}
int main() {
long val = 1;
HashMap<long, long, comparator, hash> map;
map.containsValue(val, comparator);
std::cout << "!!!Hello World!!!" << std::endl;
return 0;
}
This refuses to compile, giving me this error:
g++ -O0 -g3 -Wall -c -fmessage-length=0 -MMD -MP -MF"src/Test.d" -MT"src/Test.o" -o "src/Test.o" "../src/Test.cpp"
../src/Test.cpp: In instantiation of ‘bool HashMap<KEY, VALUE, compareByKey, hashByKey>::containsValue(const VALUE&, int (*)(const VALUE&, const VALUE&)) const [with KEY = long int; VALUE = long int; int (* compareByKey)(const KEY&, const KEY&) = comparator; std::size_t (* hashByKey)(const KEY&) = hash]’:
../src/Test.cpp:78:35: required from here
../src/Test.cpp:53:79: error: no matching function for call to ‘HashTable<MapEntry<long int, long int> >::contains(MapEntry<long int, long int>&, <unresolved overloaded function type>)’ return hashTable->contains(mapEntry, compareMapValue<KEY, VALUE, comparator>);
../src/Test.cpp:12:7: note: candidate: bool HashTable<VALUE>::contains(const VALUE&, int (*)(const VALUE&, const VALUE&)) const [with VALUE = MapEntry<long int, long int>] bool contains(const VALUE& value, int (*customCompare)(const VALUE&,const VALUE&)) const{
../src/Test.cpp:12:7: note: no known conversion for argument 2 from ‘<unresolved overloaded function type>’ to ‘int (*)(const MapEntry<long int, long int>&, const MapEntry<long int, long int>&)’
Does anyone know for a solution of above? My C++ skills have become rustier since I'm no longer habitually working in that language...
Upvotes: 0
Views: 59
Reputation: 118352
A fundamental property of C++ templates, is that all template parameters must be resolved at compile time. That is, every template parameter must be known at compile time. The line with the compilation error:
return hashTable->contains(mapEntry,
compareMapValue<KEY, VALUE, comparator>);
But this comparator
is a parameter to this function:
bool containsValue(const VALUE& value,
int (*comparator)(const VALUE&, const VALUE&);
Therefore, generally speaking: what this "comparator` is, is not known at compile time. The actual value gets passed in at runtime. This is the fundamental reason for your compilation error: all template parameters must be specified at compile time. Simply put, this overall approach that's used here simply won't work.
Upvotes: 2