Pizer’s Weblog

programming, DSP, math

A little C++0x finger exercise

with 4 comments

Since the C++ compiler of GCC 4.3.2 comes with partial C++0x support and I had not tested all of these features yet, I decided to do a little finger exercise: “function”.

“function” is a useful library feature of Boost, TR1, and the upcoming C++ standard library. It’s basically a class template mimicing the behaviour of a function pointer. But it’s much more flexible because it applies to any “callable object” — function pointers and objects with overloaded function call operator. Also, the signatures don’t have to match perfectly as long as the parameters and return values are implicitly convertible. I’m going to show you how this library feature can be implemented with C++0x features — including variadic templates and rvalue references. But before going into any details let me give an example of how code that uses this “function” library might look like. In case you’re already familiar with this library feature you can skip the following piece of code and paragraph of text.

  #include <iostream>
  #include <ostream>

  #include "function.hpp"

  using std::cout;
  using std::endl;

  int plus(int a, int b) {
     return a+b;
  }

  int minus(int a, int b) {
     return a-b;
  }

  struct accumulate_products {
     int* pacc;
     accumulate_products(int* p) : pacc(p) {}
     int operator()(int a, int b) const {
        *pacc += a*b;
        return *pacc;
     }
  };

  int main() {
     function<int(int,int)> f = plus;
     cout << f(23,17) << endl;
     f = minus;
     cout << f(23,17) << endl;
     int accum = 0;
     f = accumulate_products(&accum);
     f(1,2);
     f(3,4);
     f(5,6);
     cout << accum << endl;
  }

As you might have noticed, the template parameter is actually a function signature. This example shows how we can assign function pointers to this object as well as some other function object.

As far as the C++ language is concerned this function signature is only one template parameter. Do we even need variadic templates for this? Yes, we do. We do because we want to support functions that take any number of parameters. This is what “variadic” is all about. What I’m going to do is to partially specialize the class template “function” with a template taking a variable number of type parameters. One for the return type and zero or more for the function’s parameter types. The rvalue reference feature is used to achieve both: move semantics and perfect forwarding. You may also notice some other tricks: “C++ type erasure” for runtime polymorphism, “copy & swap” for efficient and exception-safe assignment. Without further ado, here’s the content of the file “function.hpp”:

  #ifndef FUNCTION_HPP_INCLUDED
  #define FUNCTION_HPP_INCLUDED

  #include <utility>    // std::move, std::forward
  #include <algorithm>  // std::swap

  template<typename R, typename ... P>
  class abstract_callable
  {
  public:
     virtual ~abstract_callable() {}
     virtual R operator()(P ... p) = 0;
     virtual abstract_callable* clone() const = 0;

     template<typename F>
     // requires std::CopyConstructible<F>
     //       && std::Callable<F&,P...>
     //       && std::Convertible<std::Callable<F&,P...>::result_type,R>
     class wrapper /*[[check_names]]*/ : public abstract_callable
     {
     public:
        explicit wrapper(F const& f) : func(f) {}
        explicit wrapper(F && f) : func(std::move(f)) {}
        R operator() /*[[override]]*/ (P ... p) {
           return func(std::forward<P>(p) ...);
        }
        abstract_callable* clone /*[[override]]*/ () const {
           return new wrapper(*this);
        }
     private:
        F func;
     };
  };

  template<typename Signature>
  class function;

  template<typename R, typename ... P>
  class function<R(P...)>
  {
     typedef abstract_callable<R,P...> ac_type;
  public:
     typedef R result_type;

     function() : ptr(0) {}

     template<typename F>
     // requires std::CopyConstructible<F>
     //       && std::Callable<F&,P...>
     //       && std::Convertible<std::Callable<F&,P...>::result_type,R>
     function(F f) : ptr(0) {
        typedef typename ac_type::template wrapper<F> wrapper_t;
        ptr = new wrapper_t(std::move(f));
     }

     function(function const& f) : ptr(f.ptr->clone()) {}
     function(function && f) : ptr(f.ptr) {f.ptr=0;}
     ~function() { delete ptr; }

     void swap(function & f) {
       using std::swap;
       swap(ptr,f.ptr);
     }

     function& operator=(function f) {
       swap(f);
       return *this;
     }

     R operator()(P ... p) const {
       return (*ptr)(std::forward<P>(p) ...);
     }
  private:
     ac_type* ptr;
  };

  template<typename Signature>
  inline void swap(function<Signature> & f1, function<Signature> & f2) {
     f1.swap(f2);
  }

  #endif // FUNCTION_HPP_INCLUDED

There you go! This compiles with the option -std=c++0x and seems to work (I didn’t test it that much).

Try implementing it with C++03 (current C++) and you’ll end up writing many more lines to support up to N function parameters. For every number of parameters you need to write a specialization. Sure, with a bit of macro trickery you can reduce the typing effort but the number of lines of code the preprocessor will emit is still very high. Also, without perfect forwarding you have to think about how parameters are accepted and forwarded to the next function. There’s no perfect solution for that in C++03.

Besides variadic templates and rvalue references I hinted at two other features. Have you spot it? I commented them out with because GCC does not yet support concepts and attributes. The idea behind these kinds of attributes is to make the compiler check the member functions’ names and reject the code if something doesn’t add up. But this has not yet been voted into the draft. It’s just a proposal for now. The concepts part makes the compiler type-check the templates some more and generate error messages that are hopefully human-readable in case the user tries to assign incompatible function pointers/objects.

Finally, I’d like to mention that this implementation is just a simple demonstration. There’s some room for improvement. I think it’s reasonable to expect that a good standard library implementation will outperform this simple version for “small” function objects such as raw function pointers and reference closures.

- P

About these ads

Written by pizer

March 28, 2009 at 9:59 pm

4 Responses

Subscribe to comments with RSS.

  1. Hi Pizer! Wanted to share this with you:

    http://www.equation.com/servlet/equation.cmd?call=fortran

    Found it about a week ago, its a gcc 4.4.0 build for windows (cygwin-built but standalone). GCC 4.4.0 has even more support for C++0x :)

    Dark

    April 1, 2009 at 4:36 am

  2. Hi dark! Thanks for the tipp! I may test it at work. At home I use Linux, though.

    pizer

    April 13, 2009 at 2:49 pm

  3. In accumulate_products, line “pacc += a*b;” should read “*pacc += a*b;”, otherwise you might be dereferencing invalid pointer later on. Pretty interesting example, btw.

    Mr.D

    April 25, 2009 at 10:31 am

  4. You’re right Mr.D. I fixed it. Thanks for reporting it. :)

    pizer

    April 26, 2009 at 7:32 pm


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: