Angus Comber
Angus Comber

Reputation: 9708

How to declare a templated function so that can be passed in a class constructor/function

I want to pass in a user defined function to a class which requires a user defined matching function. In ye olde days of C I would have used a function pointer with void* arguments. But there must be a better way...

Here is roughly the sort of thing I want to do. One limitation I have is that the platform I am on has no standard library. But the basic core language C++11 is available.

What I need to do:

#include <iostream>

using namespace std;

// TODO - replace this C construct with C++ equivalent
//typedef bool(*match_key)(const void* key1, const void* key2);

// somehow declare this as a typedef?  need a way to declare a signature in C++
typedef template<class T>
bool (*match_key)(const T& key1, const T& key2);


// *** User defined matching function
bool mymatcher(const int i, const int j) {
    return i == j;
}

template<class K>
class hashmap {
public:
    hashmap<K>(const K& key, match_key matchfunc) : key_(key), cmp(matchfunc) { }

    bool matched(const K& key) {
        return cmp(key_, key);
    }

private:
    const K key_;
    match_key cmp;
};


int main()
{
    int i = 3;
    int j = 4;

    hashmap<int> hm(i, mymatcher);
    cout << "i matches j? " << (hm.matched(j) ? "yes" : "no") << endl;

    return 0;
}

Upvotes: 3

Views: 76

Answers (3)

JiaHao Xu
JiaHao Xu

Reputation: 2748

The way passing a function as a pointer is very restrictive in C++. It won't be able to container any callable object in C++. To be specific, it will block the use of functor in C++.

Here functor, referred to any type T that satisfies:

  1. which has overloaded calling operator;

Or

  1. has user-defined conversion functions that enables it to be statically casted to a function pointer.

It either case, any object of such type is callable, and can be used as if they are names of functions.

An example to this is std::less, which is frequently used when using algorithms that need to compare 2 objects.

In order to be able to pass any callable object, you have to templaterize the type of the function:

template <class K, class Cmp = std::less<>>
class hashmap {
public:
    hashmap<K>(const K& key, Cmp _cmp = {}): key_(key), cmp(_cmp) { }

    bool matched(const K& key) {
        return cmp(key_, key);
    }

private:
    const K key_;
    Cmp cmp;
};

Upvotes: 0

Oliv
Oliv

Reputation: 18051

#include <iostream>

using namespace std;

// TODO - replace this C construct with C++ equivalent
//typedef bool(*match_key)(const void* key1, const void* key2);

// somehow declare this as a typedef?  need a way to declare a signature in C++
typedef template<class T>
using match_key = bool (*)(const T& key1, const T& key2);


// *** User defined matching function
bool mymatcher(const int i, const int j) {
    return i == j;
}

template<class K>
class hashmap{
public:
    hashmap(const K& key, match_key<K> matchfunc) : key_(key), cmp(matchfunc) { }

    bool matched(const K& key) {
        return cmp(key_, key);
    }

private:
    const K key_;
    match_key<K> cmp;
};


int main()
{
    int i = 3;
    int j = 4;

    hashmap<int> hm(i, mymatcher);
    cout << "i matches j? " << (hm.matched(j) ? "yes" : "no") << endl;

    return 0;
}

Upvotes: 6

463035818_is_not_an_ai
463035818_is_not_an_ai

Reputation: 122516

If the T of the match_key is supposed to be the same as the K of the hashmap, you can make the signature part of the hashmap:

template <typename T>
struct hashmap {
    typedef bool (*match_key)(const T& key1, const T& key2);
    ....
}

...otherwise I would make the type of the comparator a second template parameter:

template <typename K, typename C>
struct hashmap {
     C cmp;
     hashmap(const K& key, C matchfunc) : key_(key), cmp(matchfunc) { }
     ...
}

this would give the user greater flexibility but also opens the door to long compiler errors.

Upvotes: 4

Related Questions