Rokiのチラ裏

学生による学習のログ

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);}

かなり短く済む。

参照
http://faithandbrave.hateblo.jp/entry/20131120/1384924475
http://www.atnnn.com/p/operator-larrow/
https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html