About
RSS

Bit Focus Blog


How std::function is implemented [1/2]

Posted at 2018-04-12 07:22:50 | Updated at 2018-04-12 07:23:32

This article is about how std::function is implemented and provide some implements that compiled in pre-C++11.

The std::function in C++11 is very fantastic as in a static compiling language like C++ it provides a set of interfaces to wrap any kind of callable objects. A more fantastic fact is that the only C++11 feature that std::function involves is variadic template.

So if we implements a simplified version of std::function with an arbitrary number of template parameters (say, 3 parameters, 1 for the return type and 2 for parameter types), it could be done in pre-C++11 so we don't have to learn the C++11 features right now.

Let us get down to the implements.

0. One pointer version

The std::function could be implemented as a class with only one pointer as its only data member, and of course several virtual functions.

The key point is to declare a virtual base class which could be used to wrap any kinds of callable object, like this.

Code Snippet 0-0

template <typename Ret, typename Arg0, typename Arg1>
class function<Ret(Arg0, Arg1)> {
    // the virtual base class for callables
    struct callable_base {
        virtual Ret operator()(Arg0 arg0, Arg1 arg1) = 0;
        virtual ~callable_base() {}
    };

    callable_base* callable_ptr;
public:
    Ret operator()(Arg0 arg0, Arg1 arg1)
    {
        // a call to the function is routed to the member pointer.
        return (*callable_ptr)(arg0, arg1);
    }
private:
    // here is the sub-template-class that inherits callable_base
    template <typename F>
    struct callable
        : callable_base
    {
        // it can store and make use of any callable object since it is a template class
        F functor;

        callable(F functor)
            : functor(functor)
        {}

        virtual Ret operator()(Arg0 arg0, Arg1 arg1)
        {
            return functor(arg0, arg1);
        }
    };
public:
    // so that create a 'function' instance is to specialize the 'callable' template with the type of callable object
    template <typename F>
    function(F f)
        : callable_ptr(new callable<F>(f))
    {}
};

The implements above is fine to be instanciated and be called, but apparently there are resource leak problems since there is new in the constructor but no delete in the destructor.

Before we implements the destructor we must be aware of that copy-constructor and copy-assign operator overload shall be implements along with the destructor.

So let's consider about them.

The basic idea to create copy-constructor is to copy each members recursively. However the problem we have here is we don't know how to copy an abstract base pointer. To solve this, we can add a "copy-constructor" as a virtual function and implement it in the subclass.

Code Snippet 0-1

template <typename Ret, typename Arg0, typename Arg1>
class function<Ret(Arg0, Arg1)> {

    // ...
    struct callable_base {
        virtual Ret operator()(Arg0 arg0, Arg1 arg1) = 0;
        // add a clone virtual function
        virtual callable_base* clone() const = 0;
        virtual ~callable_base() {}
    };


    template <typename F>
    struct callable
        : callable_base
    {
        F functor;

        // ...

        // subclass implements it to make a copy of itself
        virtual callable_base* clone() const
        {
            return new callable<F>(functor);
        }
    };

    callable_base* callable_ptr;
public:
    // use clone to copy a function object
    function(function const& rhs)
        : callable_ptr(rhs.callable_ptr->clone())
    {}

    function& operator=(function const& rhs)
    {
        delete callable_ptr;
        callable_ptr = rhs.callable_ptr->clone();
    }

    // and delete the pointer when destructed
    ~function()
    {
        delete callable_ptr;
    }

This is a oversimplification though it could run. The real world implements (like the implement shipped with gcc) uses more tricks to avoid heap allocation in some situation. Let me explain it in the next post.

Post tags: 

Leave a comment:




Creative Commons License Your comment will be licensed under
CC-NC-ND 3.0


. Back to Bit Focus Blog
NijiPress - Copyright (C) Neuron Teckid @ Bit Focus
About this site