3

I'm trying to write a function to parse command line arguments. This is the function declaration:

void parse(int, char const **);

Just in case, I have also tried (const char)**, const char **, and cchar ** using a typedef const char cchar. However, all of these (as expected, since they should all be identical) result in an error if I pass a char ** into the function, as in:

void main(int argc, char **argv) {
    parse(argc, argv);

The error I get from GNU's compiler is error: invalid conversion from 'char**' to 'const char**' and the one from Clang is candidate function not viable: no known conversion from 'char **' to 'const char **' for 2nd argument.

I have seen such solutions suggested as declaring a pointer to a const pointer to char (const char * const *), but I don't want either pointer to be const because I want to be able to modify the pointer so I can iterate over an argument using for(; **argv; ++*argv). How can I declare a "non-const pointer to non-const pointer to const char"?

autistic
  • 1
  • 3
  • 35
  • 80
Dead Beetle
  • 111
  • 1
  • 5
  • 7
    C is not the same as C++; you need to choose a language. And you could try things like `char*const*`; however `main` wants to be declared `int main(int argc, char**argv);` – Basile Starynkevitch May 29 '17 at 00:53
  • I was under the impression that all clean (no warnings) C code was valid C++ code. I'm writing this for a C++ program and using a C++ compiler, but shouldn't a C solution work just as well? – Dead Beetle May 29 '17 at 00:57
  • 2
    In your case only data is `const`. The pointer is still mutable. You can cast it to `const` using `parse(argc, const_cast(argv));`. – Henri Menke May 29 '17 at 00:59
  • 2
    [Implicit conversion from char** to const char**](https://stackoverflow.com/questions/7016098/implicit-conversion-from-char-to-const-char) – BLUEPIXY May 29 '17 at 01:02
  • 5
    "I was under the impression that all clean (no warnings) C code was valid C++ code."-- Well, no. For example, there is no need to cast the result of `malloc()` in C, but this would not compile in C++. – ad absurdum May 29 '17 at 01:03
  • 1
    [Why C doesn't allow implicit conversion from char ** to const char *const *](https://stackoverflow.com/q/35319842/841108) – Basile Starynkevitch May 29 '17 at 01:03
  • 3
    ^ also, the same code can be correct in C and correct in C++ but do different things in each. (Associativity of the conditional operator is one example that comes to mind) – M.M May 29 '17 at 01:12
  • 1
    "I was under the impression that all clean (no warnings) C code was valid C++ code" - That's very wrong. Identical syntax does not mean the semantics are the same!. Try `static const int i = 10; static int a[i];`! Or `auto i = 10;` in a block! – too honest for this site May 29 '17 at 01:45
  • 2
    Or try out a variable-length array on your C++ compiler. Standard C and standard C++ have a common ancestor in K&R C, and they have a common subset large enough to program in, but each has features the other lacks. As people who come around here seem never to get tired of hearing, the two are separate languages. – John Bollinger May 29 '17 at 02:16
  • *"I was under the impression that all clean (no warnings) C code was valid C++ code."* Explain `int main(void) { char *fubar = (void *)0; }`... The languages differ in numerous ways! Either you *can use* a C compiler to compile your C code, which means you probably want the `[c]` tag, or you *need* a C++ compiler to compile your C++ code, which means you probably want the `[c++]` tag. **Don't try to restrict yourself to a common subset; that'd be like willfully sleeping on the footpath of a dark, vomit-soaked alley between a free hotel and a homeless shelter.** It's just unnecessary and silly. – autistic May 29 '17 at 03:47
  • Following on from that, you almost certainly should *never* use *both* tags! That's actually what we call "tag spamming" here; it's a nuisance because those who have expertise in C might not have expertise in C++ and vice-versa, so you almost certainly won't get a *single* answer that you can *reasonably* accept. The answer you've accepted is `[c++]`, as are the error messages. I'm removing the `[c]` tag. – autistic May 29 '17 at 03:50

2 Answers2

7

The function should be declared as:

void parse(int, char const * const *);

In C++, char ** can implicitly add const at all pointer depths, so you can call it as parse(argc, argv).

In C, const can only be added at the first pointer depth (this is a design defect in the language). Here is a dedicated thread. So you have to call the function as: parse(argc, (char const * const *)argv); unfortunately.

M.M
  • 138,810
  • 21
  • 208
  • 365
5

The safest signature that prevents modification of the arguments whilst allowing any other const combination to call the function is this:

parse(int argc, char const* const* argv);

That means that argv is a pointer to a const pointer to a const char

You can happily iterate over the parameters like this:

for(auto arg = argv + 1; *arg; ++arg)
{
    if(!std::strcmp(*arg, "--help"))
        return print_help();
    else if(!std::strcmp(*arg, "-v") || !std::strcmp(*arg, "--verbose"))
        verbose_flag = true;
    // ... etc...
}

Notice there is no need to accept the variable int argc because the array of character arrays is null terminated.

So I normally use this:

struct config
{
    // program options and switches
};

config parse_commandline(char const* const* argv);
Galik
  • 47,303
  • 4
  • 80
  • 117