Template Specializations

I'm messing around with code from a previous post to try to make parsing a bit easier using template specialization and came up with this:

#include "SerialMessenger.h"

namespace Parse{
    template <class T> T getValue(const char* string, T& value)
    {
      static char dataString[16];
      strcpy(dataString, string);
      value = dataString;
      return value;
    };
    
    template <> int getValue(const char* string, int& value)
    {
      value = atoi(string);
      return value;
    };
    
    template <> double getValue(const char* string, double& value)
    {
      value = atof(string);
      return value;
    };
    
    template <> char getValue(const char* string, char& value)
    {
      value = string[0];
      return value;
    };
};

/*
 * Example Message
 * $GPRMC,001225,A,2832.1834,N,08101.0536,W,12,25,251211,1.2,E,A*03
 */

void parseSerialMssg(const char* mssg);
void parseSoftSoftSerialMssg(const char* mssg);

SoftwareSerial softSerial(4,5);

SerialMessenger serial(Serial, 254, '

I'm trying to put the namespace into the header file (SerialMessenger.h) but Im getting this error:

/var/folders/r7/cw1jznxs6dj06v6_pgttpkh00000gn/T/arduino_build_114862/sketch/SerialMessengerWithParsing.ino.cpp.o (symbol from plugin): In function `sw':
(.text+0x0): multiple definition of `int Parse::getValue<int>(char const*, int&)'
/var/folders/r7/cw1jznxs6dj06v6_pgttpkh00000gn/T/arduino_build_114862/sketch/SerialMessenger.cpp.o (symbol from plugin):(.text+0x0): first defined here
/var/folders/r7/cw1jznxs6dj06v6_pgttpkh00000gn/T/arduino_build_114862/sketch/SerialMessengerWithParsing.ino.cpp.o (symbol from plugin): In function `sw':
(.text+0x0): multiple definition of `double Parse::getValue<double>(char const*, double&)'
/var/folders/r7/cw1jznxs6dj06v6_pgttpkh00000gn/T/arduino_build_114862/sketch/SerialMessenger.cpp.o (symbol from plugin):(.text+0x0): first defined here
/var/folders/r7/cw1jznxs6dj06v6_pgttpkh00000gn/T/arduino_build_114862/sketch/SerialMessengerWithParsing.ino.cpp.o (symbol from plugin): In function `sw':
(.text+0x0): multiple definition of `char Parse::getValue<char>(char const*, char&)'
/var/folders/r7/cw1jznxs6dj06v6_pgttpkh00000gn/T/arduino_build_114862/sketch/SerialMessenger.cpp.o (symbol from plugin):(.text+0x0): first defined here
collect2: error: ld returned 1 exit status

can anyone tell me what I'm doing wrong?
, '\n', ',', parseSerialMssg);  // defining start & end markers as '


I'm trying to put the namespace into the header file (SerialMessenger.h) but Im getting this error:

~~~
§_DISCOURSE_HOISTED_CODE_1_§
~~~

can anyone tell me what I'm doing wrong?
 and '\n' respectively

SerialMessenger sw(softSerial, 254, '

I'm trying to put the namespace into the header file (SerialMessenger.h) but Im getting this error:

§_DISCOURSE_HOISTED_CODE_1_§

can anyone tell me what I'm doing wrong?
, '\n', ',', parseSerialMssg);
//SerialMessenger serial1(Serial1, 16, '\0', '\n', parseMssg);

struct GprmcData{
  char* timeString;
  char active;
  double latitude;
  char latDirection;
  double longitude;
  char lonDirection;
  int speed;
  int direction;
  char* dateString;
  double magVariation;
  char varDirection;
  char mode;
  int chkSum;
};

bool newGPRMC = false;
GprmcData data;

void setup()
{
  serial.begin(9600);
  sw.begin(9600);
}

void loop()
{
 
  if(newGPRMC)
  {
    printnewGPRMC();
    newGPRMC = false;
  }
  SerialMessenger::update();
 
}

void parseSerialMssg(const char* mssg)
{
  Serial.print(F("From callback function:\t"));
  Serial.println(mssg);
  if (strstr(mssg, "GPRMC"))
  {
    Serial.print(F("Parsing GPRMC sentence...\n"));
    char gprmc[strlen(mssg) + 1];
    strcpy(gprmc, mssg);
    strtok(gprmc, ",");
    Parse::getValue(strtok(NULL, ","), data.timeString);
    Parse::getValue(strtok(NULL, ","), data.active);
    Parse::getValue(strtok(NULL, ","), data.latitude);
    Parse::getValue(strtok(NULL, ","), data.latDirection);
    Parse::getValue(strtok(NULL, ","), data.longitude);
    Parse::getValue(strtok(NULL, ","), data.lonDirection);
    Parse::getValue(strtok(NULL, ","), data.speed);
    Parse::getValue(strtok(NULL, ","), data.direction);
    Parse::getValue(strtok(NULL, ","), data.dateString);
    Parse::getValue(strtok(NULL, ","), data.magVariation);
    Parse::getValue(strtok(NULL, ","), data.varDirection);
    Parse::getValue(strtok(NULL, "*"), data.mode);
    Parse::getValue(strtok(NULL, ","), data.chkSum);

newGPRMC = true;
  }
  else if (strstr(mssg, "GPGGA"))
  {
    Serial.print(F("Parsing GPGGA sentence...\n"));
  }
}

void printnewGPRMC()
{
  Serial.println(data.timeString);
  Serial.println(data.active);
  Serial.println(data.latitude, 6);
  Serial.println(data.latDirection);
  Serial.println(data.longitude, 6);
  Serial.println(data.lonDirection);
  Serial.println(data.speed);
  Serial.println(data.direction);
  Serial.println(data.dateString);
  Serial.println(data.magVariation);
  Serial.println(data.varDirection);
  Serial.println(data.mode);
  Serial.println(data.chkSum);
}

//void parseSoftSoftSerialMssg(const char* mssg)
//{
//  Serial.print(F("New SoftSerial Message:\t"));
//  Serial.println(mssg);
//}


I'm trying to put the namespace into the header file (SerialMessenger.h) but Im getting this error:

~~~
§_DISCOURSE_HOISTED_CODE_1_§
~~~

can anyone tell me what I'm doing wrong?

I believe you have to indicate which specialization...

template <> int getValue<int>(const char* string, int& value)

I'm not sure I understand you.

I tried moving the entire namespace definition:

namespace Parse{
    template <class T> T getValue(const char* string, T& value)
    {
      static char dataString[16];
      strcpy(dataString, string);
      value = dataString;
      return value;
    };
    
    template <> int getValue(const char* string, int& value)
    {
      value = atoi(string);
      return value;
    };
    
    template <> double getValue(const char* string, double& value)
    {
      value = atof(string);
      return value;
    };
    
    template <> char getValue(const char* string, char& value)
    {
      value = string[0];
      return value;
    };
};

into the header, like this:

#ifndef SERIALMESSENGER_H
#define SERIALMESSENGER_H

#include <SoftwareSerial.h>

namespace Parse{
    template <class T> T getValue(const char* string, T& value)
    {
      static char dataString[16];
      strcpy(dataString, string);
      value = dataString;
      return value;
    };
    
    template <> int getValue(const char* string, int& value)
    {
      value = atoi(string);
      return value;
    };
    
    template <> double getValue(const char* string, double& value)
    {
      value = atof(string);
      return value;
    };
    
    template <> char getValue(const char* string, char& value)
    {
      value = string[0];
      return value;
    };
};

#if ARDUINO >= 100
  #include "Arduino.h"
  #if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__)
    #define MAX_SERIAL_PORTS 2
  #elif defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega16U4__)
    #define MAX_SERIAL_PORTS 1
  #elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
    #define MAX_SERIAL_PORTS 5
  #endif
#else
  #define MAX_SERIAL_PORTS 2
#endif

#define MAX_DATA_FIELDS 10

enum DataTypes{
  CHAR,
  INT,
  DOUBLE,
  STRING,
};

typedef uint8_t byte_size_t; 


class SerialMessenger {
  public:
    SerialMessenger(HardwareSerial& device, size_t maxMessageLength, char startMarker, char endMarker, char delimiter, void (*parseFunction)(const char*), bool err = false);
    SerialMessenger(SoftwareSerial& device, size_t maxMessageLength, char startMarker, char endMarker, char delimiter, void (*parseFunction)(const char*), bool err = false);
    ~SerialMessenger();
    void begin(uint32_t baudRate);
    char* checkForValidMessage(const char startMarker, const char endMarker);
    static void update(void);
    void loadTemplate(DataTypes* types);
    
    
  private:
    DataTypes dataTemplate[MAX_DATA_FIELDS];
    HardwareSerial* hwStream;
    SoftwareSerial* swStream;
    Stream* stream;
    void (*callback)(const char*);
    char* incomingMessage;
    byte_size_t bufferSize;
    byte_size_t idx = 0;
    char startMkr;
    char endMkr;
    char delim;
    bool errors;
    static byte_size_t instanceCount;
    static SerialMessenger* instances[MAX_SERIAL_PORTS];
    enum {
      WAITING,
      PARSING,
    }state = WAITING;
};

#endif

PS I renamed the function parseValue()

This (yours)...

template <> int getValue(const char* string, int& value)

...versus this (mine) (syntax my not be correct)...

template <> int getValue<int>(const char* string, int& value)

The first is a method named getValue. The second is a template specialization of getValue.

(It would be helpful if you post a snippet that does not reference a library none of us can access.)

I suppose you are correct. :wink:

this version compiles and functions as expected... I will have to look over your advice a bit later.

Thanks!

example.ino

#include "SerialMessenger.h"

/*
 * Example Message
 * $GPRMC,001225,A,2832.1834,N,08101.0536,W,12,25,251211,1.2,E,A*03
 */

namespace Parse{
    template <class T> T parseValue(const char* string, T& value)
    {
      static char dataString[8];
      strcpy(dataString, string);
      value = dataString;
      return value;
    };
    
    template <> int parseValue(const char* string, int& value)
    {
      value = atoi(string);
      return value;
    };
    
    template <> double parseValue(const char* string, double& value)
    {
      value = atof(string);
      return value;
    };
    
    template <> char parseValue(const char* string, char& value)
    {
      value = string[0];
      return value;
    };
};

void parseSerialMssg(const char* mssg);
//void parseSoftSoftSerialMssg(const char* mssg);

//SoftwareSerial softSerial(4,5);

SerialMessenger serial(Serial, 254, '

SerialMessenger.h

#ifndef SERIALMESSENGER_H
#define SERIALMESSENGER_H

#include <SoftwareSerial.h>

#if ARDUINO >= 100
  #include "Arduino.h"
  #if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__)
    #define MAX_SERIAL_PORTS 2
  #elif defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega16U4__)
    #define MAX_SERIAL_PORTS 1
  #elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
    #define MAX_SERIAL_PORTS 5
  #endif
#else
  #define MAX_SERIAL_PORTS 2
#endif

#define MAX_DATA_FIELDS 10

enum DataTypes{
  CHAR,
  INT,
  DOUBLE,
  STRING,
};

typedef uint8_t byte_size_t; 


class SerialMessenger {
  public:
    SerialMessenger(HardwareSerial& device, size_t maxMessageLength, char startMarker, char endMarker, char delimiter, void (*parseFunction)(const char*), bool err = false);
    SerialMessenger(SoftwareSerial& device, size_t maxMessageLength, char startMarker, char endMarker, char delimiter, void (*parseFunction)(const char*), bool err = false);
    ~SerialMessenger();
    void begin(uint32_t baudRate);
    char* checkForValidMessage(const char startMarker, const char endMarker);
    static void update(void);
    void loadTemplate(DataTypes* types);
    
    
  private:
    DataTypes dataTemplate[MAX_DATA_FIELDS];
    HardwareSerial* hwStream;
    SoftwareSerial* swStream;
    Stream* stream;
    void (*callback)(const char*);
    char* incomingMessage;
    byte_size_t bufferSize;
    byte_size_t idx = 0;
    char startMkr;
    char endMkr;
    char delim;
    bool errors;
    static byte_size_t instanceCount;
    static SerialMessenger* instances[MAX_SERIAL_PORTS];
    enum {
      WAITING,
      PARSING,
    }state = WAITING;
};

#endif

SerialMessenger.cpp

#include "SerialMessenger.h"
#include "SoftwareSerial.h"

byte_size_t SerialMessenger::instanceCount = 0;
SerialMessenger* SerialMessenger::instances[MAX_SERIAL_PORTS] = {nullptr};

SerialMessenger::SerialMessenger(HardwareSerial& device, size_t maxMessageLength, char startMarker, char endMarker, char deliminator, void (*parseFunction)(const char*), bool err)
{
  startMkr = startMarker;
  endMkr = endMarker;
  delim = deliminator;
  instances[instanceCount++] = this;
  hwStream = &device;
  callback = parseFunction;
  bufferSize = maxMessageLength > 254? 254 : maxMessageLength;
  incomingMessage = new char[bufferSize + 1];
  errors = err;
}

SerialMessenger::SerialMessenger(SoftwareSerial& device, size_t maxMessageLength, char startMarker, char endMarker, char deliminator, void (*parseFunction)(const char*), bool err)
{
  startMkr = startMarker;
  endMkr = endMarker;
  delim = deliminator;
  instances[instanceCount++] = this;
  swStream = &device;
  callback = parseFunction;
  bufferSize = maxMessageLength > 254? 254 : maxMessageLength;
  incomingMessage = new char[bufferSize + 1];
  errors = err;
}

SerialMessenger::~SerialMessenger(){ free(incomingMessage); incomingMessage = NULL;}

void SerialMessenger::update()
{
  for (size_t i = 0; i < instanceCount; i++)
  {
    if (const char* message = instances[i]->checkForValidMessage(instances[i]->startMkr, instances[i]->endMkr))
    {
      instances[i]->callback(message);
    }
  }
}

char* SerialMessenger::checkForValidMessage(const char startMarker, const char endMarker) //, const char checksumToken)
{
  stream = !hwStream ? (Stream*)swStream : hwStream;
  if (stream->available())
  {
    if (state == WAITING)
    {
      if (startMkr)
      {
        if (stream->read() == startMarker)
        {
          state = PARSING;
        }
        return nullptr;
      }
      else
      {
        state = PARSING;
      }
    }
    else if (state == PARSING)
    {
      incomingMessage[idx] = stream->read();
      if (incomingMessage[idx] == endMarker)
      {
        incomingMessage[idx] = '\0';
        idx = 0;
        if (startMkr)
        {
          state = WAITING;
        }
        return incomingMessage;
      }
      else
      {
        idx++;
        if (idx > bufferSize - 1)
        {
          if (errors)
          {
            stream->println(F("Bad Message"));  //<< Error back to sender
          }
          idx = 0;
          incomingMessage[idx] = '\0';
          if (startMkr)
          {
            state = WAITING;
          }
        }
      }
    }
  }
  return nullptr;
}

void SerialMessenger::begin(uint32_t baudRate)
{
  if (hwStream)
  {
    hwStream->begin(baudRate);
  }
  else
  {
    swStream->begin(baudRate);
  }
}

void SerialMessenger::loadTemplate(DataTypes* types)
{
  byte_size_t i = 0;
  while(types[i])
  {
    dataTemplate[i] = types[i];
    i++;
  }
}

, '\n', ',', parseSerialMssg);  // defining start & end markers as '


SerialMessenger.h

§DISCOURSE_HOISTED_CODE_1§


SerialMessenger.cpp

§DISCOURSE_HOISTED_CODE_2§


 and '\n' respectively

//SerialMessenger sw(softSerial, 254, '

SerialMessenger.h

§_DISCOURSE_HOISTED_CODE_1_§

SerialMessenger.cpp

§_DISCOURSE_HOISTED_CODE_2_§

, '\n', ',', parseSerialMssg);
//SerialMessenger serial1(Serial1, 16, '\0', '\n', parseMssg);

struct GprmcData{
 char timeString[7];
 char active;
 double latitude;
 char latDirection;
 double longitude;
 char lonDirection;
 int speed;
 int direction;
 char dateString[7];
 double magVariation;
 char varDirection;
 char mode;
 int chkSum;
};

bool newGPRMC = false;
GprmcData data;

void setup()
{
 serial.begin(9600);
//  sw.begin(9600);
}

void loop()
{
 if(newGPRMC)
 {
   printnewGPRMC();
   newGPRMC = false;
 }
 SerialMessenger::update();
}

void parseSerialMssg(const char* mssg)
{
 Serial.print(F("From callback function:\t"));
 Serial.println(mssg);
 if (strstr(mssg, "GPRMC"))
 {
    Serial.print(F("Parsing GPRMC sentence...\n"));
    char gprmc[strlen(mssg) + 1];
    strcpy(gprmc, mssg);
    strtok(gprmc, ",");
    char* ptr;
    Parse::parseValue(strtok(NULL, ","), ptr);
    strcpy(data.timeString, ptr);
    Parse::parseValue(strtok(NULL, ","), data.active);
    Parse::parseValue(strtok(NULL, ","), data.latitude);
    Parse::parseValue(strtok(NULL, ","), data.latDirection);
    Parse::parseValue(strtok(NULL, ","), data.longitude);
    Parse::parseValue(strtok(NULL, ","), data.lonDirection);
    Parse::parseValue(strtok(NULL, ","), data.speed);
    Parse::parseValue(strtok(NULL, ","), data.direction);
    Parse::parseValue(strtok(NULL, ","), ptr);
    strcpy(data.dateString, ptr);
    Parse::parseValue(strtok(NULL, ","), data.magVariation);
    Parse::parseValue(strtok(NULL, ","), data.varDirection);
    Parse::parseValue(strtok(NULL, "*"), data.mode);
    Parse::parseValue(strtok(NULL, ","), data.chkSum);
    newGPRMC = true;
 }
 else if (strstr(mssg, "GPGGA"))
 {
   Serial.print(F("Parsing GPGGA sentence...\n"));
 }
}

void printnewGPRMC()
{
 Serial.println(data.timeString);
 Serial.println(data.active);
 Serial.println(data.latitude, 6);
 Serial.println(data.latDirection);
 Serial.println(data.longitude, 6);
 Serial.println(data.lonDirection);
 Serial.println(data.speed);
 Serial.println(data.direction);
 Serial.println(data.dateString);
 Serial.println(data.magVariation);
 Serial.println(data.varDirection);
 Serial.println(data.mode);
 Serial.println(data.chkSum);
}

//void parseSoftSoftSerialMssg(const char* mssg)
//{
//  Serial.print(F("New SoftSerial Message:\t"));
//  Serial.println(mssg);
//}


SerialMessenger.h

§DISCOURSE_HOISTED_CODE_1§


SerialMessenger.cpp

§DISCOURSE_HOISTED_CODE_2§

Excellent. You are welcome.

So, I see my problem... I can declare and define the function template and the specializations in the ino file:

example.ino (compiles)

template<class T> void f(T t)  {return t;};  

template<> void f<const char*>(const char* ptr);//{return ptr;};
template<> void f<int>(int i){return i;};
template<> void f<char>(char c){return c;};
template<> void f(double d){return d;};

void setup() {

}

void loop() {

}

but, in a header, I can only declare the specializations:

myHeader.h (compiles)

#ifndef MYHEADER_H
#define MYHEADER_H

template<class T> void f(T t)  {return t;};  

template<> void f<const char*>(const char* ptr);//{return ptr;};
template<> void f<int>(int i); //{return i;};
template<> void f<char>(char c); //{return c;};
template<> void f(double d); //{return d;};

#endif

I'm stumped on how to define them?

myHeader.h (won't compile)

#ifndef MYHEADER_H
#define MYHEADER_H

template<class T> void f(T t)  {return t;};  

template<> void f<const char*>(const char* ptr){return ptr;};
template<> void f<int>(int i){return i;};
template<> void f<char>(char c){return c;};
template<> void f(double d){return d;};

#endif

I finally realized the other error I made, which is that the template functions ought to be declared inline.

So (and this is not yet perfected as I have to fix a method for strings) this is where I ended up...

example.ino

#include "SerialMessenger.h"

/*
 * Example Message
 * $GPRMC,001225,A,2832.1834,N,08101.0536,W,12,25,251211,1.2,E,A*03
*/

void parseSerialMssg(const char* mssg);
//void parseSoftSoftSerialMssg(const char* mssg);

//SoftwareSerial softSerial(4,5);

SerialMessenger serial(Serial, 254, '

SerialMessenger.h

#ifndef SERIALMESSENGER_H
#define SERIALMESSENGER_H

#include <SoftwareSerial.h>

#define MAX_FRAME_WIDTH 16

#if ARDUINO >= 100
  #include "Arduino.h"
  #if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__)
    #define MAX_SERIAL_PORTS 2
  #elif defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega16U4__)
    #define MAX_SERIAL_PORTS 1
  #elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
    #define MAX_SERIAL_PORTS 5
  #endif
#else
  #define MAX_SERIAL_PORTS 2
#endif

#define MAX_DATA_FIELDS 10

typedef uint8_t byte_size_t; 

namespace Parse{
    template <class T> inline void parseValue(const char* string, T& value){return value;};

    template <> inline void parseValue<const char*>(const char* string, const char*& value)
    {
      static char dataString[MAX_FRAME_WIDTH];
      strcpy(dataString, string);
      value = dataString;
    };
    
    template <> inline void parseValue<int>(const char* string, int& value)
    {
      value = atoi(string);
    };
    
    template <> inline void parseValue<double>(const char* string, double& value)
    {
      value = atof(string);
    };
    
    template <> inline void parseValue<char>(const char* string, char& value)
    {
      value = string[0];
    };
};

class SerialMessenger {
  public:
    SerialMessenger(HardwareSerial& device, size_t maxMessageLength, char startMarker, char endMarker, char delimiter, void (*parseFunction)(const char*), bool err = false);
    SerialMessenger(SoftwareSerial& device, size_t maxMessageLength, char startMarker, char endMarker, char delimiter, void (*parseFunction)(const char*), bool err = false);
    ~SerialMessenger();
    void begin(uint32_t baudRate);
    char* checkForValidMessage(const char startMarker, const char endMarker);
    static void update(void);
    
  private:
    HardwareSerial* hwStream;
    SoftwareSerial* swStream;
    Stream* stream;
    void (*callback)(const char*);
    char* incomingMessage;
    byte_size_t bufferSize;
    byte_size_t idx = 0;
    char startMkr;
    char endMkr;
    char delim;
    bool errors;
    static byte_size_t instanceCount;
    static SerialMessenger* instances[MAX_SERIAL_PORTS];
    enum {
      WAITING,
      PARSING,
    }state = WAITING;
};

#endif

SerialMessenger.cpp

#include "SerialMessenger.h"
#include "SoftwareSerial.h"

byte_size_t SerialMessenger::instanceCount = 0;
SerialMessenger* SerialMessenger::instances[MAX_SERIAL_PORTS] = {nullptr};

SerialMessenger::SerialMessenger(HardwareSerial& device, size_t maxMessageLength, char startMarker, char endMarker, char delimiter, void (*parseFunction)(const char*), bool err)
{
  startMkr = startMarker;
  endMkr = endMarker;
  delim = delimiter;
  instances[instanceCount++] = this;
  hwStream = &device;
  callback = parseFunction;
  bufferSize = maxMessageLength > 254? 254 : maxMessageLength;
  incomingMessage = new char[bufferSize + 1];
  errors = err;
}

SerialMessenger::SerialMessenger(SoftwareSerial& device, size_t maxMessageLength, char startMarker, char endMarker, char delimiter, void (*parseFunction)(const char*), bool err)
{
  startMkr = startMarker;
  endMkr = endMarker;
  delim = delimiter;
  instances[instanceCount++] = this;
  swStream = &device;
  callback = parseFunction;
  bufferSize = maxMessageLength > 254? 254 : maxMessageLength;
  incomingMessage = new char[bufferSize + 1];
  errors = err;
}

SerialMessenger::~SerialMessenger(){ free(incomingMessage); incomingMessage = NULL;}

void SerialMessenger::update()
{
  for (size_t i = 0; i < instanceCount; i++)
  {
    if (const char* message = instances[i]->checkForValidMessage(instances[i]->startMkr, instances[i]->endMkr))
    {
      instances[i]->callback(message);
    }
  }
}

char* SerialMessenger::checkForValidMessage(const char startMarker, const char endMarker) //, const char checksumToken)
{
  stream = !hwStream ? (Stream*)swStream : hwStream;
  if (stream->available())
  {
    if (state == WAITING)
    {
      if (startMkr)
      {
        if (stream->read() == startMarker)
        {
          state = PARSING;
        }
        return nullptr;
      }
      else
      {
        state = PARSING;
      }
    }
    else if (state == PARSING)
    {
      incomingMessage[idx] = stream->read();
      if (incomingMessage[idx] == endMarker)
      {
        incomingMessage[idx] = '\0';
        idx = 0;
        if (startMkr)
        {
          state = WAITING;
        }
        return incomingMessage;
      }
      else
      {
        idx++;
        if (idx > bufferSize - 1)
        {
          if (errors)
          {
            stream->println(F("Bad Message"));  //<< Error back to sender
          }
          idx = 0;
          incomingMessage[idx] = '\0';
          if (startMkr)
          {
            state = WAITING;
          }
        }
      }
    }
  }
  return nullptr;
}

void SerialMessenger::begin(uint32_t baudRate)
{
  if (hwStream)
  {
    hwStream->begin(baudRate);
  }
  else
  {
    swStream->begin(baudRate);
  }
}

@Coding Badly, I appreciate your help!, '\n', ',', parseSerialMssg);  // defining start & end markers as '


SerialMessenger.h

§DISCOURSE_HOISTED_CODE_1§


SerialMessenger.cpp

§DISCOURSE_HOISTED_CODE_2§


@Coding Badly, I appreciate your help! and '\n' respectively

//SerialMessenger sw(softSerial, 254, '

SerialMessenger.h

§_DISCOURSE_HOISTED_CODE_1_§

SerialMessenger.cpp

§_DISCOURSE_HOISTED_CODE_2_§

@Coding Badly, I appreciate your help!, '\n', ',', parseSerialMssg);
//SerialMessenger serial1(Serial1, 16, '\0', '\n', parseMssg);

struct GprmcData{
 char timeString[7];
 char active;
 double latitude;
 char latDirection;
 double longitude;
 char lonDirection;
 int speed;
 int direction;
 char dateString[7];
 double magVariation;
 char varDirection;
 char mode;
 int chkSum;
};

bool newGPRMC = false;
GprmcData data;

void setup()
{
 serial.begin(9600);
//  sw.begin(9600);
}

void loop()
{
 if(newGPRMC)
 {
   printnewGPRMC();
   newGPRMC = false;
 }
 SerialMessenger::update();
}

void parseSerialMssg(const char* mssg)
{
 Serial.print(F("From callback function:\t"));
 Serial.println(mssg);
 if (strstr(mssg, "GPRMC"))
 {
    Serial.print(F("Parsing GPRMC sentence...\n"));
    char gprmc[strlen(mssg) + 1];
    strcpy(gprmc, mssg);
    strtok(gprmc, ",");
    char* ptr = nullptr;
    Parse::parseValue(strtok(NULL, ","), ptr);
    strcpy(data.timeString, ptr);
    Parse::parseValue(strtok(NULL, ","), data.active);
    Parse::parseValue(strtok(NULL, ","), data.latitude);
    Parse::parseValue(strtok(NULL, ","), data.latDirection);
    Parse::parseValue(strtok(NULL, ","), data.longitude);
    Parse::parseValue(strtok(NULL, ","), data.lonDirection);
    Parse::parseValue(strtok(NULL, ","), data.speed);
    Parse::parseValue(strtok(NULL, ","), data.direction);
    Parse::parseValue(strtok(NULL, ","), ptr);
    strcpy(data.dateString, ptr);
    Parse::parseValue(strtok(NULL, ","), data.magVariation);
    Parse::parseValue(strtok(NULL, ","), data.varDirection);
    Parse::parseValue(strtok(NULL, "*"), data.mode);
    Parse::parseValue(strtok(NULL, ","), data.chkSum);
    newGPRMC = true;
 }
 else if (strstr(mssg, "GPGGA"))
 {
   Serial.print(F("Parsing GPGGA sentence...\n"));
 }
}

void printnewGPRMC()
{
 Serial.println(data.timeString);
 Serial.println(data.active);
 Serial.println(data.latitude, 6);
 Serial.println(data.latDirection);
 Serial.println(data.longitude, 6);
 Serial.println(data.lonDirection);
 Serial.println(data.speed);
 Serial.println(data.direction);
 Serial.println(data.dateString);
 Serial.println(data.magVariation);
 Serial.println(data.varDirection);
 Serial.println(data.mode);
 Serial.println(data.chkSum);
}

//void parseSoftSoftSerialMssg(const char* mssg)
//{
//  Serial.print(F("New SoftSerial Message:\t"));
//  Serial.println(mssg);
//}


SerialMessenger.h

§DISCOURSE_HOISTED_CODE_1§


SerialMessenger.cpp

§DISCOURSE_HOISTED_CODE_2§


@Coding Badly, I appreciate your help!

I finally realized the other error I made, which is that the template functions ought to be declared inline.

That is completely unnecessary.

myHeader.h (won't compile)

You forgot the <double> template specifier.

Jiggy-Ninja:
That is completely unnecessary . You forgot the template specifier.

What is completely unnecessary?

Where did i forget the specifier?

inline is not necessary.

This: template<> void f(double d){return d;};
Should be: template<> void f<double>(double d){return d;};

Jiggy-Ninja:
inline is not necessary.

This: template<> void f(double d){return d;};
Should be: template<> void f<double>(double d){return d;};

that's not the final code I posted, just an example trying to compile.

My (1.8.2) IDE won't compile my example (post #7) without inline.

Perhaps you can show me an example that would.

EDIT:

reference this thread:

So in summary: For non fully specialized function templates, i.e. ones that carry at least one unknown type, you can omit inline, and not receive errors, but still they are not inline. For full specializations, i.e. ones that use only known types, you cannot omit it.

Or move the full specializations out of the .h file and into a .cpp.

Jiggy-Ninja:
Or move the full specializations out of the .h file and into a .cpp.

for example, how?

BulldogLowell:
for example, how?

Literally just cut them out of the .h file, paste them into a .cpp file, and wrap them in the namespace.

Jiggy-Ninja:
Literally just cut them out of the .h file, paste them into a .cpp file, and wrap them in the namespace.

yeah, that doesn't compile...

What doesn't compile? This does:

#ifndef SERIALMESSENGER_H
#define SERIALMESSENGER_H

#include <SoftwareSerial.h>

#define MAX_FRAME_WIDTH 16

#if ARDUINO >= 100
  #include "Arduino.h"
  #if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__)
    #define MAX_SERIAL_PORTS 2
  #elif defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega16U4__)
    #define MAX_SERIAL_PORTS 1
  #elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
    #define MAX_SERIAL_PORTS 5
  #endif
#else
  #define MAX_SERIAL_PORTS 2
#endif

#define MAX_DATA_FIELDS 10

typedef uint8_t byte_size_t;

namespace Parse{
    template <class T> void parseValue(const char* string, T& value){return value;};
};

class SerialMessenger {
  public:
    SerialMessenger(HardwareSerial& device, size_t maxMessageLength, char startMarker, char endMarker, char delimiter, void (*parseFunction)(const char*), bool err = false);
    SerialMessenger(SoftwareSerial& device, size_t maxMessageLength, char startMarker, char endMarker, char delimiter, void (*parseFunction)(const char*), bool err = false);
    ~SerialMessenger();
    void begin(uint32_t baudRate);
    char* checkForValidMessage(const char startMarker, const char endMarker);
    static void update(void);
    
  private:
    HardwareSerial* hwStream;
    SoftwareSerial* swStream;
    Stream* stream;
    void (*callback)(const char*);
    char* incomingMessage;
    byte_size_t bufferSize;
    byte_size_t idx = 0;
    char startMkr;
    char endMkr;
    char delim;
    bool errors;
    static byte_size_t instanceCount;
    static SerialMessenger* instances[MAX_SERIAL_PORTS];
    enum {
      WAITING,
      PARSING,
    }state = WAITING;
};

#endif
#include "SerialMessenger.h"
#include "SoftwareSerial.h"

byte_size_t SerialMessenger::instanceCount = 0;
SerialMessenger* SerialMessenger::instances[MAX_SERIAL_PORTS] = {nullptr};

SerialMessenger::SerialMessenger(HardwareSerial& device, size_t maxMessageLength, char startMarker, char endMarker, char delimiter, void (*parseFunction)(const char*), bool err)
{
  startMkr = startMarker;
  endMkr = endMarker;
  delim = delimiter;
  instances[instanceCount++] = this;
  hwStream = &device;
  callback = parseFunction;
  bufferSize = maxMessageLength > 254? 254 : maxMessageLength;
  incomingMessage = new char[bufferSize + 1];
  errors = err;
}

SerialMessenger::SerialMessenger(SoftwareSerial& device, size_t maxMessageLength, char startMarker, char endMarker, char delimiter, void (*parseFunction)(const char*), bool err)
{
  startMkr = startMarker;
  endMkr = endMarker;
  delim = delimiter;
  instances[instanceCount++] = this;
  swStream = &device;
  callback = parseFunction;
  bufferSize = maxMessageLength > 254? 254 : maxMessageLength;
  incomingMessage = new char[bufferSize + 1];
  errors = err;
}

SerialMessenger::~SerialMessenger(){ free(incomingMessage); incomingMessage = NULL;}

void SerialMessenger::update()
{
  for (size_t i = 0; i < instanceCount; i++)
  {
    if (const char* message = instances[i]->checkForValidMessage(instances[i]->startMkr, instances[i]->endMkr))
    {
      instances[i]->callback(message);
    }
  }
}

char* SerialMessenger::checkForValidMessage(const char startMarker, const char endMarker) //, const char checksumToken)
{
  stream = !hwStream ? (Stream*)swStream : hwStream;
  if (stream->available())
  {
    if (state == WAITING)
    {
      if (startMkr)
      {
        if (stream->read() == startMarker)
        {
          state = PARSING;
        }
        return nullptr;
      }
      else
      {
        state = PARSING;
      }
    }
    else if (state == PARSING)
    {
      incomingMessage[idx] = stream->read();
      if (incomingMessage[idx] == endMarker)
      {
        incomingMessage[idx] = '\0';
        idx = 0;
        if (startMkr)
        {
          state = WAITING;
        }
        return incomingMessage;
      }
      else
      {
        idx++;
        if (idx > bufferSize - 1)
        {
          if (errors)
          {
            stream->println(F("Bad Message"));  //<< Error back to sender
          }
          idx = 0;
          incomingMessage[idx] = '\0';
          if (startMkr)
          {
            state = WAITING;
          }
        }
      }
    }
  }
  return nullptr;
}

void SerialMessenger::begin(uint32_t baudRate)
{
  if (hwStream)
  {
    hwStream->begin(baudRate);
  }
  else
  {
    swStream->begin(baudRate);
  }
}

namespace Parse{
template <>  void parseValue<const char*>(const char* string, const char*& value)
    {
      static char dataString[MAX_FRAME_WIDTH];
      strcpy(dataString, string);
      value = dataString;
    };
    
    template <>  void parseValue<int>(const char* string, int& value)
    {
      value = atoi(string);
    };
    
    template <>  void parseValue<double>(const char* string, double& value)
    {
      value = atof(string);
    };
    
    template <>  void parseValue<char>(const char* string, char& value)
    {
      value = string[0];
    };
}

Notice that I moved only the full specializations to the cpp file. The unspecialized template stays in the header.

You might say that it's confusing to have the implementations is two different places. I won't argue that point, it's completely fair. If you want them to be in the header, you only need to mark the full specializations with inline, it is not needed on the unspecialized one.

Jiggy-Ninja:
Notice that I moved only the full specializations to the cpp file. The unspecialized template stays in the header.

You might say that it's confusing to have the implementations is two different places. I won't argue that point, it's completely fair. If you want them to be in the header, you only need to mark the full specializations with inline, it is not needed on the unspecialized one.

Thanks so much. I'll go back and see what I did that didn't work...

I appreciate the example!