left arrow operator(<-)を引数付きのメンバ関数にも対応してみた
ツイッターから見させて頂いたこちらのエントリ。
http://www.atnnn.com/p/operator-larrow/
内容としては、クラスのポインタを持っていて関数を呼び出したい時、rigth arrow operatorを用いる事でそのメゾットを呼び出す事ができる。では、関数ポインタがあってそれをクラスから呼び出したい時どうする?そう、left arrow operatorを使いなさい!…というもの。(上記リンクからコピペ)
#include <iostream> template<class T> struct larrow { larrow(T* a_) : a(a_) { } T* a; }; template <class T, class R> R operator<(R (T::* f)(), larrow<T> it) { return (it.a->*f)(); } template<class T> larrow<T> operator-(T& a) { return larrow<T>(&a); } struct C { void f() { std::cout << "foo\n"; } }; int main() { C x; (&C::f)<-x; }
operator overloadを駆使してうまくleft arrow operatorの視覚的な表現を可能にしている。しかし、上記の記述だとまずconstなメンバ関数を呼ぶ事ができない。まあそれは単純にconst版をオーバーロードすれば解決するので良しとする。
次に、引数付きのメンバ関数を呼ぶことができない。メンバ関数ポインタを渡すために、オーバーロードしているoperatorはLess than operatorなので、当然だが可変な引数を受け取る手段はない。
...という事で、tupleで受け取る実装を試しに作ってみた。
#include<iostream> #include<utility> #include<tuple> #include<map> namespace atnnn{ template<class _Tp> struct larrow{ larrow(_Tp& a_):a(&a_){} _Tp* const a; }; }; template<class _Tp,class _Fp> constexpr _Fp operator<(_Fp (_Tp::* mem_func_ptr)(),atnnn::larrow<_Tp> it) { return (it.a->*mem_func_ptr)(); } template<class _Tp,class _Fp> constexpr _Fp operator<(_Fp (_Tp::* mem_func_ptr)()const,atnnn::larrow<_Tp> it) { return (it.a->*mem_func_ptr)(); } template<class _Fp, class Tuple, size_t... Size, class _Tp> auto apply_(_Fp&& f,Tuple&& args,std::index_sequence<Size...>,atnnn::larrow<_Tp> it) { return (it.a->*f)(std::get<Size>(std::forward<Tuple>(args))...); } template<class _Fp, class Tuple, class _Tp, class Indices=std::make_index_sequence<std::tuple_size<Tuple>::value>> auto apply(_Fp&& f,Tuple&& args,atnnn::larrow<_Tp> it) { return apply_(f,args,Indices(),it); } template<class _Tp,class _Fp,class... _FuncArgs,class... _TupleArgs> _Fp operator<( std::pair<_Fp (_Tp::*)(_FuncArgs...),std::tuple<_TupleArgs...>>&& p, atnnn::larrow<_Tp> it) { return apply(std::forward<_Fp (_Tp::*)(_FuncArgs...)>(p.first), std::forward<std::tuple<_TupleArgs...>>(p.second), it); } template<class _Tp> constexpr atnnn::larrow<_Tp> operator-(_Tp& a){return atnnn::larrow<_Tp>(a);}
使ってみる。
#ifdef __GNUC__ struct X{ void f(){std::cout<<__PRETTY_FUNCTION__<<"\n";} std::size_t sum(std::size_t s,std::size_t t) { std::cout<<__PRETTY_FUNCTION__<<"\n"; return s+t; } }; int main() { X x; (&X::f)<-x; std::size_t result=(std::make_pair(&X::sum,std::make_tuple(1,2))<-x); // here } #endif
pairを使って1st argumentにはメンバ関数ポインタ、2nd argumentにはtupleを用いて可変的な引数を表現したつもり。
ただ、見た目的にはなんというか、とても直感的とは言えないので、なんだか微妙。
tuple展開が楽しかった。
#追記
std::applyが標準ライブラリ(experimental)内に実装されている事を知り、そちらも使って書いてみた。 (tuple展開も容易になった時代 - Rokiのチラ裏から丸コピ。)
#include<map> #include<experimental/tuple> template<class _Tp> struct larrow{ larrow(_Tp* a_):a(a_){} _Tp* a; }; template<class _Tp,class R> constexpr R operator<(R (R::* f)(),larrow<_Tp> it) { return (it.a->*f)(); } template<class _Tp,class R> constexpr R operator<(R (R::* f)()const,larrow<_Tp> it) { return (it.a->*f)(); } template<class _Tp,class R,class... FuncArgs,class... Tuple_Args> R operator<(std::pair<R (_Tp::*)(FuncArgs...),std::tuple<Tuple_Args...>> pr,larrow<_Tp> it) { return std::experimental::apply(pr.first,std::tuple_cat(std::make_tuple(it.a),pr.second)); } template<class _Tp> larrow<_Tp> operator-(_Tp& a){return larrow<_Tp>(&a);}
かなり短く済む。