c++ iterating objects taken in by template function - c++

I have 2 pieces of code that look similar and I want to make use of templates to prevent copied code.
if(!myVector.empty()) {
for(auto& i : myVector)
{
std::cout << i << std::endl;
//some other code that is similar to below
}
}
if(!myUnorederedMap.empty()) {
for(auto i : myUnorderedMap)
{
std::cout << i.second << std::endl;
//some other code that is similar to top
}
}
How do I write a function template for the iterators when I have to call .second on my map but not my vector?

template <typename T>
T const& getValue(T const& t)
{
return t;
}
template <typename T, typename U>
U const& getValue(std::pair<T, U> const& p)
{
return p.second;
}
template <typename Container>
void foo(Container const& container)
{
if(!container.empty()) {
for(const auto& i : container)
{
std::cout << getValue(i) << std::endl;
}
}
}
Although, the line if(!container.empty()) does not seem to serve any purpose. You can just as well write:
template <typename Container>
void foo(Container const& container)
{
for(const auto& i : container)
{
std::cout << getValue(i) << std::endl;
}
}

Related

How to avoid duplicated code when using recursive parameter packs C++

How do you avoid code duplication when using varadic parameters in c++? Notice that I'm using templates recursively to achieve my goals, therefore I need some base cases and a recursive case. This creates a lot of code duplication, are there ways I could reduce this duplication?
Below, an example is provided of code that creates an arbitrary tensor (N dimensional array).
It's working fine but there's too much duplication. How can I avoid writing duplicated code when using template parameter packs recursively like this?
#include <cstddef>
#include <array>
#include <iostream>
template<typename T, std::size_t...>
class Tensor;
template<typename T, std::size_t N>
class Tensor<T, N> {
using Type = std::array<T, N>;
Type data;
public:
Tensor()
{
zero();
}
void zero()
{
fill(0);
}
Type::iterator begin() { return data.begin(); }
Type::iterator end() { return data.end(); }
void fill(T value)
{
std::fill(data.begin(), data.end(), value);
}
void print() const
{
std::cout << "[";
for(const auto& v : data)
{
std::cout << v << ",";
}
std::cout << "]";
}
};
template<typename T, std::size_t N, std::size_t M>
class Tensor<T, N, M>
{
using Type = std::array<Tensor<T, M>, N>;
Type data;
public:
Tensor()
{
zero();
}
void zero()
{
fill(0);
}
Type::iterator begin() { return data.begin(); }
Type::iterator end() { return data.end(); }
void fill(T value)
{
for(auto& v: data) {
std::fill(v.begin(), v.end(), value);
}
}
void print() const
{
std::cout << "[";
for(const auto& v : data)
{
v.print();
std::cout << ",";
}
std::cout << "]";
}
};
template<typename T, std::size_t N, std::size_t... M>
class Tensor<T, N, M...>
{
using Type = std::array<Tensor<T, M...>, N>;
Type data;
public:
Type::iterator begin() { return data.begin(); }
Type::iterator end() { return data.end(); }
Tensor()
{
zero();
}
void zero()
{
fill(0);
}
void fill(T value)
{
for(auto& v: data) {
v.fill(value);
}
}
void print() const
{
std::cout << "[";
for(const auto& v : data)
{
v.print();
std::cout << ",";
}
std::cout << "]";
}
};
The only difference between a single-dimension tensor and a multiple-dimension tensor is the type of std::array, T for single and Tensor<T, M...> for another.
template<typename T, std::size_t N, std::size_t... M>
class Tensor<T, N, M...> {
using InnerT = std::conditional_t<(sizeof...(M) > 0),
Tensor<T, M...>,
T>;
using Type = std::array<InnerT, N>;
Type data;
}
Then, use if constexpr to distinguish single-dimension case,
void fill(T value)
{
if constexpr(sizeof...(M) > 0) {
for(auto& v: data) {
v.fill(value);
}
} else {
std::fill(data.begin(), data.end(), value);
}
}
void print() const
{
std::cout << "[";
for(const auto& v : data)
{
if constexpr(sizeof...(M) > 0) {
v.print();
std::cout << ",";
} else {
std::cout << v << ",";
}
}
std::cout << "]";
}
Demo

C++ for range loop for template class

I have a template class below that depends on 2 classes U and T
and have implemented the iterator function for both classes (see the end of this post).
I can iterate over the respective vectors of the class using iterators,
but I was wondering if it was possible to do the same using the for range loop syntax instead of using iterators.
With something like
for (auto x : myclass <double>)
{
std::cout << x << std::endl ;
}
I know it does not work but I could not manage to find the syntax if any,
Thanks in advance for your answer
#include<iostream>
#include<string>
#include<vector>
template<class U,class T>
class MyClass
{
public:
MyClass(
const std::vector<U> & vect_u ,
const std::vector<T> & vect_t )
{
m_vect_u = vect_u;
m_vect_t = vect_t;
}
~MyClass(){}
// begin()
template<class Z>
typename std::enable_if<std::is_same<Z,T>::value, typename std::vector<Z>::iterator>::type
begin() noexcept { return m_vect_t.begin(); }
template<class Z>
typename std::enable_if<std::is_same<Z,U>::value, typename std::vector<Z>::iterator>::type
begin() noexcept { return m_vect_u.begin(); }
// end()
template<class Z>
typename std::enable_if<std::is_same<Z,T>::value, typename std::vector<Z>::iterator>::type
end() noexcept { return m_vect_t.end(); }
template<class Z>
typename std::enable_if<std::is_same<Z,U>::value, typename std::vector<Z>::iterator>::type
end() noexcept { return m_vect_u.end(); }
// cbegin()
template<class Z>
typename std::enable_if<std::is_same<Z,T>::value, typename std::vector<Z>::const_iterator>::type
cbegin() const noexcept { return m_vect_t.cbegin(); }
template<class Z>
typename std::enable_if<std::is_same<Z,U>::value, typename std::vector<Z>::const_iterator>::type
cbegin() const noexcept { return m_vect_u.cbegin(); }
// cend()
template<class Z>
typename std::enable_if<std::is_same<Z,T>::value, typename std::vector<Z>::const_iterator>::type
cend() const noexcept { return m_vect_t.cend(); }
template<class Z>
typename std::enable_if<std::is_same<Z,U>::value, typename std::vector<Z>::const_iterator>::type
cend() const noexcept { return m_vect_u.cend(); }
private:
std::vector<U> m_vect_u ;
std::vector<T> m_vect_t ;
};
int main()
{
std::vector<double> vect_double = {1.5,2.5,3.5} ;
std::vector<int> vect_int = {-1,-2,-3} ;
MyClass<double,int> myclass( vect_double, vect_int);
std::cout << "iteration over int" << std::endl ;
for(auto itr = myclass.begin<int>(); itr < myclass.end<int>() ; ++itr)
{
std::cout << *itr << std::endl ;
}
std::cout << "iteration over double" << std::endl ;
for(auto itr = myclass.begin<double>(); itr < myclass.end<double>() ; ++itr)
{
std::cout << *itr << std::endl ;
}
return 0 ;
}
You might provide function to return "range" (directly std::vector or a wrapper):
template <typename Z> // Or SFINAE or if constexpr or any other implementation
auto& getVector() { return std::get<std::vector<Z>&>(std::tie(m_vect_u, m_vect_t)); }
template <typename Z>
const auto& getVector() const { return std::get<const std::vector<Z>&>(std::tie(m_vect_u, m_vect_t)); }
and then
std::cout << "iteration over int" << std::endl ;
for (auto e : myclass.getVector<int>())
{
std::cout << e << std::endl ;
}
Demo
You need an object that has non-templated begin and end. For example a class template that wraps MyClass:
template <typename Z,typename U,typename T>
struct MyClassWrapper {
MyClass<U,T>* parent;
auto begin() { return parent-> template begin<Z>(); }
auto end() { return parent-> template end<Z>();}
};
template <typename Z,typename U,typename T>
MyClassWrapper<Z,U,T> make_wrapper(MyClass<U,T>* p){
return {p};
}
Then this works:
for (auto& x : make_wrapper<int>(&myclass)){
std::cout << x << "\n";
}
for (auto& x : make_wrapper<double>(&myclass)){
std::cout << x << "\n";
}
Live Demo

STL container with a specific type as a generic argument

Is there any way that I can make a function which takes a container with a specific type (lets say std::string) as a parameter
void foo(const std::container<std::string> &cont)
{
for(std::string val: cont) {
std::cout << val << std::endl;
}
}
and call it for every type of stl container as input? like above?
std::set<std::string> strset;
std::vector<std::string> strvec;
std::list<std::string> strlist;
foo(strset);
foo(strvec);
foo(strlist);
You can make foo a function template taking a template template parameter for the container type.
e.g.
template<template<typename...> typename C>
void foo(const C<std::string> &cont)
{
for(std::string val: cont) {
std::cout << val << std::endl;
}
}
LIVE
Depending on if you want to overload foo for other cases or not
// Doesn't participate in overload resolution when not applicable
template<typename Container, typename = std::enable_if_t<std::is_same_v<typename Container::value_type, std::string>>>
void foo(const Container &cont) {
for(std::string val: cont) {
std::cout << val << std::endl;
}
}
// simpler
template<typename Container>
void foo(const Container &cont) {
static_assert(std::is_same_v<typename Container::value_type, std::string>, "Container must contain std::string")
for(std::string val: cont) {
std::cout << val << std::endl;
}
}
You might use a different test to std::is_same, such as std::is_convertible to allow
std::vector<char *> c_strings;
foo(c_strings);
You may want to consider using iterators instead. An intermediate result may look like
template<typename Iter>
void foo(Iter begin, Iter end) {
using T = decltype(*begin);
std::for_each(begin, end, [] (cons T & t) {
std::out << t << '\n';
}
}
Now using a callable template:
template<typename Iter, typename Callable>
void foo(Iter begin, Iter end, Callable & c) {
std::for_each(begin, end, c);
}
We just learned to use what the STL already offers.
Adding on to #songyuanyao's answer, I think we can generalize it further to:
template<template<typename...> typename C, typename ... D>
void foo(const C<D...> &cont)
{
for(const auto& val: cont) {
std::cout << val << std::endl;
}
}

Non-overloaded C++ Function That Can Print a vector and a vector of vectors?

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

Can C++ enable_if have a default implementation?

I want to write a function print which behaves differently according to the type of its argument.
Here is my implementation:
template <typename T, typename std::enable_if<std::is_array<T>::value, int>::type = 0>
void print(const T &v) {
std::cout << "array: ";
for (const auto &e : v) {
std::cout << e << ", ";
}
std::cout << std::endl;
}
template <typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
void print(const T &v) {
std::cout << "integral: " << v << std::endl;
}
template <typename T, typename std::enable_if<!(std::is_array<T>::value || std::is_integral<T>::value), int>::type = 0>
void print(const T &v) {
std::cout << "default: " << v << std::endl;
}
This code works as expected, but the conditions in the last specification are too complicated.
Is there any solution to simplify the last one?
A general approach you can use for a default case is to have a function which takes a variable argument list. This will only be used if no other function matches. Here is an example:
template <typename T, typename std::enable_if<std::is_array<T>::value, int>::type = 0>
void print_helper(const T &v,int) {
std::cout << "array: ";
for (const auto &e : v) {
std::cout << e << ", ";
}
std::cout << std::endl;
}
template <typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
void print_helper(const T &v,int) {
std::cout << "integral: " << v << std::endl;
}
template <typename T>
void print_helper(const T &v,...) {
std::cout << "default: " << v << std::endl;
}
template <typename T>
void print(const T &v)
{
print_helper(v,0);
}
For only two overloads, the extra function may not be worth it, but as you get more overloads this form can really pay off for the default case.
We can use an extra chooser to make things better for us, courtesy of Xeo:
struct otherwise{ otherwise(...){} };
template<unsigned I>
struct choice : choice<I+1>{};
// terminate recursive inheritence at a convenient point,
// large enough to cover all cases
template<> struct choice<10>{};
Then each ranking on our choice list will be preferred to the next, and we just have to disable as we go:
// just forward to our first choice
template <class T> void print(const T &v) { print(v, choice<0>{}); }
Where our top choice is array:
template <class T, class = std::enable_if_t<std::is_array<T>::value>>
void print(const T& v, choice<0> ) { ... }
And then integral:
template <class T, class = std::enable_if_t<std::is_integral<T>::value>>
void print(const T& v, choice<1> ) { ... }
And then anything
template <class T>
void print(const T& v, otherwise ) { ... }
This structure allows for arbitrarily many choices.