How to enforce a formal protocol with C++ templates? - c++

When using the compile-time duck typing inherent with the template style, is there any way to enforce the requirement that the template argument implements certain methods with certain signatures?
struct ProtocolT {
void g() const;
void h();
}
// I want the compiler to check that T conforms to ProtocolT
// that is, T must implement g() and h() rather than just g()
template <typename T>
void f(const T& x) {
x.g();
}
Of course, even without this, there is perfect type safety: if the template argument T does not have a method used in the template function implementation, the compiler will always complain.
But I find it appealing to state clearly that class T must have all the methods specified in some class ProtocolT. It would allow me to constrain the design earlier in the development process by requiring methods from T that I don't yet use in the template function implementation.
Even if I didn't include any unused methods in ProtocolT, I still think a verified protocol conformance would help when I need to write a class usable as T. (Of course, no one stops me from writing ProtocolT for documentation purposes, but then the compiler won't validate that ProtocolT includes at least all the required methods.)

The feature you are looking for is known as concepts. They are currently a technical specification; GCC has an implementation of concepts lite.
Using concepts would look something like (I'm not too familiar with the syntax, so it would probably be slightly different):
template <typename T>
concept bool Protocol = requires(const T a, T b) {
{ a.g() } -> void;
{ b.h() } -> void;
};
void f(const Protocol& x) {
x.g();
}
However, if you want a solution you can use right now, you can emulate concepts with a variety of techniques.
You could write type-traits to detect if a function does what you want.
You could also use the detection idiom, which abstracts the previous technique, greatly reducing boilerplate. For your example:
template <typename T>
using g_t = decltype(std::declval<const T&>().g());
template <typename T>
using h_t = decltype(std::declval<T&>().h());
template <typename T>
constexpr bool meets_protocol_v = std::experimental::is_detected_exact_v<void, g_t, T>
&& std::experimental::is_detected_exact_v<void, h_t, T>;
In using it, you could either be SFINAE friendly and SFINAE off of meets_protocol_v, or you could static assert:
template <typename T>
void f(const T& x) {
static_assert(meets_protocol_v<T>, "Doesn't meet protocol");
x.g();
}

Maybe inserting corresponding static_assert:
static_assert
(
::std::is_same< void, decltype(::std::declval< T >().h()) >::value
, "T must implement void h(void)"
);
Also note that in your example when T follows ProtocolT requirements it still won't work because f accepts a const reference to T, while ProtocolT only says it should have non-const g().

Related

Perfect forwarding with a specific type

When writing a threadsafe std::stack wrapper I made the following two overloads for push:
void push(const value_type& value)
{
auto guard = std::scoped_lock{_mutex};
_stack.push(value);
_cv.notify_one();
}
void push(value_type&& value)
{
auto guard = std::scoped_lock{_mutex};
_stack.push(std::move(value));
_cv.notify_one();
}
They are nearly the same except that one takes a const l-value reference and one takes an r-value reference. Normally I would use perfect forwarding to deal with this (now using the glorious C++20 abbreviated template declaration):
void push(auto&& value)
{
auto guard = std::scoped_lock{_mutex};
_stack.push(std::forward<decltype(value)>(value));
_cv.notify_one();
}
The problem here is that this accepts any type, while it should accept only value_type and references to it.
Is there some standard way to solve this? So far I came up with two approches. Either use a std::enable_if to somehow check if the template type is value_type or a reference to it, or use a concept.
You can assert it:
template<typename T>
void push(T&& value)
{
static_assert(is_same_v<remove_reference_t<T>, value_type>);
// ...
}
You could alternatively use is_convertible instead of is_same so it works more naturally.
I repeat #Ayxan Haqverdili's example in multiple versions. Also note, because you mention that you want a standard way to solve this, that C++20 concepts are the standard way now. Unless you are writing code that requires compatibility with old language versions, that should be used. The primary reason is that it clearly and concisely documents the requirements of the function at the declaration site. This is even more eloquent than static_assert, and allows overloading instead of direct failure. std::enable_if is simply the old method of instating requirement.
An Inline Concept
template <class T>
requires std::convertible_to<T, value_type>
void push(T &&value);
A Custom Concept
This lets you use the convenient parameter auto.
template <class T, class U>
concept ForwardType = std::convertible_to<T, U>
void push(ForwardType<value_type> auto &&value);
Using std::enable_if
This should be compatible with C++11.
template <class T>
std::enable_if_t<std::is_convertible_v<T>> push(T &&value);

std::add_const_t<T>& vs const T& [duplicate]

Some type transformations in <type_traits> can also be expressed using core language syntax (e.g. std::add_const<T>::type is/seems equivalent to const T). Dtto for std::add_lvalue_reference, and perhaps others. What is the use for these type traits?
I fully understand the standard would be providing an "incomplete toolbox" without them, and I can imagine use in a meta way, something like this:
template<typename In, template <typename> class Modifier>
struct Apply {
typedef typename Modifier<T>::type Out;
};
Apply<int, std::add_const>
Are there any other use cases for these traits which can be expressed syntactically, or are they just included "out of a sense of completeness" and for the occasional meta-use?
Those traits come from Boost and the proposal to add them to the standard, N1345, quotes Andrei Alexandrescu as saying:
"I understand the symmetry argument for adding add_const, add_volatile, add_cv, and add_pointer, however I would argue in favor of eliminating them. The language-provided equivalents are just simpler and nicer."
The same proposal also gives this rationale:
Author's Note: superficially the add_const, add_volatile and add_cv classes are irrelevant, since, for example, add_const::type is the same as T const, for all T (currently this does not apply to function types - but issue 295 addresses this). However the experience from boost is that several users have asked for these templates to be present in the library for the following reasons: (a) Some users find these more explicit - when combining transformation templates in particular, users like the kind of "built in documentation" that these templates provide. (b) Not all users are aware that cv-qualifying a reference is allowed and has no effect, or that cv-qualifying a type that is already cv-qualified is allowed and has no effect. (c) Compilers may emit warnings when cv-qualifying a type that is a reference, or already has a cv-qualifier, these templates can be implemented such that these messages are suppressed in these cases.
Also, for add_reference (renamed to add_lvalue_reference in the standard):
Author's Note: the add_reference template was one of the original motivations behind the boost type traits library. However the resolution to issue 106 makes the template appear largely redundant. In spite of that add_reference may well be useful in suppressing compiler warnings when inadvertently creating references to references in template code.
These traits are provided for occasional meta-use. It makes it possible to transport wanted cv-qualifiers around in meta programming.
template<class T,template<class> class Trait>
struct transform
{
/* working with T creating newT*/
typedef Trait<newT>::type type;
};
template<class T>
struct special_transform
: transfrom<T, std::add_const>
{};
In this case you could not replace std::add_const with const.
add_const can be used to resolve type deduction conflicts.
template <typename T>
class wrapper;
template <typename T>
bool operator==(wrapper<T> const& w, T const& t);
Problems arise if we use wrapper<T const>:
wrapper<int const> w = { 42 };
assert(w == 42); // Error: conflicting deduced types
T is simultaneously deduced to be both int and int const. This can be resolved using add_const:
template <typename T>
bool operator==(wrapper<T> const& w, add_const_t<T>& t);
The only use case I know is illustrated below:
struct F
{
bool f() const { return true; }
bool f() { return false; }
};
assert(!F{}.f())
assert(std::add_const_t< F >{}.f());
It also needed for testing of cv-ref-qualified member-functions functionality, that may differs for different overloadings (only for lref-qualified modern C++ has handy std::as_const function):
struct F
{
int g() & { return 1; }
int g() const & { return 2; }
int g() && { return 3; }
int g() const && { return 4; }
};
F f;
assert(f.g() == 1);
assert(std::as_const(f).g() == 2);
assert(F{}.g() == 3);
assert(std::add_const_t< F >{}.g() == 4); // rarely needed, but if needed, then it helps

How can I get the C++ compiler to deduce T indirectly?

My template-fu is rather weak. I have this code:
template<typename T>
void Foo(void(*func)(T*)) { }
void Callback(int* data) { }
int Test()
{
Foo(Callback);
}
...but I'd like something more readable than C's nasty function pointer syntax of void(*func)(T*).
Someone on my team suggested this:
template<typename T>
struct Types
{
typedef void Func(T*);
};
template<typename T>
void Foo2(typename Types<T>::Func* func) {}
void Test2()
{
Foo2(Callback); // could not deduce template argument for 'T'
Foo2<int>(Callback); // ok
}
(I'm still debating whether this is actually more readable, but that's a separate issue.)
How can I help the compiler figure out what T is without needing to explicitly specify it in the caller?
You can extract T from the function type using a traits class.
template<class F>
struct CallbackTraits;
template<class T>
struct CallbackTraits<void(*)(T)>
{
typedef T ArgumentType;
};
Your example can be modified like this:
template<typename F>
void Foo(F func)
{
typedef typename CallbackTraits<F>::ArgumentType T;
}
void Callback(int* data) { }
int Test()
{
Foo(Callback);
}
This technique is used in the boost type-traits library:
http://www.boost.org/doc/libs/1_57_0/libs/type_traits/doc/html/boost_typetraits/reference/function_traits.html
This blog post goes into a bit more detail about the implementation of the technique:
https://functionalcpp.wordpress.com/2013/08/05/function-traits/
Unfortunately this approach hides the information in the signature of Foo about the constraints on the argument passed in. In the above example the argument must be a function of type void(T*).
This alternative syntax does the same as the original example while being slightly more readable:
template<typename T>
void Foo(void func(T*)) { }
Another alternative syntax that may be more readable can be achieved using c++11's alias templates as follows:
template<typename T>
using Identity = T;
template<typename T>
void Foo(Identity<void(T*)> func) { }
Unforunately the latest MSVC fails to compile this, reporting an internal compiler error.
You won't be able to deduce the type based on a nested name: there is no reason why different instantiations of the outer type won't define an identical inner type. You could use a using alias, though:
template <typename T>
using Function = auto (*)(T*) -> void;
template <typename T>
void Foo(Function<T>) {
}
Personally, I would recommend against using any of that, however: in practice it seems much more advisable to actually take a function object which later allows using object with suitable function call operators to be used. For callbacks it is quite common that you'll need to pass in some auxiliary data. That is, you would either use an unconstrained template or one which takes a type-erased type, depending on what you want to do exactly:
template <typename Fun>
void Unconstrained(Fun fun) {
}
template <typename T>
void TypeErased(std::function<void(T*)> fun) {
}
The unconstrained version has the advantage that it can potentially inline the function call but it has the disadvantage that every function object type creates a new instantiation and that the argument types are likely to vary. The type-erased version effectively has to do something like a virtual function call but there is just one instantiation of the function template (per argument type T, of course).
Admittedly, the type-erased version's type won't be deduced from a function pointer (or any other argument which isn't a std::function<void(X*)>), i.e., you may want to have a forwarding function
template <typename T>
void TypeErased(Function<T> fun) {
TypeErased(std::function<void(T)>(fun));
}
In C++98 and C++03 template argument deduction only works with functions (and methods).
I don't think the picture changed in the more recent standards.

How do I check my template class is of a specific classtype?

In my template-ized function, I'm trying to check the type T is of a specific type. How would I do that?
p/s I knew the template specification way but I don't want to do that.
template<class T> int foo(T a) {
// check if T of type, say, String?
}
Thanks!
Instead of checking for the type use specializations. Otherwise, don't use templates.
template<class T> int foo(T a) {
// generic implementation
}
template<> int foo(SpecialType a) {
// will be selected by compiler
}
SpecialType x;
OtherType y;
foo(x); // calls second, specialized version
foo(y); // calls generic version
If you don't care about compile-time, you may use boost::is_same.
bool isString = boost::is_same<T, std::string>::value;
As of C++11, this is now part of the standard library
bool isString = std::is_same<T, std::string>::value
hmm because I had a large portion of
same code until the 'specification'
part.
You can use overloading, but if a large part of the code would work for any type, you might consider extracting the differing part into a separate function and overload that.
template <class T>
void specific(const T&);
void specific(const std::string&);
template <class T>
void something(const T& t)
{
//code that works on all types
specific(t);
//more code that works on all types
}
I suppose you could use the std::type_info returned by the typeid operator
I suspect someone should tell you why it might not be a good idea to avoid using overloading or specialization. Consider:
template<class T> int foo(T a) {
if(isAString<T>()) {
return a.length();
} else {
return a;
}
}
You might think on a first sight that it will work for int too, because it will only try to call length for strings. But that intuition is wrong: The compiler still checks the string branch, even if that branch is not taken at runtime. And it will find you are trying to call a member function on non-classes if T is an int.
That's why you should separate the code if you need different behavior. But better use overloading instead of specialization, since it's easier to get a clue how things work with it.
template<class T> int foo(T a) {
return a;
}
int foo(std::string const& a) {
return a.length();
}
You have also better separated the code for different paths of behavior. It's not all anymore clued together. Notice that with overloading, the parameters may have different type forms and the compiler will still use the correct version if both match equally well, as is the case here: One can be a reference, while the other can not.
You can check using type_traits (available in Boost and TR1) (e.g. is_same or is_convertible) if you really want to avoid specialization.
You can perform static checks on the type that you have received (look at the boost type traits library), but unless you use specialization (or overloads, as #litb correctly points out) at one point or another, you will not be able to provide different specific implementations depending on the argument type.
Unless you have a particular reason (which you could add to the question) not to use the specialization in the interface just do specialize.
template <> int subtract( std::string const & str );
If you are using C++11 or later, std::is_same does exactly what you want:
template <typename T>
constexpr bool IsFloat() { return std::is_same<T, float>::value; }
template <typename T>
void SomeMethodName() {
if (IsFloat<T>()) {
...
}
}
http://en.cppreference.com/w/cpp/types/is_same

Handling of references in C++ templates

I currently have a function template, taking a reference, that does something in essence equivalent to:
template <typename T>
void f(T& t)
{
t = T();
}
Now, I can call:
int a;
f(a);
To initialize my variable a.
I can even do:
std::vector<int> a(10);
f(a[5]);
However, this will fail:
std::vector<bool> a(10);
f(a[5]);
The reason being a[5] returns an object with reference semantic, but not a reference. So I need to be able to write:
template <typename T>
void f(T a)
{
a = T();
}
But if I add this new template and try to compile the first example (with int), I obtain the following error:
test_multi_tmpl.cc: In function ‘int main()’:
test_multi_tmpl.cc:20: error: call of overloaded ‘f(int&)’ is ambiguous
test_multi_tmpl.cc:6: note: candidates are: void f(T&) [with T = int]
test_multi_tmpl.cc:12: note: void f(T) [with T = int]
Any ideas how to solve this? I wouldn't like to overload f just for std::vector<bool>::reference as this construct might appears in other places ...
I think specialising f for std::vector<bool>::reference is your only option.
Note that using std::vector<bool> is probably a bad idea in the first place (the std::vector<bool> specialisation is deprecated for future versions of the c++ language) so you could just use std::deque<bool> instead.
I'm not sure whether you already know about this...
The specialization std::vector is not really a STL container because it does not meet the necessary requirements. In particular, it's not possible to created proxied containers that satisfy the STL concepts because of the reference semantics (you can't fake a reference). Check this article for more information. (Also, as Autopulated mentioned there should be a compiler directive to provide control over std::vector in the future C++ Standard.)
A simple workaround that could solve your problem is by overloading function f for this particular type (and others if they appear and are not many). Notice that this is an overload and not an specialization. You might also wanna check this for why not specialize function templates.
void f(std::vector<bool>::reference t)
{
/* ... */
}
There are two ways to do this, one is, as you suggest, specialize for std::vector<bool>::reference. The other is by using type-traits dispatching.
template <class T>
void f (T& t) {
f_impl(t, is_reference_wrapper_type<T>());
}
template <class T>
void f_impl(T& t, mpi::false_) {
t = T();
}
template <class T>
void f_impl(T& t, mpi::true_) {
// do nothing, or voodoo here
}
Note the code above is un-tested and there would be more complex means of dispatching based on a trait -- or a set of traits -- in this situation.
This would also mean that you would need to implement is_reference_wrapper_type like so:
template <class T>
struct is_reference_wrapper_type : mpi::false_ {};
template <>
struct is_reference_wrapper_type<std::vector<bool>::reference> : mpi::true_ {};
Using traits or template specialization would make it work.