3
const char* getString() {
  std::string myString = "Hello!";
  return myString.str().c_str();
}

How can I get this function to return a const char * which will live past the local scope in which it is declared? I believe I need to use malloc, but I'm not sure.

RouteMapper
  • 2,484
  • 1
  • 26
  • 45
  • 14
    Why do that? Just return `std::string` and keep it around. Use `s.c_str()` when you need that `char const*`. (any solution will amount to reinventing `std::string`) – R. Martinho Fernandes Jun 14 '13 at 14:18
  • 2
    Are you sure you need to return a `const char *`? – Oliver Charlesworth Jun 14 '13 at 14:18
  • I'm using an API which demands a `const char*`. I would never have done it that way, but that's just what it is... – RouteMapper Jun 14 '13 at 14:18
  • 2
    Use strdup/strndup and don't forget to delete the returned string when you're done – rectummelancolique Jun 14 '13 at 14:19
  • Does the api have a function that takes a `const char *` (in which case just return `std::string` and call `api_func( getString().c_str() );`) or takes a pointer to a function with no arguments and returning a `const char *` (less likely, and harder to work around). – BoBTFish Jun 14 '13 at 14:21
  • 2
    @RouteMapper Do you know the expected lifetime of this `char *`? Is this API going to `free` it? `delete` it? Is *your* code responsible for destroying it at some point? By converting a `std::string` to `char*`, you've lost automatic lifetime management. – Drew Dormann Jun 14 '13 at 14:22
  • The API takes a callback which I define. The API requests a `const char *` from my callback. I need to ensure that the API has a live pointer to a `const char *` or else the API is delving into Undefined Behavior Land. – RouteMapper Jun 14 '13 at 14:23
  • @DrewDormann - The API doesn't delete the `char *`, AFAIK. – RouteMapper Jun 14 '13 at 14:24
  • @RouteMapper Yuck. What does the API say about ownership of the `const char *`? – BoBTFish Jun 14 '13 at 14:24
  • 3
    I'm dealing specifically with Line 54 of the following file within the LLVM API - http://llvm.org/docs/doxygen/html/MCExternalSymbolizer_8cpp_source.html. – RouteMapper Jun 14 '13 at 14:27
  • could you return string literals instead ? – Sander De Dycker Jun 14 '13 at 14:31
  • 1
    Haven't quite made out what is going on here yet, but it looks like the strings are being looked up in a table of symbols somewhere? In which case the lifetime of the strings is tied to the lifetime of the table, you shouldn't be returning anything local. – BoBTFish Jun 14 '13 at 14:33
  • I can return a `const char *`. So yes, string literals would be included in that. – RouteMapper Jun 14 '13 at 14:33
  • @BobTFish - `symbolLookup` is a callback which the API user is required to define outside the API. I look up the symbol and return the `const char *` representation of that symbol if I find one. Else, I return `NULL`. – RouteMapper Jun 14 '13 at 14:34
  • You want a one of these, right: http://llvm.org/docs/doxygen/html/group__LLVMCDisassembler.html#ga744daae29a3269eb9457502dc5f17a6d – BoBTFish Jun 14 '13 at 14:35
  • @RouteMapper : I know it's possible - my question was whether you can limit yourself to ONLY returning string literals. That would avoid the whole ownership issue. – Sander De Dycker Jun 14 '13 at 14:35
  • @RouteMapper So do you store a `std::string` in the symbol table? – BoBTFish Jun 14 '13 at 14:36
  • @BobTFish - Yes, I have created a callback function of that type. It's the return values that are creating an issue. I need the return values to live long enough for the API to actually use them. – RouteMapper Jun 14 '13 at 14:36
  • @BobTFish - No, I don't store a `std::string` in the symbol table. Should I store it elsewhere so it doesn't die? – RouteMapper Jun 14 '13 at 14:37
  • @RouteMapper I'm trying to establish why you can't do `return table[index].c_str();` It would live as long as the `std::string` wasn't written to. (And depending on what the container actually is, as long as you don't do anything that would invalidate iterators). – BoBTFish Jun 14 '13 at 14:38
  • @RouteMapper What I'm getting at is that the fact that this is a pointer to a character string is misleading here. Let's pretend for a moment you need to return a `const Widget *`. The idea is that you have a `Widget` stored in a container somewhere, e.g. `std::vector`, and return a pointer to that object: `return &v[index];`. – BoBTFish Jun 14 '13 at 14:41
  • I suppose I could. I hadn't thought of that. – RouteMapper Jun 14 '13 at 14:43

5 Answers5

5

I guess you need to have a static lookup table of strings so that SymbolLookUp can return the c_str value.

Peter Wood
  • 23,859
  • 5
  • 60
  • 99
  • 1
    I think you're right. Perhaps as I find symbols, I should map them somewhere permanent so they don't die. Then, I can return the `c_str` value corresponding to the mapped object. – RouteMapper Jun 14 '13 at 14:41
  • @RouteMapper Agreed. But of course be wary of writing to the contained `std::string`s or operations that invalidate iterators. – BoBTFish Jun 14 '13 at 14:42
  • 1
    @BobTFish - Of course. I would simply store the `std::string` and only ever return the `c_str` values to which they correspond. – RouteMapper Jun 14 '13 at 14:44
  • Then yes, this is the (/a) solution. – BoBTFish Jun 14 '13 at 14:47
3

It looks like SymbolLookup API you’re hooking into is assuming that the symbols are stored in a quasi-permanent structure and not generated on the fly. If your symbols are really transient, you need to make them permanent in some way, either C style through something like

return strdup(myString.c_str());

or in a more idiomatic C++ style with:

static std::vector<std::string> sStringPool;
sStringPool.push_back(myString);
return sStringPool.back().c_str();

Naturally, this is going to lead to unbounded memory growth, but if you have no other information about string lifetimes, there are few alternatives. If you want to get clever, you can at least unique the strings:

static std::set<std::string> sStringPool;
return sStringPool.insert(sStringPool.end(), myString)->c_str();
microtherion
  • 3,938
  • 1
  • 15
  • 18
0

Simply return the std::string and keep using it.

Just be careful about its lifetime, for example

const char* s = getString().c_str(); doStuff(s);

will cause the second statement to fail because the temporary string returned by getString only lives until the end of the statement.

If the API you are using takes a const char*, the following would work:

callTheAPI(getString().c_str());

and that string would then live for until the API returns.

Alexander Gessler
  • 45,603
  • 7
  • 82
  • 122
-1

EDIT: note, that it answers the question as it was stated. At time of this edit it seem obvious the question is misstated wrt original intent, but improved answer can't be provided until consolidation, if it's possible at all.)

Just don't do it. The whole point of using string class is to not use malloc and whatever crap. If you can't provide a const char* to a suitably stable existing location, return std::string from the function (or some other string class matching your taste).

Balog Pal
  • 16,195
  • 2
  • 23
  • 37
  • 3
    Please refer to comments on the OP to see why this is not doable. – RouteMapper Jun 14 '13 at 14:24
  • please edit the question to reflect the API and its requirements with some code sample... The info this far is too vague – Balog Pal Jun 14 '13 at 14:27
  • Check out Line 54 at the following link to the LLVM API: http://llvm.org/docs/doxygen/html/MCExternalSymbolizer_8cpp_source.html – RouteMapper Jun 14 '13 at 14:29
  • to me it implies the call is not supposed to retrieve an already existing thins, that is stored in the param structures or outer env. So it does not match your question in any way. – Balog Pal Jun 14 '13 at 14:38
  • The call retrieves a `const char *` representation of a symbol. The API developers never took it upon themselves to find symbols; they left this to the API user. So, it actually matches my question. – RouteMapper Jun 14 '13 at 14:40
-2
const char* getString() {
  std::string myString = "Hello!";
  return myString.str().c_str();
}

1) The correct syntax is myString.c_str()

2) That function returns a string on stack, so when you return such data, the compiler either makes a copy of it or does some optimization

3) Note that it returns const char * that means the correct way to bind that return value is to have statement such as const char *returnValue = getString() or when you pass that into a function that expects a const char. And because we are assigning a const char * to the returnValue, from C++ spec, that data will live passed the lifetime of the function. So that means we can do const char *str = getString() where getString is your function and it is defined and that the value returned by getString() lives passed the function's lifetime.

I don't see any necessary problem with the code you've posted above.

dchhetri
  • 6,926
  • 4
  • 43
  • 56
  • 1
    This is just very wrong. I think what you are thinking of is that if you returned a `std::string` *by value*, you could bind that temporary to a reference to `const`, and extend its lifetime. – BoBTFish Jun 14 '13 at 14:45
  • 2
    This returns a dangling pointer without any hope of usefulness. – Balog Pal Jun 14 '13 at 14:46
  • Yea you guys are correct, I was actually thinking about strings literals as in `const char *str="hello"; return str;` which should remain in memory till the life of the app since those are placed in text-segment memory. With the std::string, the char it points to will get deleted. My mistake. – dchhetri Jun 14 '13 at 18:09