How to simulate metaclasses in C++?

60 Views Asked by At

In Object Pascal we can writing:

type
  Animal = class
  end;
    
  Cat = class(Animal)
  end;
    
  Dog = class(Animal)
  end;
    
  MetaAnimal = class of Animal;
    
function factory(m: MetaAnimal): Animal;
begin
  Result := m.create();
end;

...

var pAnimal: Animal;
pAnimal := factory(Cat);

In C++, we can use a switch:

Animal* factory(int n){
    switch(n){
        case 0: return new Animal(); break;
        case 1: return new Cat(); break;
        case 2: return new Dog(); break;
    }
}

This solution is bad, because in another file we can declare:

class Cow: public Animal{};

But factory() won't know about it.

Is there a way to simulate this in C++? Maybe any hack of the VMT table? In this example, I don't care about the cleanliness of the code. If a hack is depended on ABI, it can be conditionally compiled by #ifdef.

1

There are 1 best solutions below

0
On

Short answer, no, there is nothing like meta-classes in C++.

However, your factory solution is fine, you just need to make it more dynamic, for instance by using a std::(unordered_)map of creator functors, eg:

using AnimalPtr = std::unique_ptr<Animal>;
using AnimalCreator = AnimalPtr (*)();

std::unordered_map<std::string, AnimalCreator> animalFactory;

AnimalPtr createAnimal(const std::string& type){
    return animalFactory.at(type)();
}

void registerAnimal(const std::string& type, AnimalCreator creator)
{
    animalFactory[type] = creator;
}

...

registerAnimal("Animal", []() -> AnimalPtr { return std::make_unique<Animal>(); });
registerAnimal("Cat",    []() -> AnimalPtr { return std::make_unique<Cat>();    });
registerAnimal("Dog",    []() -> AnimalPtr { return std::make_unique<Dog>();    });
...
registerAnimal("Cow", []() -> AnimalPtr { return std::make_unique<Cow>(); });

...

auto pAnimal = createAnimal("Cat");