As an example, I want to use a constraint to ensure that the function isinf is implemented for a template parameter T. If T is one of float, double, long double or an integral type, this can be done in the following way:
#include <cmath>
template <typename T>
concept Test = requires (T a) {
std::isinf(a);
};
However, I also want to use this constraint for custom, non-standard data types for which I implement my own isinf function. This function is not contained in the std namespace, so I tried the following instead:
#include <cmath>
template <typename T>
concept Test = requires (T a) {
using std::isinf;
isinf(a);
};
This does not work since every statement in the requires clause should be a valid requirement and using std::isinf is not a "requirement" at all.
I see two workarounds to this problem:
Move the using std::isinf; clause to the global namespace. But this introduces isinf to the global namespace, which I'd like to avoid.
Encapsulate the using std::isinf; clause with the concept definition in a namespace named ABC, then add using ABC::Test; directly after the namespace. This seems a little bit weird.
Is there a better solution?
The way this sort of thing works in Ranges is by creating a Customization Point Object. This closely mirrors your second option (we stick a using-declaration in a custom namespace) except we also provide an mechanism for users to call the correct isinf without having to write a bunch of the same kind of boilerplate themselves.
A customization point object for isinf would look something like this:
namespace N {
// make our own namespace
namespace impl {
// ... where we can bring in std::isinf
using std::isinf;
struct isinf_t {
// our type is constrained on unqualified isinf working
// in a context where std::isinf can be found
template <typename T>
requires requires (T t) {
{ isinf(t) } -> std::same_as<bool>;
}
constexpr bool operator()(T t) const {
// ... and just invokes that (we know it's valid and bool at this point)
return isinf(t);
}
};
}
// we provide an object such that `isinf(x)` incorporates ADL itself
inline constexpr auto isinf = impl::isinf_t{};
}
And now that we have an object, a concept follows directly:
template <typename T>
concept Test = requires (T t) {
N::isinf(t);
}
This is precisely how the range concept is specified.
This should work. I'm not that comfortable with c++20 concepts so I cannot guarantee that the syntax is correct.
namespace impl {
using std::isinf;
template<class T>
auto adl_isinf(T t) -> decltype(isinf(t));
// { return isinf(t); } // implementation not necessary depending on whether
// requires is an unevaluated context
}
template <typename T>
concept Test = requires (T a) {
impl::adl_isinf(a);
};
Related
I want to test whether a type can be passed to some function, but I'd like to use ADL on the function lookup and include a function from a certain namespace.
Consider this code:
#include <utility>
#include <vector>
template<class T>
concept Swappable = requires(T& a, T& b)
{
swap(a,b);
};
static_assert(Swappable<std::vector<int>>); // #1
static_assert(Swappable<int>); // #2
#1 succeeds, it finds std::swap because std is an associated namespace of std::vector<int>. But #2 fails, a built-in type has no associated namespace.
How would I write something like:
template<class T>
concept Swappable = requires(T& a, T& b)
{
using std::swap; // illegal
swap(a,b);
};
AFAIK, you're not allowed to use a using-declaration inside a requires-expression.
(NOTE Although there is a perfectly fine standard C++ concept for this, std::swappable, this example uses swap for exposition only. I'm not particularly looking to test whether something is actually swappable, I'm just trying to find a way to implement such a concept where a customization function has a default implementation in a known namespace, but might have overloads in an associated namespace.)
EDIT As a workaround, I can implement the concept in a separate namespace where the names are pulled in. Not too happy about it but it works.
namespace detail
{
using std::swap;
template<class T>
concept Swappable = requires(T& a, T& b)
{
swap(a,b);
};
}
// and then either use it
using detail::Swappable;
// or redefine it
template<class T>
concept Swappable = detail::Swappable<T>;
You can put it inside a lambda:
template<class T>
concept Swappable = []{
using std::swap;
return requires(T& a, T& b) { swap(a, b); };
}();
Avoid using old using-based idioms. Instead, use the customization point equivalents like ranges::swap.
That is, you should not require users to use using-based idioms in their code. Provide a customization point object that does what it needs to. The operator() overloads/templates can be constrained to create the effect of the using idiom without requiring the user to actually invoke using.
ranges::swap is a good example of how this gets done.
Interesting, the below code works well again:
#include <concepts>
template <typename T, typename U = T>
concept Swappable = requires(T&& a, U&& b) {
std::swap(a, b);
};
int main() {
static_assert(Swappable<int>);
static_assert(Swappable<int, int>);
return 0;
}
I see an example from cppconference, but I could not get expected results. Cpp reference. See code here:
template <typename T, typename U = T>
concept Swappable = requires(T&& a, U&& b) {
swap(std::forward<T>(a), std::forward<U>(b));
swap(std::forward<U>(b), std::forward<T>(a));
};
int main() {
static_assert(!Swappable<int>); // Wrong, int is definately swappable!
static_assert(!Swappable<int, int>); // Wrong, int is definately swappable!
return 0;
}
I am currently trying to get my feet wet with concepts. Let us assume that I have a concept:
template <class T> concept Initable = requires (T&t) { { init(t) }; };
// just for demonstration purposes, real concept makes more sense ...
and I want to provide an adoption layer for a third-party class like std::optional to implement this concept, what would be the most seemless way for me to do so?
Obviously, the following piece of code fails:
template <std::semiregular T>
T& init(std::optional<T> &v) { /* contents not that important */ v = T{}; return *v; }
static_assert(Initable<std::optional<int>>, "Oh no!"); // Fails!
The reason is two-phase lookup.
When trying to resolve init in the Initable concept during phase 1, my definition of init is not available, because it is provided below the Initable concept.
When trying to resolve it during phase 2, my definition is not found via argument-dependent lookup, because it is not provided in the std namespace.
Two obvious solutions, thus, would be to either provide the definition of init before defining the Initable concept or move init to the std namespace.
But I want to implement that concept for std::optional without
relying on a particular definition/include order,
populating the std namespace and
using too much boiler-plate code at the caller site.
What would be the best way to do so? Could I make it somewhat easier to accomplish this when defining the Initable concept?
Basically I am asking, is this possible?
#include <concepts>
#include <optional>
template <class T>
concept Initable =
requires (T& t) { init(t); } // May be changed to make the task easier.
;
// Insert code *here* that magically makes std::optional implement Initable.
static_assert(Initable<std::optional<int>>, "std::optional is not initable!");
And, if not, what would be the next best thing?
Resolving to any function that is visible during template instantiation is clearly against the principles of two-phase lookup. I decided to take a different approach instead and use a dummy parameter that is defined in a separate customization namespace:
namespace CP { // CP = CustomizationPoint
struct Token {};
}
It is now possible, to define the init function in either the CP namespace or the namespace, the object is defined in:
namespace std {
template <class T> void init(std::optional<T> &v, CP::Token) { ... }
// illegal, but works -- do not declare stuff in namespace std.
}
namespace CP {
template <class T> void init(std::optional<T> &v, CP::Token) { ... }
}
This is already pretty nice. It would be even nicer, if the definition in the std namespace would not require the CP::Token parameter. This can be done by providing a function object that will resolve to the correct function (similar to customization point objects that got introduced into the standard library):
constexpr inline struct {
template <class T>
auto operator()(std::optional<T>& v, CP::Token t) const
-> decltype(init(v, t), []{}())
{
init(v, t);
}
template <class T>
auto operator()(std::optional<T>& v, ...) const
-> decltype(init(v), []{}())
{
init(v)
}
} init;
This function object is a bit bulky, but it will resolve to the variant with the CP::Token parameter, if it is available and otherwise fall back to the variant without the token parameter, if that one is available.
To me, this seems to be a pretty sane approach that is perfectly extendable and even allows us to override an implementation that already has the right name, but the wrong semantics.
The concept must be modified slightly for this to work:
template <class T> concept Initable = requires (T&t) { init(t, CP::Token{}); };
Question may be weird so here is a brief motivational example:
#include <vector>
#include <type_traits>
template <typename T>
// workaround for gcc 8.3 where volatile int is not trivially copyable
using is_tc = std::is_trivially_copyable<std::remove_cv<T>>;
// static assert passes compile, oops
static_assert(is_tc<std::vector<int>>::value);
As you can see mistake is that I have passed the type trait itself to another type trait instead of passing ::type or using std::remove_cv_t.
Obvious solution is for me to not make mistakes, but I wonder if is there a way C++ type traits could restrict their input types so that they do not accept other type_traits as arguments.
Now the hard thing is that there is a huge set of type traits in type_traits so IDK how would one go best about implementing this.
Note: I am not saying C++ should do this, I know it is a lot of work to prevent rare bugs, I am just trying to learn about more complicated concepts design where your restriction is not based on semantics of types(aka has ++ and *) but on the fact that types belong to a huge set of types(and that set includes the type you are restricting on).
Well, assuming you always need a ::type as argument where possible, here's a quick workaround:
template<class T> concept HasType = requires { typename T::type; };
template<class T> concept HasNoType = !HasType<T>;
template<HasNoType T> using remove_cv = std::remove_cv<T>;
template<HasNoType T> using remove_cv_t = typename remove_cv<T>::type;
Other than patching STL headers, or subclassing STL types (which is not always permitted), you cannot redefine what was predefined.
your restriction is not based on semantics of types(aka has ++ and *) but on the fact that types belong to a huge set of types
Whatever goes, you'll need a predicate to specify this set (operator ∊S for a given S). For instance has ++ is as good a predicate as any other.
The predicate can be refined with more levels of indirections and some boilerplate, say
template<class T> struct not_a_type_trait =
std::integral_constant<bool, HasNoType<T>> {};
template<class T> inline constexpr not_a_type_trait_v = not_a_type_trait<T>::value;
template<class T> concept NotATrait = not_a_type_trait_v<T>;
struct AnArg { using type = void; };
template<> struct not_a_type_trait<AnArg>: std::true_type {};
// now can be an arg to remove_cv
Or, here in this particular case, you can simply blacklist all the STL's traits, but that would be a really huge predicate to be updated with each Standard revision.
Concept: Is a TransformationTrait declared in the std namespace
I wonder if is there a way C++ type traits could restrict their input types so that they do not accept other type_traits
Since metafunction traits are actually types themselves (which is kind of also the root of your problem), we can leverage this and construct a concept for T for whether Argument-Dependent Lookup (ADL) can find a smaller select set of STL-functions via ADL on an object of type T (in a non-evaluated context), where T may potentially be a metafunction trait; essentially an ADL-based (possibly brittle - see below) mechanism to query whether a given type T is defined in the std namespace or not, as opposed to the verbose approach of querying whether T is exactly one of numerous trait types defined in the std namespace.
If we combine this with the TransformationTrait requirement:
C++ named requirements: TransformationTrait
A TransformationTrait is a class template that provides a transformation of its template type parameter.
Requirements:
Takes one template type parameter (additional template parameters are optional and allowed)
The transformed type is a publicly accessible nested type named type
we can construct a common concept for a type T being a transformation trait in the std namespace. Note however that relying on ADL in this sense can be somewhat brittle, if for some reason a given project starts to declare functions which overloads function names from the STL. Expanding the smaller select set of STL-functions in the concept for possible ADL-lookup will make it harder to break from a non-std-implementor perspective.
E.g. defining a few concepts as follows:
namespace traits_concepts {
template <typename T>
concept FindsStlFunctionByAdlLookupOnT = requires(T t) {
// Rely on std::as_const being found by ADL on t, i.e.
// for t being an object of a type in namespace std.
as_const(t);
// If we are worried that a user may define an as_const
// function in another (reachable/found by lookup)
// namespace, expand the requirement with additional
// STL functions (that can be found via ADL).
move(t);
// ...
// Remember to add the appropriate includes.
};
template <typename T>
concept IsTransformationTrait = requires {
// REQ: The transformed type is a publicly accessible
// nested type named type.
typename T::type;
};
template <typename T>
concept IsStlTransformationTrait =
IsTransformationTrait<T> && FindsStlFunctionByAdlLookupOnT<T>;
template <typename T>
concept IsNotStlTransformationTrait = !IsStlTransformationTrait<T>;
} // namespace traits_concepts
Applied as:
namespace not_std {
template <traits_concepts::IsNotStlTransformationTrait T>
struct NotAnStlTrait {
using type = T;
};
struct Foo {};
}; // namespace not_std
// Is an STL transformation trait
static_assert(
traits_concepts::IsStlTransformationTrait<std::remove_cv<const int>>);
// Is not an STL transformation trait.
static_assert(
!traits_concepts::IsStlTransformationTrait<std::remove_cv_t<const int>>);
static_assert(
!traits_concepts::IsStlTransformationTrait<not_std::NotAnStlTrait<int>>);
static_assert(!traits_concepts::IsStlTransformationTrait<not_std::Foo>);
int main() {}
And, for a custom trait added to std (assume for now that we are a compiler vendor; adding names to the std namespace is UB):
namespace std {
// Assume we are a compiler vendor.
// (Adding names to the std namespace is UB).
template <traits_concepts::IsNotStlTransformationTrait T>
struct custom_stl_trait {
using type = T;
};
}; // namespace std
static_assert(
traits_concepts::IsStlTransformationTrait<std::custom_stl_trait<int>>);
static_assert(!traits_concepts::IsStlTransformationTrait<
std::custom_stl_trait<int>::type>);
int main() {}
DEMO.
I think that it is possible if all traits would have check for other traits, like all traits are inherited from _Trait, and do st is_base_of_v on its template parameter:
template<class T>
struct remove_cv : private _Trait
{
static_assert(!is_base_of_v<_Trait, T>, "Don't pass traits to traits");
using type = T;
};
If you want warning instead of hard error, this is harder. Need to make static_assert to evaluate as always true, but instantiating [[deprecated]] class for trait passed to trait.
Another simple solution would be to mark [[deprecated]] all traits that require ::type or ::value, deprecate them in favor of _t / _v. This is non-standard, but can be done under some preprocessor macro. or this deprecation can be made available by including a header that lists those deprecations.
The usual way to tag a set of type is to use a trait ;-)
template <class T>
struct is_trait : std::false_type {};
template <class T>
struct is_trait <is_trait <T>> : std:: true_type {};
template <class T>
inline constexpr auto is_trait_v = is_trait:: value;
I am writing a library and there is a function that performs an (unqualified) call to free function foo using an arbitrary type as argument:
namespace lib {
template <typename T>
auto libfunc(T && t)
{
return foo(std::forward<T>(t));
}
} // namespace lib
A user of the library can write an overload of foo for his own types:
namespace app {
class Foo { };
template <typename> class Bar { };
int foo(Foo const &) { return 99; }
template <typename T>
char foo(Bar<T> const &) { return 'x'; }
} // namespace app
The correct funtion foo is found by ADL so code like this works:
app::Foo foo;
app::Bar<void**> bar;
auto x = lib::libfunc(foo);
auto y = lib::libfunc(bar);
However, if I want to write a version of foo that works for types from the std-namespace, no matching function foo is found unless I place foo in the std-namespace which is not allowed:
#ifdef EVIL
namespace std {
#endif
template <typename T>
double foo(std::vector<T> const & ) { return 1.23; }
#ifdef EVIL
} // namespace std
#endif
std::vector<int> vec;
lib::libfunc(vec); // Only works if EVIL is defined
Is it possible to change the code so that a user can enable the functionality foo for a type without invading its namespace? I thought about partial template specializations of a class template in the lib-namespace but is there any other possibility?
I've found two solutions to this problem. Both have their downsides.
Declare All Std Overloads
Let overloads for standard types be found by normal lookup. This basically means declaring all of them before using the extension function. Remember: when you perform an unqualified call in a function template, normal lookup happens at the point of definition, while ADL happens at the point of instantiation. This means that normal lookup only finds overloads visible from where the template is written, whereas ADL finds stuff defined later on.
The upside of this approach is that nothing changes for the user when writing his own functions.
The downside is that you have to include the header of every standard type you want to provide an overload for, and provide that overload, in the header that just wants to define the extension point. This can mean a very heavy dependency.
Add Another Argument
The other option is to pass a second argument to the function. Here's how this works:
namespace your_stuff {
namespace adl {
struct tag {}
void extension_point() = delete; // this is just a dummy
}
template <typename T>
void use_extension_point(const T& t) {
using adl::extension_point;
extension_point(t, adl::tag{}); // this is the ADL call
}
template <typename T>
void needs_extension_point(const T& t) {
your_stuff::use_extension_point(t); // suppress ADL
}
}
Now you can, at basically any point in the program, provide overloads for std (or even global or built-in) types like this:
namespace your_stuff { namespace adl {
void extension_point(const std::string& s, tag) {
// do stuff here
}
void extension_point(int i, tag) {
// do stuff here
}
}}
The user can, for his own types, write overloads like this:
namespace user_stuff {
void extension_point(const user_type& u, your_stuff::adl::tag) {
// do stuff here
}
}
Upside: Works.
Downside: the user must add the your_stuff::adl::tag argument to his overloads. This will be probably seen as annoying boilerplate by many, and more importantly, can lead to the big puzzling "why doesn't it find my overload" problem when the user forgets to add the argument. On the other hand, the argument also clearly identifies the overloads as fulfilling a contract (being an extension point), which could be important when the next programmer comes along and renames the function to extensionPoint (to conform with naming conventions) and then freaks out when things don't compile anymore.
Imagine I'm writing some container template or something. And the time comes to specialize std::swap for it. As a good citizen, I'll enable ADL by doing something like this:
template <typename T>
void swap(my_template<T>& x, my_template<T>& y) {
using std::swap;
swap(x.something_that_is_a_T, y.something_that_is_a_T);
}
This is very neat and all. Until I want to add an exception specification. My swap is noexcept as long as the swap for T is noexcept. So, I'd be writing something like:
template <typename T>
void swap(my_template<T>& x, my_template<T>& y)
noexcept(noexcept(swap(std::declval<T>(), std::declval<T>())))
Problem is, the swap in there needs to be the ADL-discovered swap or std::swap. How do I handle this?
I think I would move it into a separate namespace
namespace tricks {
using std::swap;
template <typename T, typename U>
void swap(T &t, U &u) noexcept(noexcept(swap(t, u)));
}
template <typename T>
void swap(my_template<T>& x, my_template<T>& y)
noexcept(noexcept(tricks::swap(std::declval<T>(), std::declval<T>())))
{
using std::swap;
swap(x.something_that_is_a_T, y.something_that_is_a_T);
}
Alternatively you can move the whole code up into tricks and delegate to there.
There is a similar problem for return types:
// Want to be compatible with both boost::tuple and std::tuple
template<typename Tuple>
auto first(Tuple&& tuple)
-> /* ??? */
{
// Introduce name into scope
using std::get;
// but ADL can still pick boost::get for boost::tuple
return get<0>(std::forward<Tuple>(tuple));
}
Using decltype( get<0>(std::forward<Tuple>(tuple)) ) isn't correct as get isn't in scope.
Possible workarounds are:
Introducing a dummy template (get in my example, swap in your case) in the enclosing scope; this includes putting the using std::swap declaration in the enclosing namespace, with the drawback of polluting the namespace.
Use of a type trait: typename std::tuple_element<0, typename std::remove_reference<Tuple>::type>::type (actually this one is problematic but for reasons that don't belong here) in my example, and a potential is_nothrow_swappable<T>::value in your case. Specializations then allow the template to be extended for other types if need be.
Rather than declaring but not defining a function template, which seems likely to cause confusion, I would write my own type trait (which is what should probably be in the standard library, anyway). Following the lead of the standard library, I would define something like the following:
#include <type_traits>
#include <utility>
namespace adl {
using std::swap;
template<typename T, typename U>
struct is_nothrow_swappable : std::integral_constant<
bool,
noexcept(swap(std::declval<T &>(), std::declval<U &>()))
> {
};
} // namespace adl
We have to define our own namespace to import std::swap into (to avoid giving it to everyone), but of course, if it were in the standard library that wouldn't be necessary because they can already make unqualified calls to swap.
C++17 has solved this particular use case with std::is_nothrow_swappable: http://en.cppreference.com/w/cpp/types/is_swappable