How do I capture a smart pointer in a lambda?
c++, lambda, smart-pointers
Solution
A lambda (even one with a universal capture like `[=]`) only actually captures variables used within its definition. Since in your example, `pointer` is never used inside the lambda, it's not captured and thus when it goes out of scope, it's the last shared pointer referring to `stored` and `free()` is called.
If you want to capture `pointer`, you could force its use:
return [=](const char * delim) mutable -> const char *
{
pointer;
const char *b = strtok_r(stored, delim, &tmpstorage);
stored = nullptr;
return b;
};
However, this is rather hackish. You want your functor stateful and with nontrivial state management. To me, this is a strong indicator an actual named class (instead of a lambda) would be in order. So I would change it like this:
std::function<const char *(const char *)> test(const char *input)
{
struct Tokenizer
{
std::shared_ptr<char> pointer;
char* stored;
char* tmpstorage;
explicit Tokenizer(char* stored) : pointer(stored, free), stored(stored), tmpstorage(nullptr) {}
const char* operator() (const char * delim)
{
const char *b = strtok_r(stored, delim, &tmpstorage);
stored = nullptr;
return b;
}
};
return Tokenizer(strdup(input));
}
Problem
What is best way to capture a smart pointer in a lambda? One attempt of mine lead to a use-after-free bug. Example code: ``` #include <cstring> #include <functional> #include <memory> #include <iostream> std::function<const char *(const char *)> test(const char *input); int main() { std::cout.sync_with_stdio(false); std::function<const char *(const char *)> a = test("I love you"); const char *c; while ((c = a(" "))){ std::cout << c << std::endl; } return 0; } std::function<const char *(const char *)> test(const char *input) { char* stored = strdup(input); char *tmpstorage = nullptr; std::shared_ptr<char> pointer = std::shared_ptr<char>(stored, free); return [=](const char * delim) mutable -> const char * { const char *b = strtok_r(stored, delim, &tmpstorage); stored = nullptr; return b; }; } ``` fails, as shown by AddressSanitizer.