I am trying to wrap my mind around C++ 20 concept and constraint by porting some of my old code.
struct Status
{
std::string status;
std::time_t statusDate;
};
struct CurrentStatusStack
{
std::vector<Status> statusVec;
std::vector<std::filesystem::path> ticketPathVec;
};
void setBar(CurrentStatusStack s)
{
ui->setLatestStatusMessage(s.statusVec.back().status);
ui->setLatestStatusMessagePath(s.ticketPathVec.back());
}
To translate the above code block to a similar but generic function setFoo(const T& t)
and to make sure T
type implements function that setBar
requires I wrote a few concepts:
- A
ContainerOf
concept
/*
`ContainerOf` requires container (could be a vector or a set
or other STL-like containers) to implement at least:
1. `cbegin()` that returns a const iterator pointer to the first element.
2. `empty()` an emptiness check fn.
*/
template <class Container, typename T>
concept ContainerOf = requires(Container a, T)
{
requires std::regular<Container>;
requires std::same_as<typename Container::value_type, T>;
{
a.cbegin()
} -> std::same_as<typename Container::const_iterator>;
{
a.empty()
} -> std::same_as<bool>;
};
- A
HasTicketPaths
concept
/*
`HasTicketPaths` ensures that that structure given implements a `getTicketPaths`
function that returns a container that satisfies `ContainerOf` with
`std::filesystem::path` elements in it.
*/
template <typename T>
concept HasTicketPaths = requires(T t)
{
{
t.getTicketPaths()
} -> ContainerOf<fs::path>;
};
- A
IsStatus
concept
/*
To set status the obj needs at least two elements:
1. Status string and
2. the date the status was posted,
`IsStatus` ensure those constrients by requiring `status` and
`statusDate` functions that return a `std::string` and `std::time_t`
respectively.
*/
template <typename T>
concept IsStatus = requires(T t)
{
{
t.status()
} -> std::same_as<std::string>;
{
t.statusDate()
} -> std::same_as<std::time_t>;
};
Now I think all have to do is somehow combine those two concepts in HasStatus
concept and change the function prototype to
template <typename T>
requires HasStatus<T> && requires HasTicketPaths<T>
void setFoo(const T& t);
but I am not sure how to do that.
I imagined it'd look something like this
/*
`HasStatus` ensures that the container has a `getStatus` function that return
a container with each element type ensuring `IsStatus`'s requirement.
*/
template <typename T>
concept HasStatus = requires(T t)
{
{
t.getStatus()
} -> ContainerOf<IsStatus>;
};
but it generates the following error
invalid use of ‘decltype(auto) [requires ::IsStatus<<placeholder>, >]’ in template argument
70 | } -> ContainerOf<IsStatus>;
I think I misunderstood how concepts
actually work but I am not sure where/what to look for.