xtensor-stack / xtensor

C++ tensors with broadcasting and lazy computing
BSD 3-Clause "New" or "Revised" License
3.37k stars 399 forks source link

Example code from the page "Extending xtensor" gives "incomplete type"? #2794

Open amwink opened 5 months ago

amwink commented 5 months ago

I'm trying to use xtensor for defining an image class with extra header information. The example Structures that embed shape and strides from the documentation page "Extending xtensor" comes very close to what I'm after. What I'm trying to do is have a class that also includes header information for loading NIfTI images (and provides file I/O for those).

Putting the code together in a program I had to change a few lines to reduce errors (for example, I could not find the struct m_raw which was used in the example code and add a few forward class template declarations) but I got as far as compiling main().

However, now the compiler gives the error

/usr/bin/g++-12  -c  "test-xtensor/main.cpp" -Wall  -std=gnu++20  -gdwarf-2 -O0 -Wall  -o ../build-Debug/test-xtensor/main.cpp.o -Iquantstack/include -I.
In file included from quantstack/include/xtensor/xcontainer.hpp:23,
             from quantstack/include/xtensor/xarray.hpp:20,
             from quantstack/include/xtensor/xadapt.hpp:20,
             from test-xtensor/main.cpp:1:
quantstack/include/xtensor/xiterable.hpp: In instantiation of 'class xt::xconst_iterable<raw_tensor_adaptor<double> >':
bis/quantstack/include/xtensor/xiterable.hpp:151:11:   required from 'class xt::xiterable<raw_tensor_adaptor<double> >'
bis/quantstack/include/xtensor/xiterable.hpp:311:11:   required from 'class xt::xcontiguous_iterable<raw_tensor_adaptor<double> >'
bis/quantstack/include/xtensor/xcontainer.hpp:71:11:   required from 'class xt::xcontainer<raw_tensor_adaptor<double> >'
test-xtensor/main.cpp:41:7:   required from 'class raw_tensor_adaptor<double>'
test-xtensor/main.cpp:82:14:   required from here
quantstack/include/xtensor/xiterable.hpp:43:15: error: invalid use of incomplete type 'struct xt::xiterable_inner_types<raw_tensor_adaptor<double> >'
   43 |         using inner_shape_type = typename iterable_types::inner_shape_type;
  |               ^~~~~~~~~~~~~~~~
quantstack/include/xtensor/xiterable.hpp:23:12: note: declaration of 'struct xt::xiterable_inner_types<raw_tensor_adaptor<double> >'
   23 |     struct xiterable_inner_types;
      |            ^~~~~~~~~~~~~~~~~~~~~

In the listing below

From the error it is not clear to me if the error is with the arguments to the call or the template parameters of the class? The call seems more likely as the class was mainly copied, but as mentioned I had to change some things to remove errors messages.

There are many similar errors (incomplete type) all referring to xtensor/xiterable.hpp and xtensor/xcontainer.hpp.

If anyone has built this example I would love to hear what the missing link is! Sorry that my 'minimal example' is so long.

code:

#include <xtensor/xadapt.hpp>
#include <xtensor/xstrides.hpp>

template <class T>
struct raw_tensor {
    using container_type = std::vector<T>;
    using shape_type = std::vector<std::size_t>;
    container_type m_data;
    shape_type m_shape;
    shape_type m_strides;
    shape_type m_backstrides;
    static constexpr xt::layout_type layout = xt::layout_type::dynamic;
};

template <class T>
class raw_tensor_adaptor;

template <class T>
struct xcontainer_inner_types;

template <class T>
struct xcontainer_inner_types<raw_tensor_adaptor<T>> {
    using container_type = typename raw_tensor<T>::container_type;
    using inner_shape_type = typename raw_tensor<T>::shape_type;
    using inner_strides_type = inner_shape_type;
    using inner_backstrides_type = inner_shape_type;
    using shape_type = inner_shape_type;
    using strides_type = inner_shape_type;
    using backstrides_type = inner_shape_type;
    static constexpr xt::layout_type layout = raw_tensor<T>::layout;
};

template <class T>
struct xiterable_inner_types;

template <class T>
struct xiterable_inner_types<raw_tensor_adaptor<T>>
    : xt::xcontainer_iterable_types<raw_tensor_adaptor<T>> { };

template <class T>
class raw_tensor_adaptor : public xt::xcontainer<raw_tensor_adaptor<T>>,
                           public xt::xcontainer_semantic<raw_tensor_adaptor<T>> {

friend class base_type;

    public:
    using reference = T&;
    using const_reference = const T&;
    using self_type = raw_tensor_adaptor<T>;
    using base_type = xt::xcontainer<self_type>;
    using semantic_base = xt::xcontainer_semantic<self_type>;

    raw_tensor_adaptor(const raw_tensor_adaptor&) = default;
    raw_tensor_adaptor& operator=(const raw_tensor_adaptor&) = default;

    raw_tensor_adaptor(raw_tensor_adaptor&&) = default;
    raw_tensor_adaptor& operator=(raw_tensor_adaptor&&) = default;

    template <class E> raw_tensor_adaptor (const xt::xexpression<E>& e): base_type() { semantic_base::assign(e); }
    template <class E> self_type& operator=(const xt::xexpression<E>& e) {   return semantic_base::operator=(e); }

    template <class... Args>
    reference operator()(Args... args) { /* Should forward to table<T>:operator()(args...) */ }

    template <class... Args>
    const_reference operator()(Args... args) const { /* Should forward to table<T>::operator()(args...) */ }

    reference operator[](const xt::xindex& index) { return element(index.cbegin(), index.cend()); }
    const_reference operator[](const xt::xindex& index) const { return element(index.cbegin(), index.cend()); }

    reference operator[](size_t i) { return operator()(i); }
    const_reference operator[](size_t i) const { return operator()(i); }

    template <class It> 
    reference element(It first, It last) { /* Should forward to table<T>::element(first, last) */ }
    template <class It> 
    const_reference element(It first, It last) { /* Should forward to table<T>::element(first, last) */ }

protected:
    base_type::inner_shape_type& shape_impl() { return base_type::m_shape; };
    const base_type::inner_shape_type& shape_impl() const { return base_type::m_shape; };

    base_type::inner_strides_type& strides_impl() { return base_type::m_strides; };
    const base_type::inner_strides_type& strides_impl() const  { return base_type::m_strides; };

    base_type::inner_backstrides_type& backstrides_impl() { return base_type::m_backstrides; };
    const base_type::inner_backstrides_type& backstrides_impl() const { return base_type::m_backstrides; };

    public:
    void resize(const base_type::shape_type& shape) {
        if(base_type::m_shape != shape)
            resize(shape, xt::layout_type::row_major);
    }

    void resize(const base_type::shape_type& shape, xt::layout_type l) {
        base_type::m_shape = shape;
        base_type::m_strides.resize(shape.size());
        base_type::m_backstrides.resize(shape.size());
        auto data_size = compute_strides(base_type::m_shape, l, base_type::m_strides, base_type::m_backstrides);
        base_type::m_data.resize(data_size);
    }

    void resize(const base_type::shape_type& shape, const base_type::strides_type& strides) {
        base_type::m_shape = shape;
        base_type::m_strides = strides;
        base_type::m_backstrides.resize(shape.size());
        adapt_strides(base_type::m_shape, base_type::m_strides, base_type::m_backstrides);
        base_type::m_data.resize(compute_size(base_type::m_shape));
    }   
};

int main() {
    using tensor_type = raw_tensor_adaptor<double>;
    auto v = std::vector<double> ( { 1, 2, 3, 4, 5 } );
    tensor_type b (v), c(v), d(v);
    tensor_type e = b + c - d;

    return 0;   
}
amwink commented 4 months ago

So if I use all the code minus the resize() methods, for the struct that has the std::vector<T> and strides, and in main() declare a raw_tensor < int >, the example compiles without errors.

Puzzle 1: However, when I add the resize() functions to the raw_tensor_adaptor class (without the template parameter), I get errors that shape_type, m_shape etc. are all unknown. But surely these resize() functions need to be declared inside the class (they cannot be in the struct with the types)?

Puzzle 2: And in the main function, while the line raw_tensor <int> a; compiles,

if I add the line raw_tensor_adaptor <int> b;

I get the errors:

include/xtensor/xiterable.hpp:288:19: error: no type named 'xexpression_type' in 'struct 
include/xtensor/xaccessible.hpp:35:15: error: no type named 'reference' in 'struct 
include/xtensor/xaccessible.hpp:36:15: error: no type named 'const_reference' in 'struct 
include/xtensor/xaccessible.hpp:37:15: error: no type named 'size_type' in 'struct 
include/xtensor/xaccessible.hpp:47:48: error: no type named 'const_reference' in 'struct 
include/xtensor/xaccessible.hpp:111:26: error: 'at' has not been declared in 'using base_type = class 
include/xtensor/xaccessible.hpp:112:35: error: 'operator[]' has not been declared in 'using base_type = class 
include/xtensor/xaccessible.hpp:113:26: error: 'back' has not been declared in 'using base_type = class 
include/xtensor/xaccessible.hpp:114:26: error: 'front' has not been declared in 'using base_type = class 
include/xtensor/xaccessible.hpp:115:26: error: 'periodic' has not been declared in 'using base_type = class 
include/xtensor/xcontainer.hpp:79:15: error: no type named 'storage_type' in 'struct 
include/xtensor/xcontainer.hpp:80:15: error: no type named 'storage_type' in 'struct 
include/xtensor/xcontainer.hpp:82:15: error: no type named 'reference' in 'struct 
include/xtensor/xcontainer.hpp:83:15: error: no type named 'const_reference' in 'struct 
include/xtensor/xcontainer.hpp:86:15: error: no type named 'size_type' in 'struct 
include/xtensor/xcontainer.hpp:88:15: error: no type named 'storage_type' in 'struct 
include/xtensor/xcontainer.hpp:89:15: error: no type named 'storage_type' in 'struct 
include/xtensor/xcontainer.hpp:107:15: error: no type named 'storage_type' in 'struct 
include/xtensor/xcontainer.hpp:108:15: error: no type named 'storage_type' in 'struct 
include/xtensor/xcontainer.hpp:140:32: error: 'at' has not been declared in 'using accessible_base = class 
include/xtensor/xcontainer.hpp:141:32: error: 'shape' has not been declared in 'using accessible_base = class 
include/xtensor/xcontainer.hpp:142:41: error: 'operator[]' has not been declared in 'using accessible_base = class 
include/xtensor/xcontainer.hpp:143:32: error: 'back' has not been declared in 'using accessible_base = class 
include/xtensor/xcontainer.hpp:144:32: error: 'front' has not been declared in 'using accessible_base = class 
include/xtensor/xcontainer.hpp:146:32: error: 'periodic' has not been declared in 'using accessible_base = class 
include/xtensor/xcontainer.hpp:182:15: error: no type named 'storage_type' in 'struct 
include/xtensor/xsemantic.hpp:64:15: error: no type named 'temporary_type' in 'struct 

And finally,

test-xtensor/main.cpp:74:36: error: no matching function for call to 'raw_tensor_adaptor<int>::raw_tensor_adaptor()'
   74 |         raw_tensor_adaptor < int > b;
      |                                    ^
test-xtensor/main.cpp:60:5: note: candidate: 'template<class E> raw_tensor_adaptor<T>::raw_tensor_adaptor(const xt::xexpression<E>&) [with T = int]'
   60 |     raw_tensor_adaptor(const xt::xexpression<E>& e) : base_type() {
      |     ^~~~~~~~~~~~~~~~~~
test-xtensor/main.cpp:60:5: note:   template argument deduction/substitution failed:
test-xtensor/main.cpp:74:36: note:   candidate expects 1 argument, 0 provided
   74 |         raw_tensor_adaptor < int > b;
      |                                    ^
test-xtensor/main.cpp:56:5: note: candidate: 'raw_tensor_adaptor<T>::raw_tensor_adaptor(raw_tensor_adaptor<T>&&) [with T = int]'
   56 |     raw_tensor_adaptor(raw_tensor_adaptor&&) = default;
      |     ^~~~~~~~~~~~~~~~~~
test-xtensor/main.cpp:56:5: note:   candidate expects 1 argument, 0 provided
test-xtensor/main.cpp:53:5: note: candidate: 'raw_tensor_adaptor<T>::raw_tensor_adaptor(const raw_tensor_adaptor<T>&) [with T = int]'
   53 |     raw_tensor_adaptor(const raw_tensor_adaptor&) = default;
      |     ^~~~~~~~~~~~~~~~~~
test-xtensor/main.cpp:53:5: note:   candidate expects 1 argument, 0 provided

I tried initialising b with a value, a vector or (of course) the already-declared a, but the errors remain the same.

The example tells me to define the semantics and implement the methods and functions that are specific to the raw_tensor class. But I have the feeling that I'm doing that in the wrong place.

Has anyone got this example to work with an instance of raw_tensor_adaptor? Many thanks!

I've pasted the code here as .cpp attachments are not possible:

#include <xtensor/xadapt.hpp>
#include <xtensor/xstrides.hpp>

// example from https://github.com/xtensor-stack/xtensor/blob/master/docs/source/external-structures.rst

template <class T>
struct raw_tensor {
    using container_type = std::vector<T>;
    using shape_type = std::vector<std::size_t>;
    container_type 
        m_data;
    shape_type 
        m_shape,
        m_strides,
        m_backstrides;
    static constexpr xt::layout_type 
        layout = xt::layout_type::dynamic;
};

// This is the adaptor we need to define to plug raw_tensor in xtensor
template <class T>
class raw_tensor_adaptor;

template <class T>
struct xt::xcontainer_inner_types<raw_tensor_adaptor<T>> {
    using container_type = typename raw_tensor<T>::container_type;
    using inner_shape_type = typename raw_tensor<T>::shape_type;
    using inner_strides_type = inner_shape_type;
    using inner_backstrides_type = inner_shape_type;
    using shape_type = inner_shape_type;
    using strides_type = inner_shape_type;
    using backstrides_type = inner_shape_type;
    static constexpr layout_type layout = raw_tensor<T>::layout;
};

template <class T>
struct xt::xiterable_inner_types<raw_tensor_adaptor<T>>
    : xcontainer_iterable_types<raw_tensor_adaptor<T>> {
};

template <class T>
class raw_tensor_adaptor : public xt::xcontainer<raw_tensor_adaptor<T>>,
                           public xt::xcontainer_semantic<raw_tensor_adaptor<T>> {

public:

    using self_type = raw_tensor_adaptor<T>;
    using base_type = xt::xcontainer<self_type>;
    using semantic_base = xt::xcontainer_semantic<self_type>;

    // ... specific constructors here

    raw_tensor_adaptor(const raw_tensor_adaptor&) = default;
    raw_tensor_adaptor& operator=(const raw_tensor_adaptor&) = default;

    raw_tensor_adaptor(raw_tensor_adaptor&&) = default;
    raw_tensor_adaptor& operator=(raw_tensor_adaptor&&) = default;

    template <class E>
    raw_tensor_adaptor(const xt::xexpression<E>& e) : base_type() {
        semantic_base::assign(e);
    }

    template <class E>
    self_type& operator=(const xt::xexpression<E>& e) {
        return semantic_base::operator=(e);
    }   

};

int main() {

    raw_tensor < int > a;
    // raw_tensor_adaptor < int > b ( a );

    return 0;

}