How do I capture a smart pointer in a lambda? - c++

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.

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));
}

Just capture the variable by value and let the copy constructor and destructor worry about ownership semantics- that's what smart pointers are for.

Related

reason for pointer to a const pointer when using static_cast

I have tried to solve an exercise from a book but I failed on the static_cast. I used the qsort Method from cstdlib. I have to cast the parameters of my function to a C-String (const char*). But I always get the error message: stattic_cast from type 'const void*' to type 'const char**' casts away qualifiers.
int scmp(const void *s1, const void *s2) {
const char *c1 = (static_cast<const char**>(s1));
const char *c2 = (static_cast<const char**>(s2));
....
}
const char *sfield[] = {"one", "two", "three", "four", "five"};
qsort(sfield, 10, 4, scmp);
The solution is as follows
const char *c1 = *(static_cast<const char* const*>(s1));
What is the reason for the last const and where does it come from? Why I have to cast to a pointer to a constant pointer to char const?
It comes from the origin pointer. static_cast may not discard the const qualifier. So you can only cast void const* to a T const*.
Now, it just so happens that your T is a char const*. You were probably led astray by the leading const in your original code. It does not apply where one may think it applied.
The qsort comparator parameters are pointers to const versions of the elements being compared. In your example the elements being compared are const char *, so a pointer to const of that is const char * const *. Hence the correct version of the code:
int scmp(const void *s1, const void *s2)
{
auto pc1 = static_cast<const char * const *>(s1);
auto pc2 = static_cast<const char * const *>(s2);
char const *c1 = *pc1;
char const *c2 = *pc2;
return strcmp(c1, c2); // or whatever
}
You can do away with pc1, pc2 and apply * operator to the result of the cast if you like .
Perhaps you mistakenly assumed the arguments were the elements being compared, when in fact they are pointers to the elements being compared .
If it is still not clear then maybe it would help to use a symbolic name for the element type:
using ELEMENT_T = const char *;
int scmp(void const *s1, void const *s2)
{
auto pc1 = static_cast<ELEMENT_T const *>(s1);
auto pc2 = static_cast<ELEMENT_T const *>(s2);
ELEMENT_T c1 = *pc1;
ELEMENT_T c2 = *pc2;
return strcmp(c1, c2); // or whatever
}
The same pattern would work for elements that are not pointers (e.g. integer elements).

Constant character pointer using unique_ptr

Let's say that I have a vector<string> input and I need to pass this to a function that takes a const char** argument. My thought had been to use a unique_ptr like this:
const auto output = make_unique<char*>(size(input));
But I can't seem to turn a const unique_ptr<const*> into a const char**. Is there a way to accomplish this, or perhaps a simpler alternative?
I would just build a vector of pointers to the c_str()'s of the strings and then get a pointer to that.
std:vector<const char*> pointers;
pointers.reserve(input.size());
for (const auto& e : input)
pointers.push_back(e.c_str()); // get const char *'s
auto argument = pointers.data(); // get pointer to const char*'s - const char**
Or using a unique_ptr
auto pointers = std::make_unique<const char*[]>(size(input))
for (size_t i = 0; i < input.size(); ++i)
pointers[i]= input[i].c_str(); // get const char *'s
auto argument = pointers.get(); // get pointer to const char*'s - const char**
I assume you need this to fit some interface you have no control of, otherwise I would consider adapting the interface in question to avoid unnecessary creation of temporary data just for the sake of fitting an ill-fitted interface…
Since you just need a temporary array of known size, the simplest solution would probably be to allocate an array of pointers and fill it with pointers to the strings in your vector:
auto pointers = std::make_unique<const char*[]>(size(v));
std::transform(begin(v), end(v), &pointers[0], [](const auto& s) { return s.c_str(); });
This array could also be placed on the stack to avoid dynamic memory allocation. But since you're working with strings here and are willing to copy data into a temporary array, I assume performance is not critical, so I guess there's no need for the added complexity…
two approaches, depending on whether the c interface requires null termination or not:
#include <vector>
#include <string>
#include <algorithm>
auto make_c_interface_null_terminated(std::vector<std::string> const &input) -> std::vector<const char*>
{
auto result = std::vector<const char*>(input.size() + 1);
auto to_c_str = [](auto&& str) { return str.c_str(); };
std::transform(begin(input), end(input), begin(result), to_c_str);
// implied: result[result.size() - 1] = nullptr
return result;
}
auto make_c_interface(std::vector<std::string> const &input) -> std::vector<const char*>
{
auto result = std::vector<const char*>(input.size());
auto to_c_str = [](auto&& str) { return str.c_str(); };
std::transform(begin(input), end(input), begin(result), to_c_str);
return result;
}
extern "C" void c_interface_requires_null(const char** argv);
extern "C" void c_interface_sized(size_t size, const char** args);
void test(std::vector<std::string> const &input)
{
auto output1 = make_c_interface_null_terminated(input);
c_interface_requires_null(output1.data());
auto output2 = make_c_interface(input);
c_interface_sized(output1.size(), output1.data());
}

Why cant i use static_cast<const char**>(str) instead of (const char**)str?

I have an issue, it doesn't want to cast using static_cast<>. What can it be?
void myCompare(const void *str)
{
const char *ca = *(static_cast<const char**>(str)); //error
const char *a = *(const char **)str; //ok
}
You're casting away const on the second level, which static_cast is not allowed to do (in fact, no "C++" cast apart from const_cast):
void const*
char const* *
// ^^^^^^^^^^^ ^^^^^
// pointee cv-qualifiers
// of pointee
Instead, write
const char *ca = *(static_cast<const char* const*>(str));
The (char const**) cast works here because it is equivalent to a static_cast followed by a const_cast (as per [expr.cast]/(4.3)) - i.e. it was equivalent to
const char *ca = *(const_cast<const char**>(static_cast<const char* const*>(str)));

Array of C strings, initialized with string literals

The Ghostscript interpreter API has a function
GSDLLEXPORT int GSDLLAPI gsapi_init_with_args(void *instance, int argc, char **argv)
The final argument argv is a pointer to an array of C strings, which are interpreted as command-line arguments. I obviously cannot change the signature of the function gsapi_init_with_args to take a const char ** argument instead.
If I were willing to ignore (or silence) the deprecated conversion from string constant to 'char*' warning, then I would write simply
char *gs_argv[] = {"", "-dNOPAUSE", "-dBATCH", ...};
and pass gs_argv as the final argument. But I would prefer to fix my code so that I am not relying on an external function to behave in the way I expect it to (and effectively treat gs_argv as const char**).
Is there any simple way to declare gs_argv as an array of pointers to (non-const) C strings, and initialize its elements with string literals? (That is, using a similar approach to how I can initialize a single C string: using char c_str[] = "abc".) The best I can think of is to use
const char *gs_argv0[] = {"", "-dNOPAUSE", "-dBATCH", ...};
and then copy the contents, element by element, into gs_argv.
Please note that I understand why the compiler gives this warning (and have read the answers to, among others, this question). I am asking for a solution, rather than an explanation.
You can use:
char arg1[] = "";
char arg2[] = "-dNOPAUSE";
char arg3[] = "-dBATCH";
char* gs_argv0[] = {arg1, arg2, arg3, NULL};
int argc = sizeof(gs_argv0)/sizeof(gs_argv0[0]) - 1;
gsapi_init_with_args(instance, argc, gs_argv0)
Create copies of the string literals using strdup. This is more verbose, but fixes the warning.
char* gs_argv0[NARGS];
gs_argv0[0] = strdup("");
gs_argv0[1] = strdup("-dNOPAUSE");
// ...
Note that you will also need to free the memory allocated by strdup if you want to prevent leaks.
You might also want to add a comment to your code saying why you are doing this, to make it clear for future readers.
If you can guarantee that the function will not modify the non-const parameter, then it is acceptable to use const_cast in this situation.
A C++14 solution.
#define W(x) \
(([](auto& s)->char* \
{ \
static char r[sizeof(s)]; \
strcpy (r, s); \
return r; \
})(x))
char* argv[] =
{ W("--foo=bar",
W("baz"),
nullptr
};
Since this code requires C++11, there's a lower cost C++11 solution in another answer below. I'm leaving this one for posterity.
There are pretty much two choices: ignore it and const_cast, or do the right thing. Since this is modern C++, you're supposed to have nice, RAII classes. Thus, the simplest, safest thing to do is to safely wrap such an array.
// https://github.com/KubaO/stackoverflown/tree/master/questions/args-cstrings-32484688
#include <initializer_list>
#include <type_traits>
#include <cstdlib>
#include <cassert>
#include <vector>
class Args {
struct str_vector : std::vector<char*> {
~str_vector() { for (auto str : *this) free(str); }
} m_data;
void append_copy(const char * s) {
assert(s);
auto copy = strdup(s);
if (copy) m_data.push_back(copy); else throw std::bad_alloc();
}
public:
Args(std::initializer_list<const char*> l) {
for (auto str : l) append_copy(str);
m_data.push_back(nullptr);
}
template <std::size_t N>
Args(const char * const (&l)[N]) {
for (auto str : l) append_copy(str);
m_data.push_back(nullptr);
}
/// Initializes the arguments with a null-terminated array of strings.
template<class C, typename = typename std::enable_if<std::is_same<C, char const**>::value>::type>
Args(C l) {
while (*l) append_copy(*l++);
m_data.push_back(nullptr);
}
/// Initializes the arguments with an array of strings with given number of elements.
Args(const char ** l, size_t count) {
while (count--) append_copy(*l++);
m_data.push_back(nullptr);
}
Args(Args && o) = default;
Args(const Args &) = delete;
size_t size() const { return m_data.size() - 1; }
char ** data() { return m_data.data(); }
bool operator==(const Args & o) const {
if (size() != o.size()) return false;
for (size_t i = 0; i < size(); ++i)
if (strcmp(m_data[i], o.m_data[i]) != 0) return false;
return true;
}
};
Let's see how it works:
#include <iostream>
extern "C" int gsapi_init_with_args(void*, int argc, char** argv) {
for (int i = 0; i < argc; ++i)
std::cout << "arg " << i << "=" << argv[i] << std::endl;
return 0;
}
int main()
{
Args args1 { "foo", "bar", "baz" };
const char * args2i[] { "foo", "bar", "baz", nullptr };
Args args2 { (const char **)args2i };
const char * args3i[] { "foo", "bar", "baz" };
Args args3 { args3i };
const char * const args4i[] { "foo", "bar", "baz" };
Args args4 { args4i };
const char * args5i[] { "foo", "bar", "baz" };
Args args5 { args5i, sizeof(args5i)/sizeof(args5i[0]) };
assert(args1 == args2);
assert(args2 == args3);
assert(args3 == args4);
assert(args4 == args5);
gsapi_init_with_args(nullptr, args1.size(), args1.data());
}
Output:
arg 0=foo
arg 1=bar
arg 2=baz
Try to const_cast it:
gsapi_init_with_args(instance, argc, const_cast<char**>(argv));
Maybe it will help with fixing warning.
Inspired by n.m.'s C++14 version, here's a C++11 version. The trick is to use an evaluated empty lambda expression to generate a fresh type, so that each instantiation of W__ is unique.
template <typename T, int N> static char * W__(const char (&src)[N], T) {
static char storage[N];
strcpy(storage, src);
return storage;
}
#define W(x) W__(x, []{})
char * argv[] = {
W("foo"),
W("bar")
};
The static in front of W__'s return type means that W__ has internal linkage and won't bloat the object file with extra symbols. It has nothing to do with the static in front of storage, as the latter indicates the static storage duration for the local variable. The code below would be perfectly valid, but of course doing the wrong thing and having undefined behavior:
template <typename T, int N> static char * BAD(const char (&src)[N], T) {
char storage[N];
strcpy(storage, src);
return storage;
}
Since a lambda has to be evaluated, you can't simply make its type a template argument:
template<typename> void G();
G<decltype([]{})>(); // doesn't work

returning a const char* from a function

I have to replace some char array code. So say I have a class that has an std::string member variable.
class foo
{
private:
std::string _sBar;
public:
const char* getBar() const { return _sBar.c_str(); }
};
existing code expects that const char*'s are returned in the string accessor functions, so I can't return a const std::string reference.
But isn't there some rule that when the stack unwinds that you can no longer trust the return value from the _sBar.c_str() ?
Yes, that's correct. Better if you ask the caller to supply a buffer with a fixed size say, the caller allocates as:
const int MAX = 1000; // choose some suitable value
char buff[MAX];
And the caller has a foo object,
foo a;
...
a.getBar(buff, MAX);
...
And you define getBar as:
void getBar(char *buffer, int size) const {
strncpy(buffer, _sBar.c_str(), size -1);
buffer[size -1] = 0;
}
you can make a copy of that string with new [ ] operator inside your member function, and it will be stored independently from the class object. In the class:
plublic:
const char* getBar() const {
char * str = new char[_sBar.length()+1];
strcpy(str, _sBar.c_str());
return str;}
In main:
foo object;
///some code
const char* bar = object.getBar();
///some code
delete [] bar;
Note it's good style to free the memory using delete [ ].