Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions include/wtf/buffer/detail_/buffer_holder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,14 @@ class BufferHolder {
/// Type of a pointer to *this
using holder_pointer = std::unique_ptr<holder_type>;

/// Type of an element of *this
using value_type = fp::Float;

/// Type of a view to an element of *this
using view_type = fp::FloatView<fp::Float>;
using view_type = fp::FloatView<value_type>;

/// Type of a const view to an element of *this
using const_view_type = fp::FloatView<const fp::Float>;
using const_view_type = fp::FloatView<const value_type>;

/// Type used to describe the RTTI of the held buffer
using rtti_type = rtti::TypeInfo;
Expand All @@ -52,13 +55,13 @@ class BufferHolder {
using size_type = std::size_t;

/// Type of a view that would act like *this
using buffer_view_holder = BufferViewHolder<fp::Float>;
using buffer_view_holder = BufferViewHolder<value_type>;

/// Type of a pointer to a buffer_view_holder object
using buffer_view_holder_pointer = std::unique_ptr<buffer_view_holder>;

/// Type of a view that would act like a const version of *this
using const_buffer_view_holder = BufferViewHolder<const fp::Float>;
using const_buffer_view_holder = BufferViewHolder<const value_type>;

/// Type of a pointer to a const_buffer_view_holder object
using const_buffer_view_holder_pointer =
Expand Down
87 changes: 85 additions & 2 deletions include/wtf/buffer/float_buffer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,14 @@ class FloatBuffer {
/// Pull in types from the holder_type
///@{
using holder_pointer = typename holder_type::holder_pointer;
using value_type = typename holder_type::value_type;
using size_type = typename holder_type::size_type;
using view_type = typename holder_type::view_type;
using const_view_type = typename holder_type::const_view_type;
///@}

using buffer_view = BufferView<fp::Float>;
using const_buffer_view = BufferView<const fp::Float>;
using buffer_view = BufferView<value_type>;
using const_buffer_view = BufferView<const value_type>;

// -------------------------------------------------------------------------
// Ctors and assignment operators
Expand All @@ -58,6 +59,24 @@ class FloatBuffer {
*/
FloatBuffer() noexcept = default;

/** @brief Creates a FloatBuffer from a list of specified values.
*
* @tparam T The type of floating-point value being held. Must satisfy the
* concepts::FloatingPoint concept.
*
* This ctor will copy the elements in @p buffer into a std::vector and
* then invoke the std::vector-based ctor to create the FloatBuffer. See
* that ctor for more details.
*
* @param[in] buffer The initial values for the buffer.
*
* @throw std::bad_alloc if creating the held buffer fails. Strong throw
* guarantee.
*/
template<concepts::FloatingPoint T>
explicit FloatBuffer(std::initializer_list<T> buffer) :
FloatBuffer(std::vector<T>(buffer)) {}

/** @brief Creates a FloatBuffer from a std::vector.
*
* @tparam T The type of floating-point value being held. Must satisfy
Expand Down Expand Up @@ -356,6 +375,70 @@ auto make_float_buffer(Args&&... args) {
return FloatBuffer(std::forward<Args>(args)...);
}

/** @brief Wraps the process of making a FloatBuffer from a vector of Float
* objects.
*
* @tparam TupleType A std::tuple of floating-point types to try. Must be
* provided by the caller.
*
* @note Presently this method is restricted to Float objects which all
* contain the same type. Future versions may relax this restriction.
*
* This function creates a FloatBuffer object by unwrapping each of the
* provided Float objects and storing the unwrapped values in a FloatBuffer.
*
* @param[in] buffer The vector of Float objects to make the FloatBuffer from.
*
* @return The FloatBuffer created by unwrapping the Float objects
* in @p buffer.
*
* @throws std::runtime_error if the elements of @p buffer can not all be
* unwrapped to the same type. Strong throw
* guarantee.
*/
template<typename TupleType>
auto make_float_buffer(std::vector<FloatBuffer::value_type> buffer) {
FloatBuffer rv;
if(buffer.size() == 0) return rv;
auto initializer = [&](auto value0) {
using T = std::decay_t<decltype(value0)>;
std::vector<T> temp_buffer(buffer.size());
rv = FloatBuffer(std::move(temp_buffer));
rv.at(0) = value0;
};
fp::visit_float<TupleType>(initializer, buffer[0]);
for(std::size_t i = 1; i < buffer.size(); ++i) {
auto appender = [&](auto value) { rv.at(i) = value; };
fp::visit_float<TupleType>(appender, buffer[i]);
}
return rv;
}

/** @brief Wraps the process of making a FloatBuffer from an initializer list of
* Float objects.
*
* @tparam TupleType A std::tuple of floating-point types to try. Must be
* provided by the caller.
*
* This function uses the initializer list to create a std::vector of Float
* objects and then calls the std::vector-based make_float_buffer function.
* See that function for more details.
*
* @param[in] buffer The initializer list of Float objects to make the
* FloatBuffer from.
*
* @return The FloatBuffer created by unwrapping the Float objects.
*
* @throws std::bad_alloc if creating the std::vector fails. Strong throw
* guarantee.
* @throws ??? if the vector-based overload throws. Same throw guarantee.
*/
template<typename TupleType>
auto make_float_buffer(std::initializer_list<FloatBuffer::value_type> buffer) {
return make_float_buffer<TupleType>(
std::vector<FloatBuffer::value_type>(buffer));
}

template<concepts::FloatingPoint T>
std::span<T> contiguous_buffer_cast(FloatBuffer& buffer) {
if(!buffer.is_contiguous()) {
Expand Down
10 changes: 10 additions & 0 deletions include/wtf/fp/detail_/float_holder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,13 @@ class FloatHolder {
*/
const_type_info_reference type() const { return m_type_; }

/** @brief Is the type of the type-erased object read-only?
*
* @return True if the type of the type-erased object is const-qualified,
* and false otherwise.
*/
bool is_const() const { return is_const_(); }

protected:
/// Initializes *this with the given type info object
explicit FloatHolder(type_info ti) : m_type_(std::move(ti)) {}
Expand All @@ -177,6 +184,9 @@ class FloatHolder {
/// Creates a read-only view of the held floating-point value
virtual const_float_view_type* as_view_() const = 0;

/// True if the held value is const, false otherwise
virtual bool is_const_() const = 0;

/// Base checked that types are equal, derived need only change values
virtual void change_value_(const FloatHolder& other) = 0;

Expand Down
22 changes: 22 additions & 0 deletions include/wtf/fp/detail_/float_model.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,9 @@ class FloatModel : public FloatHolder {
return new const_view_type(data());
}

/// Implements is_const by checking if FloatType is const
bool is_const_() const override { return std::is_const_v<FloatType>; }

/// Implements FloatHolder::change_value by downcasting and calling
/// set_value
void change_value_(const FloatHolder& other) override {
Expand All @@ -204,4 +207,23 @@ class FloatModel : public FloatHolder {
value_type value_;
};

/** @brief Wraps the process of visiting zero or more FloatModel objects via
* FloatHolder references.
*
* @relates FloatModel
*
* @tparam TupleType A tuple of floating-point types to try.
* @tparam Visitor The type of the visitor being invoked.
* @tparam Args The cv-qualified FloatHolder objects to downcast.
*/
template<typename TupleType, typename Visitor, typename... Args>
auto visit_float_model(Visitor&& visitor, Args&&... args) {
auto lambda = [&](auto&&... inner_args) {
return visitor(*inner_args.data()...);
};

return wtf::detail_::dispatch<FloatModel, TupleType>(
lambda, std::forward<Args>(args)...);
}

} // namespace wtf::fp::detail_
31 changes: 31 additions & 0 deletions include/wtf/fp/float.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -202,9 +202,15 @@ class Float {
bool operator!=(const Float& other) const { return !(*this == other); }

private:
template<typename TupleType, typename Visitor, typename... Args>
friend auto visit_float(Visitor&& visitor, Args&&... args);

template<concepts::FloatingPoint T>
friend Float make_float(T value);

auto& holder_() { return *m_holder_; }
const auto& holder_() const { return *m_holder_; }

template<typename T>
requires concepts::UnmodifiedFloatingPoint<std::decay_t<T>>
friend T float_cast(Float& f);
Expand Down Expand Up @@ -328,4 +334,29 @@ IGNORE_DANGLING_REFERENCE T float_cast(Float& f) {
}
return *pderived->data();
}

/** @brief Wraps the process of visiting zero or more Float objects.
*
* @relates Float
*
* @tparam TupleType A std::tuple of floating-point types to try. Must be
* provided by the caller.
* @tparam Visitor The type of the visitor to call. Must be a callable object.
* Will be inferred by the compiler.
* @tparam Args The cv-qualified Float objects to type-restore. Will be
* inferred by the compiler.
*
* The visitor should have the signature: `R(T, U, ...)` where T, U, ... are
* types found in @p TupleType and R is the return type (which may be void).
* T, U, ... may be cv-qualified and may repeat types.
*
* @return The result of invoking @p visitor with the type-restored Float
* objects.
*/
template<typename TupleType, typename Visitor, typename... Args>
auto visit_float(Visitor&& visitor, Args&&... args) {
return visit_float_model<TupleType>(std::forward<Visitor>(visitor),
(args.holder_())...);
};

} // namespace wtf::fp
43 changes: 42 additions & 1 deletion tests/unit_tests/wtf/buffer/float_buffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,14 @@ TEMPLATE_LIST_TEST_CASE("FloatBuffer", "[wtf]", default_fp_types) {
SECTION("ctors and assignment") {
SECTION("default ctor") { REQUIRE(defaulted.size() == 0); }

SECTION("Initializer list of typed floats") {
FloatBuffer buf_from_init_list{one, two, three};
REQUIRE(buf_from_init_list.size() == 3);
REQUIRE(buf_from_init_list.at(0) == one);
REQUIRE(buf_from_init_list.at(1) == two);
REQUIRE(buf_from_init_list.at(2) == three);
}

SECTION("By vector") {
FloatBuffer buf_from_vector(val);
REQUIRE(buf_from_vector.size() == 3);
Expand Down Expand Up @@ -229,7 +237,7 @@ TEMPLATE_LIST_TEST_CASE("FloatBuffer", "[wtf]", default_fp_types) {
}
}

TEMPLATE_LIST_TEST_CASE("make_float_buffer", "[wtf]", all_fp_types) {
TEMPLATE_LIST_TEST_CASE("make_float_buffer(args...)", "[wtf]", all_fp_types) {
using vector_type = std::vector<TestType>;
TestType one{1.0}, two{2.0}, three{3.0};
vector_type val{one, two, three};
Expand All @@ -241,6 +249,39 @@ TEMPLATE_LIST_TEST_CASE("make_float_buffer", "[wtf]", all_fp_types) {
REQUIRE(buffer.at(2) == three);
}

TEST_CASE("make_float_buffer(vector<Float>)") {
float one{1.0};
auto wrap_one = wtf::fp::make_float(one);
double two{2.0};
auto wrap_two = wtf::fp::make_float(two);

FloatBuffer ones_corr(std::vector<float>{one, one, one});
std::vector<wtf::fp::Float> wrapped_ones{wrap_one, wrap_one, wrap_one};
FloatBuffer twos_corr(std::vector<double>{two, two, two});
std::vector<wtf::fp::Float> wrapped_twos{wrap_two, wrap_two, wrap_two};
std::vector<wtf::fp::Float> mixed{wrap_one, wrap_two, wrap_one};

REQUIRE(make_float_buffer<all_fp_types>(wrapped_ones) == ones_corr);
REQUIRE(make_float_buffer<all_fp_types>(wrapped_twos) == twos_corr);
REQUIRE_THROWS_AS(make_float_buffer<all_fp_types>(mixed),
std::runtime_error);
}

TEST_CASE("make_float_buffer(initializer_list<Float>)") {
float one{1.0};
auto wrap_one = wtf::fp::make_float(one);
double two{2.0};
auto wrap_two = wtf::fp::make_float(two);

FloatBuffer ones_corr(std::vector<float>{one, one, one});
FloatBuffer twos_corr(std::vector<double>{two, two, two});

REQUIRE(make_float_buffer<all_fp_types>({one, one, one}) == ones_corr);
REQUIRE(make_float_buffer<all_fp_types>({two, two, two}) == twos_corr);
REQUIRE_THROWS_AS(make_float_buffer<all_fp_types>({one, two, one}),
std::runtime_error);
}

TEMPLATE_LIST_TEST_CASE("contiguous_buffer_cast", "[wtf]", all_fp_types) {
using vector_type = std::vector<TestType>;
TestType one{1.0}, two{2.0}, three{3.0};
Expand Down
15 changes: 15 additions & 0 deletions tests/unit_tests/wtf/fp/detail_/float_model.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ TEMPLATE_LIST_TEST_CASE("FloatModel", "[float_model]", test_wtf::all_fp_types) {
REQUIRE(m.get_value() == new_val);
}

SECTION("is_const") { REQUIRE_FALSE(m.is_const()); }

SECTION("data()") {
auto p = m.data();
REQUIRE(*p == val);
Expand Down Expand Up @@ -117,3 +119,16 @@ TEMPLATE_LIST_TEST_CASE("FloatModel", "[float_model]", test_wtf::all_fp_types) {
REQUIRE_FALSE(m.are_equal(m4));
}
}

TEMPLATE_LIST_TEST_CASE("visit_float_model", "[float_model]",
test_wtf::all_fp_types) {
using float_t = TestType;
using model_t = FloatModel<float_t>;

float_t val = 2.71;
model_t m(val);

auto visitor = [=](auto&& wrapped_value) { REQUIRE(wrapped_value == val); };

visit_float_model<std::tuple<float_t>>(visitor, m);
}
11 changes: 11 additions & 0 deletions tests/unit_tests/wtf/fp/float.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,3 +145,14 @@ TEST_CASE("float_cast", "[wtf]") {

REQUIRE_THROWS_AS(float_cast<double>(f), std::runtime_error);
}

TEMPLATE_LIST_TEST_CASE("visit_float", "[wtf]", test_wtf::all_fp_types) {
using float_t = TestType;

float_t val = 2.71;
Float m(val);

auto visitor = [=](auto&& wrapped_value) { REQUIRE(wrapped_value == val); };

visit_float<std::tuple<float_t>>(visitor, m);
}