Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing traits for strong types with C++ concepts

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

like image 918
NoSenseEtAl Avatar asked Sep 02 '21 12:09

NoSenseEtAl


1 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 /*, ..*/>;
like image 134
Jarod42 Avatar answered Oct 17 '22 21:10

Jarod42