Skip to content

Instantly share code, notes, and snippets.

@Redchards
Last active December 5, 2023 13:54
Show Gist options
  • Save Redchards/c5be14c2998f1ca1d757 to your computer and use it in GitHub Desktop.
Save Redchards/c5be14c2998f1ca1d757 to your computer and use it in GitHub Desktop.
Simple implementation of a function like std::bind.
#include <iostream>
#include <functional>
#include <tuple>
#include <type_traits>
// Actually, a subtle bug may arise when the function is using index_constant as parameter.
// For this reason, a more correct implementation should define a specialized class, hidden in a namespace,
// and forbid the use in other places.
// An easier, more correct, but more verbose way would be to just define two different classes for the argument list
// and the bounded arguments, to avoid the confusion with the index_constant bracket operator overload.
// However, as this is only an academic example, it should strive to stay extremly simple and straightforward, so this
// improvment will not be applied. I just wanted anyone wanting to use this somewhere to be aware of this caveheat.
// Moreover, you should use std::bind anyway.
template<size_t n>
using index_constant = std::integral_constant<size_t, n>;
template<class ... Args>
class binder_list
{
public:
template<class ... TArgs>
constexpr binder_list(TArgs&&... args) noexcept
: boundedArgs_{std::forward<TArgs>(args)...}
{}
template<size_t n>
constexpr decltype(auto) operator[](index_constant<n>) noexcept
{
return std::get<n>(boundedArgs_);
}
private:
std::tuple<Args...> boundedArgs_;
};
template<class ... Args>
class callee_list
{
public:
template<class ... TArgs>
constexpr callee_list(TArgs&&... args) noexcept
: boundedArgs_{std::forward<TArgs>(args)...}
{}
template<class T, std::enable_if_t<(std::is_placeholder<std::remove_reference_t<T>>::value == 0)>* = nullptr>
constexpr decltype(auto) operator[](T&& t) noexcept
{
return std::forward<T>(t);
}
template<class T, std::enable_if_t<(std::is_placeholder<T>::value != 0)>* = nullptr>
constexpr decltype(auto) operator[](T) noexcept
{
return std::get<std::is_placeholder<T>::value - 1>(std::move(boundedArgs_));
}
private:
std::tuple<Args&&...> boundedArgs_;
};
template<class Fn, class ... Args>
class binder
{
public:
template<class TFn, class ... TArgs>
constexpr binder(TFn&& f, TArgs&&... args) noexcept
: f_{std::forward<TFn>(f)},
argumentList_{std::forward<TArgs>(args)...}
{}
// Please C++, give me a way of detecting noexcept :'(
template<class ... CallArgs>
constexpr decltype(auto) operator()(CallArgs&&... args)
//noexcept(noexcept(call(std::make_index_sequence<sizeof...(Args)>{}, std::declval<Args>()...)))
{
return call(std::make_index_sequence<sizeof...(Args)>{}, std::forward<CallArgs>(args)...);
}
private:
template<class ... CallArgs, size_t ... Seq>
constexpr decltype(auto) call(std::index_sequence<Seq...>, CallArgs&&... args)
//noexcept(noexcept(f_(this->binder_list<CallArgs...>{std::declval<CallArgs>()...}[this->argumentList_[index_constant<Seq>{}]]...)))
{
return f_((callee_list<CallArgs...>{std::forward<CallArgs>(args)...}[argumentList_[index_constant<Seq>{}]])...);
}
private:
std::function<std::remove_reference_t<std::remove_pointer_t<Fn>>> f_;
binder_list<Args...> argumentList_;
};
namespace me
{
template<class Fn, class ... Args>
binder<Fn, Args...> bind(Fn&& f, Args&&... args)
{
return binder<Fn, Args...>{std::forward<Fn>(f), std::forward<Args>(args)...};
}
}
@Redchards
Copy link
Author

Thanks a lot for this sample!
Helped me once with my problem, much better than reading standard or boost sources.

By the way. AFAIK, std::bind stores copies of the given parameters. Something like std::tuple<typename std::decay_t<Args>...> boundedArgs_; could improve it.

Same response as above with the little added bonus that yeah, the "binder_list" class is useless, no flipping idea why I wrote it this way. Gonna update it with proper standard compliance and taking your remark into account. Thanks.

Copy link

ghost commented Jul 20, 2020

you can detect noexcept, like this:

noexcept(run(args...))
it returns a bool, plug that back into noexcept

noexcept(noexcept(run(args...)))

@Redchards
Copy link
Author

you can detect noexcept, like this:

noexcept(run(args...))
it returns a bool, plug that back into noexcept

noexcept(noexcept(run(args...)))

Yes, as you can see in the code comments, I have indeed tried to do that but it must've been acting out back then. Bear in mind that I have written this code a few years ago.

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