dimanche 2 avril 2023

How to express a list of objects with a common interface in C++

I am struggling to express a list containing various derived class instances in a container object according to a common interface. Adding them to a vector affords options, using the base class, pointers and references. Casting them to concrete types using a discriminator is proving problematic later.

In C#, give or take, I would express it this way:

public interface IOrderable 
{
  decimal GetUnitPrice( );
}
public class BaseProduct 
{
  public Guid Id {get;set;}
  public string Name {get;set;}
  public decimal UnitPrice {get;set;}
  //
  public GetUnitPrice( return UnitPrice; }
}
public class Digital : BaseProduct, IOrderable
{
  public int Bytes {get;set;}
}
public class Physical : BaseProduct, IOrderable
{
  public int MassInGrammes {get;set}
}
public class Order
{
  public List<IOrderable> listOfItems;
  public int TotalBytes() 
  { 
     int t = 0;
     foreach( var i in listOfItems )
     { 
        var d = i as Digital;          // <-- in C# this patterny works fine 
        if( d != null ) t = t + d.Bytes;
     }
     return t;
  }
  public decimal TotalMass() { ... }
}

When I try to achieve this in C++, I begin with the base class, and create a vector.

class BaseProduct
{
public:
  string type;    // <-- discriminator
  etc.
}
class Order
{
public:
  vector<BaseProduct> listOfItems;
  int totalBytes() {
    int t=0;
    for( BaseProduct bp: listOfItems )
    {
      if( bp.type == "digital" )
       ... and this is where the wheels come off
           how to go from BaseProuct to Digital?
      if( bp.type == "physical" )
       ... etc.
    }
    return t;
  }
}

If I create a list based on pointers I can see how to reinterpret_cast to a known derived type. This is my best guess.

I could not get a version with references to objects to work - the vector definition - closest was wrapped_reference<object&>.

I feel am making heavy weather of this, largely because of C++'s focus on object ownership/lifetime, and the general advice that pointers are somehow evil, even in modern C++. (How things have changed! C++ WAS pointer-land once!)

What then is a good (not necessarily best) pattern for this kind of mixed type list in C++?

(Please note, the above is only pseudo code, the problem emerged in a knarly byte-level message parser, where there is one class per message type. Two is enough to find a pattern.)

Aucun commentaire:

Enregistrer un commentaire