Node-addon-api: Napi::Value::ToString().Utf8Value().c_str()

Created on 8 Nov 2019  路  5Comments  路  Source: nodejs/node-addon-api

Hey, thanks for this wonderful abstraction!

I ran into an issue where using info[0].ToString().Utf8Value().c_str() returned garbled text:

void print(const Napi::CallbackInfo& info) {
    const char* word = info[0].ToString().Utf8Value().c_str();
    logFile << word << "\n";
}

However, this totally works:

void print(const Napi::CallbackInfo& info) {
    std::string str = info[0].ToString().Utf8Value();
    const char* word = str.c_str();
    logFile << word << "\n";
}

Perhaps there is a gap in my C++ knowledge here.
Let me know if you need more information!

Most helpful comment

c_str() returns a pointer to the underlying memory location. It is only valid for the lifetime of the string itself. In the code info[0].ToString().Utf8Value().c_str() the std::string is a temporary value and is release immediately after calling c_str(), so the pointer is immediately invalidated.

You are correct that you should assign the string while you're using the pointer returned by c_str().

All 5 comments

Hi @Wulf,
it should work in the same way. What logFile do? Could you specify your operating system and Node.js version?
I tried this code

#include <napi.h>
#include <iostream>

void PrintOK(const Napi::CallbackInfo& info) {
  std::string str = info[0].ToString().Utf8Value();
  const char* word = str.c_str();
  std::cout << word << "\n";
}

void PrintKO(const Napi::CallbackInfo& info) {
  const char* word = info[0].ToString().Utf8Value().c_str();
  std::cout << word << "\n";
}

Napi::Object Init(Napi::Env env, Napi::Object exports) {
  exports.Set(Napi::String::New(env, "printOK"),Napi::Function::New(env, PrintOK));
  exports.Set(Napi::String::New(env, "printKO"),Napi::Function::New(env, PrintKO));
  return exports;
}

NODE_API_MODULE(hello, Init)

and I have the same output.

c_str() returns a pointer to the underlying memory location. It is only valid for the lifetime of the string itself. In the code info[0].ToString().Utf8Value().c_str() the std::string is a temporary value and is release immediately after calling c_str(), so the pointer is immediately invalidated.

You are correct that you should assign the string while you're using the pointer returned by c_str().

Hey @NickNaso, thanks for that test snippet. It's working fine for me too. I don't know why it doesn't work here. I'm on ArchLinux using node v11.1.0 and the logFile is just an output file stream:

std::ofstream logFile("log.txt");

@rolftimmermans, thanks for the explanation. How do you suggest keeping the std::string value valid for the lifetime for an asynchronous operation? Should the characters be copied in the constructor of the AsyncWorker?

If you change SpellWorker to use std::string instead of const char* (for the word parameter and the word member) it should work.

@devsnek Cool.

@NickNaso just found out that this problem also occurs in your example if you use a large string. On my machine, 15 characters or more seems to do the trick.

Thanks everyone :rocket:

Was this page helpful?
0 / 5 - 0 ratings

Related issues

alexisfrjp picture alexisfrjp  路  6Comments

mhdawson picture mhdawson  路  4Comments

fivdi picture fivdi  路  8Comments

timarandras picture timarandras  路  4Comments

NickNaso picture NickNaso  路  3Comments