Mock member function call to lambda - c++

Using c++17 and gmock, I am mocking a class and would like to redirect calls to one of its member functions to a lambda. Is this possible?
He're a minimal example:
#include <gmock/gmock.h>
using ::testing::_;
using ::testing::Invoke;
using ::testing::Return;
class Foo
{
public:
virtual uint8_t MyCall(const uint8_t in) const
{
return in;
}
};
class MockFoo : public Foo
{
public:
MOCK_METHOD(uint8_t, MyCall, (const uint8_t), (const, override));
};
TEST(MyTest, MyTestCase)
{
MockFoo mock_foo;
ON_CALL(mock_foo, MyCall(_)).WillByDefault(Invoke([](const uint8_t to) {
static_cast<void>(to);
}));
}
I am getting the following error when compiling:
demo.cpp: In member function 'virtual void MyTest_MyTestCase_Test::TestBody()':
demo.cpp:82:7: error: no matching function for call to 'testing::internal::OnCallSpec<unsigned char(unsigned char)>::WillByDefault(std::decay<MyTest_MyTestCase_Test::TestBody()::<lambda(uint8_t)> >::type)'
}));
^
In file included from external/gtest/googlemock/include/gmock/gmock-function-mocker.h:42:0,
from external/gtest/googlemock/include/gmock/gmock.h:61,
from demo.cpp:2:
external/gtest/googlemock/include/gmock/gmock-spec-builders.h:323:15: note: candidate: testing::internal::OnCallSpec<F>& testing::internal::OnCallSpec<F>::WillByDefault(const testing::Action<F>&) [with F = unsigned char(unsigned char)]
OnCallSpec& WillByDefault(const Action<F>& action) {
^~~~~~~~~~~~~
external/gtest/googlemock/include/gmock/gmock-spec-builders.h:323:15: note: no known conversion for argument 1 from 'std::decay<MyTest_MyTestCase_Test::TestBody()::<lambda(uint8_t)> >::type {aka MyTest_MyTestCase_Test::TestBody()::<lambda(uint8_t)>}' to 'const testing::Action<unsigned char(unsigned char)>&

The return type of your lambda does not match that of your member function
The error messages of gmock can be somewhat cryptic, and it doesn't help when you are working with types that are typedefs to fundamental types; in this case uint8_t.
If we look closer at the error message:
error: no matching function for call to
'testing::internal::OnCallSpec<unsigned char(unsigned char)>
::WillByDefault(std::decay<MyTest_MyTestCase_Test::TestBody()
::<lambda(uint8_t)> >::type)'
it's actually providing some hints:
the OnCallSpec of unsigned char(unsigned char),
does not match the type of the provided (WillByDefault) callable.
The former, when looking at your program and manually translating in the fixed-width typedefs, actually tell us:
the OnCallSpec of uint8_t(uint8_t),
which makes it more apparent that something is wrong with the type of the default callable, namely the lambda.
In this particular case, the return type of the lambda is (implicitly) void, thus the mismatch of (disregarding CV-qualifiers) void(uint8_t) with the "on call spec" of uint8_t(uint8_t).

Related

Opaque error message for Hello World LLVM pass in C++

I wrote a simple Hello World LLVM pass using the new pass manager.
It is important to note that I did not obtain the entire LLVM source and write the pass somewhere in this source tree.
Instead I installed llvm with
sudo apt install llvm
However when I want to compile my pass code to a dynamic library to run it with opt as a plugin I get the following error message (and yes, later on I will also use cmake for this but it gave me a similar error):
$ clang -shared -I/usr/include/llvm-13 -I/usr/include/llvm-c-13 -o libpass_test.so pass_test.cpp
In file included from pass_test.cpp:1:
In file included from /usr/include/llvm-13/llvm/IR/PassManager.h:48:
/usr/include/llvm-13/llvm/IR/PassManagerInternal.h:85:16: error: member reference base type 'bool (llvm::StringRef, llvm::PassManager<llvm::Function> &, llvm::ArrayRef<llvm::PassBuilder::PipelineElement>)' is not a structure or union
return Pass.run(IR, AM, ExtraArgs...);
~~~~^~~~
/usr/include/llvm-13/llvm/IR/PassManagerInternal.h:67:12: note: in instantiation of member function 'llvm::detail::PassModel<llvm::Function, bool (&)(llvm::StringRef, llvm::PassManager<llvm::Function> &, llvm::ArrayRef<llvm::PassBuilder::PipelineElement>), llvm::PreservedAnalyses, llvm::AnalysisManager<llvm::Function>>::run' requested here
explicit PassModel(PassT Pass) : Pass(std::move(Pass)) {}
^
/usr/include/llvm-13/llvm/IR/PassManager.h:547:29: note: in instantiation of member function 'llvm::detail::PassModel<llvm::Function, bool (&)(llvm::StringRef, llvm::PassManager<llvm::Function> &, llvm::ArrayRef<llvm::PassBuilder::PipelineElement>), llvm::PreservedAnalyses, llvm::AnalysisManager<llvm::Function>>::PassModel' requested here
Passes.emplace_back(new PassModelT(std::forward<PassT>(Pass)));
^
pass_test.cpp:29:6: note: in instantiation of function template specialization 'llvm::PassManager<llvm::Function>::addPass<bool (&)(llvm::StringRef, llvm::PassManager<llvm::Function> &, llvm::ArrayRef<llvm::PassBuilder::PipelineElement>)>' requested here
FPM.addPass(passHook);
^
In file included from pass_test.cpp:1:
In file included from /usr/include/llvm-13/llvm/IR/PassManager.h:48:
/usr/include/llvm-13/llvm/IR/PassManagerInternal.h:88:44: error: type 'bool (&)(llvm::StringRef, llvm::PassManager<llvm::Function> &, llvm::ArrayRef<llvm::PassBuilder::PipelineElement>)' cannot be used prior to '::' because it has no members
StringRef name() const override { return PassT::name(); }
^
/usr/include/llvm-13/llvm/IR/PassManagerInternal.h:67:12: note: in instantiation of member function 'llvm::detail::PassModel<llvm::Function, bool (&)(llvm::StringRef, llvm::PassManager<llvm::Function> &, llvm::ArrayRef<llvm::PassBuilder::PipelineElement>), llvm::PreservedAnalyses, llvm::AnalysisManager<llvm::Function>>::name' requested here
explicit PassModel(PassT Pass) : Pass(std::move(Pass)) {}
^
/usr/include/llvm-13/llvm/IR/PassManager.h:547:29: note: in instantiation of member function 'llvm::detail::PassModel<llvm::Function, bool (&)(llvm::StringRef, llvm::PassManager<llvm::Function> &, llvm::ArrayRef<llvm::PassBuilder::PipelineElement>), llvm::PreservedAnalyses, llvm::AnalysisManager<llvm::Function>>::PassModel' requested here
Passes.emplace_back(new PassModelT(std::forward<PassT>(Pass)));
^
pass_test.cpp:29:6: note: in instantiation of function template specialization 'llvm::PassManager<llvm::Function>::addPass<bool (&)(llvm::StringRef, llvm::PassManager<llvm::Function> &, llvm::ArrayRef<llvm::PassBuilder::PipelineElement>)>' requested here
FPM.addPass(passHook);
^
2 errors generated.
The source code is this:
pass_test.h:
#ifndef PASS_TEST_H
#define PASS_TEST_H
using namespace llvm;
class PassTest : public PassInfoMixin<PassTest> {
public:
PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
};
#endif
pass_test.cpp:
#include <llvm/IR/PassManager.h>
#include <llvm/Passes/PassBuilder.h>
#include <llvm/Passes/PassPlugin.h>
#include <llvm/Support/raw_ostream.h>
#include "pass_test.h"
//------------------------------------------------------------------------------
// The pass functionality
//------------------------------------------------------------------------------
PreservedAnalyses PassTest::run(Function &F, FunctionAnalysisManager &AM)
{
errs() << F.getName() << "\n";
return PreservedAnalyses::all();
}
//------------------------------------------------------------------------------
// Register the pass as a plugin for opt
//------------------------------------------------------------------------------
// the hook of my pass for opt
bool passHook(StringRef Name, FunctionPassManager &FPM,
ArrayRef<PassBuilder::PipelineElement>)
{
if (Name != "pass_test")
return false;
FPM.addPass(passHook);
return true;
}
// the pass builder hook using my pass hook
void builderHook(PassBuilder &PB)
{
PB.registerPipelineParsingCallback(passHook);
}
// information for this specific pass
llvm::PassPluginLibraryInfo getPassTestPluginInfo()
{
return {LLVM_PLUGIN_API_VERSION, "pass_test",
LLVM_VERSION_STRING, builderHook};
}
// public entry for my pass
extern "C" LLVM_ATTRIBUTE_WEAK llvm::PassPluginLibraryInfo
llvmGetPassPluginInfo()
{
return getPassTestPluginInfo();
}
I am happy for any hint what the actual problem is because I don't understand what exactly this error message is telling me.

Templatized constructor unable to perform conversion

Here is a standalone use case of what I am trying to achieve
//Bar.hpp
#ifndef BAR_HPP
#define BAR_HPP
constexpr bool areNamesEqual(const char* name1,const char* name2)
{
return ((*name1 == *name2) && (*name1 == '\0' || areNamesEqual(name1 + 1,name2 + 1)));
}
#endif
Then I have a class which uses this comparison utility as follows
// Foo.hpp
#ifndef FOO_HPP
#define FOO_HPP
#include "Bar.hpp"
class Foo
{
public:
template<typename T_0>
Foo(const T_0 & var_0)
{
static_assert(areNamesEqual(T_0::formatter_name,"Hole"),"Incorrect hole type supplied!");
}
};
#endif
Finally I have another class, which actually provides an argument for the comparison as follows
// Hole.hpp
#ifndef HOLE_HPP
#define HOLE_HPP
class Hole {
public:
Hole(double dx) : d(dx) {}
static constexpr const char* formatter_name = "Hole";
private:
double d;
};
#endif
In my main.cpp when I invoke this as below
//main.cpp
#include "Foo.hpp"
#include "Hole.hpp"
int main()
{
Foo f(43);
return 0;
}
g++(6.3) with --std=c++14 gives me following error
In file included from main.cpp:1:0:
Foo.hpp: In instantiation of ‘Foo::Foo(const T_0&) [with T_0 = int]’:
main.cpp:6:13: required from here
Foo.hpp:12:36: error: ‘formatter_name’ is not a member of ‘int’
static_assert(areNamesEqual(T_0::formatter_name,"Hole"),"Incorrect hole type supplied!");
Why cant the compiler convert double type to Hole class implicitly ?
I am not sure if conversion operator of Hole class would help me out here.
:UPDATE:
Updated the snippet to show the error for int literal.
Let's analyse the compiler error:
Foo.hpp: In instantiation of ‘Foo::Foo(const T_0&) [with T_0 = int]’:
Means T_0 is deduced to type int (side note: are you sure you are not giving the error when you tried with 43 literal, instead of 43.0?)
So, the type of T_0 is fixed from here. Then:
Foo.hpp:12:36: error: ‘formatter_name’ is not a member of ‘int’.
Which is true: the primitive type int does not have members at all, so it does not have formatter_name member in particular.
This explains the error, which is as-prescribed by the C++ standard.
Now, you mention expecting conversion, is it because of the non-explicit constructor for Hole taking a double?
If so, this conversion would implicitly happen only if you gave a double to a "context" expecting an Hole instance.
E.g, if you changed the Foo initialization to Foo f<Hole>(43.0); or Foo f(Hole{43.0});
This is absolutely not the case in you example: you give a double to Foo constructor that is templated on the argument type, and you do not force the template type yourself. So function template type deduction kicks-in, and it exactly matches the argument type. Additionally, implementing any conversion operator would not change that fact.

tr1::functional error with g++ 4.8.1

Code:
#include <tr1/functional>
class Test
{
public:
Test() { ; }
virtual void foo() = 0;
};
void someFunc(Test& j)
{
j.foo();
}
void func(Test& j)
{
std::tr1::bind(someFunc, std::tr1::ref(j));
}
Using g++ 4.8.1 on Linux, compiling with --std=c++11 I get:
In file included from foo.cpp:1:0:
/usr/include/c++/4.8.1/tr1/functional: In instantiation of ‘class std::tr1::reference_wrapper<Test>’:
foo.cpp:17:44: required from here
/usr/include/c++/4.8.1/tr1/functional:495:9: error: cannot allocate an object of abstract type ‘Test’
operator()(_Args&... __args) const
^
foo.cpp:3:7: note: because the following virtual functions are pure within ‘Test’:
class Test
^
foo.cpp:7:18: note: virtual void Test::foo()
virtual void foo() = 0;
^
This doesn't seem to make any sense. Using the corresponding boost classes works fine. Can someone confirm this is a TR1 bug in G++ 4.8.1?
The libstdc++ tr1::reference_wrapper implementation has this:
template<typename... _Args>
typename result_of<_M_func_type(_Args...)>::type
operator()(_Args&... __args) const
{
return __invoke(get(), __args...);
}
The result_of expression uses a by-value _M_func_type parameter (which is the template parameter of the reference_wrapper i.e. Test), so it tries to form the function type Test(), which uses a by-value Test return type, which is invalid for an incomplete or abstract type. I think I fixed this for std::reference_wrapper ages ago, it needs to use result_of<_M_func_type&(Args...)>.
The TR1 implementation in libstdc++ is not really maintained any longer - TR1 served its purpose but its time has passed.

parsing error invoking static function on object instance?

I'm wondering if the following code should be considered valid c++, gcc and clang both choke on it while Microsoft and the embedded compiler (green hills) our project uses seem to parse this without problems. This line of code gives gcc and clang trouble:
foo().Bar<int>();
It appears gcc thinks the < is a less than operator instead of specifying a template argument. If Bar is changed to not be a template function gcc accepts it.
class Test1
{
public:
template<typename U>
static void Bar() {}
};
template<typename T>
class Test2
{
public:
Test2()
{
foo().Bar<int>();
}
Test1 foo() { return t; }
Test1 t;
};
int main()
{
Test2<int> t;
}
From my research calling a static member function on an object instance is valid c++. So what are you thoughts? Here is gcc's error:
Error.cpp: In constructor ‘Test2<T>::Test2()’:
Error.cpp:14:17: error: expected primary-expression before ‘int’
foo().Bar<int>();
^
Error.cpp:14:17: error: expected ‘;’ before ‘int’
The correct syntax is:
foo().template Bar<int>();
(Adding to #Dieter answer)
Otherwise you have to use a temporary:
Test1 tt = foo();
tt.Bar<int>();
(in this case you could have simply used t instead of tt, but that was not the point, of course).

g++ 4.1.2 compiler error

I have the following code (stripped down version from actual project to reproduce
the issue) that results in a compiler error on RHEL5 (g++ version 4.1.2):
----------- driver (test.cpp)--------------
#include <iostream>
#include <classa.hpp>
#include <func.hpp>
namespace globals {
static int kth(const A& a) {
return kth(a.ival());
}
}
using namespace globals;
int main() {
A a;
std::cout << func(a) << std::endl;
return 0;
}
----------class A (classa.hpp)------------
class A {
public:
A():val(0){}
const int ival() const {return val;}
private:
int val;
};
------- namespace globals (func.hpp) ------
namespace globals {
int kth(const int& c) {
return c;
}
template <class T>
int func(const T& key) {
return kth(key);
}
}
--------------------------------------------
Compiling it using g++ 4.1.2 gives me the following error:
func.hpp: In function ‘int globals::func(const T&) [with T = A]’:
test.cpp:15: instantiated from here
func.hpp:8: error: invalid initialization of reference of type ‘const int&’ from
expression of type ‘const A’
func.hpp:2: error: in passing argument 1 of ‘int globals::kth(const int&)’
Same code compiles and runs perfectly fine on RHEL4 (g++ version 3.4.6)! Any explanations/ideas/suggestions on how to resolve this error(?) on RHEL5 will
be much appreciated!
Edit:
Thanks Sergey. That is the obvious solution that I am aware of already. But I forgot to add that the restriction is that func.hpp cannot be edited (for e.g., its 3rd party write-protected). Any workarounds?
Here's what happens. When the function func() is defined, the compiler doesn't know about the function kth(const A&) yet because it is defined later in the code. So when it encounters a reference to kth() inside func(), it assumes that it is a reference to kth(const int&). Now when func() is actually instantiated, it fails to compile it because T is A, not int. I am not sure why it works in another version of the compiler, but I think it is because it actually starts resolving references when a template function is instantiated, not when it is declared. But this looks like a bug in the older version because with such behavior a function definition changes depending on where it is instantiated from, which is very confusing.
The only way to fix your code that it works with any compiler would be to put the definition of kth(const A&) between kth(const int&) and func() or a forward declaration of kth(const A&) somewhere above func().
Update
With the restriction of not editing func.hpp the best workaround I can think of is to create a custom header file with something like this:
#include <classa.hpp>
namespace globals {
static int kth(const A& a); // defined later, but used by func.hpp
}
#include <func.hpp>
I also don't see why kth(const A&) is defined as static, but used by a global header. I'd rather put it into the classa.cpp and its declaration into the classa.hpp. But this may be some design feature or artifact I am not aware of.