Created
December 14, 2021 10:49
-
-
Save modeco80/b3f18050b58d1dae6cbc7061eb26342a to your computer and use it in GitHub Desktop.
Naive C++20 std::function<R(Args...)> implementation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <utility> | |
#include <new> | |
namespace detail { | |
template<class T, class ...Args> | |
struct BaseInvocable { | |
virtual ~BaseInvocable() = default; | |
virtual T operator()(Args&&...) = 0; | |
}; | |
template<class T, class ...Args> | |
struct PlainFn : public BaseInvocable<T, Args...> { | |
using FuncT = T(*)(Args...); | |
constexpr PlainFn(FuncT ptr) | |
: fp(ptr) { | |
} | |
T operator()(Args&&... args) override { | |
return fp(std::forward<Args>(args)...); | |
} | |
private: | |
FuncT fp{}; | |
}; | |
} // namespace detail | |
template<class T> | |
struct Function; | |
template<class R, class... Args> | |
struct Function<R(Args...)> { | |
// constructor for "bare" functions, | |
// or captureless lambda functions | |
template<class TBareFunction> | |
constexpr Function(TBareFunction barePtr) | |
: invokee(choose_construct<detail::PlainFn<R, Args...>>(barePtr)) { | |
} | |
// TODO: move and copy. | |
~Function() { | |
// not gonna ask, but | |
// we can handle this case too | |
if(!invokee) | |
return; | |
// We only need to call the BaseInvocable | |
// d-tor if we're using SSO. | |
if(using_sso) | |
invokee->~BaseInvocable(); | |
else | |
delete invokee; | |
} | |
constexpr R operator()(Args&&... args) { | |
return invokee->operator()(std::forward<Args>(args)...); | |
} | |
private: | |
// utilitarian helper to construct inside of the | |
// SSO ball | |
template<class T, class...Args2> | |
constexpr T* choose_construct(Args2&&... args) { | |
if constexpr(sizeof(T) <= sizeof(__sso_storage)) { | |
using_sso = true; | |
return new (&__sso_storage[0]) T(std::forward<Args2>(args)...); | |
} else { | |
using_sso = false; | |
return new T(std::forward<Args2>(args)...); | |
} | |
} | |
bool using_sso{}; | |
detail::BaseInvocable<R, Args...>* invokee{}; | |
// SSO used for small function types. | |
alignas(detail::BaseInvocable<R, Args...>) unsigned char __sso_storage[16]; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment