I have a pointer-like struct that goes in the place of a pointer.
The difference with a pointer is that it has extra information that the (also special) allocator can use to deallocate the memory.
This pointer-like structure works well for all basic uses.
I can allocate and deallocate memory, dereferrence, increment,->, etc.
Now I want to use this pointers to be managed by a STL-like container.
Early on, I realized that STL vector basically cannot handle non-raw pointers.
T* is too hard coded, and the standard basically rules out anything that is not a pointer.
Inspired by Boost.Interprocess' offset_ptr<T> I decided to use Boost.Container vector, which is very customizable and in principle can manage anything, the allocator passed to the boost::container::vector can handle anything that is pointer-like.
Now the class boost::container::vector<T, myallocator_with_special_pointer<T>> can do anything... except resize()!!
Looking at the code in boost/container/vector.hpp it seems that the process of resizing (which is basically and allocation, followed by a copy (or move) and deallocation) involves raw pointers.
The offending line is:
[line 2729:] T * const new_buf = container_detail::to_raw_pointer
(allocator_traits_type::allocate(this->m_holder.alloc(), new_cap, this->m_holder.m_start));
Which is later followed by
[line 3022:] this->m_holder.start(new_start); // new_start is the same as new_buf above.
// member ::start(pointer&) will need to convert a raw pointer to the pointer typedef.
Both lines absolutely kill the possibility of using anything that is not a raw_pointer. Even if I have a conversion operator to a raw pointer, other information about the special pointer will be lost.
It seems pretty silly that this small detail forbids the use of non-raw pointers. Given all the effort for the container to be general (e.g. defining the pointer typedef), why this portion of the code uses T* just for resizing?
In other words, why Boost Container doesn't use this line instead
[alternative] pointer const new_buf =
allocator_traits_type::allocate(this->m_holder.alloc(), new_cap, this->m_holder.m_start);
Is there a workaround or an alternative way to use Boost Container vector to handle non-raw pointers?
Boost.Container says in its manual page http://www.boost.org/doc/libs/1_64_0/doc/html/container/history_and_reasons.html#container.history_and_reasons.Why_boost_container
Boost.Container is a product of a long development effort that started
in 2004 with the experimental Shmem library, which pioneered the use
of standard containers in shared memory. Shmem included modified SGI
STL container code tweaked to support non-raw allocator::pointer types
and stateful allocators. Once reviewed, Shmem was accepted as
Boost.Interprocess and this library continued to refine and improve
those containers.
The current implementation (in the context of resize) goes against this design goal.
I asked a less specific question here, about other traits of the allocators: Is it still possible to customize STL vector's "reference" type?
For reference the allocator that specifies the special pointer (which is propagated to the container) is something like this,
template<class T>
struct allocator{
using value_type = T;
using pointer = array_ptr<T>; // simulates T*
using const_pointer = array_ptr<T const>; // simulates T const*
using void_pointer = array_ptr<void>; // simulates void*
using const_void_pointer = array_ptr<void const>; // simulates void const*
some_managed_shared_memory& msm_;
allocator(some_managed_shared_memory& msm) : msm_(msm){}
array_ptr<T> allocate(mpi3::size_t n){
auto ret = msm_.allocate(n*sizeof(T));
return static_cast<array_ptr<T>>(ret);
}
void deallocate(array_ptr<T> ptr, mpi3::size_t = 0){
msm_.deallocate(ptr);
}
};
Full working code http://coliru.stacked-crooked.com/a/f43b6096f9464cbf
#include<iostream>
#include <boost/container/vector.hpp>
template<typename T>
struct array_ptr;
template<>
struct array_ptr<void> {
using T = void;
T* p;
int i; //some additional information
// T& operator*() const { return *p; }
T* operator->() const { return p; }
// operator T*() const { return p; }
template<class TT>
operator array_ptr<TT>() const{return array_ptr<TT>((TT*)p, i);}
operator bool() const{return p;}
array_ptr(){}
array_ptr(std::nullptr_t) : p(nullptr){}
array_ptr(T* ptr, int _i) : p(ptr), i(_i){}
template<class Other>
array_ptr(array_ptr<Other> other) : p(other.p), i(other.i){}
};
template<>
struct array_ptr<void const> {
using T = void const;
T* p;
int i; //some additional information
// T& operator*() const { return *p; }
T* operator->() const { return p; }
operator T*() const { return p; }
array_ptr(){}
array_ptr(std::nullptr_t) : p(nullptr){}
array_ptr(T* ptr, int _i) : p(ptr), i(_i){}
template<class Other>
array_ptr(array_ptr<Other> other) : p(other.p), i(other.i){}
};
template<typename T>
struct array_ptr {
T* p;
int i; //some additional information
T& operator*() const { return *p; }
T* operator->() const { return p; }
T& operator[](std::size_t n) const{
assert(i == 99);
return *(p + n);
}
bool operator==(array_ptr const& other) const{return p == other.p and i == other.i;}
bool operator!=(array_ptr const& other) const{return not((*this)==other);}
// operator T*() const { return p; }
array_ptr& operator++(){++p; return *this;}
array_ptr& operator+=(std::ptrdiff_t n){p+=n; return *this;}
array_ptr& operator-=(std::ptrdiff_t n){p-=n; return *this;}
array_ptr operator+(std::size_t n) const{array_ptr ret(*this); ret+=n; return ret;}
std::ptrdiff_t operator-(array_ptr const& other) const{return p - other.p;}
array_ptr(){}
array_ptr(std::nullptr_t) : p(nullptr), i(0){}
operator bool() const{return p;}
array_ptr(T* ptr, int _i) : p(ptr), i(_i){}
array_ptr(T* ptr) : p(ptr), i(0){}
array_ptr(int) : p(nullptr), i(0){}
array_ptr(array_ptr<void> const& other) : p(static_cast<T*>(other.p)), i(other.i){}
};
struct some_managed_shared_memory {
array_ptr<void> allocate(size_t n) { return array_ptr<void>(::malloc(n), 99); }
void deallocate(array_ptr<void> ptr) { if (ptr) ::free(ptr.p); }
};
template<typename T>
struct allocator{
using value_type = T;
using pointer = array_ptr<T>; // simulates T*
using const_pointer = array_ptr<T const>; // simulates T const*
using void_pointer = array_ptr<void>; // simulates void*
using const_void_pointer = array_ptr<void const>; // simulates void const*
some_managed_shared_memory& msm_;
allocator(some_managed_shared_memory& msm) : msm_(msm){}
array_ptr<T> allocate(size_t n){
auto ret = msm_.allocate(n*sizeof(T));
return static_cast<array_ptr<T>>(ret);
}
void deallocate(array_ptr<T> ptr, std::size_t = 0){
msm_.deallocate(ptr);
}
};
int main() {
some_managed_shared_memory realm;
boost::container::vector<int, allocator<int> > v(10, realm);
assert( v[4] == 0 );
v[4] = 1;
assert( v[4] == 1 );
for(std::size_t i = 0; i != v.size(); ++i) std::cout << v[i] << std::endl;
for(auto it = v.begin(); it != v.end(); ++it) std::cout << *it << std::endl;
// none of these compile:
v.push_back(8);
assert(v.size() == 11);
v.resize(100);
std::cout << v[89] << std::endl; // will fail an assert because the allocator information is lost
//v.assign({1,2,3,4,5});
}
I looked into things.
The TL;DR seems to be: non-raw pointers are supported, but they need a implicit conversion from raw in some operations. Whether or not this is by design, I don't know, but it doesn't seem to contradict the design goal.
In fact this is very analogous to the history of allocator support: STL containers had support for custom allocators, but not for stateful allocators (meaning, non-default-constructible allocator types).
Allocator Versions
At first I tried some of the allocator versions:
using version = boost::container::version_0; // seems unsupported, really
using version = boost::container::version_1;
using version = boost::container::version_2; // does different operations
But it had no (decisive) effect. Maybe the documentation has clues.
Pointer Arithmetic
After that I looked into the specific errors. Looking at the cited line/error it dawned on me that the raw-pointer might have been an accident. Looking at the output of these:
std::cout << boost::container::container_detail::impl::version<allocator<int> >::value << "\n";
array_ptr<int> p;
auto rawp = boost::container::container_detail::to_raw_pointer(p);
std::cout << typeid(rawp).name() << "\n";
std::cout << typeid(p).name() << "\n";
std::cout << typeid(p + 5).name() << "\n";
std::cout << typeid(p - 5).name() << "\n";
Shows something like¹
1
int*
array_ptr<int>
int*
int*
¹ prettified with the help of c++filt -t
This lead me to define pointer arithmetic:
template <typename T, typename N>
array_ptr<T> operator+(array_ptr<T> const& p, N n) { return array_ptr<T>(p.p+n, p.i); }
template <typename T>
array_ptr<T>& operator++(array_ptr<T>& p) { return ++p.p, p; }
template <typename T>
array_ptr<T> operator++(array_ptr<T>& p, int) { auto q = p.p++; return array_ptr<T>(q, p.i); }
template <typename T, typename N>
array_ptr<T> operator-(array_ptr<T> const& p, N n) { return array_ptr<T>(p.p-n, p.i); }
template <typename T>
ptrdiff_t operator-(array_ptr<T> const& a, array_ptr<T> const& b) { return a.p - b.p; }
Now the output becomes
1
int*
array_ptr<int>
array_ptr<int>
array_ptr<int>
Many more use cases compile successfully with these definitions. Assuming that the "annotation" data inside the array_pointer is valid after increment, it should not lose any allocator information
The Real Culprit
With that out of the way, some things still don't compile. Specifically, in some spots the allocator's pointer type is constructed back from a raw-pointer. This fails because there's no suitable "default" conversion constructor. If you declare the constructors with the data value optional, everything compiles, but you could argue that this loses information as there is a path from
array_pointer<T> p;
auto* rawp = to_raw_pointer(p);
array_pointer<T> clone(rawp); // oops lost the extra info in p
OBSERVATION
Note that, as you apparently realized (judging from the commented operators), adding the default constructor argument removes the need for the arithmetic operations (except pre-increment).
However, adding them makes sure that the lossy conversion path is taken less often, which could be important to your use case.
DEMO TIME
Live On Coliru
#if COMPILATION_INSTRUCTIONS
clang++ -std=c++14 -Wall -Wfatal-errors $0 -o $0x.x && $0x.x $# && rm -f $0x.x; exit
#endif
#define DEFAULT_DATA = 0
#define DEFINE_ARITHMETIC_OPERATIONS
#include <iostream>
#include <boost/container/vector.hpp>
#include <typeinfo>
template<typename T>
struct array_ptr {
T* p;
int i; //some additional information
T& operator*() const { return *p; }
T* operator->() const { return p; }
operator T*() const { return p; }
array_ptr(){}
//array_ptr(std::nullptr_t) : p(nullptr), i(0){}
array_ptr(T* ptr, int _i DEFAULT_DATA) : p(ptr), i(_i){}
};
template<>
struct array_ptr<void> {
using T = void;
T* p;
int i; //some additional information
// T& operator*() const { return *p; }
T* operator->() const { return p; }
operator T*() const { return p; }
template<class T>
operator array_ptr<T>() const{return array_ptr<T>((T*)p, i);}
// array_ptr& operator++(){++p; return *this;}
array_ptr(){}
array_ptr(std::nullptr_t) : p(nullptr){}
array_ptr(T* ptr, int _i DEFAULT_DATA) : p(ptr), i(_i){}
template<class Other>
array_ptr(array_ptr<Other> other) : p(other.p), i(other.i){}
};
template<>
struct array_ptr<void const> {
using T = void const;
T* p;
int i; //some additional information
// T& operator*() const { return *p; }
T* operator->() const { return p; }
operator T*() const { return p; }
// array_ptr& operator++(){++p; return *this;}
// template<class Other> array_ptr(array_ptr<Other> const& other) : p(other.p), i(other.i){}
array_ptr(){}
array_ptr(std::nullptr_t) : p(nullptr){}
array_ptr(T* ptr, int _i DEFAULT_DATA) : p(ptr), i(_i){}
template<class Other>
array_ptr(array_ptr<Other> other) : p(other.p), i(other.i){}
};
struct some_managed_shared_memory {
array_ptr<void> allocate(size_t n) { return array_ptr<void>(::malloc(n), 99); }
void deallocate(array_ptr<void> ptr) { if (ptr) ::free(ptr.p); }
};
template<typename T>
struct allocator{
using version = boost::container::version_1;
using value_type = T;
using pointer = array_ptr<T>; // simulates T*
using const_pointer = array_ptr<T const>; // simulates T const*
using void_pointer = array_ptr<void>; // simulates void*
using const_void_pointer = array_ptr<void const>; // simulates void const*
some_managed_shared_memory& msm_;
allocator(some_managed_shared_memory& msm) : msm_(msm){}
array_ptr<T> allocate(size_t n){
auto ret = msm_.allocate(n*sizeof(T));
return static_cast<array_ptr<T>>(ret);
}
void deallocate(array_ptr<T> ptr, std::size_t = 0){
msm_.deallocate(ptr);
}
};
#ifdef DEFINE_ARITHMETIC_OPERATIONS
template <typename T, typename N>
array_ptr<T> operator+(array_ptr<T> const& p, N n) { return array_ptr<T>(p.p+n, p.i); }
template <typename T>
array_ptr<T>& operator++(array_ptr<T>& p) { return ++p.p, p; }
template <typename T>
array_ptr<T> operator++(array_ptr<T>& p, int) { auto q = p.p++; return array_ptr<T>(q, p.i); }
template <typename T, typename N>
array_ptr<T> operator-(array_ptr<T> const& p, N n) { return array_ptr<T>(p.p-n, p.i); }
template <typename T>
ptrdiff_t operator-(array_ptr<T> const& a, array_ptr<T> const& b) { return a.p - b.p; }
#endif
int main() {
std::cout << boost::container::container_detail::impl::version<allocator<int> >::value << "\n";
if (1) { // some diagnostics
array_ptr<int> p;
auto rawp = boost::container::container_detail::to_raw_pointer(p);
std::cout << typeid(rawp).name() << "\n";
std::cout << typeid(p).name() << "\n";
std::cout << typeid(p + 5).name() << "\n";
std::cout << typeid(p - 5).name() << "\n";
}
some_managed_shared_memory realm;
boost::container::vector<int, allocator<int> > v(10, realm);
assert( v[4] == 0 );
v[4] = 1;
assert( v[4] == 1 );
for(std::size_t i = 0; i != v.size(); ++i) std::cout << v[i] << std::endl;
// these compile:
v.push_back(12);
v.resize(100);
v.assign({1,2,3,4,5});
}
Prints
1
Pi
9array_ptrIiE
9array_ptrIiE
9array_ptrIiE
0
0
0
0
1
0
0
0
0
0
Related
I am trying to wrap my head around expression templates. In the wikipedia article, an example is given, where an expression template VecSum stores const references to its two operands. A Vec is an expression template that wraps an std::vector<double>. I will first pose my question and then give a complete rundown of the example below.
Can I re-use expressions that use const references to temporaries? And if not, how would I implement light-weight, re-useable expression templates?
For three Vecs a, b, and c the expression a+b+c is of type
VecSum<VecSum<Vec, Vec>, Vec>
If I understand correctly, the inner VecSum is a temporary and the outer VecSum stores a const reference to the inner VecSum. I believe the lifetime of the inner VecSum temporary is guaranteed until the expression a+b+c gets evaluated. Correct? Does this mean that the expression cannot be reused without the danger of creating dangling references?
auto expr = a + b + c;
Vec v1 = expr; // ok
Vec v2 = expr; // not ok!
If so, how can this example be modified, so that
the expressions are reusable
the expressions do not store copies of their operands (at least in situations where it is not necessary)?
Full code example
For completeness - and in case the wikipedia article is updated in the meantime, let me repeat the example code here and give an example in the main that I believe creates a dangling reference.
#include <cassert>
#include <vector>
template <typename E>
class VecExpression {
public:
double operator[](size_t i) const
{
// Delegation to the actual expression type. This avoids dynamic polymorphism (a.k.a. virtual functions in C++)
return static_cast<E const&>(*this)[i];
}
size_t size() const { return static_cast<E const&>(*this).size(); }
};
class Vec : public VecExpression<Vec> {
std::vector<double> elems;
public:
double operator[](size_t i) const { return elems[i]; }
double &operator[](size_t i) { return elems[i]; }
size_t size() const { return elems.size(); }
Vec(size_t n) : elems(n) {}
// construct vector using initializer list
Vec(std::initializer_list<double> init) : elems(init) {}
// A Vec can be constructed from any VecExpression, forcing its evaluation.
template <typename E>
Vec(VecExpression<E> const& expr) : elems(expr.size()) {
for (size_t i = 0; i != expr.size(); ++i) {
elems[i] = expr[i];
}
}
};
template <typename E1, typename E2>
class VecSum : public VecExpression<VecSum<E1, E2> > {
E1 const& _u;
E2 const& _v;
public:
VecSum(E1 const& u, E2 const& v) : _u(u), _v(v) {
assert(u.size() == v.size());
}
double operator[](size_t i) const { return _u[i] + _v[i]; }
size_t size() const { return _v.size(); }
};
template <typename E1, typename E2>
VecSum<E1, E2>
operator+(VecExpression<E1> const& u, VecExpression<E2> const& v) {
return VecSum<E1, E2>(*static_cast<const E1*>(&u), *static_cast<const E2*>(&v));
}
int main() {
Vec v0 = {23.4,12.5,144.56,90.56};
Vec v1 = {67.12,34.8,90.34,89.30};
Vec v2 = {34.90,111.9,45.12,90.5};
auto expr = v0 + v1 + v2;
Vec v1 = expr; // ok
Vec v2 = expr; // not ok!
}
Edit:
I just realized this might be a duplicate of this question. However the answers to both questions are very different and all usefull.
The comment above has a very effective way to check the problem with the dangling reference. Note that if you try to print the values from the main function in your example the program will still work because the object that will have the dangling reference bound to it will be created also on the stack space of main. I tried to move the code which is assigned to expr inside a function and the program crashed as expected (the temporary object will be in another stack frame):
auto makeExpr1(Vec const& v0, Vec const& v1, Vec const& v2) {
return v0 + v1 + v2;
}
// ... in main:
auto expr = makeExpr1(v0, v1, v2);
The problem you highlighted here appears in the cases of creating an expression that can be lazily evaluated in languages like C++. A somehow similar situation can occur in the context of range expressions (C++20 ranges).
Below is my quick attempt to fix that code and make it work with lvalues and rvalues added with the operator + (I apologise for the ugly parts and possible mistakes). This will store copy of their operands only when they are going to be out of scope and will result in dangling references in the old code.
Regarding re-usability: as long as you define a type for every operation and a corresponding operator '?' function ('?' being the simbol of the operation) this approch should give you a starting point for any binary operation on such a vector.
#include <cassert>
#include <vector>
#include <utility>
#include <iostream>
/*
* Passes lvalues and stores rvalues
*/
template <typename T> class Wrapper;
template <typename T> class Wrapper<T&> {
private:
T& ref;
public:
Wrapper(T& ref) : ref(ref) {}
T& get() { return ref; }
const T& get() const { return ref; }
};
template <typename T> class Wrapper<T&&> {
private:
T value;
public:
Wrapper(T&& ref) : value(std::move(ref)) {}
T& get() { return value; }
const T& get() const { return value; }
};
template <typename E>
class VecExpression {
public:
double operator[](size_t i) const
{
// Delegation to the actual expression type. This avoids dynamic polymorphism (a.k.a. virtual functions in C++)
return static_cast<E const&>(*this)[i];
}
size_t size() const { return static_cast<E const&>(*this).size(); }
};
/*
* Forwards the reference and const qualifiers
* of the expression type to the expression itself
*/
template <typename E> constexpr E& forwardRef(VecExpression<E>& ve) {
return static_cast<E&>(ve);
}
template <typename E> constexpr const E& forwardRef(const VecExpression<E>& ve) {
return static_cast<const E&>(ve);
}
template <typename E> constexpr E&& forwardRef(VecExpression<E>&& ve) {
return static_cast<E&&>(ve);
}
class Vec : public VecExpression<Vec> {
std::vector<double> elems;
public:
double operator[](size_t i) const { return elems[i]; }
double &operator[](size_t i) { return elems[i]; }
size_t size() const { return elems.size(); }
Vec(size_t n) : elems(n) {}
// construct vector using initializer list
Vec(std::initializer_list<double> init) : elems(init) {}
// A Vec can be constructed from any VecExpression, forcing its evaluation.
template <typename E>
Vec(VecExpression<E> const& expr) : elems(expr.size()) {
std::cout << "Expr ctor\n"; // Very quick test
for (size_t i = 0; i != expr.size(); ++i) {
elems[i] = expr[i];
}
}
// Move ctor added for checking
Vec(Vec&& vec) : elems(std::move(vec.elems)) {
std::cout << "Move ctor\n"; // Very quick test
}
};
/*
* Now VecSum is a sum between possibly const - qualified
* and referenced expression types
*/
template <typename E1, typename E2>
class VecSum : public VecExpression<VecSum<E1, E2>> {
Wrapper<E1> _u;
Wrapper<E2> _v;
public:
VecSum(E1 u, E2 v) : _u(static_cast<E1>(u)), _v(static_cast<E2>(v)) {
assert(_u.get().size() == _v.get().size());
}
double operator[](size_t i) const { return _u.get()[i] + _v.get()[i]; }
size_t size() const { return _v.get().size(); }
};
/*
* Used to create a VecSum by capturing also the reference kind
* of the arguments (will be used by the Wrapper inside VecSum)
*/
template <typename E1, typename E2>
auto makeVecSum(E1&& e1, E2&& e2) {
return VecSum<E1&&, E2&&>(std::forward<E1>(e1), std::forward<E2>(e2));
}
/*
* Now the operator+ takes the vector expressions by universal references
*/
template <typename VE1, typename VE2>
auto operator+(VE1&& ve1, VE2&& ve2) {
return makeVecSum(forwardRef(std::forward<VE1>(ve1)), forwardRef(std::forward<VE2>(ve2)));
}
// Now this will work
auto makeExpr1(Vec const& v0, Vec const& v1, Vec const& v2) {
return v0 + v1 + v2;
}
// This will also work - the rvalue is stored in the
// expression itself and both will have the same lifetime
auto makeExpr2(Vec const& v0, Vec const& v1) {
return v0 + v1 + Vec({1.0, 1.0, 1.0, 1.0});
}
int main() {
Vec v0 = {23.4,12.5,144.56,90.56};
Vec v1 = {67.12,34.8,90.34,89.30};
Vec v2 = {34.90,111.9,45.12,90.5};
auto expr = makeExpr1(v0, v1, v2);
Vec v1_ = expr;
Vec v2_ = expr;
auto expr_ = makeExpr2(v0, v1);
for (size_t i = 0; i < v1_.size(); ++i)
std::cout << v1_[i] << " ";
std::cout << std::endl;
for (size_t i = 0; i < v2_.size(); ++i)
std::cout << v2_[i] << " ";
std::cout << std::endl;
for (size_t i = 0; i < expr.size(); ++i)
std::cout << expr[i] << " ";
std::cout << std::endl;
for (size_t i = 0; i < expr_.size(); ++i)
std::cout << expr_[i] << " ";
std::cout << std::endl;
}
I'm trying to find a [better] way to run/check a potentially unsafe expression or perform multiple null checks in a more elegant way.
Here is an example of codes I would like to improve:
if (myObjectPointer &&
myObjectPointer->getSubObject() &&
myObjectPointer->getSubObject()->getSubSubObject() &&
myObjectPointer->getSubObject()->getSubSubObject()->getTarget()) {
// Use safely target
... *(myObjectPointer->getSubObject()->getSubSubObject()->getTarget()) ...
}
I tried to find a more elegant way to achieve this (instead of the above verbose null checks). Here is my first thoughts:
template<typename T>
bool isSafe(T && function) {
try {
function();
// Just running the func above, but we could e.g. think about returning the actual value instead of true/fase - not that important.
return true;
}
catch (...) {
return false;
}
}
...
// And use the above as follow :
if(isSafe([&](){ myObjectPointer->getSubObject()->getSubSubObject()->getTarget(); })) {
// Use safely target
}
...
The problem with the above is that we can't catch signals (Segmentation fault, ...). And I obviously don't want to handle all signals in the program, but only in this very specific check/eval function.
I'm I tackling the problem the wrong way ? Any other recommendations ? or the verbose if is inevitable ?
Many thanks in advance.
I was thinking about this, and like Jarod42 said, there must be some variadic template stuff. I'm not the best at this, but came up with this:
#include <memory>
#include <functional>
#include <iostream>
template <typename T, typename MemFn, typename... Params>
void safeExecute(T* ptr, MemFn memFn, Params&&... params) {
if (ptr != nullptr)
safeExecute(std::invoke(memFn, ptr), std::forward<Params>(params)...);
}
template <typename T, typename MemFn>
void safeExecute(T* ptr, MemFn memFn) {
if (ptr != nullptr) std::invoke(memFn, ptr);
}
struct Target {
void Bar() { std::cout << "tada!\n"; };
};
template<typename T>
class Object {
private:
std::unique_ptr<T> ptr;
public:
Object() : ptr(std::make_unique<T>()) {}
T* Get() { return ptr.get(); }
};
using SubSubObject = Object<Target>;
using SubObject = Object<SubSubObject>;
using MyObject = Object<SubObject>;
int main() {
auto myObjectPtr = std::make_unique<MyObject>();
safeExecute(myObjectPtr.get(),
&MyObject::Get,
&SubObject::Get,
&SubSubObject::Get,
&Target::Bar);
}
edit:
I've been playing with the idea of having a more general return type, so I experimented with the option not to call the member function, but to return an std::optional pointer to the object. This lead me to the following code:
#include <memory>
#include <functional>
#include <iostream>
#include <optional>
template <typename T, typename MemFn, typename... Params>
auto safeGetObject(T* ptr, MemFn memFn, Params&&... params)
-> decltype(safeGetObject(std::invoke(memFn, std::declval<T>()), std::forward<Params>(params)...))
{
if (ptr != nullptr) return safeGetObject(std::invoke(memFn, ptr), std::forward<Params>(params)...);
return {};
}
template <typename T, typename MemFn>
auto safeGetObject(T* ptr, MemFn memFn) -> std::optional<decltype(std::invoke(memFn, std::declval<T>()))> {
if (ptr != nullptr) return std::invoke(memFn, ptr);
return {};
}
struct Target {
int Bar(int a, int b) const noexcept {
return a+b;
};
};
template<typename T>
class Object {
private:
std::unique_ptr<T> ptr;
public:
Object() noexcept : ptr(std::make_unique<T>()) {}
T* Get() const noexcept { return ptr.get(); }
};
using SubSubObject = Object<Target>;
using SubObject = Object<SubSubObject>;
using MyObject = Object<SubObject>;
int main() {
auto myObjectPtr = std::make_unique<MyObject>();
auto optionalTarget = safeGetObject(
myObjectPtr.get(),
&MyObject::Get,
&SubObject::Get,
&SubSubObject::Get);
auto result = optionalTarget ? optionalTarget.value()->Bar(3, 4) : -1;
std::cout << " result " << result << '\n';
}
Putting possible design issues aside, you could use an extended version of std::optional. Since not all intefaces are under your control, you would have to wrap the functions were necessary into a free-function. Let's assume you can change the class MyClass of myObjectPointer, but not the classes of the sub-objects.
class MyClass {
public:
optional<std::reference_wrapper<SubObjectClass>> getSubObject();
};
optional<std::reference_wrapper<SubSubObjectClass>> getSubSubObject(SubObjectClass& s) {
SubSubObjectClass* ptr = s.getSubSubObject();
if (ptr) {
return std::ref(s.getSubSubObject());
} else {
return {};
}
}
optional<std::reference_wrapper<Target>> getTarget(SubSubObjectCLass& s) {
...
}
You can now write something like
optional<MyClass*> myObjectPointer = ...;
myObjectPointer.and_then(MyClass::getSubObject)
.and_then(getSubSubObject)
.and_then(getTarget)
.map( doSomethingWithTarget ):
OK, I might delete my previous answer, because I've been rethinking this, now considering using std::optional and chaining.
Your original
myObjectPointer->getSubObject()->getSubSubObject()->getTarget()
is not really reproducible, since operator->() cannot be static. But we can use another operator, like operator>>(). Thus:
#include <memory>
#include <iostream>
#include <optional>
#include <functional>
struct Target {
int Bar(int a, int b) const noexcept { return a+b; };
};
template<typename T>
class Object {
private:
T* const ptr;
public:
Object(T* ptr) noexcept : ptr(ptr) {}
T* Get() const noexcept { return ptr; }
};
using SubSubObject = Object<Target>;
using SubObject = Object<SubSubObject>;
using MyObject = Object<SubObject>;
template <typename T>
auto makeOptional(T* ptr) -> std::optional< std::reference_wrapper<T>> {
if (ptr) return std::ref(*ptr);
return {};
}
template <typename T, typename MemFn>
auto operator>> (std::optional<std::reference_wrapper<T>> optObj, MemFn memFn)
-> std::optional< std::reference_wrapper<std::remove_pointer_t<decltype(std::invoke(memFn, std::declval<T>()))>>> {
if (optObj) return makeOptional(std::invoke(memFn, *optObj));
return {};
}
int main() {
{
//complete
auto TargetPtr = std::make_unique<Target>();
auto subSubObjectPtr = std::make_unique<SubSubObject>(TargetPtr.get());
auto subObjectPtr = std::make_unique<SubObject>(subSubObjectPtr.get());
auto myObjectPtr = std::make_unique<MyObject>(subObjectPtr.get());
auto optionalMyObject = makeOptional(myObjectPtr.get());
auto optionalTarget = optionalMyObject >> &MyObject::Get >> &SubObject::Get >> &SubSubObject::Get;
auto result = (optionalTarget) ? optionalTarget->get().Bar(3, 4) : -1;
std::cout << "result is " << result << '\n';
}
{
// incomplete
auto subObjectPtr = std::make_unique<SubObject>(nullptr);
auto myObjectPtr = std::make_unique<MyObject>(subObjectPtr.get());
auto optionalMyObject = makeOptional(myObjectPtr.get());
auto optionalTarget = optionalMyObject >> &MyObject::Get >> &SubObject::Get >> &SubSubObject::Get;
auto result = (optionalTarget) ? optionalTarget->get().Bar(3, 4) : -1;
std::cout << "result is " << result << '\n';
}
}
will work...
Let me know if this is what you're looking for.
edit:
I've also tried putting it in a wrapper class
#include <memory>
#include <iostream>
#include <functional>
#include <optional>
struct Target {
constexpr int Bar(int a, int b) const noexcept { return a + b; };
};
template<typename T>
class Object {
private:
T* const ptr;
public:
constexpr Object(T* const ptr) noexcept : ptr(ptr) {}
constexpr T* Get() const noexcept { return ptr; }
};
using SubSubObject = Object<Target>;
using SubObject = Object<SubSubObject>;
using MyObject = Object<SubObject>;
template<typename T>
class ObjectWrapper {
private:
std::optional<std::reference_wrapper<T>> optRefObj{};
public:
constexpr ObjectWrapper(T* ptr) noexcept
: optRefObj(ptr ? std::make_optional(std::ref(*ptr)) : std::nullopt)
{}
template<typename MemFn>
constexpr auto operator>>(MemFn memFn) const noexcept {
return ObjectWrapper<std::remove_pointer_t<decltype(std::invoke(memFn, std::declval<T>()))>>
(optRefObj ? std::invoke(memFn, *optRefObj) : nullptr);
}
constexpr operator bool() const noexcept { return optRefObj.has_value(); }
constexpr T* Get() noexcept { return optRefObj ? &optRefObj->get() : nullptr; }
};
int main() {
{
//complete
auto const TargetPtr = std::make_unique<Target>();
auto const subSubObjectPtr = std::make_unique<SubSubObject>(TargetPtr.get());
auto const subObjectPtr = std::make_unique<SubObject>(subSubObjectPtr.get());
auto const myObjectPtr = std::make_unique<MyObject>(subObjectPtr.get());
auto const myObjWrp = ObjectWrapper(myObjectPtr.get());
auto optionalTarget = myObjWrp >> &MyObject::Get >> &SubObject::Get >> &SubSubObject::Get;
auto const result = optionalTarget ? optionalTarget.Get()->Bar(3, 4) : -1;
std::cout << "result is " << result << '\n';
}
{
// incomplete
auto const subObjectPtr = std::make_unique<SubObject>(nullptr);
auto const myObjectPtr = std::make_unique<MyObject>(subObjectPtr.get());
auto const myObjWrp = ObjectWrapper(myObjectPtr.get());
auto optionalTarget = myObjWrp >> &MyObject::Get >> &SubObject::Get >> &SubSubObject::Get;
auto const result = optionalTarget ? optionalTarget.Get()->Bar(3, 4) : -1;
std::cout << "result is " << result << '\n';
}
}
I'm studying c++ templates and reading <<C++ Templates: The Complete Guide>>. I don't understand the flowing about expression template:
The code as following:
//exprarray.h
#include <stddef.h>
#include <cassert>
#include "sarray.h"
template<typename T>
class A_Scale
{
public:
A_Scale(T const& t):value(t){}
T operator[](size_t) const
{
return value;
}
size_t size() const
{
return 0;
}
private:
T const& value;
};
template<typename T>
class A_Traits
{
public:
typedef T const& exprRef;
};
template<typename T>
class A_Traits<A_Scale<T> >
{
public:
typedef A_Scale<T> exprRef;
};
template<typename T,typename L1,typename R2>
class A_Add
{
private:
typename A_Traits<L1>::exprRef op1;
typename A_Traits<R2>::exprRef op2;
public:
A_Add(L1 const& a,R2 const& b):op1(a),op2(b)
{
}
T operator[](size_t indx) const
{
return op1[indx] + op2[indx];
}
size_t size() const
{
assert(op1.size()==0 || op2.size()==0 || op1.size() == op2.size());
return op1.size() != 0 ? op1.size() : op2.size();
}
};
template<typename T,typename L1,typename R2>
class A_Mul
{
private:
typename A_Traits<L1>::exprRef op1;
typename A_Traits<R2>::exprRef op2;
public:
A_Mul(L1 const& a,R2 const& b):op1(a),op2(b)
{
}
T operator[](size_t indx) const
{
return op1[indx] * op2[indx];
}
size_t size() const
{
assert(op1.size()==0 || op2.size()==0 || op1.size() == op2.size());
return op1.size() != 0 ? op1.size():op2.size();
}
};
template<typename T,typename Rep = SArray<T> >
class Array
{
public:
explicit Array(size_t N):expr_Rep(N){}
Array(Rep const& rep):expr_Rep(rep){}
Array& operator=(Array<T> const& orig)
{
assert(size() == orig.size());
for (size_t indx=0;indx < orig.size();indx++)
{
expr_Rep[indx] = orig[indx];
}
return *this;
}
template<typename T2,typename Rep2>
Array& operator=(Array<T2,Rep2> const& orig)
{
assert(size() == orig.size());
for (size_t indx=0;indx<orig.size();indx++)
{
expr_Rep[indx] = orig[indx];
}
return *this;
}
size_t size() const
{
return expr_Rep.size();
}
T operator[](size_t indx) const
{
assert(indx < size());
return expr_Rep[indx];
}
T& operator[](size_t indx)
{
assert(indx < size());
return expr_Rep[indx];
}
Rep const& rep() const
{
return expr_Rep;
}
Rep& rep()
{
return expr_Rep;
}
private:
Rep expr_Rep;
};
template<typename T,typename L1,typename R2>
Array<T,A_Add<T,L1,R2> >
operator+(Array<T,L1> const& a,Array<T,R2> const& b)
{
return Array<T,A_Add<T,L1,R2> >(A_Add<T,L1,R2>(a.rep(),b.rep()));
}
template<typename T,typename L1,typename R2>
Array<T,A_Mul<T,L1,R2> >
operator*(Array<T,L1> const& a,Array<T,R2> const& b)
{
return Array<T,A_Mul<T,L1,R2> >(A_Mul<T,L1,R2>(a.rep(),b.rep()));
}
template<typename T,typename R2>
Array<T,A_Mul<T,A_Scale<T>,R2> >
operator*(T const& a,Array<T,R2> const& b)
{
return Array<T,A_Mul<T,A_Scale<T>,R2> >(A_Mul<T,A_Scale<T>,R2>(A_Scale<T>(a),b.rep()));
}
The test code:
//test.cpp
#include "exprarray.h"
#include <iostream>
using namespace std;
template <typename T>
void print (T const& c)
{
for (int i=0; i<8; ++i) {
std::cout << c[i] << ' ';
}
std::cout << "..." << std::endl;
}
int main()
{
Array<double> x(1000), y(1000);
for (int i=0; i<1000; ++i) {
x[i] = i;
y[i] = x[i]+x[i];
}
std::cout << "x: ";
print(x);
std::cout << "y: ";
print(y);
x = 1.2 * x;
std::cout << "x = 1.2 * x: ";
print(x);
x = 1.2*x + x*y;
std::cout << "1.2*x + x*y: ";
print(x);
x = y;
std::cout << "after x = y: ";
print(x);
return 0;
}
My questions is why A_Traits for A_Scale is by value not by reference.
template<typename T>
class A_Traits
{
public:
typedef T const& exprRef;
};
template<typename T>
class A_Traits<A_Scale<T> >
{
public:
typedef A_Scale<T> exprRef;
};
The reason from the book as following:
This is necessary because of the following: In general, we can declare them to be references because most temporary nodes are bound in the top-level expression and therefore live until the end of the evaluation of that complete expression. The one exception are the A_Scalar nodes. They are bound within the operator functions and might not live until the end of the evaluation of the complete expression. Thus, to avoid that the members refer to scalars that don't exist anymore, for scalars the operands have to get copied "by value."
More detail please refer to the chapter 18 of C++ Templates: The Complete Guide
Consider, for example, the right hand side of
x = 1.2*x + x*y;
What the quote says is that this is composed of two different categories.
The heavy array x and y objects are not defined within this expression, but rather before it:
Array<double> x(1000), y(1000);
So, as you build expressions using them, you don't have to worry whether they're still alive - they were defined beforehand. Since they're heavy, you want to capture them by reference, and, fortunately, their lifetime makes that possible.
Conversely, the lightweight A_Scale objects are generated within the expression (e.g., implicitly by the 1.2 above). Since they're temporaries, you have to worry about their lifetime. Since they're lightweight, it's not a problem.
That's the rationale for the traits class differentiating between them: the former are by reference, and the latter are by value (they are copied).
I'm trying to write a custom allocator, which preallocates space for a fixed number of elements. However, I have some problems with understanding the requirements.
allocator.h
#pragma once
#ifndef _ALLOCATOR_H
#define _ALLOCATOR_H
template<typename T>
class Allocator
{
public:
// typedefs
typedef T value_type;
typedef value_type* pointer;
typedef const value_type* const_pointer;
typedef value_type& reference;
typedef const value_type& const_reference;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
public:
// convert an allocator<T> to allocator<U>
template<typename U>
struct rebind
{
typedef Allocator<U> other;
};
public:
explicit Allocator(void)
{
mCurElement = 0;
mMaxElements = 650000000;
mBase = reinterpret_cast<pointer>(::operator new(mMaxElements * sizeof(T)));
}
virtual ~Allocator(void)
{
::operator delete(mBase);
}
explicit Allocator(Allocator const &oOther)
{
mCurElement = oOther.mCurElement;
mMaxElements = oOther.mMaxElements;
mBase = oOther.mBase;
}
template<typename U>
explicit Allocator(Allocator<U> const &oOther)
{
mCurElement = 0;
mMaxElements = 650000000;
mBase = oOther.mBase;
}
// address
pointer address(reference r) { return &r; }
const_pointer address(const_reference r) { return &r; }
// memory allocation
pointer allocate(size_type nElements, typename std::allocator<void>::const_pointer = 0)
{
if (mCurElement > mMaxElements)
return NULL;
//pointer p = reinterpret_cast<pointer>(::operator new(cnt * sizeof(T)));
pointer p = &mBase[mCurElement];
mCurElement += nElements;
return p;
}
void deallocate(pointer pAddress, size_type)
{
//::operator delete(pAddress);
mCurElement--;
}
// size
size_type max_size() const
{
return std::numeric_limits<size_type>::max() / sizeof(T);
}
// construction/destruction
void construct(pointer pAddress, const T& oObject)
{
new(pAddress) T(oObject);
}
void destroy(pointer pAddress)
{
pAddress->~T();
}
bool operator==(Allocator const&) { return true; }
bool operator!=(Allocator const& oAllocator) { return !operator==(oAllocator); }
public:
T *getBase(void) const { return mBase; }
private:
static usize_t mId;
T *mBase;
usize_t mMaxElements;
usize_t mCurElement;
};
#endif // _ALLOCATOR_H
allocator.cpp
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <sstream>
#include <set>
#include <ctime>
#include "allocator.h"
typedef unsigned int uint_t;
typedef unsigned long long usize_t;
usize_t Allocator<usize_t>::mId;
void testStdAllocator(usize_t nIterations, usize_t nMaxValue)
{
std::set<usize_t, std::less<usize_t>, Allocator<usize_t>> st;
std::string id = "Standard Set";
clock_t start = clock();
for (usize_t i = 0; i < nIterations; i++)
{
usize_t val = (usize_t)(rand() % nMaxValue) + 1;
if (i % 1000000 == 0)
std::cout << id << " testing ... " << i << "/" << nIterations << "\r";
st.insert(val);
}
std::cout << id << " Elapsed: " << clock() - start << std::endl;
}
int main(int argc, char *argv[])
{
usize_t iterations = 650000000;
usize_t val = 6500000;
std::cout << "Allocator" << std::endl;
testStdAllocator(iterations, val);
return 0;
}
The problem I have is:
Why do I need the template <typename U> ...? (I found an example and adpated it)
When I made it compilable and tested it the std::set apparently creates copies of the allocator, so I would have to pass around the pointer. I can use an std::shared_ptr for that, but I don't really see why this should be needed in the first place.
Apparently there is something about proxied containers where the template <typename U> is needed for, but this again creates the additional problem of passing the pointer around for an (apparently) different allocator type.
So I would appreciate some pointers where I'm going wrong.
When you pass an allocator to std::set<T, C A> it is meant to have an allocate() function allcoating space for T objects. However, the std::set<T, C, A> will not allocate any T object. It will, instead, allocate _Node<T> objects where _Node is some tree node representation capable of holding T objects but also containing suitable pointers to other nodes.
To allocate an object of _Node<T> an allocator based on A is needed. This allocator's type is obtained from A::rebind<_Node<T>>::other and initialized appropriately by passing the original allocator object (or an object created from that) as constructor argument.
Of course, using stateful allocators does assume that you use the C++11 allocator model. Prior to C++11 allocators did not appropriately construct other allocators and they were essentially stateless. In case you need to use code prior to C++11 but want to deal with allocators, you might want to use the containers from BSL: these are allocator aware and do compile with C++03 compilers.
I have the following code (compiler: MSVC++ 10):
std::vector<float> data;
data.push_back(1.0f);
data.push_back(1.0f);
data.push_back(2.0f);
// lambda expression
std::for_each(data.begin(), data.end(), [](int value) {
// Can I get here index of the value too?
});
What I want in the above code snippet is to get the index of the value in the data vector inside the lambda expression. It seems for_each only accepts a single parameter function. Is there any alternative to this using for_each and lambda?
In C++14 thanks to generalized lambda captures you can do something like so:
std::vector<int> v(10);
std::for_each(v.begin(), v.end(), [idx = 0] (int i) mutable {
// your code...
++idx; // 0, 1, 2... 9
});
Alternatively, you can use &value - &data[0], although it might be a bit more expensive.
std::for_each(data.begin(), data.end(), [&data](float const& value) {
int idx = &value - &data[0];
});
I don't think you can capture the index, but you can use an outer variable to do the indexing, capturing it into the lambda:
int j = 0;
std::for_each(data.begin(), data.end(), [&j](float const& value) {
j++;
});
std::cout << j << std::endl;
This prints 3, as expected, and j holds the value of the index.
If you want the actual iterator, you maybe can do it similarly:
std::vector<float>::const_iterator it = data.begin();
std::for_each(data.begin(), data.end(), [&it](float const& value) {
// here "it" has the iterator
++it;
});
Something like this:
template <typename IteratorT, typename FunctionT>
FunctionT enumerate(IteratorT first,
IteratorT last,
typename std::iterator_traits<IteratorT>::difference_type initial,
FunctionT func)
{
for (;first != last; ++first, ++initial)
func(initial, *first);
return func;
}
Used as:
enumerate(data.begin(), data.end(), 0, [](unsigned index, float val)
{
std::cout << index << " " << val << std::endl;
});
I think that the simplest way is to use std::accumulate:
std::accumulate(data.begin(), data.end(), 0, [](int index, float const& value)->int{
...
return index + 1;
});
This solution works with any container and it don't require a variable or custom classes.
Another way to wrap iterators for enumerate:
Required headers:
#include <algorithm>
#include <iterator>
#include <utility>
Wrapping iterator:
template<class Iter, class Offset=int>
struct EnumerateIterator : std::iterator<std::input_iterator_tag, void, void, void, void> {
Iter base;
Offset n;
EnumerateIterator(Iter base, Offset n = Offset()) : base (base), n (n) {}
EnumerateIterator& operator++() { ++base; ++n; return *this; }
EnumerateIterator operator++(int) { auto copy = *this; ++*this; return copy; }
friend bool operator==(EnumerateIterator const& a, EnumerateIterator const& b) {
return a.base == b.base;
}
friend bool operator!=(EnumerateIterator const& a, EnumerateIterator const& b) {
return !(a == b);
}
struct Pair {
Offset first;
typename std::iterator_traits<Iter>::reference second;
Pair(Offset n, Iter iter) : first (n), second(*iter) {}
Pair* operator->() { return this; }
};
Pair operator*() { return Pair(n, base); }
Pair operator->() { return Pair(n, base); }
};
Enumerate overloads:
template<class Iter, class Func>
Func enumerate(Iter begin, Iter end, Func func) {
typedef EnumerateIterator<Iter> EI;
return std::for_each(EI(begin), EI(end), func);
}
template<class T, int N, class Func>
Func enumerate(T (&a)[N], Func func) {
return enumerate(a, a + N, func);
}
template<class C, class Func>
Func enumerate(C& c, Func func) {
using std::begin;
using std::end;
return enumerate(begin(c), end(c), func);
}
Copied test from James:
#include <array>
#include <iostream>
struct print_pair {
template<class Pair>
void operator()(Pair const& p) {
std::cout << p.first << ": " << p.second << "\n";
}
};
int main() {
std::array<float, 5> data = {1, 3, 5, 7, 9};
enumerate(data, print_pair());
return 0;
}
I don't include providing an offset here; though it's fully ready in EnumerateIterator to start at otherwise than 0. The choice left is what type to make the offset and whether to add overloads for the extra parameter or use a default value. (No reason the offset has to be the iterator's difference type, e.g. what if you made it some date related type, with each iteration corresponding to the next day?)
Roger Pate suggested in a comment to my other answer creating an iterator wrapper that performs the enumeration. Implementing it was a bit of a beating.
This iterator wrapper takes a forward iterator whose value type is T (called the "inner iterator") and transforms it into a forward iterator whose value type is a pair<int, T&>, where int is the distance type of the inner iterator.
This would be quite simple, except for two things:
The std::pair constructor takes its arguments by const reference so we can't initialize a data member of type T&; we'll have to create our own pair type for the iterator.
In order to support the correct semantics for the iterator, we need an lvalue (operator* needs to return a reference and operator-> needs to return a pointer), so the pair needs to be a data member of the iterator. Since it contains a reference, we'll need a way to "reset" it and we'll need it to be lazily initialized so that we can correctly handle end iterators. boost::optional<T> seems not to like it if T is not assignable, so we'll write our own simple lazy<T>.
The lazy<T> wrapper:
#include <new>
#include <type_traits>
// A trivial lazily-initialized object wrapper; does not support references
template<typename T>
class lazy
{
public:
lazy() : initialized_(false) { }
lazy(const T& x) : initialized_(false) { construct(x); }
lazy(const lazy& other)
: initialized_(false)
{
if (other.initialized_)
construct(other.get());
}
lazy& operator=(const lazy& other)
{
// To the best of my knowledge, there is no clean way around the self
// assignment check here since T may not be assignable
if (this != &other)
construct(other.get());
return *this;
}
~lazy() { destroy(); }
void reset() { destroy(); }
void reset(const T& x) { construct(x); }
T& get() { return reinterpret_cast< T&>(object_); }
const T& get() const { return reinterpret_cast<const T&>(object_); }
private:
// Ensure lazy<T> is not instantiated with T as a reference type
typedef typename std::enable_if<
!std::is_reference<T>::value
>::type ensure_t_is_not_a_reference;
void construct(const T& x)
{
destroy();
new (&object_) T(x);
initialized_ = true;
}
void destroy()
{
if (initialized_)
reinterpret_cast<T&>(object_).~T();
initialized_ = false;
}
typedef typename std::aligned_storage<
sizeof T,
std::alignment_of<T>::value
>::type storage_type;
storage_type object_;
bool initialized_;
};
The enumerating_iterator:
#include <iterator>
#include <type_traits>
// An enumerating iterator that transforms an iterator with a value type of T
// into an iterator with a value type of pair<index, T&>.
template <typename IteratorT>
class enumerating_iterator
{
public:
typedef IteratorT inner_iterator;
typedef std::iterator_traits<IteratorT> inner_traits;
typedef typename inner_traits::difference_type inner_difference_type;
typedef typename inner_traits::reference inner_reference;
// A stripped-down version of std::pair to serve as a value type since
// std::pair does not like having a reference type as a member.
struct value_type
{
value_type(inner_difference_type f, inner_reference s)
: first(f), second(s) { }
inner_difference_type first;
inner_reference second;
};
typedef std::forward_iterator_tag iterator_category;
typedef inner_difference_type difference_type;
typedef value_type& reference;
typedef value_type* pointer;
explicit enumerating_iterator(inner_iterator it = inner_iterator(),
difference_type index = 0)
: it_(it), index_(index) { }
enumerating_iterator& operator++()
{
++index_;
++it_;
return *this;
}
enumerating_iterator operator++(int)
{
enumerating_iterator old_this(*this);
++*this;
return old_this;
}
const value_type& operator*() const
{
value_.reset(value_type(index_, *it_));
return value_.get();
}
const value_type* operator->() const { return &**this; }
friend bool operator==(const enumerating_iterator& lhs,
const enumerating_iterator& rhs)
{
return lhs.it_ == rhs.it_;
}
friend bool operator!=(const enumerating_iterator& lhs,
const enumerating_iterator& rhs)
{
return !(lhs == rhs);
}
private:
// Ensure that the template argument passed to IteratorT is a forward
// iterator; if template instantiation fails on this line, IteratorT is
// not a valid forward iterator:
typedef typename std::enable_if<
std::is_base_of<
std::forward_iterator_tag,
typename std::iterator_traits<IteratorT>::iterator_category
>::value
>::type ensure_iterator_t_is_a_forward_iterator;
inner_iterator it_; //< The current iterator
difference_type index_; //< The index at the current iterator
mutable lazy<value_type> value_; //< Pair to return from op* and op->
};
// enumerating_iterator<T> construction type deduction helpers
template <typename IteratorT>
enumerating_iterator<IteratorT> make_enumerator(IteratorT it)
{
return enumerating_iterator<IteratorT>(it);
}
template <typename IteratorT, typename DifferenceT>
enumerating_iterator<IteratorT> make_enumerator(IteratorT it, DifferenceT idx)
{
return enumerating_iterator<IteratorT>(it, idx);
}
A test stub:
#include <algorithm>
#include <array>
#include <iostream>
struct print_pair
{
template <typename PairT>
void operator()(const PairT& p)
{
std::cout << p.first << ": " << p.second << std::endl;
}
};
int main()
{
std::array<float, 5> data = { 1, 3, 5, 7, 9 };
std::for_each(make_enumerator(data.begin()),
make_enumerator(data.end()),
print_pair());
}
This has been minimally tested; Comeau and g++ 4.1 both accept it if I remove the C++0x type traits and aligned_storage (I don't have a newer version of g++ on this laptop to test with). Please let me know if you find any bugs.
I'm very interested in suggestions about how to improve this. Specifically, I'd love to know if there is a way around having to use lazy<T>, either by using something from Boost or by modifying the iterator itself. I hope I'm just being dumb and that there's actually a really easy way to implement this more cleanly.
Following the standard convention for C and C++, the first element has index 0, and the last element has index size() - 1.
So you have to do the following;-
std::vector<float> data;
int index = 0;
data.push_back(1.0f);
data.push_back(1.0f);
data.push_back(2.0f);
// lambda expression
std::for_each(data.begin(), data.end(), [&index](float value) {
// Can I get here index of the value too?
cout<<"Current Index :"<<index++; // gets the current index before increment
});
You could also pass a struct as third argument to std::for_each and count the index in it like so:
struct myStruct {
myStruct(void) : index(0) {};
void operator() (float i) { cout << index << ": " << i << endl; index++; }
int index;
};
int main()
{
std::vector data;
data.push_back(1.0f);
data.push_back(4.0f);
data.push_back(8.0f);
// lambda expression
std::for_each(data.begin(), data.end(), myStruct());
return 0;
}
Maybe in the lambda function, pass it a int& instead of value int, so you'd have the address. & then you could use that to deduce your position from the first item
Would that work? I don't know if for_each supports references