Sfinae and template specialization - c++11

I want to provide a function in my library:
namespace Foo {
template<typename G>
typename std::enable_if<std::is_fundamental<G>::value, std::vector<uint8_t>>::type toBytes(const G &g) {
//something here
}
}
However I want to provide basic implementation only for basic types. If an user wants to add a specialization I thought it was enough to write in the user code:
namespace Foo {
template<>
std::vector<uint8_t> toBytes<struct A>(const struct A &g) {
//something here
}
}
However it doesn't compile, Gcc provides this error:
std::vector Foo::toBytes(const A&)’ does not match any
template declaration
Am I missing anything?

I would make use of the C++20 feature Concept.Concepts naturally also support template specialization:
#include <iostream>
namespace detail {
template<typename T, typename U >
concept SameHelper = std::is_same_v<T, U>;
}
template<typename T, typename U >
concept same_as = detail::SameHelper<T, U> && detail::SameHelper<U, T>;
template<typename T>
concept fundamental = same_as<T, bool> || same_as<T, char>;
template<typename T>
void toBytes(T) {std::cout << "I am not fundamental" << std::endl;}
template<fundamental T>
void toBytes(T){std::cout << "I am fundamental" << std::endl;}
int main() {
toBytes(true);
toBytes('c');
toBytes(1);
}
You can run the code online to see the following output:
I am fundamental
I am fundamental
I am not fundamental

Related

Name hiding of base member in inherited variadic template

I have the following code that works in VS2017:
template <typename ... Args>
struct Composite: Args...
{
using Composite<Args...>::foo;
void foo(float exposure)
{
return this->foo(*this, exposure);
}
void internalBar(float e) { std::cout << "it works" << e; }
};
it is used in this way:
struct A
{
template <typename T>
void foo(T& device, float exposure)
{
device.internalBar(exposure);
}
};
struct B
{};
struct C
{};
int main(int argc, char *argv[])
{
auto u = Composite<A, B, C>();
u.foo(5.0f);
return 0;
}
The problem is with the line using Composite<Args...>::foo because is not in the c++ standard. That's why it does not works with gcc: Composite<Args...> is not a base class of Composite.
I had to use this line because Composite hides the foo of A.
How can i pull in the scope of a single packed parameter?
Thanks.
You can solve lots of problems by adding a level of indirection.
template <typename ... Args>
struct CompositeFooWrapper: Args...
{
};
template <typename ... Args>
struct Composite: CompositeFooWrapper<Args...>
{
using CompositeFooWrapper<Args...>::foo;
Demo: https://godbolt.org/z/ddsKc3rvx

why is specialised template being called?

I created a template called debug which is indirectly invoked through the function errorMsg. I then specialised the template to account for char * (code w/comments below hopefully helps with explanations)
After some playing around I was surprised that even though I defined the template specialisations at a point after they're called in errorMsg(), they were still being used.
I would have assumed because it had not yet been defined at the point the main template would instantiate a default copy or an error would occur
Any help resolving this issue would be great thanks
#include "header.h"
int main()
{
//std::vector<std::string> s_vec{"abc","cede","rfind"};
int i = 3;
int *j = &i;
errorMsg(std::cout,"hey"); //<---calls debug
}
//defined specialisations after its invoked inside errorMsg
template <>
inline std::string debug(char * p)
{
std::cout<<"specialsed char"<<std::endl;
return debug(std::string(p));
}
template <>
inline std::string debug(const char *p)
{
std::cout<<"specialised const char"<<std::endl;
return debug(std::string(p));
(header.h)
#include <iostream>
#include <sstream>
#include <string>
//(1)
template <typename T>
std::string debug(const T&s)
{
std::cout<<"unspecialised obj"<<std::endl;
std::ostringstream oss;
oss<<s;
return oss.str();
}
//(2)
template <typename T>
std::string debug(T *ptr)
{
std::cout<<"unspecialised raw ptr"<<std::endl;
std::ostringstream oss;
oss << "pointer: "<<ptr;
if (ptr)
{
oss<<" "<<debug(*ptr);
}
else
oss<<" null pointer";
return oss.str();
}
template <typename T, typename... Args> void print(std::ostream &os,const T &t,const Args&...rest);
template <typename T> std::ostream &print(std::ostream &os, const T &t);
template <typename... Args>
void errorMsg(std::ostream &os,Args &&...args)
{
print(os,debug(std::forward<Args>(args))...); //debug called here
}
template <typename T>
std::ostream &print(std::ostream &os, const T &t)
{
return os<<t<<std::endl;
}
template <typename T, typename... Args>
void print(std::ostream &os,const T &t,const Args&...rest)
{
os<<t<<", ";
print(os,rest...);
}
result:
specialised const char
unspecialised obj
hey
[temp.expl.spec]/6 If a template, a member template or a member of a class template is explicitly specialized then that specialization shall be declared before the first use of that specialization that would cause an implicit instantiation to take place, in every translation unit in which such a use occurs; no diagnostic is required.
Your program is ill-formed; no diagnostic required.

typedef on std::function to shorten it

I want to shorten the typing of std::function<bool(int)> to just func<bool(int)> in my own namespace. It's just a personal preference.
I tried the following code below but I'm encountering syntax errors.
//MyHeader.hpp
template<typename S> struct func; //signature
template<typename R, typename ...Args>
struct func<R(Args...)>;
template<typename R, typename ...Args>
//typename typedef std::function<R(Args...)> func; // <R(Args...)>;
using func = std::function<R(Args...)>; //<---closes to solve.
//SomeClass.cpp
func<bool(int)> f;
func<void()> g = [] {
//some code here...
}
How can I achieve this?
Your solution is a bit too complicated, you might want to use the alias template like this
#include <functional>
template <class Fct> using func = std::function<Fct>;
which you can instantiate the in the desired way:
int test(bool) { return 1; }
func<int(bool)> f(test);
funct<void()> g = [](){};

Checking for method parameter at runtime with SFINAE

I know one could check the existence of a particular method using expression SFINAE in C++11 as follows.
What I can't find though, is an example to do the same, checking method arguments as well. In particular I would like to match a method that takes a const parameter.
#include <iostream>
struct A
{
void method() const
{
return;
}
};
template <typename T, typename = std::string>
struct hasMethod
: std::false_type
{
};
template <typename T>
struct hasMethod<T, decltype(std::declval<T>().method())>
: std::true_type
{ };
int main() {
std::cout << hasMethod<A>::value << std::endl;
}
In reality I would like the hasMethod:: to match
void method(const Type& t) const
{
return;
}
What is the syntax to pass to decltype?
I have tried:
struct hasMethod<T, decltype(std::declval<T>().method(const int&))>
: std::true_type
but it obviously doesn't work.

Is it possible for C++ to write a template that takes a Container as parameter?

I would try to make my point clear with an example:
We have
template <class RandomAccessIterator>
void sort (RandomAccessIterator first, RandomAccessIterator last);
But I'm thinking if it is ok to make it more convenient:
template <typename T> void sort(std::vector<T>& container) {
std::sort( container.begin(), container.end() );
}
template <typename T> void sort(std::list<T>& container);
template <typename T> void sort(std::array<T>& container);
//e.t.c
You know there are many container types, it is possible to code once for all the container types?
void sort(ContainerType<ElementType> &container);
//and container should have begin() and end() methods,
//otherwise the compiler would warn me.
You are talking about concepts in C++. The idea is discussed for a long time for now, but they are still not in the standard. See here:
template<Sortable Cont>
void sort(Cont& container);
The work is close to the end for now, several experimental implementations are already available, and we expect them to hit C++17, hopefully. The nicest thing about concepts is their straightforward error messages:
list<int> lst = ...; // oops, bidirectional iterators
sort(lst); // error: 'T' is not a/an 'Sortable' type
In modern compilers, errors related to templatized code are very confusing. Compare with this example, compiled with Visual Studio 2013:
std::list<int> l;
std::sort(l.begin(), l.end());
// error C2784: 'unknown-type std::operator -(std::move_iterator<_RanIt> &,const std::move_iterator<_RanIt2> &)' : could not deduce template argument for 'std::move_iterator<_RanIt> &' from 'std::_List_iterator<std::_List_val<std::_List_simple_types<int>>>'
// error C2784: 'unknown-type std::operator -(const std::reverse_iterator<_RanIt> &,const std::reverse_iterator<_RanIt2> &)' : could not deduce template argument for 'const std::reverse_iterator<_RanIt> &' from 'std::_List_iterator<std::_List_val<std::_List_simple_types<int>>>'
// error C2784: 'unknown-type std::operator -(const std::_Revranit<_RanIt,_Base> &,const std::_Revranit<_RanIt2,_Base2> &)' : could not deduce template argument for 'const std::_Revranit<_RanIt,_Base> &' from 'std::_List_iterator<std::_List_val<std::_List_simple_types<int>>>'
There is even a tag on SO: c++-concepts.
Its easy to write a sort function that works for any container. Just write:
template<class C>
void sort(C& container) {
std::sort( container.begin(), container.end() );
}
However, if you want your sort function to be picked ONLY for containers, it becomes a little bit more difficult: As concepts are not yet available, you have to write your own type trait for all containers and use SFINAE.
Edit: Come to think of it, as any class with a random access iterator is probably a container anyway, it should be enough to write the following (without the need for a container trait):
#include <type_traits>
template<class C>
typename std::enable_if<std::is_same<typename std::iterator_traits<typename C::iterator>::iterator_category, std::random_access_iterator_tag>{}>::type
sort(C& container) {
std::sort( container.begin(), container.end() );
}
#include <iterator>
#include <utility>
#include <type_traits>
namespace detail
{
using std::begin;
template <typename T, typename = void>
struct has_begin : std::false_type {};
template <typename T>
struct has_begin<T, decltype(void(begin(std::declval<T&>())))> : std::true_type {};
using std::end;
template <typename T, typename = void>
struct has_end : std::false_type {};
template <typename T>
struct has_end<T, decltype(void(end(std::declval<T&>())))> : std::true_type {};
}
template <typename T> using has_begin = detail::has_begin<T>;
template <typename T> using has_end = detail::has_end<T>;
Usage:
template <typename ContainerType>
void sort(ContainerType& container)
{
static_assert(has_begin<ContainerType>{} && has_end<ContainerType>{},
"Invalid container type");
}
Tests:
#include <vector>
#include <list>
namespace X
{
struct A {};
A* begin(A&) { return {}; }
A* end(A&) { return {}; }
}
struct B {};
int main()
{
std::vector<int> v; sort(v); // OK
std::list<int> l; sort(l); // OK
X::A a; sort(a); // OK
int arr[3]{}; sort(arr); // OK
B b; sort(b); // error: Invalid container type
}
DEMO
At the time of answering this question, MikeMB's answer gives the approach but doesn't compile. But here is my attempt. A much lesser complicated approach. You will have to overload SortHelper to accept comparator as well.
#include <iostream>
#include <vector>
#include <list>
#include <iterator>
#include <algorithm>
template<typename C>
void SortHelper(C& container, std::random_access_iterator_tag)
{
std::sort(std::begin(container), std::end(container));
}
template<typename C>
void SortHelper(C& container, std::bidirectional_iterator_tag)
{
container.sort();
}
template<class C>
void sort(C& container)
{
SortHelper(container, typename std::iterator_traits<typename C::iterator>::iterator_category());
}
int main()
{
std::vector<int> ints1 { 3, 2, 1 };
std::list<int> ints2 { 3, 2, 1 };
sort(ints1);
sort(ints2);
std::cout << "printing ints1\n";
for (auto e : ints1 ) { std::cout << e << "\n" ; }
std::cout << "printing ints2\n";
for (auto e : ints2 ) { std::cout << e << "\n" ; }
}
Output
printing ints1
1
2
3
printing ints2
1
2
3

Resources