Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

safe access method #420

Merged
merged 1 commit into from Sep 18, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 6 additions & 1 deletion CMakeLists.txt
Expand Up @@ -76,6 +76,7 @@ set(XTENSOR_HEADERS
)

OPTION(XTENSOR_ENABLE_ASSERT "xtensor bound check" OFF)
OPTION(XTENSOR_CHECK_DIMENSION "xtensor dimension check" OFF)
OPTION(BUILD_TESTS "xtensor test suite" OFF)
OPTION(BUILD_BENCHMARK "xtensor benchmark" OFF)
OPTION(DOWNLOAD_GTEST "build gtest from downloaded sources" OFF)
Expand All @@ -85,10 +86,14 @@ if(DOWNLOAD_GTEST OR GTEST_SRC_DIR)
set(BUILD_TESTS ON)
endif()

if(XTENSOR_ENABLE_ASSERT)
if(XTENSOR_ENABLE_ASSERT OR XTENSOR_CHECK_DIMENSION)
add_definitions(-DXTENSOR_ENABLE_ASSERT)
endif()

if(XTENSOR_CHECK_DIMENSION)
add_definitions(-DXTENSOR_ENABLE_CHECK_DIMENSION)
endif()

if(DEFAULT_COLUMN_MAJOR)
add_definitions(-DDEFAULT_LAYOUT=layout_type::column_major)
endif()
Expand Down
2 changes: 2 additions & 0 deletions docs/source/build-options.rst
Expand Up @@ -16,6 +16,8 @@ Build
- ``DOWNLOAD_GTEST``: downloads ``gtest`` and builds it locally instead of using a binary installation.
- ``GTEST_SRC_DIR``: indicates where to find the ``gtest`` sources instead of downloading them.
- ``XTENSOR_ENABLE_ASSERT``: activates the assertions in ``xtensor``.
- ``XTENSOR_CHECK_DIMENSION``: turns on ``XTENSOR_ENABLE_ASSERT`` and activates dimensions check in ``xtensor``.
Note that the dimensions check should not be activated if you expect ``operator()`` to perform broadcasting.

All these options are disabled by default. Enabling ``DOWNLOAD_GTEST`` or setting ``GTEST_SRC_DIR``
enables ``BUILD_TESTS``.
Expand Down
3 changes: 2 additions & 1 deletion docs/source/expression.rst
Expand Up @@ -142,7 +142,8 @@ Shape
Element access
~~~~~~~~~~~~~~

- ``operator()`` is an access operator which can take multiple integral arguments of none.
- ``operator()`` is an access operator which can take multiple integral arguments or none.
- ``at()`` is similar to ``operator()`` but checks that its number of arguments does not exceed the number of dimensions, and performs bounds check. This should not be used where you expect ``operator()`` to perform broadcasting.
- ``operator[]`` has two overloads: one that takes a single integral argument and is equivalent to the call of ``operator()`` with one argument, and one with a single multi-index argument, which can be of size determined at runtime. This operator also supports braced initializer arguments.
- ``element()`` is an access operator which takes a pair of iterators on a container of indices.

Expand Down
21 changes: 21 additions & 0 deletions include/xtensor/xbroadcast.hpp
Expand Up @@ -107,6 +107,10 @@ namespace xt

template <class... Args>
const_reference operator()(Args... args) const;

template <class... Args>
const_reference at(Args... args) const;

const_reference operator[](const xindex& index) const;
const_reference operator[](size_type i) const;

Expand Down Expand Up @@ -250,6 +254,23 @@ namespace xt
return detail::get_element(m_e, args...);
}

/**
* Returns a constant reference to the element at the specified position in the expression,
* after dimension and bounds checking.
* @param args a list of indices specifying the position in the function. Indices
* must be unsigned integers, the number of indices should be equal to the number of dimensions
* of the expression.
* @exception std::out_of_range if the number of argument is greater than the number of dimensions
* or if indices are out of bounds.
*/
template <class CT, class X>
template <class... Args>
inline auto xbroadcast<CT, X>::at(Args... args) const -> const_reference
{
check_access(shape(), args...);
return this->operator()(args...);
}

/**
* Returns a constant reference to the element at the specified position in the expression.
* @param index a sequence of indices specifying the position in the function. Indices
Expand Down
42 changes: 42 additions & 0 deletions include/xtensor/xcontainer.hpp
Expand Up @@ -91,6 +91,12 @@ namespace xt
template <class... Args>
const_reference operator()(Args... args) const;

template <class... Args>
reference at(Args... args);

template <class... Args>
const_reference at(Args... args) const;

reference operator[](const xindex& index);
reference operator[](size_type i);
const_reference operator[](const xindex& index) const;
Expand Down Expand Up @@ -470,6 +476,7 @@ namespace xt
inline auto xcontainer<D>::operator()(Args... args) -> reference
{
XTENSOR_ASSERT(check_index(shape(), args...));
XTENSOR_CHECK_DIMENSION(shape(), args...);
size_type index = data_offset<size_type>(strides(), static_cast<size_type>(args)...);
return data()[index];
}
Expand All @@ -485,10 +492,45 @@ namespace xt
inline auto xcontainer<D>::operator()(Args... args) const -> const_reference
{
XTENSOR_ASSERT(check_index(shape(), args...));
XTENSOR_CHECK_DIMENSION(shape(), args...);
size_type index = data_offset<size_type>(strides(), static_cast<size_type>(args)...);
return data()[index];
}

/**
* Returns a reference to the element at the specified position in the expression,
* after dimension and bounds checking.
* @param args a list of indices specifying the position in the function. Indices
* must be unsigned integers, the number of indices should be equal to the number of dimensions
* of the expression.
* @exception std::out_of_range if the number of argument is greater than the number of dimensions
* or if indices are out of bounds.
*/
template <class D>
template <class... Args>
inline auto xcontainer<D>::at(Args... args) -> reference
{
check_access(shape(), args...);
return this->operator()(args...);
}

/**
* Returns a constant reference to the element at the specified position in the expression,
* after dimension and bounds checking.
* @param args a list of indices specifying the position in the function. Indices
* must be unsigned integers, the number of indices should be equal to the number of dimensions
* of the expression.
* @exception std::out_of_range if the number of argument is greater than the number of dimensions
* or if indices are out of bounds.
*/
template <class D>
template <class... Args>
inline auto xcontainer<D>::at(Args... args) const -> const_reference
{
check_access(shape(), args...);
return this->operator()(args...);
}

/**
* Returns a reference to the element at the specified position in the container.
* @param index a sequence of indices specifying the position in the container. Indices
Expand Down
43 changes: 34 additions & 9 deletions include/xtensor/xexception.hpp
Expand Up @@ -151,14 +151,39 @@ namespace xt
}
}

/*******************
* check_dimension *
*******************/

template <class S, class... Args>
inline void check_dimension(const S& shape, Args... args)
{
if (sizeof...(Args) > shape.size())
{
throw std::out_of_range("Number of arguments (" + std::to_string(sizeof...(Args)) + ") us greater "
+ "than the number of dimensions (" + std::to_string(shape.size()) + ")");
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should check_access and check_dimension be in xstrides?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure we want to check the dimension at all, since we have a clear rule about what to do when too many arguments are provided.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry I missed the comments before I merged


/****************
* check_access *
****************/

template <class S, class... Args>
inline void check_access(const S& shape, Args... args)
{
check_dimension(shape, args...);
check_index(shape, args...);
}

#ifdef XTENSOR_ENABLE_ASSERT
#define XTENSOR_ASSERT(expr) XTENSOR_ASSERT_IMPL(expr, __FILE__, __LINE__)
#define XTENSOR_ASSERT_IMPL(expr, file, line) \
try \
{ \
expr; \
} \
catch (std::exception & e) \
catch (std::exception& e) \
{ \
throw std::runtime_error(std::string(file) + ':' + std::to_string(line) + ": check failed\n\t" + std::string(e.what())); \
}
Expand All @@ -167,12 +192,15 @@ namespace xt
#endif
}

#ifdef XTENSOR_ENABLE_CHECK_DIMENSION
#define XTENSOR_CHECK_DIMENSION(S, ARGS) XTENSOR_ASSERT(check_dimension(S, ARGS))
#else
#define XTENSOR_CHECK_DIMENSION(S, ARGS)
#endif

#ifdef XTENSOR_ENABLE_ASSERT
#define XTENSOR_ASSERT_MSG(PREDICATE, MESSAGE) \
if ((PREDICATE)) \
{ \
} \
else \
if (!(PREDICATE)) \
{ \
throw std::runtime_error(std::string("Assertion error!\n") + MESSAGE + \
"\n " + __FILE__ + '(' + std::to_string(__LINE__) + ")\n"); \
Expand All @@ -183,10 +211,7 @@ namespace xt
#endif

#define XTENSOR_PRECONDITION(PREDICATE, MESSAGE) \
if ((PREDICATE)) \
{ \
} \
else \
if (!(PREDICATE)) \
{ \
throw std::runtime_error(std::string("Precondition violation!\n") + MESSAGE + \
"\n " + __FILE__ + '(' + std::to_string(__LINE__) + ")\n"); \
Expand Down
20 changes: 20 additions & 0 deletions include/xtensor/xfunction.hpp
Expand Up @@ -182,6 +182,9 @@ namespace xt
template <class... Args>
const_reference operator()(Args... args) const;

template <class... Args>
const_reference at(Args... args) const;

const_reference operator[](const xindex& index) const;
const_reference operator[](size_type i) const;

Expand Down Expand Up @@ -489,6 +492,23 @@ namespace xt
return access_impl(std::make_index_sequence<sizeof...(CT)>(), static_cast<size_type>(args)...);
}

/**
* Returns a constant reference to the element at the specified position in the expression,
* after dimension and bounds checking.
* @param args a list of indices specifying the position in the function. Indices
* must be unsigned integers, the number of indices should be equal to the number of dimensions
* of the expression.
* @exception std::out_of_range if the number of argument is greater than the number of dimensions
* or if indices are out of bounds.
*/
template <class F, class R, class... CT>
template <class... Args>
inline auto xfunction<F, R, CT...>::at(Args... args) const -> const_reference
{
check_access(shape(), args...);
return this->operator()(args...);
}

template <class F, class R, class... CT>
inline auto xfunction<F, R, CT...>::operator[](const xindex& index) const -> const_reference
{
Expand Down
42 changes: 42 additions & 0 deletions include/xtensor/xfunctorview.hpp
Expand Up @@ -162,6 +162,10 @@ namespace xt

template <class... Args>
reference operator()(Args... args);

template <class... Args>
reference at(Args... args);

reference operator[](const xindex& index);
reference operator[](size_type i);

Expand All @@ -170,6 +174,10 @@ namespace xt

template <class... Args>
const_reference operator()(Args... args) const;

template <class... Args>
const_reference at(Args... args) const;

const_reference operator[](const xindex& index) const;
const_reference operator[](size_type i) const;

Expand Down Expand Up @@ -524,6 +532,23 @@ namespace xt
return m_functor(m_e(args...));
}

/**
* Returns a reference to the element at the specified position in the expression,
* after dimension and bounds checking.
* @param args a list of indices specifying the position in the function. Indices
* must be unsigned integers, the number of indices should be equal to the number of dimensions
* of the expression.
* @exception std::out_of_range if the number of argument is greater than the number of dimensions
* or if indices are out of bounds.
*/
template <class F, class CT>
template <class... Args>
inline auto xfunctorview<F, CT>::at(Args... args) -> reference
{
check_access(shape(), args...);
return this->operator()(args...);
}

/**
* Returns a reference to the element at the specified position in the expression.
* @param index a sequence of indices specifying the position in the function. Indices
Expand Down Expand Up @@ -569,6 +594,23 @@ namespace xt
return m_functor(m_e(args...));
}

/**
* Returns a constant reference to the element at the specified position in the expression,
* after dimension and bounds checking.
* @param args a list of indices specifying the position in the function. Indices
* must be unsigned integers, the number of indices should be equal to the number of dimensions
* of the expression.
* @exception std::out_of_range if the number of argument is greater than the number of dimensions
* or if indices are out of bounds.
*/
template <class F, class CT>
template <class... Args>
inline auto xfunctorview<F, CT>::at(Args... args) const -> const_reference
{
check_access(shape(), args...);
return this->operator()(args...);
}

/**
* Returns a constant reference to the element at the specified position in the expression.
* @param index a sequence of indices specifying the position in the function. Indices
Expand Down
19 changes: 19 additions & 0 deletions include/xtensor/xgenerator.hpp
Expand Up @@ -88,6 +88,8 @@ namespace xt

template <class... Args>
const_reference operator()(Args... args) const;
template <class... Args>
const_reference at(Args... args) const;
const_reference operator[](const xindex& index) const;
const_reference operator[](size_type i) const;

Expand Down Expand Up @@ -189,6 +191,23 @@ namespace xt
return m_f(args...);
}

/**
* Returns a constant reference to the element at the specified position in the expression,
* after dimension and bounds checking.
* @param args a list of indices specifying the position in the function. Indices
* must be unsigned integers, the number of indices should be equal to the number of dimensions
* of the expression.
* @exception std::out_of_range if the number of argument is greater than the number of dimensions
* or if indices are out of bounds.
*/
template <class F, class R, class S>
template <class... Args>
inline auto xgenerator<F, R, S>::at(Args... args) const -> const_reference
{
check_access(shape(), args...);
return this->operator()(args...);
}

template <class F, class R, class S>
inline auto xgenerator<F, R, S>::operator[](const xindex& index) const -> const_reference
{
Expand Down
18 changes: 18 additions & 0 deletions include/xtensor/xreducer.hpp
Expand Up @@ -124,6 +124,8 @@ namespace xt

template <class... Args>
const_reference operator()(Args... args) const;
template <class... Args>
const_reference at(Args... args) const;
const_reference operator[](const xindex& index) const;
const_reference operator[](size_type i) const;

Expand Down Expand Up @@ -398,6 +400,22 @@ namespace xt
return element(arg_array.cbegin(), arg_array.cend());
}

/**
* Returns a constant reference to the element at the specified position in the expression,
* after dimension and bounds checking.
* @param args a list of indices specifying the position in the function. Indices
* must be unsigned integers, the number of indices should be equal to the number of dimensions
* of the expression.
* @exception std::out_of_range if the number of argument is greater than the number of dimensions
* or if indices are out of bounds.
*/
template <class F, class CT, class X>
template <class... Args>
inline auto xreducer<F, CT, X>::at(Args... args) const -> const_reference
{
check_access(shape(), args...);
return this->operator()(args...);
}
/**
* Returns a constant reference to the element at the specified position in the reducer.
* @param index a sequence of indices specifying the position in the reducer. Indices
Expand Down