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?
#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?
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.