Использование std::any с gmock приводит к другому поведению на gcc 7 и 9
Я пытался смоделировать шаблонную функцию в классе и найти хороший пример в этом вопросе
Поэтому я попытался использовать std::any
с googlemock, и ниже код подходит.
#include <any>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
using ::testing::Return;
class Base
{
public:
virtual ~Base() = default;
template<typename T>
T GetData() const
{
auto data = GetDataImpl();
return std::any_cast<T>(data);
}
virtual std::any GetDataImpl() const = 0;
};
class BaseMock : public Base
{
public:
virtual ~BaseMock() = default;
MOCK_CONST_METHOD0(GetDataImpl, std::any());
};
const Base& GetBase()
{
static BaseMock base;
return base;
}
template<typename T>
T GetData()
{
return GetBase().GetData<T>();
}
TEST(MyTest, BaseReturnBool)
{
const auto& base = reinterpret_cast<const BaseMock&>(GetBase());
EXPECT_CALL(base, GetDataImpl()).WillOnce(Return(std::any(true)));
EXPECT_TRUE(GetData<bool>());
}
TEST(MyTest, BaseReturnString)
{
const auto& base = reinterpret_cast<const BaseMock&>(GetBase());
EXPECT_CALL(base, GetDataImpl()).WillOnce(Return(std::any(std::string("test"))));
EXPECT_EQ(GetData<std::string>(), "test");
}
Он отлично работает с GCC7.4.0 на Ubuntu 18.04, с gtest release-1.8.1.
$ g++ --version
g++ (Ubuntu 7.4.0-1ubuntu1~18.04) 7.4.0
...
$ g++ -std=c++17 -o utest test_gmock_with_std_any.cpp -lgmock -lgtest -lgtest_main -pthread
Но это не в состоянии построить на GCC 9.1.0:
$ g++-9 --version
g++-9 (Ubuntu 9.1.0-2ubuntu2~18.04) 9.1.0
...
$ g++-9 -std=c++17 -o utest test_gmock_with_std_any.cpp -lgmock -lgtest -lgtest_main -pthread
In file included from /usr/include/c++/9/bits/move.h:55,
from /usr/include/c++/9/bits/nested_exception.h:40,
from /usr/include/c++/9/exception:144,
from /usr/include/c++/9/new:40,
from /usr/include/c++/9/any:37,
from test_gmock_with_std_any.cpp:1:
/usr/include/c++/9/type_traits: In instantiation of ‘struct std::__and_<std::is_copy_constructible<testing::internal::ReferenceOrValueWrapper<std::any> >, std::is_constructible<testing::internal::ReferenceOrValueWrapper<std::any>, const testing::internal::ReferenceOrValueWrapper<std::any>&> >’:
/usr/include/c++/9/any:181:58: required by substitution of ‘template<class _ValueType, class _Tp, class _Mgr, typename std::enable_if<std::__and_<std::is_copy_constructible<_Tp>, std::is_construct
ible<_Tp, _ValueType&&> >::value, bool>::type <anonymous>, typename std::enable_if<(! std::__is_in_place_type<_Tp>::value), bool>::type <anonymous> > std::any::any(_ValueType&&) [with _ValueType = const testing::internal::ReferenceOrValueWrapper<std::any>&; _Tp = testing::internal::ReferenceOrValueWrapper<std::any>; _Mgr = std::any::_Manager_external<testing::internal::ReferenceOrValueWrapper<
std::any> >; typename std::enable_if<std::__and_<std::is_copy_constructible<_Tp>, std::is_constructible<_Tp, _ValueType&&> >::value, bool>::type <anonymous> = <missing>; typename std::enable_if<(! s
td::__is_in_place_type<_Tp>::value), bool>::type <anonymous> = <missing>]’
/usr/include/c++/9/type_traits:883:12: required from ‘struct std::is_constructible<testing::internal::ReferenceOrValueWrapper<std::any>, const testing::internal::ReferenceOrValueWrapper<std::any>&
>’
/usr/include/c++/9/type_traits:901:12: required from ‘struct std::__is_copy_constructible_impl<testing::internal::ReferenceOrValueWrapper<std::any>, true>’
/usr/include/c++/9/type_traits:907:12: required from ‘struct std::is_copy_constructible<testing::internal::ReferenceOrValueWrapper<std::any> >’
/usr/include/c++/9/type_traits:131:12: required from ‘struct std::__and_<std::is_copy_constructible<testing::internal::ReferenceOrValueWrapper<std::any> >, std::is_constructible<testing::internal:
:ReferenceOrValueWrapper<std::any>, testing::internal::ReferenceOrValueWrapper<std::any>&&> >’
/usr/include/c++/9/any:181:58: required by substitution of ‘template<class _ValueType, class _Tp, class _Mgr, typename std::enable_if<std::__and_<std::is_copy_constructible<_Tp>, std::is_constructible<_Tp, _ValueType&&> >::value, bool>::type <anonymous>, typename std::enable_if<(! std::__is_in_place_type<_Tp>::value), bool>::type <anonymous> > std::any::any(_ValueType&&) [with _ValueType = testing::internal::ReferenceOrValueWrapper<std::any>; _Tp = testing::internal::ReferenceOrValueWrapper<std::any>; _Mgr = std::any::_Manager_external<testing::internal::ReferenceOrValueWrapper<std::an
y> >; typename std::enable_if<std::__and_<std::is_copy_constructible<_Tp>, std::is_constructible<_Tp, _ValueType&&> >::value, bool>::type <anonymous> = <missing>; typename std::enable_if<(! std::__is_in_place_type<_Tp>::value), bool>::type <anonymous> = <missing>]’
/usr/local/include/gmock/gmock-spec-builders.h:1423:50: required from ‘testing::internal::ActionResultHolder<T>::ActionResultHolder(testing::internal::ActionResultHolder<T>::Wrapper) [with T = std
::any; testing::internal::ActionResultHolder<T>::Wrapper = testing::internal::ReferenceOrValueWrapper<std::any>]’
/usr/local/include/gmock/gmock-spec-builders.h:1405:12: required from ‘static testing::internal::ActionResultHolder<T>* testing::internal::ActionResultHolder<T>::PerformDefaultAction(const testing::internal::FunctionMockerBase<F>*, typename testing::internal::RvalueRef<typename testing::internal::Function<F>::ArgumentTuple>::type, const string&) [with F = std::any(); T = std::any; typename t
esting::internal::RvalueRef<typename testing::internal::Function<F>::ArgumentTuple>::type = std::tuple<>&&; std::string = std::__cxx11::basic_string<char>]’
/usr/local/include/gmock/gmock-spec-builders.h:1542:46: required from ‘testing::internal::UntypedActionResultHolderBase* testing::internal::FunctionMockerBase<F>::UntypedPerformDefaultAction(void*, const string&) const [with F = std::any(); std::string = std::__cxx11::basic_string<char>]’
/usr/local/include/gmock/gmock-spec-builders.h:1538:42: required from here
/usr/include/c++/9/type_traits:131:12: error: incomplete type ‘std::is_copy_constructible<testing::internal::ReferenceOrValueWrapper<std::any> >’ used in nested name specifier
131 | struct __and_<_B1, _B2>
| ^~~~~~~~~~~~~~~~
/usr/include/c++/9/type_traits: In instantiation of ‘struct std::__and_<std::is_copy_constructible<testing::internal::ReferenceOrValueWrapper<std::any> >, std::__not_<std::is_constructible<testing::
internal::ReferenceOrValueWrapper<std::any>, const testing::internal::ReferenceOrValueWrapper<std::any>&> >, std::__not_<std::__is_in_place_type<testing::internal::ReferenceOrValueWrapper<std::any> > > >’:
/usr/include/c++/9/type_traits:150:27: required from ‘constexpr const bool std::__and_v<std::is_copy_constructible<testing::internal::ReferenceOrValueWrapper<std::any> >, std::__not_<std::is_const
ructible<testing::internal::ReferenceOrValueWrapper<std::any>, const testing::internal::ReferenceOrValueWrapper<std::any>&> >, std::__not_<std::__is_in_place_type<testing::internal::ReferenceOrValue
Wrapper<std::any> > > >’
/usr/include/c++/9/any:192:27: required by substitution of ‘template<class _ValueType, class _Tp, class _Mgr, typename std::enable_if<__and_v<std::is_copy_constructible<_Tp>, std::__not_<std::is_c
onstructible<_Tp, _ValueType&&> >, std::__not_<std::__is_in_place_type<_Tp> > >, bool>::type <anonymous> > std::any::any(_ValueType&&) [with _ValueType = const testing::internal::ReferenceOrValueWra
pper<std::any>&; _Tp = testing::internal::ReferenceOrValueWrapper<std::any>; _Mgr = std::any::_Manager_external<testing::internal::ReferenceOrValueWrapper<std::any> >; typename std::enable_if<__and_
v<std::is_copy_constructible<_Tp>, std::__not_<std::is_constructible<_Tp, _ValueType&&> >, std::__not_<std::__is_in_place_type<_Tp> > >, bool>::type <anonymous> = <missing>]’
/usr/include/c++/9/type_traits:883:12: required from ‘struct std::is_constructible<testing::internal::ReferenceOrValueWrapper<std::any>, const testing::internal::ReferenceOrValueWrapper<std::any>&
>’ /usr/include/c++/9/type_traits:901:12: required from ‘struct std::__is_copy_constructible_impl<testing::internal::ReferenceOrValueWrapper<std::any>, true>’ /usr/include/c++/9/type_traits:907:12: required from ‘struct std::is_copy_constructible<testing::internal::ReferenceOrValueWrapper<std::any> >’
/usr/include/c++/9/type_traits:131:12: required from ‘struct std::__and_<std::is_copy_constructible<testing::internal::ReferenceOrValueWrapper<std::any> >, std::is_constructible<testing::internal::ReferenceOrValueWrapper<std::any>, testing::internal::ReferenceOrValueWrapper<std::any>&&> >’
/usr/include/c++/9/any:181:58: required by substitution of ‘template<class _ValueType, class _Tp, class _Mgr, typename std::enable_if<std::__and_<std::is_copy_constructible<_Tp>, std::is_construct
ible<_Tp, _ValueType&&> >::value, bool>::type <anonymous>, typename std::enable_if<(! std::__is_in_place_type<_Tp>::value), bool>::type <anonymous> > std::any::any(_ValueType&&) [with _ValueType = t
esting::internal::ReferenceOrValueWrapper<std::any>; _Tp = testing::internal::ReferenceOrValueWrapper<std::any>; _Mgr = std::any::_Manager_external<testing::internal::ReferenceOrValueWrapper<std::any> >; typename std::enable_if<std::__and_<std::is_copy_constructible<_Tp>, std::is_constructible<_Tp, _ValueType&&> >::value, bool>::type <anonymous> = <missing>; typename std::enable_if<(! std::__i
s_in_place_type<_Tp>::value), bool>::type <anonymous> = <missing>]’
/usr/local/include/gmock/gmock-spec-builders.h:1423:50: required from ‘testing::internal::ActionResultHolder<T>::ActionResultHolder(testing::internal::ActionResultHolder<T>::Wrapper) [with T = std::any; testing::internal::ActionResultHolder<T>::Wrapper = testing::internal::ReferenceOrValueWrapper<std::any>]’
/usr/local/include/gmock/gmock-spec-builders.h:1405:12: required from ‘static testing::internal::ActionResultHolder<T>* testing::internal::ActionResultHolder<T>::PerformDefaultAction(const testing
::internal::FunctionMockerBase<F>*, typename testing::internal::RvalueRef<typename testing::internal::Function<F>::ArgumentTuple>::type, const string&) [with F = std::any(); T = std::any; typename t
esting::internal::RvalueRef<typename testing::internal::Function<F>::ArgumentTuple>::type = std::tuple<>&&; std::string = std::__cxx11::basic_string<char>]’
/usr/local/include/gmock/gmock-spec-builders.h:1542:46: required from ‘testing::internal::UntypedActionResultHolderBase* testing::internal::FunctionMockerBase<F>::UntypedPerformDefaultAction(void*
, const string&) const [with F = std::any(); std::string = std::__cxx11::basic_string<char>]’ /usr/local/include/gmock/gmock-spec-builders.h:1538:42: required from here /usr/include/c++/9/type_traits:136:12: error: incomplete type ‘std::is_copy_constructible<testing::internal::ReferenceOrValueWrapper<std::any> >’ used in nested name specifier
136 | struct __and_<_B1, _B2, _B3, _Bn...> | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/c++/9/type_traits: In instantiation of ‘constexpr const bool std::__and_v<std::is_copy_constructible<testing::internal::ReferenceOrValueWrapper<std::any> >, std::__not_<std::is_construc
tible<testing::internal::ReferenceOrValueWrapper<std::any>, const testing::internal::ReferenceOrValueWrapper<std::any>&> >, std::__not_<std::__is_in_place_type<testing::internal::ReferenceOrValueWrapper<std::any> > > >’: /usr/include/c++/9/any:192:27: required by substitution of ‘template<class _ValueType, class _Tp, class _Mgr, typename std::enable_if<__and_v<std::is_copy_constructible<_Tp>, std::__not_<std::is_c
onstructible<_Tp, _ValueType&&> >, std::__not_<std::__is_in_place_type<_Tp> > >, bool>::type <anonymous> > std::any::any(_ValueType&&) [with _ValueType = const testing::internal::ReferenceOrValueWra
pper<std::any>&; _Tp = testing::internal::ReferenceOrValueWrapper<std::any>; _Mgr = std::any::_Manager_external<testing::internal::ReferenceOrValueWrapper<std::any> >; typename std::enable_if<__and_
v<std::is_copy_constructible<_Tp>, std::__not_<std::is_constructible<_Tp, _ValueType&&> >, std::__not_<std::__is_in_place_type<_Tp> > >, bool>::type <anonymous> = <missing>]’
/usr/include/c++/9/type_traits:883:12: required from ‘struct std::is_constructible<testing::internal::ReferenceOrValueWrapper<std::any>, const testing::internal::ReferenceOrValueWrapper<std::any>&
>’
/usr/include/c++/9/type_traits:901:12: required from ‘struct std::__is_copy_constructible_impl<testing::internal::ReferenceOrValueWrapper<std::any>, true>’
/usr/include/c++/9/type_traits:907:12: required from ‘struct std::is_copy_constructible<testing::internal::ReferenceOrValueWrapper<std::any> >’
/usr/include/c++/9/type_traits:131:12: required from ‘struct std::__and_<std::is_copy_constructible<testing::internal::ReferenceOrValueWrapper<std::any> >, std::is_constructible<testing::internal::ReferenceOrValueWrapper<std::any>, testing::internal::ReferenceOrValueWrapper<std::any>&&> >’
/usr/include/c++/9/any:181:58: required by substitution of ‘template<class _ValueType, class _Tp, class _Mgr, typename std::enable_if<std::__and_<std::is_copy_constructible<_Tp>, std::is_construct
ible<_Tp, _ValueType&&> >::value, bool>::type <anonymous>, typename std::enable_if<(! std::__is_in_place_type<_Tp>::value), bool>::type <anonymous> > std::any::any(_ValueType&&) [with _ValueType = testing::internal::ReferenceOrValueWrapper<std::any>; _Tp = testing::internal::ReferenceOrValueWrapper<std::any>; _Mgr = std::any::_Manager_external<testing::internal::ReferenceOrValueWrapper<std::an
y> >; typename std::enable_if<std::__and_<std::is_copy_constructible<_Tp>, std::is_constructible<_Tp, _ValueType&&> >::value, bool>::type <anonymous> = <missing>; typename std::enable_if<(! std::__i
s_in_place_type<_Tp>::value), bool>::type <anonymous> = <missing>]’
/usr/local/include/gmock/gmock-spec-builders.h:1423:50: required from ‘testing::internal::ActionResultHolder<T>::ActionResultHolder(testing::internal::ActionResultHolder<T>::Wrapper) [with T = std
::any; testing::internal::ActionResultHolder<T>::Wrapper = testing::internal::ReferenceOrValueWrapper<std::any>]’
/usr/local/include/gmock/gmock-spec-builders.h:1405:12: required from ‘static testing::internal::ActionResultHolder<T>* testing::internal::ActionResultHolder<T>::PerformDefaultAction(const testing::internal::FunctionMockerBase<F>*, typename testing::internal::RvalueRef<typename testing::internal::Function<F>::ArgumentTuple>::type, const string&) [with F = std::any(); T = std::any; typename testing::internal::RvalueRef<typename testing::internal::Function<F>::ArgumentTuple>::type = std::tuple<>&&; std::string = std::__cxx11::basic_string<char>]’
/usr/local/include/gmock/gmock-spec-builders.h:1542:46: required from ‘testing::internal::UntypedActionResultHolderBase* testing::internal::FunctionMockerBase<F>::UntypedPerformDefaultAction(void*, const string&) const [with F = std::any(); std::string = std::__cxx11::basic_string<char>]’
/usr/local/include/gmock/gmock-spec-builders.h:1538:42: required from here
/usr/include/c++/9/type_traits:150:27: error: ‘value’ is not a member of ‘std::__and_<std::is_copy_constructible<testing::internal::ReferenceOrValueWrapper<std::any> >, std::__not_<std::is_constructible<testing::internal::ReferenceOrValueWrapper<std::any>, const testing::internal::ReferenceOrValueWrapper<std::any>&> >, std::__not_<std::__is_in_place_type<testing::internal::ReferenceOrValueWrapper<std::any> > > >’
150 | inline constexpr bool __and_v = __and_<_Bn...>::value;
Итак, вопросы:
- Вышеуказанный код действителен или нет?
- Если это верно, что не так с проблемой? Может быть, ошибка в gcc-9?
[Обновить]
Похоже, первопричину проблемы можно упростить до приведенного ниже кода, где ReferenceOrValueWrapper
это класс в gmock.
#include <any>
#include <cstdio>
template <typename T>
class ReferenceOrValueWrapper {
public:
explicit ReferenceOrValueWrapper(T value)
: value_(std::move(value)) {
}
T Unwrap() { return std::move(value_); }
const T& Peek() const {
return value_;
}
private:
T value_;
};
template <typename T>
class ReferenceOrValueWrapper<T&> {
public:
typedef T& reference;
explicit ReferenceOrValueWrapper(reference ref)
: value_ptr_(&ref) {}
T& Unwrap() { return *value_ptr_; }
const T& Peek() const { return *value_ptr_; }
private:
T* value_ptr_;
};
int main()
{
// Below code generates the same error
constexpr bool b = std::is_copy_constructible<ReferenceOrValueWrapper<std::any>>::value;
printf("b: %d\n", b);
}
Когда мы ставим std::any
в ReferenceOrValueWrapper
проблема в том, что ни один член не назван value
в std::is_copy_constructible<ReferenceOrValueWrapper<std::any>>