Created
June 1, 2022 20:00
-
-
Save vdaghan/fb283e95e08b4842b398e153bb1f1d39 to your computer and use it in GitHub Desktop.
Concept Examples
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
These are some concepts I had to create myself for my projects. | |
There may be shorter ways or more elegant ways to do these, yet I could not find them myself. | |
If you have any suggestions, please feel free to comment. | |
PS: I'll add tests for all of these concepts, eventually. | |
PPS: You can copy and paste this file as_is to Compiler Explorer (https://godbolt.org/) to play with. | |
Just make sure that you are compiling with a C++20-compliant compiler, with appropriate flags (/std:c++20 for MSVC, --std=c++20 for gcc). | |
*/ | |
#include <concepts> | |
#include <iostream> | |
#include <functional> | |
#include <list> | |
#include <type_traits> | |
#include <vector> | |
template <typename F> concept CallableVoid = requires (F f) { f(); }; | |
template <typename F, typename... Args> concept CallableWith = std::invocable<F, Args...>; | |
//TODO: Does not work when user can't specialise for argument type. | |
template <typename F, typename... Args> concept Callable = CallableWith<F, Args...> || CallableVoid<F>; | |
//TODO: Assumes the single argument of G can be {}-constructible. It shouldn't. | |
template <typename F, typename G> concept Composable = requires(F f, G g) { | |
requires std::invocable<F, decltype(g({}))>; | |
}; | |
template <typename F, typename T> concept Returns = requires(F f, T t) { | |
{f({})} -> std::same_as<T>; | |
}; | |
template <typename T> concept Container = std::ranges::range<T>; | |
template <typename T> using TypeOfElements = std::ranges::range_value_t<T>; | |
template <typename T, typename... Args> concept ContainerOfCallables = Container<T> && Callable<TypeOfElements<T>, Args...>; | |
template <typename T> concept IsVector = requires (T t, T::value_type v) { | |
//requires std::same_as<T, std::vector<decltype(v)>>; //< Alternative | |
requires std::same_as<T, std::vector<typename T::value_type>>; | |
}; | |
//template <typename T> concept IsVector = std::same_as<T, std::vector<typename T::value_type>>; //< Alternative | |
// Dirty little macro for tests below | |
#define CHECK(...) std::cout << #__VA_ARGS__ << ": " << ##__VA_ARGS__ << std::endl; | |
int main(void) { | |
std::cout << std::boolalpha; | |
CHECK(IsVector<std::vector<int>>) | |
CHECK(IsVector<std::vector<std::vector<int>>>) | |
CHECK(IsVector<std::vector<std::list<int>>>) | |
CHECK(IsVector<std::list<std::vector<int>>>) | |
CHECK(IsVector<std::list<int>>) | |
CHECK(IsVector<int>) | |
using F_Void_Int = std::function<int(void)>; | |
using F_Int_Void = std::function<void(int)>; | |
auto void_double_Lambda = []() -> double { | |
return 0.0; | |
}; | |
std::function<double(void)> void_double_Lambda_as_stdfunction = []() -> double { | |
return 0.0; | |
}; | |
auto double_double_Lambda = [](double x) -> double { | |
return x*x; | |
}; | |
CHECK(CallableVoid<F_Void_Int>) | |
CHECK(CallableVoid<F_Int_Void>) | |
CHECK(CallableVoid<decltype(void_double_Lambda)>) | |
CHECK(CallableVoid<decltype(void_double_Lambda_as_stdfunction)>) | |
CHECK(Callable<F_Void_Int>) | |
CHECK(Callable<F_Int_Void>) | |
using F_Int_Int = std::function<int(int)>; | |
using F_Int_String = std::function<std::string(int)>; | |
CHECK(Composable<F_Int_Int, F_Int_Int>) | |
CHECK(Composable<F_Int_String, F_Int_Int>) | |
CHECK(Composable<F_Int_Int, F_Int_String>) | |
using F_Int_VectorOfInt = std::function<std::vector<int>(int)>; | |
CHECK(Returns<F_Int_Int, int>) | |
CHECK(Returns<F_Int_String, std::string>) | |
CHECK(Returns<F_Int_VectorOfInt, int>) | |
std::cout << std::endl; | |
} | |
/* Returns: | |
IsVector<std::vector<int>>: true | |
IsVector<std::vector<std::vector<int>>>: true | |
IsVector<std::vector<std::list<int>>>: true | |
IsVector<std::list<std::vector<int>>>: false | |
IsVector<std::list<int>>: false | |
IsVector<int>: false | |
CallableVoid<F_Void_Int>: true | |
CallableVoid<F_Int_Void>: false | |
CallableVoid<decltype(void_double_Lambda)>: true | |
CallableVoid<decltype(void_double_Lambda_as_stdfunction)>: true | |
Callable<F_Void_Int>: true | |
Callable<F_Int_Void>: false | |
Composable<F_Int_Int, F_Int_Int>: true | |
Composable<F_Int_String, F_Int_Int>: true | |
Composable<F_Int_Int, F_Int_String>: false | |
Returns<F_Int_Int, int>: true | |
Returns<F_Int_String, std::string>: true | |
Returns<F_Int_VectorOfInt, int>: false | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment