Showing posts with label tmp. Show all posts
Showing posts with label tmp. Show all posts

Tuesday, April 23, 2013

Making functions: How to provide a simple interface when working with std::function

While working on the lundi project, a minor, but not negligible, issue arose: How can we spare the user from producing manually all those std::functions from their callable objects?

The hypothetical expose function template takes std::function<R(P...)> and exposes them to the lua VM. A typical use case would be:

std::function<void(int, int)> lamarck = [](int a, int b){ 
  std::cout << a << b; 
};
expose(lamarck);

That's nice and all, but why do you need to write the signature of the function you just typed? That's redundant. The thing is, lambdas are actually function objects. The preceding lambda declaration is roughly equivalent to

struct blanche {
  void operator()(int a, int b) const { 
    std::cout << a << b; 
  }
};
std::function<void(int, int)> lamarck = blanche();

Note the braces; we're not assigning a type here.

Now, we could put pretty much anything in function objects; the only thing that makes them function objects are the presence of operator(). That would be insane, of course, to provide an implicit conversion to std::function.

Nobody is keeping you from doing it, though:

template<typename R, typename... P>
std::function<R(P...)> make_function(R(func const &)(P...)) {
  return std::function<R(P...)>(func);
}

Well, that's the easy part, but what about function objects? You can't just match a function object's call signature because it's not easily accessible. You can, though, look at his operator() to work out the parameter and return types. Note that it's not possible if operator() is a template or if it's overloaded.

This is where decltype is useful:

typedef decltype(&T::operator()) ptmf_type;
ptmf_type should look (in the case of void(int, int)) like void (mytype::*)(int, int) const, which can be matched in a template to extract all the needed types. We might as well work out directly the function type:
template<typename T>
struct ptmf_traits {
  typedef void type;
};

template<typename O, typename R, typename... P>
struct ptmf_traits<R(O::*)(P...) const> {
  typedef std::function<R(P...)> type;
};

Now, you can just use ptmf_traits<decltype(&LambdaType::operator())>::type to get a function type:

template<typename Func>
ptmf_traits<decltype(&Func::operator())>::type 
make_function_from_lambda(Func const &func) {
  return func;
}

With this function, the type of the std::function produced is fully deduced from the argument type:

auto my_func = make_function_from_lambda([](int a, int b){
  std::cout << a << b;
});
// my_func's type is std::function<void(int, int)>

Of course, you need a bit of boilerplate to accept free functions and maybe some tweaking to make it do what you want, but the core idea is there. You can find a more complete version here.

You may want to follow me on twitter.

Tuesday, August 16, 2011

Using SFINAE to recurse into collection types

Last week, during an interview, I have been asked about TMP. The question was "How would you create a function that takes a vector and prints it to the console ? It should do so recursively."

I though about SFINAE and using type traits, but was totally unable to come up with code. I never wrote any TMP code, in fact, I just read some books and articles about it. So the interviewer came up with his solution, which was to embed to type argument into a function parameter template, and create a "default function" for built-ins. That would look like that:

template <typename T> void print_r(const T& t) {
  std::cout << t;
}
 
template <typename T> void print_r(const std::vector<T>& v) {
  typedef typename std::vector<T>::const_iterator iter_t;
  for(iter_t iter = v.begin(); iter != v.end(); iter++)
    print_r(*iter);
}

Yes, of course it works. But the problem here is that it only works for std::vector, and you would have to write such a function for every collection type in the template library. Not sexy. And C++ does not have a generic collection hierarchy like C# has (which would be stupid anyway.) Since I won't give up so easily, I wrote my code using SFINAE. These functions detects the presence of const_iterators and recurse into the collection if such a type is present :

template <typename T>
struct has_const_iterator {
  typedef char yes[1];
  typedef char no[2];
 
  template <typename C>
  static yes& test(typename C::const_iterator*);
 
  template <typename>
  static no& test(...);
  static const bool value = sizeof(test<T>(0)) == sizeof(yes);
};
 
template <typename T> void print_r(const T& t, 
    char(*)[!(has_const_iterator::value)] = 0) {
  std::cout << t;
}
 
template <typename T> void print_r(const T& v, 
    char(*)[has_const_iterator<T>::value] = 0) {
  std::cout << "[";
  typedef typename T::const_iterator iter_t;
  for(iter_t iter = v.begin(); iter != v.end(); iter++) {
        if(iter != v.begin()) std::cout << " ";
    print_r(*iter);
  }
  std::cout << "]";
}

Okay, this is longer, but much more flexible. Now we can use these function templates with pretty much any container type which has a const_iterator, even proxy or hand-made classes.
The only problem is that the char(*)[has_const_iterator::value] = 0 is quite bulky. This can be improved (but not shortened) by using enable_if from boost or the new(future) standard library.

You may want to follow me on twitter.