Skip to content

Instantly share code, notes, and snippets.

@talybin
Last active April 11, 2026 10:20
Show Gist options
  • Select an option

  • Save talybin/9d250de408025bc6221b976d0b6e74f8 to your computer and use it in GitHub Desktop.

Select an option

Save talybin/9d250de408025bc6221b976d0b6e74f8 to your computer and use it in GitHub Desktop.
#include <iostream>
#include <functional>
#include <any>
namespace detail
{
// Like std::decay but preserves reference qualifiers:
// arrays -> pointer, functions -> function pointer, strips cv but NOT ref.
template <class T>
using transform_arg_t = std::conditional_t<
std::is_array_v<std::remove_reference_t<T>> || std::is_function_v<std::remove_reference_t<T>>,
std::decay_t<T>,
std::remove_cv_t<T>
>;
// Get function signature to be stored in std::any
template <class T>
struct fn_signature : fn_signature<decltype(std::function(std::declval<T>()))> {};
template <class T>
struct fn_signature<std::function<T>> : fn_signature<T> {};
template <class R, class... Args>
struct fn_signature<R(Args...)> {
using type = std::function<R(transform_arg_t<Args>...)>;
};
template <class T>
using fn_signature_t = typename fn_signature<T>::type;
} // namespace detail
struct function_any
{
function_any() = default;
template <class F,
std::enable_if_t<!std::is_same_v<std::decay_t<F>, function_any>, int> = 0,
class = detail::fn_signature_t<std::decay_t<F>>
>
function_any(F&& f)
: fn(detail::fn_signature_t<F>(std::forward<F>(f)))
{ }
explicit operator bool() const noexcept { return fn.has_value(); }
template <class R = void, class... Args>
R operator()(Args&&... args) const
{
using F = detail::fn_signature_t<R(Args...)>;
return std::any_cast<F>(fn)(std::forward<Args>(args)...);
}
private:
std::any fn;
};
int main()
{
function_any fn;
std::cout << "--- Testing three arguments ---\n";
fn = [](const char* str, int& i, int* buf) {
std::cout << str << '\n';
i = 42;
buf[1] = 71;
};
int buf[3] = { 100, 200, 300 };
int x = 10;
fn("foobar", x, buf);
std::cout << "x: " << x << '\n';
for (int b : buf)
std::cout << b << '\n';
std::cout << "--- Testing one argument ---\n";
fn = [](double val) {
std::cout << val << '\n';
};
fn(3.14);
std::cout << "--- Testing return value ---\n";
fn = [](int a, int b) { return a + b; };
int result = fn.operator()<int>(3, 4); // result == 7
std::cout << "result: " << result << '\n';
}
@talybin
Copy link
Copy Markdown
Author

talybin commented Aug 14, 2021

Output:

--- Testing three arguments ---
foobar
x: 42
100
71
300
--- Testing one argument ---
3.14
--- Testing return value ---
result: 7

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