Skip to content

Instantly share code, notes, and snippets.

@modeco80
Created December 14, 2021 10:49
Show Gist options
  • Save modeco80/b3f18050b58d1dae6cbc7061eb26342a to your computer and use it in GitHub Desktop.
Save modeco80/b3f18050b58d1dae6cbc7061eb26342a to your computer and use it in GitHub Desktop.
Naive C++20 std::function<R(Args...)> implementation
#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