All:
Thanks for help.
I am new to C option parsing, for now, what I want is to use popt library to parsing the each argument and prnit them out.
#include <iostream>
#include <string>
#include <cstring>
#include <popt.h>
using namespace std;
int main(int argc, const char* argv[]){
char* dt1;
char* dt2;
struct poptOption {
const char * longName; /* may be NULL */
char shortName; /* may be ’\0’ */
int argInfo;
void * arg; /* depends on argInfo */
int val; /* 0 means don’t return, just update flag */
char * descrip; /* description for autohelp -- may be NULL */
char * argDescrip; /* argument description for autohelp */
};
struct poptOption optionsTable[]={
{"start",'s',POPT_ARG_STRING,dt1,'s',"The date format should like YYYY-MM-DD.",0},
{"end",'e',POPT_ARG_STRING,dt2,'e',"The date format should like YYYY-MM-DD.",0},
//~ POPT_AUTOHELP
//~ {NULL,0,0,NULL,0}
};
poptContext optCon;
optCon = poptGetContext (0, argc, argv, optionsTable, 0);
const char* portname = poptGetArg(optCon);
cout<<portname<<endl;
return 0;
}
When I compile it, I got error llike:
test.cpp: In function ‘int main(int, const char**)’
test.cpp:27: warning: deprecated conversion from string constant to ‘char*’
test.cpp:27: warning: deprecated conversion from string constant to ‘char*’
test.cpp:30: error: cannot convert ‘main(int, const char**)::poptOption*’ to ‘const poptOption*’ for argument ‘4’ to ‘poptContext_s* poptGetContext(const char*, int, const char**, const poptOption*, unsigned int)’
I don't think you should be defining the struct poptOption in your program. That struct should be defined for you in the popt include file. Try removing that struct definition.
Note, I think you also need to uncomment this line:
//~ {NULL,0,0,NULL,0}
The reason that this warning is being reported is a feature of the C language, but the mistake in the code is due to how you are attempting to use popt.
The types (char*) and (const char*) in C are different types. One is not really the other, and while C allows assignment from one type to another without blowing up, any decent C compiler will give you a warning. You can hide the warnings with a type cast, but it's generally a bad idea.
A C-style string is of the type (const char*), and you are assigning it to the field descrip in poptOption which is defined as a (char*). This raises a compiler warning because now, someone who reaches into that memory by following the reference from the optionsTable array could attempt to change the contents of the string. That's an odd thing to allow, considering the string is defined as a constant. That's why you get the warning.
The mistake in your code is that you are using popt incorrectly, with your own definition of the poptOption struct. If you look within the file that you include (popt.h) on line 55 you will see the poptOption struct, as it is defined by the popt authors. It is:
struct poptOption {
/*#observer#*/ /*#null#*/ const char * longName; /* may be NULL */
char shortName; /* may be '\0' */
int argInfo;
/*#shared#*/ /*#null#*/ void * arg; /* depends on argInfo */
int val; /* 0 means don't return, just update flag */
/*#shared#*/ /*#null#*/ const char * descrip; /* description for autohelp -- may be NULL */
/*#shared#*/ /*#null#*/ const char * argDescrip; /* argument description for autohelp */
};
or removing comments
struct poptOption {
const char * longName;
char shortName;
int argInfo;
void * arg;
int val;
const char * descrip;
const char * argDescrip;
};
and you clearly see that even the authors expected a (const char *), and not the (char *) you defined.
Related
Generally, I would like to implement a SerialLog class containing a collection of methods that format strings using a printf-style API and output the compiled message to the Arduino serial console using the Serial.print() method contained within the basic arduino library.
Such code would enable much cleaner serial console log invocations within my code (the below begins by showing the required nested function calls of the core Arduino c++ library, whereas the latter two calls show the API I would like to implement):
# normal debug logging for Arduino using the provided functions
Serial.log(sprintf("A log message format string: %d/%f", 123, 456.78));
# the call I would like to use for string formatting
String message = SerialLog.sprintf("A log message format string: %d/%f", 123, 456.78);
# the call I would like to use to output a formatted string
SerialLog.printf("A log message format string: %d/%f", 123, 456.78);
As can be seen in the above examples, my intention is to create a set of class methods for serial console output with arguments that mirror the normal C++ printf function.
I've attempted to implement such a printf-style API using simple variadic definitions like myVariadicPrintfFunction(const char * format, ...) but such a function definition appears to require that all arguments are of type const char *. This is not the behavior I want. As such, my current implementation uses templates to enable arguments of any type (obviously the type must be ultimately acceptable to the C++ core printf function, though).
My implementation includes the following public methods within a SerialLog class:
SerialLog::sprint (String sprint(const char * format)): Accepts a const char * argument. Returns the string as the Arduino String object.
SerialLog::sprintf (template <typename ...Args> String sprintf(const char * format, Args ...args)): Accepts a const char * argument as the format string and any number of additional arguments (of various types) which will be substituted within the format string. Returns the string as the Arduino String object.
SerialLog::print (SerialLog& print(const char * format)): Same as SerialLog::sprint to output the string to the serial console using Serial.print() instead of simply returning it.
SerialLog::printf (template <typename ...Args> SerialLog& printf(const char * format, Args ...args)): Uses the return value of SerialLog::sprintf to output the string to the serial console using Serial.print() instead of simply returning it.
As with the normal C++ printf function, both SerialLog::sprintf and SerialLog::printf must accept a format string as the first argument followed by any number of acceptable argument of any acceptable type which are used as the substitution values for the provided format string.
For example, a format of "This %s contains %d substituted %s such as this float: %d." with additional arguments of string (as a char *), 4 (as an int), "values" (as a char *), and 123.45 (as a float) would result in the following compiled string: "This string contains 4 substituted values such as this float: 123.45.".
I have been unable to achieve the described behavior using the following code:
debug.h
#include <stdio.h>
#include <Arduino.h>
namespace Debug
{
class SerialLog
{
public:
String sprint(const char * format);
template <typename ...Args>
String sprintf(const char * format, Args ...args);
SerialLog& print(const char * format);
template <typename ...Args>
SerialLog& printf(const char * format, Args ...args);
} /* END class SerialLog */
} /* END namespace Debug */
debug.cpp
#include <debug.h>
namespace Debug
{
String SerialLog::sprint(const char * format)
{
return String(format);
}
template <typename ...Args>
String SerialLog::sprintf(const char * format, Args ...args)
{
char buffer[256];
snprintf(buffer, 256, format, args...);
return String(buffer);
}
SerialLog& SerialLog::print(const char * format)
{
Serial.print(format);
return *this;
}
template <typename ...Args>
SerialLog& SerialLog::printf(const char * format, Args ...args)
{
Serial.print(this->sprintf(format, args...));
return *this;
}
} /* END namespace Debug */
At this time, the follow errors occur during compilation:
C:\Temp\ccz35B6U.ltrans0.ltrans.o: In function `setup':
c:\arduino-app/src/main.cpp:18: undefined reference to `String RT::Debug::SerialLog::sprintf<char const*>(char const*, char const*)'
c:\arduino-app/src/main.cpp:22: undefined reference to `RT::Debug::SerialLog& RT::Debug::SerialLog::printf<char const*>(char const*, char const*)'
c:\arduino-app/src/main.cpp:26: undefined reference to `RT::Debug::SerialLog& RT::Debug::SerialLog::printf<char const*>(char const*, char const*)'
c:\arduino-app/src/main.cpp:29: undefined reference to `RT::Debug::SerialLog& RT::Debug::SerialLog::printf<char const*>(char const*, char const*)'
c:\arduino-app/src/main.cpp:30: undefined reference to `RT::Debug::SerialLog& RT::Debug::SerialLog::printf<char const*, int, double>(char const*, char const*, int, double)'
collect2.exe: error: ld returned 1 exit status
*** [.pio\build\debug\firmware.elf] Error 1
Note: The above code is extracted from a larger Debug namespace and an expanded SerialLog class that contains additional methods, so the following error message line numbers will not correctly represent the example code shown.
The full VSCode build log (using the PlatformIO extension) can be located as a Gist at gist.github.com/robfrawley/7ccbdeffa064ee522a18512b77d7f6f9. Moreover, the entire project codebase can be referenced at github.com/src-run/raspetub-arduino-app, with the relevant projects for this question located at lib/Debug/Debug.h and lib/Debug/Debug.cpp.
Lastly, while I am proficient in many other languages like Python, PHP, Ruby, and others, this is the first C++ project! I am learning the C++ language through this application's implementation and am aware that many suboptimal choices exist within the codebase; different aspects of this application will be amended and improved as my knowledge of C++ evolves. As such, I am not particularly interested in comments regarding deficiencies in my implementation or verbose opinion pieces explaining the shortcomings in my understanding of C++. Please keep any discussion focused on the singular question outlined above.
Thanks for taking the time to read through this entire question and I greatly appreciate any assistance provided!
Not sure (without a full example it's difficult) but I suppose the problem is that you've declared only the template methods inside debug.h and you have defined them inside debug.cpp.
A general suggestion: in C++, ever declare and define template things (classes, functions, methods, variables) inside header files
The point is that, in this case, the compiler implement the specific template method when is needed. So if you write in main.cpp
char const * bar = "bar";
RT::Debug::SerialLog::printf("foo format: %s %i %lf", bar, 0, 1.1);
the compiler know that needs a RT::Debug::SerialLog::printf<char const*, int, double> but can't implement it because, in main.cpp, see only the content of debug.h, where the template method SerialLog::printf() is declared but not defined. So the compiler can't implement the char const *, int, double version of the method.
I suggest to change the files as follows
--- debug.h
#include <stdio.h>
#include <Arduino.h>
namespace Debug
{
class SerialLog
{
public:
String sprint(const char * format);
template <typename ...Args>
String sprintf(const char * format, Args ...args)
{
char buffer[256];
snprintf(buffer, 256, format, args...);
return String(buffer);
}
SerialLog& print(const char * format);
template <typename ...Args>
SerialLog& printf(const char * format, Args ...args)
{
Serial.print(this->sprintf(format, args...));
return *this;
}
} /* END class SerialLog */
} /* END namespace Debug */
--- debug.cpp
#include <debug.h>
namespace Debug
{
String SerialLog::sprint(const char * format)
{
return String(format);
}
SerialLog& SerialLog::print(const char * format)
{
Serial.print(format);
return *this;
}
} /* END namespace Debug */
---- end files
This way, if you write in main.cpp
RT::Debug::SerialLog::printf("foo format: %s %i %lf", bar, 0, 1.1);
the compiler know that needs a RT::Debug::SerialLog::printf<char const*, int, double> and can implement it because can see, from debug.h, the definition of SerialLog::printf().
In C, I can do this to have an unspecified number of arguments in a function:
#include <elf.h>
#include <stddef.h>
#include <stdlib.h>
extern char **__environ;
int __libc_start_main
(
int (*main)(),
int argc,
char **argv
)
{
int ret;
Elf32_auxv_t *auxv;
size_t aux[38];
/* ... */
exit(main(argc, argv, __environ, aux));
}
However, when doing this in C++, the compiler emits this error:
test.c: In function ‘int __libc_start_main(int (*)(), int, char**)’:
test.c:21:45: error: too many arguments to function
exit(main(argc, argv, __environ, aux));
^
How do I do this in C++?
I understand that the C/C++ standards don't allow this, but I'm currently writing an implementation of the standard C library.
The short answer is: You don't.
In C++ all functions have a prototype; there is no such thing as an "unspecified number of arguments".
If you want to call main as main(argc, argv, __environ, aux), you need to declare it as int (*main)(int, char **, char **, void *) or similar.
Try either:
void foo(...);
or
template <typename ... ARGS> void foo(ARGS && ... args) { ... body }
First option is the same as void foo() (little known C language fact). First option requires some sort of additional argument (for example printf(char *, ...), where first argument allows function to detect, how to parse following arguments).
Second option requires you to commit to a function body somewhere in a header.
I want to test the use of alignas(), so I write down these code:
#include <vector>
using namespace std;
template<typename X>
void user(const vector<X>& vx)
{
constexpr int bufmax = 1024;
alignas(X) buffer[bufmax];
const int max = min(vx.size(), bufmax / sizeof(X));
uninitialized_copy(vx.begin(), vx.begin()+max, buffer);
}
However, when I compile it with g++, the compiler outputs an error: "expected primary-expression before alignas(X)". Who could explain this? I don't know the exact usages of alignas().
alignas(X) is an aligment specifier, which is applied to type. Your error happens because you didn't specified a type here.
Judging from the rest of the code, you probably want to create aligned buffer of characters: alignas(X) char buffer[bufmax];
add below data type to your code
char alignas(X) buffer[bufmax];
alignas(X) char buffer[bufmax];
I also bumped on the same while reading the book.
I have successfully integrated openssl dev library in main.cpp file. openssl/evp.h is currently included.
I can use EVP_DigestInit, EVP_DigestUpdate functions successfully without any error. But if I try to call EVP_DigestFinal, it gives following error:
No matching function for call to 'EVP_DigestFinal'
Whereas, the functions are defined in the same header file, evp.h. What should I do in order to use this function too? Thanks for help.
Related code:
EVP_MD_CTX ctx;
char ch[128];
int val = 128;
EVP_DigestFinal(&ctx, ch, val);
From what I find at OpenSSL.org, the signature is
int EVP_DigestFinal(EVP_MD_CTX *ctx, unsigned char *md, int *s);
To match that you need unsigned char for the second parameter and an address for the third parameter, something like
EVP_MD_CTX ctx;
unsigned char ch[128];
_______^
int val = 128;
EVP_DigestFinal(&ctx, ch, &val);
__________________________^
I'm trying to make a program involving files assign2.cpp, Player.h, Player.cpp, Team.h, Team.cpp which reads data from a txt file on player info (like hits, atBat, position, name and number) and displays it out into assign2.cpp. assign2.cpp is what contains int main() and is suppose to contain very little code because relies on the other files to do the work.
The error:
request for member getName which is of non-class type ‘char’...
Please help, I've been trying to find the issue and can never do so. The compilation failure :
In file included from Team.cpp:1:0:
Team.h:34:11: warning: extra tokens at end of #endif directive [enabled by default]
Team.cpp: In constructor ‘Team::Team()’:
Team.cpp:15:5: warning: unused variable ‘numPlayers’ [-Wunused-variable]
Team.cpp: In member function ‘void Team::sortByName()’:
Team.cpp:49:56: error: request for member ‘getName’ in ‘((Team*)this
-> Team::playerObject[(j + -1)]’, which is of non-class type ‘char’
Team.cpp:49:74: error: request for member ‘getName’ in ‘bucket’, which is of non-class type ‘int’
Team.cpp: In member function ‘void Team::print()’:
Team.cpp:63:18: error: request for member ‘print’ in ‘((Team*)this)- >Team::playerObject[i]’, which is of non-class type ‘char’
make: *** [Team.o] Error 1
Team.h
#ifndef TEAM_H
#define TEAM_H
#include "Player.h"
class Team
{
private:
char playerObject[40];
int numPlayers; // specifies the number of Player objects
// actually stored in the array
void readPlayerData();
void sortByName();
public:
Team();
Team(char*);
void print();
};
#endif / *Team.h* /
Team.cpp
#include "Team.h"
#include <cstring>
#include <iostream>
#include <iomanip>
#include <stdio.h>
#include <string.h>
#include <fstream>
#include <cstdlib>
using namespace std;
Team::Team()
{
strcpy (playerObject,"");
int numPlayers = 0;
}
Team::Team(char* newPlayerObject)
{
strncpy(playerObject, newPlayerObject, 40);
readPlayerData();
}
void Team::readPlayerData()
{
ifstream inFile;
inFile.open("gamestats.txt");
if (!inFile){
cout << "Error, couldn't open file";
exit(1);
}
inFile.read((char*) this, sizeof(Team));
inFile.close();
}
void Team::sortByName()
{
int i, j;
int bucket;
for (i = 1; i < numPlayers; i++)
{
bucket = playerObject[i];
for (j = i; (j > 0) && (strcmp(playerObject[j-1].getName(), bucket.getName()) > 0); j--)
playerObject[j] = playerObject[j-1];
playerObject[j] = bucket;
}
}
Player.h (incase anyone needs it)
#ifndef PLAYER_H
#define PLAYER_H
class Player
{
// Data members and method prototypes for the Player class go here
private:
int number;
char name[26];
char position[3];
int hits;
int atBats;
double battingAverage;
public:
Player();
Player(int, char*, char*, int, int);
char* getName();
char* getPosition();
int getNumber();
int getHits();
int getAtBats();
double getBattingAverage();
void print();
void setAtBats(int);
void setHits(int);
};
#endif
I'm very stuck, Thanks in advance.
In the Team constructor on this line
playerObject = newPlayerObject;
you're trying to assign a value of type char* to a member of type char[40], which doesn't work, since they are two different types. In any case, you probably would need to copy the data from the input instead of just trying to hold the pointer internally. Something like
strncpy(playerObject, newPlayerObject, 40);
Generally, you will always be able to assign a char[N] to a char*, but not the other way around, but that's just because C++ will automatically convert the char[N] to a char*, they are still different types.
Your declaration is:
char playerObject[40];
And your constructor reads:
Team::Team(char* newPlayerObject)
{
playerObject = newPlayerObject;
The error message you referenced in the title of this question obviously comes from here, and it is self explanatory. An array and a pointer are two completely different, incompatible types, when it comes to this kind of an assignment.
What you need to do depends entirely on what you expect to happen, and what your specifications are.
A) You could be trying to initialize the array from the character pointer, in which case you'll probably want to use strcpy(). Of course, you have to make sure that the string, including the null byte terminator, does not exceed 40 bytes, otherwise this will be undefined behavior.
Incidently, this is what you did in your default constructor:
Team::Team()
{
strcpy (playerObject,"");
}
B) Or, your playerObject class member should, perhaps, be a char * instead, and should be either assigned, just like that, or perhaps strdup()ed. In which case your default constructor will probably need to do the same.
Whichever one is the right answer for you depends entirely on your requirements, that you will have to figure out yourself.