passing a char ** to a function(const char **) in C++ - c++

I am trying to use the function rrd_update_r of the round robin database.
int rrd_update_r(const char *filename,const char *_template,
int argc,const char **argv);
The function accepts the as 3rd and 4th argument the well known argc, argv.
Even though I am using C++ (and g++) for this project, rrd is written in C and consequently I could use the function wordexp(char *, wordexp_t*) provided in GNY/Linux to split the arguments of a string into an argv array.
The problem is that wordexp_t returns a member of char ** type (as argv), which is incompatible apparently with the rrd_update_r function call.
/usr/include/rrd.h:238:15: error: initializing argument 4 of ‘int rrd_update_r(const char*, const char*, int, const char**)’ [-fpermissive]
To my surprise I could find no help on the matter either. This Why can't I convert 'char**' to a 'const char* const*' in C? solution did not work.
So I am left wondering: how can I pass the char ** into const char ** ?
The full function is
#include <errno.h> // Error number definitions
#include <rrd.h>
#include <wordexp.h>
void splitToArgs(string& parametersString) //parametersString contains space separated words (parameters).
{
wordexp_t we;
int er = 0;
if ( (er=wordexp(parametersString.c_str() , &we, 0)) != 0)
{
cout << "error in word expansion " << er << endl;
}
else
{
if (we.we_wordc>0)
{
char * filename = we.we_wordv[1]; //filename is part of the parameters string
rrd_clear_error();
int ret = rrd_update_r( filename , NULL , we.we_wordc, we.we_wordv );
if ( ret != 0 )
{
cout << "rrd_update error # = " << ret << " error string = " << rrd_get_error() ;
}
}
}
wordfree(&we);
}
This use of const_cast (if correct) also does not work
error: invalid conversion from ‘char**’ to ‘const char**’ [-fpermissive]
const char **w = const_cast<char**>(we.we_wordv);

const_cast<const char**>(whatever)
is the correct way to const_cast in this case.

Im going to give you an example passing a char to a enum ( which is in fact a constant )
enum TIngrediente
{
TOMATE,
QUESO,
NATA,
CEBOLLA,
POLLO,
HUEVO,
SALAMI,
ANCHOA,
BACON,
GAMBA
};
string tolower(string s)
{
string r = s;
for (int i = 0; i < s.size(); ++i)
r[i] = tolower(r[i]);
return r;
}
TIngrediente StrToIngrediente(string s)
{
s=tolower(s);
int i;
while (i < INGREDIENTES.size() and INGREDIENTES[i] != s)
++i;
return (TIngrediente)i;
}
Now the only thing you need to do is make another function changing again from const char to char ( very easy)
This way youll trans form the char constant to a const char, be ware youll need to add a library, cctype and local to make this work.

Related

Why I cant use atoi() function on a "string" instead of the char pointer in C++?

I tried the following code and it is giving me error.
int main() {
string String = "1235";
int num = atoi(String);
cout << num << endl;
return 0;
}
/*
error: cannot convert 'std::__cxx11::string {aka std::__cxx11::basic_string<char>}' to 'const char*' for argument '1' to 'int atoi(const char*)'
int num = atoi(String);
^
mingw32-make.exe[1]: *** [Debug/main.cpp.o] Error 1
mingw32-make.exe: *** [All] Error 2
*/
But if I use the following code it works perfectly fine.
int main() {
char* String = "1235";
int num = atoi(String);
cout << num << endl;
return 0;
}
//prints out 1235
I know I can solve my problem using stoi() function.
int main() {
string String = "1235";
int num = stoi(String);
cout << num << endl;
return 0;
}
//prints out 1235
I can solve my problem by using a char pointer instead of string. But I just want to know why this can't be done by placing string itself into atoi(). How does atoi() work internally?
I just wanna know how does atoi() function work in C++
Because const char* and std::string are incompatible, the implicit conversion
cause error.
If you still want to use std:string:
int main() {
string String = "1235";
int num = atoi(String.c_str());
cout << num << endl;
return 0;
}
see this ref.
While std::stoi accepts std::string as input, ::atoi does not.
Note: std::string is a c++ class type, const char* is a basic data type. Although std::string does have a member function .c_str(), which can return its C-Style representation with const char* type.
Protype declarations of std::stoi in <string>:
int stoi( const std::string& str, std::size_t* pos = nullptr, int base = 10 );
int stoi( const std::wstring& str, std::size_t* pos = nullptr, int base = 10);
Protype declaration of ::atoi in <stdlib.h>:
int atoi (const char *__nptr);

Inserting double into char* array element

How do I insert a double data type into a char* array? I've tried using std::to_string() as well as c_str() however neither seem to be doing the trick.
Essentially I am trying to call a c++ program using execvp() which from reading the man page I think it needs to take in a char* array.
I am trying to call the program, then pass the doubles a, b, and c as the argv parameters for the program execvp() is calling.
E.G. when a = 0.1, b =0.1, c = 0.1 execvp(args[0], args) should do the same as the terminal call: ./RL 0.1 0.1 0.1
#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <string>
int main()
{
char *args[5];
args[0] = "RL";
std::string temp;
for(double a = 0.1; a < 1; a += 0.1){
for(double b = 0.1; b < 1; b += 0.1){
for(double c = 0.1; c < 1; c+= 0.1){
temp = std::to_string(a); //attempted to convert the double to a string
args[1] = temp.c_str(); //attempted to convert the string to c string
temp = std::to_string(b);
args[2] = temp; //attempted to just insert the string
args[3] = c; //attempted to insert as double
std::cout << "Calling: " << a << " " << b << " " << c << std::endl;
execvp(args[0], args);
}
}
}
return 0;
}
I get errors like this:
RLdriver.cpp:14:32: error: assigning to 'char *' from incompatible type 'const
std::__1::basic_string<char, std::__1::char_traits<char>,
std::__1::allocator<char> >::value_type *' (aka 'const char *')
args[1] = temp.c_str();
~~~~~^~~~~~~
RLdriver.cpp:16:27: error: assigning to 'char *' from incompatible type
'std::string' (aka 'basic_string<char, char_traits<char>, allocator<char>
>')
args[2] = temp;
^~~~
RLdriver.cpp:17:27: error: assigning to 'char *' from incompatible type 'double'
args[3] = c;
EDIT: I'm aware exec() would not really work in this sense of looping as it replaces the current program, but I know how to deal with that later on, just trying to get the passing of args working.
There are two problems with this code:
you cannot fill your "array of char pointers" args with temporary objects as you try, this will not reflect what you need. Use instead (see below) const char* const args[] and fill it with pointers to real object data tempa, tempb, tempc.
execvp expects char* const[] while in C++ you would always produce const char* const[], so you need to cast something like execvp(const_cast<char* const*>(args));
You may change your code to, then it will work:
#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <string>
int main()
{
for(double a = 0.1; a < 1; a += 0.1){
for(double b = 0.1; b < 1; b += 0.1){
for(double c = 0.1; c < 1; c+= 0.1){
std::string tempa = std::to_string(a);
std::string tempb = std::to_string(b);
std::string tempc = std::to_string(c);
const char* const args[] = {tempa.c_str(), tempb.c_str(), tempc.c_str(), nullptr};
std::cout << "Calling: " << a << " " << b << " " << c << std::endl;
execvp(args[0], const_cast<char*const*>(args));
}
}
}
return 0;
}
(tested)
Use of
args[1] = temp.c_str();
is a problems since the LHS is of type char* and the RHS is of type char const*. You can't assign a char const* to a char*.
A simple fix is to use:
args[1] = strdup(temp.c_str());
strdup is not a standard library function but some compilers support it natively. If your compiler does not support it, it's not too hard to implement it yourself. Lookup the on the web and you fill most likely find one.
Make sure that you deallocate the string returned by strdup. You'll have to consult your compiler's documentation to figure out how memory was allocated in strdup. You'll need to use the appropriate deallocation mechanism.
Your first way, i.e.
temp = std::to_string(a); //attempted to convert the double to a string
args[1] = temp.c_str(); //attempted to convert the string to c string
is correct, except that you need to change the declaration char *args[5]; to const char *args[5];. Of course, if you want to pass 3 different command line arguments, you need 3 different temp variables (temp1, temp2, temp3).

Having problems with cstring arrays

I am having trouble with my code with something with arrays. I am getting the following errors
In function ‘int main(int, const char**)’:
75: error: cannot convert ‘char*’ to ‘char (*)[81]’ for argument ‘1’ ion(char (*)[81], OneItem*, int&, int&)’
In function ‘void parseInformation(char (*)[81], OneItem*, int&, in
164: error: ISO C++ forbids comparison between pointer and integer
166: error: incompatible types in assignment of ‘const char [2]’ to
169: error: ISO C++ forbids comparison between pointer and integer
174: error: ISO C++ forbids comparison between pointer and integer
174: error: ISO C++ forbids comparison between pointer and integer
176: error: invalid conversion from ‘char*’ to ‘char’
The code doesn't line up with the line numbers. I have tried multiple things googled some things still haven't found a solution.
const int MAX_CHARACTERS = 80;
const int MAX_INVENTORY = 12;
typedef char OneLine[MAX_CHARACTERS + 1];
struct OneItem
{
char product[MAX_CHARACTERS + 1];
int quantity;
float unitPrice;
float totalPrice;
};
int main( const int argc, const char* argv[] )
{
OneLine fileName;
ifstream inFile;
OneLine readLine;
OneItem inventory[MAX_INVENTORY];
int readLineIndex;
int structureCounter = 0;
int averageQuantity;
float averagePrice;
float averageTotalPrice;
displayIntroduction();
getFileName( argc, argv, fileName );
if (!inFile)
{
cout << "File not found: " << fileName << endl;
}
else
{
inFile.open(fileName);
while(!inFile.getline(readLine, MAX_CHARACTERS, '\n').eof())
{
if (structureCounter < MAX_INVENTORY)
{
parseInformation(readLine,inventory, readLineIndex, structureCounter);
}
}
void parseInformation(OneLine readLine[],OneItem inventory[], int & readLineIndex, int & structureCounter)
{
int tempIndex = 0;
int valueCounter = 0;
OneLine tempArray;
while(readLine[readLineIndex] != '\n')
{
tempArray = "\0";
while(readLine[readLineIndex] == ' ')
{
readLineIndex += 1;
}
while(readLine[readLineIndex] != ' ' && readLine[readLineIndex] != '\n')
{
tempArray[tempIndex] = readLine[readLineIndex];
tempIndex += 1;
readLineIndex += 1;
}
if(valueCounter == 0)
{
for(int i = 0; i <= strlen(tempArray); i++)
{
inventory[structureCounter].product[i] = tempArray[i];
}
valueCounter += 1;
}
else if(valueCounter == 1)
{
inventory[structureCounter].quantity = atoi(tempArray);
valueCounter += 1;
}
else
{
inventory[structureCounter].unitPrice = atof(tempArray);
structureCounter += 1;
}
}
return;
Your definition of parseInformation is wrong. It says that readLine should be an array of OneLine, but it only wants a single OneLine. It should be:
void parseInformation(OneLine readLine,OneItem inventory[], int & readLineIndex, int & structureCounter)
This is causing all the errors you're getting, because you're calling the function with a single OneLine, not an array. And inside the function you're comparing readLine[readLineIndex] with a character, which requires readLine to be an array of characters, not an array of OneLine.
The OneLine typedef makes this an array already, you didn't need to add [] to the parameter declaration.
As Barmar said you need to change first argument, but there is one more problem, here:
OneLine tempArray;
while(readLine[readLineIndex] != '\n')
{
tempArray = "\0";
You cannot make such assign. tempArray needs to be of type const char* or you need to make it this way:
tempArray[some_index] = '\0';

c++ returning and using char array pointers

Here is my code:
#include<stdio.h>
#define MAXLINE 100
/*print the reverse of the input*/
int getline1(char line[], int maxline);
char *reverse(char);
main(){
int len;
char line[MAXLINE];
char *rp;
while ((len = getline1(line, MAXLINE)) > 0)
rp = reverse(line);
printf("%s", *rp);
return 0;
}
int getline1(char s[], int lim){
int c, i;
for (i = 0; (c=getchar()) != EOF && c != '\n'; i++)
if (i > lim-1)
continue;
else
s[i] = c;
if (c == '\n'){
s[i] = c;
i++;
}
s[i] = '\0';
return i;
}
char *reverse(char ca[]){
int i;
int i1 = 0;
char *rp;
char reversed[MAXLINE];
for (i = MAXLINE-1; i >= 0; i--){
reversed[i1] = ca[i];
i1++;
}
rp = reversed;
return rp;
}
But when I try to compile it, I get the following errors:
reverse.cpp: In function ‘int main()’:
reverse.cpp:14:20: error: invalid conversion from ‘char*’ to ‘char’ [-fpermissive]
reverse.cpp:7:7: error: initializing argument 1 of ‘char* reverse(char)’ [-fpermissive]
reverse.cpp:15:19: warning: format ‘%s’ expects argument of type ‘char*’, but argument 2 has type ‘int’ [-Wformat]
I don't have much experience with C++. What am I doing wrong? I just want to make a pointer to a char array and return it.
I just want to make a pointer to a char array and return it.
You appear to want to return a string. That is not a pointer to a char array. Even if your program compiled, you would invoke UB, as you return a pointer to an automatic object- and there are quite a few other runtime errors in your code as well. You got lucky that you also made a compile-time error so the compiler did not accept your program. This C++ program achieves what you intend:
#include <string>
#include <iostream>
std::string reverse(std::string val) {
return std::string(val.rbegin(), val.rend());
}
int main() {
std::string str;
while(std::getline(std::cout, str))
std::cout << reverse(str);
}
What am I doing wrong?
You're learning C89 intead of C++11. They're really different things.
If you wish to learn to code C++, you must learn std::string, and the rest of the Standard library. You will not get anywhere with char*, char[], and MAGIC_BUFFER_SIZE.
You first declare the function prototype
char *reverse(char);
But the actual function is declared as
char *reverse(char ca[])
That's your problem.
What are you trying to achieve ? There are logical errors in the code ...
while ((len = getline1(line, MAXLINE)) > 0)
rp = reverse(line);
printf("%s", *rp);
this part will call reverse on every /n character but printf will never be called...
Also you have string of 100 chars and your reverse will put leading char on end of reverse string.. so if you have string of 5 chars you will have garbage on first 95 positions and then 5 chars you need ...

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.