Uninitialized 'const __FUNCTION__' [-fpermissive]

Hello,
I am building a library with a function:

void foo(const char* functionName = __FUNCTION__);

I call foo in my main loop: foo();

Compiling for Uno => No problem.
Compiling ESP8266 => uninitialized 'const __FUNCTION__' [-fpermissive] error.

Explicitly providing the __FUNCTION__ argument in the function foo(__FUNCTION__); works for both Uno and ESP8266?

Any idea's why gcc is ok with __FUNCTION__ (or __func__) with Uno but not with ESP whereas
explicitly providing __FUNCTION__ as parameter when calling foo() works for both architectures?

Note: according to Function Names (Using the GNU Compiler Collection (GCC)) __FUNCTION__ is GCC C99 and others compatible.

Best regards,
Johi.

Don't post snippets (Snippets R Us!)

I don't see how can the compiler use FUNCTION as a default value in the prototype since It is not defined outside of function scope. ie you need to be within the body of the function for this variable to be set. It does not work at the function definition level

try this code:

void test(const char* funcName = __func__)
{
  if (funcName == nullptr) Serial.println("nullptr");
  else if (*funcName == '\0') Serial.println("empty cString");
  else Serial.println(funcName);
}

void setup() {
  Serial.begin(115200);
  Serial.print("No param: "); test();
  Serial.print("With param: "); test(__func__);
}

void loop() {}

it will work fine both on UNO and ESP32
both will raise a warning when compiling

 warning: '__func__' is not defined outside of function scope
 void test(const char* funcName = __func__)
                                ^

and both will print

No param: empty cString
With param: setup

makes sense?

Hello J-M-L,
Thank you for your answer, but is is not an answer to the raised topic.
Posting all the code of the library is a bit difficult as it is too large, so I will try to explain the essence:

The function I am referring to is a member function of a C++ object located in a separate library.
It has the following signature:

class WawiWifi : public WawiWifiLight, public WawiPr
{
    void wawiBreak(unsigned char index, char* text, const char* file = __FILE__, const char* function = __FUNCTION__, short line = __LINE__);
};

The Arduino IDE compiler has no problems whatsoever with the default value __FUNCTION__ for the parameter function as long as I compile for the Uno, but as soon as I compile for the ESP 8266, I get the error in the title of this message.

Indeed if I use the value __FUNCTION__ as a value for a parameter in a member of loop() there is no issue whatsoever. That I tried to explain in my previous post, but maybe the layout was a bit confusing, apologies for that.

Best Regards,
Johi.

well the explanation still holds.

You cannot use __FUNCTION__ as a default value in your method prototype as this does not exist yet. It's only defined within the body of the function itself (in between the {})

The compiler for ESP32 is less permissive than the one for AVR, so that's possibly why you see a different behavior

I would suggest

class WawiWifi : public WawiWifiLight, public WawiPr
{
    void wawiBreak(unsigned char index, char* text, const char* file = __FILE__, const char* function = nullptr, short line = __LINE__);
};

and in the implementation of the code for the method you do

  void wawiBreak(unsigned char index, char* text, const char* file, const char* function, short line) {
  if (function == nullptr) function = __func__; // default value, use wawiBreak instead
  ... // your code
}

Looks like to get the name of the enclosing function, you have to use the macro __FUNCTION__ on an AVR processor (UNO, Nano, MEGA...) and the macro __func__ on the ESP8266.

__FUNCTION__ is provided for backward compatibility with old versions of GCC and might not be available, so better to use __func__ on both.

Hello johnwasser & J-M-L Jackson,
I indicated in my initial request, but __func__ and __FUNCTION__ fail to compile on ESP8266. But only as part of the header class, not as part of the main code. If the are in the main code, they both compile.
Below a minimal .ino with a .h that reproduces the case.
It is very weird that commenting out the header makes the code compile.

#include "WawiWifi.h"

void setup() 
{
  Serial.print(__FUNCTION__);
  Serial.print(__func__);
}

void loop() 
{}
class WawiWifi 
{
public:
  void wawiBreak(const char* function = __func__) {};
  void wawiBreak2(const char* function = __FUNCTION__) {};
};

Best regards,
Johi.

For the third time - the char array does not exist until you are in the body of the function.
What are you trying to achieve by having this non existing default value for one of your parameter?

The fact that it does not compile in a pure header is not surprising and you get a warning in main code - as it seems the array is initialized to empty string at that stage but it’s totally useless, you’d be getting a working behavior by initializing the pointer to nullptr as default.

Dear J-M-L,
Thank you for the answer.
I looked deeper into it and tried it in VC2019 in a console application.
There it also does not compile and reports an error in line with your analysis.
I was put on the wrong leg by the code compiling properly for the arduino uno whereas it should report an error.
Johi.

You can get most of the desired effect if you use macros to provide the argument rather than trying to default the arguments.

#define WAWIBREAK() wawiBreak(__func__)
#define WAWIBREAK2() wawiBreak(__FUNCTION__)

class WawiWifi
{
  public:
    void wawiBreak(const char* function)
    {
      Serial.println(function);
    };

    void wawiBreak2(const char* function)
    {
      Serial.println(function);
    };
} Wawi;

void setup()
{
  Serial.begin(115200);
  delay(200);
  
  Serial.println(__FUNCTION__);
  Serial.println(__func__);
  Wawi.WAWIBREAK();
  Wawi.WAWIBREAK2();
}

void loop() {}

In the Arduino IDE, set preferences to show all warning during compilation, then you will likely see a warning while compiling for an UNO. The compiler options are set to be more permissive with the AVR boards than for the ESP8266 boards for backwards compatibility purposes.

< edit >
I tried the code posted in reply #7 on an UNO, compilation is successful with the following warning:

In file included from /home/test/Arduino/forumtest/forumtest.ino:1:0:
/home/test/Arduino/forumtest/WawiWifi.h:4:41: warning: '__func__' is not defined outside of function scope
   void wawiBreak(const char* function = __func__) {}; 
                                         ^~~~~~~~
/home/test/Arduino/forumtest/WawiWifi.h: In member function 'void WawiWifi::wawiBreak(const char*)':
/home/test/Arduino/forumtest/WawiWifi.h:4:41: warning: unused parameter 'function' [-Wunused-parameter]
/home/test/Arduino/forumtest/WawiWifi.h: In member function 'void WawiWifi::wawiBreak2(const char*)':
/home/test/Arduino/forumtest/WawiWifi.h:5:42: warning: unused parameter 'function' [-Wunused-parameter]
   void wawiBreak2(const char* function = __FUNCTION__) {}; 
                                          ^~~~~~~~~~~~
1 Like

Hello johnwasser,
Yes, you are right. I used a similar approach to solve the case.
In retrospect, using the __function__ argument in the header as default value for the function parameter is a bit stupid from my side as, if it would have worked (for some compiles on my pc C++ simulation environment it did), it will report the header line where the function was declared and not the place where the function was called hence rendering the whole idea completely useless. (using __FILE__ and __LINE__ has the same problem: I report the name of the header file and the line where the function is declared.) So completely and utterly useless as the idea was to report the line where the function was used.
Macro's are indeed the way to go as the precompiler takes charge.
Best Regards,
Johi.

For future reference, when you get access to C++20, use std::source_location instead of macros:

https://en.cppreference.com/w/cpp/utility/source_location

void foo(const char* functionName = __FUNCTION__); // bad
                          ↓
void foo(std::source_location location = std::source_location::current()); // good