I would like to achieve something like this:
template<class IT>
size_t foo(IT begin,IT end) {return end-begin;}
template<template (class) class FOO>
class BAR
{
public:
any_container<any_type> container;
size_t call_foo
{
FOO<any_container<any_type>::iterator>(container.begin(), container.end());
}
};
Moreover, I want to be able to pass function, lambda or function object as FOO.
Probably std::function should be used here but it is not possible to declare std::function<size_t(T,T)> with arbitrary type T.
And I definitely don't want to specify type of inner container or its iterator on template BAR argument list.
Is there any way to solve this in elegant way?
Based on your comment I think you are looking for something like this:
#include <algorithm>
#include <iostream>
#include <iterator>
#include <utility>
#include <vector>
struct Sender {
template<typename Iter>
std::size_t operator()(Iter begin, Iter end) {
for(Iter cur = begin; cur != end; ++cur) {
// your low-level send stuff
}
return (end - begin);
}
};
struct Receiver {
template<typename Iter>
std::size_t operator()(Iter begin, Iter end) {
for(Iter cur = begin; cur != end; ++cur) {
// your low-level receive stuff
}
return (end - begin);
}
};
template<typename Fn>
struct Bar
{
protected:
Fn _fn;
public:
Bar(Fn fn) : _fn(std::move(fn)) { }
template<typename Container>
std::size_t call_fn(Container & container)
{
return _fn(std::begin(container), std::end(container));
}
};
template<typename Fn>
auto create_bar(Fn && fn) -> Bar<typename std::remove_reference<Fn>::type> {
return { std::forward<Fn>(fn) };
}
Usage is simple:
template<typename Iter>
std::size_t my_foo(Iter begin, Iter end) {
return (end - begin);
}
int main(int argc, char ** argv) {
typedef typename std::vector<int>::iterator iter;
// functors
auto functor = create_bar(Sender());
// lambdas
auto lambda = create_bar([](iter begin, iter end) -> std::size_t { return (end - begin); });
// plain old functions
auto function = create_bar(&my_foo<iter>);
std::vector<int> tmp = { 0, 1, 2, 5};
std::cout << "Functor: " << functor.call_fn(tmp) << std::endl;
std::cout << "Lambda: " << lambda.call_fn(tmp) << std::endl;
std::cout << "Function: " << function.call_fn(tmp) << std::endl;
return 0;
}
Live example here
Related
I have two structures with methods returning iterators on begin and end of object collection they own. Methods have different names (this may seem like a bad app architecture, but this is only a simplified model):
struct A {
std::vector<int>::iterator a_begin() { return v.begin(); }
std::vector<int>::iterator a_end() { return v.end(); }
std::vector<int> v = { 1, 2 };
};
struct B {
std::vector<float>::iterator b_begin() { return v.begin(); }
std::vector<float>::iterator b_end() { return v.end(); }
std::vector<float> v = { 1.0f, 2.0f };
};
I want to write a template function which will iterate over given object (of type A or type B) and do some job with its elements. My approach is:
template<class T>
void foo(T t) {
if constexpr (std::is_same_v<T, A>) {
for (auto it = t.a_begin(); it != t.a_end(); it++) {
// a lot of stuff
}
} else if constexpr (std::is_same_v<T, B>) {
for (auto it = t.b_begin(); it != t.b_end(); it++) {
// the same stuff
}
}
}
It looks a bit ugly for me because of for loops bodies are the same. Is there any way to improve this?
I'm taking your claim about naming and complexity at face value, so abstract and bridge.
namespace detail {
inline auto foo_begin(A& a) { return a.a_begin(); }
inline auto foo_end (A& a) { return a.a_end(); }
inline auto foo_begin(B& b) { return b.b_begin(); }
inline auto foo_end (B& b) { return b.b_end(); }
}
template<class T>
void foo(T t) {
for (auto it = detail::foo_begin(t); it != detail::foo_end(t); ++it) {
// the same stuff
}
}
The operation you wanted to vary is the range selection. So a small overload set for the types you care about should do it nicely.
If you do this often, a range adapter may be worthwhile to consider. You can write it by hand, or with C++20's std::ranges::subrange you may even leverage this overload set itself.
template<class T>
void foo(T t) {
for (auto &item : std::ranges::subrange(detail::foo_begin(t), detail::foo_end(t))) {
// the same stuff
}
}
The key concept for iterators is that two iterators define a sequence. That's all there is to it: just use a pair of iterators, rather than a container:
template <class It>
void foo(It begin, It end) {
while (begin != end) {
// a lot of stuff
++begin;
}
}
Now you can call it with a range defined by any kind of container you like:
A a;
foo(a.a_begin(), a.a_end());
B b;
foo(b.b_begin(), b.b_end());
Maybe this helps to rethink your problem.
#include <vector>
#include <algorithm>
template<typename T>
void DoStuff(const T& value)
{
};
template<typename T>
void DoAllStuffFor(const std::vector<T>& v)
{
std::for_each(v.begin(), v.end(), DoStuff<T>);
}
int main()
{
std::vector<int> v1 = { 1, 2 };
std::vector<double> v2 = { 1, 2 };
DoAllStuffFor(v1);
DoAllStuffFor(v2);
}
Is there any way to receive any iterable container as a parameter?
I would like a function to be able to receive any container while being able to begin() and end() on it. Unless I am wrong, every container should have these functions (std::view does not?).
Specifically, is there a way to receive a Container as an argument?
C++20 and/or tempates are fine.
void do_stuff(any_iterable_collection<int> &coll) {
for (auto it = coll.begin() ; it != coll.end() ; ++it) {
// do stuff with *it
}
}
std::list<int> list;
std::vector<int> vector;
std::set<int> set;
do_stuff(list);
do_stuff(vector);
do_stuff(set);
You can simply use the same way the standard perform this action:
template <typename Iterator>
void do_stuff(Iterator first, Iterator last) {
for (auto it = first; it != last; it = std::next(it)) {
// do stuff with *it
}
}
int main() {
std::vector<int> vec = {1, 5, 9};
do_stuff(vec.begin(), vec.end());
return EXIT_SUCCESS;
}
If you insist on the container:
template <template<typename> class Container>
void do_stuff(Container<int> &container) {
for (auto it = std::begin(container); it != std::end(container); it = std::next(it)) {
// do stuff with *it
std::cout << *it << std::endl;
}
}
Or for more generally container:
template <template<typename> class Container, typename CType>
void do_stuff(Container<CType> &container) {
for (auto it = std::begin(container); it != std::end(container); it = std::next(it)) {
// do stuff with *it
std::cout << *it << std::endl;
}
}
A simplified c++20 concept based on your requirements:
#include <concepts>
template <typename C, typename T>
concept any_iterable_collection =
std::same_as<typename C::value_type, T> &&
requires (C c) {
{ c.begin() } -> std::forward_iterator;
{ c.end() } -> std::forward_iterator;
{ const_cast<const C&>(c).begin() } -> std::forward_iterator;
{ const_cast<const C&>(c).end() } -> std::forward_iterator;
};
Usage:
void do_stuff(const any_iterable_collection<int> auto& coll);
DEMO
As an exercise I'm trying to implement Python's str.join method in C++. I will eventually add the function as a method of the std::string class but I figure getting it to work is more of a priority. I've defined the function as follows:
template<typename Iterable>
std::string join(const std::string sep, Iterable iter);
Is there any way that I can ensure that the Iterable type is actually iterable? E.g. I wouldn't want to receive an int or char..
In C++, rather than having one Iterable, we pass in an iterator (almost a pointer) to the front and the end of the range:
template<typename Iter>
std::string join(const std::string &sep, Iter begin, Iter end);
Note that the sep should be passed as const reference, as you don't need to copy it.
You don't need to worry about whether the Iter is actually an iterator, though. This is because the code will simply fail to compile if it doesn't work.
For example, suppose you implemented it like so (this is a bad implementation):
template<typename Iter>
std::string join(const std::string &sep, Iter begin, Iter end) {
std::string result;
while (begin != end) {
result += *begin;
++begin;
if (begin != end) result += sep;
}
return result;
}
Then the type passed in as Iter must have an operator++, an operator!=, and an operator* to work, which is the well understood contract of an iterator.
All standard c++ collections has begin() and end() member functions. You could make use of that fact to ensure that the passed argument is actually a collection (in your terminology - iterable) by some SFINAE (c++11 example):
#include <array>
#include <list>
#include <vector>
#include <map>
#include <string>
template <class Iterable>
auto join(const std::string sep, const Iterable& iterable) -> decltype(iterable.begin(), iterable.end(), std::string{}) {
(void)sep; // to suppress warning that sep isn't used
// some implementation
return {};
}
int main() {
join(";", std::array<int, 5>{});
join(";", std::list<int>{});
join(";", std::vector<float>{});
join(";", std::string{});
join(";", std::map<int, float>{});
//join(";", int{}); // does not compile as int is not a collection
}
[live demo]
You may use template template syntax and - if needed - use SFINAE to make sure that proper class members are existing:
#include <vector>
#include <list>
#include <string>
#include <map>
#include <ostream>
//! Single value containers.
template< template<class> class L, class T,
class EntryT = typename L<T>::value_type>
std::string my_join(const std::string_view sep, const L<T>& anyTypeIterable)
{
std::stringstream ss;
bool first = true;
for (const EntryT& entry : anyTypeIterable)
{
if (first) first = false;
else ss << sep;
ss << entry;
}
return ss.str();
}
//! std::map specialization - SFINAE used here to filter containers with pair value_type
template< template<class, class> class L, class T0, class T1,
class EntryT = typename L<T0, T1>::value_type,
class FirstT = typeof(EntryT::first),
class SecondT = typeof(EntryT::second)>
std::string my_join(const std::string_view sep, const L<T0, T1>& anyTypeIterable)
{
std::stringstream ss;
bool first = true;
for (const EntryT& entry : anyTypeIterable)
{
if (first) first = false;
else ss << sep;
ss << entry.first << sep << entry.second;
}
return ss.str();
}
int main()
{
std::cout << my_join("; ", std::vector<int>({1, 2, 3, 4})) << std::endl;
std::cout << my_join("; ", std::list<int>({1, 2, 3, 4})) << std::endl;
std::cout << my_join("; ", std::string("1234")) << std::endl;
std::cout << my_join("; ", std::map<int, int>({ {1, 2}, {3, 4} })) << std::endl;
return 0;
}
// Output:
// 1; 2; 3; 4
// 1; 2; 3; 4
// 1; 2; 3; 4
// 1; 2; 3; 4
From https://devblogs.microsoft.com/oldnewthing/20190619-00/?p=102599 :
template<typename C, typename T = typename C::value_type>
auto do_something_with(C const& container)
{
for (int v : container) { ... }
}
Or if the container doesn't implement value_type:
template<typename C, typename T = std::decay_t<decltype(*begin(std::declval<C>()))>>
auto do_something_with(C const& container)
{
for (int v : container) { ... }
}
Or if you want only containers containing types convertible to int:
template<typename C, typename T = std::decay_t<decltype(*begin(std::declval<C>()))>,
typename = std::enable_if_t<std::is_convertible_v<T, int>>>
auto do_something_with(C const& container)
{
for (int v : container) { ... }
}
But see the comments in that blog post for more refinements.
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