Dispatch on execution policy by type or enum?

229 views Asked by At

In C++ I basically have two choices in policy based design patterns: I can use individual types (based on which an overload is selected) or specify an enum that contains all policies and I would dispatch over them at runtime. What is the preferred way of writing policy based design nowadays?

Demo

#include <cstdio>

/* Policy using types */

namespace type_policy
{
    struct detached_t{};
    struct deferred_t{};

    inline constexpr detached_t detached = detached_t{};
    inline constexpr deferred_t deferred = deferred_t{};
}

auto do_something(type_policy::detached_t pol)
{
    printf("detached type policy selected\n");
}

auto do_something(type_policy::deferred_t pol)
{
    printf("deferred type policy selected\n");
}

/* Policy using enums */

enum class enum_policy
{
    detached,
    deferred,
};

static auto do_something_else(const enum_policy pol)
{
    if (pol == enum_policy::deferred) {
        printf("deferred enum policy selected\n");
    } else {
        printf("detached enum policy selected\n");
    }
}

int main()
{
    do_something(type_policy::deferred);
    do_something_else(enum_policy::detached);
}

Note: When the enum dispatch is built into a static function, the compiler is able to eliminate the conditional at compile time as well. It is also less verbose from the start... should it be the preferred way?

2

There are 2 answers

0
linuxfever On BEST ANSWER

I'd say it depends. Do you want to give clients (that includes yourself) the ability to add new policies of their own easily? If so, a type-based design would allow them to add new policies without having to recompile their existing code; all they'd need is:

namespace type_policy
{
    struct another_t {};
    inline constexpr another_t another = another_t{};
}

auto do_something(type_policy::another_t pol)
{
    printf("another type policy selected\n");
}

An enum-based design, on the other hand, would only be extensible by the enum owner. Furthermore, if you change the enum to add a new policy, you'd force clients to recompile their code, even though they may not be interested in the new policy you've just added.

Having said that, a drawback of the type-based design is that policies may end up being scattered among many files, as opposed to enum-based where all policies are nicely grouped together in the enum class.

Another drawback of the type-based design is that it's slightly harder to use with runtime values (as you need to know the exact type to pass in do_something), as opposed to enum-based where a simple cast from an int to enum_policy will work just fine.

0
cbuchart On

In general enums are the natural way to express one-of-many options instead of using trait-like mechanisms that are harder to understand and force you to generate new overloads for any new option.

Regarding this last point, the usage of enums will simplify changes on your available policies with a smaller and clearer interface. Any policy-base function (do_something_else) will keep one single overload, instead of breaking API compatibility.

Finally, since C++11 the usage of enum classes increases its safety in comparison to C-like enums that can be casted implicitly from any integer.