1

Which of the following C++ lambdas/statements are supposed to work according to the latest C++ specification?

Context in case this is relevant: see here.

I tested the following code snippets with -std=c++17 on Fedora 33 with clang 11.0.0 and gcc 10.2.1.

Update: Replace __PRETTY_FUNCTION__ with __func__ for standard compliance. The same behavior can be observed.

Update2: Example using const char * s = __func__ as default argument to verify that it should be valid within a function scope (thanks to @BenVoigt).


1. LLVM __func__ within lambda default argument

void clang() {
  [](const char* c = __func__) {std::cout << c << std::endl;}();
}

Expected behavior (CLANG):

  • Print out clang\n (void clang() for __PRETTY_FUNCTION__)

Observed behavior (CLANG):

  • Compiler warning: warning: predefined identifier is only valid inside function [-Wpredefined-identifier-outside-function]
  • Print out \n (top level() for __PRETTY_FUNCTION__)

2. GCC ignores statements

template <typename L>
constexpr std::string_view methodName(L l) { return l(); }
#define __METHOD_NAME__ (\
  __func__, /* needed for pointer to work */ \
  methodName([](const char* c = __func__) {return std::string_view(c);}) \
)
void gcc1() {
  std::cout << [](const char* c = __func__) { return c; }() << std::endl;  // GCC: This statement doesn't do anything
  std::cout << [](const char* c = __func__) { return c; }("gcc") << std::endl;
  std::cout << __METHOD_NAME__ << std::endl;  // GCC: This statement somehow conflicts with the statements above
}
void gcc2() {
  std::cout << __METHOD_NAME__ << std::endl;  // GCC: This statement itself works
}

Expected output (GCC):

gcc1
gcc
gcc1
gcc2

Observed output (GCC):

gcc
gcc2

3. GCC Compile error

void gcc3() {
  std::string_view s = [](const char* c = __func__) { return std::string_view(c); }();
  std::cout << s << std::endl;
}

Expected behavior (GCC): Compiles without problems.

Observed behavior (GCC): error: internal compiler error: in finish_expr_stmt

darkdragon
  • 392
  • 5
  • 13
  • @Enrico: What do you mean by "define functions instead"? The g++ extension for "local functions"? A member function of a locally-defined class type? – Ben Voigt Oct 16 '20 at 16:20
  • I was referring to using a function rather than a lambda function object. But I had not understood the point of the question, which is how these `__variables__` behaves, lambdas being just a tool to condition what these variables refer too. (And probably I'm still misundertanding the question.) – Enlico Oct 16 '20 at 16:25

1 Answers1

0

[class.local] The local class is in the scope of the enclosing scope, and has the same access to names outside the function as does the enclosing function. [Note: A declaration in a local class cannot odr-use (6.2) a local entity from an enclosing scope. — end note]

A lambda is a local class, and as such it cannot use variables from the enclosing scope (e.g. __func__) other than in its capture clause.

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
  • Thanks for the clarification. Do you see any way to pass `__func__` (as a macro) to a `constexpr methodName()` other than [template-handling the string character by character](https://akrzemi1.wordpress.com/2017/06/28/compile-time-string-concatenation/) or waiting for [C++20 `std::basic_fixed_string` as template parameter](http://www.modernescpp.com/index.php/c-20-the-core-language#h1-2-string-literals-as-template-parameters) which doesn't seem to be implemented anywhere yet? – darkdragon Oct 16 '20 at 17:07
  • @darkdragon `__func__` is not a macro, it's a bona fide variable. – n. m. could be an AI Oct 16 '20 at 17:08
  • I need a _macro_ like `#define __METHOD_NAME__ methodName(__func__)` to use within my logger. – darkdragon Oct 16 '20 at 17:10
  • I'm not quite sure why you can't just use `__func__` or `__PRETTY_FUNCTION__` as is. Why wrap it in a lambda or anything else? – n. m. could be an AI Oct 16 '20 at 17:12
  • If you have a problem defining a logger macro or function, ask a question about defining a logger macro or function. There is a zillion loggers out there that can print function names. Explain why their strategies don't work for you, with examples. – n. m. could be an AI Oct 16 '20 at 17:18
  • Because I want to have the full function including class name and namespace, but remove the clutter for return types and parameters. This should be done at compile time, therefore using `constexpr`. My `methodName()` will do further string handling using C++20 `substr()` and `find()`. – darkdragon Oct 17 '20 at 15:28
  • It isn't quite clear what your problem is. If you want to write a `constexpr` function that manipulates a string, write one, and pass it `__func__` or whatever as an argument. Why do you need a lambda for that? – n. m. could be an AI Oct 17 '20 at 23:48
  • Because function parameters are not `constexpr` and strings can't be used as template argument prior to C++20 (although no compiler implemented `std::basic_fixed_string` yet). If you find a way without lambdas, show me. – darkdragon Oct 18 '20 at 08:52
  • A way to do what exactly? So far I only see you trying to print a function name. This doesn't require anything special. If you want to print a transformed function name, then the transformer function can be constexpr and it will be evaluated at compile time. Do you want to make the lambda that somehow contains `__func__` a template argument, in lieu of `__func__` itself? You better show it in the code and explain why you need it in a template, as opposed to say a constexpr function, otherwise people will get really confused trying to suggest irrelevant solutions. – n. m. could be an AI Oct 18 '20 at 14:41
  • I linked my desired `constexpr` function as [context](https://stackoverflow.com/a/64384924/3779655). Already adding another `constexpr std::string_view` doesn't work: https://godbolt.org/z/zfr1hK (Thanks for the hint to godbolt.org!) – darkdragon Oct 18 '20 at 19:21
  • Why do you need to make local variables of `transform_method_name` `constexpr`? Leave them as normal variables. The function itself is `constexpr`, that should be enough. – n. m. could be an AI Oct 18 '20 at 22:36
  • O_o thanks for letting me know! I thought everything within a `constexpr` function needs to be `constexpr`... Is there a way to force `constexpr` evaluation from the _caller_ side (something like a constexpr cast)? I found `consteval` (C++20) for the _callee_ side, but would prefer to force it inline e.g. `cout << (constexpr)transform_method_name(__func__);`. – darkdragon Oct 19 '20 at 05:54
  • You can initialise a constexpr variable with the result of the call, and/or static_assert its result, as I show in th example. If the call cannot be made at compile time, the compiler will complain. – n. m. could be an AI Oct 19 '20 at 06:41
  • Is there a way to "inline" this like `std::cout << (constexpr auto x = transform_method_name(__func__), x) << std::endl;`? – darkdragon Oct 19 '20 at 07:47
  • Hmm no, don't think so, but just `<< transform_method_name(__func__)` should be OK. `constexpr auto x = ...` doesn't force anything, it just verifies that RHS is constexpr. You can verify it once and then use many times. – n. m. could be an AI Oct 19 '20 at 17:07
  • But I need to verify it for every `__func__` separately, I guess!? When I just used `<< transform_method_name(__func__)`, godbolt.org added all the functions like `substr()` to the assembly... – darkdragon Oct 19 '20 at 18:56
  • Tried to make them `transform_method_name` `static`? A non-static function still needs to be generated. – n. m. could be an AI Oct 19 '20 at 19:22