I followed one of the threads over here on S.O on how to implement an Outputtable trait class to check at compile time whether a type can be output on std::ostream. Implementation of the class is the following:
template<typename U>
struct OstreamOutputableTrait
{
template<typename T>
static decltype(std::declval<std::ostream&>() << std::declval<T>(), std::true_type{} )
IsOstreamOutputtable(std::ostream& os, const T& var) {}
// for vector<T>
template<typename T>
static std::false_type IsOstreamOutputtable(std::ostream& os, const std::vector<T>& var)
{}
template<typename > static auto IsOstreamOutputtable(...) {
return std::false_type {};
}
static const auto value =
decltype(IsOstreamOutputtable(std::declval<std::ostream&>(), std::declval<U>()))::value;
};
// operator implementation
template<typename Key, typename T>
std::ostream& operatorImpl(std::ostream& os, const std::map<Key,T>& map, const std::true_type& ) {
for(auto it = map.begin(); it!= map.end(); ++it)
os << "(" << (*it).first << " ; " << (*it).second << ")" <<"\n";
return os;
}
template<typename Key, typename T>
std::ostream& operatorImpl(std::ostream& os, const std::map<Key, T>& map, const std::false_type& ) {
os << "\nElements of the map are not printable.\n";
return os;
}
// operator << on <Key, value> maps
template<typename Key, typename T>
std::ostream& operator<<(std::ostream& os, const std::map<Key, T>& map)
{
// redirect using SFINAE to correct Impl of the operator<< : ty always returns true!
auto ty = std::integral_constant<bool,
OstreamOutputableTrait<typename std::decay_t<T> >::value &&
OstreamOutputableTrait<typename std::decay_t<Key> >::value>();
operatorImpl(os, map, ty);
return os;
}
Problem I have is that variable ty in the function above ALWAYS returns std::true_type. Does that mean that both <Key,T> typenames of the std::map template class are outputtable on std::ostream by default in the STL ? If anyone can explain where I may be doing something wrong, that would help me a lot.
Thanks
Amine
Your error is in declaring the variadic version of IsOstreamOutputtable()
template <typename>
static auto IsOstreamOutputtable (...)
{ return std::false_type {}; }
You declare it as template function with a template parameter that can't be deduced from the arguments.
So, when you call the function from decltype()
static const auto value =
decltype(IsOstreamOutputtable(std::declval<std::ostream&>(), std::declval<U>()))::value;
the compiler can't take in considerations the variadic version.
Solutions.
(1) you can explicit the template parameter calling IsOstreamOutputtable()
// ..........................VVV explicit template parameter
decltype(IsOstreamOutputtable<U>(std::declval<std::ostream&>(), std::declval<U>()))::value;
so the compiler can use the variadic version,
Problem: this solution doesn't works for the std::vector version
(2) (better) you can make the variadic version of IsOstreamOutputtable() a non-template one
// no needs of template
static auto IsOstreamOutputtable (...)
{ return std::false_type {}; }
Off Topic: the IsOstreamOutputtable() methods are called only inside a decltype(), so there is no needs of define, you have only to declare them . (and if you define them, please, be coherent: the body can't be empty for a method returning std::true_type).
Using also trailing return type, and other minor simplifications, your typewriting can be a little reduced
template <typename U>
struct OstreamOutputableTrait
{
template <typename T>
static auto IsOstreamOutputtable (T const & var)
-> decltype( std::declval<std::ostream&>() << var, std::true_type{} );
// special case for vector<T>
template <typename T>
static std::false_type IsOstreamOutputtable (std::vector<T> const &);
static std::false_type IsOstreamOutputtable (...);
static constexpr auto value
= decltype(IsOstreamOutputtable(std::declval<U>()))::value;
};
#max66,
My idea was that:
template<typename T>
static decltype(std::declval<std::ostream&>() << std::declval<T>(), std::true_type{} )
IsOstreamOutputtable(std::ostream& os, const T& var) {}
would check that for each type T that can be output using the operator<<, decltype would return the the type of std::true_type. In this context, the variadic overload:
template<typename > static auto IsOstreamOutputtable(...) {
return std::false_type {};
}
doesn't have any utility but to cause confusion. Having said that, if I use the syntax:
decltype(IsOstreamOutputtable<U>(std::declval<std::ostream&>(), std::declval<U>()))::value;
would the variadic function have priority over the first one that returns std::true_type?
Related
Consider this function:
template<typename T>
void f(T c) {
std::cout<<c<<std::endl;
}
You see that it will not compile for types which does not have an operator<< overload.
Now I want to write a function that acts like a fallback for this case.
/*Fallback*/
template<>
void f(T c) {
std::cout<<"Not Printing"<<std::endl;
}
How must this function be defined to do the job?
Pre-C++20
To have these overloads work in a fallback way, we can start by defining a trait that detects the validity of the expression involving operator <<
namespace detail {
template<typename T, typename = void>
struct streamable : std::false_type{};
template<typename T>
struct streamable<T, decltype(std::declval<std::ostream&>() << std::declval<T&>(), void())> : std::true_type {};
}
It's just your typical use of the detection idiom with as little extra library support as possible. Depending on the standard you are building against, this may be written in other ways (for instance std::void_t can be used, if available).
Now, the two overloads can be specified rather simply:
template<typename T>
auto f(T c) -> std::enable_if_t<detail::streamable<T>::value, void> {
std::cout<<c<<std::endl;
}
template<typename T>
auto f(T c) -> std::enable_if_t<!detail::streamable<T>::value, void> {
/// other code
}
Post C++20, concepts and constraints make it a whole lot easier. It can even be written ad-hoc:
template<typename T>
requires requires(std::ostream& os, T& c) { os << c; }
void f(T c) {
std::cout<<c<<std::endl;
}
template<typename T> // No extra step, subsumed by the above when possible
void f(T c) {
// other code
}
With concepts (C++20), we can achieve this like so:
template<typename T>
concept Streamable = requires(T t){std::declval<std::ostream&>() << t; };
template<Streamable T>
void f(T c) { std::cout << c << std::endl; }
/*Fallback*/
template<typename T>
void f(T c) { std::cout << "fallback" < <std::endl; }
Demo
Test:
struct Foo{};
int main()
{
Foo foo;
f(foo); // prints "fallback"
int a = 42;
f(a); // prints "42"
}
If you want to make doubly sure that your fallback will only happen if your type is not Streamable, you can constrain it, too:
template<typename T> requires (!Streamable<T>)
void f(T c) { /*...*/ }
You have several options of doing this. Arguably the most elegant way is to define your own type trait (similar to the ones in type_traits).
Let's define a is_streamable type trait. It takes two template arguments: S is the data type of the file stream (e.g. std::ostream or std::fstream or any other type that defines a custom streaming operator that is compatible with T) and secondly the data type of the object to be streamed into this file stream T:
template<typename S, typename T, typename = void>
struct is_streamable : std::false_type {
};
template<typename S, typename T>
struct is_streamable<S, T, decltype(std::declval<S&>() << std::declval<T&>(), void())> : std::true_type {
};
So far this type trait compiles with C++11 and onwards. For C++14 and later we can create a convenient alias for it similar to other type traits in C++17:
template <typename S, typename T>
static constexpr is_streamable_v = is_streamable<S,T>::value;
This type trait will now be the basis for the next step which will make use of SFINAE (C++11 onwards), constexpr if (C++17 onwards) or concepts (C++20).
In C++11 you could achieve this with either by putting the different implementations into partial specialisations of the same struct and call it with a helper function:
class f_imp {
};
template <typename T>
class f_imp<T,true> {
public:
static constexpr void imp(T c) {
std::cout << "streamable: " << c << std::endl;
}
};
template <typename T>
class f_imp<T,false> {
public:
static constexpr void imp(T c) {
std::cout << "not streamable" << std::endl;
}
};
template <typename T>
void f(T c) {
return f_imp<T,is_streamable<std::ostream,T>::value>::imp(c);
}
Try it here!
Alternatively you could apply SFINAE either by adding a second input parameter or applying it to the return type:
template<typename T, typename std::enable_if<is_streamable<std::ostream,T>::value>::type* = nullptr>
void f(T t) {
std::cout << "streamable" << std::endl;
}
template<typename T, typename std::enable_if<!is_streamable<std::ostream,T>::value>::type* = nullptr>
void f(T t) {
std::cout << "not streamable" << std::endl;
}
Try it here!
In C++17 you can actually use a constexpr if to avoid adding a second template argument and overloading of the function altogether. You can insert all the code inside the function and use if constexpr in combination with std::is_same_v and our is_streamable_v to decide at compile time which branch of our code each template type should take. This is in particular convenient if adding two specialisations would result in duplicate code but it might be harder to read.
template<typename T>
void f(T c) {
if constexpr (is_streamable_v<std::ostream,T>) {
std::cout << "streamable:" << c << std::endl;
} else {
// Fallback
std::cerr << "not streamable" << std::endl;
}
return;
}
Try it here!
Finally in C++20 you could use this type trait to define a concepts such as streamable and not_streamable:
template <typename T>
concept streamable = is_streamable_v<std::ostream,T>;
template <typename T>
concept not_streamable = !streamable<T>;
Then you can go on to apply them to your two overloads of the functions
template <streamable T>
void f(T c) {
std::cout << "streamable: " << c << std::endl;
}
template <not_streamable T>
void f(T c) {
std::cout << "not streamable" << std::endl;
}
Try it here!
Be aware that you will have to also apply the same logic to any custom streaming operator of a templated class, e.g. of a templated vector. Instead of declaring the operator for any template parameter typename T you would have to only declare it for streamable element types only. In C++20 for example with said streamable concept:
template <streamable T>
std::ostream& operator << (std::ostream& os, std::vector<T> const& vec) {
for (auto const& v: vec) {
os << v << " ";
}
return os;
}
Otherwise - as the template argument to the is_streamable operator is std::vector<T> as a whole - the compiler sees the operator << for std::vector<T> without checking if it would result in a compilation error for an unstreamable type T which does not define the operator << itself.
Try it here!
I would want to make a template of a << operator in C++, that would show a Object that is a "range" (by that i mean any object like : std::vector, std::set, std::map, std::deque). How can i achieve this? I've been googling and looking in docs for a few days now, but without any effect. I've been doing few templates and been overriding few operators before, but these were inside of a certain class that was representing a custom vector class. I cant seem to find a good way of implementing this, because it collides with a standard cout. How do i do it then, inside of a class that can pass a vector,set,map,deque as an argument, and operator inside? I would also want this operator to return the begin() and end() iterator of an object. By now i have this code:
template <typename T>
ostream& operator<<(ostream& os, T something)
{
os << something.begin() << something.end();
return os;
}
it doesnt really work, and i think that experienced C++ programmer can explain me why.
Thanks in advance for any answer for that problem.
Your overload will match on pretty much everything causing ambiguity for the types for which operator<< already has an overload.
I suspect that you want to print all elements in the container here: os << something.begin() << something.end();. This will not work because begin() and end() return iterators. You could dereference them
if(something.begin() != something.end())
os << *something.begin() << *std::prev(something.end());
but you'd only get the first and last element printed. This would print all of them:
for(const auto& v : something) os << v;
To solve the ambiguity problem, you could use template template parameters and enable the operator<< overload for the containers you'd like to support.
Example:
#include <deque>
#include <iostream>
#include <iterator>
#include <list>
#include <map>
#include <type_traits>
#include <vector>
// helper trait - add containers you'd like to support to the list
template <typename T> struct is_container : std::false_type {};
template <typename... Ts> struct is_container<std::vector<Ts...>> : std::true_type{};
template <typename... Ts> struct is_container<std::list<Ts...>> : std::true_type{};
template <typename... Ts> struct is_container<std::deque<Ts...>> : std::true_type{};
template <typename... Ts> struct is_container<std::map<Ts...>> : std::true_type{};
// C is the container template, like std::vector
// Ts... are the template parameters used to create the container.
template <template <typename...> class C, typename... Ts>
// only enable this for the containers you want to support
typename std::enable_if<is_container<C<Ts...>>::value, std::ostream&>::type
operator<<(std::ostream& os, const C<Ts...>& something) {
auto it = something.begin();
auto end = something.end();
if(it != end) {
os << *it;
for(++it; it != end; ++it) {
os << ',' << *it;
}
}
return os;
}
An alternative could be to make it generic but to disable the overload for types that already supports streaming.
#include <iostream>
#include <iterator>
#include <type_traits>
// A helper trait to check if the type already supports streaming to avoid adding
// an overload for std::string, std::filesystem::path etc.
template<typename T>
class is_streamable {
template<typename TT>
static auto test(int) ->
decltype( std::declval<std::ostream&>() << std::declval<TT>(), std::true_type() );
template<typename>
static auto test(...) -> std::false_type;
public:
static constexpr bool value = decltype(test<T>(0))::value;
};
template <typename T,
typename U = decltype(*std::begin(std::declval<T>())), // must have begin
typename V = decltype(*std::end(std::declval<T>())) // must have end
>
// Only enable the overload for types not already streamable
typename std::enable_if<not is_streamable<T>::value, std::ostream&>::type
operator<<(std::ostream& os, const T& something) {
auto it = std::begin(something);
auto end = std::end(something);
if(it != end) {
os << *it;
for(++it; it != end; ++it) {
os << ',' << *it;
}
}
return os;
}
Note: The last example works in clang++ and MSVC but it fails to compile in g++ (recursion depth exceeded).
For containers with a value_type that is in itself not streamable, like the std::pair<const Key, T> in a std::map, you need to add a separate overload. This needs to be declared before any of the templates above:
template <typename Key, typename T>
std::ostream &operator<<(std::ostream &os, const std::pair<const Key, T>& p) {
return os << p.first << ',' << p.second;
}
Your code has the right idea but is missing a few things.
template <typename T>
ostream& operator<<(ostream& os, T something)
{
os << something.begin() << something.end();
return os;
}
Iterable containers (like std::map and such) should be outputted by iterating through all their elements, and outputting each one-by-one. Here, you're only outputting the beginning and end iterators, which aren't the same as elements themselves.
We can instead use *it to get an element from its iterator in the container. So, the code below will output all elements in a standard container of type T. I also include some additional pretty-printing.
template <typename T>
std::ostream &operator<<(std::ostream &os, const T &o) {
auto it = o.begin();
os << "{" << *it;
for (it++; it != o.end(); it++) {
os << ", " << *it;
}
return os << "}";
}
If we just use
template <typename T>
ahead of this function declaration, then it will conflict with existing << operator declarations. That is, when we writestd::cout << std::string("hello world");, does this call our function implementation, or does this call the function implementation from <string>? Of course, we want to use the standard operator<< implementations if available. We do this by limiting the template so that it only works for standard containers with begin() and end() members, but not for std::string, which has begin() and end() but also has an existing operator<< implementation that we want to use.
template <typename T,
typename std::enable_if<is_iterable<T>::value, bool>::type = 0,
typename std::enable_if<!std::is_same<T, std::string>::value, bool>::type = 0>
The second std::enable_if is straightforward: the template should cover types as long as they aren't std::string. The first std::enable_if checks if the type T is iterable. We need to make this check ourselves.
template <typename T>
class is_iterable {
private:
typedef char True[1];
typedef char False[2];
template <typename Q,
typename std::enable_if<
std::is_same<decltype(std::declval<const Q &>().begin()),
decltype(std::declval<const Q &>().begin())>::value,
char>::type = 0>
static True &test(char);
template <typename...>
static False &test(...);
public:
static bool const value = sizeof(test<T>(0)) == sizeof(True);
};
is_iterable has two versions of the function test. The first version is enabled if begin() and end() exist on type T, and their return types are the same (there are more precise ways to do checks, but this suffices for now). The second version is called otherwise. The two versions' return types are different, and by checking the size of the return type, we can set value, which will be true if and only if T is iterable (in our case, if T defines begin() and end() and their return types are the same).
Finally, we note that std::map<T1, T2>'s elements are actually of type std::pair<T1, T2>, so we need to additionally overload operator<< for templated pairs.
template <typename T1, typename T2>
std::ostream &operator<<(std::ostream &os, const std::pair<T1, T2> &o) {
return os << "(" << o.first << ", " << o.second << ")";
}
Putting it all together, we can try this. Note that it even works for nested iterator types like listUnorderedSetTest.
#include <iostream>
#include <list>
#include <map>
#include <set>
#include <type_traits>
#include <unordered_set>
#include <vector>
template <typename T>
class is_iterable {
private:
typedef char True[1];
typedef char False[2];
template <typename Q,
typename std::enable_if<
std::is_same<decltype(std::declval<const Q &>().begin()),
decltype(std::declval<const Q &>().begin())>::value,
char>::type = 0>
static True &test(char);
template <typename...>
static False &test(...);
public:
static bool const value = sizeof(test<T>(0)) == sizeof(True);
};
template <typename T1, typename T2>
std::ostream &operator<<(std::ostream &os, const std::pair<T1, T2> &o) {
return os << "(" << o.first << ", " << o.second << ")";
}
template <typename T,
typename std::enable_if<is_iterable<T>::value, bool>::type = 0,
typename std::enable_if<!std::is_same<T, std::string>::value, bool>::type = 0>
std::ostream &operator<<(std::ostream &os, const T &o) {
auto it = o.begin();
os << "{" << *it;
for (it++; it != o.end(); it++) {
os << ", " << *it;
}
return os << "}";
}
int main() {
std::vector<std::string> vectorTest{"hello", "world", "!"};
std::cout << vectorTest << std::endl;
std::set<const char *> setTest{"does", "this", "set", "work", "?"};
std::cout << setTest << std::endl;
std::map<std::string, std::size_t> mapTest{
{"bob", 100}, {"alice", 16384}, {"xavier", 216}};
std::cout << mapTest << std::endl;
std::list<std::unordered_set<std::string>> listUnorderedSetTest{
{"alice", "abraham", "aria"},
{"carl", "crystal", "ciri"},
{"november", "nathaniel"}};
std::cout << listUnorderedSetTest << std::endl;
return 0;
}
This outputs:
{hello, world, !}
{does, this, set, work, ?}
{(alice, 16384), (bob, 100), (xavier, 216)}
{{alice, abraham, aria}, {carl, crystal, ciri}, {november, nathaniel}}
There's a lot of additional related discussion at Templated check for the existence of a class member function? which you might find helpful. The downside of this answer is a check against std::string instead of a check for existing operator<< implementations, which I think can be solved with a bit more work into type checking with decltype.
I've seen types that have corresponding to_string() function, but haven't overloaded operator<<(). So, when inserting to stream, one has to << to_string(x) which is verbose. I'm wondering whether it's possible to write a generic function that users operator<<() if supported and falls back to << to_string() if not.
SFINAE is overkill, use ADL.
The trick is to make sure that an operator<< is available, not necessarily the one supplied by the type definition:
namespace helper {
template<typename T> std::ostream& operator<<(std::ostream& os, T const& t)
{
return os << to_string(t);
}
}
using helper::operator<<;
std::cout << myFoo;
This trick is commonly used in generic code which needs to choose between std::swap<T> and a specialized Foo::swap(Foo::Bar&, Foo::Bar&).
Try
template <typename T>
void print_out(T t) {
print_out_impl(std::cout, t, 0);
}
template <typename OS, typename T>
void print_out_impl(OS& o, T t,
typename std::decay<decltype(
std::declval<OS&>() << std::declval<T>()
)>::type*) {
o << t;
}
template <typename OS, typename T>
void print_out_impl(OS& o, T t, ...) {
o << t.to_string();
}
LIVE
Yes, it is possible.
#include <iostream>
#include <sstream>
#include <string>
#include <type_traits>
struct streamy
{
};
std::ostream&
operator<<(std::ostream& os, const streamy& obj)
{
return os << "streamy [" << static_cast<const void *>(&obj) << "]";
}
struct stringy
{
};
std::string
to_string(const stringy& obj)
{
auto oss = std::ostringstream {};
oss << "stringy [" << static_cast<const void *>(&obj) << "]";
return oss.str();
}
template <typename T>
std::enable_if_t
<
std::is_same
<
std::string,
decltype(to_string(std::declval<const T&>()))
>::value,
std::ostream
>&
operator<<(std::ostream& os, const T& obj)
{
return os << to_string(obj);
}
int
main()
{
std::cout << streamy {} << '\n';
std::cout << stringy {} << '\n';
}
The generic operator<< will only be available if the expression to_string(obj) is well-typed for obj a const T& and has a result of type std::string. As you have already conjectured in your comment, this is indeed SFINAE at work. If the decltype expression is not well-formed, we will get a substitution failure and the overload will disappear.
However, this will likely get you into troubles with ambiguous overloads. At the very least, put your fallback operator<< into its own namespace and only drag it in locally via a using declaration when needed. I think you will be better off writing a named function that does the same thing.
namespace detail
{
enum class out_methods { directly, to_string, member_str, not_at_all };
template <out_methods> struct tag {};
template <typename T>
void
out(std::ostream& os, const T& arg, const tag<out_methods::directly>)
{
os << arg;
}
template <typename T>
void
out(std::ostream& os, const T& arg, const tag<out_methods::to_string>)
{
os << to_string(arg);
}
template <typename T>
void
out(std::ostream& os, const T& arg, const tag<out_methods::member_str>)
{
os << arg.str();
}
template <typename T>
void
out(std::ostream&, const T&, const tag<out_methods::not_at_all>)
{
// This function will never be called but we provide it anyway such that
// we get better error messages.
throw std::logic_error {};
}
template <typename T, typename = void>
struct can_directly : std::false_type {};
template <typename T>
struct can_directly
<
T,
decltype((void) (std::declval<std::ostream&>() << std::declval<const T&>()))
> : std::true_type {};
template <typename T, typename = void>
struct can_to_string : std::false_type {};
template <typename T>
struct can_to_string
<
T,
decltype((void) (std::declval<std::ostream&>() << to_string(std::declval<const T&>())))
> : std::true_type {};
template <typename T, typename = void>
struct can_member_str : std::false_type {};
template <typename T>
struct can_member_str
<
T,
decltype((void) (std::declval<std::ostream&>() << std::declval<const T&>().str()))
> : std::true_type {};
template <typename T>
constexpr out_methods
decide_how() noexcept
{
if (can_directly<T>::value)
return out_methods::directly;
else if (can_to_string<T>::value)
return out_methods::to_string;
else if (can_member_str<T>::value)
return out_methods::member_str;
else
return out_methods::not_at_all;
}
template <typename T>
void
out(std::ostream& os, const T& arg)
{
constexpr auto how = decide_how<T>();
static_assert(how != out_methods::not_at_all, "cannot format type");
out(os, arg, tag<how> {});
}
}
template <typename... Ts>
void
out(std::ostream& os, const Ts&... args)
{
const int dummy[] = {0, ((void) detail::out(os, args), 0)...};
(void) dummy;
}
Then use it like so.
int
main()
{
std::ostringstream nl {"\n"}; // has `str` member
out(std::cout, streamy {}, nl, stringy {}, '\n');
}
The function decide_how gives you full flexibility in deciding how to output a given type, even if there are multiple options available. It is also easy to extend. For example, some types have a str member function instead of an ADL find-able to_string free function. (Actually, I already did that.)
The function detail::out uses tag dispatching to select the appropriate output method.
The can_HOW predicates are implemented using the void_t trick which I find very elegant.
The variadic out function uses the “for each argument” trick, which I find even more elegant.
Note that the code is C++14 and will require an up-to-date compiler.
Based on the answer of #MSalters (credits goes to him), this one solves my problem and should make a complete answer.
#include <iostream>
#include <string>
#include <type_traits>
struct foo_t {};
std::string to_string(foo_t) {
return "foo_t";
}
template <class CharT, class Traits, class T>
typename std::enable_if<std::is_same<CharT, char>::value,
std::basic_ostream<CharT, Traits>&>::type
operator<<(std::basic_ostream<CharT, Traits>& os, const T& x) {
return os << to_string(x);
}
int main() {
std::cout << std::string{"123"} << std::endl;
std::cout << foo_t{} << std::endl;
}
Gathering the information from Using SFINAE to check for global operator<<? and templates, decltype and non-classtypes, I got the following code:
http://ideone.com/sEQc87
Basically I combined the code from both questions to call print function if it has ostream declaration, or to call the to_string method otherwise.
Taken from question 1
namespace has_insertion_operator_impl {
typedef char no;
typedef char yes[2];
struct any_t {
template<typename T> any_t( T const& );
};
no operator<<( std::ostream const&, any_t const& );
yes& test( std::ostream& );
no test( no );
template<typename T>
struct has_insertion_operator {
static std::ostream &s;
static T const &t;
static bool const value = sizeof( test(s << t) ) == sizeof( yes );
};
}
template<typename T>
struct has_insertion_operator :
has_insertion_operator_impl::has_insertion_operator<T> {
};
Taken from question 2
template <typename T>
typename std::enable_if<has_insertion_operator<T>::value, T>::type
print(T obj) {
std::cout << "from print()" << std::endl;
}
template <typename T>
typename std::enable_if<!has_insertion_operator<T>::value, T>::type
print(T obj) {
std::cout << obj.to_string() << std::endl;
}
Then my classes are like so:
struct Foo
{
public:
friend std::ostream& operator<<(std::ostream & os, Foo const& foo);
};
struct Bar
{
public:
std::string to_string() const
{
return "from to_string()";
}
};
And test output:
int main()
{
print<Foo>(Foo());
print<Bar>(Bar());
//print<Bar>(Foo()); doesn't compile
//print<Foo>(Bar()); doesn't compile
print(Foo());
print(Bar());
print(42);
print('a');
//print(std::string("Hi")); seg-fault
//print("Hey");
//print({1, 2, 3}); doesn't compile
return 0;
}
The print(std::string("Hi")); line seg-faults. Can anyone tell me why?
Both your functions print() are supposed to return something, but instead return nothing (unlike the versions in the Q&As you linked). This is undefined behavior per paragraph 6.6.3/2 of the C++11 Standard.
If print() is not supposed to return anything, let it return void, and put the SFINAE constraint in the template parameter list:
template <typename T,
typename std::enable_if<
has_insertion_operator<T>::value, T>::type* = nullptr>
void print(T obj) {
std::cout << "from print()" << std::endl;
}
template <typename T,
typename std::enable_if<
!has_insertion_operator<T>::value, T>::type* = nullptr>
void print(T obj) {
std::cout << obj.to_string() << std::endl;
}
Here is a live example containing the above change.
If you are working with C++03 and cannot specify default arguments for function template parameters, just avoid specifying a type as the second template argument to std::enable_if, or specify void:
template <typename T>
typename std::enable_if<has_insertion_operator<T>::value>::type
print(T obj) {
std::cout << "from print()" << std::endl;
}
template <typename T>
typename std::enable_if<!has_insertion_operator<T>::value>::type
print(T obj) {
std::cout << obj.to_string() << std::endl;
}
Is there a way, presumably using templates, macros or a combination of the two, that I can generically apply a function to different classes of objects but have them respond in different ways if they do not have a specific function?
I specifically want to apply a function which will output the size of the object (i.e. the number of objects in a collection) if the object has that function but will output a simple replacement (such as "N/A") if the object doesn't. I.e.
NO_OF_ELEMENTS( mySTLMap ) -----> [ calls mySTLMap.size() to give ] ------> 10
NO_OF_ELEMENTS( myNoSizeObj ) --> [ applies compile time logic to give ] -> "N/A"
I expect that this might be something similar to a static assertion although I'd clearly want to compile a different code path rather than fail at build stage.
From what I understand, you want to have a generic test to see if a class has a certain member function. This can be accomplished in C++ using SFINAE. In C++11 it's pretty simple, since you can use decltype:
template <typename T>
struct has_size {
private:
template <typename U>
static decltype(std::declval<U>().size(), void(), std::true_type()) test(int);
template <typename>
static std::false_type test(...);
public:
typedef decltype(test<T>(0)) type;
enum { value = type::value };
};
If you use C++03 it is a bit harder due to the lack of decltype, so you have to abuse sizeof instead:
template <typename T>
struct has_size {
private:
struct yes { int x; };
struct no {yes x[4]; };
template <typename U>
static typename boost::enable_if_c<sizeof(static_cast<U*>(0)->size(), void(), int()) == sizeof(int), yes>::type test(int);
template <typename>
static no test(...);
public:
enum { value = sizeof(test<T>(0)) == sizeof(yes) };
};
Of course this uses Boost.Enable_If, which might be an unwanted (and unnecessary) dependency. However writing enable_if yourself is dead simple:
template<bool Cond, typename T> enable_if;
template<typename T> enable_if<true, T> { typedef T type; };
In both cases the method signature test<U>(int) is only visible, if U has a size method, since otherwise evaluating either the decltype or the sizeof (depending on which version you use) will fail, which will then remove the method from consideration (due to SFINAE. The lengthy expressions std::declval<U>().size(), void(), std::true_type() is an abuse of C++ comma operator, which will return the last expression from the comma-separated list, so this makes sure the type is known as std::true_type for the C++11 variant (and the sizeof evaluates int for the C++03 variant). The void() in the middle is only there to make sure there are no strange overloads of the comma operator interfering with the evaluation.
Of course this will return true if T has a size method which is callable without arguments, but gives no guarantees about the return value. I assume wou probably want to detect only those methods which don't return void. This can be easily accomplished with a slight modification of the test(int) method:
// C++11
template <typename U>
static typename std::enable_if<!is_void<decltype(std::declval<U>().size())>::value, std::true_type>::type test(int);
//C++03
template <typename U>
static typename std::enable_if<boost::enable_if_c<sizeof(static_cast<U*>(0)->size()) != sizeof(void()), yes>::type test(int);
There was a discussion about the abilities of constexpr some times ago. It's time to use it I think :)
It is easy to design a trait with constexpr and decltype:
template <typename T>
constexpr decltype(std::declval<T>().size(), true) has_size(int) { return true; }
template <typename T>
constexpr bool has_size(...) { return false; }
So easy in fact that the trait loses most of its value:
#include <iostream>
#include <vector>
template <typename T>
auto print_size(T const& t) -> decltype(t.size(), void()) {
std::cout << t.size() << "\n";
}
void print_size(...) { std::cout << "N/A\n"; }
int main() {
print_size(std::vector<int>{1, 2, 3});
print_size(1);
}
In action:
3
N/A
This can be done using a technique called SFINAE. In your specific case you could implement that using Boost.Concept Check. You'd have to write your own concept for checking for a size-method. Alternatively you could use an existing concept such as Container, which, among others, requires a size-method.
You can do something like
template< typename T>
int getSize(const T& t)
{
return -1;
}
template< typename T>
int getSize( const std::vector<T>& t)
{
return t.size();
}
template< typename T , typename U>
int getSize( const std::map<T,U>& t)
{
return t.size();
}
//Implement this interface for
//other objects
class ISupportsGetSize
{
public:
virtual int size() const= 0;
};
int getSize( const ISupportsGetSize & t )
{
return t.size();
}
int main()
{
int s = getSize( 4 );
std::vector<int> v;
s = getSize( v );
return 0;
}
basically the most generic implementation is always return -1 or "NA" but for vector and maps it will return the size. As the most general one always matches there is never a build time failure
Here you go. Replace std::cout with the output of your liking.
template <typename T>
class has_size
{
template <typename C> static char test( typeof(&C::size) ) ;
template <typename C> static long test(...);
public:
enum { value = sizeof(test<T>(0)) == sizeof(char) };
};
template<bool T>
struct outputter
{
template< typename C >
static void output( const C& object )
{
std::cout << object.size();
}
};
template<>
struct outputter<false>
{
template< typename C >
static void output( const C& )
{
std::cout << "N/A";
}
};
template<typename T>
void NO_OF_ELEMENTS( const T &object )
{
outputter< has_size<T>::value >::output( object );
}
You could try something like:
#include <iostream>
#include <vector>
template<typename T>
struct has_size
{
typedef char one;
typedef struct { char a[2]; } two;
template<typename Sig>
struct select
{
};
template<typename U>
static one check (U*, select<char (&)[((&U::size)!=0)]>* const = 0);
static two check (...);
static bool const value = sizeof (one) == sizeof (check (static_cast<T*> (0)));
};
struct A{ };
int main ( )
{
std::cout << has_size<int>::value << "\n";
std::cout << has_size<A>::value << "\n";
std::cout << has_size<std::vector<int>>::value << "\n";
}
but you have to be careful, this does neither work when size is overloaded, nor when it is a template. When you can use C++11, you can replace the above sizeof trick by decltype magic