Skip to content

Commit 378736b

Browse files
committed
Merge branch 'feature/any' into feature/type_erasure
# Conflicts: # include/boost/openmethod/interop/virtual_any.hpp
2 parents 20a2a96 + 7018caf commit 378736b

4 files changed

Lines changed: 100 additions & 7 deletions

File tree

include/boost/openmethod/interop/virtual_any.hpp

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,48 @@ class virtual_any;
1919
template<class Any, class Registry = BOOST_OPENMETHOD_DEFAULT_REGISTRY>
2020
class virtual_any_ref;
2121

22-
namespace detail {
22+
BOOST_OPENMETHOD_OPEN_NAMESPACE_DETAIL_UNLESS_MRDOCS
2323

24+
//! Test if argument is a wide `any` (exposition only)
25+
//!
26+
//! Evaluates to `true` if `T` is a specialization of @ref virtual_any or of
27+
//! @ref virtual_any_ref, and `false` otherwise.
28+
//!
29+
//! This constrains the constructor and the assignment operator of
30+
//! @ref virtual_any that take a value, excluding both wide types - every
31+
//! specialization of them, not only the ones matching this `virtual_any`. A
32+
//! @ref virtual_any argument then selects the copy or move operation instead
33+
//! of being stored inside the `any`, and a @ref virtual_any_ref argument is
34+
//! rejected outright rather than stored: a handle is not a registered class,
35+
//! so its @ref registry::static_vptr would be null.
36+
//!
37+
//! @tparam T A type.
2438
template<typename T>
25-
struct is_virtual_any_aux : std::false_type {};
39+
constexpr bool IsVirtualAny = false;
2640

41+
//! Recognize a virtual_any (exposition only)
42+
//!
43+
//! The specialization of @ref IsVirtualAny that matches a
44+
//! `virtual_any`, and evaluates to `true`.
45+
//!
46+
//! @tparam Any An `any` type.
47+
//! @tparam Registry A @ref registry.
2748
template<class Any, class Registry>
28-
struct is_virtual_any_aux<virtual_any<Any, Registry>> : std::true_type {};
49+
constexpr bool IsVirtualAny<virtual_any<Any, Registry>> = true;
2950

51+
//! Recognize a virtual_any_ref (exposition only)
52+
//!
53+
//! The specialization of @ref IsVirtualAny that matches a
54+
//! `virtual_any_ref`, and evaluates to `true`.
55+
//!
56+
//! @tparam Any An `any` type, possibly const-qualified.
57+
//! @tparam Registry A @ref registry.
3058
template<class Any, class Registry>
31-
struct is_virtual_any_aux<virtual_any_ref<Any, Registry>> : std::true_type {};
59+
constexpr bool IsVirtualAny<virtual_any_ref<Any, Registry>> = true;
60+
61+
BOOST_OPENMETHOD_CLOSE_NAMESPACE_DETAIL_UNLESS_MRDOCS
62+
63+
namespace detail {
3264

3365
// Common implementation for the use_*_any_types registrars: register Root
3466
// as a class, and each element of the Classes list as a class derived
@@ -139,7 +171,8 @@ class virtual_any {
139171
template<
140172
typename T,
141173
typename = std::enable_if_t<
142-
!detail::is_virtual_any_aux<std::decay_t<T>>::value &&
174+
!BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS
175+
IsVirtualAny<std::decay_t<T>> &&
143176
!std::is_same_v<std::decay_t<T>, Any> &&
144177
std::is_constructible_v<Any, T&&>>>
145178
virtual_any(T&& value)
@@ -214,7 +247,8 @@ class virtual_any {
214247
template<
215248
typename T,
216249
typename = std::enable_if_t<
217-
!detail::is_virtual_any_aux<std::decay_t<T>>::value &&
250+
!BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS
251+
IsVirtualAny<std::decay_t<T>> &&
218252
!std::is_same_v<std::decay_t<T>, Any> &&
219253
std::is_constructible_v<Any, T&&>>>
220254
auto operator=(T&& value) -> virtual_any& {

test/CMakeLists.txt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,24 @@ openmethod_compile_fail_test(
168168
openmethod_compile_fail_test(
169169
compile_fail_virtual_any_ref_by_ref
170170
"virtual_any_ref is a cheap handle, pass it by value")
171+
# Copy-initializing a virtual_any from a virtual_any_ref is ill-formed: the
172+
# value constructor is constrained away, so storing the handle inside the
173+
# `any` would take two user-defined conversions. The diagnostic is the
174+
# compiler's own, and the wording varies: "conversion from ... to non-scalar
175+
# type ... requested" on gcc, "no viable conversion from" on clang, C2440
176+
# "cannot convert from" on MSVC.
177+
#
178+
# MSVC needs /permissive- here. In its default mode - which `/std:c++17` does
179+
# not turn off - it accepts the extra user-defined conversion and compiles the
180+
# file, so the test would not fail. Note this means the constraint does not
181+
# actually protect MSVC users building in the default mode; only the
182+
# direct-initialization form, which no compiler rejects, is worse.
183+
openmethod_compile_fail_test(
184+
compile_fail_virtual_any_from_ref "conversion from|cannot convert from")
185+
if (MSVC)
186+
target_compile_options(
187+
boost_openmethod-compile_fail_virtual_any_from_ref PRIVATE /permissive-)
188+
endif()
171189
# "use of a deleted function" on gcc, "call to deleted function" on clang,
172190
# "attempting to reference a deleted function" on MSVC.
173191
openmethod_compile_fail_test(

test/Jamfile

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,18 @@ for local src in [ glob test_*.cpp ]
4343
run mix_release_debug/main.cpp mix_release_debug/lib.cpp unit_test_framework ;
4444

4545

46-
for local src in [ glob compile_fail_*.cpp ]
46+
for local src in [ glob compile_fail_*.cpp : compile_fail_virtual_any_from_ref.cpp ]
4747
{
4848
compile-fail $(src) ;
4949
}
5050

51+
# Excluded from the glob above because it needs /permissive- on msvc: copy-
52+
# initializing a virtual_any from a virtual_any_ref takes two user-defined
53+
# conversions, which msvc accepts in its default mode, so the compile would
54+
# succeed and the test fail. See test/CMakeLists.txt for the details.
55+
compile-fail compile_fail_virtual_any_from_ref.cpp
56+
: <toolset>msvc:<cxxflags>/permissive- ;
57+
5158
build-project dynamic_loading ;
5259
build-project implicit_shared_libraries ;
5360

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright (c) 2018-2026 Jean-Louis Leroy
2+
// Distributed under the Boost Software License, Version 1.0.
3+
// See accompanying file LICENSE_1_0.txt
4+
// or copy at http://www.boost.org/LICENSE_1_0.txt)
5+
6+
#include <any>
7+
#include <string>
8+
9+
#include <boost/openmethod.hpp>
10+
#include <boost/openmethod/interop/std_any.hpp>
11+
12+
using namespace boost::openmethod;
13+
14+
struct Dog {
15+
std::string name;
16+
};
17+
18+
BOOST_OPENMETHOD_REGISTER(use_std_any_types<Dog>);
19+
20+
int main() {
21+
std::any dog(Dog{"Snoopy"});
22+
virtual_any_ref<std::any> ref(dog);
23+
24+
// A virtual_any cannot be copy-initialized from a virtual_any_ref. The
25+
// value constructor is constrained to reject every wide type, so storing
26+
// the handle inside the `any` would take two user-defined conversions -
27+
// virtual_any_ref to std::any, then std::any to virtual_any - which is
28+
// one more than an implicit conversion sequence allows. Were the handle
29+
// stored, its static_vptr would be null: a handle is not a registered
30+
// class.
31+
virtual_std_any copy = ref;
32+
33+
return 0;
34+
}

0 commit comments

Comments
 (0)