Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Google GMock and etl::delegate work not together #837

Open
NicoMayer opened this issue Feb 7, 2024 · 2 comments
Open

Google GMock and etl::delegate work not together #837

NicoMayer opened this issue Feb 7, 2024 · 2 comments

Comments

@NicoMayer
Copy link

Hello, I use ETL in an embedded project. GoogleTest is used for software testing. There is now a major problem with etl::delegate and gmock when implementing a test.

The Test:

#include "etl/delegate.h"
#include "gmock/gmock.h"

using namespace std;
using namespace testing;

using Callback = etl::delegate<void(void)>;

class IHardwareSensor {
 public:
  virtual int attachSensorCallback(Callback callback) = 0;
};

class HardwareSensorMock : public IHardwareSensor {
 public:
  MOCK_METHOD(int, attachSensorCallback, (Callback));
};

class Sensor {
 public:
  Sensor(IHardwareSensor &sensor) { sensor.attachSensorCallback(Callback::create<Sensor, &Sensor::handleNewSensorValue>(*this)); }

 private:
  void handleNewSensorValue() {}
};

TEST(SensorTest, init) {
  HardwareSensorMock mockedHardwareSensor;

  Callback newSensorValueCallback;

  EXPECT_CALL(mockedHardwareSensor, attachSensorCallback(An<Callback>())).Times(Exactly(1)).WillOnce(DoAll(SaveArg<0>(&newSensorValueCallback), Return(0)));

  Sensor sensor(mockedHardwareSensor);
}

The Compiler Output (In German)

[build] Microsoft (R) C/C++-Optimierungscompiler Version 19.29.30038.1 für x86
[build] Copyright (C) Microsoft Corporation. Alle Rechte vorbehalten.
[build] 
[build] C:\Projekte\xxxxxxxx\checkout\software\application\xxxxxxxx\_libs\etl\include\etl\private/delegate_cpp11.h(566): error C2039: "()" ist kein Member von "std::tuple<_Ty &&>".
[build]         with
[build]         [
[build]             _Ty=etl::delegate<void (void)>
[build]         ]
[build] C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30037\include\tuple(210): note: Siehe Deklaration von "std::tuple<_Ty &&>"
[build]         with
[build]         [
[build]             _Ty=etl::delegate<void (void)>
[build]         ]
[build] C:\Projekte\xxxxxxxx\checkout\software\application\xxxxxxxx\_libs\etl\include\etl\private/delegate_cpp11.h(123): note: Siehe Verweis auf die gerade kompilierte Instanziierung "TReturn etl::delegate<void (void)>::const_lambda_stub<TLambda>(void *)" der Funktions-Vorlage.
[build]         with
[build]         [
[build]             TReturn=void,
[build]             TLambda=std::tuple<etl::delegate<void (void)> &&>
[build]         ]
[build] C:\Projekte\xxxxxxxx\checkout\software\application\xxxxxxxx\_libs\etl\include\etl\private/delegate_cpp11.h(124): note: Siehe Verweis auf die gerade kompilierte Instanziierung "TReturn etl::delegate<void (void)>::const_lambda_stub<TLambda>(void *)" der Funktions-Vorlage.
[build]         with
[build]         [
[build]             TReturn=void,
[build]             TLambda=std::tuple<etl::delegate<void (void)> &&>
[build]         ]
[build] C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30037\include\tuple(170): note: Siehe Verweis auf die gerade kompilierte Instanziierung "etl::delegate<void (void)>::delegate<std::tuple<_Ty &&>,void>(const TLambda &)" der Funktions-Vorlage.
[build]         with
[build]         [
[build]             _Ty=etl::delegate<void (void)>,
[build]             TLambda=std::tuple<etl::delegate<void (void)> &&>
[build]         ]
[build] C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30037\include\tuple(170): note: Siehe Verweis auf die gerade kompilierte Instanziierung "etl::delegate<void (void)>::delegate<std::tuple<_Ty &&>,void>(const TLambda &)" der Funktions-Vorlage.
[build]         with
[build]         [
[build]             _Ty=etl::delegate<void (void)>,
[build]             TLambda=std::tuple<etl::delegate<void (void)> &&>
[build]         ]
[build] C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30037\include\tuple(258): note: Siehe Verweis auf die gerade kompilierte Instanziierung "std::_Tuple_val<_This>::_Tuple_val<_Ty>(_Other &&)" der Funktions-Vorlage.
[build]         with
[build]         [
[build]             _This=Callback,
[build]             _Ty=std::tuple<etl::delegate<void (void)> &&>,
[build]             _Other=std::tuple<etl::delegate<void (void)> &&>
[build]         ]
[build] C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30037\include\tuple(258): note: Siehe Verweis auf die gerade kompilierte Instanziierung "std::_Tuple_val<_This>::_Tuple_val<_Ty>(_Other &&)" der Funktions-Vorlage.
[build]         with
[build]         [
[build]             _This=Callback,
[build]             _Ty=std::tuple<etl::delegate<void (void)> &&>,
[build]             _Other=std::tuple<etl::delegate<void (void)> &&>
[build]         ]
[build] C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30037\include\tuple(340): note: Siehe Verweis auf die gerade kompilierte Instanziierung "std::tuple<Callback>::tuple<std::_Exact_args_t,_Ty,,0>(_Tag,_This2 &&)" der Funktions-Vorlage.
[build]         with
[build]         [
[build]             _Ty=std::tuple<etl::delegate<void (void)> &&>,
[build]             _Tag=std::_Exact_args_t,
[build]             _This2=std::tuple<etl::delegate<void (void)> &&>
[build]         ]
[build] C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30037\include\tuple(340): note: Siehe Verweis auf die gerade kompilierte Instanziierung "std::tuple<Callback>::tuple<std::_Exact_args_t,_Ty,,0>(_Tag,_This2 &&)" der Funktions-Vorlage.
[build]         with
[build]         [
[build]             _Ty=std::tuple<etl::delegate<void (void)> &&>,
[build]             _Tag=std::_Exact_args_t,
[build]             _This2=std::tuple<etl::delegate<void (void)> &&>
[build]         ]
[build] C:\Projekte\xxxxxxxx\checkout\external\googletest\googlemock\include\gmock/gmock-actions.h(1047): note: Siehe Verweis auf die gerade kompilierte Instanziierung "std::tuple<Callback>::tuple<std::tuple<_Ty &&>,,0>(_This2 &&) noexcept(false)" der Funktions-Vorlage.
[build]         with
[build]         [
[build]             _Ty=etl::delegate<void (void)>,
[build]             _This2=std::tuple<etl::delegate<void (void)> &&>
[build]         ]
[build] C:\Projekte\xxxxxxxx\checkout\external\googletest\googlemock\include\gmock/gmock-actions.h(1047): note: Siehe Verweis auf die gerade kompilierte Instanziierung "std::tuple<Callback>::tuple<std::tuple<_Ty &&>,,0>(_This2 &&) noexcept(false)" der Funktions-Vorlage.
[build]         with
[build]         [
[build]             _Ty=etl::delegate<void (void)>,
[build]             _This2=std::tuple<etl::delegate<void (void)> &&>
[build]         ]
[build] C:\Projekte\xxxxxxxx\checkout\software\application\xxxxxxxx\2_sensor_actuator\test\SensorTest.cpp(32): note: Siehe Verweis auf die gerade kompilierte Instanziierung "testing::internal::DoAllAction<testing::internal::SaveArgAction<0,Callback *>,testing::internal::ReturnAction<int>>::operator testing::Action<F>(void) const<R,Callback>" der Funktions-Vorlage.
[build]         with
[build]         [
[build]             F=int (Callback),
[build]             R=int
[build]         ]
[build] C:\Projekte\xxxxxxxx\checkout\software\application\xxxxxxxx\2_sensor_actuator\test\SensorTest.cpp(32): note: Siehe Verweis auf die gerade kompilierte Instanziierung "testing::internal::DoAllAction<testing::internal::SaveArgAction<0,Callback *>,testing::internal::ReturnAction<int>>::operator testing::Action<F>(void) const<R,Callback>" der Funktions-Vorlage.
[build]         with
[build]         [
[build]             F=int (Callback),
[build]             R=int
[build]         ]
[build] C:\Projekte\xxxxxxxx\checkout\software\application\xxxxxxxx\_libs\etl\include\etl\private/delegate_cpp11.h(556): error C2039: "()" ist kein Member von "std::tuple<_Ty &&>".
[build]         with
[build]         [
[build]             _Ty=etl::delegate<void (void)>
[build]         ]
[build] C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30037\include\tuple(210): note: Siehe Deklaration von "std::tuple<_Ty &&>"
[build]         with
[build]         [
[build]             _Ty=etl::delegate<void (void)>
[build]         ]
[build] C:\Projekte\xxxxxxxx\checkout\software\application\xxxxxxxx\_libs\etl\include\etl\private/delegate_cpp11.h(114): note: Siehe Verweis auf die gerade kompilierte Instanziierung "TReturn etl::delegate<void (void)>::lambda_stub<TLambda>(void *)" der Funktions-Vorlage.
[build]         with
[build]         [
[build]             TReturn=void,
[build]             TLambda=std::tuple<etl::delegate<void (void)> &&>
[build]         ]
[build] C:\Projekte\xxxxxxxx\checkout\software\application\xxxxxxxx\_libs\etl\include\etl\private/delegate_cpp11.h(115): note: Siehe Verweis auf die gerade kompilierte Instanziierung "TReturn etl::delegate<void (void)>::lambda_stub<TLambda>(void *)" der Funktions-Vorlage.
[build]         with
[build]         [
[build]             TReturn=void,
[build]             TLambda=std::tuple<etl::delegate<void (void)> &&>
[build]         ]
[build] C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30037\include\tuple(170): note: Siehe Verweis auf die gerade kompilierte Instanziierung "etl::delegate<void (void)>::delegate<std::tuple<_Ty &&>,void>(TLambda &)" der Funktions-Vorlage.
[build]         with
[build]         [
[build]             _Ty=etl::delegate<void (void)>,
[build]             TLambda=std::tuple<etl::delegate<void (void)> &&>
[build]         ]
[build] C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30037\include\tuple(170): note: Siehe Verweis auf die gerade kompilierte Instanziierung "etl::delegate<void (void)>::delegate<std::tuple<_Ty &&>,void>(TLambda &)" der Funktions-Vorlage.
[build]         with
[build]         [
[build]             _Ty=etl::delegate<void (void)>,
[build]             TLambda=std::tuple<etl::delegate<void (void)> &&>
[build]         ]
[build] C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30037\include\tuple(258): note: Siehe Verweis auf die gerade kompilierte Instanziierung "std::_Tuple_val<_This>::_Tuple_val<std::tuple<_Ty &&>&>(_Other)" der Funktions-Vorlage.
[build]         with
[build]         [
[build]             _This=const Callback &,
[build]             _Ty=etl::delegate<void (void)>,
[build]             _Other=std::tuple<etl::delegate<void (void)> &&> &
[build]         ]
[build] C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30037\include\tuple(258): note: Siehe Verweis auf die gerade kompilierte Instanziierung "std::_Tuple_val<_This>::_Tuple_val<std::tuple<_Ty &&>&>(_Other)" der Funktions-Vorlage.
[build]         with
[build]         [
[build]             _This=const Callback &,
[build]             _Ty=etl::delegate<void (void)>,
[build]             _Other=std::tuple<etl::delegate<void (void)> &&> &
[build]         ]
[build] C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30037\include\tuple(340): note: Siehe Verweis auf die gerade kompilierte Instanziierung "std::tuple<const Callback &>::tuple<std::_Exact_args_t,std::tuple<_Ty &&>&,,0>(_Tag,_This2)" der Funktions-Vorlage.
[build]         with
[build]         [
[build]             _Ty=etl::delegate<void (void)>,
[build]             _Tag=std::_Exact_args_t,
[build]             _This2=std::tuple<etl::delegate<void (void)> &&> &
[build]         ]
[build] C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30037\include\tuple(340): note: Siehe Verweis auf die gerade kompilierte Instanziierung "std::tuple<const Callback &>::tuple<std::_Exact_args_t,std::tuple<_Ty &&>&,,0>(_Tag,_This2)" der Funktions-Vorlage.
[build]         with
[build]         [
[build]             _Ty=etl::delegate<void (void)>,
[build]             _Tag=std::_Exact_args_t,
[build]             _This2=std::tuple<etl::delegate<void (void)> &&> &
[build]         ]
[build] C:\Projekte\xxxxxxxx\checkout\external\googletest\googlemock\include\gmock/gmock-actions.h(1047): note: Siehe Verweis auf die gerade kompilierte Instanziierung "std::tuple<const Callback &>::tuple<std::tuple<_Ty &&>&,,0>(_This2) noexcept" der Funktions-Vorlage.
[build]         with
[build]         [
[build]             _Ty=etl::delegate<void (void)>,
[build]             _This2=std::tuple<etl::delegate<void (void)> &&> &
[build]         ]
[build] C:\Projekte\xxxxxxxx\checkout\external\googletest\googlemock\include\gmock/gmock-actions.h(1047): note: Siehe Verweis auf die gerade kompilierte Instanziierung "std::tuple<const Callback &>::tuple<std::tuple<_Ty &&>&,,0>(_This2) noexcept" der Funktions-Vorlage.
[build]         with
[build]         [
[build]             _Ty=etl::delegate<void (void)>,
[build]             _This2=std::tuple<etl::delegate<void (void)> &&> &
[build]         ]
[build] [5/7] Building CXX object test\CMakeFiles\xxxxxxxx_dev_test.dir\__\_types\test\EventManagerTest.cpp.obj
[build] Microsoft (R) C/C++-Optimierungscompiler Version 19.29.30038.1 für x86
[build] Copyright (C) Microsoft Corporation. Alle Rechte vorbehalten.
[build] 
[build] [6/7] Building CXX object test\CMakeFiles\xxxxxxxx_dev_test.dir\__\_types\test\SwitchingRecordTest.cpp.obj
[build] Microsoft (R) C/C++-Optimierungscompiler Version 19.29.30038.1 für x86
[build] Copyright (C) Microsoft Corporation. Alle Rechte vorbehalten.
[build] 
[build] C:\Projekte\xxxxxxxx\checkout\software\application\xxxxxxxx\_types\test\SwitchingRecordTest.cpp(189): warning C4305: "Initialisierung": Verkürzung von "double" in "_Ty"
[build]         with
[build]         [
[build]             _Ty=float
[build]         ]
[build] C:\Projekte\xxxxxxxx\checkout\software\application\xxxxxxxx\_types\test\SwitchingRecordTest.cpp(197): warning C4305: "Initialisierung": Verkürzung von "double" in "_Ty"
[build]         with
[build]         [
[build]             _Ty=float
[build]         ]
[build] ninja: build stopped: subcommand failed.
[proc] Der Befehl "c:/tools/cmake-3.25.2-windows-x86_64/cmake-3.25.2-windows-x86_64/bin/cmake.exe --build C:/Projekte/xxxxxxxx/builds/xxxxxxxx_dev/Win32_VCpp.TestCoverage --parallel 18 --target all" wurde mit dem Code "1" beendet.
[driver] Build abgeschlossen: 00:00:01.630
[build] Der Build wurde mit dem Exitcode 1 abgeschlossen.

The problem is that the wrong delegate constructors are being called.
To fix the problem I have added a std::is_invocable_r check to the constructors

The quick fix

    //*************************************************************************
    // Construct from lambda or functor.
    //*************************************************************************
    template <typename TLambda, typename = etl::enable_if_t<etl::is_class<TLambda>::value && !etl::is_same<etl::delegate<TReturn(TParams...)>, TLambda>::value && std::is_invocable_r<TReturn, TLambda, TParams...>::value, void>>
    ETL_CONSTEXPR14 delegate(TLambda& instance)
    {
      assign((void*)(&instance), lambda_stub<TLambda>);
    }

    //*************************************************************************
    // Construct from const lambda or functor.
    //*************************************************************************
    template <typename TLambda, typename = etl::enable_if_t<etl::is_class<TLambda>::value && !etl::is_same<etl::delegate<TReturn(TParams...)>, TLambda>::value && std::is_invocable_r<TReturn, TLambda, TParams...>::value, void>>
    ETL_CONSTEXPR14 delegate(const TLambda& instance)
    {
      assign((void*)(&instance), const_lambda_stub<TLambda>);
    }

So the question now is how I should deal with the problem in the long term.

Best regards and sorry for my bad English
Nico

@jwellbelove
Copy link
Contributor

Unfortunately, many type traits are next to impossible for the ETL to reverse engineer due to the reliance on the compiler's 'insider knowledge'.
The only way to use your fix would be to only enable it if the STL is being used.
#if ETL_USING_STL

@ilya-a1
Copy link

ilya-a1 commented Jul 8, 2024

The following does work for unittests:
EXPECT_CALL(mockedHardwareSensor, attachSensorCallback(_)).Times(Exactly(1)).WillOnce(
Invoke( [&newSensorValueCallback](auto& cb){ newSensorValueCallback = Callback::create(cb); return 0; }
);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants