0

I'm working on a template class. Not that it matters, but my goal is a list class that will use some static members to maintain a linked list of class objects. Each different class will have it's own list. Not only that, but each class will have a compile-time allocated pool of available instances of the object rather than pulling them from a common heap. I'm working on a bare-metal system with limited resources. Part of the reason for avoiding a heap is to avoid fragmentation in an embedded system that will run for long periods of time. Another part is determinism. I don't want the number of ObjectA allocated to interact with the number of ObjectB being allocated and I want a predictable number of each to be available. In real-time embedded systems, predictability and consistency are often more important than raw performance. I do this all the time in C but I'm trying to graduate to C++ while keeping the efficiencies of C.

Here's a pared-down example that illustrates the crux of my C++ problem.

template <class T>
class ListOf<T>
{
public:
  static ListOf<T> *method();
};

I want to be able to derive classes that have all of the methods and properties of the ListOf template class plus additional "payload" properties. I thought I could do this by subclassing a class that is derived from the template:

class Something
{
// nothing here.  This is only used to let me give a unique name to my derived class.
};

class ListOfSomething : public ListOf<Something>
{
  int payloadProperty; // specific to the derived class
};

And I expect to be able to do this:

ListOfSomething *ptr;
ptr = ListOfSomething::method();

but the compiler tells me that I can't assign a ListOf* to a ListOfSomething* and I guess I can understand that. I'm asking to convert a base class pointer to a derived class pointer. I can work around it with a static cast like this

ListOfSomething *ptr;
ptr = static_cast<ListOfSomething *>ListOfSomething::method();

But that seems pretty clunky. Seems like the whole point of the template concept is to do stuff like this.

Maybe my whole approach is wrong. Referring to my broader goal, not to the specific casting problem, I know that I could add a pointer to T to my template class:

template <class T>
class ListOf<T>
{
public:
  ListOf<T> *method();
  T *payload;
};

class Something
{
int payloadProperty;
};

class ListOfSomething : public ListOf<Something>
{
};

but I was trying to avoid a level of indirection. With this method, I have to allocate the payload separately and dereference a pointer every time I want to access it. I am looking for some way to extend the ListOf class that is equivalent to just adding some more members to the class, effectively creating

class ListOfSomething
{
public:
  ListOfSomething *method();
  T payload;
};

but somehow encapsulating the list mechanics of allocation. deallocation, traversal, etc for reusability.

  • Don't add "solved" to the title; when you accept an answer, your question is automatically marked as solved. If you want to show the final working code, prefer posting your own answer, rather than editing it into the question. – HolyBlackCat Sep 14 '21 at 21:05

2 Answers2

3

What we do is not derive ListOfSomething from the list base class. We would make ListOfSomething the singleton and have a generic list class.

class ListOfSomething
{
  public:
    
   static ListOfSomething& instance()
   {
     static ListOfSomething los;
     return los;
   }

   // Obtain a reference to the list.
   ListOf<Something>& get_list() { return the_list; }

  private: 
    // Hide the constructor to force the use of the singleton instance.
    ListOfSomething() {}

    // The actual list of objects.
    ListOf<Something> the_list;

    // Add your properties.
    
};

An alternative would be to pass the type of the derived class to the base class. It is a bit obfuscated but will work. This is often used in singleton base classes.

template <class T, class L>
class ListOf<T>
{
  public:
    // I would advice to return a reference not a pointer.
    static L& method() 
    {
      static L list;
      return L;
    }

  private:
    // Hide the constructor.
    ListOf();
    ~ListOf();
};

Next in your derived class:

class ListOfSomething : public ListOf<Something, ListOfSomething>
{
  // To allow the construction of this object with private constructor in the base class.
  friend class ListOf<Something, ListOfSomething>;

  public: 
    // Add your public functions here.

  private:
    // Hide the constructor and destructor.
    ListOfSomething();
    ~ListOfSomething();
};
Bart
  • 1,405
  • 6
  • 32
  • I'm going to have to stare at this some more before I understand it fully. However, I suspect the trick of passing the derived class name as a template parameter is the solution I am looking for. I don't think that would ever have occurred to me. – transconductance Sep 13 '21 at 18:34
  • @transconductance Same for me, it is not my invention. Do not forget the `friend` part otherwise calling the constructor of `ListOfSomething` will not work as it is hidden. – Bart Sep 14 '21 at 06:36
0

As explained by Bart, the trick here is to pass the name of the derived class as a template parameter. Here is the final form of the class that works as originally desired:

template <class T>
class ListOf<T>
{
public:
  static T *method();
};

and you invoke it like this

class ListOfSomething : public ListOf<ListOfSomething>
{
public:
  int payload;
};

Thus, the compiler is happy with:

ListOfSomething *ptr;
ptr = ListOfSomething::method();