converting class to using template (for different data type variables)

I’m working to up my coding skills incrementally. I have spent several days reading tutorials and trying to get through a knothole in my understanding. Perhaps you could point me to a better reference or make some suggestions to help me get back on track.

First I converted some code to using classes and that dramatically reduced the code size. Next, I want to take my class and add templates so I can have the class accept multiple different data types for some of the variables. This code is for updating the fields on an oled display, only erasing the portion of the oled in which the data has changed, thus eliminating flickering of the screen. I am doing all of this inline within an existing .cpp file in a larger project (that is, I have not separated it out into its own .cpp and .h file… and it works fine without trying to add templates to the code. Here is the working relevant code, thinned down to provide a meaningful example, without templates:

#define screen2PixelsPerChar 7
#define screen2RowVolt   1
#define screen2ColAlt          6*screen2PixelsPerChar  
#define screen2VoltErase     6*screen2PixelsPerChar
#define screen2VoltFormat  "%5s"

class LCDfield {
  private:
    float   currentValue;
    float   lastValue;
    int     column;
    int     row;
    int     eraseWidth;
    String  formatString;
    int     f2sDigits;  //number of decimal places
    
  public:
    LCDfield(float currentValue, int column, int row, int eraseWidth, String formatString, int f2sDigits){
      this->currentValue = currentValue;
      this->column = column;
      this->row = row;
      this->eraseWidth = eraseWidth;
      this->formatString = formatString;
      this->f2sDigits = f2sDigits;
    }

    void Update() {
      #define lcdBuffSize 14 
      char   lcdBuff[lcdBuffSize];      //for snprintf  
      if(currentValue != lastValue) {
        lastValue = currentValue;
        oled.clearField(column, row, eraseWidth);
        if(f2sDigits != 0){ 
          snprintf(lcdBuff, lcdBuffSize, formatString.c_str(), float2string(currentValue,f2sDigits));
        }
        else {
          snprintf(lcdBuff, lcdBuffSize, formatString.c_str(), currentValue);
        }
        oled.print(lcdBuff);
      }
    }
}; // don't forget the semicolon at the end of the class

  // set up instances:
  // LCDfield(float currentValue, int column, int row, int eraseWidth, String formatString, int f2sDigits)
  LCDfield LCDaltVolts(measuredAltVolts, screen2ColAlt, 1, screen2VoltErase,screen2VoltFormat,2);
 

  //update each LCDfield
  LCDaltVolts.Update();

The last variable, is a flag of sorts which I pass if the currentValue variable is a float and I need to convert it to a string. The value of that variable is the number of decimal places to display.

Now I try to add a template type for the currentValue field and for the lastValue variable with the following code.

#define screen2PixelsPerChar 7
#define screen2RowVolt   1
#define screen2ColAlt          6*screen2PixelsPerChar  
#define screen2VoltErase     6*screen2PixelsPerChar
#define screen2VoltFormat  "%5s"

template<typename anyType> class LCDfield {
  private:
    anyType   currentValue;
    anyType   lastValue;
    int     column;
    int     row;
    int     eraseWidth;
    String  formatString;
    int     f2sDigits;  //number of decimal places
    
  public:
    LCDfield(anyTypecurrentValue, int column, int row, int eraseWidth, String formatString, int f2sDigits){
      this->currentValue = currentValue;
      this->column = column;
      this->row = row;
      this->eraseWidth = eraseWidth;
      this->formatString = formatString;
      this->f2sDigits = f2sDigits;
    }

    void Update() {
      #define lcdBuffSize 14 
      char   lcdBuff[lcdBuffSize];      //for snprintf  
      if(currentValue != lastValue) {
        lastValue = currentValue;
        oled.clearField(column, row, eraseWidth);
        if(f2sDigits != 0){ 
          snprintf(lcdBuff, lcdBuffSize, formatString.c_str(), float2string(currentValue,f2sDigits));
        }
        else {
          snprintf(lcdBuff, lcdBuffSize, formatString.c_str(), currentValue);
        }
        oled.print(lcdBuff);
      }
    }
}; // don't forget the semicolon at the end of the class

  // set up instances:
  // LCDfield(float currentValue, int column, int row, int eraseWidth, String formatString, int f2sDigits)
  LCDfield LCDaltVolts(measuredAltVolts, screen2ColAlt, 1, screen2VoltErase,screen2VoltFormat,2);
 

  //update each LCDfield
  LCDaltVolts.Update();

The only changes are the “template” and “anyType” additions.

This generates the following error:
error: a template declaration cannot appear at block scope

Now that seems to indicate that some of this needs to be the .h file, but every variation of that I have tried just blows up. Any experienced suggestions are welcome. (Remember, this is only about learning how to convert a class to use template for variables. I am not seeking a full critique of my coding, thank you).

well, I split the class code between the .cpp and the .h files and got rid of the prior error.
But, I’m still clearly not getting this template stuff…
Below is the new code. Here is the error now:
error: ‘template class LCDfield’ used without template parameters

flagged to this line of code: void LCDfield::Update() {

code in .h file:

template<typename T> class LCDfield {
  private:
    T  currentValue;
    T  lastValue;
    int    column;
    int    row;
    int    eraseWidth;
    String formatString;
    int    f2sDigits;  //number of decimal places
    //char   lcdBuff[lcdBuffSize];      //for snprintf
  public:
    LCDfield(T currentValue, int column, int row, int eraseWidth, String formatString, int f2sDigits);
     void Update();
   
   
   
}; // don't forget the semicolon at the end of the class

and the code in the .cpp file:

  #define screen2PixelsPerChar 7
  #define screen2RowVolt   1
  #define screen2ColAlt    6*screen2PixelsPerChar
  #define screen2VoltErase  6*screen2PixelsPerChar
  #define screen2VoltFormat "%5s"

 
   LCDfield::LCDfield(T currentValue, int column, int row, int eraseWidth, String formatString, int f2sDigits){
      this->currentValue = currentValue;
      this->column = column;
      this->row = row;
      this->eraseWidth = eraseWidth;
      this-> formatString = formatString;
      this->f2sDigits = f2sDigits;
    }

    void LCDfield::Update() {
      #define lcdBuffSize 14 
      char   lcdBuff[lcdBuffSize];      //for sprintf  
      if(currentValue != lastValue) {
        lastValue = currentValue;
        oled.clearField(column, row, eraseWidth);
        if(f2sDigits != 0){ 
          snprintf(lcdBuff, lcdBuffSize, formatString.c_str(), float2string(currentValue,f2sDigits));
        }
        else {
          snprintf(lcdBuff, lcdBuffSize, formatString.c_str(), currentValue);
        }
        oled.print(lcdBuff);
      }
    }

  // set up instances:
  // LCDfield(T currentValue, int column, int row, int eraseWidth, String formatString, int f2sDigits)
  LCDfield LCDaltVolts(measuredAltVolts, screen2ColAlt, 1, screen2VoltErase,screen2VoltFormat,2);
 


  //update each LCDfield
  LCDaltVolts.Update();

For templated class, declaration and definition must be in the same file.

Thank you for your reply.

I am thinking that I must not be understanding what the template declaration is. I keep studying.