Rangeの要素をstatic_castとかdynamic_castする

Rangeの要素の型を変換したいと思ったときに、Ovenのconverted Rangeアダプタだと、implicit_castしかできなくて、static_castとかdynamic_castとかできないので、できたらいいなと思った。
transformedと関数オブジェクトとかboost::lambda::ll_static_castとかでなんやかんやすればできるけど、それを毎回書いていられないので、Rangeアダプタにまとめてしまおうと。

(ただし、egg周りの実装が全然把握できてなくて、converted Rangeアダプタの実装を元にして作りましたけど、なにがどうなっているかちゃんと分かっていません。)

hwm/detail/XXX_cast.hpp

#ifndef HWM_XXX_CAST_TYPE
    #error no cast type specified
#endif

#include <boost/preprocessor/cat.hpp>

#define HWM_DETAIL_XXX_CASTED               BOOST_PP_CAT(HWM_XXX_CAST_TYPE, ed)
#define HWM_DETAIL_XXX_CASTED_DETAIL        BOOST_PP_CAT(HWM_DETAIL_XXX_CASTED, _detail)
#define HWM_DETAIL_MAKE_XXX_CASTED          BOOST_PP_CAT(X_make_, HWM_DETAIL_XXX_CASTED)    
#include <pstade/egg/function.hpp>
#include <pstade/egg/pipable.hpp>
#include <pstade/oven/transformed.hpp>

namespace hwm {

namespace HWM_DETAIL_XXX_CASTED_DETAIL {
    using namespace pstade;
    using namespace pstade::oven;

    template<typename To>
    struct little {

        template<typename CastTo>
        struct cast
        {
            typedef CastTo result_type;
            template<typename T>
            result_type operator()(T const &t) const {
                return HWM_XXX_CAST_TYPE<To>(t);
            }
            
            template<typename T>
            result_type operator()(T &t) const {
                return HWM_XXX_CAST_TYPE<To>(t);
            }
        };

        template<typename Myself, typename Range>
        struct apply :
            result_of<
                X_make_transformed<To>(
                    Range &,
                    cast<To>)
           >    
        {};

        template<typename Result, typename Range>
        Result call(Range &rng) const
        {
            return
                X_make_transformed<To>()(
                    rng,
                    cast<To>() );
        }
    };
}   //namespace HWM_DETAIL_XXX_CASTED_DETAIL

template<typename To>
struct HWM_DETAIL_MAKE_XXX_CASTED :
    pstade::egg::function<HWM_DETAIL_XXX_CASTED_DETAIL::little<To> >
{};

template<typename To>
struct HWM_DETAIL_XXX_CASTED
    :   pstade::egg::result_of_pipable<HWM_DETAIL_MAKE_XXX_CASTED<To> >::type
    ,   pstade::egg::lookup_pipable_operator
{};

}  //namespace hwm

#undef HWM_XXX_CAST_TYPE
#undef HWM_DETAIL_XXX_CASTED        
#undef HWM_DETAIL_XXX_CASTED_DETAIL     
#undef HWM_DETAIL_MAKE_XXX_CASTED

hwm/static_casted.hpp

#ifndef	HWM_STATIC_CASTED_HPP
#define HWM_STATIC_CASTED_HPP

#define HWM_XXX_CAST_TYPE static_cast
#include "./detail/XXX_casted.hpp"

#endif	//HWM_STATIC_CASTED_HPP

hwm/dynamic_casted.hpp
hwm/reinterpret_casted.hpp
hwm/const_casted.hpp
もそんな感じ

hwm/casted.hpp

#ifndef HWM_CASTED_HPP
#define HWM_CASTED_HPP

#include "./static_casted.hpp"
#include "./dynamic_casted.hpp"
#include "./reinterpret_casted.hpp"
#include "./const_casted.hpp"

#endif  //HWM_CASTEAD_HPP

これで、castが出来ます。

#include <iostream>
#include <vector>
#include <pstade/oven/io.hpp>

//インクルードパスとかなんとなく空気読んでくだしあ。
#include <hwm/casted.hpp>

void static_casted_test()
{
    std::vector<int> is;
    for(int i = 0; i < 5; ++i) {
        is.push_back(-i);
    }

    namespace oven = pstade::oven;

    std::cout << (is | hwm::static_casted<unsigned char>() | hwm::static_casted<unsigned int>()) << std::endl;
}

struct X
{
    virtual ~X() {}
    friend std::ostream & operator<< (std::ostream &os, X const &x) {
        return os << "A as X";
    }

protected:
    X() {}
};

struct Y
{
    virtual ~Y() {}
    friend std::ostream & operator<< (std::ostream &os, Y const &y) {
        return os << "A as Y";
    }

protected:
    Y() {}
};

struct Z
{
    virtual ~Z() {}
    friend std::ostream & operator<< (std::ostream &os, Z const &z) {
        return os << "A as Z";
    }

protected:
    Z() {}
};

struct A
:   X, Y, Z
{};

#include <boost/make_shared.hpp>
#include <pstade/oven/constants.hpp>
#include <pstade/oven/indirected.hpp>

void dynamic_casted_test()
{
    std::vector<boost::shared_ptr<X> > xs;
    for(int i = 0; i < 3; ++i) {
        xs.push_back(boost::make_shared<A>());
    }

    namespace oven = pstade::oven;
    std::cout << (xs | oven::indirected | hwm::dynamic_casted<X const &>()) << std::endl;
    std::cout << (xs | oven::indirected | hwm::dynamic_casted<Y &>()) << std::endl;
    std::cout << (xs | oven::indirected | hwm::dynamic_casted<Y &>() | hwm::dynamic_casted<Z &>()) << std::endl;
    //compilation error.(expected)
    //std::cout << (xs | oven::indirected | oven::constants | hwm::dynamic_casted<Y &>()) << std::endl;
    std::cout << (xs | oven::indirected | oven::constants | hwm::dynamic_casted<Y const &>()) << std::endl;
}

#include <pstade/oven/outdirected.hpp>
#include <pstade/oven/pointed.hpp>
#include <pstade/oven/transformed.hpp>

struct to_str {
    typedef std::string result_type;
    result_type operator() (char const *s) const {
        return std::string(s, s+4);
    }
};

void reinterpret_casted_test()
{
    namespace oven = pstade::oven;
    typedef union
    {
        int     magic_;
        char    fourcc_[4];
    } fcc_t;

    std::vector<int> is;

    fcc_t fcc;
    fcc.fourcc_[0] = 'R';
    fcc.fourcc_[1] = 'I';
    fcc.fourcc_[2] = 'F';
    fcc.fourcc_[3] = 'F';

    is.push_back(fcc.magic_);

    fcc.fourcc_[0] = 'W';
    fcc.fourcc_[1] = 'A';
    fcc.fourcc_[2] = 'V';
    fcc.fourcc_[3] = 'E';

    is.push_back(fcc.magic_);

    fcc.fourcc_[0] = 'f';
    fcc.fourcc_[1] = 'm';
    fcc.fourcc_[2] = 't';
    fcc.fourcc_[3] = ' ';

    is.push_back(fcc.magic_);
    
    std::cout << (is | oven::pointed | oven::outdirected | hwm::reinterpret_casted<char const *>() | oven::transformed(to_str())) << std::endl;
}

struct B
{
    friend std::ostream & operator<< (std::ostream &os, B const &b) {
        return os << "const version is called#B";
    }

    friend std::ostream & operator<< (std::ostream &os, B &b) {
        return os << "non-const version is called#B";
    }
};

#include <pstade/oven/identities.hpp>

void const_casted_test()
{
    namespace oven = pstade::oven;
    std::vector<B> const bs(3);

    std::cout << (bs | oven::identities) << std::endl;
    std::cout << (bs | hwm::const_casted<B &>()) << std::endl;
}

int main()
{
    static_casted_test();
    dynamic_casted_test();
    reinterpret_casted_test();
    const_casted_test();
}

出力

{0,255,254,253,252}
{A as X,A as X,A as X}
{A as Y,A as Y,A as Y}
{A as Z,A as Z,A as Z}
{A as Y,A as Y,A as Y}
{RIFF,WAVE,fmt }
{const version is called#B,const version is called#B,const version is called#B}
{non-const version is called#B,non-const version is called#B,non-const version is called#B}

で、とりあえず書いてみましたが、バグがあるかもしれません。
使う人がいたら自己責任ということでよろしくです。