Understanding Templates with Derived Classes

This seems to achieve what I am looking for... But I don't fully understand 'how' it works.

template <class C>
class Foo {
    public:
  static void printName(){ Serial.println(C::name); }
};  

class Bar : public Foo<Bar> {
  public:
  static const char* name;
}; const char* Bar::name = "Bar";

class Baz : public Foo<Baz> {
  public:
  static const char* name;
}; const char* Baz::name = "Baz";

void setup() {
 Serial.begin(115200);
 Bar::printName();
 Baz::printName();
}

void loop() { }

Why is there not an error when referencing 'name' inside Foo? At that point Bar hasn't been created to declare what name is, and there's no knowing that C:: will even have a var called name.

Does the compiler take it on blind faith that you'll resolve that yourself?

Because you have declared 'name' as public static in both classes Bar and Baz and are invoking using Bar::printName() and Baz::printName(). The base class has no role whatsoever.

I do get an error if I try to create a different derived class that DOESN'T have a 'name':

sketch.ino: In instantiation of 'static void Foo<C>::printName() [with C = Daz]':
sketch.ino:25:7:   required from here
sketch.ino:4:28: error: 'name' is not a member of 'Daz'
   static void printName(){ Serial.println(C::name); }
                            ^~~~~~
Error during build: exit status 1

So the compiler is checking that at least.

Trying it this way made it click a little more for me I guess:

template <class C>
class Foo {
    public:
  static void printName(){ Serial.println(C::name); }
};  

class Bar : public Foo<Bar> {
  public:
  static const char* name;
}; const char* Bar::name = "Bar";

class Baz : public Foo<Baz> {
  public:
  static const char* name;
}; const char* Baz::name = "Baz";

class Daz : public Foo<Daz> {
  public:
  static int name;
}; int Daz::name = 5;

void setup() {
 Serial.begin(115200);
 Bar::printName();
 Baz::printName();
 Daz::printName();
}

void loop() { }

Output:

Bar
Baz
5

The variable 'name' should be declared in the base class and then set by the derived classes.

Like so?

template <class C>
class Foo {
    public:
  static void printName(){ Serial.println(name); }
  static const char* name;
};  

class Bar : public Foo<Bar> {
  public:
};
template<>
const char* Foo<Bar>::name = "Bar";

class Baz : public Foo<Baz> {
  public:
};
template<>
const char* Foo<Baz>::name = "Baz";

void setup() {
 Serial.begin(115200);
 Bar::printName();
 Baz::printName();
}

void loop() { }

This was my original approach I think. If I recall correctly it worked in the Wokwi simulator but not on the real hardware with VSCode/Platformio and a Teensy 4.1. But I'm not with the hardware at the moment so I can't confirm that.

Example. Untested.

class Base
{
    protected:
        char _name[100]
    public:
        Base(const char* name)
        {
            strcpy_s(_name, name, 100); //Check syntax.
        }

        void printName()
        {
            printf("Name: %s", _name);
        }
}

class Bar :: public Base
{
    public:
        //Check syntax
        //Basically we are just invoking the base class constructor with a defaukt name
        Bar() :: Base("BAR")
        {
        }

        //Check syntax
        //Basically we are just invoking the base class constructor with a user supplied name
        Bar(const char* name) :: Base(name)
        {
        }
}

class Bar1 :: public Base
{
public:
        //Check syntax
        //Basically we are just invoking the base class constructor
        Bar1() :: Base("BAR1")
        {
        }

        //Check syntax
        //Basically we are just invoking the base class constructor with a user supplied name
        Bar1(const char* name) :: Base(name)
        {
        }
}

//Declare pointers for later initialisation
Base *b = NULL;
Base *b1 = NULL;

void setup()
{
    b = new Bar(); //Create instance of class Bar
    b1 = new Bar1(); //Create instance of class Bar1

    b -> printName();
    b1 -> printName();

    b = new Bar("1234"); //Create instance of class Bar
    b1 = new Bar1("5678"); //Create instance of class Bar1

    b -> printName();
    b1 -> printName();
}

I'm not certain that particular example would carry across how I need to use it.

I'm looking for the name variable to be shared across all instances of a certain derived class (although unique to each class that derives)... And for 'name' to accessible inside base. The templating isn't related to my attempt to get 'name' to work... It's just that by base class IS a template class, so it's important to include it here.

In that case you will need to make Bar itself a base class of another class, move the _name variable inside it, make it protected static and implement the printName() method in it. Then make classes FooBar derive from Bar This way all your pointers can be of type Base, and initialised as FoorBar().

Okay, I'll have another think/play when I'm back with the hardware and see if that does the trick. Thanks!

Note: All instances of FooBar will share Bar's static variables. So make other classes FooBar1, Foobar2, etc that derive from Bar.

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