Wee
Wee

Reputation: 21

validity of the lambda function return value

I'm back to programming in C ++ after many years and I have some doubts.

I created this function:

typedef  std::function<const char *(void)> GetMessageLog;

void addLog(byte logLevel, GetMessageLog get)
{
  if (loglevelActiveFor(LOG_TO_SERIAL, logLevel)) {
    Serial.print(millis());
    Serial.print(F(" : "));
    Serial.println(get());
  }
  if (loglevelActiveFor(LOG_TO_SYSLOG, logLevel)) {
    syslog(logLevel, get());
  }
  if (loglevelActiveFor(LOG_TO_WEBLOG, logLevel)) {
    Logging.add(logLevel, get());
  }    
}

I would like to use it as follows:

addLog(LOG_LEVEL_INFO, [&]()
{
  String log = F("HX711: GPIO: SCL=");
  log += pinSCL;
  log += F(" DOUT=");
  log += pinDOUT;
  return log.c_str();
});

The validity of log.c_str () is guaranteed until addLog ends or if something interrupts the normal program flow (any event handler), the string object is destroyed?

Upvotes: 2

Views: 314

Answers (4)

Swift - Friday Pie
Swift - Friday Pie

Reputation: 14613

Is that Arduino API? That way you cause UB, String demolishes its resources on exit from closure.

Technically if you redesign type

typedef  std::function<String(void)> GetMessageLog;

then you can write

addLog(LOG_LEVEL_INFO, [&]() -> String
{
  String log = F("HX711: GPIO: SCL=");
  log += pinSCL;
  log += F(" DOUT=");
  log += pinDOUT;
  return log;
});

If compiler doesn't support named return value optimization, convert that in a one-liner to reduce amount of copy operations.

Upvotes: 1

Nadav Har&#39;El
Nadav Har&#39;El

Reputation: 13751

Welcome back to C++! You'll find that it has changed quite a bit in this century, and for the better.

The object "String log" lives only during the call of addLog. You cannot return log.c_str() because this will return a dangling pointer to an object which will no longer exist after the return. The solution is simple - just return "log" itself. Have this function (and GetMessageLog) return not an old-style C "char *", but a modern C++ "std::string".

In old C++, returning an std::string from a function was often frowned upon, because it always involved copying of that string, sometimes multiple times. This is no longer true, with the advent of move constructors (which are probably the most important new feature in C++11). The function builds a string, and when returning it, the string is not copied but rather "moved" - which involves copying just the pointer which it holds to its data array, but not copying the array itself.

In modern C++, you will very rarely use old-style bare pointers like you used char* in this example. You will typically use objects like std::string instead of char*, containers like std::vector instead of int*, smart pointers like std::unique_ptr instead of T*. All these objects are safer than the bare pointers because they give you less chance to mess up the lifetime of the object, and are exception-safe (i.e., if an exception occurs in the middle of the code, you won't forget to free memory you allocated).

Upvotes: 1

6502
6502

Reputation: 114559

String is a local to the lambda so (assuming it's more or less like std::string) you cannot use c_str as return value because when the caller will access it the local is already dead.

One more potential problem is that you're capturing by reference with [&] the variables pinSCL and pinDOUT. If the lambda is stored away and its lifetime ends after the lifetime of those two variables then calling it is also undefined behavior.

Upvotes: 1

That actually depends on what String is, but most likely, the return value of log.c_str() is valid only as long as log itself is (that would certainly be the case with std::string). Which means that in your case, it's not usable at all: when the lambda returns, log is destroyed, so the pointer returned from the lambda is dangling already.

Fortunately, the solution is simple. Change the lambda's return type to String and have it return log itself instead. If you need a const char* eventually, you can call c_str() on the return value, where you will have much better control over the lifetime.

Upvotes: 4

Related Questions