HEKTO
HEKTO

Reputation: 4191

From forward declaration and conditional compilation to what?

I have a Container class, which has been designed with an item type, declared in the header and conditionally defined in the cpp-file:

Container.h:

struct Container
{
  Container();
  // other members
private:
  struct ITEM;
  ITEM* table;
};

Container.cpp:

#include "Container.h"

#if CONTAINER_TYPE == 1

struct Container::ITEM
{
  // some data fields
};

#elif CONTAINER_TYPE == 2

struct Container::ITEM
{
  // some data fields
};

#else
#error "..."
#endif

Container::Container()
{
  table = new ITEM[100];
}

Now I want to get rid of conditional compilation, and instead to use some combination of templates and inheritance to choose the item type during the Container construction:

// main.cpp
#include "Container.h"

int main()
{
  Container c(1);
  // and so on
}

My requirements:

What are my options? Are there any design patterns, applicable here?

Upvotes: 1

Views: 105

Answers (2)

Remy Lebeau
Remy Lebeau

Reputation: 597036

If you want the user of the container to decide the type of item used at runtime, while still hiding the implementation details, you will have to do something more like this:

struct Container
{
  enum ItemType { eItemType1, eItemType2 };

  Container(ItemType aItemType);
  ~Container();

  // other members

private:
  struct ITEM {
    virtual ~ITEM() {}
  };
  ITEM** table;
};

#include "Container.h"

struct ItemType1 : Container::ITEM
{
  // some data fields
};

struct ItemType2 : Container::ITEM
{
  // some data fields
};

Container::Container(Container::ItemType aItemType)
{
  table = new ITEM*[100];

  for (int i = 0; i < 100; ++i)
  {
    switch (aItemType)
    {
      case eItemType1:
        table[i] = new ItemType1;
        break;

      case eItemType2:
        table[i] = new ItemType2;
        break;

      default:
        table[i] = new ITEM;
        break;
  }
}

Container::~Container()
{
  for (int i = 0; i < 100; ++i)
    delete table[i];
  delete[] table;
}

#include "Container.h"

int main()
{
  Container c(eItemType1);
  // and so on
}

If you absolutely cannot have a vtable in ITEM, and the table content must be held in sequential memory, then you will have to do something more like this instead:

struct Container
{
  enum ItemType { ItemType1, ItemType2 };

  Container(ItemType aItemType);
  ~Container();

  // other members

private:
  void* table;
  ItemType type;
};

#include "Container.h"

struct ItemType1
{
  // some data fields
};

struct ItemType2
{
  // some data fields
};

Container::Container(Container::ItemType aItemType)
{
  type = aItemType;

  switch (aItemType)
  {
      case eItemType1:
        table = new ItemType1[100];
        break;

      case type2:
        table = new ItemType2[100];
        break;

      default:
        table = NULL;
        break;
  }
}

Container::~Container()
{
  switch (type)
  {
      case eItemType1:
        delete[] static_cast<ItemType1*>(table);
        break;

      case type2:
        delete[] static_cast<ItemType2*>(table);
        break;
  }
}

#include "Container.h"

int main()
{
  Container c(eItemType1);
  // and so on
}

Upvotes: 2

ThunderStorm
ThunderStorm

Reputation: 766

I don't think you have many options if you want to hide the template semantics from your customers. Using templates is easy though. If you wanted to have the data type of the stored data as template arguments it could look something like this:

template <typename ITEM>
struct Container
{
    Container(ITEM a) {
        //not sure what you want to do here
        table = new ITEM[100];
    }
    // other members
private:
    //the pointer to the stored array
    ITEM* table;
};

int main()
{
    Container<int>(1);
}

Notice that the only thing I did was to make ITEM a template argument. Now you can define which type of data your Container should have when you create an instance of it.

Furthermore if you are looking at performance I don't see why you wouldn't just use a pointer to the data. This way you keep the overhead low and the elements don't get copied each time you copy the container object.

Might not be the best solution but you could start with that. Let me know what you had in mind.

Upvotes: 0

Related Questions