0

I'm experiencing some problems which can be resumed by the following piece of code:

template <typename Key, typename Data, typename fct>
size_t wrapper(const std::pair<Key, Data> & p)
{
  return fct(p.first);
}

int main(int argc, char *argv[])
{
  size_t val = 
    wrapper<int, int, dft_hash_fct<int>>(std::pair<int,int>(5,9));

  return 0;
}

I'm using clang compiler version 3.4 and this code does not compile with the following error

test-tmp.C:17:5: error: no matching function for call to 'wrapper'
    wrapper<int, int, dft_hash_fct<int>>(std::pair<int,int>(5,9));
    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test-tmp.C:9:8: note: candidate template ignored: invalid explicitly-specified argument
      for template parameter 'fct'

The idea is to wrap a hash function (the template parameter fct) on std::pair for only taking the first field.

dft_hash_fct is another template defines as follows:

template <typename Key>
size_t dft_hash_fct(const Key & key)
{
  return SuperFastHash(key);
}

This generic function works; it has been used in other contexts.

The purpose of all this is to reuse a hash based set (not map) as a map of keys to items of any type. The hash based ser receives a hash function in construction time.

Thanks for your comments (David, Andrey and Kazark)

Edited:

Well, I see, typename fct is a type, so I cannot handle as a pointer function; sorry for the trivia. Unfortunately, I believe that the approach of passing the function as parameter in the wrapper does not work, because the hash set expects a function pointer with the following signature:

size_t (*the_function)(const Key & key);

So, realizing this, thanks to your observations, I changed the code in question to:

template <typename Key, typename Data, size_t (*fct)(const Key & k)>
size_t wrapper(const std::pair<Key, Data> & p)
{
  return (*fct)(p.first);
}

int main(int argc, char *argv[])
{
  size_t val = 
    wrapper<int, int, dft_hash_fct<int>>(std::pair<int,int>(5,9));

  return 0;
}

that compiles, links and runs. In addition, I put this line:

size_t (*fct)(const std::pair<int, int>&) = 
    wrapper<int, int, dft_hash_fct<int>>;

cout <<  (*fct)(std::pair<int, int>(4,6)) << endl;

And that compiles, links ans runs too. So, I can say that the compiler (and of course according to the language) can instantiate the function and handle a function pointer to it.

So, after that I tried to modify my original code, which is a derived class of HAshSet intended for managing pairs hashed by first field.

I declare some as:

template <typename Key, typename Data>
class HashMap : public HashSet<std::pair<Key, Data>>
{
  ...
  HashMap(size_t (*function)(const Key & key))
    : HashSet<Key, Data>(wrapper<Key, Data, function>)
  {

  }

..
};

But the compilation (with std=c++11) fails with the error

./tpl_dynSetHash.H:353:7: error: no matching constructor for initialization of
      'HashSet<std::pair<unsigned long, long>>'
    : HashSet<std::pair<Key,Data>(
      ^
testDynSetHash.C:178:8: note: in instantiation of member function
      'HashMap<unsigned long, long>::HashMap' requested here
  HMap table; 

However, if I substitute the call to base constructor by

: HashSet<Key, Data>(wrapper<Key, Data, dft_hash_fct<Key>)

That compiles fine. Thus, I believe that the problem is with the parameter type declaration (but I do not know what is).

5
  • 2
    You need to provide a type for a type template argument, but you provided a function. Commented Sep 6, 2013 at 19:25
  • You're using clang++3.4, can you / are you allowed to use C++11 features? Commented Sep 6, 2013 at 19:26
  • 1) You're missing a > at the end of wrapper<Key, Data, dft_hash_fct<Key> in your last code block. 2) HashMap(size_t (*function)(const Key & key)) : HashSet<Key, Data>(wrapper<Key, Data, function>) is not possible because function is not a compile-time constant here (but a function parameter); only compile-time constants (constant expressions) are allowed as non-type template arguments (I'm referring to the third argument of wrapper<..>). Commented Sep 6, 2013 at 22:18
  • 1) Yes, it was a typo; that compiles. 2) I see, thanks. Any workaround ? I would want to have a HashMap constructor that receives the function pointer (of the same way that HashSet) and the passes to HashSet constructor; in wrapped form, because the key is the first field, not the entire pair Commented Sep 6, 2013 at 22:39
  • You are not allowed to use run-time values as template non-type arguments. Only compile-time values can be used as template arguments. This is why it works with explicit dft_hash_fct<int> as template argument (value known at compile time), but does not work if the function pointer comes through constructor parameter (value not known at compile time). If you want to use run-time function pointers, you have no other choice but to use the approach I described in my answer. I.e. the function pointer must arrive through function parameters, not through template parameters. Commented Sep 6, 2013 at 23:17

3 Answers 3

4

The standard idiom to pass functions is to pass them as function objects, e.g.

template <typename Key, typename Data, typename Fct>
size_t wrapper(const std::pair<Key, Data> & p, Fct fct)
{
  return fct(p.first);
}

Then call the wrapper using:

int main(int argc, char *argv[])
{
  // no explicit template arguments required
  size_t val = 
    wrapper(std::pair<int,int>(5,9), &dft_hash_fct<int>);

  return 0;
}

In your code, on the other hand:

template <typename Key, typename Data, typename fct>
size_t wrapper(const std::pair<Key, Data> & p)
{
  return fct(p.first);
}

typename fct introduces an alias for a type. Inside this function, fct names a type; therefore fct(p.first) creates an object of type fct, and this object needs to be converted to a size_t in order to return it from wrapper. You can use this as well, but the type you had to use would have to look like this:

struct dft_hash_fct_t
{
    size_t result;
    dft_hash_fct_t(int p) : result(SuperFashHash(p)) {}
    operator size_t() const { return result; }
};

Which is probably not what you intended.

Sign up to request clarification or add additional context in comments.

Comments

1

The template declaration in

template <typename Key, typename Data, typename fct>
size_t wrapper(const std::pair<Key, Data> & p)
{
  return fct(p.first);
}

declares template parameterfct as a type, but you are trying to pass a function pointer to it. You can make fct function pointer template parameter like this:

template <typename Key, typename Data, size_t(*fct)(const Key&)>
size_t wrapper(const std::pair<Key, Data> & p)
{
  return fct(p.first);
}

However, the more idiomatic way is (as DyP says) to pass a function object so that the function works with function pointers as well as objects overloading operator():

template <typename Key, typename Data, typename Fct>
size_t wrapper(const std::pair<Key, Data> & p, Fct fct)
{
  return fct(p.first);
}

Then when calling it you pass the function as a parameter

wrapper(std::pair<int,int>(5,9), dft_hash_fct<int>);

Comments

1

The code you wrote makes no sense within the context of your intent. Your fct template parameter is a type. That means that

return fct(p.first);

is a function-style cast, not an application of () operator (i.e. it is not a function call). In your code you are attempting to cast p.first to type fct and then attempting to return the result of that cast as size_t. Was that your intent? I doubt that it was. On top of that you are trying to pass a function pointer value dft_hash_fct<int> as a template argument for fct, i.e. you are passing a value where a type is expected. How did you expect it to work?

The description you provided seems to imply that you actually wanted to call a functor with type fct from inside wrapper instead of performing a cast. In order to do that you have to obtain the functor itself somehow. Remember again that fct is not a functor, its is just the type of the functor.

The typical approach would be to pass the functor from the outside, as function parameter

template <typename Key, typename Data, typename fct>
size_t wrapper(const std::pair<Key, Data> & p, fct f)
{
  return f(p.first);
}

Now you can use your wrapper template with class-based functors, as well as with ordinary functions

size_t val = wrapper(std::pair<int,int>(5,9), dft_hash_fct<int>);

Note that dft_hash_fct<int> has to be supplied as function argument, not as template argument. There's no need to explicitly specify template arguments, since they will be deduced by the compiler.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.