I was reading somewhere that it would be possible to do c++ metaprogramming using function overloads instead of SFINAE. I came up with a toy exercise to test this. I want to detect if a particular type is a nested vector. My code that works is
#include <type_traits>
#include <vector>
#include <iostream>
template <typename T> struct Tag {};
template <typename TLeaf >
consteval bool is_nested(Tag<TLeaf>)
{
return true;
}
template <typename TLeaf , typename TSub, typename enable = std::enable_if_t< !std::is_same_v<TLeaf, TSub> >>
consteval bool is_nested(Tag<TSub>)
{
return false;
}
template <typename TLeaf , typename TSub>
consteval bool is_nested(Tag<std::vector<TSub>>)
{
return is_nested<TLeaf>(Tag<TSub>{});
}
template <typename TSub, typename TLeaf>
consteval bool is_nested()
{
return is_nested<TLeaf>(Tag<TSub>{});
}
int main(){
using type_nested1 = std::vector<std::string>;
using type_nested2 = std::vector<type_nested1>;
std::cout << is_nested<type_nested1, std::string>() << std::endl;
std::cout << is_nested<type_nested2, std::string>() << std::endl;
std::cout << is_nested<int, std::string>() << std::endl;
std::cout << is_nested<type_nested1, int>() << std::endl;
std::cout << is_nested<type_nested2, int>() << std::endl;
}
https://godbolt.org/z/zGEf9cej5
and the output is
Program returned: 0
Program stdout
1
1
0
0
0
but I'm disappointed that I had to use std::enable_if_t to disambiguate the two overloads. Can this be rewritten to keep the spirit of the exercise but eliminate any SFINAE crud from the solution?
If you just pass two arguments all the way through, you don't need to do anything special:
template <typename T> struct Tag {};
template <typename T> inline constexpr Tag<T> tag;
template <typename T>
consteval bool is_nested(Tag<T>, Tag<T>) {
return true;
}
template <typename L, typename T>
consteval bool is_nested(Tag<L>, Tag<T>) {
return false;
}
template <typename L, typename T>
consteval bool is_nested(Tag<std::vector<L>>, Tag<T>) {
return is_nested(tag<L>, tag<T>);
}
template <typename L, typename T>
consteval bool is_nested() {
return is_nested(tag<L>, tag<T>);
}
Where you can combine the first two overloads into one:
template <typename L, typename T>
consteval bool is_nested(Tag<L>, Tag<T>) {
return std::is_same_v<L, T>;
}
Which was also true of your original example (but it's easier to see if all your parameters are in the same spot):
template <typename TLeaf , typename TSub>
consteval bool is_nested(Tag<TSub>) {
return std::is_same_v<TLeaf, TSub>;
}
Related
I want to use SFINAE to select specific function to be compiled in a template class with non-type
argument, here is how i do it:
#include<iostream>
#include<type_traits>
template <bool T>
struct is_right
{
template <class Q = std::bool_constant<T>>
typename std::enable_if<std::is_same<Q, std::bool_constant<true>>::value, bool>::type check()
{
return true;
}
template <class Q = std::bool_constant<!T>>
typename std::enable_if<std::is_same<Q, std::bool_constant<false>>::value, bool>::type check()
{
return false;
}
};
int main() {
is_right<false> is_fs;
is_right<true> is_ri;
if(!is_fs.check() && is_ri.check()){
std::cout << "It is right!" << std::endl;
}
return 0;
}
I compile it with g++ 8.1.0 using c++17 standard.
But the compliler gives me a error said "no matching function for call to 'is_right::check()'".
However if I just substitue the non-type template argument with a type argument, it works:
#include<iostream>
#include<type_traits>
template <class T>
struct is_right
{
template <class Q = T>
typename std::enable_if<std::is_same<Q, std::bool_constant<true>>::value, bool>::type check()
{
return true;
}
template <class Q = T>
typename std::enable_if<std::is_same<Q, std::bool_constant<false>>::value, bool>::type check()
{
return false;
}
};
int main() {
is_right<std::bool_constant<false>> is_fs;
is_right<std::bool_constant<true>> is_ri;
if(!is_fs.check() && is_ri.check()){
std::cout << "It is right!" << std::endl;
}
return 0;
}
Is there any essential difference between them?
The default template parameter for the second check function is wrong, it should be std::bool_constant<T> and not std::bool_constant<!T>.
The call is_fs.check() has no matching function, because you're testing is_same<bool_constant<false>, bool_constant<true>> in the first overload and is_same<bool_constant<true>, bool_constant<false>> in the second overload.
Also note that right now the call is_ri.check() is ambiguous, because both check functions are valid in is_right<true>.
I have this code :
#include <iostream>
#include <type_traits>
template <typename T, std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
using FloatingPoint = T;
template <typename T, std::enable_if_t<std::is_integral_v<T>, bool> = true>
using Integral = T;
template <typename T> void f(Integral<T>) { std::cout << "integral" << std::endl; }
template <typename T> void f(FloatingPoint<T>) {
std::cout << "floating point" << std::endl;
}
int main() {
f(5);
return 0;
}
This code does not compile :
prog.cc:12:28: error: redefinition of 'f'
template <typename T> void f(FloatingPoint<T>) {
^
prog.cc:10:28: note: previous definition is here
template <typename T> void f(Integral<T>) { std::cout << "integral" << std::endl; }
^
prog.cc:17:3: error: no matching function for call to 'f'
f(5);
^
prog.cc:12:28: note: candidate template ignored: substitution failure [with T = int]
template <typename T> void f(FloatingPoint<T>)
It is kind of weird because it understands that there is a substitution failure, but he does not want to take the first overloading.
So I know two ways to handle with this. The first thing is to put the enable_if either as an argument, or as a return type. Is there other way to handle this in a "pretty" way? Or have I to use one of the two solutions I wrote before?
The goal is to write clean code. I'd love to use concept from C++20 but it seems that it is not possible everywhere yet (I mean GCC, Clang, and MSVC) https://en.cppreference.com/w/cpp/compiler_support. Or this website is not up to date and both MSVC (2019), GCC 10 and clang 9 supports well concept?
I think your problem is that FloatingPoint and Integral directly depend on T. Re-organizing your code you can get the following. You can run the code here: https://onlinegdb.com/By-T4bBfB .
#include <iostream>
#include <type_traits>
template<typename T>
using isFloatingPoint = std::enable_if_t<std::is_floating_point_v<T>, bool>;
template<typename T>
using isIntegral = std::enable_if_t<std::is_integral_v<T>, bool>;
template <typename T, isIntegral<T> = true>
void f(T) {
std::cout << "integral" << std::endl;
}
template <typename T, isFloatingPoint<T> = true>
void f(T) {
std::cout << "floatingPoint" << std::endl;
}
int main() {
f(5);
f(5.0);
}
Alternatively, given that you are using C++17 you can use if constexpr. You say that is not scalable in the comments. In my opinion it is, but most likely I do not understand the constraints you are operating under. For this see https://godbolt.org/z/gLc7YE . if constexpr allows you to have only one function and even different return types pretty easily.
#include <type_traits>
#include <string>
template<typename T>
auto f(T){
if constexpr (std::is_floating_point_v<T>){
return 0.0f;
}
else if constexpr (std::is_integral_v<T>){
return 0;
} else {
return 0.0;
}
}
int main() {
static_assert(std::is_same_v<decltype(f(5)), int>, "ERROR 1");
static_assert(std::is_same_v<decltype(f(5.0)), float>, "ERROR 2");
static_assert(std::is_same_v<decltype(f(std::string{"JJ"})), double>, "ERROR 3");
return 0;
}
Finally I would also to note that having a different return type for the two f() in your code would solve your issue (run here the code: https://onlinegdb.com/By-9HZrMS):
#include <iostream>
#include <type_traits>
template <typename T, std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
using FloatingPoint = T;
template <typename T, std::enable_if_t<std::is_integral_v<T>, bool> = true>
using Integral = T;
template <typename T> int f(Integral<T>) { std::cout << "integral" << std::endl; return 0;}
template <typename T> float f(FloatingPoint<T>) {
std::cout << "floating point" << std::endl;
return 0.0f;
}
int main() {
f(5);
f(5.0);
return 0;
}
I am trying to write an stream operator << that can output to a std::tuple of streams instead of one stream. So basically, I am trying to write the Unix tee command in c++, and do:
std::tie(std::cout,std::clog) << 1;
I tried to write the stream operator recursively using variadic template programming in c++11. What I have so far is as in the code below. But the code doesn't compile, and the error message is quite long.
My question is, how to fix the code to make it work?
The first error message from compiling with g++ -std=c++11 (gcc-4.8.1) was:
test.cpp:24:33: error: no match for 'operator<<' (operand types are 'std::tuple<std::basic_ostream<char, std::char_traits<char> >&, std::basic_ostream<char, std::char_traits<char> >&>' and 'int')
std::tie(std::cout,std::cout) << 1;
P.S. I searched SO, and there is some posts about writing a composite stream object. The code involves a lot of internals of stream and streambuf. What I seek here is a simple/naive solution in a dozen of lines to accomplish a similar task.
Thanks
My code:
#include <iostream>
#include <tuple>
template<typename _Value, typename Head, typename ... Tail>
struct _tee_stream {
static std::tuple<Head,Tail...>& _print(std::tuple<Head,Tail...>& _s, const _Value& _t) {
return std::make_tuple(std::get<0>(_s) << _t ,_tee_stream<Tail...,_Value>::_print(_s,_t));
}
};
template<typename _Value>
struct _tee_stream<_Value, std::tuple<>> {
static std::tuple<>& _print(std::tuple<>& _s, const _Value& _t) {
return _s;
}
};
template< typename _Value, typename... _TElements>
std::tuple<_TElements...>& operator<<(std::tuple<_TElements...>& _s, const _Value& _t) {
return _tee_stream<_Value, std::tuple<_TElements...>>::_print(_s, _t);
};
int main() {
std::tie(std::cout,std::cout) << 1; //no compile
std::make_tuple(std::cout,std::cout) << 1; //no compile either
}
UPDATE:
below is what worked for me based on #KerrekSB's example code:
#include <iostream>
#include <tuple>
#include <utility>
#include <fstream>
template <unsigned int N>
struct tee_stream
{
template <typename ...Args, typename T>
static std::tuple<Args...> & print(std::tuple<Args...> & t, T && x)
{
std::get<sizeof...(Args) - N>(t) << x;
tee_stream<N - 1>::print(t, std::forward<T>(x));
return t;
}
};
template <>
struct tee_stream<0>
{
template <typename ...Args, typename T>
static std::tuple<Args...> & print(std::tuple<Args...> &, T &&) {}
};
template <typename ...Args, typename T>
std::tuple<Args...> & operator<<(std::tuple<Args...> & t, T && x)
{
return tee_stream<sizeof...(Args)>::print(t, std::forward<T>(x));
}
template <typename ...Args, typename T>
std::tuple<Args...> & operator<<(std::tuple<Args...> && t, T && x)
{
return tee_stream<sizeof...(Args)>::print(t, std::forward<T>(x));
}
int main()
{
std::ofstream os("a.txt");
auto t = std::tie(std::cout, os);
t << "Foo" << "Bar\n";
std::tie(std::cout, os) << "Foo" << "Bar\n";
}
http://ideone.com/fBXuvP
#Jarod42,
Thanks a lot. Your code does the recursion/looping much more compactly than my prototype, and fixes the use of reference types in my code. The original test cases work as your demo showed. However, I still can't get your version compile if I use std::endl (instead of "Foo") or std::ofstream (instead of std::cout) in the stream, as shown below in the "not OK" lines. Any idea how to fix those as well?
#include <iostream>
#include <fstream>
#include <tuple>
#if 1 // Not in C++11 // make_index_sequence
#include <cstdint>
template <std::size_t...> struct index_sequence {};
template <std::size_t N, std::size_t... Is>
struct make_index_sequence : make_index_sequence<N - 1, N - 1, Is...> {};
template <std::size_t... Is>
struct make_index_sequence<0u, Is...> : index_sequence<Is...> {};
#endif // make_index_sequence
namespace detail
{
template <typename Tuple, typename T, std::size_t...Is>
Tuple output(const Tuple& t, const T& x, index_sequence<Is...>) {
return Tuple{(std::get<Is>(t) << x)...};
}
}
template <typename ...Args, typename T>
std::tuple<Args&...> operator<<(const std::tuple<Args&...>& t, const T& x) {
return detail::output(t, x, make_index_sequence<sizeof...(Args)>());
}
int main() {
std::ofstream os("aa.txt");
os << "Hi" << std::endl;
std::tie(std::cout, std::cout) << "Foo" << "Bar"; //OK
std::tie(std::cout, std::cout) << "Foo" << "Bar" << std::endl; //not OK on endl
std::tie(std::cout, os) << 1 << "Foo" << "Bar"; //not OK on ofstream
return 0;
}
You may use the following:
#if 1 // Not in C++11 // make_index_sequence
#include <cstdint>
template <std::size_t...> struct index_sequence {};
template <std::size_t N, std::size_t... Is>
struct make_index_sequence : make_index_sequence<N - 1, N - 1, Is...> {};
template <std::size_t... Is>
struct make_index_sequence<0u, Is...> : index_sequence<Is...> {};
#endif // make_index_sequence
// Helper. C++11 cannot use: auto f() { return foo(); }
// Usage: auto f() -> Return(foo())
#define Return(ret) decltype(ret) { return ret; }
namespace detail
{
template <typename Tuple, typename T, std::size_t...Is>
auto output(const Tuple& t, const T& x, index_sequence<Is...>) ->
Return (std::tie(std::get<Is>(t) << x...))
}
template <typename ...Args, typename T>
auto operator<<(const std::tuple<Args&...>& t, const T& x) ->
Return(detail::output(t, x, make_index_sequence<sizeof...(Args)>()))
live example
One problem lies with the argument: std::tuple<_TElements...>& _s.
Since you're attempting to use operator << with temporary values returned by std::tie and std::make_tuple, you're getting a compiler error because you can't bind the temporary to a reference.
A const reference (std::tuple<_TElements...> const& _s) would work though.
(You can use an rvalue reference too)
And don't use names that start with an underscore (_); those are reserved to the implementation.
While trying to answer one question here, I found this question:
How to recursively dereference pointer (C++03)?
Adapted code from the answer is following:
template<typename T> T& dereference(T &v) { return v; }
template<typename T> const T& dereference(const T &v) { return v; }
template <typename T>
typename std::enable_if<!std::is_pointer<T>::value, T&>::type
dereference(T *v) {
return dereference(*v);
}
However, in this test it is failing to dereference pointer-to-pointer into the value type:
template <typename T>
class A
{
public:
bool compare(T a, T b){
return dereference(a) < dereference(b);
}
};
int main()
{
int u = 10;
int *v = &u;
int **w = &v;
int i = 5;
int *j = &i;
int **k = &j;
A<int> a;
A<int*> b;
A<int**> c;
std::cout << a.compare(i, u) << std::endl;
std::cout << b.compare(j, v) << std::endl;
// This fails - 5 < 10 == 0
std::cout << **k << " < " << **w << " == " << c.compare(k, w) << std::endl;
return 0;
}
Obviously, w and k are derefenced only one time, which causes operator< to be called on two pointers.
I can fix this by adding the following:
template <typename T>
typename std::enable_if<!std::is_pointer<T>::value, T&>::type
dereference(T **v) {
return dereference(*v);
}
but then it will fail for int***.
Is there any way to make this recursively without adding levels manually?
Note This is just "theoretical" question.
This is possible with the use of a custom can_dereference trait:
template <typename T>
struct can_dereference_helper {
template <typename U, typename = decltype(*std::declval<U>())>
static std::true_type test(U);
template <typename...U>
static std::false_type test(U...);
using type = decltype(test(std::declval<T>()));
};
template <typename T>
struct can_dereference :
can_dereference_helper<typename std::decay<T>::type>::type {};
and some mutually-recursive functions with a bit'o tag dispatching:
template <typename T>
auto recursive_dereference(T&& t, std::false_type) ->
decltype(std::forward<T>(t)) {
return std::forward<T>(t);
}
template <typename T>
auto recursive_dereference(T&& t) ->
decltype(recursive_dereference(std::forward<T>(t), can_dereference<T>{}));
template <typename T>
auto recursive_dereference(T&& t, std::true_type) ->
decltype(recursive_dereference(*std::forward<T>(t))) {
return recursive_dereference(*std::forward<T>(t));
}
template <typename T>
auto recursive_dereference(T&& t) ->
decltype(recursive_dereference(std::forward<T>(t), can_dereference<T>{})) {
return recursive_dereference(std::forward<T>(t), can_dereference<T>{});
}
See it work live at Coliru. This may seem like overkill compared to Kerrek's answer, but I went for a generic approach that will dereference anything that supports operator*. I'll let you decide which tool fits your problem best.
You can do it with a trait to compute the ultimate return type, here dubbed remove_all_pointers:
#include <type_traits>
template <typename T> struct remove_all_pointers
{ typedef typename std::remove_reference<T>::type type; };
template <typename T> struct remove_all_pointers<T *>
{ typedef typename std::remove_reference<T>::type type; };
template <typename T>
T & dereference(T & p)
{ return p; }
template <typename U>
typename remove_all_pointers<U>::type & dereference(U * p)
{ return dereference(*p); }
int main(int argc, char * argv[])
{
return dereference(argv);
}
You may need to add CV-variants; I'm still thinking about that.
Is there an easy way to do the following with C++11 & Boost:
use the standard definitions of std::hash whenever available from <functional>
use boost::hash_value to define std::hash in those cases where std::hash is missing but boost::hash_value is available in <boost/functional/hash.hpp>.
For example:
std::hash<std::vector<bool>> should come from the standard library,
std::hash<std::vector<unsigned>> should be implemented with boost::hash_value.
The first idea that comes to mind is to use SFINAE and try std::hash<> if possible and otherwise use boost::hash_value(), like this:
#include <string>
#include <functional>
#include <type_traits>
#include <boost/functional/hash.hpp>
struct my_struct_0 {
std::string s;
};
template <typename T>
struct has_std_hash_subst { typedef void type; };
template <typename T, typename C = void>
struct has_std_hash : std::false_type {};
template <typename T>
struct has_std_hash<
T,
typename has_std_hash_subst<decltype( std::hash<T>()(T()) ) >::type
> : std::true_type {};
template <typename T>
static typename std::enable_if<has_std_hash<T>::value, size_t>::type
make_hash(const T &v)
{
return std::hash<T>()(v);
}
template <typename T>
static typename std::enable_if<(!has_std_hash<T>::value), size_t>::type
make_hash(const T &v)
{
return boost::hash_value(v);
}
int main()
{
make_hash(std::string("Hello, World!"));
make_hash(my_struct_0({ "Hello, World!" }));
}
Unfortunately, there is always a default specialization of std::hash that triggers static_assert failure. This may not be the case with other libraries but it is the case with GCC 4.7.2 (see bits/functional_hash.h:60):
/// Primary class template hash.
template<typename _Tp>
struct hash : public __hash_base<size_t, _Tp>
{
static_assert(sizeof(_Tp) < 0,
"std::hash is not specialized for this type");
size_t operator()(const _Tp&) const noexcept;
};
So the above SFINAE approach doesn't work — static_assert in there is a show-stopper. Therefore, you cannot really determine when std::hash is available.
Now, this does not really answer your question but might come handy — it is possible to do this trick the other way around — check for Boost implementation first and only then fall back to std::hash<>. Consider the below example that uses boost::hash_value() if it is available (i.e. for std::string and my_struct_0) and otherwise uses std::hash<> (i.e. for my_struct_1):
#include <string>
#include <functional>
#include <type_traits>
#include <boost/functional/hash.hpp>
struct my_struct_0 {
std::string s;
};
struct my_struct_1 {
std::string s;
};
namespace boost {
size_t hash_value(const my_struct_0 &v) {
return boost::hash_value(v.s);
}
}
namespace std {
template <>
struct hash<my_struct_1> {
size_t operator()(const my_struct_1 &v) const {
return std::hash<std::string>()(v.s);
}
};
}
template <typename T>
struct has_boost_hash_subst { typedef void type; };
template <typename T, typename C = void>
struct has_boost_hash : std::false_type {};
template <typename T>
struct has_boost_hash<
T,
typename has_boost_hash_subst<decltype(boost::hash_value(T()))>::type
> : std::true_type {};
template <typename T>
static typename std::enable_if<has_boost_hash<T>::value, size_t>::type
make_hash(const T &v)
{
size_t ret = boost::hash_value(v);
std::cout << "boost::hash_value(" << typeid(T).name()
<< ") = " << ret << '\n';
return ret;
}
template <typename T>
static typename std::enable_if<(!has_boost_hash<T>::value), size_t>::type
make_hash(const T &v)
{
size_t ret = std::hash<T>()(v);
std::cout << "std::hash(" << typeid(T).name()
<< ") = " << ret << '\n';
return ret;
}
int main()
{
make_hash(std::string("Hello, World!"));
make_hash(my_struct_0({ "Hello, World!" }));
make_hash(my_struct_1({ "Hello, World!" }));
}
Hope it helps.
UPDATE: Perhaps you could use the hack described here as pointed out by #ChristianRau and make the first SFINAE approach work! Though it is very dirty :)
My answer might not be correct, but I will try to explain why I think that the answer is no.
I don't think that std::hash<T> and boost:hash<T> can be used interchangeably, so I've tried hiding object creation (even if this is not perfect solution), and return their result, which is size_t. Method should be of course chosen at compile time, so function dispatch is what comes to my mind, sample code:
template <typename T>
size_t createHash(const T& t, false_type)
{
return boost::hash<T>()(t);
}
template <typename T>
size_t createHash(const T& t, true_type)
{
return std::hash<T>()(t);
}
template<typename T>
size_t createHash(const T& t)
{
return createHash<T>(t, std::is_XXX<T>::type());
}
int main()
{
vector<unsigned> v; v.push_back(1);
auto h1 = createHash(v);
cout << " hash: " << h1;
//hash<vector<unsigned> > h2;
}
The idea of this code is simple: if you can construct type of type std::hash<T>, choose second implementation, if not - choose first one.
If the first implementation is chosen, code compiles without a problem, you can check it by using fe. std::is_array<T>::type() in a wrapper function, which is of course not true, so boost::hash implementation will be choosed. However, if you use a trait which will return true_t for a vector<unsigned>, like fe. std::is_class<T>::type() then the compiler will report "C++ Standard doesn't provide...", which is a result of a static_assert.
For this to work, we would need to force compiler to return true_t if a type is really constructible (it doesn't fail static_assert) and false_t if it doesn't. However, I don't think there is a possibility to do that.