Fix visit<R> for variant.

* include/std/variant (__do_visit): Add a template parameter
for enforcing same return types for visit.
(__gen_vtable_impl): Likewise.
(_S_apply_single_alt): Adjust.
(__visit_invoke_impl): New. Handle casting to void.
(__do_visit_invoke): New. Enforces same return types.
(__do_visit_invoke_r): New. Converts return types.
(__visit_invoke): Adjust.
(__gen_vtable):  Add a template parameter for enforcing
same return types for visit.
* testsuite/20_util/variant/visit_r.cc: Add a test for a visitor with
different return types.
* testsuite/20_util/variant/visit_neg.cc: New. Ensures that
visitors with different return types don't accidentally
compile with regular visitation.

From-SVN: r270216
This commit is contained in:
Ville Voutilainen 2019-04-08 22:45:48 +03:00 committed by Ville Voutilainen
parent 15f4769a12
commit 3d01c7c2f2
4 changed files with 140 additions and 16 deletions

View file

@ -1,3 +1,22 @@
2019-04-08 Ville Voutilainen <ville.voutilainen@gmail.com>
Fix visit<R> for variant.
* include/std/variant (__do_visit): Add a template parameter
for enforcing same return types for visit.
(__gen_vtable_impl): Likewise.
(_S_apply_single_alt): Adjust.
(__visit_invoke_impl): New. Handle casting to void.
(__do_visit_invoke): New. Enforces same return types.
(__do_visit_invoke_r): New. Converts return types.
(__visit_invoke): Adjust.
(__gen_vtable): Add a template parameter for enforcing
same return types for visit.
* testsuite/20_util/variant/visit_r.cc: Add a test for a visitor with
different return types.
* testsuite/20_util/variant/visit_neg.cc: New. Ensures that
visitors with different return types don't accidentally
compile with regular visitation.
2019-04-08 Christophe Lyon <christophe.lyon@linaro.org>
* testsuite/27_io/filesystem/iterators/caching.cc: Add

View file

@ -138,7 +138,9 @@ namespace __variant
constexpr variant_alternative_t<_Np, variant<_Types...>> const&&
get(const variant<_Types...>&&);
template<bool __use_index=false, typename _Visitor, typename... _Variants>
template<bool __use_index=false,
bool __same_return_types = true,
typename _Visitor, typename... _Variants>
constexpr decltype(auto)
__do_visit(_Visitor&& __visitor, _Variants&&... __variants);
@ -868,12 +870,15 @@ namespace __variant
// tuple<V1&&, V2&&>, std::index_sequence<1, 2>>
// The returned multi-dimensional vtable can be fast accessed by the visitor
// using index calculation.
template<typename _Array_type, typename _Variant_tuple, typename _Index_seq>
template<bool __same_return_types,
typename _Array_type, typename _Variant_tuple, typename _Index_seq>
struct __gen_vtable_impl;
template<typename _Result_type, typename _Visitor, size_t... __dimensions,
template<bool __same_return_types,
typename _Result_type, typename _Visitor, size_t... __dimensions,
typename... _Variants, size_t... __indices>
struct __gen_vtable_impl<
__same_return_types,
_Multi_array<_Result_type (*)(_Visitor, _Variants...), __dimensions...>,
tuple<_Variants...>, std::index_sequence<__indices...>>
{
@ -915,10 +920,12 @@ namespace __variant
if constexpr (__do_cookie)
{
__element = __gen_vtable_impl<
__same_return_types,
_Tp,
tuple<_Variants...>,
std::index_sequence<__indices..., __index>>::_S_apply();
*__cookie_element = __gen_vtable_impl<
__same_return_types,
_Tp,
tuple<_Variants...>,
std::index_sequence<__indices..., variant_npos>>::_S_apply();
@ -926,15 +933,18 @@ namespace __variant
else
{
__element = __gen_vtable_impl<
__same_return_types,
remove_reference_t<decltype(__element)>, tuple<_Variants...>,
std::index_sequence<__indices..., __index>>::_S_apply();
}
}
};
template<typename _Result_type, typename _Visitor, typename... _Variants,
template<bool __same_return_types,
typename _Result_type, typename _Visitor, typename... _Variants,
size_t... __indices>
struct __gen_vtable_impl<
__same_return_types,
_Multi_array<_Result_type (*)(_Visitor, _Variants...)>,
tuple<_Variants...>, std::index_sequence<__indices...>>
{
@ -952,25 +962,56 @@ namespace __variant
}
static constexpr decltype(auto)
__visit_invoke(_Visitor&& __visitor, _Variants... __vars)
__visit_invoke_impl(_Visitor&& __visitor, _Variants... __vars)
{
if constexpr (is_same_v<_Result_type, __variant_idx_cookie>)
return std::__invoke(std::forward<_Visitor>(__visitor),
if constexpr (is_same_v<_Result_type, __variant_idx_cookie>)
return std::__invoke(std::forward<_Visitor>(__visitor),
__element_by_index_or_cookie<__indices>(
std::forward<_Variants>(__vars))...,
integral_constant<size_t, __indices>()...);
else if constexpr (!__same_return_types &&
std::is_void_v<_Result_type>)
return (void)std::__invoke(std::forward<_Visitor>(__visitor),
__element_by_index_or_cookie<__indices>(
std::forward<_Variants>(__vars))...,
integral_constant<size_t, __indices>()...);
std::forward<_Variants>(__vars))...);
else
return std::__invoke(std::forward<_Visitor>(__visitor),
__element_by_index_or_cookie<__indices>(
std::forward<_Variants>(__vars))...);
}
static constexpr decltype(auto)
__do_visit_invoke(_Visitor&& __visitor, _Variants... __vars)
{
return __visit_invoke_impl(std::forward<_Visitor>(__visitor),
std::forward<_Variants>(__vars)...);
}
static constexpr _Result_type
__do_visit_invoke_r(_Visitor&& __visitor, _Variants... __vars)
{
return __visit_invoke_impl(std::forward<_Visitor>(__visitor),
std::forward<_Variants>(__vars)...);
}
static constexpr decltype(auto)
__visit_invoke(_Visitor&& __visitor, _Variants... __vars)
{
if constexpr (__same_return_types)
return __do_visit_invoke(std::forward<_Visitor>(__visitor),
std::forward<_Variants>(__vars)...);
else
return __do_visit_invoke_r(std::forward<_Visitor>(__visitor),
std::forward<_Variants>(__vars)...);
}
static constexpr auto
_S_apply()
{ return _Array_type{&__visit_invoke}; }
};
template<typename _Result_type, typename _Visitor, typename... _Variants>
template<bool __same_return_types,
typename _Result_type, typename _Visitor, typename... _Variants>
struct __gen_vtable
{
using _Func_ptr = _Result_type (*)(_Visitor&&, _Variants...);
@ -981,7 +1022,8 @@ namespace __variant
static constexpr _Array_type
_S_apply()
{
return __gen_vtable_impl<_Array_type, tuple<_Variants...>,
return __gen_vtable_impl<__same_return_types,
_Array_type, tuple<_Variants...>,
std::index_sequence<>>::_S_apply();
}
@ -1582,7 +1624,9 @@ namespace __variant
std::get<0>(std::forward<_Variants>(__variants))...);
}
template<bool __use_index, typename _Visitor, typename... _Variants>
template<bool __use_index,
bool __same_return_types,
typename _Visitor, typename... _Variants>
constexpr decltype(auto)
__do_visit(_Visitor&& __visitor, _Variants&&... __variants)
{
@ -1592,6 +1636,7 @@ namespace __variant
std::forward<_Variants>(__variants)...));
constexpr auto& __vtable = __detail::__variant::__gen_vtable<
__same_return_types,
_Result_type, _Visitor&&, _Variants&&...>::_S_vtable;
auto __func_ptr = __vtable._M_access(__variants.index()...);
@ -1618,12 +1663,13 @@ namespace __variant
if ((__variants.valueless_by_exception() || ...))
__throw_bad_variant_access("Unexpected index");
if constexpr (std::is_void_v<_Res>)
(void) __do_visit(std::forward<_Visitor>(__visitor),
std::forward<_Variants>(__variants)...);
(void) __do_visit<false, false>(std::forward<_Visitor>(__visitor),
std::forward<_Variants>(__variants)...);
else
return __do_visit(std::forward<_Visitor>(__visitor),
std::forward<_Variants>(__variants)...);
return __do_visit<false, false>(std::forward<_Visitor>(__visitor),
std::forward<_Variants>(__variants)...);
}
#endif

View file

@ -0,0 +1,45 @@
// Copyright (C) 2019 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
// { dg-options "-std=gnu++2a" }
// { dg-do compile { target c++2a } }
#include <variant>
#include <testsuite_hooks.h>
// { dg-error "invalid conversion" "" { target *-*-* } 0 }
// { dg-prune-output "in 'constexpr' expansion" }
void
test01()
{
{
struct Visitor
{
double operator()(double) {return 0.02;}
void operator()(int) {}
};
std::variant<int, double> v;
std::visit(Visitor(), v);
}
}
int
main()
{
test01();
}

View file

@ -42,8 +42,22 @@ test01()
static_assert(std::is_void_v<decltype(std::visit<void>(Visitor{}, v1, v2))>);
}
void test02()
{
struct Visitor
{
int operator()(double) {return 42;}
double operator()(int) {return 0.02;}
};
std::variant<int, double> v;
std::visit<int>(Visitor(), v);
std::visit<const void>(Visitor(), v);
}
int
main()
{
test01();
test02();
}