4

I would like to try out c++11 range-based for loop on char* argv[] but I am getting errors. By current approach is :

for( char* c : argv )
{                                                                                               
   printf("param: %s \n", c);                                                                    
}

and in my makefile I have the following line :

g++ -c -g -std=c++11 -O2 file.cc
Patryk
  • 22,602
  • 44
  • 128
  • 244
  • 1
    Possible duplicate of [How to Write the Range-based For-Loop With Argv?](http://stackoverflow.com/questions/8572991/how-to-write-the-range-based-for-loop-with-argv) – Aconcagua Jul 13 '16 at 12:03

2 Answers2

8

argv is an array of pointers to raw strings, you can't obtain a range from it directly.


With C++17 you can use std::string_view to avoid allocating strings:

for (auto && str : std::vector<std::string_view> { argv, argv + argc })
{
    std::printf("%s\n", str.data()); // Be careful!
    std::cout << str << std::endl;   // Always fine
    fmt::print("{}\n", str);         // <3
}

Take caution when using string_view with printf because:

Unlike std::basic_string::data() and string literals, data() may return a pointer to a buffer that is not null-terminated.

argv always contains null-terminated strings so you're fine here though.


Without C++17 you can simply use std::string:

for (auto && str : std::vector<std::string> { argv, argv + argc })
    std::printf("param: %s\n", str.c_str());

Starting from C++20 you can use std::span:

for (auto && str : std::span(argv, argc))
    std::printf("param: %s\n", str);
Chnossos
  • 9,971
  • 4
  • 28
  • 40
  • This works, but: isn't that an overhead to create the vector? and secondly why can't I use `printf("param: %s\n", str);` (I am getting : `cannot pass objects of non-trivially-copyable type 'const class std::basic_string' through '...')` ? – Patryk Jul 31 '14 at 13:41
  • 1
    To get a `const char *` from a `std::string`, use the [`std::string::c_str()`](http://en.cppreference.com/w/cpp/string/basic_string/c_str) member function : `printf("param: %s\n", str.c_str());`. – Chnossos Jul 31 '14 at 13:42
  • It works but :) in my vim that is linting each c file I get the following on this line :`request for member ‘c_str’ in ‘str’, which is of non-class type ‘const int’`. Also in line with `for` I get : `ISO C++ forbids declaration of ‘str’ with no type [-fpermissive]` – Patryk Jul 31 '14 at 13:46
  • The above code compiles and runs fine with your invocation `g++ -c -g -std=c++11 -O2 test.cpp` so you're running into some unrelated problems here. – Chnossos Jul 31 '14 at 13:52
  • I have change auto to `std:string` and I got rid of them:) – Patryk Jul 31 '14 at 13:54
  • Which g++ version are you running ? – Chnossos Jul 31 '14 at 13:54
  • I use 4.8.2 on Ubuntu 14.04 (64bit) – Patryk Jul 31 '14 at 13:56
  • Strange, it should had resolve `auto` correctly ... Anyway, if my answer helped you solve your problem, you're invited to accept it to close this question. – Chnossos Jul 31 '14 at 18:29
2

You can't use the range-based loop since you don't have a range.

You can either write your own range wrapper (a kind of "array view"), or just use a normal loop:

for (char ** p = argv, e = argv + argc; p != e; ++p)
{
    // use *p
}

With a wrapper:

#include <cstddef>

template <typename T>
struct array_view
{
    T * first, * last;
    array_view(T * a, std::size_t n) : first(a), last(a + n) {}
    T * begin() const { return first; }
    T * end() const { return last; }
};

template <typename T>
array_view<T> view_array(T * a, std::size_t n)
{
    return array_view<T>(a, n);
}

Usage:

for (auto s : view_array(argv, argc))
{
    std::cout << s << "\n";
}
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084