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());
}
Related
I have an array of strings that must be allocated once and their underlying c_str must remain valid for entire duration of the program.
There's some API that provides info about some arbitrary data types. Could look like this:
// Defined outside my code
#define NUMBER_OF_TYPES 23
const char* getTypeSuffix(int index);
The getTypeSuffix is not constexpr, so this must work at least partially in runtime.
The interface that I must provide:
// Returned pointer must statically allocated (not on stack, not malloc)
const char* getReadableTypeName(int type);
Now my array should have following type:
std::string typeNames[NUMBER_OF_TYPES];
For my purposes, it will be initialized within a wrapper class, right in the constructor:
class MyNames
{
MyNames()
{
for (int i = 0; i < NUMBER_OF_TYPES; ++i)
{
names[i] = std::string("Type ") + getTypeSuffix(i);
}
}
const char* operator[](int type) { return _names[(int)type].c_str(); }
private:
std::string _names[NUMBER_OF_TYPES];
};
This is then used in an singleton-ish kind of way, for example:
const char* getReadableTypeName(int type)
{
static MyNames names;
return names[type];
}
Now what I want to improve is that I can see that the for loop in the constructor could be replaced as such:
MyNames() : _names{std::string("Type ") + getTypeSuffix(0), std::string("Type ") + getTypeSuffix(1), ... , std::string("Type ") + getTypeSuffix(NUMBER_OF_TYPES-1)}
{}
Obviously a pseudocode, but you get the point - the array can be initialized directly, leaving the constructor without body, which is neat. It also means that the array member _names can be const, further enforcing the correct usage of this helper class.
I'm quite sure there would be many other uses to filling an array by expressions in compile time, instead of having loop. I would even suspect that this is something that happens anyway during 03.
Is there a way to write a C++11 style array initializer list that has flexible length and is defined by an expression? Another simple example would be:
constexpr int numberCount = 10;
std::string numbers[] = {std::to_string(1), std::to_string(2), ... , std::to_string(numberCount)};
Again, an expression instead of a loop.
I'm not asking this question because I was trying to drastically improve performance, but because I want to learn about new, neat, features of C++14 and later.
instead of C-array use std::array, then you might write your function to return that std::array and your member can then be const:
std::array<std::string, NUMBER_OF_TYPES> build_names()
{
std::array<std::string, NUMBER_OF_TYPES> names;
for (int i = 0; i < NUMBER_OF_TYPES; ++i)
{
names[i] = std::string("Type ") + getTypeSuffix(i);
}
return names;
}
class MyNames
{
MyNames() : _names(build_names()) {}
const char* operator[](int type) const { return _names[(int)type].c_str(); }
private:
const std::array<std::string, NUMBER_OF_TYPES> _names;
};
Now you have std::array, you might use variadic template instead of loop, something like (std::index_sequence stuff is C++14, but can be implemented in C++11):
template <std::size_t ... Is>
std::array<std::string, sizeof...(Is)> build_names(std::index_sequence<Is...>)
{
return {{ std::string("Type ") + getTypeSuffix(i) }};
}
and then call it:
MyNames() : _names(build_names(std::make_index_sequence<NUMBER_OF_TYPES>())) {}
You can defer to an initialization function:
std::array<std::string, NUMBER_OF_TYPES> initializeNames()
{
std::array<std::string, NUMBER_OF_TYPES> names;
for (int i = 0; i < NUMBER_OF_TYPES; ++i) {
names[i] = std::string("Type ") + getTypeSuffix(i);
}
return names;
}
const char* getReadableTypeName(int type)
{
static auto const names = initializeNames();
return names[type].c_str();
}
which can be an immediately invoked lambda:
static auto const names = []{
std::array<std::string, NUMBER_OF_TYPES> names;
// ...
return names;
}();
or do you really need the array requirement? We're making strings anyway so I don't understand, then you can just use range-v3:
char const* getReadableTypeName(int type) {
static auto const names =
view::iota(0, NUMBER_OF_TYPES)
| view::transform([](int i){ return "Type "s + getTypeSuffix(i); })
| ranges::to<std::vector>();
return names[type].c_str():
}
You can use std::make_integer_sequence and a delegating constructor in C++14 (Implementations of std::make_integer_sequence exist in C++11, so this is not really C++14 specific) to get a template parameter pack of integers
#include <string>
#include <utility>
#define NUMBER_OF_TYPES 23
const char* getTypeSuffix(int index);
class MyNames
{
MyNames() : MyNames(std::make_integer_sequence<int, NUMBER_OF_TYPES>{}) {}
template<int... Indices>
MyNames(std::integer_sequence<int, Indices...>) : _names{ (std::string("Type ") + getTypeSuffix(Indices))... } {}
const char* operator[](int type) { return _names[(int)type].c_str(); }
private:
const std::string _names[NUMBER_OF_TYPES];
};
This means that no strings are being default constructed.
Since you ache to use new features, let's use range-v3 (soon-to-be the ranges library in C++2a) to write some really short code:
const char* getReadableTypeName(int type)
{
static const std::vector<std::string> names =
view::ints(0, 23) | view::transform([](int i) {
return "Type " + std::to_string(i);
});
return names[type].c_str();
}
https://godbolt.org/z/UVoENh
bool foo(const char* arg, const char* str[]){
for (int i=0; i < (signed)sizeof(str); i++){
if(strcmp(arg, str[i])) == 0){
return true;
}
}
return false;
}
int main(){
foo("c", {"a", "b", "c"});
return 0;
}
I want to pass an array of const char* to the function directly like it is shown here. But I keep getting the error: cannot convert '<brace-enclosed initializer list>' to 'const char**'
While there is a way to make that work using templates and passing C-arrays by reference, I'm not sure if that is what you really want: example
In C++11 I'd much prefer to use a std::intializer_list to make the call foo("c", {"a", "b", "c"}) work as intended:
#include <initializer_list>
bool foo(const char* arg, std::initializer_list<const char*> strings) {
for (const char* str : strings) {
/* ... */
}
}
While we're at it, you should consider using C++'s string facilities over those inherited from C:
#include <initializer_list>
#include <string>
bool foo(const std::string& arg, std::initializer_list<std::string> strings) {
for (const auto& str : strings) {
if (arg == str) {
return true;
}
}
return false;
}
Note that a std::initializer_list does not own the values it provides access to, so if you want to store it, use a proper container like std::vector instead.
Also, if checking for the presence of arg in str is all you want to do,
why not use std::find?
#include <algorithm>
#include <initializer_list>
#include <string>
bool foo(const std::string& arg, std::initializer_list<std::string> strings) {
return std::find(strings.begin(), strings.end(), arg) != strings.end();
}
Use a character pointer pointer (char**) to achieve this. You cannot declare parameters as arrays, but you can pass arrays into functions, as they will be converted into double pointers.
You can also pass the array by reference via a template function, so you will be able to detect its size at compile time (note that in your case the array decays to a pointer, so sizeof doesn't do what you think). Example:
#include <iostream>
#include <cstring>
template<int N>
bool foo(const char* arg, const char* (&str)[N])
{
for (std::size_t i = 0; i < N; i++) {
if (std::strcmp(arg, str[i]) == 0) {
return true;
}
}
return false;
}
int main() {
const char* arr[] = {"abc", "de"};
std::cout << std::boolalpha << foo("de", arr);
}
Live on Coliru
And btw, try to use algorithms from the C++ standard library and std::string instead of strcmp() and raw C-strings:
#include <algorithm>
#include <iostream>
#include <vector>
bool foo(const std::string arg, const std::vector<std::string>& str)
{
return std::find(str.begin(), str.end(), arg) != str.end();
}
int main()
{
std::cout << std::boolalpha << foo("de", {"abc", "de"});
}
Live on Coliru
Although it is recommended to drop C-style programming when using C++, and use stl containers instead, I would like to mention that the following "one-liner" works:
bool foo(const char* arg, const char* const* str, size_t siz) {
for (size_t i = 0; i < siz; i++)
if (!strcmp(arg, str[i])) return true;
return false;
}
int main() {
cout << foo("c", array<char*, 3> { "a", "b", "c" }.data(), 3);
system("pause");
}
The surprising surprise is that if we define the second argument of foo as const char**, the compiler (VS2015) yells and says:
bool foo(...): cannot convert argument 2 from 'char **' to 'const char **'
:O
cannot convert from non-const to const??? Does that make sense anyone?
Edit: #NeilKirk explained it in his comment. Normal. Accordingly the signature should be as:
bool foo(const char* arg, const char* const* str, size_t siz)
and everything works as expected.
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
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.
The method fails to find the char* array passed to it even though it is present in map.
When I replaced char* with std::string in map. Code works fine.
static void CreateTranslationMap();
static UString FindTranslatedString(char* propertyName);
static std::map<char*,UString> TranslationMap ;
static void CreateTranslationMap()
{
UString engString("TextAlignmentPosition");
char* transString= MSGTXT("TextAlignmentPosition");
TranslationMap.insert(std::pair<char*,UString>(transString,engString));
}
UString FindTranslatedString(char* propertyName)
{
UString NotFound("CannotFind");
std::map<char*, UString>::iterator itr;
itr = TranslationMap.begin();
itr = TranslationMap.find(propertyName);
if(itr!= TranslationMap.end())
{
return itr->second;
}
else if(itr== TranslationMap.end())
{
return NotFound;
}
}
You need to use your own custom comparator for comparing pointer to char
Use:
struct cmp_c_string
{
bool operator()(char const *lhs, char const *rhs) const
{
return std::strcmp(lhs, rhs) < 0;
}
};
std::map<char*,UString, cmp_c_string > TranslationMap ;
That's because when doing comparison for equality the map uses <.
When the Key of the map is char* you are doing comparisons of pointers (not the C-String). So you are testing to see if one pointer is less than the other pointer (ie comparing the address).
When the Key of the map is std::string you using the operator< that is defined for std::string, which actually compares the characters in the string to determine which is less than the other.
As the std::map is a template it actually takes more parameters to define how it works. The third parameters is the comparison function (which defaults to less std::less<K> which is usually operator<).
So you can use char* you just need a custom comparator operator.
bool comparitor(char const* lhs, char const* rhs)
{
return (test for less than or some other strict weak function);
}
std::map<char*, UString, comparitor> myMap;
when using char *, it just compare address.
char a[] = "hi";
char b[] = "hi";
char *c = a;
char *d = b;
c & d are different.(c != d) If you want to compare string, you should use strcmp.
But when using string, it overwrites "==" operation.
So you can just compare using "=="
string a = "hi";
string b = "hi";
a & b are same. (a == b)
You have this behavior because you use pointer to string literal which is different every time you create such a pointer. So, for example, you create 2 pointers:
char* p1 = "Hello world!";
char* p2 = "Hello world!";
While content to which p1 and p2 point is identical the pointers, themselves, are different. So p1 != p2, and you trying to store pointer in the map. You should use std::string instead or have global constants pointers which you'd use everywhere; something like:
const char* const c_transString = MSGTXT("TextAlignmentPosition");
...
TranslationMap.insert(std::pair<char*,UString>(c_transString, engString));
...
FindTranslatedString(c_transString)
Just replace char* to const char* because the map data type always take the string in const form . I took your example and it is running in my terminal. So the new code is :
#include<iostream>
using namespace std;
static void CreateTranslationMap();
static string FindTranslatedString(const char* propertyName);
static std::map<const char*,string> TranslationMap ;
static void CreateTranslationMap()
{
string engString("TextAlignmentPosition");
const char* transString= ("1");
TranslationMap.insert(std::pair<const char*,string>(transString,engString));
}
string FindTranslatedString(const char* propertyName)
{
string NotFound("CannotFind");
std::map<const char*, string>::iterator itr;
itr = TranslationMap.begin();
itr = TranslationMap.find(propertyName);
if(itr!= TranslationMap.end())
{
return itr->second;
}
else if(itr== TranslationMap.end())
{
return NotFound;
}
}
int main()
{
CreateTranslationMap();
string s =FindTranslatedString("1");
cout<<s<<endl;
return 0;
}