Skip to content

Instantly share code, notes, and snippets.

@talybin
Last active November 21, 2019 00:28
Show Gist options
  • Select an option

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

Select an option

Save talybin/d61e87cb020fee20b5a3ca43e7b9f53e to your computer and use it in GitHub Desktop.
An attempt to any function holder type with only requirement to simillar return type. Has a lot of issues. Do not use!
#pragma once
#include <functional>
#include <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 return_type = R;
using type = R(Args...);
template <class T>
using set_rtype = T(Args...);
};
// Any value auto cast
struct any_value : std::any
{
using std::any::any;
template <class T>
operator T() {
return std::any_cast<T>(*this);
}
};
// std::any or void will accept any function
template <class R = any_value>
struct any_function
{
any_function() = default;
template <class F>
any_function(F&& fn)
{
using fn_sign = fn_signature<F>;
using cast_sign = typename fn_sign::template set_rtype<R>;
fn_ = std::function<cast_sign>([fn = std::forward<F>(fn)](auto&&... args) -> R
{
if constexpr (std::is_same_v<R, void>)
fn(std::forward<decltype(args)>(args)...);
else if constexpr (std::is_same_v<typename fn_sign::return_type, void>) {
fn(std::forward<decltype(args)>(args)...);
return { };
}
else
return fn(std::forward<decltype(args)>(args)...);
});
}
template <class... Args>
R operator()(Args&&... args) const
{
using fn_type = std::function<R(Args...)>;
if (auto fn = std::any_cast<fn_type>(&fn_))
return (*fn)(std::forward<Args>(args)...);
else
throw std::bad_function_call();
}
private:
std::any fn_;
};
// Any function holder type with only requirement to simillar return type
#pragma once
#include <functional>
#include <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 return_type = R;
using type = R(Args...);
template <class T>
using set_rtype = T(Args...);
};
// void will accept any function
template <class R = void>
struct ret_function
{
ret_function() = default;
template <class F>
ret_function(F&& fn)
{
using fn_sign = fn_signature<F>;
using cast_sign = typename fn_sign::template set_rtype<R>;
static_assert(std::is_same_v<R, void> || // Skip check if R is void
std::is_convertible_v<typename fn_sign::return_type, R>);
fn_ = std::function<cast_sign>(std::forward<F>(fn));
}
template <class... Args>
R operator()(Args&&... args) const
{
using fn_type = std::function<R(Args...)>;
if (auto fn = std::any_cast<fn_type>(&fn_))
return (*fn)(std::forward<Args>(args)...);
else
throw std::bad_function_call();
}
private:
std::any fn_;
};
#include "ret_function.hpp"
#include <iostream>
int main()
{
int a = 100;
auto b = [&](int x) {
return "sum: " + std::to_string(x + a);
};
ret_function<std::string> fn(b);
std::cout << fn(42) << '\n';
try {
fn(1, 2, 3);
throw std::runtime_error("not expected");
}
catch (const std::bad_function_call&) {
std::cout << "throw expected\n";
}
fn = [](int a, int b, int c) {
#if 0
// Compile time error: return type do not match
return 4;
#else
// OK: char array is convertible to std::string
return "4";
#endif
};
std::cout << fn(1, 2, 3) << '\n';
}
@talybin

talybin commented Jun 5, 2019

Copy link
Copy Markdown
Author

Output:

sum: 142
throw expected
4

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