I have a C++ method findID that uses templates, and I want to be able to run a condition in this method based on the input type. The template parameter U will either be of type int or type string. I want to run a different condition based on the type of ID.
The code I have is follows:
template <typename S>
template <typename U>
S * findID(U ID){
for (typename vector<S*>::collectionsIter element = collection.begin() ; element != collection.end(); ++element)
if((*element)->getID() == ID) return *element;
return NULL;
}
I want my code to do the following:
template <typename S>
template <typename U>
S * findID(U ID){
***if ID is an int:
for (typename vector<S*>::collectionsIter element = collection.begin() ; element != collection.end(); ++element)
if((*element)->getID() == ID) return *element;
***if ID is a string:
for (typename vector<S*>::collectionsIter element = collection.begin() ; element != collection.end(); ++element)
if((*element)->getStringID() == ID) return *element;
***else
return NULL;
}
The reason that I want to do this is because I want to be able to compare string variables of ID to the string method of getStringID(), and the int variables of ID to the int method of getID(). In addition I do not want to break these up into separate methods, so I am trying to use templates and these conditions to refactor it into 1 method.
Just use 2 overloads:
template <typename S>
S* findID(int ID){
for (auto* element : collection)
if (element->getID() == ID) return element;
return nullptr;
}
template <typename S>
S* findID(const std::string& ID){
for (auto* element : collection)
if (element->getStringID() == ID) return element;
return nullptr;
}
Here's one way to do it using C++17 if constexpr:
struct Foo {
int id;
std::string stringId;
int getId() const { return id; }
const std::string& getStringId() const { return stringId; }
};
template <typename Cont, typename T>
auto findId(const Cont& c, const T& id) {
const auto pred = [&id](const auto& x) {
if constexpr (std::is_convertible_v<T, std::string>)
return x.getStringId() == id;
else if constexpr (std::is_convertible_v<T, int>)
return x.getId() == id;
else
static_assert(false, "Unsupported id type.");
return false;
};
const auto findIt = std::find_if(begin(c), end(c), pred);
return findIt == end(c) ? nullptr : &(*findIt);
}
int main() {
using namespace std;
vector<Foo> foos{{1, "1"}, {2, "2"}, {3, "3"}};
auto foo2Int = findId(foos, 2);
auto foo2String = findId(foos, "2"s);
cout << foo2Int->id << ", " << foo2String->stringId << '\n';
}
Related
I would like to access to a tuple element at compile time by a value constexpr in the type
#include <iostream>
#include <tuple>
#include <utility>
struct A {
static constexpr int id = 1;
void work() {
std::cout << "A" << std::endl;
}
};
struct B {
static constexpr int id = 2;
void work() {
std::cout << "B" << std::endl;
}
};
int main() {
A a;
B b;
std::tuple<A,B> t = std::make_tuple(a,b);
static constexpr int search_id = 2;
auto& item = std::get< ? ( T::id == search_id ) ? >(t);
item.work();
return 0;
}
I guess using std::apply and test would be a runtime search...
I'm using c++20
Instead of std::get a single element, you can use std::apply to iterate over the elements of the tuple and perform operations based on the element type
A a;
B b;
auto t = std::make_tuple(a, b);
static constexpr int search_id = 2;
std::apply([](auto&... items) {
([]<class T>(T& item) {
if constexpr (T::id == search_id)
item.work();
}(items), ...);
}, t);
Demo
If you really want to get a single tuple element with a specific id value, you can still use std::apply to expand the id of all elements and find the offset of the value equal to search_id as the template parameter of std::get
auto& item = std::apply([&t]<class... Args>(const Args&... items) -> auto& {
constexpr auto id = [] {
std::array ids{Args::id...};
return ids.end() - std::ranges::find(ids, search_id);
}();
return std::get<id>(t);
}, t);
item.work();
You can create constrexpr function to get index:
template <typename... Ts>
constexpr std::size_t get_index(int id)
{
constexpr int ids[] = {Ts::id...};
const auto it = std::find(std::begin(ids), std::end(ids), id);
// Handle absent id.
if (it == std::end(ids)) {
throw std::runtime("Invalid id");
}
// You can also possibly handle duplicate ids.
return std::distance(std::begin(ids), it);
}
template <int id, typename... Ts>
constexpr auto& get_item(std::tuple<Ts...>& t)
{
return std::get<get_index<Ts...>(id)>(t);
}
template <int id, typename... Ts>
constexpr const auto& get_item(const std::tuple<Ts...>& t)
{
return std::get<get_index<Ts...>(id)>(t);
}
and then
auto& item = get_item<search_id>(t);
This is a prime candidate for std::disjunction, which can be used to perform a compile-time linear search; you just need a helper type to act as the predicate:
namespace detail {
template<typename T, auto Id, auto I, typename U = std::tuple_element_t<I, T>>
struct get_by_id_pred : std::bool_constant<std::remove_cvref_t<U>::id == Id> {
static constexpr auto index = I;
};
}
template<int Id>
constexpr auto&& get_by_id(auto&& t) noexcept {
using tuple_t = std::remove_cvref_t<decltype(t)>;
return [&]<auto ...Is>(std::index_sequence<Is...>) -> auto&& {
using res = std::disjunction<detail::get_by_id_pred<tuple_t, Id, Is>...>;
static_assert(res::value, "id not found");
return std::get<res::index>(decltype(t)(t));
}(std::make_index_sequence<std::tuple_size_v<tuple_t>>{});
}
...
auto& item = get_by_id<search_id>(t);
Online Demo
How to handle an api which returns different data types for the same input data types?
Looking at the below example, apicall should return a date or a string depending on the input attribute:
#include <iostream>
#include <string>
using namespace std;
???? apicall(string datatype, string attribute)
{
// code
}
int main(int argc, char** argv)
{
string datatype = "Thomas"
string attribute = "bithday"
cout << apicall(datatype, attribute) << endl;
string datatype = "Thomas"
string attribute = "address"
cout << apicall(datatype, attribute) << endl;
}
What could be in place of ???? (apicall return datatype) and how to handle these cases?
I am trying to understand these concepts as my experience to date has been with duck typed scripting languages.
The ideal solution is to use a std::variant, which is a safe union type like.
This allows you to write the following:
using DateOrString = std::variant<DateType, std::string>;
DateOrString api_call(std::string, std::string) {
// you can return both DateType and std::string
}
// ...
auto result = api_call("", "");
auto& str = std::get<std::string>(result);
Unfortunately std::variant is a C++17 feature. However different compilers already support it.
As already has been suggested, boost has a variant class and you can use it with any C++ standard.
As last option, you may implement a "variant-like" class which handles both a date and a string. Your function should return it.
Here a demo how to quickly implement that kind of class.
Note that that class is safe because the type is checked at runtime.
As a variant object, your callee function should branch on the type, something like:
auto result = api_call(/*...*/);
if (result.is_string()) {
// result is a string
const auto& str = result.get_string();
} else {
// result is a date
const auto& date = result.get_date();
}
... returns different data types for the same input data types?
This is literally impossible. A function is defined with one (or zero) return types, and zero or more input parameter types.
The workarounds are:
Write a single function returning a variant type, such as std::variant in C++17, or Boost.Variant if that's not available.
Write multiple functions with different return types (the caller just has to choose the right one)
Invert control, so that instead of returning a value, you pass an object capable of processing all the required types:
struct APIHandler {
virtual ~APIHandler() {}
virtual void operator()(int) {}
virtual void operator()(string) {}
};
void apicall(string name, string attr, APIHandler &h) {
// dummy implementation
if (attr == "address") {
h("123 Woodford Road");
} else if (attr == "birthday") {
h(19830214);
}
}
// implement your type-specific logic here
struct MyHandler: APIHandler {
void operator()(int i) override {
cout << "got an int:" << i << '\n';
}
void operator()(string s) override {
cout << "got a string:" << s << '\n';
}
};
// and use it like:
MyHandler mh;
apicall("Thomas", "birthday", mh);
apicall("Thomas", "address", mh);
You want a std::variant in C++17 or a boost::variant or roll your own crude variant something like this:
constexpr std::size_t max() { return 0; }
template<class...Ts>
constexpr std::size_t max( std::size_t t0, Ts...ts ) {
return (t0<max(ts...))?max(ts...):t0;
}
template<class T0, class...Ts>
struct index_of_in;
template<class T0, class...Ts>
struct index_of_in<T0, T0, Ts...>:std::integral_constant<std::size_t, 0> {};
template<class T0, class T1, class...Ts>
struct index_of_in<T0, T1, Ts...>:
std::integral_constant<std::size_t,
index_of_in<T0, Ts...>::value+1
>
{};
struct variant_vtable {
void(*dtor)(void*) = 0;
void(*copy)(void*, void const*) = 0;
void(*move)(void*, void*) = 0;
};
template<class T>
void populate_vtable( variant_vtable* vtable ) {
vtable->dtor = [](void* ptr){ static_cast<T*>(ptr)->~T(); };
vtable->copy = [](void* dest, void const* src){
::new(dest) T(*static_cast<T const*>(src));
};
vtable->move = [](void* dest, void* src){
::new(dest) T(std::move(*static_cast<T*>(src)));
};
}
template<class T>
variant_vtable make_vtable() {
variant_vtable r;
populate_vtable<T>(&r);
return r;
}
template<class T>
variant_vtable const* get_vtable() {
static const variant_vtable table = make_vtable<T>();
return &table;
}
template<class T0, class...Ts>
struct my_variant {
std::size_t index = -1;
variant_vtable const* vtable = 0;
static constexpr auto data_size = max(sizeof(T0),sizeof(Ts)...);
static constexpr auto data_align = max(alignof(T0),alignof(Ts)...);
template<class T>
static constexpr std::size_t index_of() {
return index_of_in<T, T0, Ts...>::value;
}
typename std::aligned_storage< data_size, data_align >::type data;
template<class T>
T* get() {
if (index_of<T>() == index)
return static_cast<T*>((void*)&data);
else
return nullptr;
}
template<class T>
T const* get() const {
return const_cast<my_variant*>(this)->get<T>();
}
template<class F, class R>
using applicator = R(*)(F&&, my_variant*);
template<class T, class F, class R>
static applicator<F, R> get_applicator() {
return [](F&& f, my_variant* ptr)->R {
return std::forward<F>(f)( *ptr->get<T>() );
};
}
template<class F, class R=typename std::result_of<F(T0&)>::type>
R visit( F&& f ) & {
if (index == (std::size_t)-1) throw std::invalid_argument("variant");
static const applicator<F, R> table[] = {
get_applicator<T0, F, R>(),
get_applicator<Ts, F, R>()...
};
return table[index]( std::forward<F>(f), this );
}
template<class F,
class R=typename std::result_of<F(T0 const&)>::type
>
R visit( F&& f ) const& {
return const_cast<my_variant*>(this)->visit(
[&f](auto const& v)->R
{
return std::forward<F>(f)(v);
}
);
}
template<class F,
class R=typename std::result_of<F(T0&&)>::type
>
R visit( F&& f ) && {
return visit( [&f](auto& v)->R {
return std::forward<F>(f)(std::move(v));
} );
}
explicit operator bool() const { return vtable; }
template<class T, class...Args>
void emplace( Args&&...args ) {
clear();
::new( (void*)&data ) T(std::forward<Args>(args)...);
index = index_of<T>();
vtable = get_vtable<T>();
}
void clear() {
if (!vtable) return;
vtable->dtor( &data );
index = -1;
vtable = nullptr;
}
~my_variant() { clear(); }
my_variant() {}
void copy_from( my_variant const& o ) {
if (this == &o) return;
clear();
if (!o.vtable) return;
o.vtable->copy( &data, &o.data );
vtable = o.vtable;
index = o.index;
}
void move_from( my_variant&& o ) {
if (this == &o) return;
clear();
if (!o.vtable) return;
o.vtable->move( &data, &o.data );
vtable = o.vtable;
index = o.index;
}
my_variant( my_variant const& o ) {
copy_from(o);
}
my_variant( my_variant && o ) {
move_from(std::move(o));
}
my_variant& operator=(my_variant const& o) {
copy_from(o);
return *this;
}
my_variant& operator=(my_variant&& o) {
move_from(std::move(o));
return *this;
}
template<class T,
typename std::enable_if<!std::is_same<typename std::decay<T>::type, my_variant>{}, int>::type =0
>
my_variant( T&& t ) {
emplace<typename std::decay<T>::type>(std::forward<T>(t));
}
};
then your code looks like:
variant<string, int> apicall(string datatype, string attribute)
{
if (datatype > attribute) return string("hello world");
return 7;
}
int main()
{
string datatype = "Thomas"
string attribute = "bithday"
apicall(datatype, attribute).visit([](auto&&r){
cout << r << endl;
});
string datatype = "Thomas"
string attribute = "address"
apicall(datatype, attribute).visit([](auto&& r){
cout << r << endl;
});
}
with whatever visit or apply_visitor free function or method your particular variant supports.
This gets much more annoying in C++11 as we don't have generic lambdas.
You could use a variant, but it's up to the caller site to check the results. Boost and std defines two variant types, i.e. std::variant and std::any.
Lets say I have two template functions for iterating through a container:
template <typename I, typename C>
It Prev(I i, const C& c) noexcept {
Expects(i != c.end());
if (i == c.begin()) return c.end();
return i - 1;
}
and
template <typename I, typename C>
It Next(I i, const C& c) noexcept {
Expects(i != c.end());
return i + 1;
}
And a class with a GetNextElement and GetPrevElement that use these templates:
struct MyClass {
std::vector<int> elements;
int* GetNextElement(std::vector<int>::iterator i) {
auto next = Next(i, elements);
if (next == elements.end()) return nullptr;
return &*it;
}
int* GetPrevElement(std::vector<int>::iterator i) {
auto prev = Prev(i, elements);
if (prev == elements.end()) return nullptr;
return &*it;
}
};
These two methods do the same thing, except for calling a different template. How do I turn these into a single member function that might be called like
MyClass mc;
// ...
auto it = mc.elements.begin();
auto next = mc.GetElement<Next>(it);
// Or maybe mc.GetElement(it, Next);
#define OINVOKE(...) __VA_ARGS__(decltype(args)(args)...)
#define OVERLOADS_OF(...) [](auto&&...args)\
noexcept(noexcept(OINVOKE(__VA_ARGS__)))\
->decltype(OINVOKE(__VA_ARGS__))\
{ return OINVOKE(__VA_ARGS__); }
auto fNext=OVERLOADS_OF(Next);
auto fPrev=OVERLOADS_OF(Prev);
Then:
template<auto* pf>
int* GetElement(std::vector<int>::iterator i) {
auto next = (*pf)(i, elements);
if (next == elements.end()) return nullptr;
return &*it;
}
auto it = mc.elements.begin();
auto next = GetElement<&fNext>(it);
requires C++17.
This problem is greatly simplified if you can wrap your functions in struct.
struct Prev
{
template <typename I, typename C>
I operator()(I i, const C& c) noexcept {
Expects(i != c.end());
if (i == c.begin()) return c.end();
return i - 1;
}
};
struct Next
{
template <typename I, typename C>
I operator()(I i, const C& c) noexcept {
Expects(i != c.end());
return i + 1;
}
};
You can then simply pass them as template arguments.
struct MyClass
{
public:
auto begin() { return elements.begin(); }
auto end() { return elements.end(); }
template<typename T, typename I>
I GetElement(I iter)
{
return T()(iter, this->elements);
}
private:
std::vector<int> elements;
};
int main()
{
MyClass mc;
auto it = mc.begin();
auto next = mc.GetElement<Next>(it);
auto prev = mc.GetElement<Prev>(it);
}
You can try this code:
template <std::vector<int>::iterator (*FOO)(std::vector<int>::iterator, const std::vector<int>&)>
std::vector<int>::iterator GetElement(std::vector<int>::iterator i)
{
return FOO(i, elements);
}
I have a variadic class template
template <size_t ...T>
struct Foo
{
std::vector<size_t> t;
bool IsEqual()
{
//??
}
};
which I want to use like:
Foo<1,2,3,4> foo;
foo.data = {1,2,3,4};
foo.IsEqual();
How can I implement IsEqual to iterate and compare every element of the vector and return false / true if the elements are in the same order as the template parameters?
Use the index sequence trick:
bool IsEqual()
{
return t.size() == sizeof...(T) &&
IsEqual(std::make_index_sequence<sizeof...(T)>{});
}
with:
template <size_t... Is>
bool IsEqual(std::index_sequence<Is...> ) {
bool valid = true;
using expander = int[];
expander{0,
(valid = valid && t[Is] == T,
0)...
};
return valid;
}
Could even do this in one function by taking advantage of the fact that every value computation and side effect in an initializer-clause is sequenced before the next one by doing this in one go:
bool IsEqual()
{
if (t.size() == sizeof...(T)) {
auto it = t.begin();
bool valid = true;
using expander = int[];
expander{0,
(valid = valid && *it++ == T,
0)...
};
return valid;
}
else {
return false;
}
}
Simply unpack template arguments.
template <size_t ...T>
struct Foo
{
std::vector<size_t> t;
bool IsEqualTemplate(size_t index)
{
return true;
}
template <typename FIRSTARG, typename ...OTHERARGS>
bool IsEqualTemplate(size_t index, FIRSTARG firstArg, OTHERARGS... otherArgs)
{
return t[index] == firstArg && IsEqualTemplate(index + 1, otherArgs...);
}
bool IsEqual()
{
return t.size() == sizeof...(T) ? IsEqualTemplate(0, T...) : false;
}
};
Is there any existing iterator implementation (perhaps in boost) which implement some sort of flattening iterator?
For example:
unordered_set<vector<int> > s;
s.insert(vector<int>());
s.insert({1,2,3,4,5});
s.insert({6,7,8});
s.insert({9,10,11,12});
flattening_iterator<unordered_set<vector<int> >::iterator> it( ... ), end( ... );
for(; it != end; ++it)
{
cout << *it << endl;
}
//would print the numbers 1 through 12
I don't know of any implementation in a major library, but it looked like an interesting problem so I wrote a basic implementation. I've only tested it with the test case I present here, so I don't recommend using it without further testing.
The problem is a bit trickier than it looks because some of the "inner" containers may be empty and you have to skip over them. This means that advancing the flattening_iterator by one position may actually advance the iterator into the "outer" container by more than one position. Because of this, the flattening_iterator needs to know where the end of the outer range is so that it knows when it needs to stop.
This implementation is a forward iterator. A bidirectional iterator would also need to keep track of the beginning of the outer range. The flatten function templates are used to make constructing flattening_iterators a bit easier.
#include <iterator>
// A forward iterator that "flattens" a container of containers. For example,
// a vector<vector<int>> containing { { 1, 2, 3 }, { 4, 5, 6 } } is iterated as
// a single range, { 1, 2, 3, 4, 5, 6 }.
template <typename OuterIterator>
class flattening_iterator
{
public:
typedef OuterIterator outer_iterator;
typedef typename OuterIterator::value_type::iterator inner_iterator;
typedef std::forward_iterator_tag iterator_category;
typedef typename inner_iterator::value_type value_type;
typedef typename inner_iterator::difference_type difference_type;
typedef typename inner_iterator::pointer pointer;
typedef typename inner_iterator::reference reference;
flattening_iterator() { }
flattening_iterator(outer_iterator it) : outer_it_(it), outer_end_(it) { }
flattening_iterator(outer_iterator it, outer_iterator end)
: outer_it_(it),
outer_end_(end)
{
if (outer_it_ == outer_end_) { return; }
inner_it_ = outer_it_->begin();
advance_past_empty_inner_containers();
}
reference operator*() const { return *inner_it_; }
pointer operator->() const { return &*inner_it_; }
flattening_iterator& operator++()
{
++inner_it_;
if (inner_it_ == outer_it_->end())
advance_past_empty_inner_containers();
return *this;
}
flattening_iterator operator++(int)
{
flattening_iterator it(*this);
++*this;
return it;
}
friend bool operator==(const flattening_iterator& a,
const flattening_iterator& b)
{
if (a.outer_it_ != b.outer_it_)
return false;
if (a.outer_it_ != a.outer_end_ &&
b.outer_it_ != b.outer_end_ &&
a.inner_it_ != b.inner_it_)
return false;
return true;
}
friend bool operator!=(const flattening_iterator& a,
const flattening_iterator& b)
{
return !(a == b);
}
private:
void advance_past_empty_inner_containers()
{
while (outer_it_ != outer_end_ && inner_it_ == outer_it_->end())
{
++outer_it_;
if (outer_it_ != outer_end_)
inner_it_ = outer_it_->begin();
}
}
outer_iterator outer_it_;
outer_iterator outer_end_;
inner_iterator inner_it_;
};
template <typename Iterator>
flattening_iterator<Iterator> flatten(Iterator it)
{
return flattening_iterator<Iterator>(it, it);
}
template <typename Iterator>
flattening_iterator<Iterator> flatten(Iterator first, Iterator last)
{
return flattening_iterator<Iterator>(first, last);
}
The following is a minimal test stub:
#include <algorithm>
#include <iostream>
#include <set>
#include <vector>
int main()
{
// Generate some test data: it looks like this:
// { { 0, 1, 2, 3 }, { 4, 5, 6, 7 }, { 8, 9, 10, 11 } }
std::vector<std::vector<int>> v(3);
int i(0);
for (auto it(v.begin()); it != v.end(); ++it)
{
it->push_back(i++); it->push_back(i++);
it->push_back(i++); it->push_back(i++);
}
// Flatten the data and print all the elements:
for (auto it(flatten(v.begin(), v.end())); it != v.end(); ++it)
{
std::cout << *it << ", ";
}
std::cout << "\n";
// Or, since the standard library algorithms are awesome:
std::copy(flatten(v.begin(), v.end()), flatten(v.end()),
std::ostream_iterator<int>(std::cout, ", "));
}
Like I said at the beginning, I haven't tested this thoroughly. Let me know if you find any bugs and I'll be happy to correct them.
I decided to "improve" a bit on the flattening iterator concept, though as noted by James you are stuck using Ranges (except for the inner most container), so I just used ranges through and through and thus obtained a flattened range, with an arbitrary depth.
First I used a building brick:
template <typename C>
struct iterator { using type = typename C::iterator; };
template <typename C>
struct iterator<C const> { using type = typename C::const_iterator; };
And then defined a (very minimal) ForwardRange concept:
template <typename C>
class ForwardRange {
using Iter = typename iterator<C>::type;
public:
using pointer = typename std::iterator_traits<Iter>::pointer;
using reference = typename std::iterator_traits<Iter>::reference;
using value_type = typename std::iterator_traits<Iter>::value_type;
ForwardRange(): _begin(), _end() {}
explicit ForwardRange(C& c): _begin(begin(c)), _end(end(c)) {}
// Observers
explicit operator bool() const { return _begin != _end; }
reference operator*() const { assert(*this); return *_begin; }
pointer operator->() const { assert(*this); return &*_begin; }
// Modifiers
ForwardRange& operator++() { assert(*this); ++_begin; return *this; }
ForwardRange operator++(int) { ForwardRange tmp(*this); ++*this; return tmp; }
private:
Iter _begin;
Iter _end;
}; // class ForwardRange
This is our building brick here, though in fact we could make do with just the rest:
template <typename C, size_t N>
class FlattenedForwardRange {
using Iter = typename iterator<C>::type;
using Inner = FlattenedForwardRange<typename std::iterator_traits<Iter>::value_type, N-1>;
public:
using pointer = typename Inner::pointer;
using reference = typename Inner::reference;
using value_type = typename Inner::value_type;
FlattenedForwardRange(): _outer(), _inner() {}
explicit FlattenedForwardRange(C& outer): _outer(outer), _inner() {
if (not _outer) { return; }
_inner = Inner{*_outer};
this->advance();
}
// Observers
explicit operator bool() const { return static_cast<bool>(_outer); }
reference operator*() const { assert(*this); return *_inner; }
pointer operator->() const { assert(*this); return _inner.operator->(); }
// Modifiers
FlattenedForwardRange& operator++() { ++_inner; this->advance(); return *this; }
FlattenedForwardRange operator++(int) { FlattenedForwardRange tmp(*this); ++*this; return tmp; }
private:
void advance() {
if (_inner) { return; }
for (++_outer; _outer; ++_outer) {
_inner = Inner{*_outer};
if (_inner) { return; }
}
_inner = Inner{};
}
ForwardRange<C> _outer;
Inner _inner;
}; // class FlattenedForwardRange
template <typename C>
class FlattenedForwardRange<C, 0> {
using Iter = typename iterator<C>::type;
public:
using pointer = typename std::iterator_traits<Iter>::pointer;
using reference = typename std::iterator_traits<Iter>::reference;
using value_type = typename std::iterator_traits<Iter>::value_type;
FlattenedForwardRange(): _range() {}
explicit FlattenedForwardRange(C& c): _range(c) {}
// Observers
explicit operator bool() const { return static_cast<bool>(_range); }
reference operator*() const { return *_range; }
pointer operator->() const { return _range.operator->(); }
// Modifiers
FlattenedForwardRange& operator++() { ++_range; return *this; }
FlattenedForwardRange operator++(int) { FlattenedForwardRange tmp(*this); ++*this; return tmp; }
private:
ForwardRange<C> _range;
}; // class FlattenedForwardRange
And apparently, it works
I arrive a little late here, but I have just published a library (multidim) to deal with such problem. The usage is quite simple: to use your example,
#include "multidim.hpp"
// ... create "s" as in your example ...
auto view = multidim::makeFlatView(s);
// view offers now a flattened view on s
// You can now use iterators...
for (auto it = begin(view); it != end(view); ++it) cout << *it << endl;
// or a simple range-for loop
for (auto value : view) cout << value;
The library is header-only and has no dependencies. Requires C++11 though.
you can make one using iterator facade in boost.
I wrote iterator product which you can use as a template perhaps:
http://code.google.com/p/asadchev/source/browse/trunk/work/cxx/iterator/product.hpp
In addition to the answer of Matthieu, you can automatically count the amount of dimensions of the iterable/container. But first we must set up a rule when something is an iterable/container:
template<class T, class R = void>
struct AliasWrapper {
using Type = R;
};
template<class T, class Enable = void>
struct HasValueType : std::false_type {};
template<class T>
struct HasValueType<T, typename AliasWrapper<typename T::value_type>::Type> : std::true_type {};
template<class T, class Enable = void>
struct HasConstIterator : std::false_type {};
template<class T>
struct HasConstIterator<T, typename AliasWrapper<typename T::const_iterator>::Type> : std::true_type {};
template<class T, class Enable = void>
struct HasIterator : std::false_type {};
template<class T>
struct HasIterator<T, typename AliasWrapper<typename T::iterator>::Type> : std::true_type {};
template<class T>
struct IsIterable {
static constexpr bool value = HasValueType<T>::value && HasConstIterator<T>::value && HasIterator<T>::value;
};
We can count the dimensions as follows:
template<class T, bool IsCont>
struct CountDimsHelper;
template<class T>
struct CountDimsHelper<T, true> {
using Inner = typename std::decay_t<T>::value_type;
static constexpr int value = 1 + CountDimsHelper<Inner, IsIterable<Inner>::value>::value;
};
template<class T>
struct CountDimsHelper<T, false> {
static constexpr int value = 0;
};
template<class T>
struct CountDims {
using Decayed = std::decay_t<T>;
static constexpr int value = CountDimsHelper<Decayed, IsIterable<Decayed>::value>::value;
};
We then can create a view wrapper, that contains a begin() and end() function.
template<class Iterable, int Dims>
class Flatten {
public:
using iterator = FlattenIterator<Iterable, Dims>;
private:
iterator _begin{};
iterator _end{};
public:
Flatten() = default;
template<class I>
explicit Flatten(I&& iterable) :
_begin(iterable),
_end(iterable)
{}
iterator begin() const {
return _begin;
}
iterator end() const {
return _end;
}
};
To make the creation of the object Flatten a bit easier, we define a helper function:
template<class Iterable>
Flatten<std::decay_t<Iterable>, CountDims<Iterable>::value - 1> flatten(Iterable&& iterable) {
return Flatten<std::decay_t<Iterable>, CountDims<Iterable>::value - 1>(iterable);
}
Usage:
std::vector<std::vector<int>> vecs = {{1,2,3}, {}, {4,5,6}};
for (int i : flatten(vecs)) {
// do something with i
}