Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"type-switch" construct in C++11

All the time, I find myself doing something like this:

Animal *animal = ...
if (Cat *cat = dynamic_cast<Cat *>(animal)) {
    ...
}
else if (Dog *dog = dynamic_cast<Dog *>(animal)) {
    ...
}
else { assert(false); }

Once I see closures in C++11, I wonder, is something like this possible?

Animal *animal = ...
typecase(animal,
    [](Cat *cat) {
        ...
    },
    [](Dog *dog) {
        ...
    });

Implementing typecase should have been easy, but I keep running into a problem where it can't figure out the function's argument, so it can't know what to try to dynamic_cast to, because it's hard to deduce lambdas' parameters. Took a few days of searching google and SO, but finally figured it out, so I'll share my answer below.

like image 497
Verdagon Avatar asked Apr 02 '14 20:04

Verdagon


2 Answers

Implementation

template <typename T, typename B>
void action_if(B* value, std::function<void(T*)> action)
{
    auto cast_value = dynamic_cast<T*>(value);
    if (cast_value != nullptr)
    {
        action(cast_value);
    }
}

Usage

Animal* animal = ...;
action_if<Cat>(animal, [](Cat* cat)
{
    ...
});
action_if<Dog>(animal, [](Dog* dog)
{
    ...
});

I don't have access to a C++11 compiler right this second to try this out, but I hope the idea is useful. Depending on how much type inference the compiler is capable of, you may or may not have to specify the case's type twice - I'm not C++11-pro enough to tell from looking at it.

like image 74
Timothy Shields Avatar answered Sep 21 '22 12:09

Timothy Shields


I think it depends if you want do this at compile time or at run time. For compile time, Verdagon's answer is better, at runtime you can do something like this

class A {
};

class B {
};

void doForA() {
    std::cout << "I'm A" << std::endl;
}

void doForB() {
    std::cout << "I'm B" << std::endl;
}

int main() {
    std::unordered_map<std::type_index, std::function<void(void)>> mytypes;
    mytypes[typeid(A)] = doForA;
    mytypes[typeid(B)] = doForB;

    mytypes[typeid(A)]();
}

but both ways are wrong the virtual keyword is here for this, you MUST do like Arie said or there is mistake in your architecture

like image 21
newin Avatar answered Sep 18 '22 12:09

newin