Last active
October 11, 2020 14:56
-
-
Save Liam0205/463702b282d1f102b6ccb56bb9f0a82e to your computer and use it in GitHub Desktop.
Implementation of `operator->*` for `std::shared_ptr`
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <functional> | |
#include <type_traits> | |
#include <cstddef> | |
#include <utility> | |
#include <memory> | |
namespace yuuki { | |
template<size_t> // begin with 0 here! | |
struct placeholder_template | |
{}; | |
} // namespace yuuki | |
namespace std { | |
template<size_t N> | |
struct is_placeholder<yuuki::placeholder_template<N>> | |
: integral_constant<int, N + 1> // the one is important | |
{}; | |
} // namespace std | |
namespace yuuki { | |
template <typename Ret, typename... Args, size_t... Is> | |
decltype(auto) bind(Ret (*p)(Args...), std::index_sequence<Is...>) { | |
static_assert(sizeof...(Args) == sizeof...(Is)); | |
return std::bind(p, placeholder_template<Is>{}...); | |
} | |
template <typename Ret, typename... Args, typename... Given, size_t... Is> | |
decltype(auto) bind(Ret (*p)(Args...), std::index_sequence<Is...>, Given... g) { | |
static_assert(sizeof...(Args) == sizeof...(Given) + sizeof...(Is)); | |
return std::bind(p, std::forward<Given>(g)..., placeholder_template<Is>{}...); | |
} | |
template <typename Ret, typename... Args, typename... Given> | |
decltype(auto) bind(Ret (*p)(Args...), Given... g) { | |
constexpr size_t num_placeholders = sizeof...(Args) - sizeof...(Given); | |
if constexpr (sizeof...(Given) == 0) { | |
return yuuki::bind(p, std::make_index_sequence<num_placeholders>{}); | |
} else { | |
return yuuki::bind(p, std::make_index_sequence<num_placeholders>{}, std::forward<Given>(g)...); | |
} | |
} | |
template <typename T, typename Ret, typename... Args> | |
decltype(auto) pmf_conversion(Ret(T::*p)(Args...)) { | |
return reinterpret_cast<Ret(*)(T*, Args...)>(p); | |
} | |
template <typename T, typename Ret, typename... Args> | |
decltype(auto) pmf_conversion(Ret(T::*p)(Args...) const) { | |
return reinterpret_cast<Ret(*)(const T*, Args...)>(p); | |
} | |
} // namespace yuuki | |
template <typename T, typename MemberT> | |
decltype(auto) operator->*(std::shared_ptr<T> ptr, MemberT mem) { | |
if constexpr (std::is_member_function_pointer_v<MemberT>) { | |
return yuuki::bind(yuuki::pmf_conversion(mem), ptr.get()); | |
} else { | |
return ptr.get()->*mem; | |
} | |
} | |
struct Foo { | |
void bar(double) { | |
std::cerr << "You're calling " << __PRETTY_FUNCTION__ << "\n"; | |
} | |
void qux() const { | |
std::cerr << "You're calling " << __PRETTY_FUNCTION__ << "\n"; | |
} | |
int baz = 42; | |
}; | |
void bazz() { | |
std::cerr << "You're calling " << __PRETTY_FUNCTION__ << "\n"; | |
} | |
int main() { | |
yuuki::bind(bazz)(); | |
auto smartp = std::make_shared<Foo>(); | |
auto rawp = smartp.get(); | |
auto funcp = &Foo::bar; | |
auto memp = &Foo::baz; | |
(rawp->*funcp)(0.0); | |
(smartp->*funcp)(0.0); | |
((*rawp).*funcp)(0.0); | |
((*smartp).*funcp)(0.0); | |
std::cerr << "Accessing data member by member-pointer: " << (rawp->*memp) << '\n'; | |
std::cerr << "Accessing data member by member-pointer: " << (smartp->*memp) << '\n'; | |
std::cerr << "Accessing data member by member-pointer: " << ((*rawp).*memp) << '\n'; | |
std::cerr << "Accessing data member by member-pointer: " << ((*smartp).*memp) << '\n'; | |
// smartp->qux(); | |
auto funcq = &Foo::qux; | |
yuuki::pmf_conversion(funcq)(rawp); | |
(rawp->*funcq)(); | |
(smartp->*funcq)(); | |
((*rawp).*funcq)(); | |
((*smartp).*funcq)(); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment