ETLCPP / etl

Embedded Template Library
https://www.etlcpp.com
MIT License
2.05k stars 372 forks source link

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

Open NicoMayer opened 4 months ago

NicoMayer commented 4 months ago

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 commented 4 months ago

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