I have the following test code
#include <type_traits>
#include <utility>
#define LIFTMEMFN(F) \
[](auto&& self, auto&&... args) noexcept( \
noexcept((self).F(std::forward<decltype(args)>(args)...))) \
-> decltype(self.F(std::forward<decltype(args)>(args)...)) { \
return self.F(std::forward<decltype(args)>(args)...); \
}
struct Foo {
template <int k>
int run(int i, int j) {
return i + j + k;
}
int run2(int i, int j) { return i + j; }
};
int main(int argc, char* argv[]) {
Foo foo;
// In this case I need to insert the template keyword
auto fn2 = LIFTMEMFN(template run<1>);
int a = fn2(foo, 10, 20);
// but here not
int b = foo.run<1>(10, 20);
return a + b;
}
LIFTMEMFN is designed to lift a member function into a lambda so it can be called like a regular function. However I've discovered a quirk. If I want to lift a member function that is templated when I pass it to the macro I need to use the template keyword
auto fn2 = LIFTMEMFN(template run<1>);
int a = fn2(foo, 10, 20);
however if called directly then I don't
int b = foo.run<1>(10, 20);
If I leave out the template keyword then clang complans
<source>:25:16: error: expected ')'
auto fn2 = LIFTMEMFN( run<1>);
^
<source>:6:61: note: expanded from macro 'LIFTMEMFN'
noexcept((self).F(std::forward<decltype(args)>(args)...))) \
^
<source>:25:16: note: to match this '('
<source>:6:26: note: expanded from macro 'LIFTMEMFN'
noexcept((self).F(std::forward<decltype(args)>(args)...))) \
^
<source>:25:16: error: expected ')'
auto fn2 = LIFTMEMFN( run<1>);
^
<source>:7:62: note: expanded from macro 'LIFTMEMFN'
-> decltype(self.F(std::forward<decltype(args)>(args)...)) { \
^
<source>:25:16: note: to match this '('
<source>:7:27: note: expanded from macro 'LIFTMEMFN'
-> decltype(self.F(std::forward<decltype(args)>(args)...)) { \
^
<source>:25:16: error: declaration type contains unexpanded parameter pack 'args'
auto fn2 = LIFTMEMFN( run<1>);
^~~~~~~~~~~~~~~~~~
<source>:5:5: note: expanded from macro 'LIFTMEMFN'
[](auto&& self, auto&&... args) noexcept( \
^
<source>:25:16: error: expected ')'
<source>:8:57: note: expanded from macro 'LIFTMEMFN'
return self.F(std::forward<decltype(args)>(args)...); \
^
<source>:25:16: note: to match this '('
<source>:8:22: note: expanded from macro 'LIFTMEMFN'
return self.F(std::forward<decltype(args)>(args)...); \
^
<source>:25:16: error: expression contains unexpanded parameter pack 'args'
auto fn2 = LIFTMEMFN( run<1>);
^~~~~~~~~~~~~~~~~~
<source>:8:16: note: expanded from macro 'LIFTMEMFN'
return self.F(std::forward<decltype(args)>(args)...); \
^ ~~~~ ~~~~
<source>:26:13: error: no matching function for call to object of type '(lambda at <source>:25:16)'
int a = fn2(foo, 10, 20);
^~~
<source>:25:16: note: candidate template ignored: substitution failure [with self:auto = Foo &, args:auto = <int, int>]: missing 'template' keyword prior to dependent template name 'run'
auto fn2 = LIFTMEMFN( run<1>);
^ ~~~
<source>:5:5: note: expanded from macro 'LIFTMEMFN'
[](auto&& self, auto&&... args) noexcept( \
^
6 errors generated.
Compiler returned: 1
gcc complains similarly.
Is there a way to construct the macro so that all three compilers, gcc, msvc and clang can do this without the explicit template keyword?