Go Down

Topic: String to function: any best practices? (Read 1 time) previous topic - next topic

supermaggel

#15
Jun 14, 2012, 03:39 pm Last Edit: Jun 14, 2012, 04:28 pm by supermaggel Reason: 1
@GoForSmoke Haha, yeah that is true as well. It's just that i don't understand why Arduino's String implementation is so much more RAM intensive, why don't they just use the char* functions but wrap them in a human-readable format - like string.compareTo instead of strcmpstrtoantrhstr() (to exacerbate what i'm trying to say here). I'm no C++ programmer, so i don't have these methods 'baked' into my mind, but i have used PHP which inherits a lot of the same function names and also there i found they weren't really human-readable. But this creates a whole different discussion which is not my point here).

Question regarding sscanf

I'm now using the sscanf function (which is awesome and saves a lot of roll-your-own-parser-time) this way:

void dataReceivedAction(WebSocket &socket, char* dataString, byte frameLength) {

// below debug print is a leftover from the example webSocketServer library.
#ifdef DEBUG
 Serial.print("Got data: ");
 Serial.write((unsigned char*)dataString, frameLength);
 //Serial.println("\n");
#endif

// first, extract command and params if there are any
 String command;
 String param1;
 String param2;
 
 int numExtracted = sscanf(dataString, "%s %s %s", &command, &param1, &param2);
 
 switch(numExtracted) {
   case 0:
       Serial.println("ERROR, ERROR, ERROR! NO VALID REQUEST FOUND.");
   break;
   
   case 1:
       Serial.println("extraction yielded only a function call.");
       Serial.println("command: ");
       Serial.println(command);
       Serial.println("\n\n");
   break;
   
   case 2:
       Serial.println("extraction yielded a function call + 1 parameter.");
       Serial.println("command: ");
       Serial.println(command);
       Serial.println("\n");
       Serial.println("param1: ");
       Serial.println(param1);
       Serial.println("\n\n");
   break;
   
   case 3:
       Serial.println("extraction yielded a function call + 2 parameters.");
       Serial.println("command: ");
       Serial.println(command);
       Serial.println("\n");
       Serial.println("param1: ");
       Serial.println(param1);
       Serial.println("\n");
       Serial.println("param2: ");
       Serial.println(param2);
       Serial.println("\n\n");
   break;
 }

}

But, when i run this, i get a lot of gibberish AFTER the debug line (see screenshot)
Something is not right with the way i use sscanf, can anyone point out what?
Also, i know my switch-case debug could probably written in a tenth the amount of lines so proposals are greatly welcomed!

[EDIT: i've now removed all the command and param serial.println's.]
no weird gibberish and also allmost real-time communication with my app, instead of a 3 second delay.

i've changed my switch statement to the following:

Serial.println("Extraction yielded a function call + " + String(numExtracted-1) + " parameter(s).");

which just only lets me know what the signature is of what is called, enough for now but i'd like to
show the name of the function and the parameters as well..

IP adress:
192.168.1.200
Got data: SET_Mode bla test

extraction yielded a function call + 2 parameters.
function GET_Speed called...

AWOL

Code: [Select]
String command;
  String param1;
  String param2;
 
  int numExtracted = sscanf(dataString, "%s %s %s", &command, &param1, &param2);


sscanf parses strings (char*) not Strings.
"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

pYro_65

Hmmmm, I just examined zoomed through the string class. Seems some benefits can be gained here.

As I can tell,

Code: [Select]
char *buffer;         // the actual char array

is the first element. So something like the following is valid:

Code: [Select]
int numExtracted = sscanf( dataString, "%s %s %s", &command, ( char* ) &param1, ( char* ) &param2);

but there is more appropriate versions to this. The best being a core update:

Code: [Select]


class String{

  public:

    operator char*(){ return this->buffer; }  //Add this

    //Blah

  private:

    char *buffer;         // the actual char array

    //Blah
};


or without changing the core:

Code: [Select]
inline char *operator =( String &s_Str ){ return ( char* ) &s_Str; }

^^Have to cast due to buffer being private.

GoForSmoke

C++ String class, when you change a String (at least when the size changes) makes a copy of itself including overhead bytes and deletes the original which leaves a hole in the heap. One of the 'conveniences' being that you don't 'see' it happen.
Maybe the next allocation will fit in the hole. Might even fill it. If it doesn't then it goes on the end and bumps the heap size up. At the same time, the stack grows and shrinks from the top of ram down. If heap and stack collide then what happens isn't something you can find through simple examination of your source logic.

Here are some signs and observations of the road ahead for beginners:

All the C string commands boil down to simple enough ideas that you can roll on your own. You need to be aware of your own buffer lengths and do bounds checks even if you use the built-ins. I really think from having rolled my own that making your own teaches enough to lose the mystery and fear of of commands like strcmp (string compare), to know that strncpy differs from strcpy by how each deals with number of characters copied and terminating zero. Strncpy does not place a terminating zero at the end of the copied section which is essential when changing characters in the middle of a string -- it is equal to the BASIC MIDSTR$() command.

Why prefer strcmp to stringCompareTo? Because I'm not a masochist. Because getting used to the abbreviated code takes a short while and it takes less thought-space too. That's why we shorten names and words, especially technical words, or have "big words" to replace whole sentences. It's the difference between in many cases English and German, two syllables vs many. OTOH many technical names in German tell just what a thing is or does in detail, like long variable names. But say them ten times fast, you can't. Long words are harder to think.

Setting up a flag and while loop to compare two arrays is drop dead simple once you've done it a time or two yet for those who have not there's some kind of dread. It takes more mental work to constantly come up with meaningful comments!

A pointer is a pointer, it's not the data, it only points to data. You add 1 to a byte pointer and it points to the next byte. You can walk through an array by setting a pointer to the start and incrementing the pointer as you go, it's like working with an index where you don't have to use the array name and brackets, it saves typing. There's even pointers to pointers, pointers to functions and pointer tables. Take a week or more to get the basics down and do the homework because pointers are one of the most versatile and powerful things in C.

The best part of high level for me means not having to know what CPU registers are being used. I use libraries when I need them but I know that behind every convenient function there are a couple to many lines of code and maybe (probably) some stack variables in addition to passed args.

If you use sscanf or atoi or any functions that can return an error (feed atoi "123foo") then check for error returned. It's easier to code for everything going right but that's wishful non-thinking unless you made sure in previous code. If you're that good then all of the above is old hat anyway.

I find it harder to express logic in English than in Code.
Sometimes an example says more than many times as many words.

majenko

Quote
feed atoi "123foo"


It converts it to an integer containing 123.  atoi() converts the beginning of the string, so that is a perfectly valid string.  "foo123" would throw it more, in that it will return 0 ;)
Get 10% off all 4D Systems TFT screens this month: use discount code MAJENKO10

GoForSmoke

My input routines from 30+ years ago were tighter against mistakes. They had to be, they were used by fumble-typing engineers.

I find it harder to express logic in English than in Code.
Sometimes an example says more than many times as many words.

PaulS

Quote
They had to be, they were used by fumble-typing engineers.

Hey, I resemble that remark!  8)

Go Up