This question already has answers here:
use template to print all the data of any container
(2 answers)
Closed 5 years ago.
I have a vector container constructed using:
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
And deque container constructed using:
deque<int> deq;
deq.push_front(1);
deq.push_front(2);
deq.push_front(3);
I want to have a template function that displays the content of the both of these containers and for all data-types. For handling with different data types I made a template function like this:
template <typename T>
void display(vector<T> vec)
{
for(int i = 0; i < vec.size(); ++i)
{
cout << vec[i] << endl;
}
cout << endl;
}
Currently its working for vector, How can I make it work for all containers like deque, and list?
Something along these lines, perhaps:
template <typename C>
void display(const C& container) {
std::copy(std::begin(container), std::end(container),
std::ostream_iterator<decltype(*std::begin(container))>(std::cout, "\n"));
}
I guess you might use range-based for-loop:
template<typename C>
void display(const C& container)
{
for (const auto& e : container)
{
std::cout << e << std::endl;
}
}
Many STL containers support ranges with pairs as value types so
#include <iostream>
#include <deque>
#include <vector>
#include <map>
using std::cout;
using std::endl;
namespace detail {
template <typename First, typename Second>
void print_element(const std::pair<First, Second>& pr) {
cout << pr.first << " " << pr.second << endl;
}
template <typename Type>
void print_element(const Type& element) {
cout << element << endl;
}
} // namespace detail
template <typename Container>
void print_all(const Container& container) {
for (const auto& element : container) {
detail::print_element(element);
}
}
int main() {
auto v = std::vector<int>{1, 2, 3};
auto d = std::deque<int>{1, 2, 3};
auto m = std::map<int, int>{{1, 2}};
print_all(v);
print_all(d);
print_all(m);
return 0;
}
Related
I want to design a print function for STL container, include: std::vector, std::map, std::unodered_map, std::set, std::unordered_set, std::list ....
The ideal function looks like:
template<typename T>
void Show(const T& t)
{
if (istype(t, std::vector))
{
// here is a presudo code
// print vector
for (auto i: t) cout << i << endl;
}
else if (istype(t, std::unordered_map))
{
// print unordered_map
}
else
{ }
}
I think the problem happens on istype()
I know there is some function like std::is_same can do this.
But it seems it just can operate on int or float, cant be operated on std::vector, can you help on this?
But it seems it just can operate on int or float, cant be operated
on std::vector, can you help on this?
For template classes like stranded containers, you need to specify the template argument to get the concrete type and thereafter you can compare usingstd::is_same. That means something like std::is_same_v<T, std::vector<int>> or std::is_same_v<T, std::vector<float>>, etc... will work.
On the other side, you need to see whether the passed container is a specialization for the standard container. There you need your own std::is_same_v like type traits.
One possible implementation will look like as follows. Also note that you need to use the if constexpr (since c++17) instead of normal if for compile time branching. If no access to c++17, you need to use SFINAE.
(See a Demo)
#include <iostream>
#include <type_traits> // std::false_type, std::true_type
#include <vector>
#include <list>
#include <string>
#include <map>
template<typename Type, template<typename...> class Args>
struct is_specialization final : std::false_type {};
template<template<typename...> class Type, typename... Args>
struct is_specialization<Type<Args...>, Type> : std::true_type {};
template<typename ContainerType>
constexpr void show(const ContainerType& container) noexcept
{
if constexpr (is_specialization<ContainerType, std::vector>::value
|| is_specialization<ContainerType, std::list>::value)
{
for (const auto ele : container)
std::cout << ele << '\t';
std::cout << '\n';
}
else if constexpr (is_specialization<ContainerType, std::map>::value)
{
for (const auto& [key, value]: container)
std::cout << key << " " << value << '\t';
std::cout << '\n';
}
// ... so on!
}
int main()
{
std::vector<int> vec{ 1, 2, 3 };
show(vec);
std::list<int> list{ 11, 22, 33 };
show(list);
std::map<int, std::string> mp{ {1, "string1"}, {2, "string2"}, {3, "string3"} };
show(mp);
}
Common feature of all STL containers is that they are iterable in range [begin(),end()). For sequence containers you have to handle printing T as value_type, for (Unordered) associative containers printing std::pair<const Key, T> as value_type must be handled. That is all. I don't see any reasons to checking type of passed container in your implemention.
template<class T>
void Print(const T& val) {
std::cout << val;
}
template<class Key, class Value>
void Print(const std::pair<const Key,Value>& p) {
Print(p.first);
std::cout << " - ";
Print(p.second);
}
template<class Cont>
void PrintCont(const Cont& cont) {
std::cout << std::endl;
for (auto it = cont.begin(); it != cont.end(); ++it) {
Print(*it);
std::cout << " ";
}
std::cout << std::endl;
}
std::array<int,2> a{1,2};
std::vector<int> v{1,2,3};
std::list<double> l{2., 3., 5.4};
std::map<int,double> m{ {1,2.},{10,3.14} };
std::multimap<int,int> m2{ {2,3},{4,5} };
std::set<float> s{1.f, 23.f, 2.f};
std::unordered_map<int,Foo> m3{ {1,Foo(1)}, {2,Foo(2)}, {3,Foo(3)}};
PrintCont(a);
PrintCont(v);
PrintCont(l);
PrintCont(m);
PrintCont(a);
PrintCont(s);
PrintCont(m2);
PrintCont(m3);
Live demo
Consider the following overloaded function that can print a 1d vector and a vector of vector of several types like strings, ints, doubles etc.
template<typename T>
void p(const vector<vector<T>>& vec) {
int count = 0;
for (vector<T> innerVec: vec) {
cout << count++ << ": ";
for (T e :innerVec) {
cout << e << ' ';
}
cout << '\n';
}
cout << '\n';
}
template<typename T>
void p(const vector<T>& vec) {
for (T e: vec) {
cout << e << ' ';
}
cout << '\n';
}
Is there anyway I can merge these two functions into 1? I tried using SFINAE and tag dispatching but all the solutions I could come up with need a macro or multiple functions and I don't want this.
I know the question might seem odd since my solution works, but I prefer having just one function in my code. This is because I want to implement a function that can detect if I am passing in a map, vector, vector of vectors, unordered_set, multimap, etc and just print that STL data structure and having one overloaded function for each specialization is a bit annoying as it gets large quick.
I answered a similar question today. Check it out there: https://stackoverflow.com/a/60298735/8192043
Pasting the solution here:
This should work for your case. Note that I'm using a trait as implemented here in this amazing solution by #Jarod42 https://stackoverflow.com/a/29634934/8192043.
template<template<typename ...> typename C, typename D, typename ... Others>
void foo(const C<D, Others...> &object)
{
if constexpr(is_iterable<D>::value)
{
for(const auto& v : object)
{
for (const auto& w : v)
{...}
}
}
else
{
for (const auto& w : object)
{...}
}
}
Live Code
Yes, but you need an extra parameter to distinguish the inner from the outer case
#include <vector>
#include <iostream>
struct counting_prefix {
void call() { std::cout << count++ << ": "; }
int count = 0;
};
struct no_prefix {
void call() { }
};
template<typename T, typename Prefix = no_prefix>
void p(const T& e, Prefix prefix = {}) {
prefix.call();
std::cout << e << ' ';
}
template<typename T, typename Prefix = no_prefix>
void p(const std::vector<T>& vec, Prefix prefix = {}) {
for (const T& e: vec) {
prefix.call();
p(e);
}
std::cout << '\n';
}
int main() {
std::vector<std::vector<double>> stuff = { { 1., 2. }, { 3., 4. } };
p(stuff, counting_prefix{});
}
See it live
Consider the following declaration:
template <class T>
bool DoSomething(const T& value);
template <class D>
bool DoSomething(const std::vector<D>& value);
Is it possible to somehow unite this into single function declaration?
E.g. something like that:
template <class T, class D> bool DoSomething(...);
You code
template <class T>
bool DoSomething(const T& value);
is already accepting std::vector. If you want to do something in your DoSomething method which is different if T is a vector, then you can use this approach to check if the T is a specific type. Don't forget that templates are code generators.
Template parameters can be types, non-types, and templates.
And I suppose you are looking at something like this
#include <iostream>
#include <list>
#include <vector>
#include <string>
template <typename T, template <typename, typename> class Cont >
class Matrix{
public:
explicit Matrix(std::initializer_list<T> inList): data(inList){
for (auto d: data) std::cout << d << " ";
}
int getSize() const{
return data.size();
}
private:
Cont<T, std::allocator<T>> data;
};
int main(){
std::cout << std::endl;
Matrix<int, std::vector> myIntVec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
std::cout << std::endl;
std::cout << "myIntVec.getSize(): " << myIntVec.getSize() << std::endl;
Matrix<std::string, std::list> myStringList{"one", "two", "three", "four"};
std::cout << std::endl;
std::cout << "myStringList.getSize(): " << myStringList.getSize() << std::endl;
std::cout << std::endl;
}
Matrix is a simple class template, that can be initialised by a std::initializer_list . A Matrix can be used with a std::vector , or a std::list to hold its values.
live Demo
Well, template <class T> bool DoSomething(const T& value); can also be calles with any vector. If it works depends on what you are trying to do. Templates are just code generators. So whatever class T is, it can be used as template paramter if it has all the required members.
For example the following would work:
#include <iostream>
#include <vector>
#include <string>
class MyContainer {
public:
size_t size() const
{
return 2;
}
int front() const
{
return 0;
}
int back() const
{
return 1;
}
};
template<class T>
void foo(const T& t)
{
if (t.size() >= 2)
{
std::cout << "(" << t.front() << ", " << t.back() << ")" << std::endl;
}
}
int main()
{
std::vector<std::string> stringVec{ "abc", "def" };
MyContainer cont;
foo(stringVec); // prints "(abc, def)"
foo(cont); // prints "(0, 1)"
}
That's because both MyContainer and std::vector<std::string> have all the methods that are uses in the template. Actually this code should work with almost all STL-Containers.
Short answer: Geneally you can't.
Honest answer: Yes, it can be done, depends on what you plan to do with argument. In abstract case you would need to generalize the function prototype, but still have two specializations of template using SFINAE, which makes three declarations instead of two.
Long answer: In some cases you can take advantage of if constexpr
#include <iostream>
#include <type_traits>
#include <vector>
template<typename Test, template<typename...> class Ref>
struct is_specialization : std::false_type {};
template<template<typename...> class Ref, typename... Args>
struct is_specialization<Ref<Args...>, Ref>: std::true_type {};
template <class T> bool DoSomething(const T& arg)
{
if constexpr(is_specialization<T, std::vector>::value)
{
return !arg.empty();
} else
{
return bool(arg);
}
}
int main()
{
std::vector<int> s;
std::cout << DoSomething(s) << std::endl;
std::cout << DoSomething(1) << std::endl;
}
I am trying to write a simple template function that prints every element of some container, without using for loops. So far, I have
#include <iostream>
#include <vector>
#include <algorithm>
template <typename T> void print_with_space(T x){
std::cout << x << ' ';
}
template <typename T> void print_all(T beg, T end){
std::for_each(beg, end, print_with_space<int>);
std::cout << '\n';
}
int main(){
int a[] = {1, 2, 3};
std::vector<int> v(a, a+3);
print_all(v.begin(), v.end());
return 0;
}
The code compiles and runs, but only because I put print_with_space<int> inside the implementation of print_all. I would like to just have print_with_space there for obvious reasons, but then the code doesn't compile. How do I do this?
You can use:
std::for_each(beg, end, [](const typename T::value_type& value) {
print_with_space(value);
});
T is of type std::vector<>::iterator, which is a RandomAccessIterator. Every RandomAcessIterator has a underlying type, which is exposed by value_type.
So, if you pass std::vector<int>::iterator, std::vector<int>::iterator::value_type would be an int.
Now that you have the type, you can make a lambda, which will get executed for every iteration.
In C++14, you can even do:
//'auto' automatically deduces the type for you
std::for_each(beg, end, [](const auto& value) {
print_with_space(value);
});
Another option:
template <typename T> void print_all(T beg, T end) {
std::for_each(beg, end, print_with_space<decltype(*beg)>);
std::cout << '\n';
}
Alternative for C++03:
#include <iterator>
template <typename T> void print_all(T beg, T end)
{
typedef typename std::iterator_traits<T>::value_type val_t;
std::for_each(beg, end, print_with_space<val_t>);
std::cout << '\n';
}
The most flexible solution, which will work with all versions of c++, is to make print_with_space a function object.
This confers a number of advantages:
no need to specify template type at call site.
no need to fiddle around with manual type deduction.
partial specialisation can be achieved by having the functor defer to a templated free function.
Such as:
#include <iostream>
#include <iomanip>
#include <vector>
#include <algorithm>
// basic implementation
template<class T> void impl_print_with_space(const T& x)
{
std::cout << x << ' ';
}
// what about special handling for strings?
template<class C, class Ch, class Alloc>
void impl_print_with_space(const std::basic_string<C, Ch, Alloc>& x)
{
std::cout << std::quoted(x) << ' ';
}
// functor
struct print_with_space
{
template<class T> void operator()(const T& x) const
{
impl_print_with_space(x);
}
};
template <typename Iter> void print_all(Iter beg, Iter end)
{
std::for_each(beg, end, print_with_space());
std::cout << '\n';
}
int main(){
int a[] = {1, 2, 3};
std::vector<int> v(a, a+3);
print_all(v.begin(), v.end());
auto b = std::vector<std::string> { "hello", "world" };
print_all(b.begin(), b.end());
return 0;
}
I want specialize a function template for vector and map like containers. For vector I can do like below but I don't know how can I have a specialized version of the function that will be used only for map like containers.
#include <iostream>
#include <vector>
#include <map>
using namespace std;
template<typename Iterator>
void print(Iterator begin, Iterator end)
{
while (begin != end)
{
cout << *begin << endl; // compiler error for map like containers
++begin;
}
}
int main()
{
vector<int> noVec = { 1, 2, 3 };
print(noVec.begin(), noVec.end());
map<int, int> nosMap;
nosMap[0] = 1;
nosMap[1] = 2;
nosMap[3] = 3;
print(nosMap.begin(), nosMap.end());
return 0;
}
This question is similar but it suggests to use pair in vector which I don't want to do. I know the specialization can be done with SFINAE but don't know what condition to check for. It would be great if I can achieve this with C++ 11 type_traits.
The value_type of a map is some pair so you could check if the value_type of the iterator is a std::pair or not, e.g.
#include <vector>
#include <map>
#include <iostream>
template <typename>
struct is_pair : std::false_type
{ };
template <typename T, typename U>
struct is_pair<std::pair<T, U>> : std::true_type
{ };
template <typename Iter>
typename std::enable_if<is_pair<typename std::iterator_traits<Iter>::value_type>::value>::type
print(Iter begin, Iter end)
{
std::cout << "called with map-like" << std::endl;
for (; begin != end; ++begin)
{
std::cout << begin->second;
}
std::cout << std::endl;
}
template <typename Iter>
typename std::enable_if<!is_pair<typename std::iterator_traits<Iter>::value_type>::value>::type
print(Iter begin, Iter end)
{
std::cout << "called with vector-like" << std::endl;
for (; begin != end; ++begin)
{
std::cout << *begin;
}
std::cout << std::endl;
}
int main()
{
std::vector<int> vec { 1, 2, 3 };
std::map<int, int> map {{0, 0}, {1, 1}, {2, 4}, {3, 9}};
print(vec.begin(), vec.end());
print(map.begin(), map.end());
}
which prints
called with vector-like
123
called with map-like
0149
You don't need to specialize anything. All you have to do is to provide an overloaded output operator<< for std::pair, like the example below:
template<typename T1, typename T2>
std::ostream& operator<<(std::ostream &out, std::pair<T1, T2> const &mp) {
return (out << "(" << mp.first << ", " << mp.second << ")");
}
LIVE DEMO
edit:
The above solution however as #Benjamin Lindley suggested in the comments might conflict with other template overloads of the output operator<< for std::pair.
If this is the case, alternatively you could write in their own namespace (e.g., namespace detail) two template function overloads (e.g., print_elem), like the example below:
namespace detail {
template<typename T1, typename T2>
std::ostream& print_elem(std::ostream &out, std::pair<T1, T2> const &mp) {
return (out << "(" << mp.first << ", " << mp.second << ")");
}
template<typename T>
std::ostream& print_elem(std::ostream &out, T const &elem) {
return (out << elem);
}
}
and change your template print like the example below:
template<typename Iterator>
void print(Iterator begin, Iterator end)
{
while (begin != end) {
detail::print_elem(cout, *begin) << endl;
++begin;
}
}
LIVE DEMO