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

[Sketch] Support for __stdcall members #194

Open
wants to merge 22 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
aa85837
Run single header generation for the current state
OskiKervinen-MF Nov 14, 2019
403d9a2
Add comment explaining VirtualOffsetSelector
OskiKervinen-MF Nov 14, 2019
a9a8689
Create calling convention aware wrapper for function pointers
OskiKervinen-MF Nov 14, 2019
9977382
Add test for a class with cdecl memebers
OskiKervinen-MF Nov 14, 2019
fe2cdec
Adventures in function pointer type manipulation
OskiKervinen-MF Nov 14, 2019
90374ec
Move the function_traits to the mock where they are needed
OskiKervinen-MF Nov 15, 2019
2bb054d
Use func_traits to reduce the combinatory explosion of stubs
OskiKervinen-MF Nov 15, 2019
ac709c8
Fixed: Actually have also thiscall isntead of another stdcall
OskiKervinen-MF Nov 15, 2019
cb61562
Triplicate offset selector for each calling convention
OskiKervinen-MF Nov 15, 2019
86bb426
Rename function maker to Wrap, since it's not inside the class it cre…
OskiKervinen-MF Nov 15, 2019
6eefb1a
Specialize getOffset for calling conventions
OskiKervinen-MF Nov 15, 2019
ed8acaf
Parametrize mocking contexts with calling convention
OskiKervinen-MF Nov 15, 2019
79248a6
Use FuncWithConvention in places
OskiKervinen-MF Nov 15, 2019
e02e94b
Specialize methodProxyCreator for calling conventions
OskiKervinen-MF Nov 15, 2019
6d306d1
Replace raw function pointers with FuncWithConvention wrappers
OskiKervinen-MF Nov 15, 2019
f72bb97
Restore the capability to deal with const void return type
OskiKervinen-MF Nov 15, 2019
9c1d3a6
Use default convention when none is specified
OskiKervinen-MF Nov 15, 2019
6d3145e
Support mocking methods from parent classes
OskiKervinen-MF Nov 15, 2019
25c91fe
Add tests for all conventions
OskiKervinen-MF Nov 15, 2019
b3b459f
Regenerate the catch single header version
OskiKervinen-MF Nov 15, 2019
c639d85
Added calling convention markers to getOffset overloads
OskiKervinen-MF Nov 22, 2019
193c99d
Updated catch single header
OskiKervinen-MF Nov 22, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 128 additions & 36 deletions include/fakeit/Mock.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,98 @@
#include "fakeit/MockImpl.hpp"
#include "fakeit/ActualInvocation.hpp"
#include "fakeit/Prototype.hpp"
#include "mockutils/mscpp/FunctionWithConvention.hpp"

namespace fakeit {
namespace internal {
}
using namespace fakeit::internal;

// Function type manipulation utilities.
struct func_traits{

#ifdef _MSC_VER
// On MSC, always define the __cdecl variants. Only they are needed in x64.
#define CC_CDECL __cdecl
#else
// On non-MSC, don't worry about it.
#define CC_CDECL
#endif

// These are the default implementations whose presence always makes sense.

template<typename T, typename R, typename... arglist>
static R(CC_CDECL T::* remove_cv( R(CC_CDECL T::*vMethod )( arglist... ) const volatile ))( arglist... ) {
return reinterpret_cast< R ( T::* )( arglist... ) >( vMethod );
};

template<typename T, typename R, typename... arglist>
static R(CC_CDECL T::* remove_cv( R(CC_CDECL T::*vMethod )( arglist... ) volatile ))( arglist... ) {
return reinterpret_cast< R ( T::* )( arglist... ) >( vMethod );
};

template<typename T, typename R, typename... arglist>
static R(CC_CDECL T::* remove_cv( R(CC_CDECL T::*vMethod )( arglist... ) const ))( arglist... ) {
return reinterpret_cast< R ( T::* )( arglist... ) >( vMethod );
};

template<typename T, typename R, typename... arglist>
static R(CC_CDECL T::* remove_cv( R(CC_CDECL T::*vMethod )( arglist... ) ))( arglist... ) {
return vMethod;
};

// On 32-bit msc, define also the other calling conventions.
#if defined( _MSC_VER ) && ! defined( _WIN64 )

// __stdcall, used in normal functions and COM interfaces.

template<typename T, typename R, typename... arglist>
static R(__stdcall T::* remove_cv( R(__stdcall T::*vMethod )( arglist... ) const volatile ))( arglist... ) {
return reinterpret_cast< R ( T::* )( arglist... ) >( vMethod );
};

template<typename T, typename R, typename... arglist>
static R(__stdcall T::* remove_cv( R(__stdcall T::*vMethod )( arglist... ) volatile ))( arglist... ) {
return reinterpret_cast< R( __stdcall T::* )( arglist... ) >( vMethod );
};

template<typename T, typename R, typename... arglist>
static R(__stdcall T::* remove_cv( R(__stdcall T::*vMethod )( arglist... ) const ))( arglist... ) {
return reinterpret_cast< R( __stdcall T::* )( arglist... ) >( vMethod );
};

template<typename T, typename R, typename... arglist>
static R(__stdcall T::* remove_cv( R(__stdcall T::*vMethod )( arglist... ) ))( arglist... ) {
return vMethod;
};

// __thiscall, used in member functions.

template<typename T, typename R, typename... arglist>
static R(__thiscall T::* remove_cv( R(__thiscall T::*vMethod )( arglist... ) const volatile ))( arglist... ) {
return reinterpret_cast< R ( T::* )( arglist... ) >( vMethod );
};

template<typename T, typename R, typename... arglist>
static R(__thiscall T::* remove_cv( R(__thiscall T::*vMethod )( arglist... ) volatile ))( arglist... ) {
return reinterpret_cast< R( __thiscall T::* )( arglist... ) >( vMethod );
};

template<typename T, typename R, typename... arglist>
static R(__thiscall T::* remove_cv( R(__thiscall T::*vMethod )( arglist... ) const ))( arglist... ) {
return reinterpret_cast< R( __thiscall T::* )( arglist... ) >( vMethod );
};

template<typename T, typename R, typename... arglist>
static R(__thiscall T::* remove_cv( R(__thiscall T::*vMethod )( arglist... ) ))( arglist... ) {
return vMethod;
};
#endif

template<typename Func>
using remove_cv_t = decltype( remove_cv( std::declval<Func>() ) );
};

template<typename C, typename ... baseclasses>
class Mock : public ActualInvocationsSource {
MockImpl<C, baseclasses...> impl;
Expand Down Expand Up @@ -51,66 +137,73 @@ namespace fakeit {
impl.clear();
}

template<class DATA_TYPE, typename ... arglist,
template<class DATA_TYPE, typename ... arglist,
class = typename std::enable_if<std::is_member_object_pointer<DATA_TYPE C::*>::value>::type>
DataMemberStubbingRoot<C, DATA_TYPE> Stub(DATA_TYPE C::* member, const arglist &... ctorargs) {
return impl.stubDataMember(member, ctorargs...);
}

template<int id, typename R, typename T, typename ... arglist, class = typename std::enable_if<
!std::is_void<R>::value && std::is_base_of<T, C>::value>::type>
MockingContext<R, arglist...> stub(R (T::*vMethod)(arglist...) const) {
auto methodWithoutConstVolatile = reinterpret_cast<R (T::*)(arglist...)>(vMethod);
return impl.template stubMethod<id>(methodWithoutConstVolatile);
}

template<int id, typename R, typename T, typename... arglist, class = typename std::enable_if<
!std::is_void<R>::value && std::is_base_of<T, C>::value>::type>
MockingContext<R, arglist...> stub(R(T::*vMethod)(arglist...) volatile) {
auto methodWithoutConstVolatile = reinterpret_cast<R(T::*)(arglist...)>(vMethod);
return impl.template stubMethod<id>(methodWithoutConstVolatile);
MockingContext<R, arglist...> stubImpl(R(CC_CDECL T::*vMethod)(arglist...)) {
R( CC_CDECL C:: * cMethod )( arglist... ) = vMethod;
return impl.template stubMethod<id>( ConventionHelper::Wrap( cMethod ) );
}

template<int id, typename R, typename T, typename... arglist, class = typename std::enable_if<
!std::is_void<R>::value && std::is_base_of<T, C>::value>::type>
MockingContext<R, arglist...> stub(R(T::*vMethod)(arglist...) const volatile) {
auto methodWithoutConstVolatile = reinterpret_cast<R(T::*)(arglist...)>(vMethod);
return impl.template stubMethod<id>(methodWithoutConstVolatile);
std::is_void<R>::value && std::is_base_of<T, C>::value>::type>
MockingContext<void, arglist...> stubImpl(R(CC_CDECL T::*vMethod)(arglist...)) {
auto vMethodWithoutConstVolatile = reinterpret_cast< void( CC_CDECL T::* )( arglist... ) >( vMethod );
// Convert to a pointer to a method of C so that Wrap generates a correctly typed FuncWithConvention.
void( CC_CDECL C:: * cMethod )( arglist... ) = vMethodWithoutConstVolatile;
return impl.template stubMethod<id>( ConventionHelper::Wrap( cMethod ) );
}

// On 32-bit msc, define also the other calling conventions.
#if defined( _MSC_VER ) && ! defined( _WIN64 )

template<int id, typename R, typename T, typename... arglist, class = typename std::enable_if<
!std::is_void<R>::value && std::is_base_of<T, C>::value>::type>
MockingContext<R, arglist...> stub(R(T::*vMethod)(arglist...)) {
return impl.template stubMethod<id>(vMethod);
MockingContext<R, arglist...> stubImpl(R(__stdcall T::*vMethod)(arglist...)) {
R( __stdcall C:: * cMethod )( arglist... ) = vMethod;
return impl.template stubMethod<id>( ConventionHelper::Wrap( cMethod ) );
}

template<int id, typename R, typename T, typename... arglist, class = typename std::enable_if<
std::is_void<R>::value && std::is_base_of<T, C>::value>::type>
MockingContext<void, arglist...> stub(R(T::*vMethod)(arglist...) const) {
auto methodWithoutConstVolatile = reinterpret_cast<void (T::*)(arglist...)>(vMethod);
return impl.template stubMethod<id>(methodWithoutConstVolatile);
}
MockingContext<void, arglist...> stubImpl( R( __stdcall T::* vMethod )( arglist... ) )
{
auto vMethodWithoutConstVolatile = reinterpret_cast< void( __stdcall T::* )( arglist... ) >( vMethod );
// Convert to a pointer to a method of C so that Wrap generates a correctly typed FuncWithConvention.
void( __stdcall C:: * cMethod )( arglist... ) = vMethodWithoutConstVolatile;
return impl.template stubMethod<id>( ConventionHelper::Wrap( cMethod ) );
}

template<int id, typename R, typename T, typename... arglist, class = typename std::enable_if<
std::is_void<R>::value && std::is_base_of<T, C>::value>::type>
MockingContext<void, arglist...> stub(R(T::*vMethod)(arglist...) volatile) {
auto methodWithoutConstVolatile = reinterpret_cast<void (T::*)(arglist...)>(vMethod);
return impl.template stubMethod<id>(methodWithoutConstVolatile);
!std::is_void<R>::value && std::is_base_of<T, C>::value>::type>
MockingContext<R, arglist...> stubImpl(R(__thiscall T::*vMethod)(arglist...)) {
// Convert to a pointer to a method of C so that Wrap generates a correctly typed FuncWithConvention.
R( __thiscall C:: * cMethod )( arglist... ) = vMethod;
return impl.template stubMethod<id>( ConventionHelper::Wrap( cMethod ) );
}

template<int id, typename R, typename T, typename... arglist, class = typename std::enable_if<
std::is_void<R>::value && std::is_base_of<T, C>::value>::type>
MockingContext<void, arglist...> stub(R(T::*vMethod)(arglist...) const volatile) {
auto methodWithoutConstVolatile = reinterpret_cast<void (T::*)(arglist...)>(vMethod);
return impl.template stubMethod<id>(methodWithoutConstVolatile);
}
MockingContext<void, arglist...> stubImpl( R( __thiscall T::* vMethod )( arglist... ) )
{
auto vMethodWithoutConstVolatile = reinterpret_cast< void( __thiscall T::* )( arglist... ) >( vMethod );
// Convert to a pointer to a method of C so that Wrap generates a correctly typed FuncWithConvention.
void( __thiscall C:: * cMethod )( arglist... ) = vMethodWithoutConstVolatile;
return impl.template stubMethod<id>( ConventionHelper::Wrap( cMethod ) );
}

template<int id, typename R, typename T, typename... arglist, class = typename std::enable_if<
std::is_void<R>::value && std::is_base_of<T, C>::value>::type>
MockingContext<void, arglist...> stub(R(T::*vMethod)(arglist...)) {
auto methodWithoutConstVolatile = reinterpret_cast<void (T::*)(arglist...)>(vMethod);
return impl.template stubMethod<id>(methodWithoutConstVolatile);
}
#endif

template<int id, typename Func>
auto stub( Func func ) -> decltype( stubImpl<id>( func_traits::remove_cv( func ) ) )
{
return stubImpl<id>( func_traits::remove_cv( func ) );
}

DtorMockingContext dtor() {
return impl.stubDtor();
Expand All @@ -121,5 +214,4 @@ namespace fakeit {
}

};

}
44 changes: 22 additions & 22 deletions include/fakeit/MockImpl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "fakeit/DomainObjects.hpp"
#include "fakeit/FakeitContext.hpp"
#include "fakeit/ActualInvocationHandler.hpp"
#include "mockutils/mscpp/FunctionWithConvention.hpp"

namespace fakeit {

Expand Down Expand Up @@ -89,9 +90,9 @@ namespace fakeit {
return DataMemberStubbingRoot<T, DATA_TYPE>();
}

template<int id, typename R, typename T, typename ... arglist, class = typename std::enable_if<std::is_base_of<T, C>::value>::type>
MockingContext<R, arglist...> stubMethod(R(T::*vMethod)(arglist...)) {
return MockingContext<R, arglist...>(new UniqueMethodMockingContextImpl < id, R, arglist... >
template<int id, typename R, typename CONVENTION, typename ... arglist>
MockingContext<R, arglist...> stubMethod( FuncWithConvention< C, R, CONVENTION, arglist... > vMethod ) {
return MockingContext<R, arglist...>(new UniqueMethodMockingContextImpl < id, R, CONVENTION, arglist... >
(*this, vMethod));
}

Expand Down Expand Up @@ -167,20 +168,20 @@ namespace fakeit {

};

template<typename R, typename ... arglist>

template<typename R, typename CONVENTION, typename ... arglist>
class MethodMockingContextImpl : public MethodMockingContextBase<R, arglist...> {
protected:

R (C::*_vMethod)(arglist...);
FuncWithConvention<C, R, CONVENTION, arglist...> _vMethod;

public:
virtual ~MethodMockingContextImpl() = default;

MethodMockingContextImpl(MockImpl<C, baseclasses...> &mock, R (C::*vMethod)(arglist...))
MethodMockingContextImpl(MockImpl<C, baseclasses...> &mock, FuncWithConvention<C, R, CONVENTION, arglist...> vMethod)
: MethodMockingContextBase<R, arglist...>(mock), _vMethod(vMethod) {
}


virtual std::function<R(arglist&...)> getOriginalMethod() override {
void *mPtr = MethodMockingContextBase<R, arglist...>::_mock.getOriginalMethod(_vMethod);
C * instance = &(MethodMockingContextBase<R, arglist...>::_mock.get());
Expand All @@ -191,21 +192,20 @@ namespace fakeit {
}
};


template<int id, typename R, typename ... arglist>
class UniqueMethodMockingContextImpl : public MethodMockingContextImpl<R, arglist...> {
template<int id, typename R, typename CONVENTION, typename ... arglist>
class UniqueMethodMockingContextImpl : public MethodMockingContextImpl<R, CONVENTION, arglist...> {
protected:

virtual RecordedMethodBody<R, arglist...> &getRecordedMethodBody() override {
return MethodMockingContextBase<R, arglist...>::_mock.template stubMethodIfNotStubbed<id>(
MethodMockingContextBase<R, arglist...>::_mock._proxy,
MethodMockingContextImpl<R, arglist...>::_vMethod);
MethodMockingContextImpl<R, CONVENTION, arglist...>::_vMethod);
}

public:

UniqueMethodMockingContextImpl(MockImpl<C, baseclasses...> &mock, R (C::*vMethod)(arglist...))
: MethodMockingContextImpl<R, arglist...>(mock, vMethod) {
UniqueMethodMockingContextImpl(MockImpl<C, baseclasses...> &mock, FuncWithConvention<C, R, CONVENTION, arglist...> vMethod)
: MethodMockingContextImpl<R, CONVENTION, arglist...>(mock, vMethod) {
}
};

Expand Down Expand Up @@ -265,8 +265,8 @@ namespace fakeit {
return reinterpret_cast<C *>(fake);
}

template<typename R, typename ... arglist>
void *getOriginalMethod(R (C::*vMethod)(arglist...)) {
template<typename R, typename CONVENTION, typename ... arglist>
void *getOriginalMethod(FuncWithConvention<C, R, CONVENTION, arglist...> vMethod) {
auto vt = _proxy.getOriginalVT();
auto offset = VTUtils::getOffset(vMethod);
void *origMethodPtr = vt.getMethod(offset);
Expand All @@ -280,11 +280,11 @@ namespace fakeit {
return origMethodPtr;
}

template<unsigned int id, typename R, typename ... arglist>
template<unsigned int id, typename R, typename CONVENTION, typename ... arglist>
RecordedMethodBody<R, arglist...> &stubMethodIfNotStubbed(DynamicProxy<C, baseclasses...> &proxy,
R (C::*vMethod)(arglist...)) {
FuncWithConvention<C, R, CONVENTION, arglist... > vMethod ) {
if (!proxy.isMethodStubbed(vMethod)) {
proxy.template stubMethod<id>(vMethod, createRecordedMethodBody < R, arglist... > (*this, vMethod));
proxy.template stubMethod<id>(vMethod, createRecordedMethodBody <R, CONVENTION, arglist... > (*this, vMethod ));
}
Destructible *d = proxy.getMethodMock(vMethod);
RecordedMethodBody<R, arglist...> *methodMock = dynamic_cast<RecordedMethodBody<R, arglist...> *>(d);
Expand All @@ -300,10 +300,10 @@ namespace fakeit {
return *dtorMock;
}

template<typename R, typename ... arglist>
template<typename R, typename CONVENTION, typename ... arglist>
static RecordedMethodBody<R, arglist...> *createRecordedMethodBody(MockObject<C> &mock,
R(C::*vMethod)(arglist...)) {
return new RecordedMethodBody<R, arglist...>(mock.getFakeIt(), typeid(vMethod).name());
FuncWithConvention<C, R, CONVENTION, arglist... > vMethod) {
return new RecordedMethodBody<R, arglist...>(mock.getFakeIt(), typeid(vMethod._vMethod).name());
}

static RecordedMethodBody<void> *createRecordedDtorBody(MockObject<C> &mock) {
Expand Down
23 changes: 15 additions & 8 deletions include/mockutils/DynamicProxy.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,17 +100,24 @@ namespace fakeit {
{
}

template<int id, typename R, typename ... arglist>
void stubMethod(R(C::*vMethod)(arglist...), MethodInvocationHandler<R, arglist...> *methodInvocationHandler) {
template<int id, typename R, typename CONVENTION, typename ... arglist>
void stubMethod(FuncWithConvention<C, R, CONVENTION, arglist... > vMethod, MethodInvocationHandler<R, arglist...> *methodInvocationHandler) {
auto offset = VTUtils::getOffset(vMethod);
MethodProxyCreator<R, arglist...> creator;
bind(creator.template createMethodProxy<id + 1>(offset), methodInvocationHandler);
MethodProxyCreator<R, CONVENTION, arglist...> creator;
bind(creator.createMethodProxy<id + 1, CONVENTION>(offset), methodInvocationHandler);
}

void stubDtor(MethodInvocationHandler<void> *methodInvocationHandler) {
auto offset = VTUtils::getDestructorOffset<C>();
MethodProxyCreator<void> creator;
bindDtor(creator.createMethodProxy<0>(offset), methodInvocationHandler);
// For the cases we care about (COM), the destructor uses the default calling convention.
MethodProxyCreator<void, ConventionHelper::DefaultConvention> creator;
bindDtor(creator.createMethodProxy<0,ConventionHelper::DefaultConvention>(offset), methodInvocationHandler);
}

template<typename R, typename CONVENTION, typename ... arglist>
bool isMethodStubbed(FuncWithConvention<C, R, CONVENTION, arglist... > vMethod) {
unsigned int offset = VTUtils::getOffset(vMethod);
return isBinded(offset);
}

template<typename R, typename ... arglist>
Expand All @@ -124,8 +131,8 @@ namespace fakeit {
return isBinded(offset);
}

template<typename R, typename ... arglist>
Destructible *getMethodMock(R(C::*vMethod)(arglist...)) {
template<typename R, typename CONVENTION, typename ... arglist>
Destructible *getMethodMock(FuncWithConvention<C, R, CONVENTION, arglist... > vMethod) {
auto offset = VTUtils::getOffset(vMethod);
std::shared_ptr<Destructible> ptr = _methodMocks[offset];
return ptr.get();
Expand Down
Loading