Is there a way to avoid array-to-pointer decay? - c++

The parameter to main char* argv[] decays to char**, which is unfortunate, because it cannot be used with std::begin which only accepts arrays. The only workaround I see is to use a variable length array which is undesirable.
#include <iostream>
int main(int argc, char* argv[])
{
char* _argv[argc];
for (int i = 0; i < argc; ++i)
_argv[i] = argv[i];
for (arg : _argv)
{
std::cout << arg << " ";
}
return 0;
}
Desirably I want something like: char* _argv[] = { ... };

That is not possible, since the signature of main is fixed. You can copy the elements to a vector using:
std::vector<std::string> args {argv, argv + argc};
Then you can use std::begin on args

You can define a simple class wrapper to give you begin() and end().
struct Args {
int argc_;
char **argv_;
Args (int argc, char *argv[]) : argc_(argc), argv_(argv) {}
char ** begin () { return argv_; }
char ** end () { return argv_ + argc_; }
};
int main(int argc, char *argv[]) {
for (auto s : Args(argc, argv)) {
std::cout << s << '\n';
}
return 0;
}

The only workaround I see is to use a variable length array which is
undesirable.
The only workaround for what exactly? There is no problem. If you actually need an array of strings, use a vector as shown in TNAs answer; I doubt you need to modify the parameters though.
If you just need to iterate through it, there is no need to copy all the pointers.
for (auto str : boost::make_iterator_range(argv, argv + argc))
std::cout << str;
Or
for (auto ptr = argv; ptr != argv + argc; ++ptr)
std::cout << *ptr << '\n';
The parameter to main char* argv[] decays to char**, which is
unfortunate, because it cannot be used with std::begin which only
accepts arrays.
There is no other way. The amount of command line parameters is not known at compile time, and this method of passing them (instead of using two pointers-to-pointers) is historically motivated.

Related

Is there a way to save "char * argv[]" to a variable in C++?

I'd like to be able to save argv into a struct so that it can be passed to functions like so:
struct Parameters {
int argc;
char * argv[];
};
void Start(
Parameters P
) {
};
int main (
int argc,
char * argv []
) {
Parameters P;
P.argc = argc;
P.argv = & argv;
return 0;
}
But with:
clang++ -std=c++2a -stdlib=libc++ -rtlib=compiler-rt -Ofast Start.cpp -o Start && ./Start;
I'm getting this error:
Start.cpp:21:9: error: array type 'char *[]' is not assignable
Is there a way of saving argv to a variable? Any help would be very much appreciated.
A simple way is to convert it to a vector of strings:
int main(int argc, char* argv[])
{
// Note: use argv + 1 to skip the application name in args.
// If you want to include the application name then don't use
// the +1
std::vector<std::string> args(argv + 1, argv + argc);
// Now this can be passed to functions easily.
// args.size() == number of arguments.
// args[x] == the string for argument x
}
You can simply change to:
struct Parameters
{
int argc;
char ** argv;
};
Your argv array of pointers to char will decay to a pointer to pointer to char.
Then, your main becomes simpler, with:
P.argv = argv;

replacing the command line arguments int argc and char** argv with std::vector<std::string>

Following this post, where I have found a temporary workaround for my other problem, I want to know if I can replace the int argc, char** argv with a std::vector<std::string> variable/object.
Consider the imaginary code:
#include <iostream>
#include <CloseLibrary>
void someFunction(int argc, char** argv){
for (int i = 0; i < argc; ++i) {
std::cout << argv[i] << std::endl;
}
}
int myFunc(int argc, char** argv){
someFunction(argc, argv);
return 0;
}
where the CloseLibrary is a closed library that I don't have access to the source code, and the someFunction function from that library demands the int argc, char** argv command line arguments. But for some reason I can't have double pointers char** in my code.
Here in this post something like what I need is proposed, but I don't know how to use that. Can I write the code this way:
#include <iostream>
#include <CloseLibrary>
#include <vector>
void someFunction(int argc, char** argv){
for (int i = 0; i < argc; ++i) {
std::cout << argv[i] << std::endl;
}
}
int myFunc("args", [](std::vector<std::string> args){
std::vector<char *> cstrs;
cstrs.reserve(args.size());
for (auto &s : args) cstrs.push_back(const_cast<char *>(s.c_str()));
someFunction(cstrs.size(), cstrs.data());
return 0;
}
Or maybe there is a more canonical way to do this? I would appreciate it if you could help me find the correct way to do this and understand the solution. Thanks for your help in advance.
P.S.1. The char* argv[] method is ok in the body of the function but not ok in the inputs. I don't know why pybind11 does this!
P.S.2. Here on pybind11 gitter, this was suggested:
void run(const std::vector<std::string>& args) {
for(auto&& e : args) std::cout << e << '\n';
}
P.S.3. Also suggested on pybind11 Gitter:
char** argv = new char*[vec.size()]; // just like malloc(sizeof(char*)*vec.size());
for (int i = 0; i < vec.size(), i++) {
argv[i] = new char[vec[i].size()];
memcpy(argv[i], vec[i].data(), vec[i].size()); // or strcpy
}
You could use the constructor which initializes the vector from a given range, with the argv parameter acts as the starting iterator and argv+argc acting as the ending iterator.
For example, I usually start my main function with:
int main( int argc, char* argv[] )
{
std::vector< std::string > args( argv, argv + argc );
for ( auto s : args )
{
std::cout << s << std::endl;
}
}
Note that this will also capture the first argument (argv[0]) which usually (but not necessarily) hold the name of the application when it is started.
In your case, you would like to do the reverse, build up a contiguous array of char* from a std::vector< std::string >. I would do something like:
std::vector< char* > rargs( args.size(), 0 ); // Initialize N nullptrs.
for ( int i=0; i<args.size(); ++i )
{
std::strcpy( rargs[i], args[i].c_str() ); // One-by-one strcpy them
}
And then you can pass them into a function accepting an argc, argv as
someFunction( rargs.size(), rargs.data() );
For what it's worth ... Taking it completely back to your original problem of not being able to use char** with pybind11, a full working example, scavenged from the pieces you posted is below. Yes, it's not pretty, but working with pointers never is.
#include <pybind11/pybind11.h>
#include <iostream>
#if PY_VERSION_HEX < 0x03000000
#define MyPyText_AsString PyString_AsString
#else
#define MyPyText_AsString PyUnicode_AsUTF8
#endif
namespace py = pybind11;
void closed_func(int argc, char** argv){
for (int i = 0; i < argc; ++i) {
std::cout << "FROM C++: " << argv[i] << std::endl;
}
}
void closed_func_wrap(py::object pyargv11) {
int argc = 0;
std::unique_ptr<char*[]> argv;
// convert input list to C/C++ argc/argv
PyObject* pyargv = pyargv11.ptr();
if (PySequence_Check(pyargv)) {
Py_ssize_t sz = PySequence_Size(pyargv);
argc = (int)sz;
argv = std::unique_ptr<char*[]>{new char*[sz]};
for (Py_ssize_t i = 0; i < sz; ++i) {
PyObject* item = PySequence_GetItem(pyargv, i);
argv[i] = (char*)MyPyText_AsString(item);
Py_DECREF(item);
if (!argv[i] || PyErr_Occurred()) {
argv = nullptr;
break;
}
}
}
// bail if failed to convert
if (!argv) {
std::cerr << "argument is not a sequence of strings" << std::endl;
return;
}
// call the closed function with the proper types
closed_func(argc, argv.get());
}
PYBIND11_MODULE(HelloEposCmd, m)
{
m.def("run", &closed_func_wrap, "runs the HelloEposCmd");
}
Which after compiling can be used as expected:
$ python - a b c d=13
>>> import HelloEposCmd
>>> import sys
>>> HelloEposCmd.run(sys.argv)
FROM C++: -
FROM C++: a
FROM C++: b
FROM C++: c
FROM C++: d=13
>>>

How to Loop Through a const char**?

I have a const char** called glfwNames which holds the C version of a string array of the required GLFW library extensions. Would it be possible to loop through either the const char* (string), or the individual characters of the string separated by '\0'?
const char** glfwNames = glfwGetRequiredInstanceExtensions(&glfwCount)
for (const char** name = glfwNames; *name; ++name)
{
slog("GLFW Extensions to use: %s", *name);
}
This is what I've attempted from one of the answers, and the return value of
glfwGetRequiredInstanceExtensions
is an array of extension names, required by GLFW http://www.glfw.org/docs/latest/group__vulkan.html#ga1abcbe61033958f22f63ef82008874b1
If glfwNames is nullptr-terminated:
#include <cstdio>
int main()
{
char const *glfwNames[] = { "foo", "bar", "baz", nullptr };
for (char const **p = glfwNames; *p; ++p)
std::puts(*p);
}
If you *know* the number of strings:
std::uint32_t glfwCount;
const char** glfwNames = glfwGetRequiredInstanceExtensions(&glfwCount)
for (std::uint32_t i{}; i < glfwCount; ++i)
{
slog("GLFW Extensions to use: %s", glfwNames[i]);
}
To also loop through the individual chars:
for (std::uint32_t i{}; i < glfwCount; ++i)
{
for(char const *p{ glfwNames[i] }; *p; ++p)
std::putchar(*p);
}
A common pattern that I use to loop through the arguments to main is via std::for_each:
#include <algorithm>
int main(int argc, char* argv[]) {
std::for_each( argv + 1, argv + argc, handler );
}
where handler is any function taking a const char*, const std::string&, or std::string_view (I use the later).
Would a similar approach work for your problem? Notice that this approach requires you to know the length of your array of strings.
As a side note, it is important to know that the return argument of std::for_each is the function provided (handler in this case). That enables the suggested pattern to make a last call once the input is known to have been exhausted:
#include <algorithm>
int main(int argc, char* argv[]) {
std::for_each( argv + 1, argv + argc, handler )("Argument To Last Call");
}
This can be used to implement state machines that receive the termination trigger at the end.

pass array of strings to a function which takes const char**

I have a function which takes an array of const char** as a parameter
void Foo(const char** bar);
I can pass an array of const char * to it
const char *bar[2];
bar[0] = "test";
bar[1] = "me";
Foo(bar); // works fine
I want to do the same when 'bar' is std::string array instead of const char *
std::string bar[2];
bar[0] = "test";
bar[1] = "me";
Foo(bar); // cannot convert argument 1 from 'std::string [1]' to 'const char **'
I know the way convert std::string to const char *. Is there any way I can do it in the above case
Not sure what you're trying to achieve exactly but here
// Example program
#include <iostream>
#include <string>
void Foo(const char** bar, int num) {
while(num > 0) {
std::cout << bar[--num] << std::endl << std::flush;
}
}
const char** toCharArray(std::string* arr, int num) {
// If we ever alloc with new with have to delete
const char** buffer = new const char*[num];
for(int i = 0; i < num; i++) {
buffer[i] = arr[i].c_str();
}
return buffer;
}
int main()
{
std::string bar[2];
bar[0] = "test";
bar[1] = "me";
// Capture the result
const char** charBar = toCharArray(bar, 2);
Foo(charBar, 2);
// So we can free it later
delete[] charBar;
}
Arrays in memory do not have a length or size member like other languages so we pass in the size via function arguments. Second since we want to pass in an array of strings and get an array of chars out of it, we'll need to construct another array dynamically. Only way to do this is to use new. This stores the chars on the heap instead of the stack so when the function toCharArray finishes, the data will live on. So we store the result in charBar so that we can delete[] the array later.
you cannot do it directly
auto arr = std::vector<const char*>();
auto s1 = std::string("test");
auto s2 = std::string("me");
arr.push_back(s1.c_str());
arr.push_back(s2.c_str());
Foo(arr.data());

How to convert string into char * array

I have changed my code, now while the compilation these errors occur:
`check.cpp: In function ‘int main()’:`
check.cpp:14:55: error: invalid conversion from ‘const char**’ to ‘char* const*’ [-fpermissive]
/usr/include/getopt.h:152:12: error: initializing argument 2 of ‘int getopt(int, char* const*, const char*)’ [-fpermissive]
int main() {
string text="-f input.gmn -output.jpg";
int argc=text.length();
cout<<"argc: "<<argc<<endl;
char const * argv = text.c_str();
cout<<"argv: "<<argv<<endl;
int c = getopt (argc, &argv, "f:s:o:pw:h:z:t:d:a:b:?");
return 0;
}
You can use text.c_str() to convert a std::string into a const char*. See here.
To elaborate on my answer, there are many ways to create the array you need, but this is already described here, here, here and here. A simple solution to your problem that does not involve new/malloc or intensive uses of the STL and istringstream/back_inserter/copy what not and performs really fast could look like this:
/* variables. */
std::vector< char* > argv;
int i, argc, state;
char c;
/* convert string to char string with automatic garbage collection. */
std::vector< char > tokens(text.begin(), text.end());
tokens.push_back(0);
/* tokenize string with space. */
for (state=0, argc=0, i=0; (c=tokens[i]); i++) {
if (state) {
if (c == ' ') {
tokens[i]=0;
state=0;
}
} else {
if (c != ' ') {
argv.push_back(&tokens[i]);
argc++;
state=1;
}
}
}
/* print argv. */
std::cout << "argc: " << argc << std::endl;
for (i=0; i < argc; i++) {
std::cout << "argv[" << i << "]: " << argv[i] << std::endl;
}
/* call getopt. */
c = getopt(argc, &argv[0], "f:s:o:pw:h:z:t:d:a:b:?");
This is just an example, but one advantage of this kind of code is that you can use other characters as delimiter, not just space, and that you need not care about releasing the allocated memory since std::vector does this for you on function exit.
In short, you have an array argv which contains 100 pointers to strings, of which only the first is set. argv[1] hasn't been set to anything, so is pointing somewhere random. And in this case, illegal.
Moreoever, what getoption expects is going to be more like this:
argv[0] = "progname";
argv[1] = "-f";
argv[2] = "input.gmn"
argv[3] = "-output.jpg"
argv[4] = 0
Note the =0 at the end to stop getoption chargins through random bits of memory
First bug with your code is the comparison:
for (int i=0; i<=stringLength; i++) {
arv[i]=text[i];
}
Use i< stringLength instead of i<=stringLength.
The second bug is that arv is not null-terminated.
After fixing both bugs, your code should look like this:
for (int i=0; i < stringLength; i++) {
arv[i]=text[i];
}
arv[stringLength] = '\0';
By the way, the correct function signature of getopt is this:
int getopt(int argc, char * const argv[], const char *optstring);
which takes second and third argument as const. That means, you can do this:
char const * s = text.c_str();
int c = getopt (argc, &s, "f:s:o:pw:h:z:t:d:a:b:?");
No need of any conversion, using manual loop.