Reputation: 931
More specifically I have a vector of some struct
std::vector<SomeStruct> extensions = getThoseExtensions();
where someStructVariable.extensionName
returns a string.
And I want to create a set of extensionName
, something like this std::set<const char*>
.
Process is fairly straightforward when done using some for
loops but I want to use std::transform
from <algorithm>
instead.
std::transform
has four parameters.
1,2. First range (to transform first range from and to)
3. Second range/inserter (to transform second range)
4. A function
This is what I have so far
auto lambdaFn =
[](SomeStruct x) -> const char* { return x.extensionName; };
std::transform(availableExtensions.begin(),
availableExtensions.end(),
std::inserter(xs, xs.begin()),
lambdaFn);
because there's no "proper context" for std::back_inserter
in std::set
I'm using std::inserter(xs, xs.begin())
.
The problem is I'm trying to return stack mem in my lambda function. So how do I get around this problem?
Oddly enough if I remove return
from the function it works just like I expect it to! But I don't understand why and that strikes fear of future repercussion.
EDIT:
I'm using several structs in place of SomeStruct
like VkExtensionProperties
defined in vulkan_core
typedef struct VkExtensionProperties {
char extensionName[VK_MAX_EXTENSION_NAME_SIZE];
uint32_t specVersion;
} VkExtensionProperties;
From Khronos specs
Upvotes: 4
Views: 1581
Reputation: 5095
One way to do what you want is with the following lambda
auto lambda = [](const SomeStruct& x) -> const char* { return x.extensions.data();};
The problem with this is, that you are saving pointers to memory owned by someone else (those strings). When they are destroyed (this seems to be the case at the end of the function), the pointer will be dangling. You can get around this by allocating memory in your lambda and copying the data:
auto lambda = [](const SomeStruct & x) -> const char* {
char* c = new char[x.extensions.length()+1];
std::strcpy(c, x.extensions.data());
return c;
}
But then you have to do memory management yourself (i.e. remember to free those const char*
). And that is a bad idea. You should probably reconsider what you are doing. Why are you using const char*
here and not std:: string
?
Please remember that the typical use for const char*
is to save string literals i C-code, i.e. the code
const char* str = "Hello World!";
creates a char
array of sufficient size in the static section of the memory, initializes it with the string (a compile time constant) and then saves a pointer to that in str
. This is also why this has to be a const char*
since another pointer refering to an equal string literal may (or may not) point the exactly the same char array and you don't want to enable change there. So don't just use const char*
because you see strings in C saved in those const char*
without anyone needing to free them later.
Upvotes: 1
Reputation: 36614
You probably can't create a set of char *
unless all instances of extensionName
with the same value point to the same char array (it would store unique pointers instead of unique values). If you use std::set<std::string>
instead this will both work and only store unique values and solve your variable lifetime problem as std::string
takes care of copying (or moving) itself for you where necessary:
auto lambdaFn =
[](const SomeStruct& x) { return std::string(x.extensionName); };
std::set<std::string> xs;
std::transform(availableExtensions.begin(),
availableExtensions.end(),
std::inserter(xs, xs.begin()),
lambdaFn);
Upvotes: 7
Reputation: 5530
There are a couple of things you can do here.
SomeStruct
, it is best if you changed that member to std::string
.const auto& obj
. This will not create a copy and point back to the object the container has. However, I am still afraid of this solution since this smells like bad class design where ownership and lifetime of members is ambiguous.Upvotes: 0