Arduino: pass function to class, returning String - c++

I'm trying to get my Arduino class to return String messages with all kind of information for logging. With lots of trial and error I manage to pass a reference to the logging function to the class, but can only get a char* but not a String, and I want to be able to send Strings making it so much easier to send back all kinds of data.
I have the first part working already.
The sketch:
#include <Test.h>
#include <string.h>
void setup() {
Serial.begin(115200);
Test t;
t.setLogging(writeLog);
writeLog("Test message!" + String(" .... "));
t.doSomething("This is useful.");
t.doSomething("This as well.\n");
t.doSomething("This is even more useful.\n");
bool b = true;
}
void loop() {
}
void writeLog (char* message) {
Serial.print("char function: ");
Serial.print(message);
}
void writeLog (String message) {
Serial.print("String function: ");
Serial.println(message);
}
The header file:
#ifndef TEST_h
#define TEST_h
class Test
{
public:
Test(); // The constructor.
void setLogging(void (*)(char*)); // Takes function setting where to log.
void doSomething(char*);
};
#endif
The class:
#include <Test.h>
typedef void (*LogFunction)(char*);
LogFunction writeLog;
Test::Test () {
}
void Test::doSomething (char* s) {
// Do something useful and log the result.
writeLog(s);
}
void Test::setLogging (void (*f)(char*) ) {
writeLog = f;
return;
}
Now what I want my class to be able to do is send information like this, as String, rather than char* (I also haven't found an easy way of converting "anything" to char* and then concatenating the two or more strings):
writeLog ("HydroMonitorECSensor::setCalibration Receiving calibration - haveCalibration = " + String(haveCalibration));
writeLog ("HydroMonitorECSensor::setCalibration calibratedSlope = " + String(calibratedSlope));
writeLog ("HydroMonitorECSensor::setPins capPos set to " + String(capPos));
Where haveCalibration is a bool (which as String becomes either "true" or "false"), calibratedSlope is a double and capPos is an int. This way I can easily and cleanly send complete lines to the logger. Works great within the main script - not from the class.
I tried simply changing the char* to String and adding #include <string.h> to the library files but it doesn't work.
In Test.cpp I then get void Test::setLogging (void (*f)(String) ) { and in Test.h void setLogging(void (*)(String)); and now I get error messages:
In file included from /home/wouter/Arduino/libraries/Test/Test.cpp:1:0:
/home/wouter/Arduino/libraries/Test/Test.h:10:29: error: expected ',' or '...' before '(' token
void setLogging(void (*)(String)); // Takes function setting where to log.
^
/home/wouter/Arduino/libraries/Test/Test.cpp:16:40: error: variable or field 'setLogging' declared void
void Test::setLogging (void (*f)(String) ) {
^
/home/wouter/Arduino/libraries/Test/Test.cpp:16:31: error: 'f' was not declared in this scope
void Test::setLogging (void (*f)(String) ) {
^
/home/wouter/Arduino/libraries/Test/Test.cpp:16:34: error: 'String' was not declared in this scope
void Test::setLogging (void (*f)(String) ) {
^
exit status 1
Error compiling for board NodeMCU 1.0 (ESP-12E Module).
Suggestions?
Additional info, maybe important: I'm using the Arduino IDE and compile for ESP8266.

You are using the Arduino-provided String class, but didn't include the Arduino.h header in your test.h header file. That causes it to not find the String class and compilation fails.
The following works:
main.cpp:
#include <Arduino.h>
#include <test.hpp>
void writeLog (char* message);
void writeLog (String message);
void setup() {
Serial.begin(115200);
Test t;
t.setLogging(writeLog);
writeLog("Test message!" + String(" .... "));
t.doSomething("This is useful.");
t.doSomething("This as well.\n");
t.doSomething("This is even more useful.\n");
bool b = true;
}
void loop() {
}
void writeLog (char* message) {
Serial.print("char function: ");
Serial.print(message);
}
void writeLog (String message) {
Serial.print("String function: ");
Serial.println(message);
}
test.hpp:
#ifndef TEST_h
#define TEST_h
#include <Arduino.h> //for "String" class
//Typdef for the log function. Takes a String, returns nothing
typedef void (*LogFunction)(String);
class Test
{
public:
Test(); // The constructor.
// void setLogging(void (*)(char*)); // Takes function setting where to log.
void setLogging(LogFunction); //use the typedef here
void doSomething(char*);
};
#endif
test.cpp:
#include <test.hpp>
LogFunction writeLog;
Test::Test () {
}
void Test::doSomething (char* s) {
// Do something useful and log the result.
writeLog(s);
}
//void Test::setLogging (void (*f)(char*) ) {
void Test::setLogging (LogFunction f) { //also use typedef here
writeLog = f;
return;
}

Among other things that may arise, the compiler tells you that it cannot resolve identifier String.
This can have several reasons: First, you write String, and not string (note the capital letter in your writing). Second, if you write string and not std::string, it cannot be resolved unless you have either declared using namespace std (which is not the preferred variant for several reasons) or using std::string. Third, class std::string is declared in header <string>, which is something different than <string.h>.
So I'd write #include <string> and use then std::string.

Related

error C2011: 'SelectorBox' : 'class' type redefinition

I have a problem with a following code:
#ifndef HEADER_H_
class SelectorBox{
public:
string selectorName;
map < string, string > attributeMap;
void setSelectorName(string name);
void setAttribute(string key, string value);
};
string trimTheString(string str); //trimming a string
#endif
//include libraries
#include "Header.h"
using namespace std;
int main()
{
vector <SelectorBox> vectorSelectBox;
SelectorBox *selectorBox;
//code
return 0;
}
#include "Header.h"
#include "main.cpp"
void SelectorBox::setSelectorName(string name) //setter
{
name = trimTheString(name);
selectorName = name;
}
void SelectorBox::setAttribute(string key, string value) //setter
{
key = trimTheString(key);
value = trimTheString(value);
attributeMap[key] = value;
}
When I compile a program, it shows many errors (specifically errors 4430 and 2061), but I believe that the main error is:
error C2011: 'SelectorBox' : 'class' type redefinition.
You must define HEADER_H_. You missed second line from below code.
#ifndef HEADER_H_
#define HEADER_H_
...
#endif
You are including the same header twice, first before main second after main, this causes a problem if you don't use proper include guards.
Your include guard is incomplete, so multiple includes of the same header will cause this error. The trick is to check if a header-specific preprocessor name is defined: If not, then define it, else skip the whole header.
The pattern to use is like this
#ifndef GUARD
#define GUARD
/// the actual header contents
#endif
but simply copying it into every header will cause another problem, because its lack of header specificity. The simplest way to find a good name is to derive it from the file name itself. In your case I'd name the header after the class SelectorBox defined in it, so SelectorBox.h would be a good name, and the include guards would look like this:
#ifndef SELECTORBOX_H
#define SELECTORBOX_H
class SelectorBox {
/// etc.
};
#endif
Using multiple headers with the same include guard is much worse than using no include guards at all.
I guess, you have 3 files:
Header.h
main.cpp
FileX.cpp (you didn't disclose the real name of mister X so far)
In main.cpp you are including Header.h, in FileX.cpp you are including Header.h and main.cpp. Let me show a simplified version of what happens here:
The contents of main.cpp gets transformed into
//include libraries
class SelectorBox{
public:
string selectorName;
map < string, string > attributeMap;
void setSelectorName(string name);
void setAttribute(string key, string value);
};
string trimTheString(string str); //trimming a string
using namespace std;
int main()
{
vector <SelectorBox> vectorSelectBox;
SelectorBox *selectorBox;
//code
return 0;
}
Assuming you included string and map and there is another using namespace std; somewhere before your class definition, this could compile without errors.
But now let's see what happens in FileX.cpp. Its contents gets transformed into the following, and I hope you'll see now what the compilers sees: there is more than one definition of the class SelectorBox:
class SelectorBox{
public:
string selectorName;
map < string, string > attributeMap;
void setSelectorName(string name);
void setAttribute(string key, string value);
};
string trimTheString(string str); //trimming a string
//include libraries
class SelectorBox{
public:
string selectorName;
map < string, string > attributeMap;
void setSelectorName(string name);
void setAttribute(string key, string value);
};
string trimTheString(string str); //trimming a string
using namespace std;
int main()
{
vector <SelectorBox> vectorSelectBox;
SelectorBox *selectorBox;
//code
return 0;
}
void SelectorBox::setSelectorName(string name) //setter
{
name = trimTheString(name);
selectorName = name;
}
void SelectorBox::setAttribute(string key, string value) //setter
{
key = trimTheString(key);
value = trimTheString(value);
attributeMap[key] = value;
}
... compilers use to call that a "redefinition".

G++ - Undefined Reference to member function that is defined

I am currently working on a virtual run time environment program that is at a very early stage, i am prevented from continuing my work due to a linker error when using my makefile, provided below. The error i am receiving is:
g++ controller.o processor.o test.o -o final
controller.o: In function `Controller::run()':
controller.cpp:(.text+0x1e0): undefined reference to
Processor::codeParams(char)'
controller.o: In function `Controller::fetch()':
controller.cpp:(.text+0x290): undefined reference to `Controller::pc'
controller.cpp:(.text+0x299): undefined reference to `Controller::pc'
collect2: error: ld returned 1 exit status
makefile:16: recipe for target 'final' failed
make: *** [final] Error 1
I am unsure as to why i get this error as i thought i had defined these things in the source file corresponding to the header. All files will be given below so that the program can be compiled.
test.cpp:
#include <iostream>
#include <vector>
#include "includes/controller.h"
using namespace std;
int main()
{
vector<char> prog = {0x0};
Controller contr(prog);
cout << "Error Code: " << contr.run() << endl;
return 0;
}
controller.cpp:
/*
Author(s): James Dolan
File: controller.cpp
Build: 0.0.0
Header: includes/controller.h
DoLR: 21:39 11/1/2017
Todo: n/a
*/
#include "includes/controller.h"
Controller::Controller(vector<char> prog)
{
printf("Program:"); //Display program
for(auto i : program)
{
printf("%02X", i);
}
printf("\n");
Controller::program = program;
}
Controller::~Controller ()
{
}
int Controller::run()
{
bool runFlag = true;
int errorCode = 0;
char curCode;
vector<char> curInstr;
int paramRef;
while(runFlag)
{
curCode = fetch();
printf("curCode:%02X\n", curCode);
curInstr.push_back(curCode);
paramRef = proc.codeParams(curCode);
if (paramRef == 0xffff){runFlag = false; continue;} //Check if shutdown signal was returned, if so shutdown
printf("opcode good\n");
for(int i; i<paramRef; i++){curInstr.push_back(fetch());}
}
return errorCode;
}
char Controller::fetch()
{
return program[pc++]; //Return next instruction then increment the program counter
}
controller.h:
/*
Author(s): James Dolan
File: controller.h
Source: ../controller.cpp
DoLR: 21:39 11/1/2017
Todo: n/a
*/
#ifndef CONTROLLER_H
#define CONTROLLER_H
#include <iostream>
#include <vector>
#include <cstdlib>
#include "processor.h"
using namespace std;
class Controller{
public:
Controller(vector<char> prog);
~Controller();
int run();
protected:
private:
vector<char> program;
static int pc;
char fetch();
Processor proc();
};
#endif
processor.cpp:
#include "includes/processor.h"
Processor::Processor()
{
}
Processor::~Processor()
{
}
int codeParams(char code)
{
switch(code)
{
case 0x0: //Halt
return 0;
default:
printf("[ERROR!] Invalid opcode [%02X]", code);
return 0xffff; //Return shutdown signal
}
}
processor.h:
#ifndef PROCESSOR_H
#define PROCESSOR_H
#include <iostream>
#include <cstdlib>
class Processor{
public:
Processor();
~Processor();
int codeParams(char code);
protected:
private:
};
#endif
All if any help is appreciated massively as it will help me to continue with my passion of developing a fully fledged open-source virtual runtime enviroment like the java vm, thank you for your time.
In Controller.cpp you need a int Controller::pc; or int Controller::pc = 0;
In the header file you declared a static int named pc that exists somewhere. It needs to actually exist in a translation unit somewhere (in this case Controller.cpp) so that when the linker tries to find it... it exists.
In Processor.cpp your signature should look like int Processor::codeParams(char code) to let the compiler know that is Processor's codeParams and not a random function named codeParams that happens to also take a character.
For the member function Processor::codeParams you should define it as:
int Processor::codeParams(char code)
// ~~~~~~~~~~~
{
...
}
Otherwise it's just a normal (non–member) function.
For the static member Controller::pc you should define it outside of the class definition, in controller.cpp.
// Controller.h
class Controller {
...
private:
static int pc;
};
// controller.cpp
int Controller::pc;

error: no 'void sim::sendSMS(char*)' member function declared in class 'sim'

Hey guys I'm getting this error when compiling in Arduino IDE
error: no 'void sim::sendSMS(char*)' member function declared in class 'sim'
void sim::sendSMS(char msg[160])
My Header file is:
#ifndef sim_h
#define sim_h
#include "Arduino.h"
class sim
{
public:
sim();
void smstextmode();
void testSIM900();
void sendSMS(char _msg[160]);
private:
char _msg[160];
};
#endif
My CPP file:
#include "Arduino.h"
#include "sim.h"
sim::sim()
{
_msg= msg;
}
void sim::smstextmode()
{
Serial1.write("AT+CMGF=1\r\n");
delay(2000);
}
void sim::testSIM900()
{
Serial1.write("AT\r\n");
delay(1000);
Serial1.write("AT+CSCS?\r\n");
delay(1000);
}
void sim::sendSMS(char msg[160])
{
Serial1.write("AT+CMGS=\"+8295724554\"\r\n");
delay(1500);
Serial1.write(msg);
delay(1000);
Serial1.write((char) 26)
}
So many mistakes. For example:
sim::sim()
{
_msg= msg; // where it should get this msg?
// Also it's not possible to do a copy of array like this.
}
Why do you need _msg, if you are sending msg passed as a parameter?
void sim::sendSMS(char msg[160])
If you want to call it, you have to use exactly the same data type:
char something[160] = "some text to send";
instance.sendSMS(something);
But you can't just pass string directly:
instance.sendSMS("some text to send");
as it's type of const char * and it can't be handled by type char[160].
Also you don't count with termination characte at the end of the string.

Printf and warning with #define string

I am using Qt creator, and it generates this error:
"warning: format '%s' expects argument of type 'char*',
but argument 2 has type 'const void*' [-Wformat]"
The console app is working, but I am interested if there is way to avoid this error, just interested
#include <iostream>
#include <stdio.h>
using namespace std;
bool isOpen(FILE *file);
void print(const void *text);
#define FILE_IS_OPEN "The file is now open"
int main()
{
FILE *f;
f = fopen("This.txt", "w");
if(isOpen(f))
print(FILE_IS_OPEN);
fclose(f);
return 0;
}
bool isOpen(FILE *file) { return file != NULL ? true : false; }
void print(const void *text) { printf("%s\n", text); }
Simply change
void print(const void *text);
to
void print(const char *text);
The same applies in function definition.
Format specifier %s indicates that you're trying to output a string to console, so it requires you to give the address of the string which is nothing but char * here,
you can cast it to (char *)text and pass it to printf.
Also you can't output the contents of a void * pointer they have to any of the primitive data types.

declaring class variable c++ [closed]

This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
So I'm at a complete loss...
In my code I've got
void Parser(FILE* file)
{
Parser par(file);
par.Parse();
}
and I call it in my main function with
Parser(file);
and the header file I've got (which I included in the main file) looks like:
class Parser: public Lexer
{
public:
Parser(FILE* file):Lexer(file);
int Parse();
};
and the error I'm getting is:
p02.cpp: In function 'void Parser(FILE*)':
p02.cpp:20: error: expected ';' before 'par'
p02.cpp:21: error: 'par' was not declared in this scope
make: *** [p02.o] Error 1
What I don't understand is why it is expecting a semicolon before par. Isn't that a legal declaration of a variable for that class?
Edit2: Changing my function name to not be Parser like the class name does not solve this problem. It does give me an extra error telling me that Parser is not declared in this scope, but I cannot see how that is when I've added the include file containing the Parser class right above the declaration for the function.
Edit: My Files
p02.cpp:
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <cstring>
#include <string>
#include "p02lex.h"
#include "y.tab.h"
using namespace std;
void Parser(FILE* file)
{
Parser par(file);
par.Parse();
}
int main(int argc, char* argv[])
{
char fileName[255];
switch(argc)
{
case 1:
cout << "Enter the input file name. ";
cin >> fileName;
break;
case 2:
strcpy(fileName, argv[1]);
break;
default:
cout << "Too many arguments!\n";
exit(1);
}
FILE* file = fopen(fileName, "r");
Parser(file);
fclose(file);
return 0;
}
p02lex.l:
#include "p02lex.h"
#define ID 257
...
#define PROGRAM 304
int TokenMgr(int t);
const char* getTokens(int tokenCode);
unsigned lineCount = 1, columnCount = 1;
%}
LETTER [a-z]|[A-Z]
DIGIT [0-9]
%%
// rules defined here, calling TokenMgr()
%%
int TokenMgr(int t)
{
/* int tc = t;
if (t == IDENTIFIER)
{
char s[1024];
ToLower(s, yytext, strlen(yytext));
tc = RW[s];
if (tc == 0)
tc = t;
}
PrintToken(tfs, tc, line, col);
col += yyleng; */ //JEG
printf("Token:Code=%d Name=%10s line=%3u col=%3u Spelling=\"%s\"\n", t, getTokens(t), lineCount, columnCount, yytext);
columnCount += yyleng;
return /* tc */ 0; // JEG
}
Lexer::Lexer(FILE* file)
{
yyin = file;
}
int Lexer::Scan(void)
{
return yylex();
}
const char* getTokens(int tokenCode)
{
switch(tokenCode)
{
case ID:
return "ID";
... // more cases, returning strings
default:
return NULL;
}
}
p02lex.h:
#ifndef p02lex_h
#define p02lex_h 1
#endif
int yylex(void);
class Lexer
{
public:
Lexer(FILE* file);
int Scan(void);
};
p02par.h:
#ifndef p02par_h
#define p02par_h 1
#endif
using namespace std;
#ifdef __cplusplus
extern "C"
#endif
int yyparse(void);
class Parser: public Lexer
{
public:
Parser(FILE* file):Lexer(file){}
void Parse();
// int Scan(void);
};
p02par.y:
#include <stdio.h>
#include "p02lex.h"
#include "p02par.h"
void yyerror(const char* m);
%}
%token PROGRAM
%token ID
%token SEMICOLON
%%
program:
PROGRAM ID SEMICOLON
{ printf("Stuff happens!\n"); }
%%
void yyerror(const char* m)
{
printf("%s\n", m);
}
/*Parser::Parser(FILE* file):Lexer(file)
{
}*/
int Parser::Parse()
{
return yyparse();
}
p02make:
#LEX = flex
#YACC = yacc -d
CC = g++
OBJ = p02.o p02par.o p02lex.o
p02: ${OBJ}
$(CC) -o p02 ${OBJ} -ll -ly
y.tab.h p02par.cpp: p02par.y
yacc -d -v p02par.y
mv y.tab.c p02par.cpp
p02lex.cpp: p02lex.l
lex p02lex.l
mv lex.yy.c p02lex.cpp
p02par.o: p02par.cpp p02par.h
$(CC) -c -g p02par.cpp
p02.o: p02.cpp p02lex.h p02par.h
$(CC) -c -g p02.cpp
p02lex.o: p02lex.cpp p02lex.h y.tab.h
$(CC) -c -g p02lex.cpp
that should be :
Parser(File* file):Lexer(file) {}
Wait I checked that code, rename the function: void Parser(FILE *f) to something else.
You can only include an initializer list in a constructor definition, not a constructor declaration, so it must be followed by a function body (often empty), not a semicolon. There's also a bit of a problem with a name conflict:
void Parser(FILE* file) // here you've defined Parser as the name of a function
{
Parser par(file); // but here you're trying to use it as the name of a class.
par.Parse();
}
Edit: Here's a bit of code that compiles cleanly, at least with the compilers I have handy:
#include <stdio.h>
class Lexer {
FILE *infile;
public:
Lexer(FILE *f) : infile(f) {}
};
class Parser : public Lexer {
public:
Parser(FILE *f) : Lexer(f) {}
void Parse() {}
};
void do_parse(FILE *file) {
Parser p(file);
p.Parse();
}
You need to qualify Parser because the function has the same identifier as the class:
void Parser(FILE* file)
{
class Parser par(file);
par.Parse();
}
You could also rename the function.
You also need braces here:
Parser(FILE* file):Lexer(file) {}
Change
void Parser(FILE* file)
to
Parser::Parser(FILE* file)
Constructors do not have a return type.