Class constructor not working as expected

Hello everyone. I'm fairly new to programming (and even newer to making classes for the Arduino) so please be gentle.
I've googled this issue and the closest match I was able to find to my particular problem is an older Arduino forum post found here: Class constructors with parameters - Syntax & Programs - Arduino Forum

My problem.

I am building a new LCD class which itself uses the LiquidCrystal class. It's basically a bolt-on which allows me to split a 2 x 16 character LCD screen in to four quadrants and print up to 8 chars per quadrant. It will have 4 functions in this class, one for each quadrant.
The class will initialise an instance of the LiquidCrystal class called 'lcd' (that is part of the default Arduino libarary) from within the constructor. Inside the constructor it seems to be able to use 'lcd' just fine. But when I put in code to use 'lcd' in a different function [inside the same class] then it gives me the following error:

C:\blah\LCDscreen.cpp: In member function 'void LCDscreen::q1(String)':
C:\blah\LCDscreen.cpp:28: error: '((LCDscreen*)this)->LCDscreen::lcd' does not have class type

Line 28 is the line that contains lcd.setCursor(i,0); as seen in below code snippet.

The code for the class I made is below:

LCDscreen.cpp

#include "Arduino.h"
#include "LCDscreen.h"
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include <LiquidCrystal.h>


LCDscreen::LCDscreen(uint8_t rs, uint8_t rw, uint8_t enable, uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3) {	
	LiquidCrystal lcd = LiquidCrystal(rs, rw, enable, d0, d1, d2, d3); // Pins used for LCD screen
	lcd.begin(16, 2);   // rows, columns.  use 16,2 for a 16x2 LCD, etc. This is statically set and cannot be changed
	lcd.clear();
}

// The screen is split in to four quadrants.  1,2,3,4 (top left, top right, bottom left, bottom right)
// each quadrant contains one piece of information
// Default layout is 'action' in Q1. Q2 is empty. Q3 has time left. Q4 has pressure.

void LCDscreen::q1(String strContent) {
	strContent.trim(); // Remove any white space if exists
	for (int i = 0; i < 8; i++) {
		lcd.setCursor(i,0);
		//lcd.print(" ");
	}	
	//lcd.setCursor(0,0);
	
	if (strContent.length() > 8) {
		char charBuf[50];
		strContent.toCharArray(charBuf, 50);
		String newString;
		for (int i = 0; i < 8; i++) {
			newString += charBuf[i];
			//lcd.print(newString);
		}
	} else {
		//lcd.print(strContent);
	}
}

LCDscreen.h

#ifndef LCDscreen_h
#define LCDscreen_h 

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

#include <inttypes.h>
#include "Print.h"

class LCDscreen
{
  public:
	LCDscreen(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t);
	void q1(String);
	void q2(String);
	void q3(String);;
	void q4(String);
	
    LiquidCrystal lcd(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t);
	
  private:
	String strContent;
	char charBuf[];
	String newString;
	int i;
	
};

#endif

You'll notice in LCDscreen.cpp I've commented out any calls to the 'lcd' variable outside the constructor (eg. //lcd.print(" ");). This is so that the error doesn't repeat itself. If I uncommented those lines then I would get the same error multiple times, one for each call to 'lcd' irrespective of what it is.

Could someone please point me in the right direction here? I notice that in the older forum thread (that I linked at the beginning of my post) it mentions using begin(). I tried using lcd.begin(16, 2); inside the q1 function, but this changed nothing.

Thanks in advance for your help.

lcd is just local in your constructor.

Make it a private class member variable instead.

michael_x:
lcd is just local in your constructor.

Make it a private class member variable instead.

Thanks Michael. I initially had it as private and moved to public, with same results.

..... <truncated>.....
	
  private:
	String strContent;
	char charBuf[];
	String newString;
	int i;
	LiquidCrystal lcd(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t);
	
};

Changing to private makes no difference. Same error.

So, you've changed the code. But, you haven't posted the new code. And, you'd like us to use our crystal balls to determine what the problem is.

Sorry, mine are brass.

In the .h file don't attempt to construct the object:

class LCDscreen
{
  public:
	LCDscreen(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t);
	void q1(String);
	void q2(String);
	void q3(String);
	void q4(String);
	
    LiquidCrystal lcd;   // <--------------- just this
	
  private:
	String strContent;
	char charBuf[];
	String newString;
	int i;
	
};

In the .cpp file do it here:

LCDscreen::LCDscreen(uint8_t rs, uint8_t rw, uint8_t enable, uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3)
  : lcd (rs, rw, enable, d0, d1, d2, d3)   // <------ initialize lcd
{	
	lcd.begin(16, 2);   // rows, columns.  use 16,2 for a 16x2 LCD, etc. This is statically set and cannot be changed
	lcd.clear();
}

(And take out the local declaration).

Also I wouldn't even be doing the lcd.begin in the constructor. Do that somewhere else. Make a "begin" method.

Thanks Nick. I will give this a go when I am home tonight.

PaulS:
So, you've changed the code. But, you haven't posted the new code. And, you'd like us to use our crystal balls to determine what the problem is.

Sorry, mine are brass.

I did post the new code. Have a look at my post and you will see the changes I made. The rest of the code is exactly the same, so I felt no need to re-post it.

jaykard:

PaulS:
So, you've changed the code. But, you haven't posted the new code. And, you'd like us to use our crystal balls to determine what the problem is.

Sorry, mine are brass.

I did post the new code. Have a look at my post and you will see the changes I made. The rest of the code is exactly the same, so I felt no need to re-post it.

That's not the point, the point is an issue/problem/bug is recreated by copy/pasting your entire code into an new sketch pane and hitting compile. Having to read an entire thread to reconstruct the probable state of your code isn't reliable or easy (in the general case), whereas just posting the entire new version is unambiguous and simple to get right for all concerned

MarkT:

jaykard:

PaulS:
So, you've changed the code. But, you haven't posted the new code. And, you'd like us to use our crystal balls to determine what the problem is.

Sorry, mine are brass.

I did post the new code. Have a look at my post and you will see the changes I made. The rest of the code is exactly the same, so I felt no need to re-post it.

That's not the point, the point is an issue/problem/bug is recreated by copy/pasting your entire code into an new sketch pane and hitting compile. Having to read an entire thread to reconstruct the probable state of your code isn't reliable or easy (in the general case), whereas just posting the entire new version is unambiguous and simple to get right for all concerned

Fair point.
Here is the new [and somewhat redundant] code.

You will notice the only difference is the line LiquidCrystal lcd(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t); in LCDscreen.h file is moved from public: to private:. The error remains the same.

I will be trying what Nick suggested (above) tonight and report back, so hopefully all of this below is moot.

C:\blah\LCDscreen.cpp: In member function 'void LCDscreen::q1(String)':
C:\blah\LCDscreen.cpp:28: error: '((LCDscreen*)this)->LCDscreen::lcd' does not have class type

Line 28 is the line that contains lcd.setCursor(i,0); as seen in below code snippet.

The code for the class I made is below:

LCDscreen.cpp

#include "Arduino.h"
#include "LCDscreen.h"
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include <LiquidCrystal.h>


LCDscreen::LCDscreen(uint8_t rs, uint8_t rw, uint8_t enable, uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3) {	
	LiquidCrystal lcd = LiquidCrystal(rs, rw, enable, d0, d1, d2, d3); // Pins used for LCD screen
	lcd.begin(16, 2);   // rows, columns.  use 16,2 for a 16x2 LCD, etc. This is statically set and cannot be changed
	lcd.clear();
}

// The screen is split in to four quadrants.  1,2,3,4 (top left, top right, bottom left, bottom right)
// each quadrant contains one piece of information
// Default layout is 'action' in Q1. Q2 is empty. Q3 has time left. Q4 has pressure.

void LCDscreen::q1(String strContent) {
	strContent.trim(); // Remove any white space if exists
	for (int i = 0; i < 8; i++) {
		lcd.setCursor(i,0);
		//lcd.print(" ");
	}	
	//lcd.setCursor(0,0);
	
	if (strContent.length() > 8) {
		char charBuf[50];
		strContent.toCharArray(charBuf, 50);
		String newString;
		for (int i = 0; i < 8; i++) {
			newString += charBuf[i];
			//lcd.print(newString);
		}
	} else {
		//lcd.print(strContent);
	}
}

LCDscreen.h

#ifndef LCDscreen_h
#define LCDscreen_h 

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

#include <inttypes.h>
#include "Print.h"

class LCDscreen
{
  public:
	LCDscreen(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t);
	void q1(String);
	void q2(String);
	void q3(String);;
	void q4(String);
	

	
  private:
	String strContent;
	char charBuf[];
	String newString;
	int i;
        LiquidCrystal lcd(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t);
	
};

#endif

It compiled without errors, so hopefully all is resolved. :slight_smile:

I tried the suggestion made by Nick.
Now, I must warn you, we are skirting dangerously close to territory I have never entered before.... so there is a very high chance I have done something wrong in the code. I copied it as I saw it without knowing what I was actually doing. :astonished:

Error I see:

C:\blah\LCDscreen.cpp: In constructor 'LCDscreen::LCDscreen(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t)':
C:\blah\LCDscreen.cpp:16: error: class 'LCDscreen' does not have any field named 'lcd'
C:\blah\LCDscreen.cpp:19: error: '((LCDscreen*)this)->LCDscreen::lcd' does not have class type
C:\blah\LCDscreen.cpp:20: error: '((LCDscreen*)this)->LCDscreen::lcd' does not have class type
C:\blah\LCDscreen.cpp: In member function 'void LCDscreen::q1(String)':
C:\blah\LCDscreen.cpp:30: error: '((LCDscreen*)this)->LCDscreen::lcd' does not have class type

LCDscreen.h

#ifndef LCDscreen_h
#define LCDscreen_h
#include "Arduino.h"
#include <LiquidCrystal.h> 
#include <inttypes.h>
#include "Print.h"

class LCDscreen
{
  public:
	LCDscreen(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t);

	void q1(String);
	void q2(String);
	void q3(String);
	void q4(String);

	LiquidCrystal lcd;
	
  private:
	String strContent;
	char charBuf[];
	String newString;
	int i;
	
	
};

#endif

LCDscreen.cpp

#include "Arduino.h"
#include "LCDscreen.h"
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include <LiquidCrystal.h>


LCDscreen::LCDscreen(uint8_t rs, uint8_t rw, uint8_t enable, uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3)

: lcd (rs, rw, enable, d0, d1, d2, d3) // Pins used for LCD screen

{	
	lcd.begin(16, 2);   // rows, columns.  use 16,2 for a 16x2 LCD, etc. This is statically set and cannot be changed
	lcd.clear();
}

void LCDscreen::q1(String strContent) {
	strContent.trim(); // Remove any white space if exists
	for (int i = 0; i < 8; i++) {
		lcd.setCursor(i,0);
		//lcd.print(" ");
	}	
	//lcd.setCursor(0,0);
	
	if (strContent.length() > 8) {
		char charBuf[50];
		strContent.toCharArray(charBuf, 50);
		String newString;
		for (int i = 0; i < 8; i++) {
			newString += charBuf[i];
			//lcd.print(newString);
		}
	} else {
		//lcd.print(strContent);
	}
}

void LCDscreen::q2(String strContent) { }

void LCDscreen::q3(String strContent) { }

void LCDscreen::q4(String strContent) { }

Follow up question. Once I have this class compiling without issue, what is the best way to then instantiate my class ensuring that the instance has the ability to call functions from the inherited class of LiquidCrystal?

I am thinking I would do this inside the actual Arduino code (outside setup() or loop()) using:

LiquidCrystal lcd = LCDscreen(12, 11, 10, 5, 4, 3, 2); // Pins used for LCD screen

Does this seem right to you?

Thanks very much for your help thus far. :slight_smile:

Which code compiled for you Nick? It didn't work for me. Thanks.

What you posted just then compiled with no errors.

Binary sketch size: 2,918 bytes (of a 32,256 byte maximum)

What is your main sketch? Mine is:

#include <LiquidCrystal.h>
#include <Wire.h>

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

  }  // end of setup

void loop () { }

If I omit the first line (the include) then I get:

In file included from LCDscreen.cpp:2:
LCDscreen.h:18: error: 'LiquidCrystal' does not name a type
LCDscreen.cpp: In constructor 'LCDscreen::LCDscreen(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t)':
LCDscreen.cpp:11: error: class 'LCDscreen' does not have any field named 'lcd'
LCDscreen.cpp:14: error: 'lcd' was not declared in this scope
LCDscreen.cpp: In member function 'void LCDscreen::q1(String)':
LCDscreen.cpp:21: error: 'lcd' was not declared in this scope

Hey wow! It worked!

The problem was in my Arduino code I had:

#include <LCDscreen.h>
#include <LiquidCrystal.h>

Once I removed #include <LCDscreen.h> it compiled just fine.
But dont I need to have this line so that I can use the LCDscreen class?

What I have now is: [truncated code]

#include <LiquidCrystal.h>
#include <Timing.h>
#include <ReadWrite.h>
#include <Temp.h>
#include <Chime.h>
#include <Time.h>
#include <math.h>
#include <EEPROM.h>

LiquidCrystal lcd = LCDscreen(12, 11, 10, 5, 4, 3, 2); // Pins used for LCD screen

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

  }  // end of setup

void loop () { }

The error I get is:

project:22: error: 'LCDscreen' was not declared in this scope

hmm interesting. I put the #include <LCDscreen.h> back in to the Arduino code and it gives me a different error.

Error:

project:11: error: conversion from 'LCDscreen' to non-scalar type 'LiquidCrystal' requested

Arduino code:

#include <LCDscreen.h>
#include <LiquidCrystal.h>
#include <Timing.h>
#include <ReadWrite.h>
#include <Temp.h>
#include <Chime.h>
#include <Time.h>
#include <math.h>
#include <EEPROM.h>

LiquidCrystal lcd = LCDscreen(12, 11, 10, 5, 4, 3, 2); // Pins used for LCD screen

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

  }  // end of setup

void loop () { }

This error is very different to the one before. I'm pretty sure ill be able to figure this one out. Googling the error now and trying different things.

Thanks very much for your help Nick et al. :slight_smile:

It compiled OK for me with this:

#include <LiquidCrystal.h>
#include "LCDscreen.h"
#include <Wire.h>

LCDscreen lcd = LCDscreen(12, 11, 10, 5, 4, 3, 2); // Pins used for LCD screen

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

  }  // end of setup

void loop () { }

Note the single quotes on the include and the LCDscreen lcd not LiquidCrystal lcd.

In fact this is better:

LCDscreen lcd (12, 11, 10, 5, 4, 3, 2); // Pins used for LCD screen

Your version had a temporary copy of LCDscreen being assigned to the global variable.

OK, so it seems polymorphism may not be the best way to go about this in Arduino code. My limited coding knowledge is coming from Java-land.... so I can see myself hitting brick walls until I become familiar Processing/Wiring.

Changing my code to create an LCDscreen object rather than using polymorphism gives me the below results.

Compile time error:

C:\Arduino\libraries\LCDscreen\LCDscreen.cpp: In constructor 'LCDscreen::LCDscreen(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t)':
C:\Arduino\libraries\LCDscreen\LCDscreen.cpp:15: error: no matching function for call to 'LiquidCrystal::LiquidCrystal()'
C:\Arduino\libraries\LiquidCrystal440/LiquidCrystal.h:69: note: candidates are: LiquidCrystal::LiquidCrystal(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, void (*)(int8_t))
C:\Arduino\libraries\LiquidCrystal440/LiquidCrystal.h:67: note:                 LiquidCrystal::LiquidCrystal(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t)
C:\Arduino\libraries\LiquidCrystal440/LiquidCrystal.h:65: note:                 LiquidCrystal::LiquidCrystal(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, void (*)(int8_t))
C:\Arduino\libraries\LiquidCrystal440/LiquidCrystal.h:63: note:                 LiquidCrystal::LiquidCrystal(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t)
C:\Arduino\libraries\LiquidCrystal440/LiquidCrystal.h:61: note:                 LiquidCrystal::LiquidCrystal(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t)
C:\Arduino\libraries\LiquidCrystal440/LiquidCrystal.h:59: note:                 LiquidCrystal::LiquidCrystal(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t)
C:\Arduino\libraries\LiquidCrystal440/LiquidCrystal.h:56: note:                 LiquidCrystal::LiquidCrystal(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t)
C:\Arduino\libraries\LiquidCrystal440/LiquidCrystal.h:52: note:                 LiquidCrystal::LiquidCrystal(const LiquidCrystal&)

Arduino code:

#include <LCDscreen.h>
#include <LiquidCrystal.h>
#include <Timing.h>
#include <ReadWrite.h>
#include <Temp.h>
#include <Chime.h>
#include <Time.h>
#include <math.h>
#include <EEPROM.h>

//LiquidCrystal lcd = LCDscreen(12, 11, 10, 5, 4, 3, 2); // Pins used for LCD screen
LCDscreen lcd = LCDscreen(12, 11, 10, 5, 4, 3, 2); // Pins used for LCD screen

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

  }  // end of setup

void loop () { }

LCDscreen.h

#ifndef LCDscreen_h
#define LCDscreen_h

#include "Arduino.h"
#include <LiquidCrystal.h>
#include <inttypes.h>
#include "Print.h"

class LCDscreen
{
  public:
	LCDscreen(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t);
	void q1(String);
	void q2(String);
	void q3(String);
	void q4(String);

	LiquidCrystal lcd;
	
  private:
	String strContent;
	char charBuf[];
	String newString;
	int i;
};

#endif

LCDscreen.cpp

#include "Arduino.h" 
#include "LCDscreen.h" 
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include <LiquidCrystal.h>


LCDscreen::LCDscreen(uint8_t rs, uint8_t rw, uint8_t enable, uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3)

: lcd (rs, rw, enable, d0, d1, d2, d3) // Pins used for LCD screen

{	
	lcd.begin(16, 2);   // rows, columns.  use 16,2 for a 16x2 LCD, etc. This is statically set and cannot be changed
	lcd.clear();
}

void LCDscreen::q1(String strContent) {
	strContent.trim(); // Remove any white space if exists
	for (int i = 0; i < 8; i++) {
		lcd.setCursor(i,0);
		//lcd.print(" ");
	}	
	//lcd.setCursor(0,0);
	
	if (strContent.length() > 8) {
		char charBuf[50];
		strContent.toCharArray(charBuf, 50);
		String newString;
		for (int i = 0; i < 8; i++) {
			newString += charBuf[i];
			//lcd.print(newString);
		}
	} else {
		//lcd.print(strContent);
	}
}

void LCDscreen::q2(String strContent) { }

void LCDscreen::q3(String strContent) { }

void LCDscreen::q4(String strContent) { }

I think now I am back at having an issue with my class code :frowning: If anyone could help it would be greatly appreciated. Thanks.

OK, I tried changing the Arduino code to what Nick said in his reply here Class constructor not working as expected - #16 by nickgammon - Programming Questions - Arduino Forum and receive the same error as I posted here Class constructor not working as expected - #17 by system - Programming Questions - Arduino Forum

The only difference I made was changing the Arduino code to below:

#include <LiquidCrystal.h>
#include "LCDscreen.h"
#include <Wire.h>

LCDscreen lcd (12, 11, 10, 5, 4, 3, 2); // Pins used for LCD screen

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

  }  // end of setup

void loop () { }

The Error, LCDscreen.cpp and LCDscreen.h files are still what you see in the second link I pasted above.

EDIT:
I also tried using LCDscreen lcd = LCDscreen(12, 11, 10, 5, 4, 3, 2); // Pins used for LCD screen and received the same error.

Well, I needed to reboot my PC. After it came back up I loaded the entire code again (from scratch) and this time it compiled! Woo!

Thanks very much for your help everyone, especially Nick :slight_smile: