an advanced declaration of a function? [duplicate] - c++

For reading complex pointer declarations there is the right-left rule.
But this rule does not mention how to read const modifiers.
For example in a simple pointer declaration, const can be applied in several ways:
char *buffer; // non-const pointer to non-const memory
const char *buffer; // non-const pointer to const memory
char const *buffer; // equivalent to previous declartion
char * const buffer = {0}; // const pointer to non-const memory
char * buffer const = {0}; // error
const char * const buffer = {0}; // const pointer to const memory
Now what about the use of const with a pointer of pointer declaration?
char **x; // no const;
const char **x;
char * const *x;
char * * const x;
const char * const * x;
const char * * const x;
const char * const * const x;
And what is an easy rule to read those declarations?
Which declarations make sense?
Is the Clockwise/Spiral Rule applicable?
Two real world examples
The method ASTUnit::LoadFromCommandLine uses const char ** to supply command line arguments (in the llvm clang source).
The argument vector parameter of getopt() is declared like this:
int getopt(int argc, char * const argv[], const char *optstring);
Where char * const argv[] is equivalent to char * const * argv in that context.
Since both functions use the same concept (a vector of pointers to strings to supply the arguments) and the declarations differ - the obvious questions are: Why do they differ? Makes one more sense than the other?
The intend should be: The const modifier should specify that the function does not manipulate strings of this vector and does not change the structure of the vector.

The const modifier is trivial: it modifies what precedes it, unless
nothing precedes it. So:
char const* buffer; // const modifies char
char* const buffer; // const modifies *
, etc. Generally, It's best to avoid the forms where nothing precedes
the const, but in practice, you're going to see them, so you have to
remember that when no type precedes the const, you have to logically
move it behind the first type. So:
const char** buffer;
is in fact:
char const** buffer;
, i.e. pointer to pointer to const char.
Finally, in a function declaration, a [] after reads as a * before.
(Again, it's probably better to avoid this misleading notation, but
you're going to see it, so you have to deal with it.) So:
char * const argv[], // As function argument
is:
char *const * argv,
a pointer to a const pointer to a char.

(Trying to focus on other aspects of the question)
The rule of thumb for const declarations is to read them from right to left and const modifies the next token. Exception: At the beginning of a declaration const modifies the previous token.
There is a rationale behind this exception - for elementary declarations const char c looks for some people more natural than char const c - and it is reported that a precursor form of const char c predates the final const rule.
getopt
int getopt(int argc, char * const argv[], const char *optstring);
or
int getopt(int argc, char * const * argv, const char *optstring);
Which means that argv is a pointer to const vector of pointers to non-const strings.
But one would expect following declaration:
int getopt(int argc, char const * const * argv, const char *optstring);
(pointer to const vector to const strings)
Because getopt() is not supposed to change the strings referenced via argv.
At least char ** (as used in main()) automatically converts to char * const * argv.
Clang
ASTUnit::LoadFromCommandLine(..., const char **argv, ...);
Which means that argv is a pointer to a non-const array of pointers to const strings.
Again one would expect const char * const *argv for the same reason as above.
But this is more noticeable because char ** does not convert to const char **, e.g.
int main(int argc, char **argv) {
const char **x = argv; // Compile error!
return 0;
}
yields a compile error, where
int main(int argc, char **argv) {
char * const *x = argv;
return 0;
}
and
int main(int argc, char **argv) {
const char * const *x = argv;
return 0;
}
do not.

Related

How to declare a variable as char* const*?

I have a bluez header file get_opt.h where argv is an argument:
extern int getopt_long (int ___argc, char *__getopt_argv_const *___argv,.....
which requires char* const* for argv. Because the unit is called from a different file I was trying to emulate argv but whatever permutation of declaring char, const and * is used I get unqualified-id or the compiler moans about not converting to char* const*. I can get const char * easily, of course. char const * argv = "0"; compiles OK in Netbeans with C++14 but
char * const * argv = "0";
produces
"error: cannot convert 'const char *' to 'char * const *'" in initialisation (sic).
How do you declare char* const* or am I mixing C with C++ so I'm up the wrong tree?
Let's review the pointer declarations:
char * -- mutable pointer to mutable data.
char const * -- mutable pointer to constant data.
char * const -- constant pointer to mutable data.
char const * const -- constant pointer to constant data.
Read pointer declarations from right to left.
Looking at the descriptions, which kind of pointer do you want?
Because void main(void) is in a different file I was trying to emulate
argv
void main(void) is not the correct main function declaration. The main function has to have int return value.
Simply change the void main(void) to int main(int argc, char **argv) in that "other" file.
It does not require char const * to be passed to. Do not change the type of the main function arguments.
int getopt_long (int ___argc, char * const *___argv);
int main(int argc, char **argv)
{
printf("%d\n", getopt_long(argc, argv));
}
Both language compile fine:
https://godbolt.org/z/5To5T931j
Credit #JaMiT above
[I don't know how to accept a commment]
Also, you might want to notice how you can remove the call to getopt_long() from that example while retaining the error (it's motivation for your declaration, but not the source of your issue). If you think about your presentation enough, you might realize that your question is not how to declare a char* const* but how to initialize it. Due to arrays decaying to a pointer, you could declare an array of pointers (char* const argv[]) instead of a pointer to pointer (char* const * argv). See The 1D array of pointers way portion of A: 2D Pointer initialization for how to initialize such a beast. Beware: given your setup, the "2D array way" from that answer is not an option. Reminder: The last pointer in the argument vector is null; argv[argc] == nullptr when those values are provided by the system to main().

Why can I create a vector of char * from char * const values?

I don't understand why vc++ and g++ let me initialize a vector< char * > from char * const values.
#include <iostream>
#include <vector>
using namespace std;
typedef char * str;
typedef char * const cstr;
int main(int argc, const cstr argv[])
{
const vector<str> arguments(argv + 1, argv + argc);
for (str arg : arguments)
{
cout << arg << endl;
}
return 0;
}
This is a pointer to a mutable char:
typedef char * str;
This is a constant pointer to a mutable char. The pointer can't change but the pointed char can be modified:
typedef char * const cstr;
There is an easy way to read pointer and const declarations: read it from right to left:
T * const x; // x is a constant that points to an object of type T
T const * x; // x points to a constant object of type T.
This argv is a pointer (a decayed array) to a constant cstr. Eventually, this is a pointer to a constant (two consts treated as one) pointer to char:
int main(int argc, const cstr argv[]
Construct a vector of str (of pointers to char), out of a collection of constant pointers to char. We don't care that the pointers themselves are constants, since the code does not modified the pointers, only copies them:
const vector<str> arguments(argv + 1, argv + argc)
No problem here.
Note that writing const on the left of the type is often called "west const" or, somewhat sarcastically, const west. In contrast, writing const on the right is often called east const. Semantically, east const and west const are equivalent.
The argv in the question uses the west const notation, since const is on the left of cstr. Had you used east const all the time, then it would have looked:
int main(int argc, cstr const argv[]
Arguably, this way of writing making it clearer that const applies to the variable argv, and not to the pointed value.

Create own string using const char * vs char *

I want to create my own string storage (class) and I know something about const char * and char *.
This is a part of my source:
class str_n
{
private:
char * _str;
public:
str_n(const char * str)
{
std::size_t Read_len = strlen(str);
_str = (char *) malloc(Read_len + 1);
memcpy(_str, str, Read_len + 1);
}
};
I used char * and const char * for my _str and I know if I use const char * I need to copy it to char * whlie I want to use some other functions inside of my class and also using const char * is faster (very faster (about 2x)) than char * to save the const char * param content.
Now my question: If you want to create new string storage, you use const char * or char *?
Use char *, if your str_n is mutable. You're wrong on the part that const content is modifiable. For example, this:
const char *foo = "foo";
foo[0] = 'a';
won't compile. Trying to circumvent it with const_cast is not a viable option:
const_cast<char *>(foo)[0] = 'a';
as it is undefined behavior, and usually crashes, as foo is put in read-only memory.
So, if your string is mutable, you must copy the contents of str (the parameter for the constructor) in your example (you don't have the option of using const char * as storage, and omitting the copy to make your program faster -- I suppose you meant "2x faster" because you omitted the copy).
A note: you should use new instead of malloc, it is more c++-ish (you won't need to cast to char * the result of new char[...])
Just use std::string
class str_n {
public:
str_n(const char * str_in) : str{str_in} {}
private:
std::string str;
};
you can fetch the c string from a C++ string with the .c_str() method

How to declare a pointer to pointer to constant in C++?

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"?
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.
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);

Convert const std::vector<std::string> to const char **

I have a function with signature like this:
void foo(const std::vector<std::string>& args)
{
...
}
I want convert vector args to const char ** (just like argv in main). How this can be done? We can't actually make a char ** array because then it (obviously) fails to convert args[i].c_str() that is of type const char * to char *.
The only (ugly) way I can think of is to use const_cast to cast from const char * to char *.
Would anyone suggest more elegant way of doing this? I should note I can use only c++03 features.
Thanks in advance.
It can't be done without an extra array. A const char** means "a pointer to a pointer to const char" - so you need a place where there are actual pointers to char to point to.
So you need to create that array.
struct c_str { const char* operator ()(const std::string& s) { return s.c_str(); } };
std::vector<const char*> pointers(args.size());
std::transform(args.begin(), args.end(), pointers.begin(), c_str());
// If you really want to be compatible with argv:
pointers.push_back(0);
// &pointers[0] is what you want now