How to parametrize object instantiation inside a library

Hi guys,

I’m trying to figure out how to instantiate a class inside a library I intend to write while passing parameters to the instantiated class in question from within the “wrapper class”. I.e. my new library creates a wrapper class that should give a birth (=instantiate) a class from a sub-library. The constructor of this newly created object takes parameter – and I want to fill in this parameter with a value passed into the wrapper class/object upon its creation.

The ultimate goal is to write my own library which takes care of displaying a menu-like structure on a very standard 16x2 LCD display. Hence for handling the LCD stuff I will be using LiquidCrystal library.

I need to instantiate the LCD object inside my “LCDMenu” library. In order to make my LCDMenu library versatile I obviously want to avoid “harcdoding” LCD pins into my library – I want to pass on LCD pins to the object/class I create inside my LCDMenu library which – in turns – shall instantiate the LCD object with correct pin values (it received upon its creation).

In order to understand the way I created the following test sketch with two simple libraries: class defined in LibA does nothing else then incrementing internal count by 1 upon calling IncrementArg1() function. The initial value is passed into it within class constructor.

LibB does the same plus tries to instantiate a class from LibA. A private pointer to the type of LibA class is defined in the LibA class private section – its name is _nestedlibAprivate. The LibA class is instantiated in the class LibB begin function and is declared as static to survive the begin function termination. So far so good – it compiles well.

However an attempt to pass on the pointer to the newly instantiated class into the private variable fails (currenlty commented out in the code) – an error of “undefined reference to `LibB::_nestedlibAprivate'” is raised during compilation attempt. This step is however essential to be able to work with the LibA object further in other LibB functions.

Any idea why? (And I must admit part of my inability to decipher the issue on my own is derived from the fact I don’t have clear understanding on the use/need of the begin() function).

#include "LibA.h"
#include "LibB.h"

LibA myliba(11);
LibB mylibb(33);

void setup() {
  Serial.begin(115200);
  mylibb.begin(66);
}

void loop() {
  myliba.IncrementArg1();
  Serial.print("LibA value: ");
  Serial.println(myliba.WhatisArg1());

  mylibb.IncrementArg1();
  Serial.print("LibB value: ");
  Serial.println(mylibb.WhatisArg1());
  
  delay(5000);
}
#ifndef LibA_h
#define LibA_h

#include "Arduino.h"

class LibA
{
  public:
    LibA(int LibAarg1);
    void IncrementArg1();
    int WhatisArg1();
  private:
    int _Ainternal;
};

#endif
#include "Arduino.h"
#include "LibA.h"

LibA::LibA(int LibAarg1)
{
  _Ainternal = LibAarg1;
}

void LibA::IncrementArg1()
{
  _Ainternal++;
}

int LibA::WhatisArg1() {
  return _Ainternal;
}
#ifndef LibB_h
#define LibB_h

#include "Arduino.h"
#include "LibA.h"


class LibB
{
  public:
  
    LibB(int LibBarg1);
    void begin(int seedint);
    
    void IncrementArg1();
    int WhatisArg1();

  private:
    int _Binternal;
    static LibA* _nestedlibAprivate;  
};

#endif
#include "Arduino.h"
#include "LibB.h"
#include "LibA.h"

LibB::LibB(int LibBarg1)
{
  _Binternal = LibBarg1;
}

void LibB::IncrementArg1()
{
  _Binternal=_Binternal+1;
  Serial.print("LibB internal increment: ");
  Serial.println(_Binternal);  

//  _nestedlibAprivate->IncrementArg1();
//  Serial.print("Nested LibA IncrementArg1: ");
//  Serial.println(_nestedlibAprivate->WhatisArg1());  
}

int LibB::WhatisArg1() {
  return _Binternal;
}

void LibB::begin(int seedint)
{
  static LibA s_nestedlibA(seedint);
//  _nestedlibAprivate = &s_nestedlibA;
}

why not just build a class that inherits from the LCD library? It makes sense as you wouldn't have the new class without the existing LCD library you are using (aka 1:1 dependency).

Simply add new methods to your LCD base class.

Create a constructor that passes your new constructor arguments along with the args required for the LCD.

begin() functions are typically used to set class object parameters that wouldn't be necessarily guaranteed to form properly as the compiler forms all of the objects.

  private:
    int _Binternal;
    static LibA* _nestedlibAprivate;

Why is LibA static?

  static LibA s_nestedlibA(seedint);
//  _nestedlibAprivate = &s_nestedlibA;

Why are you defining another variable, libA, that is LOCAL to begin? While making it static means that it does not go away, this variable is NOT the same variable as the member variable.

What you SHOULD have in this method is:

   _nestedlibAprivate = new LibA(seedint);

BulldogLowell:
why not just build a class that inherits from the LCD library?

While certainly not a wrong direction to explore it won't solve my particular issue as I need to instantiate other objects than just LCD from whitin my new class (e.g. I seek to include support for DS3231-based RTC too).

Thanks for the effort anyhow!

PaulS:
Why is LibA static? Why are you defining another variable, libA, that is LOCAL to begin? While making it static means that it does not go away, this variable is NOT the same variable as the member variable.

The idea was to have a pointer to LibA class as a private variable of LibB class, i.e. the LibA object would not be instantiated upon LibB creation (since only pointer variable comes to existence along with LibB instantiation).

Only once the begin() function would be called the local variable of the LibA type would come to existence = LibA object would be instantiated. Since instantiated as "static" it would survive the end of begin() function execution. And before leaving the scope of begin() function the pointer to this newly instantiated object would be passed on into the private variable = pointer - of the LibB class. Hence the instantiated object survives (since being declared as static) and I retain access to this LibA object from within other functions of LibB through the pointer.

PaulS:
What you SHOULD have in this method is:

   _nestedlibAprivate = new LibA(seedint);

The sketch compiles with "undefined reference to `LibB::_nestedlibAprivate'" error after the change. Thanks for pointing me out in the direction of the "new" function, I'll explore around the forum/net how to properly use it.

The sketch compiles with "undefined reference to `LibB::_nestedlibAprivate'" error

You made some code changes that you did not post. How are we supposed to guess what you did wrong?

Zed42:
The idea was to have a pointer to LibA class as a private variable of LibB class, i.e. the LibA object would not be instantiated upon LibB creation (since only pointer variable comes to existence along with LibB instantiation).

You may be over-thinking it!

So you are creating a class that inherits from (two: LCD and RTC) base classes.

As an example, I created a Zoo class that inherits from my Pig and Cow base classes.

class Pig {
  public:
    Pig(){Serial.println("Oink");};
    Pig(int numOinks){for(int i = 0; i < numOinks; i++)Serial.println("Oink");};
    void oink(void){Serial.println("Oink");};
    void poo(){crap();};
  private:
    void crap(){Serial.println("Pig Poo");};
};

class Cow {
  public:
    Cow(){Serial.println("Moo");};
    Cow(int numMoos){for(int i = 0; i < numMoos; i++)Serial.println("Moo");};
    void moo(void){Serial.println("Moo");};
    void poo(){crap();};
  private:
    void crap(){Serial.println("Cow Poo");};
};

class Zoo : public Pig, public Cow {  // Take note of the order here and how it affects the construction of the two base classes
  public:
    Zoo(){};
    Zoo(int numOinks, int numMoos):Pig(numOinks), Cow(numMoos){}; // constructor may pass args to the base classes
    using Cow::poo; // if you happen to have two member functions like-named, you can choose one like this
};


void setup() 
{
  Serial.begin(9600);
  Zoo zoo(3, 1);
  Serial.print(F("Making my zoo Oink: "));
  zoo.oink();
  Serial.print(F("Making my zoo Moo: "));
  zoo.moo();
  Serial.print(F("and if my zoo poos, I get "));
  zoo.poo();
}

void loop() {
  // put your main code here, to run repeatedly:

}

@BulldogLowell: in my (still apparently very limited) mental concept of object programing I will prefer not to mix (traverse) two completely different classes but rather to have my own and instantiate the two objects I need to work with inside that my class.

But thanks for the example in any case - it pushes my knowledge horizons a bit further and for sure will come handy one day (soon)!

@PaulS: my apology.

Initially I only replaced the code of the "LibB::begin(int seedint)" method with your suggested code "_nestedlibAprivate = new LibA(seedint);"

Apparently the problem was that I left the internal pointer variable "_nestedlibAprivate" declaration as "static". Once this was remedied it now works perfectly to my expectation. Thanks a lot for effort and pointing me in the right direction.

For the reference of others coming across this post I include the complete code again in working version. It is now enriched with some serial.prints (some of them redundant) to see what's going on internally in the code.

The way I now understand it: the "new" function (?) instantiates new object and returns pointer to it. The pointer in my case is stored within the LibB internal variable _nestedlibAprivate.

Main body:

#include "LibA.h"
#include "LibB.h"

LibA myliba(11);
LibB mylibb(33);

void setup() {
  Serial.begin(115200);
  mylibb.begin(66);
}

void loop() {
  myliba.IncrementArg1();
  Serial.print("LibA value: ");
  Serial.println(myliba.WhatisArg1());

  mylibb.IncrementArg1and2();
  Serial.print("LibB value: ");
  Serial.println(mylibb.WhatisArg1());
  Serial.print("LibB nested LibA value: ");
  Serial.println(mylibb.WhatisArg2());
  
  delay(5000);
}

LibA.h

#ifndef LibA_h
#define LibA_h

#include "Arduino.h"

class LibA
{
  public:
    LibA(int LibAarg1);
    void IncrementArg1();
    int WhatisArg1();
  private:
    int _Ainternal;
};

#endif

LibA.cpp

#include "Arduino.h"
#include "LibA.h"

LibA::LibA(int LibAarg1)
{
  _Ainternal = LibAarg1;
}

void LibA::IncrementArg1()
{
  _Ainternal++;
}

int LibA::WhatisArg1() {
  return _Ainternal;
}

LibB.h

#ifndef LibB_h
#define LibB_h

#include "Arduino.h"
#include "LibA.h"


class LibB
{
  public:
  
    LibB(int LibBarg1);
    void begin(int seedint);
    
    void IncrementArg1and2();
    int WhatisArg1();
    int WhatisArg2();
  private:
    int _Binternal;
    LibA* _nestedlibAprivate;   
};

#endif

LibB.cpp

#include "Arduino.h"
#include "LibB.h"
#include "LibA.h"

LibB::LibB(int LibBarg1)
{
  _Binternal = LibBarg1;
}

void LibB::IncrementArg1and2()
{
  _Binternal++;
  Serial.print("LibB internal increment: ");
  Serial.println(_Binternal);  

  _nestedlibAprivate->IncrementArg1();
  Serial.print("Nested LibA IncrementArg1: ");
  Serial.println(_nestedlibAprivate->WhatisArg1());  
}

int LibB::WhatisArg1() {
  return _Binternal;
}

int LibB::WhatisArg2() {
  return _nestedlibAprivate->WhatisArg1();
}

void LibB::begin(int seedint)
{
  _nestedlibAprivate = new LibA(seedint);
}