HelenOS sources

root/uspace/lib/cpp/include/__bits/functional/function.hpp

/* [<][>][^][v][top][bottom][index][help] */

INCLUDED FROM


DEFINITIONS

This source file includes following definitions.
  1. invoke_callable
  2. copy_callable
  3. destroy_callable
  4. swap
  5. assign
  6. target_type
  7. target
  8. target
  9. clear_
  10. swap

/*
 * Copyright (c) 2018 Jaroslav Jindrak
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 * - The name of the author may not be used to endorse or promote products
 *   derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef LIBCPP_BITS_FUNCTIONAL_FUNCTION
#define LIBCPP_BITS_FUNCTIONAL_FUNCTION

#include <__bits/functional/conditional_function_typedefs.hpp>
#include <__bits/functional/reference_wrapper.hpp>
#include <__bits/memory/allocator_arg.hpp>
#include <__bits/memory/allocator_traits.hpp>
#include <typeinfo>
#include <type_traits>
#include <utility>

namespace std
{
    /**
     * 20.9.12, polymorphic function adaptors:
     */

    namespace aux
    {
        // TODO: fix this
        /* template<class, class T, class... Args> */
        /* struct is_callable_impl: false_type */
        /* { /1* DUMMY BODY *1/ }; */

        /* template<class, class R, class... Args> */
        /* struct is_callable_impl< */
        /*     void_t<decltype(aux::invoke(declval<R(Args...)>(), declval<Args>()..., R))>, */
        /*     R, Args... */
        /* > : true_type */
        /* { /1* DUMMY BODY *1/ }; */

        /* template<class T> */
        /* struct is_callable: is_callable_impl<void_t<>, T> */
        /* { /1* DUMMY BODY *1/ }; */

        template<class Callable, class R, class... Args>
        R invoke_callable(Callable* clbl, Args&&... args)
        {
            return (*clbl)(forward<Args>(args)...);
        }

        template<class Callable>
        void copy_callable(Callable* to, Callable* from)
        {
            new(to) Callable{*from};
        }

        template<class Callable>
        void destroy_callable(Callable* clbl)
        {
            if (clbl)
                clbl->~Callable();
        }
    }

    // TODO: implement
    class bad_function_call;

    template<class>
    class function; // undefined

    /**
     * Note: Ideally, this implementation wouldn't
     *       copy the target if it was a pointer to
     *       a function, but for the simplicity of the
     *       implementation, we do copy even in that
     *       case for now. It would be a nice optimization
     *       if this was changed in the future.
     */
    template<class R, class... Args>
    class function<R(Args...)>
        : public aux::conditional_function_typedefs<Args...>
    {
        public:
            using result_type = R;

            /**
             * 20.9.12.2.1, construct/copy/destroy:
             */

            function() noexcept
                : callable_{}, callable_size_{}, call_{},
                  copy_{}, dest_{}
            { /* DUMMY BODY */ }

            function(nullptr_t) noexcept
                : function{}
            { /* DUMMY BODY */ }

            function(const function& other)
                : callable_{}, callable_size_{other.callable_size_},
                  call_{other.call_}, copy_{other.copy_}, dest_{other.dest_}
            {
                callable_ = new uint8_t[callable_size_];
                (*copy_)(callable_, other.callable_);
            }

            function(function&& other)
                : callable_{other.callable_}, callable_size_{other.callable_size_},
                  call_{other.call_}, copy_{other.copy_}, dest_{other.dest_}
            {
                other.callable_ = nullptr;
                other.callable_size_ = size_t{};
                other.call_ = nullptr;
                other.copy_ = nullptr;
                other.dest_ = nullptr;
            }

            // TODO: shall not participate in overloading unless aux::is_callable<F>
            template<class F>
            function(F f)
                : callable_{}, callable_size_{sizeof(F)},
                  call_{(call_t)aux::invoke_callable<F, R, Args...>},
                  copy_{(copy_t)aux::copy_callable<F>},
                  dest_{(dest_t)aux::destroy_callable<F>}
            {
                callable_ = new uint8_t[callable_size_];
                (*copy_)(callable_, (uint8_t*)&f);
            }

            /**
             * Note: For the moment we're ignoring the allocator
             *       for simplicity of the implementation.
             */

            template<class A>
            function(allocator_arg_t, const A& a) noexcept
                : function{}
            { /* DUMMY BODY */ }

            template<class A>
            function(allocator_arg_t, const A& a, nullptr_t) noexcept
                : function{}
            { /* DUMMY BODY */ }

            template<class A>
            function(allocator_arg_t, const A& a, const function& other)
                : function{other}
            { /* DUMMY BODY */ }

            template<class A>
            function(allocator_arg_t, const A& a, function&& other)
                : function{move(other)}
            { /* DUMMY BODY */ }

            // TODO: shall not participate in overloading unless aux::is_callable<F>
            template<class F, class A>
            function(allocator_arg_t, const A& a, F f)
                : function{f}
            { /* DUMMY BODY */ }

            function& operator=(const function& rhs)
            {
                function{rhs}.swap(*this);

                return *this;
            }

            /**
             * Note: We have to copy call_, copy_
             *       and dest_ because they can be templated
             *       by a type F we don't know.
             */
            function& operator=(function&& rhs)
            {
                clear_();

                callable_ = rhs.callable_;
                callable_size_ = rhs.callable_size_;
                call_ = rhs.call_;
                copy_ = rhs.copy_;
                dest_ = rhs.dest_;

                rhs.callable_ = nullptr;
                rhs.callable_size_ = size_t{};
                rhs.call_ = nullptr;
                rhs.copy_ = nullptr;
                rhs.dest_ = nullptr;

                return *this;
            }

            function& operator=(nullptr_t) noexcept
            {
                clear_();

                return *this;
            }

            // TODO: shall not participate in overloading unless aux::is_callable<F>
            template<class F>
            function& operator=(F&& f)
            {
                callable_size_ = sizeof(F);
                callable_ = new uint8_t[callable_size_];
                call_ = aux::invoke_callable<F, R, Args...>;
                copy_ = aux::copy_callable<F>;
                dest_ = aux::destroy_callable<F>;

                (*copy_)(callable_, (uint8_t*)&f);
            }

            template<class F>
            function& operator=(reference_wrapper<F> ref) noexcept
            {
                return (*this) = ref.get();
            }

            ~function()
            {
                if (callable_)
                {
                    (*dest_)(callable_);
                    delete[] callable_;
                }
            }

            /**
             * 20.9.12.2.2, function modifiers:
             */

            void swap(function& other) noexcept
            {
                std::swap(callable_, other.callable_);
                std::swap(callable_size_, other.callable_size_);
                std::swap(call_, other.call_);
                std::swap(copy_, other.copy_);
                std::swap(dest_, other.dest_);
            }

            template<class F, class A>
            void assign(F&& f, const A& a)
            {
                function{allocator_arg, a, forward<F>(f)}.swap(*this);
            }

            /**
             * 20.9.12.2.3, function capacity:
             */

            explicit operator bool() const noexcept
            {
                return callable_ != nullptr;
            }

            /**
             * 20.9.12.2.4, function invocation:
             */

            result_type operator()(Args... args) const
            {
                // TODO: throw bad_function_call if !callable_ || !call_
                if constexpr (is_same_v<R, void>)
                    (*call_)(callable_, forward<Args>(args)...);
                else
                    return (*call_)(callable_, forward<Args>(args)...);
            }

            /**
             * 20.9.12.2.5, function target access:
             */

            const type_info& target_type() const noexcept
            {
                return typeid(*callable_);
            }

            template<class T>
            T* target() noexcept
            {
                if (target_type() == typeid(T))
                    return (T*)callable_;
                else
                    return nullptr;
            }

            template<class T>
            const T* target() const noexcept
            {
                if (target_type() == typeid(T))
                    return (T*)callable_;
                else
                    return nullptr;
            }

        private:
            using call_t = R(*)(uint8_t*, Args&&...);
            using copy_t = void (*)(uint8_t*, uint8_t*);
            using dest_t = void (*)(uint8_t*);

            uint8_t* callable_;
            size_t callable_size_;
            call_t call_;
            copy_t copy_;
            dest_t dest_;

            void clear_()
            {
                if (callable_)
                {
                    (*dest_)(callable_);
                    delete[] callable_;
                    callable_ = nullptr;
                }
            }
    };

    /**
     * 20.9.12.2.6, null pointer comparisons:
     */

    template<class R, class... Args>
    bool operator==(const function<R(Args...)>& f, nullptr_t) noexcept
    {
        return !f;
    }

    template<class R, class... Args>
    bool operator==(nullptr_t, const function<R(Args...)>& f) noexcept
    {
        return !f;
    }

    template<class R, class... Args>
    bool operator!=(const function<R(Args...)>& f, nullptr_t) noexcept
    {
        return (bool)f;
    }

    template<class R, class... Args>
    bool operator!=(nullptr_t, const function<R(Args...)>& f) noexcept
    {
        return (bool)f;
    }

    /**
     * 20.9.12.2.7, specialized algorithms:
     */

    template<class R, class... Args>
    void swap(function<R(Args...)>& f1, function<R(Args...)>& f2)
    {
        f1.swap(f2);
    }

    template<class R, class... Args, class Alloc>
    struct uses_allocator<function<R(Args...)>, Alloc>
        : true_type
    { /* DUMMY BODY */ };
}

#endif

/* [<][>][^][v][top][bottom][index][help] */
HelenOS homepage, sources at GitHub