Skip to content

Instantly share code, notes, and snippets.

@LYP951018
Created June 10, 2017 16:26
Show Gist options
  • Save LYP951018/35a139877f1ea49d8e08280fadf1d87f to your computer and use it in GitHub Desktop.
Save LYP951018/35a139877f1ea49d8e08280fadf1d87f to your computer and use it in GitHub Desktop.
Naive Bind
#include <type_traits>
#include <functional>
#include <tuple>
#include <utility>
#include <iostream>
#include <memory>
template<int I>
struct Placeholder
{
inline static constexpr auto Value = I;
};
inline static constexpr auto _1 = Placeholder<0>{};
inline static constexpr auto _2 = Placeholder<1>{};
template<typename T>
struct IsPlaceholder : std::false_type {};
template<int I>
struct IsPlaceholder<Placeholder<I>> : std::true_type {};
template<typename T>
struct IsReferenceWrapper : std::false_type {};
template<typename T>
struct IsReferenceWrapper<std::reference_wrapper<T>> : std::true_type {};
template<int I, typename... Args, typename... UArgs>
decltype(auto) Map(std::tuple<Args...>& args, [[maybe_unused]] std::tuple<UArgs...> uargs)
{
using Type = std::tuple_element_t<I, std::tuple<Args...>>;
using DecayedType = std::decay_t<Type>;
if constexpr(IsPlaceholder<DecayedType>::value)
{
return (std::get<DecayedType::Value>(uargs));
}
else if constexpr(IsReferenceWrapper<DecayedType>::value)
{
return std::get<I>(args).get();
}
else
{
return std::get<I>(args);
}
}
template<typename F, typename... Args>
struct Binder
{
std::tuple<Args...> Parameters;
F Callable;
template<typename... UArgs>
decltype(auto) operator()(UArgs&&... uargs)
{
return Impl(std::make_integer_sequence<int, sizeof...(Args)>{}, std::forward<UArgs>(uargs)...);
}
template<int... I, typename... UArgs>
decltype(auto) Impl(std::integer_sequence<int, I...>, UArgs&&... uargs)
{
return std::invoke(Callable, Map<I>(Parameters, std::forward_as_tuple(std::forward<UArgs>(uargs)...))...);
}
};
template<typename F, typename... Args>
Binder<std::decay_t<F>, std::decay_t<Args>...> Bind(F&& f, Args&&... args)
{
return {{std::forward<Args>(args)...}, std::forward<F>(f)};
}
void f(int n1, int n2, int n3, const int& n4, int n5)
{
std::cout << n1 << ' ' << n2 << ' ' << n3 << ' ' << n4 << ' ' << n5 << '\n';
}
int g(int n1)
{
return n1;
}
struct Foo {
void print_sum(int n1, int n2)
{
std::cout << n1+n2 << '\n';
}
int data = 10;
};
int main()
{
int n = 7;
// (_1 and _2 are from std::placeholders, and represent future
// arguments that will be passed to f1)
auto f1 = Bind(f, _2, _1, 42, std::cref(n), n);
n = 10;
f1(1, 2, 1001); // 1 is bound by _1, 2 is bound by _2, 1001 is unused
// makes a call to f(2, 1, 42, n, 7)
Foo foo;
auto f3 = Bind(&Foo::print_sum, &foo, 95, _1);
f3(5);
// bind to a pointer to data member
auto f4 = Bind(&Foo::data, _1);
std::cout << f4(foo) << '\n';
// smart pointers can be used to call members of the referenced objects, too
std::cout << f4(std::make_shared<Foo>(foo)) << '\n'
<< f4(std::make_unique<Foo>(foo)) << '\n';
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment