Error with a template function in a library

I have a template function that I would like to put in a library.

The .h file

class PrintBin
{
  public:
    template <class T>
    void printBinA(T data);
};

The .cpp file

#include <Arduino.h>
#include "PrintBin.h"

template <class T>
void PrintBin::printBinA(T data);
{
  for (int b = (sizeof(data) * 8) - 1; b >= 0; b--)
  {
    if ((data & (1 << b)))
    {
      Serial.print(1);
    }
    else
    {
      Serial.print(0);
    }
  }
}

The test sketch

#include "PrintBin.h"

void setup()
{
  Serial.begin(115200);
  int var = 0b0101010110101010;
  printBinA(var);
}

void loop()
{
}

The error

Arduino: 1.8.13 (Windows 10), Board: "Arduino Nano, ATmega328P"

C:\Users\Bob2\AppData\Local\Temp\arduino_modified_sketch_99622\printBin_build.ino: In function 'void setup()':

printBin_build:7:3: error: 'printBinA' was not declared in this scope

   printBinA(var);

   ^~~~~~~~~

C:\Users\Bob2\AppData\Local\Temp\arduino_modified_sketch_99622\printBin_build.ino:7:3: note: suggested alternative: 'PrintBin'

   printBinA(var);

   ^~~~~~~~~

   PrintBin

exit status 1

'printBinA' was not declared in this scope

This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.

So, what have I done wrong ?

a template is a declaration and should be in .h file

printBinA is a member function, that you try to call without any object.

So why doesn't this work ?
the .h file

template <class T>
void printBinA(T data);

the .cpp file

#include <Arduino.h>
#include "PrintBin.h"

void printBinA(T data)
{
  for (int b = (sizeof(data) * 8) - 1; b >= 0; b--)
  {
    if ((data & (1 << b)))
    {
      Serial.print(1);
    }
    else
    {
      Serial.print(0);
    }
  }
}

the test sketch

#include "PrintBin.h"

void setup()
{
  Serial.begin(115200);
  int var = 0b0101010110101010;
  printBinA(var);
}

void loop()
{
}

the error message

Arduino: 1.8.13 (Windows 10), Board: "Arduino Nano, ATmega328P"

PrintBin.cpp:4:16: error: variable or field 'printBinA' declared void

 void printBinA(T data)

                ^

PrintBin.cpp:4:16: error: 'T' was not declared in this scope

exit status 1

variable or field 'printBinA' declared void

This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.

Ok, but I am trying to keep this simple and not have the need to create an instance of an object to use the PrintBin functions. If I simply put the template function in the .h file, like this

template <class T>
void printBinA(T data)
{
  for (int b = (sizeof(data) * 8) - 1; b >= 0; b--)
  {
    if ((data & (1 << b)))
    {
      Serial.print(1);
    }
    else
    {
      Serial.print(0);
    }
  }
}

and remove the .cpp file I can call the function perfectly well just like a non template function, but with any integer data type. However, I have seen advice previously that this is bad practice and should not be done, so I was trying to do it the correct way

That would be a static member function, but that still has to be called qualified.

printBin::printBinA()

Thanks but I think that I will stick to the "put the functions (there are more in the actual library) in the .h file" solution to avoid complications when calling them

Are there any real gotchas if I do this, bearing in mind it is only for my personal debugging that I am doing this ?

I'm not the templayte-guy, but I could imagine problems with multiple definitions,
when the .h gets included in more than one ino/cpp.

Me neither (obviously) but I don't think things will get that complicated in my personal projects, which are quite modest

For my education though I may give the static function version a try. Thanks for your help

the template is a recipe, not a cake. there is no code generated for template. code exists only for functions or classes generated from the template for specific set of template parameters. So the entire template function definition must be in .h to generate a specific function for parameter types in .ino or in some cpp

How does the linker/compiler handle duplicate incarnations triggered in different compilation units?

Never mind, I found Template Instantiation (Using the GNU Compiler Collection (GCC)).

So, that breaks the "do not define functions in .h files" 'rule' which I have seen mentioned previously in the forum. I will stick with the single .h file approach as these template functions are just for my convenience

Thanks for your insight into the requirements

For completeness here is the .h file that I came up with

#include <Arduino.h>

template <class T>
void printBinBase(T var, boolean bytes = false, boolean newLine = false )
{
  for (int bit = (sizeof(var) * 8) - 1; bit >= 0; bit--)
  {
    Serial.print( var >> bit & 1);
    if (bytes)
    {
      if (!(bit & 7))
      {
        Serial.print(" ");
      }
    }
  }
  if (newLine)
  {
    Serial.println();
  }
}

template <class T>
void printBin(T var)
{
  printBinBase(var, false, false);
}

template <class T>
void printBinln(T var)
{
  printBinBase(var, false, true);
}

template <class T>
void printBinBytes(T var)
{
  printBinBase(var, true, false);
}

template <class T>
void printBinBytesln(T var)
{
  printBinBase(var, true, true);
}


and an example sketch illustrating its use

#include <PrintBin.h>

void setup()
{
  Serial.begin(115200);
  functions();
  uint32_t var = 0b01010101101010100101010110101010;
  Serial.print("printBin(var)\t");
  printBin(var);
  Serial.println("\nnewline from sketch");
  Serial.print("printBinln(var)\t");
  printBinln(var);
  Serial.println("\nnewline from sketch");
  Serial.print("printBinBytes(var)\t");
  printBinBytes(var);
  Serial.println("\nnewline from sketch");
  Serial.print("printBinBytesln(var)\t");
  printBinBytesln(var);
  Serial.println("\nnewline from sketch");
}

void loop()
{
}

void functions()
{
  Serial.println("NOTE : unlike Serial.print(var, BIN); thes functions print leading zeroes");
  Serial.println();
  Serial.println("printBin(var);      any integer variable as a series of bits");
  Serial.println("printBinln(var);    any integer variable as a series of bits with a newline");
  Serial.println("printBinBytes(var); any integer variable as a series of bits split into bytes");
  Serial.println("printBin(var);      any integer variable as a series of bits split into bytes with a newline");
  Serial.println();
  Serial.println("Examples");
  Serial.println();
}

because it is not a function, but a template to generate functions. there will be so many functions created as may different types you use as template parameter.

Oh I say, well swerved ! :grinning:

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.