Go Down

Topic: more with char arrays and class functions (Read 984 times) previous topic - next topic

petedd

I am trying another bit of OOP with a class for reading and parsing serial input strings, such as GPS output.

I have non-OOP code to capture serial strings, save them, and parse them.  I am trying to move this to a Class so I can have multiple types of strings to capture, with different start characters or different end characters, or different delimiter characters, but one set of methods to handle them all.  Someone may have already done this, but I am trying to use this as a learning by doing exercise.

I want to be able to have multiple instances of the class where the char array size is one of the variables.  I have not found a way to set a char array while also setting the array size in a class.  For example, I want one instance of Messages, say Messages GPS to have an dataBufferSize of 80 characters and another, say, Messages IMU to have a dataBufferSize of 18 characters, as shown in the code below.  If I could do that, than the getString method could use the class member m_dataBuffer.  Instead, I have a local dataBuffer and am considering a poor workaround of copying the resultant string back to the class member m_dataBuffer.    I hate the idea of having multiple copies of the data using up RAM, even if temporarily. If I try to strcpy the local dataBuffer to the class member m_dataBuffer, I get mismatches of char and char* and complaints about non-static and const in all the variations I have tried. But that would be one workaround.

Really, what I want to do is is just have the getString function write directly to the respective m_databuffer, for example when I call GPS.getString, the captured string is written to GPS.m_dataBuffer and then I can do other things with that string (m_dataBuffer) with other Class methods.

Classes with char arrays continue to elude me...   Help to get me off center is appreciated.

Code: [Select]
[/
/*
    Demonstration of serial input string handler using Classes to
    allow for capture and parsing of different formats or serial messages

*/

class Messages {
  private:
    int         m_dataBufferSize;
    char       m_startChar;
    char       m_endChar;
    char       m_delimiters[];
  public:
    char       m_dataBuffer;
  private:
    bool       m_storeString;

  public:
    // single delimiter instance
    Messages(int DBS, char SC, char EC, char DL1)
    {
      m_dataBufferSize = DBS;
      m_startChar = SC;
      m_endChar   = EC;
      m_delimiters[0] = DL1;
    }

    // two delimter instance
    Messages(int DBS, char SC, char EC, char DL1, char DL2)
    {
      m_dataBufferSize = DBS;
      m_startChar = SC;
      m_endChar   = EC;
      m_delimiters[0] = DL1;
      m_delimiters[1] = DL2;
    }

    bool getSerialString() {
      static byte dataBufferIndex = 0;
      char dataBuffer[m_dataBufferSize + 1];  //HOW CAN I USE THE CLASS MEMBER m_dataBuffer HERE INSTEAD OF A LOCAL dataBuffer?
      while (Serial.available() > 0) {
        char incomingbyte = Serial.read();
        if (incomingbyte == m_startChar) {
          dataBufferIndex = 0;  //Initialize our dataBufferIndex variable
          m_storeString = true;
        }
        if (m_storeString) {
          //Let's check our index here, and abort if we're outside our buffer size
          //We use our define here so our buffer size can be easily modified
          if (dataBufferIndex == m_dataBufferSize) {
            //Oops, our index is pointing to an array element outside our buffer.
            dataBufferIndex = 0;
            break;
          }
          if (incomingbyte == m_endChar) {
            dataBuffer[dataBufferIndex] = 0; //null terminate the C string
            //m_storeString = false; could be declared here but not really necessary since it is set false again next time the function is called
            //Our data string is complete.  return true
            Serial.print("In getSerialString:  ");
            Serial.println(dataBuffer);
            // ??? HOW TO COPY THE LOCAL dataBuffer to m_dataBuffer???
            //strcpy(m_dataBuffer, dataBuffer); //ERROR invalid conversion from 'char' to 'char*' [-fpermissive]
            return true;
          }
          else {
            dataBuffer[dataBufferIndex++] = incomingbyte;
            dataBuffer[dataBufferIndex] = 0; //null terminate the C string
          }
        }
        else {
        }
      }
      //We've read in all the available Serial data, and don't have a valid string yet, so return false
      return false;
    }

    void outputString() {
      Serial.print("In outputString:  ");
      Serial.println(m_dataBuffer);
    }
};




//Invoke GPS and IMU instances
//Messages.InstanceName(int DBS, char SC, char EC, char DL1,(DL2) )
Messages GPS{80, '$', '\r', ','};
Messages IMU{18, '!', '\r', ':', ','};



void setup() {
  Serial.begin(115200);
  Serial.println("Serial port activated");

}
void loop() {
  if (GPS.getSerialString()) {
    Serial.println("got string");
    GPS.outputString();  // DOES NOT HAVE THE CAPTURED STRING
    while (true) {};
  }

}
code]


horace

#1
Sep 14, 2020, 07:11 am Last Edit: Sep 14, 2020, 07:14 am by horace
I noted that you have not specified a size for the array
Code: [Select]
   char       m_delimiters[?];  //  specify size??
be careful using Arduino Strings they  cause problems
you could allocate memory dynamically

petedd

I noted that you have not specified a size for the array
Code: [Select]
    char       m_delimiters[?];  //  specify size??
be careful using Arduino Strings they  cause problems
you could allocate memory dynamically
Actually, that part of works fine.  The problem I am having is regarding the m_dataBuffer. 

horace

should m_dataBuffer be a pointer to char?
Code: [Select]
char *      m_dataBuffer;

petedd

should m_dataBuffer be a pointer to char?
Code: [Select]
char *      m_dataBuffer;
That seems to work for a string literal but does not let me build the string one character at a time using array indexing.  Maybe if I maintain the local dataBuffer and then try to do a strcpy to it...  but then I get a corrupted string.

Code: [Select]

/*
    Demonstation of serial input string handler using Classes to
    allow for capture and parsing of different formats or serial messages

    1.1 uses delimiters[]
    1.2EX with char*      m_dataBuffer {"12345678901234567890123456789012345678901234567890123456789012345678901234567890"};
*/

class Messages {
  public:
  int          m_dataBufferSize;
  private:
    char       m_startChar;
    char       m_endChar;
    char       m_delimiters[];
  public:
    char*      m_dataBuffer {"12345678901234567890123456789012345678901234567890123456789012345678901234567890"};
    //                        
    bool       m_storeString;

  public:
    // single delimiter instance
    Messages(int DBS, char SC, char EC, char DL1)
    {
      m_dataBufferSize = DBS;
      m_startChar = SC;
      m_endChar   = EC;
      m_delimiters[0] = DL1;
    }

    // two delimter instance
    Messages(int DBS, char SC, char EC, char DL1, char DL2)
    {
      m_dataBufferSize = DBS;
      m_startChar = SC;
      m_endChar   = EC;
      m_delimiters[0] = DL1;
      m_delimiters[1] = DL2;
    }

    bool getSerialString() {
      static byte dataBufferIndex = 0;
      char dataBuffer[m_dataBufferSize + 1];
      while (Serial.available() > 0) {
        char incomingbyte = Serial.read();
        if (incomingbyte == m_startChar) {
          dataBufferIndex = 0;  //Initialize our dataBufferIndex variable
          m_storeString = true;
        }
        if (m_storeString) {
          //Let's check our index here, and abort if we're outside our buffer size
          //We use our define here so our buffer size can be easily modified
          if (dataBufferIndex == m_dataBufferSize) {
            //Oops, our index is pointing to an array element outside our buffer.
            dataBufferIndex = 0;
            break;
          }
          if (incomingbyte == m_endChar) {
            //dataBuffer[dataBufferIndex] = 0; //null terminate the C string
            dataBuffer[dataBufferIndex] = '\n'; //null terminate the C string
            //m_storeString = false; could be declared here but not really necessary since it is set false again next time the function is called
            //Our data string is complete.  return true
            Serial.print("Exiting getString.  dataBuffer: ");
            Serial.println(dataBuffer);
            m_dataBuffer = dataBuffer;
            return true;
          }
          else {
            dataBuffer[dataBufferIndex++] = incomingbyte;
            dataBuffer[dataBufferIndex] = 0; //null terminate the C string
          }
        }
        else {
        }
      }
      //We've read in all the available Serial data, and don't have a valid string yet, so return false
      return false;
    }

};




//Invoke GPS and IMU instances
//Messages.InstanceName(int DBS, char SC, char EC, char DL1,(DL2) )
Messages GPS{80, '$', '\r', ','};
Messages IMU{18, '!', '\r', ':', ','};



void setup() {
  Serial.begin(115200);
  Serial.println("Serial port activated");

}
void loop() {
  Serial.print("GPS.m_dataBuffer starting: ");
  Serial.println(GPS.m_dataBuffer);
  if (GPS.getSerialString()) {
    Serial.println("got string");
    Serial.print("GPS.m_dataBuffer: ");
    Serial.println(GPS.m_dataBuffer);
    while (true) {};
  }

}



INPUT:

$GPGSA,A,3,07,02,26,27,09,04,15,,,,,,1.8,1.0,1.5*33


OUTPUT:

GPS.m_dataBuffer starting: 3456789012345678901234567890123456789012345678901234567890
Exiting getString.  dataBuffer: $GPGSA,A,3,07,02,26,27,09,04,15,,,,,,1.8,1.0,1.5*33

got string
GPS.m_dataBuffer: $GPGSA,A,3,07,02,26,27,09,04,15,,,,,⸮⸮⸮

petedd

okay gang, here is a version that works but I think setting the m_dataBuffer this way is ugly and does not allow for different sized m_dataBuffer for each invocation of the class (that is, say, 80 characters for GPS and 18 characters for IMU).

Still working on this...

Code: [Select]


/*
    Demonstration of serial input string handler using Classes to
    allow for capture and parsing of different formats or serial messages

    1.1 uses delimiters[]
    1.21ex This version sets the m_dataBuffer to an 80 character literal string and works but does not allow me to change the size of the
             buffers based on the m_dataBufferSize.
*/

class Messages {
  public:
    int        m_dataBufferSize;
  private:
    char       m_startChar;
    char       m_endChar;
    char       m_delimiters[];
  public:
    char*      m_dataBuffer {"12345678901234567890123456789012345678901234567890123456789012345678901234567890"}; //UGLY!!
    bool       m_storeString;

  public:
    // single delimiter instance
    Messages(int DBS, char SC, char EC, char DL1)
    {
      m_dataBufferSize = DBS;
      m_startChar = SC;
      m_endChar   = EC;
      m_delimiters[0] = DL1;
    }

    // two delimter instance
    Messages(int DBS, char SC, char EC, char DL1, char DL2)
    {
      m_dataBufferSize = DBS;
      m_startChar = SC;
      m_endChar   = EC;
      m_delimiters[0] = DL1;
      m_delimiters[1] = DL2;
    }

    bool getSerialString() {
      static byte dataBufferIndex = 0;
      while (Serial.available() > 0) {
        char incomingbyte = Serial.read();
        if (incomingbyte == m_startChar) {
          dataBufferIndex = 0;  //Initialize our dataBufferIndex variable
          m_storeString = true;
        }
        if (m_storeString) {
          //Let's check our index here, and abort if we're outside our buffer size
          if (dataBufferIndex == m_dataBufferSize) {
            //Oops, our index is pointing to an array element outside our buffer.
            dataBufferIndex = 0;
            break;
          }
          if (incomingbyte == m_endChar) {
            m_dataBuffer[dataBufferIndex] = 0; //null terminate the C string
            //m_storeString = false; could be declared here but not really necessary since it is set false again next time the function is called
            //Our data string is complete.  return true
            Serial.print("Exiting getString.  m_dataBuffer: ");
            Serial.println(m_dataBuffer);
            return true;
          }
          else {
            m_dataBuffer[dataBufferIndex++] = incomingbyte;
            m_dataBuffer[dataBufferIndex] = 0; //null terminate the C string
          }
        }
        else {
        }
      }
      //We've read in all the available Serial data, and don't have a valid string yet, so return false
      return false;
    }

};




//Invoke GPS and IMU instances
//Messages.InstanceName(int DBS, char SC, char EC, char DL1,(DL2) )
Messages GPS{80, '$', '\r', ','};
Messages IMU{18, '!', '\r', ':', ','};

bool flag = true;

void setup() {
  Serial.begin(115200);
  Serial.println("Serial port activated");
  delay(200);

}
void loop() {


  if (flag) {
    Serial.print("GPS.m_dataBuffer starting: ");
    Serial.println(GPS.m_dataBuffer);
    flag = false;
  }

  if (GPS.getSerialString()) {
    Serial.println("got string");
    Serial.print("GPS.m_dataBuffer: ");
    Serial.println(GPS.m_dataBuffer);
    while (true) {};
  }

}

PieterP

#6
Sep 14, 2020, 09:25 pm Last Edit: Sep 14, 2020, 09:30 pm by PieterP
Actually, that part of works fine.  The problem I am having is regarding the m_dataBuffer. 
Depending on the version of GCC you're using that's either a zero-length array, or a flexible array member.
Both are GNU extensions, and you don't want to have to deal with either of them.

When I compile your class with the latest version of the AVR core (GCC 7.3), I get the error:
Code: [Select]
sketch:12:29: error: flexible array member 'Messages::m_delimiters' not at end of 'class Messages'
     char       m_delimiters[];
                             ^

So I assume you're using an older version of GCC, which interprets your m_delimiters member as an array with size zero.
In that case you're writing completely out of bounds in your constructor, m_delimiters is an array of length zero, there is no "first element":
Code: [Select]
Messages(int DBS, char SC, char EC, char DL1, char DL2)
    {
      m_dataBufferSize = DBS;
      m_startChar = SC;
      m_endChar   = EC;
      m_delimiters[0] = DL1; // <--- m_delimiters[0] does not exist
      m_delimiters[1] = DL2; // <--- m_delimiters[1] does not exist
    }

You're just overwriting memory that's not part of m_delimiters (most likely the memory of the other members of your struct).


Take a step back and think about ownership: someone has to own the memory of the string you store. Either the instance of your "Messages" class owns the string, or someone else does.

If someone else owns the memory, you can run into lifetime issues. For example:

Code: [Select]
struct Message {
  const char *message; // This just points to a string that's owned by someone else
};

Message numberToMessage(int number) {
  String str(number);
  return Message{str.c_str()};
} // Oops: str is deleted here, and the Message still contains a dangling pointer to it

In this example, the memory that stores the string is owned by the local str variable. The Message only stores a pointer to it. When the str variable is deleted, the pointer is no longer valid, because the string it points to is gone.

You can work around lifetimes issues like that, but it's cumbersome, and requires you as the programmer to be extremely aware of the lifetimes of all of your strings and messages.

The alternative is to make the Message itself own its string.
You cannot have C-style arrays with a dynamic size. The size of an array must be known at compile-time. So you'll have to create a buffer that's large enough so your longest message will fit, but not too large that it'll waste too much memory.

For example:
Code: [Select]
struct Message {
  char buffer[16] = {}; // maximum message length is 15 characters + null terminator
  Message(const char *message) {
    strncpy(buffer, message, sizeof(buffer) - 1);
  }
};

Message numberToMessage(int number) {
  String str(number);
  return Message{str.c_str()};
} // str is deleted here, but Message made a copy in the buffer that it owns


Always allocating memory for the longest possible message size is wasteful. The example above always takes 16 bytes to store a message, even if the message is empty.

As an alternative, you could allocate the buffer dynamically. This is what the String class does, for example. The Message still owns its string buffer, but it's now allocated on the heap instead of directly inside of the struct.

For example:

Code: [Select]
struct Message {
  String buffer;
  Message(const char *message) : buffer(message) {}
};

Message numberToMessage(int number) {
  String str(number);
  return Message{str.c_str()};
} // str is deleted here, but Message made a copy in the buffer that it owns

This is much easier to manage, but opens a whole new can of worms: Heap fragmentation. Dynamic allocations, especially when using strings that can have different lengths, can cause the heap memory to become fragmented. When this happens, there are many small sections of free memory, but no large contiguous sections. Allocating large amounts of memory becomes impossible and your program crashes. Desktop computers have a ton of RAM, a memory management unit an operating system to prevent this, but your Arduino has a couple of kilobytes of RAM and runs on the bare metal, so heap fragmentation is a huge issue.
Dynamically allocated strings can be non-deterministic, so you cannot always know beforehand whether heap fragmentation will occur. That's why dynamic allocations are often forbidden in critical software.

Pieter

petedd

Pieter,
Thank you again!
This is a true and valuable lesson and I appreciate you taking the time to lay it out.  I will fix the m_delimiters[] declaration.

petedd

Further... towards solving the m_dataBuffer creation with different sized buffers, I created another armstrong method wherein I see the m_dataBuffer in the constructor with a string literal.  Not pretty, but better than just setting all instantiations of the class to the maximum buffer size of the largest message type.  This is the first block of code below.

I still would like to come up with a way to dynamically build the m_dataBuffer seed.  I have tried this using a method (class function) initBuffer, that snippet shown in the second code block below.  This builds a fakeBuffer character array and then attempts to set m_dataBuffer to this array.  That does not work (the results of what actually copies is shown in the comments of the code).  I feel I am close here, but putting the value of the character array into the char * m_dataBuffer eludes me.   Ideas?

Code: [Select]
/*
    Demonstration of serial input string handler using Classes to
    allow for capture and parsing of different formats or serial messages

    1.1 uses delimiters[]
    1.21 This version sets the m_dataBuffer to an 80 character literal string and works but does not allow me to change the size of the
             buffers based on the m_dataBufferSize.
    1.22 -delimiters[] declaration changed to delimiters[3] and constructors updated to include 1, 2 , and 3 delimiters
         -uses string literal in constructor to seed m_dataBuffer to maximum size
*/

class Messages {
  public:
    int        m_dataBufferSize;
  private:
    char       m_startChar;
    char       m_endChar;
    char       m_delimiters[3];
  public:
    char*      m_dataBuffer;
    bool       m_storeString;

  public:
    // single delimiter instance
    Messages(int DBS, char SC, char EC, char DL0, char* DB)
    {
      m_dataBufferSize = DBS;
      m_startChar = SC;
      m_endChar   = EC;
      m_delimiters[0] = DL0;
      m_dataBuffer= DB;  // DB should be a string literal "aflsf" the length of DBS
    }

    // two delimter instance
    Messages(int DBS, char SC, char EC, char DL0, char DL1, char* DB)
    {
      m_dataBufferSize = DBS;
      m_startChar = SC;
      m_endChar   = EC;
      m_delimiters[0] = DL0;
      m_delimiters[1] = DL1;
      m_dataBuffer= DB;  // DB should be a string literal "aflsf" the length of DBS
    }

    // three delimter instance
    Messages(int DBS, char SC, char EC, char DL0, char DL1, char DL2, char* DB)
    {
      m_dataBufferSize = DBS;
      m_startChar = SC;
      m_endChar   = EC;
      m_delimiters[0] = DL0;
      m_delimiters[1] = DL1;
      m_delimiters[2] = DL2;
      m_dataBuffer= DB;  // DB should be a string literal "aflsf" the length of DBS
    }

    bool getSerialString() {
      static byte dataBufferIndex = 0;
      while (Serial.available() > 0) {
        char incomingbyte = Serial.read();
        if (incomingbyte == m_startChar) {
          dataBufferIndex = 0;  //Initialize our dataBufferIndex variable
          m_storeString = true;
        }
        if (m_storeString) {
          //Let's check our index here, and abort if we're outside our buffer size
          if (dataBufferIndex == m_dataBufferSize) {
            //Oops, our index is pointing to an array element outside our buffer.
            dataBufferIndex = 0;
            break;
          }
          if (incomingbyte == m_endChar) {
            m_dataBuffer[dataBufferIndex] = 0; //null terminate the C string
            //m_storeString = false; could be declared here but not really necessary since it is set false again next time the function is called
            //Our data string is complete.  return true
            Serial.print("Exiting getString.  m_dataBuffer: ");
            Serial.println(m_dataBuffer);
            return true;
          }
          else {
            m_dataBuffer[dataBufferIndex++] = incomingbyte;
            m_dataBuffer[dataBufferIndex] = 0; //null terminate the C string
          }
        }
        else {
        }
      }
      //We've read in all the available Serial data, and don't have a valid string yet, so return false
      return false;
    }

};




//Invoke GPS and IMU instances
//Messages.InstanceName(int DBS, char SC, char EC, char DL1,(DL2) )
Messages GPS{80, '$', '\r', ',', "12345678901234567890123456789012345678901234567890123456789012345678901234567890"};
Messages IMU{18, '!', '\r', ':', ',', "123456789012345678"};

bool flag = true;

void setup() {
  Serial.begin(115200);
  Serial.println("Serial port activated");
  delay(200);

}
void loop() {


  if (flag) {
    Serial.print("GPS.m_dataBuffer starting: ");
    Serial.println(GPS.m_dataBuffer);
    flag = false;
  }

  if (GPS.getSerialString()) {
    Serial.println("got string");
    Serial.print("GPS.m_dataBuffer: ");
    Serial.println(GPS.m_dataBuffer);
    while (true) {};
  }

}




Code: [Select]


    void initBuffer() {
      char fakeBuffer[m_dataBufferSize+1];
      for(int i = 0;i<m_dataBufferSize;++i){
        fakeBuffer[i] = '2'; // just fill with '2's as placeholders          
      }
      fakeBuffer[m_dataBufferSize] = 0;  // null character on end
      Serial.print("fakeBuffer: ");
      Serial.println(fakeBuffer);
      m_dataBuffer = (char*)fakeBuffer;  //THIS DOES NOT WORK - THE COMPLETE STRING IS NOT TRANSFERED.
      // this char array: 22222222222222222222222222222222222222222222222222222222222222222222222222222222
      //         becomes: 22222222222222222222222222222222222222222⸮⸮⸮=>
      
    }

};


PieterP

You've fallen into the lifetime trap. The lifetime of "fakeBuffer" ends at the end of the "initBuffer()" method. After that, the m_dataBuffer is dangling.

You're still using variable length arrays, which is a GNU extension and a bad idea. All array sizes should be compile-time constants.

Code: [Select]
Messages IMU{18, '!', '\r', ':', ',', "123456789012345678"};

That's invalid as well. You cannot pass a string literal as "char *" and then write to it. String literals are read-only.

petedd

Let me try to parse that...
Quote
You've fallen into the lifetime trap. The lifetime of "fakeBuffer" ends at the end of the "initBuffer()" method. After that, the m_dataBuffer is dangling.
1) I intended fakeBuffer to be a local variable which would die when exiting initBuffer()
2) I wanted to then move the contents of fakeBuffer into m_dataBuffer
3) I have instead moved a pointer to fakeBuffer into m_dataBuffer (correct?)
4) Since fakeBuffer is ephemeral, I don't end up with the right content when I try to work with m_dataBuffer later. (correct?)

So, is there no solution to having a Class wherein I can have different sized char arrays, with their sizes defined by the constructor?  (Again, I have lots of workarounds, but I am trying to use this as a learning exercise).

gfvalvo

#11
Sep 15, 2020, 02:49 am Last Edit: Sep 15, 2020, 03:09 am by gfvalvo
You can have you class constructor dynamically create an char array that's owned by the object using the 'new' operator. It's true that dynamic allocation could cause problems on a small memory processor. So, be sure the class's destructor properly releases the memory. Also, you need to trap and properly handle the situation where there's not enough memory available and 'new' returns nullptr. Also, carefully think about how your copy constructor and operator= function will work. Should they even be allowed?

EDIT - Other Issues with Your Code:
* If you use dynamic allocation as mentioned above, there's no need to supply a "string literal in constructor to seed m_dataBuffer to maximum size"

* Also, you have 3 overloaded constructors to allow different numbers of "delimiters". But, once the object is instantiated, you have no idea of which constructor was used or how many "delimiters" it has. I'd add a member variable that's set to the proper value by each of the constructors.

* Why is 'm_dataBufferSize' an int? Do you ever expect a buffer of negative length? If not, use an unsigned type like uint8_t or uint16_t.
No technical questions via PM. They will be ignored. Post your questions in the forum so that all may learn.

GoForSmoke

#12
Sep 15, 2020, 04:13 am Last Edit: Sep 16, 2020, 06:48 am by GoForSmoke
I got here late, i can see. What I do is put a char * in the class and point it to a char array elsewhere.

I have a single-pass parse&lex. It gets the chars 1 at a time as they arrive and finds the first string in an alpha-sorted array of  char arrays that matches *so far* and if/when it doesn't, moves to the next until it does or there -is- no match. By the time the delimiter at the end of the word is reached, match or no-match status is known and if a match, the word number is known. In the time it takes to buffer a word, this way also has it identified.

added: oh hang on, you're not trying to match the text to saved text so no lex.
 

1) http://gammon.com.au/blink  <-- tasking Arduino 1-2-3
2) http://gammon.com.au/serial <-- techniques howto
3) http://gammon.com.au/interrupts
Your sketch can sense ongoing process events in time.
Your sketch can make events to control it over time.

petedd

I got here late, i can see. What I do is put a char * in the class and point it to a char array elsewhere.


Thank you.  I'm not sure what you mean by "point it to a char array elsewhere".  How does one do that?  Would you have a code snippet you could share or insert an example into my Class code above?

Thanks again.

petedd

You can have you class constructor dynamically create an char array that's owned by the object using the 'new' operator. It's true that dynamic allocation could cause problems on a small memory processor. So, be

* Also, you have 3 overloaded constructors to allow different numbers of "delimiters". But, once the object is instantiated, you have no idea of which constructor was used or how many "delimiters" it has. I'd add a member variable that's set to the proper value by each of the constructors.

* Why is 'm_dataBufferSize' an int? Do you ever expect a buffer of negative length? If not, use an unsigned type like uint8_t or uint16_t.

Thank you.  To answer these two questions/points:

* In this case, I will be using strtok() to parse the fields.  Having the delimiters in an array, the delimiter field of strtok only needs to know the name of the array, so I believe I can get away with a member variable for the number of delimiters.  The unused array entries should be null but if I find that is not the case, I can fix that also.

* Odd that... I have m_dataBufferSize as a uint8_t in the very first version of the code and some error led me to change it to an int.  I will change it back and assume I won't have any issues with that.  Thanks.

Go Up