File allocators.hpp

File List > include > simplesquirrel > allocators.hpp

Go to the documentation of this file.

#pragma once
#ifndef SSQ_ALLOCATORS_HEADER_H
#define SSQ_ALLOCATORS_HEADER_H

#include "object.hpp"
#include "args.hpp"
#include <functional>

namespace ssq {
#ifndef DOXYGEN_SHOULD_SKIP_THIS
    namespace detail {
        template <size_t... Is>
        struct index_list {
        };

        // Declare primary template for index range builder
        template <size_t MIN, size_t N, size_t... Is>
        struct range_builder;

        // Base step
        template <size_t MIN, size_t... Is>
        struct range_builder<MIN, MIN, Is...> {
            typedef index_list<Is...> type;
        };

        // Induction step
        template <size_t MIN, size_t N, size_t... Is>
        struct range_builder : public range_builder<MIN, N - 1, N - 1, Is...> {
        };

        // Meta-function that returns a [MIN, MAX) index range
        template<size_t MIN, size_t MAX>
        using index_range = typename detail::range_builder<MIN, MAX>::type;

        template<class Ret>
        struct FuncPtr {
            const std::function<Ret()>* ptr;
        };

        template<class Ret, typename... Args>
        struct FuncPtr<Ret(Args...)> {
            const std::function<Ret(Args...)>* ptr;
        };

        template<class T>
        static T* defaultClassAllocator() {
            return new T();
        }

        template<class T, class... Args, size_t... Is>
        static T* callConstructor(HSQUIRRELVM vm, FuncPtr<T*(Args...)>* funcPtr, index_list<Is...>) {
            return funcPtr->ptr->operator()(detail::pop<Args>(vm, Is + 2)...);
        }

        template<class T, class... Args>
        static SQInteger classAllocator(HSQUIRRELVM vm) {
            static const std::size_t nparams = sizeof...(Args);
            int off = nparams;

            FuncPtr<T*(Args...)>* funcPtr;
            sq_getuserdata(vm, -1, reinterpret_cast<void**>(&funcPtr), nullptr);

            T* p = callConstructor<T, Args...>(vm, funcPtr, index_range<0, sizeof...(Args)>());
            sq_setinstanceup(vm, -2 -off, p);
            sq_setreleasehook(vm, -2 -off, &detail::classDestructor<T>);

            sq_getclass(vm, -2 -off);
            sq_settypetag(vm, -1, reinterpret_cast<SQUserPointer>(typeid(T*).hash_code()));
            sq_pop(vm, 1); // Pop class
            return nparams;
        }

        template<class T, class... Args>
        static SQInteger classAllocatorNoRelease(HSQUIRRELVM vm) {
            static const std::size_t nparams = sizeof...(Args);
            int off = nparams;

            FuncPtr<T*(Args...)>* funcPtr;
            sq_getuserdata(vm, -1, reinterpret_cast<void**>(&funcPtr), nullptr);

            T* p = callConstructor<T, Args...>(vm, funcPtr, index_range<0, sizeof...(Args)>());
            sq_setinstanceup(vm, -2 -off, p);

            sq_getclass(vm, -2 -off);
            sq_settypetag(vm, -1, reinterpret_cast<SQUserPointer>(typeid(T*).hash_code()));
            sq_pop(vm, 1); // Pop class
            return nparams;
        }

        template<class Ret, class... Args>
        static SQInteger funcReleaseHook(SQUserPointer p, SQInteger size) {
            auto funcPtr = reinterpret_cast<FuncPtr<Ret(Args...)>*>(p);
            delete funcPtr->ptr;
            return 0;
        }
    }

    template<typename T>
    inline T Object::to() const {
        sq_pushobject(vm, obj);
        try {
            auto ret = detail::pop<T>(vm, -1);
            sq_pop(vm, 1);
            return ret;
        } catch (...) {
            sq_pop(vm, 1);
            std::rethrow_exception(std::current_exception());
        }
    }
#endif
}

#endif