Skip to content

File noal_func.h

File List > src > noal_func.h

Go to the documentation of this file

#pragma once

#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <utility>


namespace noal {

template<typename Res, typename... Args>
class funcptr {
    Res (*_func)(Args...);
public:
    funcptr(Res (*func)(Args...)) : _func(func) {}
    inline Res operator()(Args... args) {
        return _func(std::forward<Args>(args)...);
    }
    static Res call(void* self, Args... args) {
        return (static_cast<funcptr*>(self))->operator()(std::forward<Args>(args)...);
    }
};

template<typename Res, typename... Args>
Res invoker(void* func, Args... args) {
    return (*static_cast<Res(**)(Args...)>(func))(std::forward<Args>(args)...);
}

template<class Class, typename Res, typename... Args>
class memberfuncptr {
    Res (Class::*_func)(Args...);
    Class* _self;
public:
    memberfuncptr(Res (Class::*func)(Args...), Class* self) : _func(func), _self(self) {}
    inline Res operator()(Args... args) {
        return (_self->*_func)(std::forward<Args>(args)...);
    }
    static Res call(void* self, Args... args) {
        return static_cast<memberfuncptr*>(self)->operator()(std::forward<Args>(args)...);
    }
};

template<class Class, typename Res, typename... Args>
class memberconstfuncptr {
    Res (Class::*_func)(Args...) const;
    Class* _self;
public:
    memberconstfuncptr(Res (Class::*func)(Args...) const, Class* self) : _func(func), _self(self) {}
    inline Res operator()(Args... args) {
        return _func(std::forward<Args>(args)...);
    }
    static Res call(void* self, Args... args) {
        return static_cast<memberconstfuncptr*>(self)->operator()(std::forward<Args>(args)...);
    }
};

template<typename Func>
struct signatureHelper;

template<typename Func, typename Res, typename... Args>
struct signatureHelper<Res (Func::*)(Args...)> {
    using type = Res(Args...);
};

template<typename Func, typename Res, typename... Args>
struct signatureHelper<Res (Func::*)(Args...) &> {
    using type = Res(Args...);
};

template<typename Func, typename Res, typename... Args>
struct signatureHelper<Res (Func::*)(Args...) const> {
    using type = Res(Args...);
};

template<typename Func, typename Res, typename... Args>
struct signatureHelper<Res (Func::*)(Args...) const &> {
    using type = Res(Args...);
};

template<typename Func, typename Sign>
class callableany;

template<typename Func, typename Res, typename... Args>
class callableany<Func, Res(Args...)> {
    Func func;
public:
    callableany(Func _func) : func(_func) {
        static_assert(std::is_trivially_copyable_v<Func>);
        static_assert(std::is_trivially_destructible_v<Func>);
    }
    inline Res operator()(Args... args) {
        return func(std::forward<Args>(args)...);
    }
    static Res call(void* self, Args... args) {
        return static_cast<callableany<Func, Res(Args...)>*>(self)->operator()(std::forward<Args>(args)...);
    }
};
template<typename Func, typename Sign = typename signatureHelper<decltype(&Func::operator())>::type>
callableany(Func) -> callableany<Func, Sign>;


template<typename Sign, size_t dataSize>
class function;

template<typename Res, typename... Args, size_t dataSize>
class function<Res(Args...), dataSize> {
    using Sign = Res(Args...);

    Res (*call)(void*, Args...);
    uint8_t data[dataSize];
public:
// default/copy/move constructors, copy/move assignment operators
    function() = default;
    template<size_t otherSize>
    function& operator=(const function<Sign, otherSize>& other) {
        static_assert(otherSize <= dataSize, "Other function object is too large");
        // static_assert(sizeof(other) <= sizeof(*this), "Other function object is too large");

        std::copy(static_cast<uint8_t*>(&other), static_cast<uint8_t*>(&other) + sizeof(other), static_cast<uint8_t*>(this));
        std::fill(static_cast<uint8_t*>(this) + sizeof(other), static_cast<uint8_t*>(this) + sizeof(*this), 0);  // possibly unnecessary - prevent data leaks?
        return *this;
    }
    template<size_t otherSize>
    function& operator=(function<Sign, otherSize>&& other) { *this = other; }
    template<size_t otherSize>
    function(const function<Sign, otherSize>& other) { *this = other; }
    template<size_t otherSize>
    function(function<Sign, otherSize>&& other) { *this = other; }

// member functions
    Res operator()(Args... args) {
        return call(data, std::forward<Args>(args)...);
    }

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

// type erasure constructors, assignment operators
    explicit function(Res (*func)(Args...)) {
        // new (data) funcptr<Res, Args...>(func);
        // call = funcptr<Res, Args...>::call;
        std::copy(static_cast<uint8_t*>(&func), static_cast<uint8_t*>(&func) + sizeof(func), data);
        call = invoker<Res, Args...>;
    }
    function& operator=(Res (*func)(Args...)) { new (this) function(func); return *this; }

    template<class Class>
    function(Res (Class::*func)(Args...), Class* self) {
        new (data) memberfuncptr<Class, Res, Args...>(func, self);
        call = memberfuncptr<Class, Res, Args...>::call;
    }

    template<class Class>
    function(Res (Class::*func)(Args...) const, Class* self) {
        new (data) memberconstfuncptr<Class, Res, Args...>(func, self);
        call = memberconstfuncptr<Class, Res, Args...>::call;
    }
    template<typename Func, typename Sign = typename signatureHelper<decltype(&Func::operator())>::type>
    explicit function(Func func) {
        new (data) callableany<Func, Sign>(func);
        call = callableany<Func, Sign>::call;
    }
    template<typename Func, typename Sign = typename signatureHelper<decltype(&Func::operator())>::type>
    function& operator=(Func func) { new (this) function(func); return *this; }
};

// template<typename Res, typename... Args>
// function(Res (*)(Args...)) -> function<Res(Args...), sizeof(funcptr<Res, Args...>)>;
template<typename Res, typename... Args>
function(Res (*)(Args...)) -> function<Res(Args...), sizeof(Res (*)(Args...))>;

template<class Class, typename Res, typename... Args>
function(Res (Class::*)(Args...), Class*) -> function<Res(Args...), sizeof(memberfuncptr<Class, Res, Args...>)>;

template<class Class, typename Res, typename... Args>
function(Res (Class::*)(Args...) const, Class*) -> function<Res(Args...), sizeof(memberconstfuncptr<Class, Res, Args...>)>;

template<typename Func, typename Sign = typename signatureHelper<decltype(&Func::operator())>::type>
function(Func) -> function<Sign, sizeof(callableany<Func, Sign>)>;

} // namespace noal