[SOLVED]Need help referencing two classes in one constructor.

Hey guys, I'm hoping someone can point me in the right direction with a little issue i'm having. I am making a library that uses functions from the Print class, and LiquidCrystal_I2C class, but I can't seem to differentiate functions from the two. The problem is if I initialize my code to use Serial as opposed to Lcd, and comment out the function setCursor(), the sketch works. However and this is another issue i'm trying to work out, if I uncomment the setCursor function and leave it there, I get the error 'class Print' has no member named 'setCursor', which I know it doesn't because it belongs to the LiquidCrystal_I2C library.

My question is what would be the correct way to go about solving this problem, and how can I tell the code what functions to use based on the constructor "Serial / Lcd"?

class OnewireKeyPad{
Print & port_;
//LiquidCrystal_I2C & port_;
	public:
	    OnewireKeyPad (Print & port) : port_ (port) { }
            //OnewireKeyPad (LiquidCrystal_I2C & port) : port_ (port) { }

You could define two instance variable to hold pointers to the constructor arg that is supplied, and initialise the one that you want to use. At runtime, test whether the pointer is valid before dereferencing it.

e.g.:

void setCursor(...)
{
    if(lcd)
    {
        lcd->setCursor(...);
    }
}

But the fact you need to use constructor args with different signatures makes me suspect that actually you aren't designing a single class - it would end up cleaner if you abstract out the common functionality into a base class and then derive subclasses from that to have the lcd-specific and Print-specific aspects of the behaviour.

I'll give it a try, maybe not tonight i'm kinda tired, but I'll definitely do it tomorrow.

Thanks.

By using a template as a top level class, you can include type specific parts,... for specific types.
You can the inherit a base class which is all the common functionality the two different instantiations can use.

class YourOriginalClass{
  //...
  void Foo(){};
};

template< typename T >
  class OnewireKeyPad : public YourOriginalClass{
    public:
      OnewireKeyPad( T &t ) : port_( t ), YourOriginalClass() {}
      T &port_;
};

template<>
  class OnewireKeyPad< LiquidCrystal_I2C > : public YourOriginalClass{
    public:
      LiquidCrystal_I2C &port_;

      OnewireKeyPad( LiquidCrystal_I2C &lcd ) : port_( lcd ), YourOriginalClass() {}

      void setCursor(){
        port_.setCursor();
     }
};

Then to use:

OnewireKeyPad< Print > oPrint( Serial );
oPrint.Foo();

OnewireKeyPad< LiquidCrystal_I2C > oLCD( LCD );
oLCD.SetCursor();
oLCD.Foo();

Foo() is available to both as it is in the common base, SetCursor is only available through the LiquidCrystal_I2C specialization.

Ok, this is what I tried

//.h file

#ifndef OnewireKeypad_h
#define OnewireKeypad_h

#include <Print.h>
#include <Arduino.h>
#include <LiquidCrystal_I2C.h>

#define NO_KEY '\0'
#define WAITING 0
#define PRESSED 1
#define RELEASED 2
#define HELD 3

#define MAX_KEYS 16

class OnewireKeyPad {

	public:
		void Init(char KP[], uint8_t Rows, uint8_t Cols, uint8_t Pin);
		char Getkey();
		void Calc(double R1, double R2, double voltage);
		void SetHoldTime(unsigned long setH_Time);
		void SetDebounceTime(unsigned long setD_Time);
		uint8_t Key_State();
		bool Readpin();
		void LatchKP();
		bool checkLatchedKey(char _key);
	
	private:
		double values[MAX_KEYS];
		bool latchedKey[MAX_KEYS];
		char _Data[MAX_KEYS];
		char Out[20];
		uint8_t _Rows, _Cols, _Pin;
		char lastKey;
};


template < typename T >
class Display : public OnewireKeyPad 
{
	public:
		Display(T & port) : port_( port ),OnewireKeyPad() {}
		T &port_;
};

template< >
class Display < LiquidCrystal_I2C > : public OnewireKeyPad
{
    public:
        Display( LiquidCrystal_I2C & lcd ) : port_( lcd ),OnewireKeyPad() {}
	LiquidCrystal_I2C & port_;    
};

#endif

Here is the .cpp file

// OnewireKP.cpp

#include "OneWireKeypad.h"

uint8_t SIZE = 16;
unsigned long time = 0,holdTime, startTime, debounceTime;
bool state, lastState = 0, lastRead = 0;


void OnewireKeyPad :: Init(char KP[], uint8_t Rows, uint8_t Cols, uint8_t Pin) 
{
  _Rows = Rows;
  _Cols = Cols;
  _Pin = Pin;
  
  startTime = 0;
  SetDebounceTime(10);
  SetHoldTime(500);
  SIZE = (_Rows * _Cols);
  
  for(byte lidx = 0; lidx < SIZE; lidx++)
  {
    _Data[lidx] = KP[lidx];
    latchedKey[lidx] = 0;
  } 
}

char OnewireKeyPad :: Getkey()
{
  int Reading = analogRead(_Pin);
  if(millis() - startTime > debounceTime)
  {
    for(byte i = 0; i < SIZE; i++)
    {
      if(Reading && Reading <= int(values[(SIZE-1)-i]) + 1.9) 
        return _Data[(SIZE-1)-i];
    }
    startTime = millis();
  }  
  return NO_KEY;
}

void OnewireKeyPad :: Calc(double R1, double R2, double voltage)
{
  uint8_t i = 0;
  for(double R = 0; R < _Rows; R++)
  {
    for(double C = 0; C < _Cols; C++)
    {
      double V = (voltage * R2)/(R2+ (R1*R) + (R2*C));
      values[i] = V * (1023 / voltage); //204.6 = 5V, 310 = 3V
	  
	    //if(_rows == 2)
	      port_->setLcdCursor(0, R );
        //else
          //port_.setLcdCursor(0, R);
       
		
      port_.print(int(values[i])); 
	  port_.print(", ");
      i++;
    }
	 
       port_.println(); 
  }
} 

void OnewireKeyPad :: SetHoldTime(unsigned long setH_Time)
{
  holdTime = setH_Time;
}

void OnewireKeyPad :: SetDebounceTime(unsigned long setD_Time)
{
  debounceTime = setD_Time;
}

uint8_t OnewireKeyPad :: Key_State()
{
  if((state = Readpin()) != lastState)
  {
    lastState = state;
    return (Readpin()? PRESSED : RELEASED);
  }
  //else KP_Hold(500);
  else if(Readpin()){ 
    time = millis();

    while(Readpin())
    {
      if((millis() - time) > holdTime)
      {
        return HELD;
      }
    }
    lastState = 0;
    return RELEASED;
  }
  return WAITING; 

}

bool OnewireKeyPad :: Readpin()
{
  return analogRead(_Pin)? 1 : 0;
}

void OnewireKeyPad :: LatchKP()
{
  char output[20];
  bool PRINT = false;

  char _key = Getkey();
  bool read = _key? 1: 0;

  if(read != lastRead)
  {
    if(read)
    {
      for(int idx = 0; idx < SIZE; idx++)
      {
        if(_key == _Data[idx])
        { 
          latchedKey[idx] = !latchedKey[idx];
          //Serial.println(latchedKey[idx]);
          if(latchedKey[idx])
          {
            sprintf(output, "Key %c was Latched",_key);
            PRINT = true;
          }
          else 
          {
            sprintf(output, "Key %c was Unlatched",_key);
            PRINT = true;
          }
        }
      }
    }
    lastRead = read;
    if(PRINT)
      port_.println(output);
  }
}

bool OnewireKeyPad :: checkLatchedKey(char _key)
{
  for(int idx = 0; idx < SIZE; idx++)
  {
    if(_key == _Data[idx])
      return latchedKey[idx];    
  }
  return false;
}

Sketch:

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <OneWireKeypad.h>

LiquidCrystal_I2C lcd(0x20,20,4); 
Display <LiquidCrystal_I2C> KP(lcd);

char KEYS[]= {
  '1','2','3',//'A',
  '4','5','6',//'B',
  '7','8','9',//'C',
  '*','0','#',//'D'
};

void setup (){
    //Serial.begin(115200);

  KP.Init(KEYS, 4, 3, A0); // (KeyMap, Rows, Columns, Pin)
  KP.Calc(4700, 1000, 5); //(Row value, Col value, Refference voltage)
}
void loop() 
{
  char Key = KP.Getkey();
  if(Key)
    Serial.println(KP.Key_State());
  KP.LatchKP();
}

Errors:

C:\Users\Andrew\Documents\Arduino\libraries\OneWireKeypad\OneWireKeypad.cpp: In member function 'void OnewireKeyPad::Calc(double, double, double)':
C:\Users\Andrew\Documents\Arduino\libraries\OneWireKeypad\OneWireKeypad.cpp:54: error: 'port_' was not declared in this scope
C:\Users\Andrew\Documents\Arduino\libraries\OneWireKeypad\OneWireKeypad.cpp:64: error: 'port_' was not declared in this scope
C:\Users\Andrew\Documents\Arduino\libraries\OneWireKeypad\OneWireKeypad.cpp: In member function 'void OnewireKeyPad::LatchKP()':
C:\Users\Andrew\Documents\Arduino\libraries\OneWireKeypad\OneWireKeypad.cpp:141: error: 'port_' was not declared in this scope

I took your sketch and made a few changes.

To have port_ accessible in the base, it must be defined there. So I made it a template too.

The only other change I had was an '->' to an '.', and the compiler reckons setLcdCursor is not defined in LiquidCrystal_I2C so I commented it out.

The library is part of the sketch in my test ( all files in the same folder, no lib ).

test_for_guy.ino (525 Bytes)

test.h (1.21 KB)

test.cpp (3.11 KB)

Also there are a few things I forgot to mention.

If you do not need unique functionality for the template types, then you can use the base class directly.
You may also have to add in a 'using' for port_ in the derived classes due to the base class being a template:

template < typename T >
class Display : public OnewireKeyPad< T >
{
	public:
		using OnewireKeyPad< T >::port_;
		Display(T & port) : OnewireKeyPad< T >( port ) {}
};

template< >
class Display < LiquidCrystal_I2C > : public OnewireKeyPad< LiquidCrystal_I2C >
{
    public:
        using OnewireKeyPad< LiquidCrystal_I2C >::port_;
        Display( LiquidCrystal_I2C & lcd ) : OnewireKeyPad< LiquidCrystal_I2C >( lcd ) {}  
};

Everything but this below works.

if(port_ == lcd) // (T == lcd) // (port_ == LiquidCrystal_I2C) //(T == LiquidCrystal_I2C)
port_.print(); // lcd
else
port_.println(); //serial

T is type LiquidCrystal_I2C, lcd is a variable of type LiquidCrystal_I2C.

you are effectively comparing something like:

int a;
if( int == a )

If you wish to do something based on a type, I have a very simple class you can use.

template < typename T, typename U > struct IsSameType{ enum { Value = false }; };
template < typename T > struct IsSameType< T, T > { enum { Value = true }; };

It sets 'Value' to 1 if the types are the same, 0 otherwise.

Then you can do this:

if( IsSameType< T, LiquidCrystal_I2C >::Value ){
  port_.print();
}else{
  port_.println();
}

That still didn't work.

.cpp

template< typename T>
void OnewireKeyPad< T > :: Calc(double R1, double R2, double voltage)
{
  uint8_t i = 0;
  for(double R = 0; R < _Rows; R++)
  {
    for(double C = 0; C < _Cols; C++)
    {
      double V = (voltage * R2)/(R2+ (R1*R) + (R2*C));
      values[i] = V * (1023 / voltage); //204.6 = 5V, 310 = 3V 
	  
      if(IsSameType< T, LiquidCrystal_I2C >::Value) 
	     port_.setCursor(0, R);	
		 
      port_.print(int(values[i])); 
	  port_.print(", ");
      i++;
    }
    if( IsSameType< T, LiquidCrystal_I2C >::Value == 0 )
       port_.println();
  }
}

Sketch

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <OneWireKeypad.h>

//LiquidCrystal_I2C lcd(0x20,20,4); 
Display <Print> KP(Serial);

char KEYS[]= {
  '1','2','3',//'A',
  '4','5','6',//'B',
  '7','8','9',//'C',
  '*','0','#',//'D'
};

void setup (){
    Serial.begin(115200);
  //lcd.init();                      // initialize the lcd 
  //lcd.backlight();
  KP.Init(KEYS, 4, 3, A0); // (KeyMap, Rows, Columns, Pin)
  KP.Calc(4700, 1000, 5); //(Row value, Col value, Refference voltage)
  delay(2000);
  //lcd.clear();
}
void loop() {
  char Key = KP.Getkey();
  if(Key){
    //Serial.setCursor(0,0);
    Serial.print(KP.Key_State());
  }
  //lcd.setCursor(0,1);
    KP.LatchKP();
}

Error

C:\Users\Andrew\Documents\Arduino\libraries\OneWireKeypad\OneWireKeypad.cpp: In member function 'void OnewireKeyPad::Calc(double, double, double) [with T = Print]':
C:\Users\Andrew\Documents\Arduino\libraries\OneWireKeypad\OneWireKeypad.cpp:166: instantiated from here
C:\Users\Andrew\Documents\Arduino\libraries\OneWireKeypad\OneWireKeypad.cpp:63: error: 'class Print' has no member named 'setCursor'

If you can't separate the unique parts into the derived classes, you could use a virtual member for the LCD version.

template< typename T >
class OnewireKeyPad {

	public:
                OnewireKeyPad( T &port ) :  port_( port ) {port_.println("asd");}
		void Init(char KP[], uint8_t Rows, uint8_t Cols, uint8_t Pin);
		char Getkey();
		void Calc(double R1, double R2, double voltage);
		void SetHoldTime(unsigned long setH_Time);
		void SetDebounceTime(unsigned long setD_Time);
		uint8_t Key_State();
		bool Readpin();
		void LatchKP();
		bool checkLatchedKey(char _key);


        protected:
          
          virtual void SetCursor( int, int ) { /*Do nothing for unknown types*/ }
          T &port_;
	
	private:
		double values[MAX_KEYS];
		bool latchedKey[MAX_KEYS];
		char _Data[MAX_KEYS];
		char Out[20];
		uint8_t _Rows, _Cols, _Pin;
		char lastKey;

        
};


template < typename T >
class Display : public OnewireKeyPad< T >
{
	public:
		Display(T & port) : OnewireKeyPad< T >( port ) {}
};

template< >
class Display < LiquidCrystal_I2C > : public OnewireKeyPad< LiquidCrystal_I2C >
{
    public:
        using OnewireKeyPad< LiquidCrystal_I2C >::port_;
        void SetCursor( int a, int b ) { port_.setCursor( a, b ); }
        Display( LiquidCrystal_I2C & lcd ) : OnewireKeyPad< LiquidCrystal_I2C >( lcd ) {}  
};

#endif
template< typename T>
void OnewireKeyPad< T > :: Calc(double R1, double R2, double voltage)
{
  uint8_t i = 0;
  for(double R = 0; R < _Rows; R++)
  {
    for(double C = 0; C < _Cols; C++)
    {
      double V = (voltage * R2)/(R2+ (R1*R) + (R2*C));
      values[i] = V * (1023 / voltage); //204.6 = 5V, 310 = 3V 
	  
      SetCursor(0, R);	
		 
      port_.print(int(values[i])); 
	  port_.print(", ");
      i++;
    }
    if( IsSameType< T, LiquidCrystal_I2C >::Value == 0 )
       port_.println();
  }
}

That worked beautifully, I would have never thought of making the function virtual.

Thank you again.

The library is made to work with these keypads HERE
resistors A,B,C = 4700 ohms
resistors D,E,F = 1000 ohms

However, it will calculate the values based on the resistors used, so even if you don't use 4k7 and 1k resistors, it will adjust the code accordingly.

No worries, also I didn't mention: You may have noticed the templates are in a cpp file which is not very common.

To extend your class for types other than Print, or LiquidCrystal_I2C you have to add in a specification at the bottom of the file like I have already for the two current classes:

template class OnewireKeyPad< Print >;
template class OnewireKeyPad< LiquidCrystal_I2C >;

Otherwise the alternative is to place all the code in the header making it available for any type.

Thanks, and yea I did notice the templates at the bottom. It works great, the only issue I see is to get it to compile, you need to include LiquidCrystal_I2C in the sketch, even if you don't have a I2C Lcd. I'll see if there is anyway around that and if not then I'll just include it in the library folder if anyone wants to use it.

The whole point to the library was to give the option of using a Lcd or the Serial monitor and have the compiler figure out what the code needs to work.

I also made one permanent change, and that was in the Calc function. The keypad works great at 5 Volts, but if you use 3.3 Volts, the analog readings become very unstable and throws everything off. So I decided to keep it at 5 Volts.

Thanks again.

To remove any requirements for the LCD, you can switch the logic to work with the Print library as it is a staple to Arduino installations.

So the default specialization is for the LCD and uses the virtual member, and the specific version is for the Print library.
Then the class does not need to know about the LCD, however this will require the code in the header file.

You can also do this too ( might be better ):

struct MissingType{};

#ifdef LiquidCrystal_I2C_h 
  typedef LiquidCrystal_I2C LCDTYPE;
#else
  typedef MissingType LCDTYPE;
#endif

template < typename T >
class Display : public OnewireKeyPad< T >
{
	public:
		Display(T & port) : OnewireKeyPad< T >( port ) {}
};

template< >
class Display < LCDTYPE > : public OnewireKeyPad< LCDTYPE >
{
    public:
        using OnewireKeyPad< LCDTYPE >::port_;
        void SetCursor( int a, int b ) { port_.setCursor( a, b ); }
        Display( LCDTYPE & lcd ) : OnewireKeyPad< LCDTYPE >( lcd ) {}  
};

in the cpp:

template class OnewireKeyPad< Print >;
template class OnewireKeyPad< LCDTYPE >;

This should make the LCD available if the user includes it in the sketch first... Should...

EDIT: scrap that, GCC 4.3.7 does not support SFINAE, oh well. My previous post should be a working answer.

I had a brain wave, the solution is nice, however it won't work with code in the cpp. It is compiled before it sees your sketch so LiquidCrystal has to be defined to be usable.

What I had was a dummy class to add in the missing functions of the LCD class:

struct MissingType{
    void setCursor( int a, int b ){}
    template< typename T > size_t print( const T &t ){}
    template< typename T > size_t println( const T &t ){}
    template< typename T, typename U > size_t print( const T &t, const U &u ){}
    template< typename T, typename U > size_t println( const T &t, const U &u ){}    
};

#ifdef LiquidCrystal_I2C_h
  typedef LiquidCrystal_I2C LCDTYPE;
#else
  typedef MissingType LCDTYPE;
#endif

To make your library dependent on the sketch ( compiled later ) you need a template, which you've already got. So simply copy the cpp file onto the end of your .h and it works fine.

After I did this, my solution above works fine. Most importantly, you do not need the LiquidCrystal_I2C library included now!
Without the LCDTYPE fix above, you will receive errors on things like IsSameType< T, LiquidCrystal_I2C >::Value

Also as you get errors from missing functions, simply add a dummy one into the MissingType struct. These functions aren't used, they simply stop the compiler from spitting out x undefined errors. You can see this is true as the functions do not return a value, which is an error if the code was used, they don't really need the {} either.

Here is the .h ( no .cpp now ).

#ifndef OnewireKeypad_h
#define OnewireKeypad_h

#include <Arduino.h>

#define NO_KEY '\0'
#define WAITING 0
#define PRESSED 1
#define RELEASED 2
#define HELD 3
template < typename T, typename U > struct IsSameType{ enum { Value = false }; };
template < typename T > struct IsSameType< T, T > { enum { Value = true }; };
#define MAX_KEYS 16

template< typename T >
  class OnewireKeyPad {
    public:
      OnewireKeyPad( T &port ) :  port_( port ) {port_.println("asd");}
      void Init(char KP[], uint8_t Rows, uint8_t Cols, uint8_t Pin);
      char Getkey();
      void Calc(double R1, double R2, double voltage);
      void SetHoldTime(unsigned long setH_Time);
      void SetDebounceTime(unsigned long setD_Time);
      uint8_t Key_State();
      bool Readpin();
      void LatchKP();
      bool checkLatchedKey(char _key);
    protected:
      virtual void SetCursor( int, int ) { /*Do nothing for unknown types*/ }
      T &port_;
    private:
      double values[MAX_KEYS];
      bool latchedKey[MAX_KEYS];
      char _Data[MAX_KEYS];
      char Out[20];
      uint8_t _Rows, _Cols, _Pin;
      char lastKey;
};

struct MissingType{
  void setCursor( int a, int b ){}
  template< typename T > size_t print( const T &t ){}
  template< typename T > size_t println( const T &t ){}
  template< typename T, typename U > size_t print( const T &t, const U &u ){}
  template< typename T, typename U > size_t println( const T &t, const U &u ){}    
};

#ifdef LiquidCrystal_I2C_h
  typedef LiquidCrystal_I2C LCDTYPE;
#else
  typedef MissingType LCDTYPE;
#endif

template < typename T >
  class Display : public OnewireKeyPad< T >{
    public:
      Display(T & port) : OnewireKeyPad< T >( port ){}
};

template< >
  class Display < LCDTYPE > : public OnewireKeyPad< LCDTYPE >{
    public:
      using OnewireKeyPad< LCDTYPE >::port_;
      void SetCursor( int a, int b ) { port_.setCursor( a, b ); }
      Display( LCDTYPE & lcd ) : OnewireKeyPad< LCDTYPE >( lcd ) {}  
};

uint8_t SIZE = 16;
unsigned long time = 0,holdTime, startTime, debounceTime;
bool state, lastState = 0, lastRead = 0;

template< typename T >
void OnewireKeyPad< T > :: Init(char KP[], uint8_t Rows, uint8_t Cols, uint8_t Pin) 
{
  _Rows = Rows;
  _Cols = Cols;
  _Pin = Pin;
  
  startTime = 0;
  SetDebounceTime(10);
  SetHoldTime(500);
  SIZE = (_Rows * _Cols);
  
  for(byte lidx = 0; lidx < SIZE; lidx++)
  {
    _Data[lidx] = KP[lidx];
    latchedKey[lidx] = 0;
  } 
}

template< typename T >
char OnewireKeyPad< T > :: Getkey()
{
  
  int Reading = analogRead(_Pin);
  if(millis() - startTime > debounceTime)
  {
    for(byte i = 0; i < SIZE; i++)
    {
      if(Reading && Reading <= int(values[(SIZE-1)-i]) + 1.9) 
        return _Data[(SIZE-1)-i];
    }
    startTime = millis();
  }  
  return NO_KEY;
}

template< typename T>
void OnewireKeyPad< T > :: Calc(double R1, double R2, double voltage)
{
  uint8_t i = 0;
  for(double R = 0; R < _Rows; R++)
  {
    for(double C = 0; C < _Cols; C++)
    {
      double V = (voltage * R2)/(R2+ (R1*R) + (R2*C));
      values[i] = V * (1023 / voltage); //204.6 = 5V, 310 = 3V 
    
      SetCursor(0, R);  
     
      port_.print(int(values[i])); 
    port_.print(", ");
      i++;
    }
    if( IsSameType< T, LCDTYPE >::Value == 0 )
       port_.println( "HI" );
  }
}

template< typename T >
void OnewireKeyPad< T > :: SetHoldTime(unsigned long setH_Time)
{
  holdTime = setH_Time;
}

template< typename T >
void OnewireKeyPad< T > :: SetDebounceTime(unsigned long setD_Time)
{
  debounceTime = setD_Time;
}

template< typename T >
uint8_t OnewireKeyPad< T > :: Key_State()
{
  if((state = Readpin()) != lastState)
  {
    lastState = state;
    return (Readpin()? PRESSED : RELEASED);
  }
  //else KP_Hold(500);
  else if(Readpin()){ 
    time = millis();

    while(Readpin())
    {
      if((millis() - time) > holdTime)
      {
        return HELD;
      }
    }
    lastState = 0;
    return RELEASED;
  }
  return WAITING; 

}

template< typename T >
bool OnewireKeyPad< T > :: Readpin()
{
  return analogRead(_Pin)? 1 : 0;
}
template< typename T >
void OnewireKeyPad<T> :: LatchKP()
{
  char output[20];
  bool PRINT = false;

  char _key = Getkey();
  bool read = _key? 1: 0;

  if(read != lastRead)
  {
    if(read)
    {
      for(int idx = 0; idx < SIZE; idx++)
      {
        if(_key == _Data[idx])
        { 
          latchedKey[idx] = !latchedKey[idx];
          //Serial.println(latchedKey[idx]);
          if(latchedKey[idx])
          {
            sprintf(output, "Key %c was Latched",_key);
            PRINT = true;
          }
          else 
          {
            sprintf(output, "Key %c was Unlatched",_key);
            PRINT = true;
          }
        }
      }
    }
    lastRead = read;
    if(PRINT)
      port_.println(output);
  }
}

template< typename T >
bool OnewireKeyPad< T > :: checkLatchedKey(char _key)
{
  for(int idx = 0; idx < SIZE; idx++)
  {
    if(_key == _Data[idx])
      return latchedKey[idx];    
  }
  return false;
}
#endif

EDIT: also you will need to do something with the global vars that are now in the .h.
Wrap them in a struct and make them static or mark them extern and define them in a cpp file.

They are fine now, but when you include your lib into multiple locations, problems may occur.

My copy+ replace borked, if you copied my code already change:
#ifdef LCDTYPE_h
To:
#ifdef LiquidCrystal_I2C_h

fixed above now.

When I made a test .h file, and replaced every OnewireKeyPad with OWKPtest, I ran a test sketch.

#include<OWKPtest.h>

Display <Print> KP(Serial);

char KEYS[]= {
  '1','2','3',//'A',
  '4','5','6',//'B',
  '7','8','9',//'C',
  '*','0','#',//'D'
};

void setup()
{
  Serial.begin(115200);
  KP.Init(KEYS, 4, 3, A0); // (KeyMap, Rows, Columns, Pin)
  KP.Calc(4700, 1000); //(Row value, Col value)
  delay(2000);
}

void loop() 
{
  char Key = KP.Getkey();
  if(Key){
    Serial.println(KP.Key_State());
  }
  KP.LatchKey();
}

And got this error.

test:3: error: expected constructor, destructor, or type conversion before '<' token
test.ino: In function 'void setup()':
test:16: error: 'KP' was not declared in this scope
test.ino: In function 'void loop()':
test:22: error: 'KP' was not declared in this scope

Does this make sense? I don't think its the global variable doing this, otherwise I would see them come up in the error message.

Something is not defined/defined correctly, or there is an error like a missing ; on a class, or missing }. Can you post the file OWKPtest.h

EDIT: you won't see errors on the globals until the header is included into multiple .cpp files. Even then you might not as they are protected by #ifdef guards in the header.

I have also seen this error when trying to use a newly installed library without restarting the IDE.

#ifndef OWKPtest_h
#define OWKPtest_h

#include <Arduino.h>

#define NO_KEY '\0'
#define WAITING 0
#define PRESSED 1
#define RELEASED 2
#define HELD 3
template < typename T, typename U > struct IsSameType{ enum { Value = false }; };
template < typename T > struct IsSameType< T, T > { enum { Value = true }; };
#define MAX_KEYS 16

template< typename T >
  class OWKPtest {
    public:
      OWKPtest( T &port ) :  port_( port ) {port_.println("asd"); }
      void Init(char KP[], uint8_t Rows, uint8_t Cols, uint8_t Pin);
      char Getkey();
      void Calc(double R1, double R2, double voltage);
      void SetHoldTime(unsigned long setH_Time);
      void SetDebounceTime(unsigned long setD_Time);
      uint8_t Key_State();
      bool Readpin();
      void LatchKP();
      bool checkLatchedKey(char _key);
    protected:
      virtual void SetCursor( int, int ) { /*Do nothing for unknown types*/ }
      T &port_;
    private:
      double values[MAX_KEYS];
      bool latchedKey[MAX_KEYS];
      char _Data[MAX_KEYS];
      char Out[20];
      uint8_t _Rows, _Cols, _Pin;
      char lastKey;
};

struct MissingType{
  void setCursor( int a, int b ){}
  template< typename T > size_t print( const T &t ){}
  template< typename T > size_t println( const T &t ){}
  template< typename T, typename U > size_t print( const T &t, const U &u ){}
  template< typename T, typename U > size_t println( const T &t, const U &u ){}    
};

#ifdef LiquidCrystal_I2C_h
  typedef LiquidCrystal_I2C LCDTYPE;
#else
  typedef MissingType LCDTYPE;
#endif

template < typename T >
  class Display : public OWKPtest< T >{
    public:
      Display(T & port) : OWKPtest< T >( port ){}
};

template< >
  class Display < LCDTYPE > : public OWKPtest< LCDTYPE >{
    public:
      using OWKPtest< LCDTYPE >::port_;
      void SetCursor( int a, int b ) { port_.setCursor( a, b ); }
      Display( LCDTYPE & lcd ) : OWKPtest< LCDTYPE >( lcd ) {}  
};

uint8_t SIZE = 16;
unsigned long time = 0,holdTime, startTime, debounceTime;
bool state, lastState = 0, lastRead = 0;

template< typename T >
void OWKPtest< T > :: Init(char KP[], uint8_t Rows, uint8_t Cols, uint8_t Pin) 
{
  _Rows = Rows;
  _Cols = Cols;
  _Pin = Pin;
  
  startTime = 0;
  SetDebounceTime(10);
  SetHoldTime(500);
  SIZE = (_Rows * _Cols);
  
  for(byte lidx = 0; lidx < SIZE; lidx++)
  {
    _Data[lidx] = KP[lidx];
    latchedKey[lidx] = 0;
  } 
}

template< typename T >
char OWKPtest< T > :: Getkey()
{
  
  int Reading = analogRead(_Pin);
  if(millis() - startTime > debounceTime)
  {
    for(byte i = 0; i < SIZE; i++)
    {
      if(Reading && Reading <= int(values[(SIZE-1)-i]) + 1.9) 
        return _Data[(SIZE-1)-i];
    }
    startTime = millis();
  }  
  return NO_KEY;
}

template< typename T>
void OWKPtest< T > :: Calc(double R1, double R2, double voltage)
{
  uint8_t i = 0;
  for(double R = 0; R < _Rows; R++)
  {
    for(double C = 0; C < _Cols; C++)
    {
      double V = (voltage * R2)/(R2+ (R1*R) + (R2*C));
      values[i] = V * (1023 / voltage); //204.6 = 5V, 310 = 3V 
    
      SetCursor(0, R);  
     
      port_.print(int(values[i])); 
    port_.print(", ");
      i++;
    }
    if( IsSameType< T, LCDTYPE >::Value == 0 )
       port_.println( "HI" );
  }
}

template< typename T >
void OWKPtest< T > :: SetHoldTime(unsigned long setH_Time)
{
  holdTime = setH_Time;
}

template< typename T >
void OWKPtest< T > :: SetDebounceTime(unsigned long setD_Time)
{
  debounceTime = setD_Time;
}

template< typename T >
uint8_t OWKPtest< T > :: Key_State()
{
  if((state = Readpin()) != lastState)
  {
    lastState = state;
    return (Readpin()? PRESSED : RELEASED);
  }
  //else KP_Hold(500);
  else if(Readpin()){ 
    time = millis();

    while(Readpin())
    {
      if((millis() - time) > holdTime)
      {
        return HELD;
      }
    }
    lastState = 0;
    return RELEASED;
  }
  return WAITING; 

}

template< typename T >
bool OWKPtest< T > :: Readpin()
{
  return analogRead(_Pin)? 1 : 0;
}
template< typename T >
void OWKPtest<T> :: LatchKP()
{
  char output[20];
  bool PRINT = false;

  char _key = Getkey();
  bool read = _key? 1: 0;

  if(read != lastRead)
  {
    if(read)
    {
      for(int idx = 0; idx < SIZE; idx++)
      {
        if(_key == _Data[idx])
        { 
          latchedKey[idx] = !latchedKey[idx];
          //Serial.println(latchedKey[idx]);
          if(latchedKey[idx])
          {
            sprintf(output, "Key %c was Latched",_key);
            PRINT = true;
          }
          else 
          {
            sprintf(output, "Key %c was Unlatched",_key);
            PRINT = true;
          }
        }
      }
    }
    lastRead = read;
    if(PRINT)
      port_.println(output);
  }
}

template< typename T >
bool OWKPtest< T > :: checkLatchedKey(char _key)
{
  for(int idx = 0; idx < SIZE; idx++)
  {
    if(_key == _Data[idx])
      return latchedKey[idx];    
  }
  return false;
}
#endif