xtensor-stack / xtensor-r

R bindings for xtensor
BSD 3-Clause "New" or "Revised" License
86 stars 15 forks source link

Optional support #71

Closed wolfv closed 5 years ago

SylvainCorlay commented 5 years ago

We should be able to merge this one should be ready to go if we require xtensor 0.19.1 and with a test.

wolfv commented 5 years ago

I checked to see how close we are to optional support and I think it would require at least another day of debugging.

Current issues:

This is a xtensor-only reproducer for the printing issue if someone wants to debug it:

template <class XT>
struct rna_proxy
{
    using cpp_type = std::decay_t<XT>;
    // constexpr static int rtype = Rcpp::traits::r_sexptype_traits<cpp_type>::rtype;

    rna_proxy(XT val) : m_val(val) {};

    inline operator bool() const
    {
        return !(m_val == -123);
    }

    inline rna_proxy& operator=(bool val)
    {
        if (val == false)
        {
            m_val = -123;
        }
        else if (m_val == -123)
        {
            m_val = 0;
        }
        return *this;
    }

    XT m_val;
};

template <class XT>
struct xproxy_inner_types<rna_proxy<XT>>
{
    using proxy = rna_proxy<XT>;
    using reference = rna_proxy<XT>;
    using pointer = rna_proxy<XT>;
};

template <class T>
struct rna_proxy_functor
{
    using underlying_type = std::decay_t<T>;

    template <class xunderlying_type, class requested_type>
    using simd_return_type = xsimd::simd_return_type<xunderlying_type, requested_type>;

    using cpp_type = std::decay_t<T>;

    using value_type = bool;
    using reference = rna_proxy<underlying_type&>;
    using const_reference = rna_proxy<const underlying_type&>;
    using pointer = rna_proxy<underlying_type*>;
    using const_pointer = rna_proxy<const underlying_type*>;

    reference operator()(T& val) {
        return val;
    }

    const_reference operator()(const T& val) const {
        return val;
    }

    // template <class align, class requested_type, std::size_t N, class E>
    // auto proxy_simd_load(const E& expr, std::size_t n) const
    // {
    //     using simd_value_type = typename E::simd_value_type;
    //     return expr.template load_simd<align, requested_type, N>(n) == simd_value_type(NA_INTEGER);
    // }

    // template <class align, class simd, class E>
    // auto proxy_simd_store(E& expr, std::size_t n, const simd& batch) const
    // {
    //     using simd_value_type = typename E::simd_value_type;
    //     return expr.template store_simd<align>(n, xsimd::select(batch, simd_value_type(0), simd_value_type(NA_INTEGER)));
    // }
};

    template <class T>
    class roptional_assembly;

    template <class T>
    struct xcontainer_inner_types<roptional_assembly<T>>
    {
        using raw_value_expression = xarray<T, layout_type::column_major>;
        using value_storage_type = typename raw_value_expression::storage_type&;
        using raw_flag_expression = xfunctor_adaptor<rna_proxy_functor<T>, xarray<T, layout_type::column_major>&>;
        using flag_storage_type = xfunctor_adaptor<rna_proxy_functor<T>, xarray<T, layout_type::column_major>&>;
        using storage_type = xoptional_assembly_storage<value_storage_type&, flag_storage_type&>;
        using temporary_type = roptional_assembly<T>;
    };

    template <class T>
    struct xiterable_inner_types<roptional_assembly<T>>
    {
        using assembly_type = roptional_assembly<T>;
        using inner_shape_type = typename xarray<T, layout_type::column_major>::inner_shape_type;
        using stepper = xoptional_assembly_stepper<assembly_type, false>;
        using const_stepper = xoptional_assembly_stepper<assembly_type, true>;
    };

    template <class T>
    class roptional_assembly : public xoptional_assembly_base<roptional_assembly<T>>,
                               public xcontainer_semantic<roptional_assembly<T>>
    {
    public:
        using base_type = xoptional_assembly_base<roptional_assembly<T>>;
        using storage_type = typename base_type::storage_type;
        using assembly_type = roptional_assembly<T>;

        // using base_type::base_type;
        // roptional_assembly() : m_value(), m_flag(m_value) {};
        roptional_assembly(xarray<T>&& rhs) : m_value(std::move(rhs)), m_flag(m_value), m_storage_proxy(m_value.storage(), m_flag)
        {
        }

        using base_type::resize;
        using base_type::reshape;

    protected:

        auto& value_impl() {
            return m_value;
        }

        const auto& value_impl() const {
            return m_value;
        }

        auto& has_value_impl() {
            return m_flag;
        }

        const auto& has_value_impl() const {
            return m_flag;
        }

        auto& storage_impl() {
            return m_storage_proxy;
        }

        const auto& storage_impl() const {
            return m_storage_proxy;
        }

    private:
        xarray<T, layout_type::column_major> m_value;
        xfunctor_adaptor<rna_proxy_functor<T>, xarray<T, layout_type::column_major>&> m_flag;
        storage_type m_storage_proxy;

        friend xoptional_assembly_base<roptional_assembly<T>>;
    };

int main()
{
    using d_opt_ass_type = roptional_assembly<int>;
    xarray<int, layout_type::column_major> aopt = {{1, -123}, {3, 4}};
    d_opt_ass_type xopti(std::move(aopt));
    // {{1,2}, {xtl::missing<int>(), 4}}));

    std::cout << xopti << std::endl;

}
SylvainCorlay commented 5 years ago

We should probably rename the current roptional_assembly to rarray_optional and also add a rtensor_optional.