Implementing traits for strong types with C++ concepts

Viewed 187

I would like to have a nice way to enable functionality(e.g. ++, *=, /) of my strong types(e.g. StockPrice, Count).

I don't like using inheritance for that (CRTP/mixins), I understand some may like it but I prefer to not use inheritance for this use case.

So I have code like this:

template<class, template<class...> class>
inline constexpr bool is_specialization = false;
template<template<class...> class T, class... Args>
inline constexpr bool is_specialization<T<Args...>, T> = true;

template<typename Underlying, typename TagType>
struct StrongT{
    Underlying val;
    using Tag = TagType;
};


template<typename T>
concept addable =  is_specialization<T, StrongT> && requires (T& t){
    typename T::Tag::addable_trait;
};

struct IndexTag{
    using addable_trait = int;
};

using Index = StrongT<size_t, IndexTag>;

struct NanosTag{
    using addable_trait = int;
};

using Nanos = StrongT<size_t, struct NanosTag>;

template<addable T>
T operator + (const T& a, const T& b){
    return T{a.val+b.val};
}

I like it since I can list my "traits" in tag struct, but I dislike it since it is a bit spammy. I can not declare tag struct inline like this:

 using Nanos = StrongT<size_t, struct NanosTag{/*...*/}>;

So is there way to do what I want in a shorter way, without using inheritance?

note: I only have one trait/concept here, obviously I want many traits supported, e.g. comparable/incrementable...

2 Answers

How about something like

// Different capability tags
struct Addable{};
struct PreIncrementable{};
// ...

template <typename Underlying,
          typename TagType,
          typename... CapabilityTags>
struct StrongT
{
    Underlying val;
    using Tag = TagType;

    friend StrongT operator+(const StrongT& lhs, const StrongT& rhs)
    requires((std::is_same_v<Addable, CapabilityTags> || ...))
    {
        return T{lhs.val + rhs.val};
    }

    StrongT& operator++()
    requires((std::is_same_v<PreIncrementable, CapabilityTags> || ...))
    {
        ++val;
        return *this;
    }

    //...
};

and then

struct NanosTag;
using Nanos = StrongT<size_t, NanosTag, Addable /*, ..*/>;

If I understand you correctly, what you are looking for is a way to make sure and even enforce programmatically that a class implements some functionality without inheritance. One possible way to do this is to create a concept that requires this functionality and then use static_assert to validate the class in question provides the functionality.

Example: Say we want a class that has the ++ and -- operators we'll have to declare a concept like this:

template<typename T>
concept ExpectedTraits = requires(T t) {
    t++;
    t--;
};

Then if we want to assert that (for example) class Object has the desired traits, we need to assert it -

static_assert(ExpectedTraits<Object>);

If class Object doesn't support ++ and --, this assertion will fail and the code won't compile until Object supports these operands.

Related