turtle icon indicating copy to clipboard operation
turtle copied to clipboard

macros don't support noexcept

Open sebkraemer opened this issue 6 years ago • 7 comments

We have some methods which are declared noexcept. Currently, we used proxy methods to work around this. Is there another way? Is there an interest in adding it to the existing function macros list?

A colleague hacked together a macro like this:

#define MOCK_NOEXCEPT_OVERRIDE_METHOD_EXT(M, n, S, t)
MOCK_METHOD_AUX(M, n, S, t, noexcept override,)
MOCK_METHOD_AUX(M, n, S, t, const noexcept override,)
MOCK_METHOD_HELPER(S, t,) #define MOCK_CONST_NOEXCEPT_OVERRIDE_METHOD_EXT(M, n, S, t)
MOCK_METHOD_AUX(M, n, S, t, const noexcept override,)
MOCK_METHOD_HELPER(S, t,) #define MOCK_NON_CONST_NOEXCEPT_OVERRIDE_METHOD_EXT(M, n, S, t)
MOCK_METHOD_AUX(M, n, S, t, noexcept override,)
MOCK_METHOD_HELPER(S, t,)

and alike (without _EXT).

Some goes for missing override, which only pops an IDE warning but would also be nice to have.

The whole solution would need the zoo of combinations with const, nonconst, explicit override and so on.

sebkraemer avatar Oct 19 '18 14:10 sebkraemer

I created a pull request with a proposed solution: GH-49 If there's interest in this feature, I'm willing to improve the solution. It's probably not complete.

sebkraemer avatar Oct 23 '18 10:10 sebkraemer

Hi Seb,

The whole solution would need the zoo of combinations with const, nonconst, explicit override and so on.

So this ultimately would need to be supported:

  • noexcept (suffix)
  • noexcept(expr) (suffix)
  • explicit (prefix)
  • explicit(expr) (prefix)
  • override (suffix)

Am I missing anything?

mat007 avatar Nov 06 '18 05:11 mat007

Hi Mat, thanks for the response. I didn't mean explicit keyword but explicitly writing "override" as part of the function sginature, but I guess it's useful nonetheless. I also assume the const/non_const cases are covered implicitly or would be part of your change in another way, similar to how static methods should work with these additions.

sebkraemer avatar Nov 06 '18 10:11 sebkraemer

I haven't really been following the latest C++ specification developments but my understanding was that up to C++14 noexcept and override were not part of the function signature, so it shouldn't really matter if a mock class' methods have them or not, right? Starting with C++17 they have become part of the signature, meaning the computed signature should already have them when overriding a method in a base class (or maybe the helpers in signature.hpp should be adjusted). For a stand-alone mock object does it matter (e.g. break compilation) if the methods have noexcept (or override) or not?

Or am I missing something?

mat007 avatar Nov 17 '18 13:11 mat007

The override is only syntactic sugar for code analysis tools, having it mocked can avoid warnings inside IDEs. In my experience, a noexcept method needs a proxy mock method to work and would not compile otherwise, even though it's not part of the signature. I cut an example from cppreference:

struct B {
   virtual void f() noexcept;
};
struct D: B {
   void f();              // ill-formed: D::f is potentially-throwing, B::f is non-throwing
};

int main()
{
}

I'm getting with either C++14 or C++17:

main.cpp:9:9: error: looser throw specifier for 'virtual void D::f()'

    void f();              // ill-formed: D::f is potentially-throwing, B::f is non-throwing

sebkraemer avatar Dec 06 '18 20:12 sebkraemer

+1 for the override. Easy to implement and could even have a short macro: MOCK_VIRTUAL/MOCK_VIRTUAL_METHOD which adds this. IMO in C++ it is much more common to mock virtual functions as the alternative requires templates from top to bottom. But there is a warning "suggest-override" which will trigger for this common case.

Flamefire avatar Jan 01 '19 17:01 Flamefire

As with C++11 you also have reference specifiers, override, noexcept, throw etc you likely need a more flexible approach.

What about using something inspired by GMock which has this (syntax very similar to Turtle):

  MOCK_METHOD(void, PenUp, (), (override));
  MOCK_METHOD(void, PenDown, (), (override));
  MOCK_METHOD(void, Forward, (int distance), (override));
  MOCK_METHOD(void, Turn, (int degrees), (override));
  MOCK_METHOD(void, GoTo, (int x, int y), (override));
  MOCK_METHOD(int, GetX, (), (const, override));
  MOCK_METHOD(int, GetY, (), (const, override));

Ok, so in turtle we need:

  • Mocked method name
  • Cardinality
  • Opt.: Signature for overloaded methods
  • Opt.: Identifier (likely the only use are overloaded methods too?)
  • Opt.: specifiers

With my ongoing work for using C++11/14 features to simplify the code I think we can have a small set of macros:

  • MOCK_METHOD(name, cardinality, opt. list of specifiers, opt. id) -> for each entry in the specifier list an overload is generated
  • MOCK_METHOD_OVERLOAD(name, cardinality, signature, id, opt. list of specifiers) -> similar

Hence you can do:

  • MOCK_METHOD(PenUp, 0) -> Generates void PenUp(); void PenUp() const by default
  • MOCK_METHOD(PenUp, 0, (const&, &&)) -> Generates void PenUp() const&; void PenUp() &&
  • MOCK_METHOD(PenUp, 0, (, const noexcept)) -> Generates void PenUp(); void PenUp() const noexcept

For ease of use I'd add a MOCK_VMETHOD which is the same but adds override to each method. Or doesn't MOCK_METHOD already require a virtual method? If so then it isn't required and we can have MOCK_METHOD add override already

Flamefire avatar Jul 25 '20 09:07 Flamefire