diff --git a/arbor/fvm_lowered_cell.hpp b/arbor/fvm_lowered_cell.hpp index a30e19d2fb08b6b313ca53147161c605d8b3b9c3..cd480f5917e859ec661fcf5fd298e55c74524aac 100644 --- a/arbor/fvm_lowered_cell.hpp +++ b/arbor/fvm_lowered_cell.hpp @@ -4,6 +4,7 @@ #include <memory> #include <type_traits> #include <unordered_map> +#include <variant> #include <vector> #include <arbor/assert.hpp> @@ -13,7 +14,6 @@ #include <arbor/morph/primitives.hpp> #include <arbor/recipe.hpp> #include <arbor/util/any_ptr.hpp> -#include <arbor/util/variant.hpp> #include "backends/event.hpp" #include "backends/threshold_crossing.hpp" @@ -41,10 +41,10 @@ struct fvm_integration_result { struct fvm_probe_scalar { probe_handle raw_handles[1] = {nullptr}; - util::variant<mlocation, cable_probe_point_info> metadata; + std::variant<mlocation, cable_probe_point_info> metadata; util::any_ptr get_metadata_ptr() const { - return util::visit([](const auto& x) -> util::any_ptr { return &x; }, metadata); + return std::visit([](const auto& x) -> util::any_ptr { return &x; }, metadata); } }; @@ -58,15 +58,15 @@ struct fvm_probe_interpolated { struct fvm_probe_multi { std::vector<probe_handle> raw_handles; - util::variant<mcable_list, std::vector<cable_probe_point_info>> metadata; + std::variant<mcable_list, std::vector<cable_probe_point_info>> metadata; void shrink_to_fit() { raw_handles.shrink_to_fit(); - util::visit([](auto& v) { v.shrink_to_fit(); }, metadata); + std::visit([](auto& v) { v.shrink_to_fit(); }, metadata); } util::any_ptr get_metadata_ptr() const { - return util::visit([](const auto& x) -> util::any_ptr { return &x; }, metadata); + return std::visit([](const auto& x) -> util::any_ptr { return &x; }, metadata); } }; @@ -122,7 +122,7 @@ struct fvm_probe_data { fvm_probe_data(fvm_probe_weighted_multi p): info(std::move(p)) {} fvm_probe_data(fvm_probe_membrane_currents p): info(std::move(p)) {} - util::variant< + std::variant< missing_probe_info, fvm_probe_scalar, fvm_probe_interpolated, @@ -133,7 +133,7 @@ struct fvm_probe_data { auto raw_handle_range() const { return util::make_range( - util::visit( + std::visit( [](auto& i) -> std::pair<const probe_handle*, const probe_handle*> { using std::data; using std::size; @@ -143,12 +143,12 @@ struct fvm_probe_data { } util::any_ptr get_metadata_ptr() const { - return util::visit([](const auto& i) -> util::any_ptr { return i.get_metadata_ptr(); }, info); + return std::visit([](const auto& i) -> util::any_ptr { return i.get_metadata_ptr(); }, info); } sample_size_type n_raw() const { return raw_handle_range().size(); } - explicit operator bool() const { return !util::get_if<missing_probe_info>(info); } + explicit operator bool() const { return !std::get_if<missing_probe_info>(&info); } }; // Samplers are tied to probe ids, but one probe id may diff --git a/arbor/fvm_lowered_cell_impl.hpp b/arbor/fvm_lowered_cell_impl.hpp index ebb430a5b79e3b0bababa031ba1e198c9d727f4d..d36c3bd78f3fb6f16c2824d0344fb0f9ef45236e 100644 --- a/arbor/fvm_lowered_cell_impl.hpp +++ b/arbor/fvm_lowered_cell_impl.hpp @@ -20,7 +20,6 @@ #include <arbor/common_types.hpp> #include <arbor/cable_cell_param.hpp> #include <arbor/recipe.hpp> -#include <arbor/util/any.hpp> #include <arbor/util/any_visitor.hpp> #include <arbor/util/optional.hpp> @@ -145,7 +144,7 @@ private: std::vector<fvm_probe_data>& probe_data, // out parameter const std::vector<cable_cell>& cells, std::size_t cell_idx, - const util::any& paddr, + const std::any& paddr, const fvm_cv_discretization& D, const fvm_mechanism_data& M, const std::vector<target_handle>& handles, @@ -356,7 +355,7 @@ void fvm_lowered_cell_impl<Backend>::initialize( std::vector<target_handle>& target_handles, probe_association_map& probe_map) { - using util::any_cast; + using std::any_cast; using util::count_along; using util::make_span; using util::value_by_key; @@ -374,19 +373,19 @@ void fvm_lowered_cell_impl<Backend>::initialize( try { cells[i] = any_cast<cable_cell&&>(rec.get_cell_description(gid)); } - catch (util::bad_any_cast&) { + catch (std::bad_any_cast&) { throw bad_cell_description(rec.get_cell_kind(gid), gid); } }); cable_cell_global_properties global_props; try { - util::any rec_props = rec.get_global_properties(cell_kind::cable); + std::any rec_props = rec.get_global_properties(cell_kind::cable); if (rec_props.has_value()) { global_props = any_cast<cable_cell_global_properties>(rec_props); } } - catch (util::bad_any_cast&) { + catch (std::bad_any_cast&) { throw bad_global_property(cell_kind::cable); } @@ -712,7 +711,7 @@ void fvm_lowered_cell_impl<Backend>::resolve_probe_address( std::vector<fvm_probe_data>& probe_data, const std::vector<cable_cell>& cells, std::size_t cell_idx, - const util::any& paddr, + const std::any& paddr, const fvm_cv_discretization& D, const fvm_mechanism_data& M, const std::vector<target_handle>& handles, diff --git a/arbor/include/arbor/recipe.hpp b/arbor/include/arbor/recipe.hpp index 2d8593f0da85a4d83b398b6434050557fba7c696..3e8d834f9a2f27d82440ce30f580036b2951fcfe 100644 --- a/arbor/include/arbor/recipe.hpp +++ b/arbor/include/arbor/recipe.hpp @@ -1,5 +1,6 @@ #pragma once +#include <any> #include <utility> #include <vector> @@ -13,7 +14,7 @@ struct probe_info { probe_tag tag; // Address type will be specific to cell kind of cell `id.gid`. - util::any address; + std::any address; probe_info(probe_info&) = default; probe_info(const probe_info&) = default; @@ -89,7 +90,7 @@ public: } // Global property type will be specific to given cell kind. - virtual util::any get_global_properties(cell_kind) const { return util::any{}; }; + virtual std::any get_global_properties(cell_kind) const { return std::any{}; }; virtual ~recipe() {} }; diff --git a/arbor/include/arbor/symmetric_recipe.hpp b/arbor/include/arbor/symmetric_recipe.hpp index 917ec892c022d3857e8d05d8dc20bcdc66e54abc..7addcf6fed71434325d763a61c41857a0101428b 100644 --- a/arbor/include/arbor/symmetric_recipe.hpp +++ b/arbor/include/arbor/symmetric_recipe.hpp @@ -1,15 +1,17 @@ #pragma once +#include <any> #include <cstddef> #include <memory> #include <unordered_map> #include <stdexcept> #include <arbor/recipe.hpp> +#include <arbor/util/unique_any.hpp> namespace arb { -// tile inherits from recipe but is not a regular recipe. +// `tile` inherits from recipe but is not a regular recipe. // It is used to describe a recipe on a single rank // that will be translated to all other ranks using symmetric_recipe. // This means it can allow connections from gid >= ncells @@ -19,7 +21,7 @@ public: virtual cell_size_type num_tiles() const { return 1; } }; -// symmetric recipe takes a tile and duplicates it across +// `symmetric_recipe` takes a tile and duplicates it across // as many ranks as tile indicates. Its functions call the // underlying functions of tile and perform transformations // on the results when needed. @@ -74,7 +76,7 @@ public: return tiled_recipe_->get_probes(i); } - util::any get_global_properties(cell_kind ck) const override { + std::any get_global_properties(cell_kind ck) const override { return tiled_recipe_->get_global_properties(ck); }; diff --git a/arbor/include/arbor/util/any.hpp b/arbor/include/arbor/util/any.hpp deleted file mode 100644 index 7198f072d59d7408a50e8fc6bb45bbf6e5d96a66..0000000000000000000000000000000000000000 --- a/arbor/include/arbor/util/any.hpp +++ /dev/null @@ -1,215 +0,0 @@ -#pragma once - -#include <memory> -#include <typeinfo> -#include <type_traits> - -// Partial implementation of std::any from C++17 standard. -// http://en.cppreference.com/w/cpp/utility/any -// -// Implements a standard-compliant subset of the full interface. -// -// - Does not avoid dynamic allocation of small objects. -// - Does not implement the in_place_type<T> constructors from the standard. -// - Does not implement the emplace modifier from the standard. - -namespace arb { -namespace util { - -// Defines a type of object to be thrown by the value-returning forms of -// util::any_cast on failure. -// http://en.cppreference.com/w/cpp/utility/any/bad_any_cast -class bad_any_cast: public std::bad_cast { -public: - const char* what() const noexcept override { - return "bad any cast"; - } -}; - -class any { -public: - constexpr any() = default; - - any(const any& other): state_(other.state_->copy()) {} - - any(any&& other) noexcept { - std::swap(other.state_, state_); - } - - template < - typename T, - typename = std::enable_if_t<!std::is_same<std::decay_t<T>, any>::value> - > - any(T&& other) { - using contained_type = std::decay_t<T>; - static_assert(std::is_copy_constructible<contained_type>::value, - "Type of contained object stored in any must satisfy the CopyConstructible requirements."); - - state_.reset(new model<contained_type>(std::forward<T>(other))); - } - - any& operator=(const any& other) { - state_.reset(other.state_->copy()); - return *this; - } - - any& operator=(any&& other) noexcept { - swap(other); - return *this; - } - - template < - typename T, - typename = std::enable_if_t<!std::is_same<std::decay_t<T>, any>::value> - > - any& operator=(T&& other) { - using contained_type = std::decay_t<T>; - - static_assert(std::is_copy_constructible<contained_type>::value, - "Type of contained object stored in any must satisfy the CopyConstructible requirements."); - - state_.reset(new model<contained_type>(std::forward<T>(other))); - return *this; - } - - void reset() noexcept { - state_.reset(nullptr); - } - - void swap(any& other) noexcept { - std::swap(other.state_, state_); - } - - bool has_value() const noexcept { - return (bool)state_; - } - - const std::type_info& type() const noexcept { - return has_value()? state_->type(): typeid(void); - } - -private: - struct interface { - virtual ~interface() = default; - virtual const std::type_info& type() = 0; - virtual interface* copy() = 0; - virtual void* pointer() = 0; - virtual const void* pointer() const = 0; - }; - - template <typename T> - struct model: public interface { - ~model() = default; - model(const T& other): value(other) {} - model(T&& other): value(std::move(other)) {} - - interface* copy() override { return new model<T>(*this); } - const std::type_info& type() override { return typeid(T); } - void* pointer() override { return &value; } - const void* pointer() const override { return &value; } - - T value; - }; - - std::unique_ptr<interface> state_; - -protected: - template <typename T> - friend const T* any_cast(const any* operand); - - template <typename T> - friend T* any_cast(any* operand); - - template <typename T> - T* unsafe_cast() { - return static_cast<T*>(state_->pointer()); - } - - template <typename T> - const T* unsafe_cast() const { - return static_cast<const T*>(state_->pointer()); - } -}; - -namespace impl { - -template <typename T> -using any_cast_remove_qual = std::remove_cv_t<std::remove_reference_t<T>>; - -} // namespace impl - -// If operand is not a null pointer, and the typeid of the requested T matches -// that of the contents of operand, a pointer to the value contained by operand, -// otherwise a null pointer. -template<class T> -const T* any_cast(const any* operand) { - if (operand && operand->type()==typeid(T)) { - return operand->unsafe_cast<T>(); - } - return nullptr; -} - -template<class T> -T* any_cast(any* operand) { - if (operand && operand->type()==typeid(T)) { - return operand->unsafe_cast<T>(); - } - return nullptr; -} - -template<class T> -T any_cast(const any& operand) { - using U = impl::any_cast_remove_qual<T>; - static_assert(std::is_constructible<T, const U&>::value, - "any_cast type can't construct copy of contained object"); - - auto ptr = any_cast<U>(&operand); - if (ptr==nullptr) { - throw bad_any_cast(); - } - return static_cast<T>(*ptr); -} - -template<class T> -T any_cast(any& operand) { - using U = impl::any_cast_remove_qual<T>; - static_assert(std::is_constructible<T, U&>::value, - "any_cast type can't construct copy of contained object"); - - auto ptr = any_cast<U>(&operand); - if (ptr==nullptr) { - throw bad_any_cast(); - } - return static_cast<T>(*ptr); -} - -template<class T> -T any_cast(any&& operand) { - using U = impl::any_cast_remove_qual<T>; - static_assert(std::is_constructible<T, U>::value, - "any_cast type can't construct copy of contained object"); - - auto ptr = any_cast<U>(&operand); - if (ptr==nullptr) { - throw bad_any_cast(); - } - return static_cast<T>(std::move(*ptr)); -} - -// Constructs an any object containing an object of type T, passing the -// provided arguments to T's constructor. -// -// This does not exactly follow the standard, which states that -// make_any is equivalent to -// return std::any(std::in_place_type<T>, std::forward<Args>(args)...); -// i.e. that the contained object should be constructed in place, whereas -// this implementation constructs the object, then moves it into the -// contained object. -// FIXME: rewrite with in_place_type when available. -template <class T, class... Args> -any make_any(Args&&... args) { - return any(T(std::forward<Args>(args) ...)); -} - -} // namespace util -} // namespace arb diff --git a/arbor/include/arbor/util/any_cast.hpp b/arbor/include/arbor/util/any_cast.hpp new file mode 100644 index 0000000000000000000000000000000000000000..b9fce2b587924f0fd990f070f134d9a95d0b9d96 --- /dev/null +++ b/arbor/include/arbor/util/any_cast.hpp @@ -0,0 +1,29 @@ +#pragma once + +// arb::util::any_cast wraps std::any_cast for std::any objects. +// +// arb::util::any_cast also has specializations for arb::util::unique_any +// and arb::util::any_pointer defined in the corresponding headers. + +#include <any> +#include <type_traits> +#include <utility> + +namespace arb { +namespace util { + +template < + typename T, + typename Any, + typename = std::enable_if_t<std::is_same_v<std::any, std::remove_cv_t<std::remove_reference_t<Any>>>> +> +T any_cast(Any&& a) { return std::any_cast<T>(std::forward<Any>(a)); } + +template <typename T> +const T* any_cast(const std::any* p) noexcept { return std::any_cast<T>(p); } + +template <typename T> +T* any_cast(std::any* p) noexcept { return std::any_cast<T>(p); } + +} // namespace util +} // namespace arb diff --git a/arbor/include/arbor/util/any_ptr.hpp b/arbor/include/arbor/util/any_ptr.hpp index 2587511bd7a3541e14cd2b7555e386f2eb32a4c9..f148c566bbd5455d32dd5158c3a6a7942060d09b 100644 --- a/arbor/include/arbor/util/any_ptr.hpp +++ b/arbor/include/arbor/util/any_ptr.hpp @@ -1,27 +1,35 @@ #pragma once -/* Specialied type erasure for pointer types. - * - * `any_ptr` represents a non-owning pointer to an arbitrary type - * that can be confirmed at run-time. - * - * Semantics: - * - * 1. An `any_ptr` value p represents either a null pointer, or - * a non-null pointer of a specific but arbitrary type T. - * - * 2. The value of the pointer as a `void*` value can be retrieved - * with the member function `as<void*>()`. - * - * 3. The value of the pointer as a type T which is not `void*` is - * retrieved with the member function `as<T>()`. If the represented - * pointer is the null pointer or a pointer to a different type, - * `as<T>()` will return the null pointer. - */ +// Specialied type erasure for pointer types. +// +// `any_ptr` represents a non-owning pointer to an arbitrary type +// that can be confirmed at run-time. +// +// Semantics: +// +// 1. An `any_ptr` value p represents either a null pointer, or +// a non-null pointer of a specific but arbitrary type T. +// +// 2. The value of the pointer as a `void*` value can be retrieved +// with the member function `as<void*>()`. +// +// 3. The value of the pointer as a pointer of type T is retrieved +// with the member function `as<T>()`. +// +// 4. When U is not `void *` and is not the type T, the member function +// will return `nullptr`. +// +// Overloads are provided for `util::any_cast`, with +// `util::any_cast<U>(anyp)` equal to `anyp.as<U>()` for a value +// `anyp` of type `util::any_ptr. +// +// `any_ptr` values are ordered by the value of the corresponding +// `void*` pointer. #include <cstddef> #include <type_traits> +#include <arbor/util/any_cast.hpp> #include <arbor/util/lexcmp_def.hpp> namespace arb { diff --git a/arbor/include/arbor/util/any_visitor.hpp b/arbor/include/arbor/util/any_visitor.hpp index 48e5dfc02c762436362c7a23c0afc48ff07698b1..d579a5ad367390e90c37e972a5603df5520928f5 100644 --- a/arbor/include/arbor/util/any_visitor.hpp +++ b/arbor/include/arbor/util/any_visitor.hpp @@ -4,14 +4,13 @@ // `operator()` from a number of functions or function objects. // // Provides the `any_visitor` class, which will call a provided functional -// with the contained value in a `util::any` if it is one of a fixed set +// with the contained value in a `std::any` if it is one of a fixed set // of types. +#include <any> #include <utility> #include <type_traits> -#include <arbor/util/any.hpp> - namespace arb { namespace util { @@ -35,7 +34,7 @@ using propagate_qualifier_t = typename propagate_qualifier<X, Y>::type; } // namespace impl // A type `any_visitor<A, B, ...>` has one public static method -// `visit(f, a)` where `a` is a possibly const lvalue or rvalue util::any, +// `visit(f, a)` where `a` is a possibly const lvalue or rvalue std::any, // and `f` is a functional object or function pointer. // // If `a` contains a value of any of the types `A, B, ...`, `f` will @@ -55,9 +54,9 @@ struct any_visitor<T> { template <typename A> static auto visit(F&& f, A&& a) { using Q = impl::propagate_qualifier_t<A, T>; - return util::any_cast<T>(&a)? - std::forward<F>(f)(util::any_cast<Q&&>(std::forward<A>(a))): - throw ::arb::util::bad_any_cast(); + return std::any_cast<T>(&a)? + std::forward<F>(f)(std::any_cast<Q&&>(std::forward<A>(a))): + throw std::bad_any_cast(); } }; @@ -66,14 +65,14 @@ struct any_visitor<T> { template <typename A> static auto visit(F&& f, A&& a) { using Q = impl::propagate_qualifier_t<A, T>; - return util::any_cast<T>(&a)? - std::forward<F>(f)(util::any_cast<Q&&>(std::forward<A>(a))): + return std::any_cast<T>(&a)? + std::forward<F>(f)(std::any_cast<Q&&>(std::forward<A>(a))): std::forward<F>(f)(); } }; template <typename F, typename A, - typename = std::enable_if_t<std::is_same<util::any, std::decay_t<A>>::value> + typename = std::enable_if_t<std::is_same_v<std::any, std::decay_t<A>>> > static auto visit(F&& f, A&& a) { return invoke_or_throw<F>::visit(std::forward<F>(f), std::forward<A>(a)); @@ -83,32 +82,18 @@ struct any_visitor<T> { template <typename T, typename U, typename... Rest> struct any_visitor<T, U, Rest...> { template <typename F, typename A, - typename = std::enable_if_t<std::is_same<util::any, std::decay_t<A>>::value> + typename = std::enable_if_t<std::is_same_v<std::any, std::decay_t<A>>> > static auto visit(F&& f, A&& a) { using Q = impl::propagate_qualifier_t<A, T>; - return util::any_cast<T>(&a)? - std::forward<F>(f)(util::any_cast<Q&&>(std::forward<A>(a))): + return std::any_cast<T>(&a)? + std::forward<F>(f)(std::any_cast<Q&&>(std::forward<A>(a))): any_visitor<U, Rest...>::visit(std::forward<F>(f), std::forward<A>(a)); } }; namespace impl { -template <typename F, typename... A> -struct invocable_impl { - template <typename G, typename = void> - struct test: std::false_type {}; - - template <typename G> - struct test<G, std::void_t<decltype(std::declval<G>()(std::declval<A>()...))>>: std::true_type {}; - - using type = typename test<F>::type; -}; - -template <typename F, typename... A> -using invocable = typename invocable_impl<F, A...>::type; - template <typename, typename...> struct overload_impl {}; template <typename F1> @@ -117,7 +102,7 @@ struct overload_impl<F1> { overload_impl(F1&& f1): f_(std::forward<F1>(f1)) {} - template <typename... A, std::enable_if_t<invocable<F1, A...>::value, int> = 0> + template <typename... A, std::enable_if_t<std::is_invocable_v<F1, A...>, int> = 0> decltype(auto) operator()(A&&... a) { return f_(std::forward<A>(a)...); } }; @@ -129,10 +114,10 @@ struct overload_impl<F1, F2, Fn...>: overload_impl<F2, Fn...> { overload_impl<F2, Fn...>(std::forward<F2>(f2), std::forward<Fn>(fn)...), f_(std::forward<F1>(f1)) {} - template <typename... A, std::enable_if_t<invocable<F1, A...>::value, int> = 0> + template <typename... A, std::enable_if_t<std::is_invocable_v<F1, A...>, int> = 0> decltype(auto) operator()(A&&... a) { return f_(std::forward<A>(a)...); } - template <typename... A, std::enable_if_t<!invocable<F1, A...>::value, int> = 0> + template <typename... A, std::enable_if_t<!std::is_invocable_v<F1, A...>, int> = 0> decltype(auto) operator()(A&&... a) { return overload_impl<F2, Fn...>::operator()(std::forward<A>(a)...); } diff --git a/arbor/include/arbor/util/expected.hpp b/arbor/include/arbor/util/expected.hpp index 17e37a80451947db697050b2d14ca500e413b57c..dd90d523dcc7c31c4f69e9919a9343116494b75e 100644 --- a/arbor/include/arbor/util/expected.hpp +++ b/arbor/include/arbor/util/expected.hpp @@ -186,6 +186,7 @@ struct expected { template < typename S, typename F, + typename = std::enable_if_t<!std::is_same_v<expected, expected<S, F>>>, typename = std::enable_if_t<!detail::conversion_hazard_v<T, expected<S, F>>>, typename = std::enable_if_t<!detail::conversion_hazard_v<unexpected<E>, expected<S, F>>> > @@ -196,6 +197,7 @@ struct expected { template < typename S, typename F, + typename = std::enable_if_t<!std::is_same_v<expected, expected<S, F>>>, typename = std::enable_if_t<!detail::conversion_hazard_v<T, expected<S, F>>>, typename = std::enable_if_t<!detail::conversion_hazard_v<unexpected<E>, expected<S, F>>> > @@ -205,10 +207,10 @@ struct expected { template < typename S, - typename = std::enable_if_t<std::is_constructible_v<T, S&&>>, typename = std::enable_if_t<!std::is_same_v<std::in_place_t, detail::remove_cvref_t<S>>>, typename = std::enable_if_t<!std::is_same_v<expected, detail::remove_cvref_t<S>>>, - typename = std::enable_if_t<!std::is_same_v<unexpected<E>, detail::remove_cvref_t<S>>> + typename = std::enable_if_t<!std::is_same_v<unexpected<E>, detail::remove_cvref_t<S>>>, + typename = std::enable_if_t<std::is_constructible_v<T, S&&>> > expected(S&& x): data_(std::in_place_index<0>, std::forward<S>(x)) {} diff --git a/arbor/include/arbor/util/typed_map.hpp b/arbor/include/arbor/util/typed_map.hpp index 22b59433106e0cbc8b9a6035dc2c29fde97cf393..1bd6214da96a72f6bedd082850e2822bb4ffd297 100644 --- a/arbor/include/arbor/util/typed_map.hpp +++ b/arbor/include/arbor/util/typed_map.hpp @@ -13,12 +13,11 @@ // m.get<int>() = {1, 2, 3}; // m.get<double>() = {1.2, 2.3}; +#include <any> #include <tuple> #include <typeindex> #include <unordered_map> -#include <arbor/util/any.hpp> - namespace arb { template <template <class> class E> @@ -27,19 +26,19 @@ struct dynamic_typed_map { // default value if no entry in map for T. template <typename T> E<T>& get() { - arb::util::any& store_entry = tmap_[std::type_index(typeid(T))]; + std::any& store_entry = tmap_[std::type_index(typeid(T))]; if (!store_entry.has_value()) { - store_entry = arb::util::any(E<T>{}); + store_entry = std::any(E<T>{}); } - return arb::util::any_cast<E<T>&>(store_entry); + return std::any_cast<E<T>&>(store_entry); } // Retrieve value by const reference associated with type T; // throw if no entry in map for T. template <typename T> const E<T>& get() const { - return arb::util::any_cast<const E<T>&>(tmap_.at(std::type_index(typeid(T)))); + return std::any_cast<const E<T>&>(tmap_.at(std::type_index(typeid(T)))); } // True if map has an entry for type T. @@ -47,7 +46,7 @@ struct dynamic_typed_map { bool has() const { return tmap_.count(std::type_index(typeid(T))); } private: - std::unordered_map<std::type_index, arb::util::any> tmap_; + std::unordered_map<std::type_index, std::any> tmap_; }; template <template <class> class E, typename... Keys> diff --git a/arbor/include/arbor/util/unique_any.hpp b/arbor/include/arbor/util/unique_any.hpp index d5b72f6f63888574279d1c9e1e00d92da798585d..0e5d7834e03e8220e0ee3a1a8600349580da5839 100644 --- a/arbor/include/arbor/util/unique_any.hpp +++ b/arbor/include/arbor/util/unique_any.hpp @@ -1,51 +1,55 @@ #pragma once +#include <any> #include <memory> #include <typeinfo> #include <type_traits> -#include <arbor/util/any.hpp> +#include <arbor/util/any_cast.hpp> -// A non copyable variant of util::any. -// The two main use cases for such a container are -// 1. storing types that are not copyable. -// 2. ensuring that no copies are made of copyable types that have to be stored -// in a type-erased container. +// A non copyable variant of std::any. The two main use cases for such a +// container are: +// 1. storing types that are not copyable, and +// 2. ensuring that no copies are made of copyable types stored within a +// in a type-erased container. // -// unique_any has the same semantics as any with the execption of copy and copy -// assignment, which are explicitly forbidden for all contained types. -// The requirement that the contained type be copy constructable has also been -// relaxed. +// util::unique_any has the same semantics as std::any with the execption of +// copy and copy assignment, which are explicitly forbidden for all contained +// types. Objects stored in util::unique_any also naturally need not be copy +// constructable. // -// The any_cast non-member functions have been overridden for unique_any, with -// the same semantics as for any. -// This makes it possible to copy the underlying stored type if the type is -// copyable. For example, the following code will compile and execute as -// expected. +// util::any_cast<T> overloads are provided for util::unique_any, with +// semantics analagous to those of std::any_cast and std::any, with copying +// of the contained value permitted if the type of that value permits it. // -// unique_any<int> a(3); -// int& ref = any_cast<int&>(a); // take a reference -// ref = 42; // update contained value via reference -// int val = any_cast<int>(a); // take a copy -// assert(val==42); +// Examples: // -// If the underlying type is not copyable, only references may be taken +// unique_any<int> a(3); +// int& ref = any_cast<int&>(a); // Take a reference. +// ref = 42; // Update contained value via reference. +// int val = any_cast<int>(a); // Take a copy. +// assert(val==42); // -// unique_any<nocopy_t> a(); -// nocopy_t& ref = any_cast<nocopy_t&>(a); // ok -// const nocopy_t& cref = any_cast<const nocopy_t&>(a); // ok -// nocopy_t v = any_cast<nocopy_t>(a); // compile time error +// // If the underlying type is not copyable, only references may be +// // taken to the contained value. For a movable but non-copyable +// // type `nocopy_t`: // -// An lvalue can be created by moving from the contained object: -// -// nocopy_t v = any_cast<nocopy_t&&>(std::move(a)); // ok -// -// After which a is in moved from state. - +// unique_any<nocopy_t> a(); +// nocopy_t& ref = any_cast<nocopy_t&>(a); // ok +// const nocopy_t& cref = any_cast<const nocopy_t&>(a); // ok +// nocopy_t v = any_cast<nocopy_t&&>(std::move(a)); // ok +// nocopy_t v = any_cast<nocopy_t>(a); // Not ok: compile-time error. namespace arb { namespace util { +namespace detail { + // TODO: C++20 replace with std::remove_cvref_t + template <typename T> + using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>; +} + + class unique_any { public: constexpr unique_any() = default; @@ -56,7 +60,8 @@ public: template < typename T, - typename = std::enable_if_t<!std::is_same<std::decay_t<T>, unique_any>::value> + typename = std::enable_if_t<!std::is_same_v<detail::remove_cvref_t<T>, unique_any>>, + typename = std::enable_if_t<!std::is_same_v<detail::remove_cvref_t<T>, std::any>> > unique_any(T&& other) { state_.reset(new model<contained_type<T>>(std::forward<T>(other))); @@ -69,7 +74,8 @@ public: template < typename T, - typename = std::enable_if_t<!std::is_same<std::decay_t<T>, unique_any>::value> + typename = std::enable_if_t<!std::is_same_v<detail::remove_cvref_t<T>, unique_any>>, + typename = std::enable_if_t<!std::is_same_v<detail::remove_cvref_t<T>, std::any>> > unique_any& operator=(T&& other) { state_.reset(new model<contained_type<T>>(std::forward<T>(other))); @@ -160,40 +166,40 @@ T* any_cast(unique_any* operand) { template<class T> T any_cast(const unique_any& operand) { - using U = impl::any_cast_remove_qual<T>; + using U = detail::remove_cvref_t<T>; static_assert(std::is_constructible<T, const U&>::value, "any_cast type can't construct copy of contained object"); auto ptr = any_cast<U>(&operand); if (ptr==nullptr) { - throw bad_any_cast(); + throw std::bad_any_cast(); } return static_cast<T>(*ptr); } template<class T> T any_cast(unique_any& operand) { - using U = impl::any_cast_remove_qual<T>; + using U = detail::remove_cvref_t<T>; static_assert(std::is_constructible<T, U&>::value, "any_cast type can't construct copy of contained object"); auto ptr = any_cast<U>(&operand); if (ptr==nullptr) { - throw bad_any_cast(); + throw std::bad_any_cast(); } return static_cast<T>(*ptr); } template<class T> T any_cast(unique_any&& operand) { - using U = impl::any_cast_remove_qual<T>; + using U = detail::remove_cvref_t<T>; static_assert(std::is_constructible<T, U&&>::value, "any_cast type can't construct copy of contained object"); auto ptr = any_cast<U>(&operand); if (ptr==nullptr) { - throw bad_any_cast(); + throw std::bad_any_cast(); } return static_cast<T>(std::move(*ptr)); } diff --git a/arbor/include/arbor/util/variant.hpp b/arbor/include/arbor/util/variant.hpp deleted file mode 100644 index ecb724a1e1d4360a86193bf286d8479ceeaf1264..0000000000000000000000000000000000000000 --- a/arbor/include/arbor/util/variant.hpp +++ /dev/null @@ -1,638 +0,0 @@ -#pragma once - -// C++14 std::variant work-alike. -// -// Key differences: -// -// * Using a type-index on operations where the type appears multiple times -// in the variant type list is not treated as an error. -// -// * No template constants in C++14, so `in_place_index` and `in_place_type` -// are constexpr functions instead. -// -// * Rather than overload `std::get` etc., uses `util::get` which wraps -// dispatches to `variant<...>::get` (`util::get` is also defined in -// private `util/meta.hpp` header for pairs and tuples.) -// -// * Assignemnt from non-variant type relies upon default conversion to -// variant type. -// -// * Swap doesn't make nothrow guarantees. -// -// * Unimplemented (yet): visit() with more than one variant argument; -// monostate; comparisons; unit tests for nothrow guarantees. - -#include <cstddef> -#include <new> -#include <stdexcept> -#include <type_traits> - -namespace arb { -namespace util { - -struct bad_variant_access: public std::runtime_error { - bad_variant_access(): std::runtime_error("bad variant access") {} -}; - -template <typename T> struct in_place_type_t {}; - -template <typename T> -static constexpr in_place_type_t<T> in_place_type() { return {}; } - -template <std::size_t I> struct in_place_index_t {}; - -template <std::size_t I> -static constexpr in_place_index_t<I> in_place_index() { return {}; }; - -namespace detail { - -template <typename... T> -struct max_sizeof: public std::integral_constant<std::size_t, 1> {}; - -template <typename H, typename... T> -struct max_sizeof<H, T...>: public std::integral_constant<std::size_t, - (max_sizeof<T...>::value > sizeof(H))? max_sizeof<T...>::value: sizeof(H)> {}; - -template <typename... T> -struct max_alignof: public std::integral_constant<std::size_t, 1> {}; - -template <typename H, typename... T> -struct max_alignof<H, T...>: public std::integral_constant<std::size_t, - (max_alignof<T...>::value > alignof(H))? max_alignof<T...>::value: alignof(H)> {}; - -// type_select_t<i, T0, ..., Tn> gives type Ti. - -template <std::size_t I, typename... T> struct type_select; - -template <typename X, typename... T> -struct type_select<0, X, T...> { using type = X; }; - -template <std::size_t I, typename X, typename... T> -struct type_select<I, X, T...> { using type = typename type_select<I-1, T...>::type; }; - -template <std::size_t I> -struct type_select<I> { using type = void; }; - -template <std::size_t I, typename... T> -using type_select_t = typename type_select<I, T...>::type; - -// type_index<T, T0, ..., Tn>::value gives i such that T is Ti, or else -1. - -template <std::size_t I, typename X, typename... T> -struct type_index_impl: std::integral_constant<std::size_t, std::size_t(-1)> {}; - -template <std::size_t I, typename X, typename... T> -struct type_index_impl<I, X, X, T...>: std::integral_constant<std::size_t, I> {}; - -template <std::size_t I, typename X, typename Y, typename... T> -struct type_index_impl<I, X, Y, T...>: type_index_impl<I+1, X, T...> {}; - -template <typename X, typename... T> -using type_index = std::integral_constant<std::size_t, type_index_impl<0, X, T...>::value>; - -// Build overload set for implicit construction from type list. - -template <typename T> -using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>; - -template <std::size_t, typename... T> -struct variant_implicit_ctor_index_impl; - -template <std::size_t I> -struct variant_implicit_ctor_index_impl<I> { - static std::integral_constant<std::size_t, std::size_t(-1)> index(...); -}; - -template <std::size_t I, typename X, typename... T> -struct variant_implicit_ctor_index_impl<I, X, T...>: variant_implicit_ctor_index_impl<I+1, T...> { - using variant_implicit_ctor_index_impl<I+1, T...>::index; - - template <typename X_nocv = std::remove_cv_t<X>, - typename = std::enable_if_t<!std::is_same<bool, X_nocv>::value>> - static std::integral_constant<std::size_t, I> index(X); - - template <typename A, - typename X_nocv = std::remove_cv_t<X>, - typename = std::enable_if_t<std::is_same<bool, X_nocv>::value>, - typename A_nocvref = remove_cvref_t<A>, - typename = std::enable_if_t<std::is_same<bool, A_nocvref>::value>> - static std::integral_constant<std::size_t, I> index(A&& a); -}; - -template <typename X, typename... T> -struct variant_implicit_ctor_index: - decltype(variant_implicit_ctor_index_impl<0, T...>::index(std::declval<X>())) {}; - -// Test for in-place types - -template <typename X> struct is_in_place_impl: std::false_type {}; -template <typename T> struct is_in_place_impl<in_place_type_t<T>>: std::true_type {}; -template <std::size_t I> struct is_in_place_impl<in_place_index_t<I>>: std::true_type {}; - -template <typename X> using is_in_place = is_in_place_impl<std::decay_t<X>>; - -// Variadic tests for nothrow. - -template <typename... T> struct are_nothrow_move_constructible; -template <> struct are_nothrow_move_constructible<>: std::true_type {}; -template <typename H, typename... T> -struct are_nothrow_move_constructible<H, T...>: - std::conditional_t<std::is_nothrow_move_constructible<H>::value, - are_nothrow_move_constructible<T...>, std::false_type> {}; - -template <typename... T> struct are_nothrow_copy_constructible; -template <> struct are_nothrow_copy_constructible<>: std::true_type {}; -template <typename H, typename... T> -struct are_nothrow_copy_constructible<H, T...>: - std::conditional_t<std::is_nothrow_copy_constructible<H>::value, - are_nothrow_copy_constructible<T...>, std::false_type> {}; - -template <typename... T> struct any_reference; -template <> struct any_reference<>: std::false_type {}; -template <typename H, typename... T> -struct any_reference<H, T...>: - std::conditional_t<std::is_reference<H>::value, std::true_type, any_reference<T...>> {}; - -// Copy and move ctor and assignment implementations. - -template <typename... T> -struct variant_dynamic_impl; - -template <> -struct variant_dynamic_impl<> { - static void copy(std::size_t i, char* data, const char* from) { - if (i!=std::size_t(-1)) throw bad_variant_access{}; - } - - static void move(std::size_t i, char* data, const char* from) { - if (i!=std::size_t(-1)) throw bad_variant_access{}; - } - - static void assign(std::size_t i, char* data, const char* from) { - if (i!=std::size_t(-1)) throw bad_variant_access{}; - } - - static void move_assign(std::size_t i, char* data, char* from) { - if (i!=std::size_t(-1)) throw bad_variant_access{}; - } - - static void swap(std::size_t i, char* data1, char* data2) { - if (i!=std::size_t(-1)) throw bad_variant_access{}; - } - - static void destroy(std::size_t i, char* data) {} - - static bool cmp_eq(std::size_t i, const char* left, const char* right) { - return i==std::size_t(-1)? true: throw bad_variant_access{}; - } - - static bool cmp_ne(std::size_t i, const char* left, const char* right) { - return i==std::size_t(-1)? false: throw bad_variant_access{}; - } -}; - -template <typename H, typename... T> -struct variant_dynamic_impl<H, T...> { - static void copy(std::size_t i, char* data, const char* from) { - if (i==0) { - new(reinterpret_cast<H*>(data)) H(*reinterpret_cast<const H*>(from)); - } - else { - variant_dynamic_impl<T...>::copy(i-1, data, from); - } - } - - static void move(std::size_t i, char* data, char* from) { - if (i==0) { - new(reinterpret_cast<H*>(data)) H(std::move(*reinterpret_cast<H*>(from))); - } - else { - variant_dynamic_impl<T...>::move(i-1, data, from); - } - } - - static void assign(std::size_t i, char* data, const char* from) { - if (i==0) { - *reinterpret_cast<H*>(data) = *reinterpret_cast<const H*>(from); - } - else { - variant_dynamic_impl<T...>::assign(i-1, data, from); - } - } - - static void move_assign(std::size_t i, char* data, char* from) { - if (i==0) { - *reinterpret_cast<H*>(data) = std::move(*reinterpret_cast<H*>(from)); - } - else { - variant_dynamic_impl<T...>::move_assign(i-1, data, from); - } - } - - static void swap(std::size_t i, char* data1, char* data2) { - using std::swap; - if (i==0) { - swap(*reinterpret_cast<H*>(data1), *reinterpret_cast<H*>(data2)); - } - else { - variant_dynamic_impl<T...>::swap(i-1, data1, data2); - } - } - - static void destroy(std::size_t i, char* data) { - if (i==0) { - reinterpret_cast<H*>(data)->~H(); - } - else { - variant_dynamic_impl<T...>::destroy(i-1, data); - } - } - - static bool cmp_eq(std::size_t i, const char* left, const char* right) { - return i==0? - *reinterpret_cast<const H*>(left)==*reinterpret_cast<const H*>(right): - variant_dynamic_impl<T...>::cmp_eq(i-1, left, right); - } - - static bool cmp_ne(std::size_t i, const char* left, const char* right) { - return i==0? - *reinterpret_cast<const H*>(left)!=*reinterpret_cast<const H*>(right): - variant_dynamic_impl<T...>::cmp_ne(i-1, left, right); - } -}; - -template <typename... T> -struct variant { - static_assert(!any_reference<T...>::value, "variant must have no reference alternative"); - alignas(max_alignof<T...>::value) char data[max_sizeof<T...>::value]; - - template <typename X> X* data_ptr() noexcept { return reinterpret_cast<X*>(&data); } - template <typename X> const X* data_ptr() const noexcept { return reinterpret_cast<const X*>(&data); } - - std::size_t which_ = -1; - static constexpr std::size_t npos = -1; - - // Explict construction by index. - - template <std::size_t I, typename... A, typename = std::enable_if_t<(I<sizeof...(T))>> - explicit variant(in_place_index_t<I>, A&&... a): which_(I) - { - using X = type_select_t<I, T...>; - new(data_ptr<X>()) X(std::forward<A>(a)...); - } - - template <std::size_t I, typename U, typename... A, typename = std::enable_if_t<(I<sizeof...(T))>> - explicit variant(in_place_index_t<I>, std::initializer_list<U> il, A&&... a): which_(I) - { - using X = type_select_t<I, T...>; - new(data_ptr<X>()) X(il, std::forward<A>(a)...); - } - - // Explicit construction by type. - - template <typename X, typename... A, std::size_t I = type_index<X, T...>::value> - explicit variant(in_place_type_t<X>, A&&... a): - variant(in_place_index_t<I>{}, std::forward<A>(a)...) {} - - template <typename X, typename U, typename... A, std::size_t I = type_index<X, T...>::value> - explicit variant(in_place_type_t<X>, std::initializer_list<U> il, A&&... a): - variant(in_place_index_t<I>{}, il, std::forward<A>(a)...) {} - - // Implicit construction from argument. - - template <typename X, - typename = std::enable_if_t<!std::is_same<variant, std::decay_t<X>>::value>, - typename = std::enable_if_t<!is_in_place<X>::value>, - typename index = variant_implicit_ctor_index<X, T...>> - variant(X&& x): - variant(in_place_index<index::value>(), std::forward<X>(x)) {} - - // Default constructible if first type is. - - template <typename X = type_select_t<0, T...>, - typename = std::enable_if_t<std::is_default_constructible<X>::value>> - variant() noexcept(std::is_nothrow_default_constructible<X>::value): which_(0) { - new(data_ptr<X>()) X; - } - - // Copy construction. - - variant(const variant& x) - noexcept(are_nothrow_copy_constructible<T...>::value): which_(x.which_) - { - variant_dynamic_impl<T...>::copy(which_, data, x.data); - } - - // Move construction. - - variant(variant&& x) - noexcept(are_nothrow_move_constructible<T...>::value): which_(x.which_) - { - variant_dynamic_impl<T...>::move(which_, data, x.data); - } - - // Copy assignment. - - variant& operator=(const variant& x) { - if (which_!=x.which_) { - variant_dynamic_impl<T...>::destroy(which_, data); - which_ = npos; - if (x.which_!=npos) { - variant_dynamic_impl<T...>::copy(x.which_, data, x.data); - which_ = x.which_; - } - } - else { - auto old_which = which_; - which_ = npos; - if (x.which_!=npos) { - try { - variant_dynamic_impl<T...>::assign(x.which_, data, x.data); - which_ = x.which_; - } - catch (...) { - variant_dynamic_impl<T...>::destroy(old_which, data); - throw; - } - } - } - return *this; - } - - // Move assignment. - - variant& operator=(variant&& x) { - if (which_!=x.which_) { - variant_dynamic_impl<T...>::destroy(which_, data); - which_ = npos; - if (x.which_!=npos) { - variant_dynamic_impl<T...>::move(x.which_, data, x.data); - which_ = x.which_; - } - } - else { - if (x.which_!=npos) { - variant_dynamic_impl<T...>::move_assign(x.which_, data, x.data); - } - } - return *this; - } - - // In place construction. - - template <std::size_t I, - typename... Args, - typename = std::enable_if_t<(I<sizeof...(T))>, - typename X = type_select_t<I, T...>, - typename = std::enable_if_t<std::is_constructible<X, Args...>::value>> - X& emplace(Args&&... args) { - if (which_!=npos) { - variant_dynamic_impl<T...>::destroy(which_, data); - which_ = npos; - } - new(data_ptr<X>()) X(std::forward<Args>(args)...); - return *data_ptr<X>(); - } - - template <std::size_t I, - typename U, - typename... Args, - typename = std::enable_if_t<(I<sizeof...(T))>, - typename X = type_select_t<I, T...>, - typename = std::enable_if_t<std::is_constructible<X, std::initializer_list<U>, Args...>::value>> - X& emplace(std::initializer_list<U> il, Args&&... args) { - if (which_!=npos) { - variant_dynamic_impl<T...>::destroy(which_, data); - which_ = npos; - } - new(data_ptr<X>()) X(il, std::forward<Args>(args)...); - which_ = I; - return *data_ptr<X>(); - } - - template <typename X, - typename... Args, - std::size_t I = type_index<X, T...>::value> - X& emplace(Args&&... args) { - return emplace<I>(std::forward<Args>(args)...); - } - - template <typename X, - typename U, - typename... Args, - std::size_t I = type_index<X, T...>::value> - X& emplace(std::initializer_list<U> il, Args&&... args) { - return emplace<I>(il, std::forward<Args>(args)...); - } - - // Swap. - - void swap(variant& rhs) { - if (which_==rhs.which_) { - if (which_!=npos) { - variant_dynamic_impl<T...>::swap(which_, data, rhs.data); - } - } - else { - variant tmp(std::move(rhs)); - rhs = std::move(*this); - *this = std::move(tmp); - } - } - - // Queries. - - std::size_t index() const { return which_; } - - bool valueless_by_exception() const { return which_==npos; } - - // Pointer access (does not throw). - - template <std::size_t I, typename = std::enable_if_t<(I<sizeof...(T))>, typename X = type_select_t<I, T...>> - X* get_if() noexcept { return which_==I? data_ptr<X>(): nullptr; } - - template <typename X, std::size_t I = type_index<X, T...>::value> - auto get_if() noexcept { return get_if<I>(); } - - template <std::size_t I, typename = std::enable_if_t<(I<sizeof...(T))>, typename X = type_select_t<I, T...>> - const X* get_if() const noexcept { return which_==I? data_ptr<X>(): nullptr; } - - template <typename X, std::size_t I = type_index<X, T...>::value> - auto get_if() const noexcept { return get_if<I>(); } - - // Reference access (throws). - - template <std::size_t I, typename = std::enable_if_t<(I<sizeof...(T))>> - auto& get() & { - if (auto* p = get_if<I>()) return *p; - else throw bad_variant_access{}; - } - - template <std::size_t I, typename = std::enable_if_t<(I<sizeof...(T))>> - auto& get() const & { - if (auto* p = get_if<I>()) return *p; - else throw bad_variant_access{}; - } - - template <std::size_t I, typename = std::enable_if_t<(I<sizeof...(T))>> - auto&& get() && { - if (auto* p = get_if<I>()) return std::move(*p); - else throw bad_variant_access{}; - } - - template <std::size_t I, typename = std::enable_if_t<(I<sizeof...(T))>> - auto&& get() const && { - if (auto* p = get_if<I>()) return std::move(*p); - else throw bad_variant_access{}; - } - - template <typename X, std::size_t I = type_index<X, T...>::value> - decltype(auto) get() { return get<I>(); } - - template <typename X, std::size_t I = type_index<X, T...>::value> - decltype(auto) get() const { return get<I>(); } - - // Comparisons. - - bool operator==(const variant& x) const { - return which_==x.which_ && (valueless_by_exception() || variant_dynamic_impl<T...>::cmp_eq(which_, data, x.data)); - } - - bool operator!=(const variant& x) const { - return which_!=x.which_ || (!valueless_by_exception() && variant_dynamic_impl<T...>::cmp_ne(which_, data, x.data)); - } -}; - -template <std::size_t I, std::size_t N> -struct variant_visit { - template <typename R, typename Visitor, typename Variant> - static R visit(std::size_t i, Visitor&& f, Variant&& v) { - if (i==I) { - return static_cast<R>(std::forward<Visitor>(f)(std::forward<Variant>(v).template get<I>())); - } - else { - return variant_visit<I+1, N>::template visit<R>(i, std::forward<Visitor>(f), std::forward<Variant>(v)); - } - } -}; - -template <std::size_t I> -struct variant_visit<I, I> { - template <typename R, typename Visitor, typename Variant> - static R visit(std::size_t i, Visitor&& f, Variant&& v) { - throw bad_variant_access{}; // Actually, should never get here. - } -}; - -template <typename X> struct variant_size_impl; -template <typename... T> -struct variant_size_impl<variant<T...>>: std::integral_constant<std::size_t, sizeof...(T)> {}; - -template <std::size_t I, typename T> struct variant_alternative; - -template <std::size_t I, typename... T> -struct variant_alternative<I, variant<T...>> { using type = type_select_t<I, T...>; }; - -template <std::size_t I, typename... T> -struct variant_alternative<I, const variant<T...>> { using type = std::add_const_t<type_select_t<I, T...>>; }; - -} // namespace detail - -template <typename... T> -using variant = detail::variant<T...>; - -template <typename X> -using variant_size = detail::variant_size_impl<std::remove_cv_t<std::remove_reference_t<X>>>; - -template <std::size_t I, typename V> -using variant_alternative_t = typename detail::variant_alternative<I, V>::type; - -// util:: variants of std::get - -template <typename X, typename... T> -decltype(auto) get(variant<T...>& v) { return v.template get<X>(); } - -template <typename X, typename... T> -decltype(auto) get(const variant<T...>& v) { return v.template get<X>(); } - -template <typename X, typename... T> -decltype(auto) get(variant<T...>&& v) { return std::move(v).template get<X>(); } - -template <typename X, typename... T> -decltype(auto) get(const variant<T...>&& v) { return std::move(v).template get<X>(); } - -template <std::size_t I, typename... T> -decltype(auto) get(variant<T...>& v) { return v.template get<I>(); } - -template <std::size_t I, typename... T> -decltype(auto) get(const variant<T...>& v) { return v.template get<I>(); } - -template <std::size_t I, typename... T> -decltype(auto) get(variant<T...>&& v) { return std::move(v).template get<I>(); } - -template <std::size_t I, typename... T> -decltype(auto) get(const variant<T...>&& v) { return std::move(v).template get<I>(); } - -// util:: variants of std::get_if - -template <typename X, typename... T> -decltype(auto) get_if(variant<T...>& v) noexcept { return v.template get_if<X>(); } - -template <typename X, typename... T> -decltype(auto) get_if(const variant<T...>& v) noexcept { return v.template get_if<X>(); } - -template <std::size_t I, typename... T> -decltype(auto) get_if(variant<T...>& v) noexcept { return v.template get_if<I>(); } - -template <std::size_t I, typename... T> -decltype(auto) get_if(const variant<T...>& v) noexcept { return v.template get_if<I>(); } - -// One-argument visitor - -template <typename Visitor, typename Variant> -decltype(auto) visit(Visitor&& f, Variant&& v) { - using R = decltype(f(get<0>(std::forward<Variant>(v)))); - - if (v.valueless_by_exception()) throw bad_variant_access{}; - std::size_t i = v.index(); - return static_cast<R>(detail::variant_visit<0, variant_size<Variant>::value>::template visit<R>(i, - std::forward<Visitor>(f), std::forward<Variant>(v))); -} - -template <typename R, typename Visitor, typename Variant> -R visit(Visitor&& f, Variant&& v) { - if (v.valueless_by_exception()) throw bad_variant_access{}; - std::size_t i = v.index(); - return static_cast<R>(detail::variant_visit<0, variant_size<Variant>::value>::template visit<R>(i, - std::forward<Visitor>(f), std::forward<Variant>(v))); -} - -// Not implementing multi-argument visitor yet! -// (If we ever have a use case...) - -} // namespace util -} // namespace arb - -namespace std { - -// Unambitious hash: -template <typename... T> -struct hash<::arb::util::variant<T...>> { - std::size_t operator()(const ::arb::util::variant<T...>& v) const { - return v.index() ^ - ::arb::util::visit([](const auto& a) { return std::hash<std::remove_cv_t<std::remove_reference_t<decltype(a)>>>{}(a); }, v); - } -}; - -// Still haven't really determined if it is okay to have a variant<>, but if we do allow it... -template <> -struct hash<::arb::util::variant<>> { - std::size_t operator()(const ::arb::util::variant<>& v) const { return 0u; }; -}; - -// std::swap specialization. -template <typename... T> -void swap(::arb::util::variant<T...>& v1, ::arb::util::variant<T...>& v2) { - v1.swap(v2); -} -} // namespace std diff --git a/arbor/mc_cell_group.cpp b/arbor/mc_cell_group.cpp index 2b6950d2e0bbce24be3205e988d2ba22f1e9e1fb..124734bec01cf29e6ad13bba9c0fd09289ae1ac4 100644 --- a/arbor/mc_cell_group.cpp +++ b/arbor/mc_cell_group.cpp @@ -1,5 +1,6 @@ #include <functional> #include <unordered_set> +#include <variant> #include <vector> #include <arbor/assert.hpp> @@ -309,7 +310,7 @@ void run_samples( std::vector<sample_record>& sample_records, fvm_probe_scratch& scratch) { - util::visit([&](auto& x) {run_samples(x, sc, raw_times, raw_samples, sample_records, scratch); }, sc.pdata_ptr->info); + std::visit([&](auto& x) {run_samples(x, sc, raw_times, raw_samples, sample_records, scratch); }, sc.pdata_ptr->info); } void mc_cell_group::advance(epoch ep, time_type dt, const event_lane_subrange& event_lanes) { diff --git a/arbor/spike_source_cell_group.cpp b/arbor/spike_source_cell_group.cpp index c5521b306f14ba6cd4f1cbb4f936a74b665d8853..0e3b3ba86ab54265894e2a2ca493b496b3e94e64 100644 --- a/arbor/spike_source_cell_group.cpp +++ b/arbor/spike_source_cell_group.cpp @@ -21,7 +21,7 @@ spike_source_cell_group::spike_source_cell_group(const std::vector<cell_gid_type auto cell = util::any_cast<spike_source_cell>(rec.get_cell_description(gid)); time_sequences_.push_back(std::move(cell.seq)); } - catch (util::bad_any_cast& e) { + catch (std::bad_any_cast& e) { throw bad_cell_description(cell_kind::spike_source, gid); } } diff --git a/arbor/util/sentinel.hpp b/arbor/util/sentinel.hpp index 30d0696d72502c65d6191c22c2c1f448efafb9ed..22e46ffc622f0f41edc0253cfa2708dd66391da0 100644 --- a/arbor/util/sentinel.hpp +++ b/arbor/util/sentinel.hpp @@ -1,8 +1,7 @@ #pragma once #include <type_traits> - -#include <arbor/util/variant.hpp> +#include <variant> #include "util/meta.hpp" @@ -28,7 +27,7 @@ struct iterator_category_select<I,S,true> { template <typename I, typename S> class sentinel_iterator { - arb::util::variant<I, S> e_; + std::variant<I, S> e_; I& iter() { arb_assert(!is_sentinel()); diff --git a/doc/cpp_common.rst b/doc/cpp_common.rst index ff4d1273affb25caab484067dc814152add6918c..0604b98746e75562a5d7f9e92b735c4698816bbd 100644 --- a/doc/cpp_common.rst +++ b/doc/cpp_common.rst @@ -112,7 +112,7 @@ Probes Opaque key, returned in sample record. - .. cpp:member:: util::any address + .. cpp:member:: std::any address Cell-type specific location info, specific to cell kind of ``id.gid``. @@ -121,35 +121,31 @@ Utility Wrappers and Containers .. cpp:namespace:: arb::util +.. cpp:class:: unique_any -.. cpp:class:: template <typename T> optional + Equivalent to :cpp:class:`std::any`, except that: - A wrapper around a contained value of type :cpp:type:`T`, that may or may not be set. - A faithful copy of the C++17 ``std::optional`` type. - See the online C++ standard documentation - `<https://en.cppreference.com/w/cpp/utility/optional>`_ - for more information. + * it can store any type that is move constructable; + * it is move only, that is, it can't be copied. -.. cpp:class:: any +.. cpp:class:: any_ptr - A container for a single value of any type that is copy constructable. - Used in the Arbor API where a type of a value passed to or from the API - is decided at run time. + Holds a pointer to an arbitrary type, together with the type information. - A faithful copy of the C++17 ``std::any`` type. - See the online C++ standard documentation - `<https://en.cppreference.com/w/cpp/utility/any>`_ - for more information. + .. cpp:function:: template <typename T> T as() - The :cpp:any:`arb::util` namespace also implementations of the - :cpp:any:`any_cast`, :cpp:any:`make_any` and :cpp:any:`bad_any_cast` - helper functions and types from C++17. + Retrieve the pointer as type T. If T is ``void *`` or the same + as the type of the pointer stored in ``any_ptr``, return the held + value, cast accordingly. Otherwise return ``nullptr``. -.. cpp:class:: unique_any + ``any_ptr`` can be used with ``util::any_cast``, so that + ``util::any_cast<T>(p)`` is equivalent to ``p.as<T>()`` for a value ``p`` + of type ``any_ptr``. - Equivalent to :cpp:class:`util::any`, except that: - - * it can store any type that is move constructable; - * it is move only, that is it can't be copied. +.. cpp:function:: template <typename T> any_cast(...) + + Equivalent to ``std::any_cast`` for ``std::any`` arguments, ``any_cast`` + also performs analagous casting for the :cpp:class:`unique_any` and + :cpp:class:`any_ptr` utility classes. diff --git a/doc/cpp_recipe.rst b/doc/cpp_recipe.rst index b08f9ae317a3edd30ed31d233e7a039998b29912..300830e03472cc5348d509c00e4e624fc732b344 100644 --- a/doc/cpp_recipe.rst +++ b/doc/cpp_recipe.rst @@ -133,7 +133,7 @@ Class Documentation By default throws :cpp:type:`std::logic_error`. If :cpp:func:`num_probes` returns a non-zero value, this must also be overridden. - .. cpp:function:: virtual util::any get_global_properties(cell_kind) const + .. cpp:function:: virtual std::any get_global_properties(cell_kind) const Global property type will be specific to given cell kind. diff --git a/example/dryrun/dryrun.cpp b/example/dryrun/dryrun.cpp index 53b5ce902cef34d8b87288eab7c00f8432fdc1dd..0ef5193d1aa5038ace8653cc9ee2177a3ee4fdb3 100644 --- a/example/dryrun/dryrun.cpp +++ b/example/dryrun/dryrun.cpp @@ -3,6 +3,7 @@ * */ +#include <any> #include <cassert> #include <fstream> #include <iomanip> @@ -80,7 +81,7 @@ public: return cell_kind::cable; } - arb::util::any get_global_properties(arb::cell_kind) const override { + std::any get_global_properties(arb::cell_kind) const override { arb::cable_cell_global_properties gprop; gprop.default_parameters = arb::neuron_parameter_defaults; return gprop; diff --git a/example/gap_junctions/gap_junctions.cpp b/example/gap_junctions/gap_junctions.cpp index 69c95b882a7cc7be017f5b243dbb1d2cd0384add..f0c3e4b38a8d73c94a299e09730cf82e0572c52f 100644 --- a/example/gap_junctions/gap_junctions.cpp +++ b/example/gap_junctions/gap_junctions.cpp @@ -3,6 +3,7 @@ * */ +#include <any> #include <fstream> #include <iomanip> #include <iostream> @@ -99,7 +100,7 @@ public: return {arb::cable_probe_membrane_voltage{loc}}; } - arb::util::any get_global_properties(cell_kind k) const override { + std::any get_global_properties(cell_kind k) const override { arb::cable_cell_global_properties a; a.default_parameters = arb::neuron_parameter_defaults; a.default_parameters.temperature_K = 308.15; diff --git a/example/generators/generators.cpp b/example/generators/generators.cpp index 66121c07b473e981bc528ce7e01dc7ae73eea2f8..48147fc064794ba181877f1956e437d99cf04711 100644 --- a/example/generators/generators.cpp +++ b/example/generators/generators.cpp @@ -6,6 +6,7 @@ * event generators, one inhibitory, and one excitatory, are attached. */ +#include <any> #include <cassert> #include <fstream> #include <iomanip> @@ -70,7 +71,7 @@ public: return cell_kind::cable; } - arb::util::any get_global_properties(arb::cell_kind) const override { + std::any get_global_properties(arb::cell_kind) const override { arb::cable_cell_global_properties gprop; gprop.default_parameters = arb::neuron_parameter_defaults; return gprop; diff --git a/example/lfp/lfp.cpp b/example/lfp/lfp.cpp index 38217129f7f8556d17846f4441634a8424ee46a3..54520d5c21e6f2927ab9b3ea4c15bcce268a845f 100644 --- a/example/lfp/lfp.cpp +++ b/example/lfp/lfp.cpp @@ -1,3 +1,4 @@ +#include <any> #include <cassert> #include <vector> #include <iostream> @@ -11,12 +12,14 @@ #include <arbor/sampling.hpp> #include <arbor/simple_sampler.hpp> #include <arbor/simulation.hpp> -#include <arbor/util/any.hpp> +#include <arbor/util/any_cast.hpp> #include <arbor/util/any_ptr.hpp> +#include <arbor/util/unique_any.hpp> -using arb::util::any; +using std::any; using arb::util::any_cast; using arb::util::any_ptr; +using arb::util::unique_any; using arb::cell_gid_type; using arb::cell_member_type; @@ -49,7 +52,7 @@ struct lfp_demo_recipe: public arb::recipe { return arb::cell_kind::cable; } - arb::util::unique_any get_cell_description(cell_gid_type) const override { + unique_any get_cell_description(cell_gid_type) const override { return cell_; } diff --git a/example/probe-demo/probe-demo.cpp b/example/probe-demo/probe-demo.cpp index 0598f02888843cbadde9649f529c7efd1e73c68e..860ffbc64c4c8529d2659a56718d8713272d95bb 100644 --- a/example/probe-demo/probe-demo.cpp +++ b/example/probe-demo/probe-demo.cpp @@ -1,3 +1,4 @@ +#include <any> #include <functional> #include <iomanip> #include <iostream> @@ -13,14 +14,14 @@ #include <arbor/morph/region.hpp> #include <arbor/simulation.hpp> #include <arbor/sampling.hpp> -#include <arbor/util/any.hpp> +#include <arbor/util/any_cast.hpp> #include <arbor/util/any_ptr.hpp> #include <tinyopt/smolopt.h> // Simulate a cell modelled as a simple cable with HH dynamics, // emitting the results of a user specified probe over time. -using arb::util::any; +using std::any; using arb::util::any_cast; using arb::util::any_ptr; @@ -108,7 +109,7 @@ struct cable_recipe: public arb::recipe { return arb::cell_kind::cable; } - arb::util::any get_global_properties(arb::cell_kind) const override { + any get_global_properties(arb::cell_kind) const override { return gprop; } diff --git a/example/ring/ring.cpp b/example/ring/ring.cpp index 0a2137988ad56c9dca71c828e5827af25b5858e9..ed2d7a020a68e1c35564f3a8d8b645b947caa9a0 100644 --- a/example/ring/ring.cpp +++ b/example/ring/ring.cpp @@ -3,6 +3,7 @@ * */ +#include <any> #include <cassert> #include <fstream> #include <iomanip> @@ -115,7 +116,7 @@ public: return {arb::cable_probe_membrane_voltage{loc}}; } - arb::util::any get_global_properties(arb::cell_kind) const override { + std::any get_global_properties(arb::cell_kind) const override { return gprop_; } diff --git a/example/single/single.cpp b/example/single/single.cpp index 920849e3c8d87336811def28d0752ad1ee7e5dfd..bbcb65569d364a03c0253df0e76d1a1c06a4bf61 100644 --- a/example/single/single.cpp +++ b/example/single/single.cpp @@ -1,3 +1,4 @@ +#include <any> #include <fstream> #include <iomanip> #include <iostream> @@ -50,7 +51,7 @@ struct single_recipe: public arb::recipe { return arb::cell_kind::cable; } - arb::util::any get_global_properties(arb::cell_kind) const override { + std::any get_global_properties(arb::cell_kind) const override { return gprop; } diff --git a/python/cells.cpp b/python/cells.cpp index cc867f6a5a368f0ec7efe9ebebea88843b9503e3..6f1b67f1f227376ff2e1dbc89738bd640fe4f96e 100644 --- a/python/cells.cpp +++ b/python/cells.cpp @@ -1,3 +1,12 @@ +#include <algorithm> +#include <any> +#include <cstddef> +#include <optional> +#include <string> +#include <unordered_map> +#include <utility> +#include <vector> + #include <pybind11/pybind11.h> #include <pybind11/stl.h> @@ -10,7 +19,8 @@ #include <arbor/morph/segment_tree.hpp> #include <arbor/schedule.hpp> #include <arbor/spike_source_cell.hpp> -#include <arbor/util/any.hpp> +#include <arbor/util/any_cast.hpp> +#include <arbor/util/optional.hpp> #include <arbor/util/unique_any.hpp> #include "cells.hpp" @@ -20,6 +30,8 @@ #include "schedule.hpp" #include "strprintf.hpp" +using arb::util::any_cast; + namespace pyarb { // Convert a cell description inside a Python object to a cell @@ -104,12 +116,12 @@ struct label_dict_proxy { throw std::string(result.error().message); } else if (result->type()==typeid(arb::region)) { // describes a region. - dict.set(name, std::move(arb::util::any_cast<arb::region&>(*result))); + dict.set(name, std::move(any_cast<arb::region&>(*result))); auto it = std::lower_bound(regions.begin(), regions.end(), name); if (it==regions.end() || *it!=name) regions.insert(it, name); } else if (result->type()==typeid(arb::locset)) { // describes a locset. - dict.set(name, std::move(arb::util::any_cast<arb::locset&>(*result))); + dict.set(name, std::move(any_cast<arb::locset&>(*result))); auto it = std::lower_bound(locsets.begin(), locsets.end(), name); if (it==locsets.end() || *it!=name) locsets.insert(it, name); } diff --git a/python/flat_cell_builder.cpp b/python/flat_cell_builder.cpp index 56a0c65e0508b48f2777c8af50a2d296e41b285c..2a2a3ca654b06ea4a5cbab72711910a7c953f30d 100644 --- a/python/flat_cell_builder.cpp +++ b/python/flat_cell_builder.cpp @@ -1,3 +1,9 @@ +#include <mutex> +#include <string> +#include <unordered_map> +#include <utility> +#include <vector> + #include <pybind11/pybind11.h> #include <pybind11/stl.h> @@ -5,6 +11,7 @@ #include <arbor/morph/morphology.hpp> #include <arbor/morph/primitives.hpp> #include <arbor/morph/segment_tree.hpp> +#include <arbor/util/any_cast.hpp> #include "conversion.hpp" #include "error.hpp" @@ -12,6 +19,8 @@ #include "s_expr.hpp" #include "strprintf.hpp" +using arb::util::any_cast; + namespace pyarb { class flat_cell_builder { @@ -96,7 +105,7 @@ public: if (dict_.locset(name)) { throw pyarb_error("Region name clashes with a locset."); } - auto& reg = arb::util::any_cast<arb::region&>(*result); + auto& reg = any_cast<arb::region&>(*result); if (auto r = dict_.region(name)) { dict_.set(name, join(std::move(reg), std::move(*r))); } @@ -108,7 +117,7 @@ public: if (dict_.region(name)) { throw pyarb_error("Locset name clashes with a region."); } - auto& loc = arb::util::any_cast<arb::locset&>(*result); + auto& loc = any_cast<arb::locset&>(*result); if (auto l = dict_.locset(name)) { dict_.set(name, sum(std::move(loc), std::move(*l))); } diff --git a/python/morph_parse.cpp b/python/morph_parse.cpp index 2df63fc59bde458b682b8f923fa2a5194b69db47..33caa33eac9a331055b27e229b4c9091194f9e91 100644 --- a/python/morph_parse.cpp +++ b/python/morph_parse.cpp @@ -1,12 +1,15 @@ -#include <arbor/util/any.hpp> +#include <any> +#include <limits> + #include <arbor/morph/region.hpp> #include <arbor/morph/locset.hpp> -#include <limits> #include "error.hpp" #include "s_expr.hpp" #include "morph_parse.hpp" +using std::any_cast; + namespace pyarb { struct nil_tag {}; @@ -32,40 +35,40 @@ bool match<arb::locset>(const std::type_info& info) { } template <typename T> -T eval_cast(arb::util::any arg) { - return std::move(arb::util::any_cast<T&>(arg)); +T eval_cast(std::any arg) { + return std::move(any_cast<T&>(arg)); } template <> -double eval_cast<double>(arb::util::any arg) { - if (arg.type()==typeid(int)) return arb::util::any_cast<int>(arg); - return arb::util::any_cast<double>(arg); +double eval_cast<double>(std::any arg) { + if (arg.type()==typeid(int)) return any_cast<int>(arg); + return any_cast<double>(arg); } template <> -arb::region eval_cast<arb::region>(arb::util::any arg) { - if (arg.type()==typeid(arb::region)) return arb::util::any_cast<arb::region>(arg); +arb::region eval_cast<arb::region>(std::any arg) { + if (arg.type()==typeid(arb::region)) return any_cast<arb::region>(arg); return arb::reg::nil(); } template <> -arb::locset eval_cast<arb::locset>(arb::util::any arg) { - if (arg.type()==typeid(arb::locset)) return arb::util::any_cast<arb::locset>(arg); +arb::locset eval_cast<arb::locset>(std::any arg) { + if (arg.type()==typeid(arb::locset)) return any_cast<arb::locset>(arg); return arb::ls::nil(); } template <typename... Args> struct call_eval { - using ftype = std::function<arb::util::any(Args...)>; + using ftype = std::function<std::any(Args...)>; ftype f; call_eval(ftype f): f(std::move(f)) {} template<std::size_t... I> - arb::util::any expand_args_then_eval(std::vector<arb::util::any> args, std::index_sequence<I...>) { + std::any expand_args_then_eval(std::vector<std::any> args, std::index_sequence<I...>) { return f(eval_cast<Args>(std::move(args[I]))...); } - arb::util::any operator()(std::vector<arb::util::any> args) { + std::any operator()(std::vector<std::any> args) { return expand_args_then_eval(std::move(args), std::make_index_sequence<sizeof...(Args)>()); } }; @@ -73,21 +76,21 @@ struct call_eval { template <typename... Args> struct call_match { template <std::size_t I, typename T, typename Q, typename... Rest> - bool match_args_impl(const std::vector<arb::util::any>& args) const { + bool match_args_impl(const std::vector<std::any>& args) const { return match<T>(args[I].type()) && match_args_impl<I+1, Q, Rest...>(args); } template <std::size_t I, typename T> - bool match_args_impl(const std::vector<arb::util::any>& args) const { + bool match_args_impl(const std::vector<std::any>& args) const { return match<T>(args[I].type()); } template <std::size_t I> - bool match_args_impl(const std::vector<arb::util::any>& args) const { + bool match_args_impl(const std::vector<std::any>& args) const { return true; } - bool operator()(const std::vector<arb::util::any>& args) const { + bool operator()(const std::vector<std::any>& args) const { const auto nargs_in = args.size(); const auto nargs_ex = sizeof...(Args); return nargs_in==nargs_ex? match_args_impl<0, Args...>(args): false; @@ -99,7 +102,7 @@ struct fold_eval { using fold_fn = std::function<T(T, T)>; fold_fn f; - using anyvec = std::vector<arb::util::any>; + using anyvec = std::vector<std::any>; using iterator = anyvec::iterator; fold_eval(fold_fn f): f(std::move(f)) {} @@ -111,14 +114,14 @@ struct fold_eval { return f(eval_cast<T>(std::move(*left)), fold_impl(left+1, right)); } - arb::util::any operator()(anyvec args) { + std::any operator()(anyvec args) { return fold_impl(args.begin(), args.end()); } }; template <typename T> struct fold_match { - using anyvec = std::vector<arb::util::any>; + using anyvec = std::vector<std::any>; bool operator()(const anyvec& args) const { if (args.size()<2u) return false; for (auto& a: args) { @@ -129,8 +132,8 @@ struct fold_match { }; struct evaluator { - using any_vec = std::vector<arb::util::any>; - using eval_fn = std::function<arb::util::any(any_vec)>; + using any_vec = std::vector<std::any>; + using eval_fn = std::function<std::any(any_vec)>; using args_fn = std::function<bool(const any_vec&)>; eval_fn eval; @@ -245,12 +248,12 @@ std::unordered_multimap<std::string, evaluator> eval_map { "'sum' with at least 2 arguments: (locset locset [...locset])")}, }; -parse_hopefully<arb::util::any> eval(const s_expr& e); +parse_hopefully<std::any> eval(const s_expr& e); -parse_hopefully<std::vector<arb::util::any>> eval_args(const s_expr& e) { - if (!e) return {std::vector<arb::util::any>{}}; // empty argument list +parse_hopefully<std::vector<std::any>> eval_args(const s_expr& e) { + if (!e) return {std::vector<std::any>{}}; // empty argument list const s_expr* h = &e; - std::vector<arb::util::any> args; + std::vector<std::any> args; while (*h) { auto arg = eval(h->head()); if (!arg) return std::move(arg.error()); @@ -267,7 +270,7 @@ parse_hopefully<std::vector<arb::util::any>> eval_args(const s_expr& e) { // 'cat' with 3 arguments: (locset region integer) // Where 'foo', 'bar' and 'cat' are the name of the function, and the // types (integer, real, region, locset) are inferred from the arguments. -std::string eval_description(const char* name, const std::vector<arb::util::any>& args) { +std::string eval_description(const char* name, const std::vector<std::any>& args) { auto type_string = [](const std::type_info& t) -> const char* { if (t==typeid(int)) return "integer"; if (t==typeid(double)) return "real"; @@ -305,7 +308,7 @@ std::string eval_description(const char* name, const std::vector<arb::util::any> // a parse_error_state with an error string and location. // // If there was an unexpected/fatal error, an exception will be thrown. -parse_hopefully<arb::util::any> eval(const s_expr& e) { +parse_hopefully<std::any> eval(const s_expr& e) { if (e.is_atom()) { auto& t = e.atom(); switch (t.kind) { diff --git a/python/morph_parse.hpp b/python/morph_parse.hpp index cb78cb57ee20f9f688e460201b70c44f3899d8f9..84dd1ba30645ddb531b98a093b69cb496e094ee0 100644 --- a/python/morph_parse.hpp +++ b/python/morph_parse.hpp @@ -1,10 +1,10 @@ #pragma once +#include <any> + #include "error.hpp" #include "s_expr.hpp" -#include <arbor/util/any.hpp> - namespace pyarb { struct parse_error_state { @@ -15,6 +15,6 @@ struct parse_error_state { template <typename T> using parse_hopefully = hopefully<T, parse_error_state>; -parse_hopefully<arb::util::any> eval(const s_expr&); +parse_hopefully<std::any> eval(const s_expr&); } // namespace pyarb diff --git a/python/recipe.hpp b/python/recipe.hpp index 51aa070ef4cafe07ff74881ed737021acd94c48e..bf421aad80948f5e4a28c39e4071deab5fc4d3b6 100644 --- a/python/recipe.hpp +++ b/python/recipe.hpp @@ -20,7 +20,7 @@ arb::probe_info cable_probe(std::string kind, arb::cell_member_type id, arb::mlo // pyarb::recipe is the recipe interface used by Python. // Calls that return generic types return pybind11::object, to avoid // having to wrap some C++ types used by the C++ interface (specifically -// util::unique_any, util::any, std::unique_ptr, etc.) +// util::unique_any, std::any, std::unique_ptr, etc.) // For example, requests for cell description return pybind11::object, instead // of util::unique_any used by the C++ recipe interface. // The py_recipe_shim unwraps the python objects, and forwards them @@ -158,13 +158,13 @@ public: } // TODO: make thread safe - arb::util::any get_global_properties(arb::cell_kind kind) const override { + std::any get_global_properties(arb::cell_kind kind) const override { if (kind==arb::cell_kind::cable) { arb::cable_cell_global_properties gprop; gprop.default_parameters = arb::neuron_parameter_defaults; return gprop; } - return arb::util::any{}; + return std::any{}; } }; diff --git a/python/s_expr.cpp b/python/s_expr.cpp index 6360713c3813b302662df47b0c69bef0fad482b7..e9db04781fb1d179c9b4d48f1fb4e7e9967af3cb 100644 --- a/python/s_expr.cpp +++ b/python/s_expr.cpp @@ -3,10 +3,10 @@ #include <string> #include <memory> #include <ostream> +#include <variant> #include <vector> #include <arbor/arbexcept.hpp> -#include <arbor/util/variant.hpp> #include "s_expr.hpp" #include "strprintf.hpp" @@ -300,23 +300,23 @@ bool s_expr::is_atom() const { } const token& s_expr::atom() const { - return state.get<0>(); + return std::get<0>(state); } const s_expr& s_expr::head() const { - return state.get<1>().head.get(); + return std::get<1>(state).head.get(); } const s_expr& s_expr::tail() const { - return state.get<1>().tail.get(); + return std::get<1>(state).tail.get(); } s_expr& s_expr::head() { - return state.get<1>().head.get(); + return std::get<1>(state).head.get(); } s_expr& s_expr::tail() { - return state.get<1>().tail.get(); + return std::get<1>(state).tail.get(); } s_expr::operator bool() const { diff --git a/python/s_expr.hpp b/python/s_expr.hpp index bf6998a15ba413ccc5767e863fc571717624ef93..8b041d56a3d580c1952d90218ed81364c225de1b 100644 --- a/python/s_expr.hpp +++ b/python/s_expr.hpp @@ -2,10 +2,9 @@ #include <string> #include <memory> +#include <variant> #include <vector> -#include <arbor/util/variant.hpp> - namespace pyarb { enum class tok { @@ -84,12 +83,12 @@ struct s_expr { // An s_expr can be one of // 1. an atom // 2. a pair of s_expr (head and tail) - // The s_expr uses a util::variant to represent these two possible states, + // The s_expr uses std::variant to represent these two possible states, // which requires using an incomplete definition of s_expr, which is stored // using a std::unique_ptr. using pair_type = s_pair<value_wrapper<s_expr>>; - arb::util::variant<token, pair_type> state = token{-1, tok::nil, "nil"}; + std::variant<token, pair_type> state = token{-1, tok::nil, "nil"}; s_expr(const s_expr& s): state(s.state) {} s_expr() = default; diff --git a/python/single_cell_model.cpp b/python/single_cell_model.cpp index 4e406d5afe6044cdec08da8a4e7ad08982a901fb..9c12ed4ce7cc7bf74913fbe09792b95fd41c4a23 100644 --- a/python/single_cell_model.cpp +++ b/python/single_cell_model.cpp @@ -1,4 +1,7 @@ +#include <any> +#include <memory> #include <string> +#include <utility> #include <vector> #include <pybind11/pybind11.h> @@ -8,10 +11,13 @@ #include <arbor/load_balance.hpp> #include <arbor/recipe.hpp> #include <arbor/simulation.hpp> +#include <arbor/util/any_cast.hpp> #include "error.hpp" #include "cells.hpp" +using arb::util::any_cast; + namespace pyarb { // @@ -45,7 +51,7 @@ struct trace_callback { void operator()(arb::probe_metadata, std::size_t n, const arb::sample_record* recs) { // Push each (time, value) pair from the last epoch into trace_. for (std::size_t i=0; i<n; ++i) { - if (auto p = arb::util::any_cast<const double*>(recs[i].data)) { + if (auto p = any_cast<const double*>(recs[i].data)) { trace_.t.push_back(recs[i].time); trace_.v.push_back(*p); } @@ -125,7 +131,7 @@ struct single_cell_recipe: arb::recipe { return {}; // No gap junctions on a single cell model. } - virtual arb::util::any get_global_properties(arb::cell_kind) const override { + virtual std::any get_global_properties(arb::cell_kind) const override { return gprop_; } }; diff --git a/python/test/cpp/s_expr.cpp b/python/test/cpp/s_expr.cpp index 62300624a8f7c0f2ac8576fc1a59fc62afb29603..ced25e9ef2d81c29fd39ad57d4ae4141dd6ecc70 100644 --- a/python/test/cpp/s_expr.cpp +++ b/python/test/cpp/s_expr.cpp @@ -1,15 +1,20 @@ #include "gtest.h" +#include <string> + #include <arbor/morph/region.hpp> #include <arbor/morph/locset.hpp> +#include <arbor/util/any_cast.hpp> -#include <morph_parse.hpp> -#include <s_expr.hpp> -#include <strprintf.hpp> +#include "morph_parse.hpp" +#include "s_expr.hpp" +#include "strprintf.hpp" using namespace pyarb; using namespace std::string_literals; +using arb::util::any_cast; + TEST(s_expr, identifier) { EXPECT_TRUE(test_identifier("foo")); EXPECT_TRUE(test_identifier("f1")); @@ -70,11 +75,11 @@ TEST(s_expr, atoms) { TEST(s_expr, parse) { auto round_trip_reg = [](const char* in) { auto x = eval(parse(in)); - return util::pprintf("{}", arb::util::any_cast<arb::region>(*x)); + return util::pprintf("{}", any_cast<arb::region>(*x)); }; auto round_trip_loc = [](const char* in) { auto x = eval(parse(in)); - return util::pprintf("{}", arb::util::any_cast<arb::locset>(*x)); + return util::pprintf("{}", any_cast<arb::locset>(*x)); }; EXPECT_EQ("(cable 3 0 1)", round_trip_reg("(branch 3)")); @@ -86,8 +91,8 @@ TEST(s_expr, parse) { EXPECT_EQ("(root)", round_trip_loc("(root)")); EXPECT_EQ("(locset \"cat_burgler\")", round_trip_loc("(locset \"cat_burgler\")")); - auto lhs = arb::util::any_cast<arb::region>(*eval(parse("(region \"dend\")"))); - auto rhs = arb::util::any_cast<arb::region>(*eval(parse("(all)"))); + auto lhs = any_cast<arb::region>(*eval(parse("(region \"dend\")"))); + auto rhs = any_cast<arb::region>(*eval(parse("(all)"))); EXPECT_EQ(util::pprintf("{}", join(lhs,rhs)), "(join (region \"dend\") (all))"); } @@ -95,7 +100,7 @@ TEST(s_expr, parse) { TEST(s_expr, comments) { auto round_trip_reg = [](const char* in) { auto x = eval(parse(in)); - return util::pprintf("{}", arb::util::any_cast<arb::region>(*x)); + return util::pprintf("{}", any_cast<arb::region>(*x)); }; EXPECT_EQ("(all)", round_trip_reg("(all) ; a comment")); diff --git a/test/simple_recipes.hpp b/test/simple_recipes.hpp index 24051eb005951838cac5e424f5d377563905ac47..9a1c1dad7d324d8792b8cc78935ca3881043b7ce 100644 --- a/test/simple_recipes.hpp +++ b/test/simple_recipes.hpp @@ -2,6 +2,7 @@ // Simple recipe classes for use in unit and validation tests. +#include <any> #include <unordered_map> #include <vector> @@ -9,6 +10,7 @@ #include <arbor/cable_cell.hpp> #include <arbor/cable_cell_param.hpp> #include <arbor/recipe.hpp> +#include <arbor/util/unique_any.hpp> namespace arb { @@ -29,16 +31,16 @@ public: return probes_.at(i); } - virtual void add_probe(cell_gid_type gid, probe_tag tag, util::any address) { + virtual void add_probe(cell_gid_type gid, probe_tag tag, std::any address) { probes_[gid].emplace_back(std::move(address), tag); } - util::any get_global_properties(cell_kind k) const override { + std::any get_global_properties(cell_kind k) const override { switch (k) { case cell_kind::cable: return cell_gprop_; default: - return util::any{}; + return std::any{}; } } diff --git a/test/ubench/CMakeLists.txt b/test/ubench/CMakeLists.txt index e37f955e33f5d870f9813866839a6a629b828ced..381a8ffa0ca8d88884d36bfbd8fae472686bcb46 100644 --- a/test/ubench/CMakeLists.txt +++ b/test/ubench/CMakeLists.txt @@ -7,8 +7,8 @@ set(bench_sources default_construct.cpp event_setup.cpp event_binning.cpp - fvm_discretize.cpp - mech_vec.cpp + # fvm_discretize.cpp + # mech_vec.cpp task_system.cpp ) diff --git a/test/ubench/mech_vec.cpp b/test/ubench/mech_vec.cpp index 00074224969deee7f1eb541672789b1c6306ebda..ed45e38f5c50d342b81678d9f5af1975b2611d9a 100644 --- a/test/ubench/mech_vec.cpp +++ b/test/ubench/mech_vec.cpp @@ -5,6 +5,7 @@ // NOTE: This targets an earlier version of the Arbor API and // will need to be reworked in order to compile. +#include <any> #include <fstream> #include <arbor/cable_cell.hpp> @@ -80,7 +81,7 @@ public: return cell_kind::cable; } - arb::util::any get_global_properties(arb::cell_kind) const override { + std::any get_global_properties(arb::cell_kind) const override { return gprop_; } }; @@ -123,7 +124,7 @@ public: return cell_kind::cable; } - arb::util::any get_global_properties(arb::cell_kind) const override { + std::any get_global_properties(arb::cell_kind) const override { return gprop_; } }; @@ -168,7 +169,7 @@ public: return cell_kind::cable; } - arb::util::any get_global_properties(arb::cell_kind) const override { + std::any get_global_properties(arb::cell_kind) const override { return gprop_; } }; @@ -211,7 +212,7 @@ public: return cell_kind::cable; } - arb::util::any get_global_properties(arb::cell_kind) const override { + std::any get_global_properties(arb::cell_kind) const override { return gprop_; } }; @@ -256,7 +257,7 @@ public: return cell_kind::cable; } - arb::util::any get_global_properties(arb::cell_kind) const override { + std::any get_global_properties(arb::cell_kind) const override { return gprop_; } }; diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 43d4eda8dc5c15450f0a77b2d781ffdb01900b6c..cd9ee32d9f9d2c1080016ff0b3018573261f30e0 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -84,7 +84,8 @@ endforeach() set(unit_sources ../common_cells.cpp test_algorithms.cpp - test_any.cpp + test_any_cast.cpp + test_any_ptr.cpp test_any_visitor.cpp test_backend.cpp test_counter.cpp @@ -159,7 +160,6 @@ set(unit_sources test_uninitialized.cpp test_unique.cpp test_unique_any.cpp - test_variant.cpp test_vector.cpp test_version.cpp diff --git a/test/unit/test_any.cpp b/test/unit/test_any.cpp deleted file mode 100644 index 5085bb1c3a545037cfb0bf78282008d2906ff522..0000000000000000000000000000000000000000 --- a/test/unit/test_any.cpp +++ /dev/null @@ -1,445 +0,0 @@ -#include <string> -#include <type_traits> -#include <typeinfo> - -#include <arbor/util/any.hpp> -#include <arbor/util/any_ptr.hpp> - -#include "../gtest.h" -#include "common.hpp" - -using namespace std::string_literals; -using namespace arb; - -TEST(any, copy_construction) { - util::any any_int(2); - EXPECT_EQ(any_int.type(), typeid(int)); - - util::any any_float(2.0f); - EXPECT_EQ(any_float.type(), typeid(float)); - - std::string str = "hello"; - util::any any_string(str); - EXPECT_EQ(any_string.type(), typeid(std::string)); -} - -namespace { - - struct moveable { - moveable() = default; - - moveable(moveable&& other): - moves(other.moves+1), copies(other.copies) - {} - - moveable(const moveable& other): - moves(other.moves), copies(other.copies+1) - {} - - int moves=0; - int copies=0; - }; - -} - -TEST(any, move_construction) { - moveable m; - - util::any copied(m); - util::any moved(std::move(m)); - - // Check that the expected number of copies and moves were performed. - // Use cast to reference to avoid copy or move of contained object. - const auto& cref = util::any_cast<moveable&>(copied); - EXPECT_EQ(cref.moves, 0); - EXPECT_EQ(cref.copies, 1); - - const auto& mref = util::any_cast<moveable&>(moved); - EXPECT_EQ(mref.moves, 1); - EXPECT_EQ(mref.copies, 0); - - // construction by any&& should not make any copies or moves of the - // constructed value - util::any fin(std::move(moved)); - EXPECT_FALSE(moved.has_value()); // moved has been moved from and should be empty - const auto& fref = util::any_cast<moveable&>(fin); - EXPECT_EQ(fref.moves, 1); - EXPECT_EQ(fref.copies, 0); - - const auto value = util::any_cast<moveable>(fin); - EXPECT_EQ(value.moves, 1); - EXPECT_EQ(value.copies, 1); -} - -TEST(any, type) { - using util::any; - - any anyi(42); - any anys("hello"s); - any anyv(std::vector<int>{1, 2, 3}); - any any0; - - EXPECT_EQ(typeid(int), anyi.type()); - EXPECT_EQ(typeid(std::string), anys.type()); - EXPECT_EQ(typeid(std::vector<int>), anyv.type()); - EXPECT_EQ(typeid(void), any0.type()); - - anyi.reset(); - EXPECT_EQ(typeid(void), anyi.type()); - - anyi = std::true_type(); - EXPECT_EQ(typeid(std::true_type), anyi.type()); -} - -TEST(any, swap) { - using util::any; - using util::any_cast; - - any any1(42); // integer - any any2(3.14); // double - - EXPECT_EQ(typeid(int), any1.type()); - EXPECT_EQ(typeid(double), any2.type()); - - any1.swap(any2); - - EXPECT_EQ(any_cast<int>(any2), 42); - EXPECT_EQ(any_cast<double>(any1), 3.14); - - EXPECT_EQ(typeid(double), any1.type()); - EXPECT_EQ(typeid(int), any2.type()); - - any1.swap(any2); - - EXPECT_EQ(any_cast<double>(any2), 3.14); - EXPECT_EQ(any_cast<int>(any1), 42); - - EXPECT_EQ(typeid(int), any1.type()); - EXPECT_EQ(typeid(double), any2.type()); -} - -// These should fail at compile time if the constraint that the contents of any -// satisfy CopyConstructable. This implementation is rock solid, so they have -// to be commented out. -/* -TEST(any, not_copy_constructable) { - util::any a(testing::nocopy<int>(3)); - - testing::nocopy<int> x(3); - util::any b(std::move(x)); -} -*/ - -// test any_cast(any*) -// - these have different behavior to any_cast on reference types -// - are used by the any_cast on reference types -TEST(any, any_cast_ptr) { - - // test that valid pointers are returned for int and std::string types - - util::any ai(42); - auto ptr_i = util::any_cast<int>(&ai); - EXPECT_EQ(*ptr_i, 42); - - util::any as("hello"s); - auto ptr_s = util::any_cast<std::string>(&as); - EXPECT_EQ(*ptr_s, "hello"); - - // test that exceptions are thrown for invalid casts - EXPECT_EQ(util::any_cast<int>(&as), nullptr); - EXPECT_EQ(util::any_cast<std::string>(&ai), nullptr); - util::any empty; - EXPECT_EQ(util::any_cast<int>(&empty), nullptr); - EXPECT_EQ(util::any_cast<int>((util::any*)nullptr), nullptr); - - // Check that constness of the returned pointer matches that the input. - { - util::any a(42); - auto p = util::any_cast<int>(&a); - - // any_cast(any*) should not return const* - EXPECT_TRUE((std::is_same<int*, decltype(p)>::value)); - } - { - const util::any a(42); - auto p = util::any_cast<int>(&a); - - // any_cast(const any*) should return const* - EXPECT_TRUE((std::is_same<const int*, decltype(p)>::value)); - } -} - -TEST(any, any_cast_ref) { - { - util::any a(42); - auto i = util::any_cast<int>(a); - EXPECT_EQ(typeid(i), typeid(int)); - EXPECT_EQ(i, 42); - - // check that we can take a reference to the - // underlying storage - auto& r = util::any_cast<int&>(a); - r = 3; - EXPECT_EQ(3, util::any_cast<int>(a)); - EXPECT_TRUE((std::is_same<int&, decltype(r)>::value)); - } - - { // check that const references can be returned to const objects - const util::any a(42); - auto& r = util::any_cast<const int&>(a); - EXPECT_TRUE((std::is_same<const int&, decltype(r)>::value)); - - // The following should fail with a static assertion if uncommented, because - // it requests a non-const reference to a const object. - //auto& instafail = util::any_cast<int&>(a); - } -} - -// test any_cast(any&&) -TEST(any, any_cast_rvalue) { - auto moved = util::any_cast<moveable>(util::any(moveable())); - EXPECT_EQ(moved.moves, 2); - EXPECT_EQ(moved.copies, 0); -} - -TEST(any, std_swap) { - util::any a1(42); - util::any a2(3.14); - - auto pi = util::any_cast<int>(&a1); - auto pd = util::any_cast<double>(&a2); - - std::swap(a1, a2); - - // test that values were swapped - EXPECT_EQ(util::any_cast<int>(a2), 42); - EXPECT_EQ(util::any_cast<double>(a1), 3.14); - - // test that underlying pointers did not change - EXPECT_EQ(pi, util::any_cast<int>(&a2)); - EXPECT_EQ(pd, util::any_cast<double>(&a1)); -} - -// test operator=(const any&) -TEST(any, assignment_from_lvalue) { - using std::string; - - auto str1 = string("one"); - auto str2 = string("two"); - util::any a(str1); - - util::any b; - b = a; // copy assignment - - // verify that b contains value stored in a - EXPECT_EQ(str1, util::any_cast<string>(b)); - - // change the value stored in b - *util::any_cast<string>(&b) = str2; - - // verify that a is unchanged and that b holds new value - EXPECT_EQ(str1, util::any_cast<string>(a)); - EXPECT_EQ(str2, util::any_cast<string>(b)); -} - -// test operator=(any&&) -TEST(any, assignment_from_rvalue) { - using std::string; - - auto str1 = string("one"); - auto str2 = string("two"); - util::any a(str1); - - util::any b; - b = std::move(a); // move assignment - - EXPECT_EQ(str1, util::any_cast<string>(b)); - - EXPECT_EQ(nullptr, util::any_cast<string>(&a)); -} - -// test template<typename T> operator=(T&&) -TEST(any, assignment_from_value) { - std::vector<int> tmp{1, 2, 3}; - - // take a pointer to the orignal data to later verify - // that the value was moved, and not copied. - auto ptr = tmp.data(); - - util::any a; - a = std::move(tmp); - - auto vec = util::any_cast<std::vector<int>>(&a); - - // ensure the value was moved - EXPECT_EQ(ptr, vec->data()); - - // ensure that the contents of the vector are unchanged - std::vector<int> ref{1, 2, 3}; - EXPECT_EQ(ref, *vec); -} - -TEST(any, make_any) { - using util::make_any; - using util::any_cast; - - { - auto a = make_any<int>(42); - - EXPECT_EQ(typeid(int), a.type()); - EXPECT_EQ(42, any_cast<int>(a)); - } - - // check casting - { - auto a = make_any<double>(42u); - - EXPECT_EQ(typeid(double), a.type()); - EXPECT_EQ(42.0, any_cast<double>(a)); - } - - // check forwarding of parameters to constructor - { - // create a string from const char* - auto a = make_any<std::string>("hello"); - - EXPECT_EQ(any_cast<std::string>(a), "hello"s); - } - - // test that we make_any correctly forwards rvalue arguments to the constructor - // of the contained object. - { - std::vector<int> tmp{1, 2, 3}; - - // take a pointer to the orignal data to later verify - // that the value was moved, and not copied. - auto ptr = tmp.data(); - - auto a = make_any<std::vector<int>>(std::move(tmp)); - - auto vec = any_cast<std::vector<int>>(&a); - - // ensure the value was moved - EXPECT_EQ(ptr, vec->data()); - - // ensure that the contents of the vector are unchanged - std::vector<int> ref{1, 2, 3}; - EXPECT_EQ(ref, *vec); - } -} - -// Tests below are for `any_ptr` class. - -TEST(any_ptr, ctor_and_assign) { - using util::any_ptr; - - any_ptr p; - - EXPECT_FALSE(p); - EXPECT_FALSE(p.has_value()); - - int x; - any_ptr q(&x); - - EXPECT_TRUE(q); - EXPECT_TRUE(q.has_value()); - - p = q; - - EXPECT_TRUE(p); - EXPECT_TRUE(p.has_value()); - - p = nullptr; - - EXPECT_FALSE(p); - EXPECT_FALSE(p.has_value()); - - p = &x; - - EXPECT_TRUE(p); - EXPECT_TRUE(p.has_value()); - - p.reset(); - - EXPECT_FALSE(p); - EXPECT_FALSE(p.has_value()); - - p.reset(&x); - - EXPECT_TRUE(p); - EXPECT_TRUE(p.has_value()); - - p.reset(nullptr); - - EXPECT_FALSE(p); - EXPECT_FALSE(p.has_value()); - - p = nullptr; - - EXPECT_FALSE(p); - EXPECT_FALSE(p.has_value()); -} - - -TEST(any_ptr, ordering) { - using util::any_ptr; - - int x[2]; - double y; - - any_ptr a(&x[0]); - any_ptr b(&x[1]); - - EXPECT_LT(a, b); - EXPECT_LE(a, b); - EXPECT_NE(a, b); - EXPECT_GE(b, a); - EXPECT_GT(b, a); - EXPECT_FALSE(a==b); - - any_ptr c(&y); - - EXPECT_NE(c, a); - EXPECT_TRUE(a<c || a>c); - EXPECT_FALSE(a==c); -} - -TEST(any_ptr, as_and_type) { - using util::any_ptr; - - int x = 0; - const int y = 0; - any_ptr p; - - EXPECT_FALSE(p.as<int*>()); - - p = &y; - EXPECT_FALSE(p.as<int*>()); - EXPECT_TRUE(p.as<const int*>()); - EXPECT_EQ(typeid(const int*), p.type()); - - p = &x; - EXPECT_TRUE(p.as<int*>()); - EXPECT_FALSE(p.as<const int*>()); - EXPECT_EQ(typeid(int*), p.type()); - - *p.as<int*>() = 3; - EXPECT_EQ(3, x); -} - -TEST(any_ptr, any_cast) { - using util::any_ptr; - using util::any_cast; - - int x = 0; - any_ptr p; - - auto c1 = any_cast<int*>(p); - EXPECT_FALSE(c1); - EXPECT_TRUE((std::is_same<int*, decltype(c1)>::value)); - - p = &x; - auto c2 = any_cast<int*>(p); - EXPECT_TRUE(c2); -} - diff --git a/test/unit/test_any_cast.cpp b/test/unit/test_any_cast.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0d6bbc233a01a49f4c854679e6acf0c046d9bcb3 --- /dev/null +++ b/test/unit/test_any_cast.cpp @@ -0,0 +1,52 @@ +#include <any> +#include <type_traits> + +#include <arbor/util/any_cast.hpp> + +#include "../gtest.h" + +using namespace arb; + +// Check std::any_cast follows std::any_cast semantics +// when applied to std::any objects. + +#define ASSERT_ANY_CAST_SAME_TYPE(type, value)\ +ASSERT_TRUE((std::is_same_v<decltype(std::any_cast<type>(value)), decltype(util::any_cast<type>(value))>)) + +TEST(any_cast, pointer) { + std::any ai(42); + + ASSERT_ANY_CAST_SAME_TYPE(int, &ai); + ASSERT_TRUE(util::any_cast<int>(&ai)); + EXPECT_EQ(42, *util::any_cast<int>(&ai)); + + ASSERT_ANY_CAST_SAME_TYPE(const int, &ai); + ASSERT_TRUE(util::any_cast<const int>(&ai)); + EXPECT_EQ(42, *util::any_cast<const int>(&ai)); + + const auto& cai = ai; + ASSERT_ANY_CAST_SAME_TYPE(int, &cai); + ASSERT_TRUE(util::any_cast<int>(&cai)); + EXPECT_EQ(42, *util::any_cast<int>(&cai)); + + // Assert type mismatch or empty any gives nullptr. + + std::any empty; + EXPECT_EQ(util::any_cast<int>(&empty), nullptr); + EXPECT_EQ(util::any_cast<float>(&ai), nullptr); + EXPECT_EQ(util::any_cast<int>((std::any*)nullptr), nullptr); +} + +TEST(any_cast, ref_and_value) { + std::any ai(42); + + ASSERT_ANY_CAST_SAME_TYPE(int, ai); + ASSERT_ANY_CAST_SAME_TYPE(int&, ai); + ASSERT_ANY_CAST_SAME_TYPE(const int&, ai); + ASSERT_ANY_CAST_SAME_TYPE(int&&, std::move(ai)); + + EXPECT_EQ(42, util::any_cast<int>(ai)); + EXPECT_EQ(42, util::any_cast<int&>(ai)); + EXPECT_EQ(42, util::any_cast<const int&>(ai)); + EXPECT_EQ(42, util::any_cast<int&&>(std::move(ai))); +} diff --git a/test/unit/test_any_ptr.cpp b/test/unit/test_any_ptr.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7f4fcf4498a1c8788e93b2f6bffcfb4d55015f32 --- /dev/null +++ b/test/unit/test_any_ptr.cpp @@ -0,0 +1,129 @@ +#include <any> +#include <type_traits> +#include <typeinfo> + +#include <arbor/util/any_cast.hpp> +#include <arbor/util/any_ptr.hpp> + +#include "../gtest.h" + +using namespace arb; +using util::any_cast; +using util::any_ptr; + +TEST(any_ptr, ctor_and_assign) { + using util::any_ptr; + + any_ptr p; + + EXPECT_FALSE(p); + EXPECT_FALSE(p.has_value()); + + int x; + any_ptr q(&x); + + EXPECT_TRUE(q); + EXPECT_TRUE(q.has_value()); + + p = q; + + EXPECT_TRUE(p); + EXPECT_TRUE(p.has_value()); + + p = nullptr; + + EXPECT_FALSE(p); + EXPECT_FALSE(p.has_value()); + + p = &x; + + EXPECT_TRUE(p); + EXPECT_TRUE(p.has_value()); + + p.reset(); + + EXPECT_FALSE(p); + EXPECT_FALSE(p.has_value()); + + p.reset(&x); + + EXPECT_TRUE(p); + EXPECT_TRUE(p.has_value()); + + p.reset(nullptr); + + EXPECT_FALSE(p); + EXPECT_FALSE(p.has_value()); + + p = nullptr; + + EXPECT_FALSE(p); + EXPECT_FALSE(p.has_value()); +} + +TEST(any_ptr, ordering) { + int x[2]; + double y; + + any_ptr a(&x[0]); + any_ptr b(&x[1]); + + EXPECT_LT(a, b); + EXPECT_LE(a, b); + EXPECT_NE(a, b); + EXPECT_GE(b, a); + EXPECT_GT(b, a); + EXPECT_FALSE(a==b); + + any_ptr c(&y); + + EXPECT_NE(c, a); + EXPECT_TRUE(a<c || a>c); + EXPECT_FALSE(a==c); +} + +TEST(any_ptr, as_and_type) { + int x = 0; + const int y = 0; + any_ptr p; + + EXPECT_FALSE(p.as<int*>()); + + p = &y; + EXPECT_FALSE(p.as<int*>()); + EXPECT_TRUE(p.as<const int*>()); + EXPECT_EQ(typeid(const int*), p.type()); + + p = &x; + EXPECT_TRUE(p.as<int*>()); + EXPECT_FALSE(p.as<const int*>()); + EXPECT_EQ(typeid(int*), p.type()); + + *p.as<int*>() = 3; + EXPECT_EQ(3, x); +} + +TEST(any_ptr, any_cast) { + int x = 0; + any_ptr p; + + auto c1 = any_cast<int*>(p); + EXPECT_FALSE(c1); + EXPECT_TRUE((std::is_same_v<int*, decltype(c1)>)); + + p = &x; + auto c2 = any_cast<int*>(p); + EXPECT_TRUE(c2); + + auto c3 = any_cast<double*>(p); + EXPECT_FALSE(c3); + + // Might want to reconsider these semantics, but here we are: + auto c4 = any_cast<const int*>(p); + EXPECT_FALSE(c4); + + p = (const int*)&x; + auto c5 = any_cast<int*>(p); + EXPECT_FALSE(c5); +} + diff --git a/test/unit/test_any_visitor.cpp b/test/unit/test_any_visitor.cpp index 6c22ec07b64fb930fd2cd0c8aa1e9b381d162833..242e5257417cae7fb06a1e412d64353c053256d3 100644 --- a/test/unit/test_any_visitor.cpp +++ b/test/unit/test_any_visitor.cpp @@ -1,19 +1,20 @@ +#include <any> #include <string> #include <type_traits> #include <typeinfo> -#include <arbor/util/any.hpp> #include <arbor/util/any_visitor.hpp> #include "../gtest.h" #include "common.hpp" using namespace std::string_literals; -using arb::util::any; -using arb::util::any_cast; -using arb::util::any_visitor; -using arb::util::bad_any_cast; +using std::any; +using std::any_cast; +using std::bad_any_cast; + using arb::util::overload; +using arb::util::any_visitor; TEST(any_visitor, simple) { enum { A0, B0, C0 }; diff --git a/test/unit/test_fvm_lowered.cpp b/test/unit/test_fvm_lowered.cpp index 5075872077fa91ff7ddbb80cf3571edae9b869f2..12e2b912f793051bb7d69626a97afe5d879be8c9 100644 --- a/test/unit/test_fvm_lowered.cpp +++ b/test/unit/test_fvm_lowered.cpp @@ -13,6 +13,7 @@ #include <arbor/sampling.hpp> #include <arbor/simulation.hpp> #include <arbor/schedule.hpp> +#include <arbor/util/any_ptr.hpp> #include <arborenv/concurrency.hpp> @@ -240,12 +241,12 @@ TEST(fvm_lowered, matrix_init) auto n = J.size(); auto& mat = J.state_; - EXPECT_FALSE(util::any_of(util::subrange_view(mat.u, 1, n), isnan)); - EXPECT_FALSE(util::any_of(mat.d, isnan)); - EXPECT_FALSE(util::any_of(S->voltage, isnan)); + EXPECT_FALSE(arb::util::any_of(util::subrange_view(mat.u, 1, n), isnan)); + EXPECT_FALSE(arb::util::any_of(mat.d, isnan)); + EXPECT_FALSE(arb::util::any_of(S->voltage, isnan)); - EXPECT_FALSE(util::any_of(util::subrange_view(mat.u, 1, n), ispos)); - EXPECT_FALSE(util::any_of(mat.d, isneg)); + EXPECT_FALSE(arb::util::any_of(util::subrange_view(mat.u, 1, n), ispos)); + EXPECT_FALSE(arb::util::any_of(mat.d, isneg)); } TEST(fvm_lowered, target_handles) { diff --git a/test/unit/test_probe.cpp b/test/unit/test_probe.cpp index 7b363f2adbb6c81be5aa6fd6d1847d1b74da8723..3c11796542bd78adfce5d81610bc4c9dfa327845 100644 --- a/test/unit/test_probe.cpp +++ b/test/unit/test_probe.cpp @@ -13,6 +13,7 @@ #include <arbor/schedule.hpp> #include <arbor/simple_sampler.hpp> #include <arbor/simulation.hpp> +#include <arbor/util/any_cast.hpp> #include <arbor/util/any_ptr.hpp> #include <arbor/util/pp_util.hpp> #include <arbor/version.hpp> @@ -37,6 +38,7 @@ #include "../simple_recipes.hpp" using namespace arb; +using util::any_cast; using multicore_fvm_cell = fvm_lowered_cell_impl<multicore::backend>; using multicore_shared_state = multicore::backend::shared_state; @@ -142,9 +144,9 @@ void run_v_i_probe_test(const context& ctx) { // to wrap an fvm_probe_interpolated; ion current density is // a scalar, so should wrap fvm_probe_scalar. - ASSERT_TRUE(util::get_if<fvm_probe_interpolated>(probe_map.data_on({0, 0}).front().info)); - ASSERT_TRUE(util::get_if<fvm_probe_interpolated>(probe_map.data_on({0, 0}).front().info)); - ASSERT_TRUE(util::get_if<fvm_probe_scalar>(probe_map.data_on({0, 2}).front().info)); + ASSERT_TRUE(std::get_if<fvm_probe_interpolated>(&probe_map.data_on({0, 0}).front().info)); + ASSERT_TRUE(std::get_if<fvm_probe_interpolated>(&probe_map.data_on({0, 0}).front().info)); + ASSERT_TRUE(std::get_if<fvm_probe_scalar>(&probe_map.data_on({0, 2}).front().info)); probe_handle p0a = get_probe_raw_handle({0, 0}, 0); probe_handle p0b = get_probe_raw_handle({0, 0}, 1); @@ -225,11 +227,11 @@ void run_v_cell_probe_test(const context& ctx) { ASSERT_EQ(1u, probe_map.size()); - const fvm_probe_multi* h_ptr = util::get_if<fvm_probe_multi>(probe_map.data_on({0, 0}).front().info); + const fvm_probe_multi* h_ptr = std::get_if<fvm_probe_multi>(&probe_map.data_on({0, 0}).front().info); ASSERT_TRUE(h_ptr); auto& h = *h_ptr; - const mcable_list* cl_ptr = util::get_if<mcable_list>(h_ptr->metadata); + const mcable_list* cl_ptr = std::get_if<mcable_list>(&h_ptr->metadata); ASSERT_TRUE(cl_ptr); auto& cl = *cl_ptr; @@ -420,10 +422,10 @@ void run_expsyn_g_cell_probe_test(const context& ctx) { ASSERT_EQ(2u, probe_map.size()); for (unsigned i: {0u, 1u}) { - const auto* h_ptr = util::get_if<fvm_probe_multi>(probe_map.data_on({i, 0}).front().info); + const auto* h_ptr = std::get_if<fvm_probe_multi>(&probe_map.data_on({i, 0}).front().info); ASSERT_TRUE(h_ptr); - const auto* m_ptr = util::get_if<std::vector<cable_probe_point_info>>(h_ptr->metadata); + const auto* m_ptr = std::get_if<std::vector<cable_probe_point_info>>(&h_ptr->metadata); ASSERT_TRUE(m_ptr); const fvm_probe_multi& h = *h_ptr; @@ -609,11 +611,11 @@ void run_ion_density_probe_test(const context& ctx) { // sorted by CV in the fvm_probe_weighted_multi object; this is assumed // below. - auto* p_ptr = util::get_if<fvm_probe_multi>(probe_map.data_on({0, 11}).front().info); + auto* p_ptr = std::get_if<fvm_probe_multi>(&probe_map.data_on({0, 11}).front().info); ASSERT_TRUE(p_ptr); const fvm_probe_multi& na_int_all_info = *p_ptr; - auto* m_ptr = util::get_if<mcable_list>(na_int_all_info.metadata); + auto* m_ptr = std::get_if<mcable_list>(&na_int_all_info.metadata); ASSERT_TRUE(m_ptr); mcable_list na_int_all_metadata = *m_ptr; @@ -627,11 +629,11 @@ void run_ion_density_probe_test(const context& ctx) { EXPECT_EQ(na_int_cv2, na_int_all_info.raw_handles[1]); EXPECT_EQ(na_int_cv2-1, na_int_all_info.raw_handles[0]); - p_ptr = util::get_if<fvm_probe_multi>(probe_map.data_on({0, 12}).front().info); + p_ptr = std::get_if<fvm_probe_multi>(&probe_map.data_on({0, 12}).front().info); ASSERT_TRUE(p_ptr); const fvm_probe_multi& ca_ext_all_info = *p_ptr; - m_ptr = util::get_if<mcable_list>(ca_ext_all_info.metadata); + m_ptr = std::get_if<mcable_list>(&ca_ext_all_info.metadata); ASSERT_TRUE(m_ptr); mcable_list ca_ext_all_metadata = *m_ptr; @@ -830,12 +832,12 @@ void run_axial_and_ion_current_sampled_probe_test(const context& ctx) { ASSERT_EQ(1u, n_sample); if (pm.tag==1) { // (whole cell probe) - const mcable_list* m = util::any_cast<const mcable_list*>(pm.meta); + const mcable_list* m = any_cast<const mcable_list*>(pm.meta); ASSERT_NE(nullptr, m); // Metadata should comprise one cable per CV. ASSERT_EQ(n_cv, m->size()); - const cable_sample_range* s = util::any_cast<const cable_sample_range*>(samples[0].data); + const cable_sample_range* s = any_cast<const cable_sample_range*>(samples[0].data); ASSERT_NE(nullptr, s); ASSERT_EQ(s->first+n_cv, s->second); @@ -847,10 +849,10 @@ void run_axial_and_ion_current_sampled_probe_test(const context& ctx) { // Probe id tells us which axial current this is. ASSERT_LT(pm.id.index, n_axial_probe); - const mlocation* m = util::any_cast<const mlocation*>(pm.meta); + const mlocation* m = any_cast<const mlocation*>(pm.meta); ASSERT_NE(nullptr, m); - const double* s = util::any_cast<const double*>(samples[0].data); + const double* s = any_cast<const double*>(samples[0].data); ASSERT_NE(nullptr, s); i_axial.at(pm.id.index) = *s; @@ -889,7 +891,7 @@ auto run_simple_samplers( double t_end, const std::vector<cable_cell>& cells, cell_gid_type probe_cell, - const std::vector<util::any>& probe_addrs, + const std::vector<std::any>& probe_addrs, const std::vector<double>& when) { cable1d_recipe rec(cells, false); @@ -920,7 +922,7 @@ auto run_simple_sampler( double t_end, const std::vector<cable_cell>& cells, cell_gid_type probe_cell, - const util::any& probe_addr, + const std::any& probe_addr, const std::vector<double>& when) { return run_simple_samplers<SampleData, SampleMeta>(ctx, t_end, cells, probe_cell, {probe_addr}, when).at(0); @@ -1187,8 +1189,8 @@ void run_exact_sampling_probe_test(const context& ctx) { return {explicit_generator(spikes)}; } - util::any get_global_properties(cell_kind k) const override { - return k==cell_kind::cable? gprop_: util::any{}; + std::any get_global_properties(cell_kind k) const override { + return k==cell_kind::cable? gprop_: std::any{}; } }; @@ -1316,9 +1318,9 @@ TEST(probe, get_probe_metadata) { EXPECT_EQ(7, mm[1].tag); EXPECT_EQ(7, mm[2].tag); - const mlocation* l0 = util::any_cast<const mlocation*>(mm[0].meta); - const mlocation* l1 = util::any_cast<const mlocation*>(mm[1].meta); - const mlocation* l2 = util::any_cast<const mlocation*>(mm[2].meta); + const mlocation* l0 = any_cast<const mlocation*>(mm[0].meta); + const mlocation* l1 = any_cast<const mlocation*>(mm[1].meta); + const mlocation* l2 = any_cast<const mlocation*>(mm[2].meta); ASSERT_TRUE(l0); ASSERT_TRUE(l1); diff --git a/test/unit/test_range.cpp b/test/unit/test_range.cpp index 9b6a14f1c123bf78a17b4316d2b5067d3ace8159..846811d03d151225a7f9df2d6c0c8297e7d033f8 100644 --- a/test/unit/test_range.cpp +++ b/test/unit/test_range.cpp @@ -65,7 +65,7 @@ TEST(range, pointer) { util::range<int *> s(&xs[l], &xs[r]); auto s_deduced = util::make_range(xs+l, xs+r); - EXPECT_TRUE((std::is_same<decltype(s), decltype(s_deduced)>::value)); + EXPECT_TRUE((std::is_same_v<decltype(s), decltype(s_deduced)>)); EXPECT_EQ(s.left, s_deduced.left); EXPECT_EQ(s.right, s_deduced.right); @@ -142,11 +142,11 @@ TEST(range, input_iterator) { TEST(range, const_iterator) { std::vector<int> xs = { 1, 2, 3, 4, 5 }; auto r = util::make_range(xs.begin(), xs.end()); - EXPECT_TRUE((std::is_same<int&, decltype(r.front())>::value)); + EXPECT_TRUE((std::is_same_v<int&, decltype(r.front())>)); const auto& xs_const = xs; auto r_const = util::make_range(xs_const.begin(), xs_const.end()); - EXPECT_TRUE((std::is_same<const int&, decltype(r_const.front())>::value)); + EXPECT_TRUE((std::is_same_v<const int&, decltype(r_const.front())>)); } TEST(range, view) { @@ -188,7 +188,7 @@ TEST(range, strictify) { auto cstr_range = util::make_range(cstr, null_terminated); auto ptr_range = util::strict_view(cstr_range); - EXPECT_TRUE((std::is_same<decltype(ptr_range), util::range<const char *>>::value)); + EXPECT_TRUE((std::is_same_v<decltype(ptr_range), util::range<const char *>>)); EXPECT_EQ(cstr, ptr_range.left); EXPECT_EQ(cstr+11, ptr_range.right); @@ -259,7 +259,7 @@ TEST(range, max_element_by) { auto i = util::max_element_by(cstr_range, [](char c) -> int { return -c; }); - EXPECT_TRUE((std::is_same<const char *, decltype(i)>::value)); + EXPECT_TRUE((std::is_same_v<const char *, decltype(i)>)); EXPECT_EQ('d', *i); EXPECT_EQ(cstr+9, i); diff --git a/test/unit/test_unique_any.cpp b/test/unit/test_unique_any.cpp index 02612a0900c1be1d84cc8fbf824a257caf4f8c28..31e99d5d18e0883a3b74f20ff758e74cbabf7497 100644 --- a/test/unit/test_unique_any.cpp +++ b/test/unit/test_unique_any.cpp @@ -1,5 +1,6 @@ #include <iostream> +#include <arbor/util/any_cast.hpp> #include <arbor/util/unique_any.hpp> #include "util/rangeutil.hpp" @@ -9,6 +10,7 @@ #include "common.hpp" using namespace arb; +using util::any_cast; TEST(unique_any, copy_construction) { using util::unique_any; @@ -48,11 +50,11 @@ TEST(unique_any, move_construction) { util::unique_any moved(std::move(m)); // Check that the expected number of copies and moves were performed. - const auto& cref = util::any_cast<const moveable&>(copied); + const auto& cref = any_cast<const moveable&>(copied); EXPECT_EQ(cref.moves, 0); EXPECT_EQ(cref.copies, 1); - const auto& mref = util::any_cast<const moveable&>(moved); + const auto& mref = any_cast<const moveable&>(moved); EXPECT_EQ(mref.moves, 1); EXPECT_EQ(mref.copies, 0); @@ -60,11 +62,11 @@ TEST(unique_any, move_construction) { // constructed value util::unique_any fin(std::move(moved)); EXPECT_FALSE(moved.has_value()); // moved has been moved from and should be empty - const auto& fref = util::any_cast<const moveable&>(fin); + const auto& fref = any_cast<const moveable&>(fin); EXPECT_EQ(fref.moves, 1); EXPECT_EQ(fref.copies, 0); - const auto value = util::any_cast<moveable>(fin); + const auto value = any_cast<moveable>(fin); EXPECT_EQ(value.moves, 1); EXPECT_EQ(value.copies, 1); } @@ -91,7 +93,6 @@ TEST(unique_any, type) { TEST(unique_any, swap) { using util::unique_any; - using util::any_cast; unique_any any1(42); // integer unique_any any2(3.14); // double @@ -121,24 +122,24 @@ TEST(unique_any, not_copy_constructable) { util::unique_any a(T(42)); - auto& ref = util::any_cast<T&>(a); + auto& ref = any_cast<T&>(a); EXPECT_EQ(ref, 42); ref.value = 100; - EXPECT_EQ(util::any_cast<T&>(a).value, 100); + EXPECT_EQ(any_cast<T&>(a).value, 100); // the following will fail if uncommented, because we are requesting // a copy of an non-copyable type. - //auto value = util::any_cast<T>(a); + //auto value = any_cast<T>(a); // Test that we can move the contents of the unique_any. // NOTE: it makes sense to sink a with std::move(a) instead // of the following, because after such an assignment a will // be in a moved from state, and enforcing std::move(a) it // is made clearer in the calling code that a has been invalidated. - // util::any_cast<T&&>(a) - T val(util::any_cast<T&&>(std::move(a))); + // any_cast<T&&>(a) + T val(any_cast<T&&>(std::move(a))); EXPECT_EQ(val.value, 100); } @@ -151,32 +152,32 @@ TEST(unique_any, any_cast_ptr) { // test that valid pointers are returned for int and std::string types unique_any ai(42); - auto ptr_i = util::any_cast<int>(&ai); + auto ptr_i = any_cast<int>(&ai); EXPECT_EQ(*ptr_i, 42); unique_any as(std::string("hello")); - auto ptr_s = util::any_cast<std::string>(&as); + auto ptr_s = any_cast<std::string>(&as); EXPECT_EQ(*ptr_s, "hello"); // test that exceptions are thrown for invalid casts - EXPECT_EQ(util::any_cast<int>(&as), nullptr); - EXPECT_EQ(util::any_cast<std::string>(&ai), nullptr); + EXPECT_EQ(any_cast<int>(&as), nullptr); + EXPECT_EQ(any_cast<std::string>(&ai), nullptr); unique_any empty; - EXPECT_EQ(util::any_cast<int>(&empty), nullptr); - EXPECT_EQ(util::any_cast<int>((util::unique_any*)nullptr), nullptr); + EXPECT_EQ(any_cast<int>(&empty), nullptr); + EXPECT_EQ(any_cast<int>((util::unique_any*)nullptr), nullptr); // Check that constness of the returned pointer matches that the input. // Check that constness of the returned pointer matches that the input. { unique_any a(42); - auto p = util::any_cast<int>(&a); + auto p = any_cast<int>(&a); // any_cast(any*) should not return const* EXPECT_TRUE((std::is_same<int*, decltype(p)>::value)); } { const unique_any a(42); - auto p = util::any_cast<int>(&a); + auto p = any_cast<int>(&a); // any_cast(const any*) should return const* EXPECT_TRUE((std::is_same<const int*, decltype(p)>::value)); @@ -186,20 +187,20 @@ TEST(unique_any, any_cast_ptr) { // test any_cast(unique_any&) TEST(unique_any, any_cast_ref) { util::unique_any ai(42); - auto& i = util::any_cast<int&>(ai); + auto& i = any_cast<int&>(ai); EXPECT_EQ(typeid(i), typeid(int)); EXPECT_EQ(i, 42); // any_cast<T>(unique_any&) returns a i = 100; - EXPECT_EQ(util::any_cast<int>(ai), 100); + EXPECT_EQ(any_cast<int>(ai), 100); } // test any_cast(const unique_any&) TEST(unique_any, any_cast_const_ref) { const util::unique_any ai(42); - auto& i = util::any_cast<const int&>(ai); + auto& i = any_cast<const int&>(ai); EXPECT_EQ(typeid(i), typeid(int)); EXPECT_EQ(i, 42); @@ -209,7 +210,7 @@ TEST(unique_any, any_cast_const_ref) { // test any_cast(unique_any&&) TEST(unique_any, any_cast_rvalue) { - auto moved = util::any_cast<moveable>(util::unique_any(moveable())); + auto moved = any_cast<moveable>(util::unique_any(moveable())); EXPECT_EQ(moved.moves, 2); EXPECT_EQ(moved.copies, 0); } @@ -218,18 +219,18 @@ TEST(unique_any, std_swap) { util::unique_any a1(42); util::unique_any a2(3.14); - auto pi = util::any_cast<int>(&a1); - auto pd = util::any_cast<double>(&a2); + auto pi = any_cast<int>(&a1); + auto pd = any_cast<double>(&a2); std::swap(a1, a2); // test that values were swapped - EXPECT_EQ(util::any_cast<int>(a2), 42); - EXPECT_EQ(util::any_cast<double>(a1), 3.14); + EXPECT_EQ(any_cast<int>(a2), 42); + EXPECT_EQ(any_cast<double>(a1), 3.14); // test that underlying pointers did not change - EXPECT_EQ(pi, util::any_cast<int>(&a2)); - EXPECT_EQ(pd, util::any_cast<double>(&a1)); + EXPECT_EQ(pi, any_cast<int>(&a2)); + EXPECT_EQ(pd, any_cast<double>(&a1)); } // test operator=(unique_any&&) @@ -244,9 +245,9 @@ TEST(unique_any, assignment_from_rvalue) { unique_any b; b = std::move(a); // move assignment - EXPECT_EQ(str1, util::any_cast<string>(b)); + EXPECT_EQ(str1, any_cast<string>(b)); - EXPECT_EQ(nullptr, util::any_cast<string>(&a)); + EXPECT_EQ(nullptr, any_cast<string>(&a)); } // test template<typename T> operator=(T&&) @@ -263,7 +264,7 @@ TEST(unique_any, assignment_from_value) { unique_any a; a = std::move(tmp); - auto vec = util::any_cast<std::vector<int>>(&a); + auto vec = any_cast<std::vector<int>>(&a); // ensure the value was moved EXPECT_EQ(ptr, vec->data()); @@ -299,7 +300,6 @@ TEST(unique_any, assignment_from_value) { TEST(unique_any, make_unique_any) { using util::make_unique_any; - using util::any_cast; { auto a = make_unique_any<int>(42); @@ -366,7 +366,7 @@ TEST(unique_any, stdvector) // push_back { using T = testing::nocopy<std::string>; - auto get = [](const unique_any& v) {return util::any_cast<const T&>(v).value;}; + auto get = [](const unique_any& v) {return any_cast<const T&>(v).value;}; std::vector<unique_any> vec; vec.push_back(T("h")); @@ -389,7 +389,7 @@ TEST(unique_any, stdvector) // sort { - auto get = [](const unique_any& v) {return util::any_cast<int>(v);}; + auto get = [](const unique_any& v) {return any_cast<int>(v);}; int n = 10; std::vector<unique_any> vec; @@ -410,7 +410,7 @@ TEST(unique_any, stdvector) // std::reverse with non-copyable type { using T = testing::nocopy<int>; - auto get = [](const unique_any& v) {return util::any_cast<const T&>(v).value;}; + auto get = [](const unique_any& v) {return any_cast<const T&>(v).value;}; int n = 10; std::vector<unique_any> vec; diff --git a/test/unit/test_variant.cpp b/test/unit/test_variant.cpp deleted file mode 100644 index 8fe0e941553ed1b8e123a1ad022f52383a978cb3..0000000000000000000000000000000000000000 --- a/test/unit/test_variant.cpp +++ /dev/null @@ -1,537 +0,0 @@ -#include <tuple> - -#include <arbor/util/variant.hpp> -#include "util/meta.hpp" - -#include "../gtest.h" -#include "common.hpp" - -#ifdef __clang__ -#pragma clang diagnostic ignored "-Wself-assign-overloaded" -#endif - -using namespace arb::util; -using testing::nocopy; -using testing::nomove; - -TEST(variant, in_place_index_ctor) { - // Equal variant alternatives okay? - { - variant<int> v0{in_place_index<0>(), 3}; - ASSERT_EQ(0u, v0.index()); - } - { - variant<int, int> v0{in_place_index<0>(), 3}; - ASSERT_EQ(0u, v0.index()); - - variant<int, int> v1{in_place_index<1>(), 3}; - ASSERT_EQ(1u, v1.index()); - } - { - variant<int, int, int> v0{in_place_index<0>(), 3}; - ASSERT_EQ(0u, v0.index()); - - variant<int, int, int> v1{in_place_index<1>(), 3}; - ASSERT_EQ(1u, v1.index()); - - variant<int, int, int> v2{in_place_index<2>(), 3}; - ASSERT_EQ(2u, v2.index()); - } - - // Check move- and copy- only types work. - { - struct foo { explicit foo(int, double) {} }; - nocopy<foo>::reset_counts(); - nomove<foo>::reset_counts(); - - variant<nocopy<foo>, nomove<foo>> v0(in_place_index<0>(), 1, 3.2); - ASSERT_EQ(0u, v0.index()); - EXPECT_EQ(0, nocopy<foo>::move_ctor_count); // (should have constructed in-place) - EXPECT_EQ(0, nocopy<foo>::move_assign_count); - nocopy<foo>::reset_counts(); - - variant<nocopy<foo>, nomove<foo>> v0bis(in_place_index<0>(), nocopy<foo>(1, 3.2)); - ASSERT_EQ(0u, v0.index()); - EXPECT_EQ(1, nocopy<foo>::move_ctor_count); // (should have move-constructed) - EXPECT_EQ(0, nocopy<foo>::move_assign_count); // (should have constructed in-place) - nocopy<foo>::reset_counts(); - - variant<nocopy<foo>, nomove<foo>> v1(in_place_index<1>(), 1, 3.2); - ASSERT_EQ(1u, v1.index()); - EXPECT_EQ(0, nomove<foo>::copy_ctor_count); // (should have constructed in-place) - EXPECT_EQ(0, nomove<foo>::copy_assign_count); - nomove<foo>::reset_counts(); - - variant<nocopy<foo>, nomove<foo>> v1bis(in_place_index<1>(), nomove<foo>(1, 3.2)); - ASSERT_EQ(1u, v1bis.index()); - EXPECT_EQ(1, nomove<foo>::copy_ctor_count); // (should have copy-constructed) - EXPECT_EQ(0, nomove<foo>::copy_assign_count); - nomove<foo>::reset_counts(); - } -} - -TEST(variant, in_place_type_ctor) { - { - variant<int> v0{in_place_type<int>(), 3}; - ASSERT_EQ(0u, v0.index()); - } - { - variant<int, double> v0{in_place_type<int>(), 3}; - ASSERT_EQ(0u, v0.index()); - - variant<int, double> v1{in_place_type<double>(), 3}; - ASSERT_EQ(1u, v1.index()); - } - // Check move- and copy- only types for in_place_type too. - { - struct foo { explicit foo(int, double) {} }; - nocopy<foo>::reset_counts(); - nomove<foo>::reset_counts(); - - variant<nocopy<foo>, nomove<foo>> v0(in_place_type<nocopy<foo>>(), 1, 3.2); - ASSERT_EQ(0u, v0.index()); - EXPECT_EQ(0, nocopy<foo>::move_ctor_count); // (should have constructed in-place) - EXPECT_EQ(0, nocopy<foo>::move_assign_count); - nocopy<foo>::reset_counts(); - - variant<nocopy<foo>, nomove<foo>> v0bis(in_place_type<nocopy<foo>>(), nocopy<foo>(1, 3.2)); - ASSERT_EQ(0u, v0.index()); - EXPECT_EQ(1, nocopy<foo>::move_ctor_count); // (should have move-constructed) - EXPECT_EQ(0, nocopy<foo>::move_assign_count); // (should have constructed in-place) - nocopy<foo>::reset_counts(); - - variant<nocopy<foo>, nomove<foo>> v1(in_place_type<nomove<foo>>(), 1, 3.2); - ASSERT_EQ(1u, v1.index()); - EXPECT_EQ(0, nomove<foo>::copy_ctor_count); // (should have constructed in-place) - EXPECT_EQ(0, nomove<foo>::copy_assign_count); - nomove<foo>::reset_counts(); - - variant<nocopy<foo>, nomove<foo>> v1bis(in_place_type<nomove<foo>>(), nomove<foo>(1, 3.2)); - ASSERT_EQ(1u, v1bis.index()); - EXPECT_EQ(1, nomove<foo>::copy_ctor_count); // (should have copy-constructed) - EXPECT_EQ(0, nomove<foo>::copy_assign_count); - nomove<foo>::reset_counts(); - } -} - -TEST(variant, converting_ctor) { - struct Z {}; - struct X { X() {} X(Z) {} }; - struct Y {}; - - // Expect resolution via overload set of one-argument constructors. - { - using var_xy = variant<X, Y>; - var_xy v0(X{}); - ASSERT_EQ(0u, v0.index()); - - var_xy v1(Y{}); - ASSERT_EQ(1u, v1.index()); - - var_xy v0bis(Z{}); - ASSERT_EQ(0u, v0bis.index()); - } - { - using var_xyz = variant<X, Y, Z>; - var_xyz v0(X{}); - ASSERT_EQ(0u, v0.index()); - - var_xyz v1(Y{}); - ASSERT_EQ(1u, v1.index()); - - var_xyz v2(Z{}); - ASSERT_EQ(2u, v2.index()); - } - - // A bool alternative should only accept (cvref qualified) bool. - { - using bool_or_ptr = variant<bool, void*>; - bool_or_ptr v0(false); - ASSERT_EQ(0u, v0.index()); - - bool_or_ptr v1(nullptr); - ASSERT_EQ(1u, v1.index()); - } -} - -TEST(variant, get) { - struct X {}; - - { - variant<int, double, X> v(2.3); - - EXPECT_THROW(get<0>(v), bad_variant_access); - EXPECT_EQ(2.3, get<1>(v)); - - EXPECT_THROW(get<int>(v), bad_variant_access); - EXPECT_EQ(2.3, get<double>(v)); - } - { - variant<nocopy<double>> v(3.1); - auto x = get<0>(std::move(v)); - // nocopy will zero value on move - EXPECT_EQ(3.1, x.value); - EXPECT_EQ(0.0, get<0>(v).value); - } - { - // should be able to modify in-place - variant<double> v(3.1); - get<0>(v) = 4.2; - EXPECT_EQ(4.2, get<0>(v)); - } -} - -TEST(variant, get_if) { - struct X {}; - - { - variant<int, double, X> v(2.3); - - EXPECT_EQ(nullptr, get_if<0>(v)); - ASSERT_NE(nullptr, get_if<1>(v)); - EXPECT_EQ(2.3, *get_if<1>(v)); - - EXPECT_EQ(nullptr, get_if<int>(v)); - ASSERT_NE(nullptr, get_if<double>(v)); - EXPECT_EQ(2.3, *get_if<double>(v)); - } - { - // should be able to modify in-place - variant<double> v(3.1); - ASSERT_NE(nullptr, get_if<0>(v)); - *get_if<0>(v) = 4.2; - EXPECT_EQ(4.2, get<0>(v)); - } -} - -TEST(variant, visit) { - struct X {}; - - // void case - struct visitor { - int* result = nullptr; - visitor(int& r): result(&r) {} - - void operator()(int) { *result = 10; } - void operator()(double) { *result = 11; } - void operator()(X) { *result = 12; } - }; - - variant<int, double, X> v0(2); - variant<int, double, X> v1(3.1); - variant<int, double, X> v2(X{}); - - int r; - auto hello = visitor(r); - - visit<void>(hello, v0); - EXPECT_EQ(10, r); - - visit<void>(hello, v1); - EXPECT_EQ(11, r); - - visit<void>(hello, v2); - EXPECT_EQ(12, r); -} - -TEST(variant, visit_deduce_return) { - struct X {}; - - struct visitor { - char operator()(int) { return 'i'; } - char operator()(double) { return 'd'; } - char operator()(X) { return 'X'; } - } hello; - - using variant_idX = variant<int, double, X>; - - EXPECT_EQ('i', visit(hello, variant_idX(1))); - EXPECT_EQ('d', visit(hello, variant_idX(1.1))); - EXPECT_EQ('X', visit(hello, variant_idX(X{}))); -} - -TEST(variant, valueless) { - struct X { - X() {} - X(const X&) { throw "nope"; } - }; - - variant<X, int> vx; - variant<X, int> vi(3); - - ASSERT_EQ(0u, vx.index()); - ASSERT_EQ(1u, vi.index()); - try { - vi = vx; - } - catch (...) { - } - EXPECT_TRUE(vi.valueless_by_exception()); - EXPECT_EQ(std::size_t(-1), vi.index()); -} - -namespace { -struct nope {}; - -struct maybe_throws_on_assign { - int i; - explicit maybe_throws_on_assign(int i): i(i) {} - maybe_throws_on_assign(const maybe_throws_on_assign& x): i(x.i) {} - maybe_throws_on_assign(maybe_throws_on_assign&& x): i(x.i) {} - - maybe_throws_on_assign& operator=(const maybe_throws_on_assign& x) { - if (x.i<0) throw nope{}; - i = x.i; - return *this; - } - - maybe_throws_on_assign& operator=(maybe_throws_on_assign&& x) { - if (x.i<0) throw nope{}; - i = x.i; - return *this; - } - - ~maybe_throws_on_assign() { ++dtor; } - - static int dtor; -}; - -int maybe_throws_on_assign::dtor = 0; -} // anonymous namespace - -TEST(variant, copy_assign) { - struct X { - X& operator=(const X&) { throw nope{}; } - }; - - using vidX = variant<int, double, X>; - - vidX v0{in_place_type<int>(), 3}; - vidX v1{in_place_type<double>(), 4.}; - - vidX valueless{X{}}; - try { valueless = valueless; } catch (...) {} - ASSERT_TRUE(valueless.valueless_by_exception()); - - vidX v; - v = v0; - ASSERT_EQ(0u, v.index()); - EXPECT_EQ(3, get<0>(v)); - - v = v1; - ASSERT_EQ(1u, v.index()); - EXPECT_EQ(4., get<1>(v)); - - v = valueless; - EXPECT_TRUE(v.valueless_by_exception()); - - v = v1; - ASSERT_EQ(1u, v.index()); - EXPECT_EQ(4., get<1>(v)); - - using vbM = variant<bool, maybe_throws_on_assign>; - maybe_throws_on_assign::dtor = 0; - - vbM w0(in_place_type<maybe_throws_on_assign>(), 2); - vbM w1(in_place_type<maybe_throws_on_assign>(), -2); - ASSERT_EQ(0, maybe_throws_on_assign::dtor); - - EXPECT_THROW(w0 = w1, nope); - EXPECT_TRUE(w0.valueless_by_exception()); - EXPECT_EQ(1, maybe_throws_on_assign::dtor); - - maybe_throws_on_assign::dtor = 0; - vbM w2(in_place_type<maybe_throws_on_assign>(), 2); - - ASSERT_EQ(0, maybe_throws_on_assign::dtor); - - EXPECT_THROW(w2 = std::move(w1), nope); - EXPECT_FALSE(w2.valueless_by_exception()); - EXPECT_EQ(0, maybe_throws_on_assign::dtor); -} - -TEST(variant, equality) { - struct X { - int i; - X(int i): i(i) {} - X& operator=(const X&) { throw "nope"; } - bool operator==(X x) const { return i==x.i; } - // Crazy != semantics on purpose: - bool operator!=(X x) const { return i==x.i+1; } - }; - - ASSERT_TRUE(X{1} == X{1}); - ASSERT_FALSE(X{1} == X{0}); - ASSERT_FALSE(X{1} == X{2}); - - ASSERT_TRUE(X{1} != X{0}); - ASSERT_FALSE(X{1} != X{1}); - ASSERT_FALSE(X{1} != X{2}); - - using vidX = variant<int, double, X>; - auto valueless = []() { - vidX v{X{0}}; - try { v = v; } catch (...) {}; - return v; - }; - - EXPECT_TRUE(valueless() == valueless()); - EXPECT_FALSE(valueless() != valueless()); - - EXPECT_TRUE(vidX{3} == vidX{3}); - EXPECT_FALSE(vidX{3.0} == vidX{3}); - EXPECT_FALSE(vidX{X{3}} == vidX{3}); - EXPECT_FALSE(valueless() == vidX{3}); - - EXPECT_TRUE(vidX{X{2}} == vidX{X{2}}); - EXPECT_FALSE(vidX{X{2}} == vidX{2}); - EXPECT_FALSE(vidX{X{2}} == vidX{2.0}); - EXPECT_FALSE(vidX{X{2}} == valueless()); - - EXPECT_FALSE(vidX{3} != vidX{3}); - EXPECT_TRUE(vidX{3.0} != vidX{3}); - EXPECT_TRUE(vidX{X{3}} != vidX{3}); - EXPECT_TRUE(valueless() != vidX{3}); - - EXPECT_TRUE(vidX{X{2}} != vidX{X{1}}); // note custom != - EXPECT_FALSE(vidX{X{2}} != vidX{X{2}}); // note custom != - EXPECT_FALSE(vidX{X{2}} != vidX{X{3}}); // note custom != - EXPECT_TRUE(vidX{X{2}} != vidX{2}); - EXPECT_TRUE(vidX{X{2}} != vidX{2.0}); - EXPECT_TRUE(vidX{X{2}} != valueless()); -} - -TEST(variant, hash) { - // Just ensure we find std::hash specializations. - - std::hash<variant<>> h0; - EXPECT_TRUE((std::is_same<std::size_t, decltype(h0(std::declval<variant<>>()))>::value)); - - std::hash<variant<int, double>> h2; - (void)h2(variant<int, double>(3.1)); - EXPECT_TRUE((std::is_same<std::size_t, decltype(h2(std::declval<variant<int, double>>()))>::value)); -} - -namespace { -struct counts_swap { - static unsigned n_swap; - friend void swap(counts_swap&, counts_swap&) { ++counts_swap::n_swap; } -}; -unsigned counts_swap::n_swap = 0; -} - -TEST(variant, swap) { - struct X { - X() {} - X& operator=(const X&) { throw "nope"; } - }; - using vidX = variant<int, double, X>; - - auto valueless = []() { - vidX v{X{}}; - try { v = v; } catch (...) {}; - return v; - }; - - { - vidX a(valueless()), b(valueless()); - ASSERT_TRUE(a.valueless_by_exception()); - ASSERT_TRUE(b.valueless_by_exception()); - std::swap(a, b); - EXPECT_TRUE(a.valueless_by_exception()); - EXPECT_TRUE(b.valueless_by_exception()); - }; - - { - vidX a(valueless()), b(3.2); - ASSERT_TRUE(a.valueless_by_exception()); - ASSERT_EQ(1u, b.index()); - - std::swap(a, b); - EXPECT_TRUE(b.valueless_by_exception()); - EXPECT_EQ(1u, a.index()); - ASSERT_NE(nullptr, get_if<1>(a)); - EXPECT_EQ(3.2, get<1>(a)); - } - - { - vidX a(1.2), b(3); - std::swap(a, b); - - ASSERT_EQ(0u, a.index()); - EXPECT_EQ(3, get<int>(a)); - - ASSERT_EQ(1u, b.index()); - EXPECT_EQ(1.2, get<double>(b)); - } - - { - variant<counts_swap> y0, y1; - ASSERT_EQ(0u, counts_swap::n_swap); - - std::swap(y0, y1); - EXPECT_EQ(1u, counts_swap::n_swap); - } -} - -// Test generic accessors for pair, tuple. - -TEST(variant, get_pair_tuple) { - { - using pair_ni_nd = std::pair<nocopy<int>, nocopy<double>>; - - nocopy<int>::reset_counts(); - nocopy<double>::reset_counts(); - - auto f = first(pair_ni_nd{2, 3.4}); - EXPECT_EQ(2, f.value); - EXPECT_EQ(1, nocopy<int>::move_ctor_count); - - auto s = second(pair_ni_nd{2, 3.4}); - EXPECT_EQ(3.4, s.value); - EXPECT_EQ(1, nocopy<double>::move_ctor_count); - - nocopy<int>::reset_counts(); - nocopy<double>::reset_counts(); - - auto g0 = ::arb::util::get<0>(pair_ni_nd{2, 3.4}); - EXPECT_EQ(2, g0.value); - EXPECT_EQ(1, nocopy<int>::move_ctor_count); - - auto g1 = ::arb::util::get<1>(pair_ni_nd{2, 3.4}); - EXPECT_EQ(3.4, g1.value); - EXPECT_EQ(1, nocopy<double>::move_ctor_count); - } - - { - struct X {}; - using tuple_ni_nd_nx = std::tuple<nocopy<int>, nocopy<double>, nocopy<X>>; - - nocopy<int>::reset_counts(); - nocopy<double>::reset_counts(); - nocopy<X>::reset_counts(); - - auto f = first(tuple_ni_nd_nx{2, 3.4, X{}}); - EXPECT_EQ(2, f.value); - EXPECT_EQ(1, nocopy<int>::move_ctor_count); - - auto s = second(tuple_ni_nd_nx{2, 3.4, X{}}); - EXPECT_EQ(3.4, s.value); - EXPECT_EQ(1, nocopy<double>::move_ctor_count); - - nocopy<int>::reset_counts(); - nocopy<double>::reset_counts(); - - auto g0 = ::arb::util::get<0>(tuple_ni_nd_nx{2, 3.4, X{}}); - EXPECT_EQ(2, g0.value); - EXPECT_EQ(1, nocopy<int>::move_ctor_count); - - auto g1 = ::arb::util::get<1>(tuple_ni_nd_nx{2, 3.4, X{}}); - EXPECT_EQ(3.4, g1.value); - EXPECT_EQ(1, nocopy<double>::move_ctor_count); - - auto g2 = ::arb::util::get<2>(tuple_ni_nd_nx{2, 3.4, X{}}); - (void)g2; - EXPECT_EQ(1, nocopy<X>::move_ctor_count); - } -}