Reputation:
I'm writing a library for genetic algorithms/neuroevolution. Currently, the program uses polymorphism to allow multiple types of genomes. So my code looks like this:
class Genome { // Interface
// Some abstract functions
}
class SpecificGenome : public Genome {
// implementation
public:
int do_something(int x); // Specific behavior, which only this SpecificGenome has
}
class Population {
public:
Population(size_t population_size, std::function<std::unique_ptr<Genome>()> create_genome);
// Some functions
std::vector<std::unique_ptr<Genome>> members;
}
std::unique_ptr<Genome> create_specific_genome(){
return std::unique_ptr<Genome>(std::make_unique<SpecificGenome>());
}
int main() {
Population p(150, &create_specific_genome);
int y = static_cast<SpecificGenome>(*p.members[0].get())->do_something(4);
}
I am thinking of changing it so that it'll use templates instead of polymorphism because every genome encodes a phenome, which can have any type of behavior and doesn't share anything in common with other phenome types and some genomes use a direct encoding scheme and don't have to be decoded to a phenome. So every genome has to be cast to his corresponding subclass, by the user, to expose its behavior, which looks very ugly. The Problem is that every subclass of a Genome is a Genome and needs to have a few specific functions to work, so polymorphism would make perfect sense.
Edit: The question as it is now is very unclear, so I wanted to add some additional explanation. I want to be able to create a Population with any type of Genome (NeuralNetwork, NEAT Genome, an Image, ...), so I did this using an Interface and subclasses so that the vector in the population can store every type using a genome pointer. This was useful because every Genome type needs to have specific methods like crossover and mutation. The problem arises, when I want to use specific functions like computing the output of the neural network, the genome decodes or getting the pixel data of the image, the genome decodes. This made me question if using Templates instead of inheritance would be better.
Upvotes: 0
Views: 151
Reputation: 41760
Template might help your case better, but has other implication.
For example, your list cannot be heterogeneous in which genome type it contains. It must be all of the same type. If you need heterogenicity, then you'll have to implement some sort of type erasure.
Here's an example that look like your example but with static polymorphism:
// no inheritance
class SpecificGenome {
public:
int do_something(int x);
}
template<typename G, typename C>
class Population {
public:
Population(size_t population_size, C create_genome);
// no need for pointers. Values work just fine.
std::vector<G> members;
}
// Deduction guide using the create_genome function return type
template<typename C>
Population(std::size_t, C) -> Population<std::invoke_result_t<C>, C>;
SpecificGenome create_specific_genome() {
return SpecificGenome{};
}
int main() {
// Uses class template argument deduction
Population p(150, create_specific_genome);
int y = p.members[0].do_something(4);
}
By the way, if you still go with std::unique_ptr
, you could use it with a much better syntax:
std::unique_ptr<Genome> create_specific_genome() {
// no need for casts
return std::make_unique<SpecificGenome>();
}
// no need for calls to `get`.
int y = static_cast<SpecificGenome&>(*p.members[0]).do_something(4);
Upvotes: 2