giuseppe
giuseppe

Reputation: 194

How to specialize a function using enum

I'm trying to refactor some code. Basically is a state machine based with enum. There are a lot of switch statements and functions that got called with different names and ambiguations.

Since they force me to keep the enum, I would like to refactor it using template. Basically I would like to use template to implement polymorphism. Since the states are limited there should be a way but I cannot find the best one.

#include <iostream>

enum class AnimalType
{
    Dog,
    Cat
};

template<AnimalType T>
void Foo()
{
    std::cout << "Unknown animal\n";
}

template<>
void Foo<AnimalType::Dog>()
{
    std::cout << "I'm a dog\n";
}

template<>
void Foo<AnimalType::Cat>()
{
    std::cout << "I'm a cat\n";
}

int main()
{
    AnimalType CurrentAnimal = AnimalType::Dog;
    // Foo<CurrentAnimal>(); Won't compile
    return 0;
}

Upvotes: 0

Views: 92

Answers (2)

Ghasem Ramezani
Ghasem Ramezani

Reputation: 2888

As mentioned by @P Kramer's answer:

Note : you can't use your construct to make decissions at runtime. Templates only lead to compile time polymorphism.

You can't do that, but you can use the Compile-Time Dispatch and runtime parameter by passing the desired value as parameter while they are separated by Function Template Specialization. For example turn your enumerations value into actual types:

struct animal_t
{
    std::string const name;
    explicit animal_t(std::string const& name_)
        : name(name_)
    {
    }
    auto operator()() const
    {
        return name;
    }
};

struct dog_t final : animal_t
{
    using animal_t::animal_t;
};

struct cat_t final : animal_t
{
    using animal_t::animal_t;
};

They you are able to specialize the function template:

/*!
 *
 * Other Programmer(s) interface
 *
 */
template<typename Animal>
auto function(Animal const&)
{
    assert(false);
}

/*!
 *
 * Implementation
 *
 */
template<>
auto function(cat_t const& animal)
{
    return animal();
}
template<>
auto function(dog_t const& animal)
{
    return animal();
}

Now user (other programmer) of your library could easily interact with it for example by a GUI library:

QObject::connect(button1, &QPushButton::clicked, &application, [] {
    cat_t cat("Some Cat");
    auto const message = QString::fromStdString(function(cat));
    QMessageBox::information(nullptr, " ", message);
});
QObject::connect(button2, &QPushButton::clicked, &application, [] {
    dog_t dog("Some Dog");
    auto const message = QString::fromStdString(function(dog));
    QMessageBox::information(nullptr, " ", message);
});

Result: just for copy/past: runtime_dispatch_v1

enter image description here

Upvotes: 0

Pepijn Kramer
Pepijn Kramer

Reputation: 12849

You need a compile time evaluatable constant, this will work

int main()
{
    constexpr auto CurrentAnimal = AnimalType::Dog;
    Foo<CurrentAnimal>();
    return 0;
}

or directly use

Foo<AnimalType::Dog>();  

Note : you can't use your construct to make decissions at runtime. Templates only lead to compile time polymorphism

Upvotes: 1

Related Questions