Splitting templated class and class function from .ino out to .cpp and .h

I am now several days into trying to figure out how to split a working template class and related template functions out of a working .ino file and into a .cpp and .h, keeping the main code in the .ino. Could anyone spare the time to advise me on how to split this out. I seem to mess up the references to either the variables or to the functions each time I try. I cen’t imagine I’m the first fellow to struggle through this. Many thanks.

Here is the working code in one .ino file.

float measuredAltVolts = 12.5;
int   measuredAltAmps = -5;

#define screen2PixelsPerChar 7

#define screen2RowVolt   1
#define screen2RowAmp    2

#define screen2ColAlt    6*screen2PixelsPerChar
#define screen2ColBat   13*screen2PixelsPerChar

#define screen2VoltErase  6*screen2PixelsPerChar
#define screen2AmpErase   8*screen2PixelsPerChar

#define screen2VoltFormat "%5s  %5s"
#define screen2AmpFormat "%+3i  %+3i"


//helper function
char *float2string(float v, uint8_t decimals) {
  const int OUTPUT_BUFS = 7;                              // maximum number of floats in a single sprintf
  const int MAX_OUTPUT = 13;                              // largest possible output string
  float absV;
  static char outputBuffers[OUTPUT_BUFS][MAX_OUTPUT + 1];
  static uint8_t callCount;
  char   formatter[] = "%d.%0_d";


  char *pos = outputBuffers[++callCount % OUTPUT_BUFS];   // Select buffer to use this time (Round-robben)
  //__ return (dtostrf(v,MAX_OUTPUT , decimals, pos));    //<<-- This works GREAT, but is about 1.2K in code size larger then what is here.

  formatter[5] = decimals + '0';                          // create the number for the # of decimal characters we ultimatly want.

  unsigned int mult = 1;                                  // Calc multplier for fractional portion of number
  uint8_t multleft = decimals;
  while (multleft--) {
    mult *= 10;
  }

  absV = v;
  if (absV < 0)
    absV *= -1;

  snprintf(pos, MAX_OUTPUT, formatter,
           (int)v,                                        // Whole number, with its sign
           (unsigned int) ((absV * mult)) % mult);      // Fraction, w/o a sign.

  return pos;

}

//Template Class

template <class T>
class LCDfield {
  private:
    T  currentValue;
    T  lastValue = 0;
    int    column;
    int    row;
    int    eraseWidth;
    String formatString;
    int    f2sDigits;  //number of decimal places
  public:
    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 Update();
}; // don't forget the semicolon at the end of the class


//Template Function

template <class T>
void LCDfield<T>::Update() {
#define lcdBuffSize 14
  char   lcdBuff[lcdBuffSize];      //for snprintf
  if (currentValue != lastValue) {
    lastValue += currentValue;
    if (f2sDigits >= 0) {
      snprintf(lcdBuff, lcdBuffSize, formatString.c_str(), float2string(currentValue, f2sDigits), float2string(lastValue, f2sDigits));
      Serial.print("with float2string   ");
      Serial.println(lcdBuff);
    }
    else {
      snprintf(lcdBuff, lcdBuffSize, formatString.c_str(), currentValue, lastValue);
      Serial.print("withOUT float2string   ");
      Serial.println(lcdBuff);
    }
    currentValue++;
  }
}

// create instances
LCDfield <float> LCDaltVolts(measuredAltVolts, screen2ColAlt, screen2RowVolt, screen2VoltErase, screen2VoltFormat, 2);
LCDfield <int>   LCDaltAmps(measuredAltAmps, screen2ColAlt - 1, screen2RowAmp, screen2AmpErase, screen2AmpFormat, -1);

void setup() {
  Serial.begin(115200);
  Serial.println("Serial Out Started");
}

void loop() {
  LCDaltVolts.Update();
  LCDaltAmps.Update();
  Serial.println("");
  delay(1000);
}

Dangalang... I found a solution. Who'd thunk it. Just put all the class stuff (and the helper function) in a .h file and don't even create a .cpp file. I would not have thought of that...

see answer here, along with a two other, more complicated solutions as well.
Scroll down to "Splitting up template classes"

https://www.learncpp.com/cpp-tutorial/133-template-classes/

so for completeness:
That's everything from //helper function through to //create instances gets put in a .h file.
An #include filename.h then goes in the files (.ino or .cpp) which then use the classes and template functions.

That works as long as there is only one file in your sketch that uses the library. If you include that .h in more than one source file you will get multiple declarations of your helper function.

For a library that can be used in multiple source files, you would put the class declaration in the .h and the function and variable declarations in a .cpp.

johnwasser:
That works as long as there is only one file in your sketch that uses the library. If you include that .h in more than one source file you will get multiple declarations of your helper function.

It won't cause a linker error when template functions are used. Each compilation unit will have a unique version of each function that gets implemented from the template.

johnwasser:
For a library that can be used in multiple source files, you would put the class declaration in the .h and the function and variable declarations in a .cpp.

Again, not applicable when using templates.

Thanks guys.