Skip to content

Instantly share code, notes, and snippets.

@pplux
Created July 13, 2020 13:05
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save pplux/4ef53360dba1d362538de9f4dedafd09 to your computer and use it in GitHub Desktop.
Save pplux/4ef53360dba1d362538de9f4dedafd09 to your computer and use it in GitHub Desktop.
#include <iostream>
#include <assert.h>
#include <vector>
template<class T>
struct Func{};
// std::func replacement without dynamic memory allocation (captures are limited to 48 bytes)
// this could be extended to support captures >48 bytes... of course, with a bit more of logic
// and some state.
template<class R, class... Args>
struct Func<R(Args...)> {
template<class U>
Func(U &&callable) { set(std::move(callable)); }
template<class U>
Func& operator=(U &&callable) { set(std::move(callable)); return *this; }
Func(Func&& other) noexcept {
swap(std::move(other));
}
Func& operator=(Func &&other) noexcept {
swap(std::move(other));
}
~Func() {
if (_destroy) _destroy(_space);
}
R operator()(Args... args) {
return _func(_space, std::forward<Args>(args)...);
}
template<class U>
void set(U &&callable) {
assert(sizeof(U) <= sizeof(_space));
new (_space)U(std::move(callable));
_func = [](void *ptr, Args... args) -> R { return (*reinterpret_cast<U*>(ptr))(std::forward<Args>(args)...); };
_destroy = [](void *ptr) { reinterpret_cast<U*>(ptr)->~U();};
};
void swap(Func &&other) {
std::swap(_space, other._space);
std::swap(_func, other._func);
std::swap(_destroy, other._destroy);
}
R (*_func)(void *, Args...) = nullptr;
void (*_destroy)(void *) = nullptr;
uint8_t _space[64-sizeof(_func)-sizeof(_destroy)];
};
int main ()
{
int f = 5.0f;
std::vector<Func<float(int,int)>> callbacks;
callbacks.push_back([](int a, int b) -> float { return a+b;});
callbacks.push_back([f](int a, int b) -> float { return f+a+b;});
for(auto &&f: callbacks) {
printf("%f\n", f(1,2));
}
}
@meshula
Copy link

meshula commented Jan 30, 2021

This is a really nice reference, thanks for posting.

@pplux
Copy link
Author

pplux commented Oct 3, 2023

Based on some conversations, this is an example of the same concept but for Callbacks that doesn't need to be stored

#include <stdio.h>
#include <utility>

template<class T> struct Callback;

template<class R, class... Args>
struct Callback<R(Args...)> {
    Callback() {}
    
    template<class F>
    Callback(F *f) {
        _func = [](void *data, Args... args) -> R {
            F *f = (F*)data;
            return (*f)(args...);
        };
        data = (void*)f;
    }

    template<class F>
    Callback(F &&func) : Callback(&func) {}

    R operator()(Args... args) {
        return _func(data, std::forward<Args>(args)...);
    }

    R (*_func)(void *, Args...) = nullptr;
    void *data = nullptr;
};

__attribute__((noinline))
void test(Callback<void(int)> callback, int times) {
    for (int i = 0; i < times; i++) {
        callback(i);
    }
}

int main(int argc, char **argv) {
    float f = 2.5f;
    auto lambda = [&](int i) { printf("%s %f\n", argv[i], f); };
    test(&lambda, argc);

    // TEST 2
    test([&](int i) { printf("%s %f\n", argv[i], f);} , argc);
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment