Is it possible to maintain knowledge of the derived class in any std c++ container without using pointers, dynamically casting return values from the container? I know I can create a vector or what ever of pointers of some base class type, and have them retain their sub classes. But the question is do I have to use pointers?
Example:
struct A {
int x = 0, y = 0, z = 0;
virtual void foo() { cout << "A" << endl; };
};
struct B : public A {
int a = 1, b = 1, c = 1;
virtual void foo() { cout << "B" << endl; };
};
int main() {
<SOMECONTAINER><A> a(2);
a[0] = A();
a[1] = B();
B * p;
B& n = dynamic_cast<B&>(a[1]); // Always throws?
p = dynamic_cast<B*>(&a[1]); // Always zero?
cout << p << endl;
}
Yes, you do have to use pointers. Otherwise, attempting to put a B into a container of A results in slicing: the B gets cut down into an A (this is not limited to containers, the exact same thing happens if you do A a = B() or if you pass a B to a function expecting an A).
When you later take it back out, it's an A that has absolutely no knowledge its lineage includes an illustrious forefather of type B -- and no matter what way you look at an A, you can't make it a B.
I am going to ignore alignment, or rather assume that data after a pointer is sufficiently aligned.
template<class T, unsigned N>
struct poly_anna;
template<class T,unsigned N>
struct poly_bob {
typedef poly_anna<T,N> poly_anna_;
T*(*get)(poly_anna_*) = nullptr;
void(*destroy)(poly_anna_*) = nullptr;
void(*move_to)(poly_anna_ *,poly_anna_*) = nullptr;
void(*copy_to)(poly_anna_ const*, poly_anna_*)=nullptr;
};
template<class T, unsigned N>
struct poly_anna {
private:
poly_bob<T,N> const*bob=nullptr;
char buff[N];
public:
template<class U> static poly_bob<T,N> const* get_bob() {
static poly_bob<T,N> b={
[](poly_anna*a)->T&{ return *(U*)&a->buff[0]; },
[](poly_anna*a){ ((U*)&a->buff[0])->~U(); a->bob = nullptr; },
[](poly_anna*s,poly_anna*d){
if (s->bob==d->bob){
*((U*)&d->buff[0])=std::move(*((U*)&d->buff[0]));
return;
}
if (d->bob != nullptr) {
d->bob->destroy(b);
}
d->store( std::move( *(U*)&s->buff[0] ) );
},
[](poly_anna const* s, poly_anna*d){
if (d->bob == s->bob){
*(U*)&d->buff[0] = *(U const*)&s->buff[0];
return;
}
if (d->bob){ d->bob->destroy(d); }
d->store( *(U const*)*s->buff[0] );
}
};
return &b;
};
template<class U_>
void store(U_&& u){
typedef typename std::decay<U_>::type U;
static_assert( sizeof(U)<=N, "N not large enough" );
if (bob) bob->destroy( this );
bob = get_bob<U>();
new (&buff[0]) U( std::forward<U_>(u) );
}
void reset(){ if (bob) bob->destroy(this); }
T& get() {
return bob->get(this);
}
T const& get() const {
return bob->get(const_cast<poly_anna*>(this));
}
poly_anna( poly_anna const& o ){
if (o.bob) o.bob->copy_to( &o, this );
}
poly_anna( poly_anna && o ){
if (o.bob) o.bob->move_to( &o, this );
}
poly_anna&operator=( poly_anna const& o ){
if (o.bob) o.bob->copy_to( &o, this );
else if (bob) bob->destroy(this);
return *this
}
poly_anna&operator=( poly_anna && o ){
if (o.bob) o.bob->move_to( &o, this );
else if (bob) bob->destroy(this);
return *this
}
poly_anna()=default;
~poly_anna(){if(bob)bob->destroy(this);}
explicit operator bool()const{return bob;}
};
That is my attempt at a polymorphic variant. It stores T and children of T so long as they are no larger than N and can be stored in std containers.
Let me know if it compiles.
You need pointers for virtual member function dispatch.
When you think about it, without pointers, you are left with "by value". Value semantics and and polymorphism don't really make sense together.
A value has a single context / type. It is atomic and simple. WYSIWIG, so to speak. Sure, you can cast it, but then you have ... another value.
There's an oft-quoted programmer proverb: There is no problem which cannot be solved with an additional layer of indirection, except too many layers of indirection.
Putting it into practice, take a look at boost::variant.
If your container stores boost::variants allowing all the (sub-)classes you want to store, you can avoid pointers.
That can be a win, but need not be.
Measure before you commit to such a solution.
I think there are actually two questions here:
Is it possible to get polymorphic semantics in an STL container without using pointers and the associated dynamic allocation?
Is is possible to retain the concrete type of a polymorphic object stored in an STL container?
The answer to 1 is yes, with some effort. As some have mentioned, one way to do this is to use a variant type like boost::variant. The problem with that approach is that you lose the ability to interact naturally with the objects stored in the variant and instead have to write visitors, which have a lot of syntactic overhead.
If the class hierarchy is bounded then a better approach might be to use a variation on boost::variant (no pun intended) specifically designed to preserve polymorphic semantics and its associated syntax. One example might be the emplacer. As noted in dyp's comment above, emplacer is a restricted variant.
As for question 2, I'm not aware of any way to do that without using typeid() or a hand-rolled type system.
Related
I have two classes,
template<class Type>
class SafePtr {
public:
SafePtr() {}
~SafePtr() {}
void Lock(Type* data, void* key)
{
if (!pKey)
{
pKey = key;
pData = data;
}
}
Type* Unlock(void* key) const
{
if (key == pKey)
return pData;
}
Type* operator->()
{
return pData;
}
private:
Type* pData = nullptr;
void* pKey = nullptr;
};
template<class Type>
class SafePtrArray {
public:
SafePtrArray() {}
~SafePtrArray() {}
template<class... Args>
SafePtr<Type> CreatePtr(Args&&... args)
{
Type* data = new Type(args...);
ptrs.insert(ptrs.end(), data);
SafePtr<Type> ptr;
ptr.Lock(data, this);
return ptr;
}
Type* UnlockPtr(const SafePtr<int>& ptr)
{
return ptr.Unlock(this);
}
void Destroy(const SafePtr<int>& ptr)
{
Type* pointer = ptr.Unlock(this);
for (auto itr = ptrs.begin(); itr != ptrs.end(); itr++)
{
if ((*itr) == pointer)
{
delete pointer;
ptrs.erase(itr);
}
}
}
private:
std::vector<Type*> ptrs;
};
The goal is to protect a pointer so that the user can access its members but not get to manipulate its actual pointer (mainly delete it prematurely). And also I need to store all the pointers in an array so that when the parent object destroys, I can automatically destroy all the allocated pointers
For this I use two classes, SafePtr and SafePtrArray. SafePtrArray creates and stores the pointers and wraps them in the SafePtr and returns it to the user. SafePtr is just a wrapper and should not let the user get access to the underlying pointer but will allow them to access its members.
It works fine at first but soon I found this error,
int main()
{
SafePtrArray<int> ptr;
auto pInt = ptr.CreatePtr();
int* i = pInt.operator->(); // Users can get access to the underlying pointer using this.
ptr.Destroy(pInt);
}
Is there a way to prevent users from getting access to the underlying type and prevent them from manipulating the pointer while having the privilege to access its members?
I still think you try to solve a problem that has more to do with possible flaws in the design of the API/of the code, the documentation, or with the lack of C++ knowledge of the one using it, with a "solution" that has more cons than pros.
If a C++ programmer does not know what ownership is or does not respect it and blindly deletes objects or frees the memory of pointers, then there will be much bigger concerns. You likely will move the problem just to a different part of the code.
Having that said, the closest you can do to not expose the pointer right now is something like this:
(The code is just a proof of concept, so things like call might need to be improved)
#include <iostream>
#include <string>
struct Test {
void foo(int x, int y, std::string str) {
std::cout << x << " " << y << " " << str << std::endl;
}
double test = 0.5;
};
template <typename T>
struct Ptr {
template <auto M, typename... Args>
auto call(Args... args) {
return (obj.*M)(std::forward<Args>(args)...);
}
template <auto M>
auto get() {
return (obj.*M);
}
protected:
T obj;
};
int main() {
Ptr<Test> p;
p.call<&Test::foo>(1, 2, "hello");
std::cout << p.get<&Test::test>() << std::endl;
return 0;
}
But I still don't think that this is a good approach.
And a user can still mess around with the code and do something bad like:
int main() {
Ptr<Test> p;
delete &p;
return 0;
}
Or this, which for sure is undefined behavior, but that does not really matter as deleting a not owned object will also result in undefined behavior at some point:
template<typename T>
struct Ptr {
protected:
T *obj;
}
template<typename T>
struct Ptr2 {
public:
T *obj;
};
int main()
{
Ptr<Test> p;
Ptr2<Test> *p2 = reinterpret_cast<Ptr2<Test>*>(&p);
std::cout << p2->obj << std::endl;
}
So there is no protection again such things.
Besides the shown code, there is a proposal for reflection that is feature complete now, which would allow getting information about the members of a type, but this was not added to c++20, and one for metaclasses which is also not in the standard yet.
With these two proposals, you might be able to implement something better useable. But my concerns about the benefits of this remain.
Is there a way to prevent users from getting access to the underlying type and prevent them from manipulating the pointer while having the privilege to access its members?
Under certain conditions, no, this is not possible. If the underlying Type is a standard layout class then providing access to the first non-static non-bitfield data member breaks your goal. (Caveat: providing access to just the value of the member is a different story.) The address of that member can be converted to a pointer to the underlying object via reinterpret_cast, which allows calling delete on that pointer. (Well, "allows" in the sense that the call is syntactically valid. Not much else matters for "allows" since we are headed into undefined behavior anyway.)
For classes that are not standard layout, there are probably compiler-specific (non-portable) methods to achieve the same effect (converting the address of a data member into a pointer to the underlying object). There is no reason for a compiler to try actively to thwart such things.
If a programmer is determined to invoke undefined behavior, there is little you can do to stop it.
let's assume I have a super polymorphic base class Shape where many other shape classes are derived from it.
now if I have a vector of Shape pointers that contains a pointer to a list of different shape types like this:
vector<Shape*> p; // p contains pointer to many different shape objects
I know to have access to methods and members of each shape in vector p, I need to use dynamic_cast.
but what if I don't know what vector p actually contains at runtime? how can i safely find the type of an object contained in vector p at runtime?
i also know i can check if casting by dynamic_cast returns NULL or not for success. but does that mean to find the actual type of my shape object in vector p I have to do something like this:
if (dynamic_cast<Circle*> p[i] !=NULL){
// do stuff
}
else if (...) {
}
and repeat this pattern for all other shape types?
but this becomes cumbersome if I have 100 possible shapes. is there any better way to achieve this at rumtime?
ps- consider following scenario:
lets say I need to iterate through the Shape* vector and for example put all the circle objects in a separate vector and vector etc ... now i need to know the actual type of the objects. checking the return if typeid and dynamic_casts for many shapes is not practical.
You can use typeid in typeinfo header.
See for instance this question: How to determine actual object type at runtime in C++;
However, the actual question is "why do you need to know the actual type of your object?": that this is AFAIK not that frequent to need such a functionnality, since polymorphimsm already allows for managing a vast majority of use cases.
I know to have access to methods and members of each shape in vector
p, I need to use dynamic_cast.
No, not necessarily!
In your case, maybe the following is enough, assuming Shape has an area method, (re)defined in Circle and Rectangle (who both extend the Shape class):
std::vector<Shape*> shapes;
Rectangle rect(...);
Circle circle(...);
shapes.push_back( &rect );
shapes.push_back( &circle );
shapes[0]->area(); // --> calls Rectangle::area()
shapes[1]->area(); // --> calls Circle::area()
I came up with solution that I'm not really proud of, but maybe it will be helpfull in creating better one.
Key thing I was trying to achieve was to get rid of explicit dynamic_cast and got this one working. There is still a need to name your derieved type twice though.
Also, it uses std::function which is told to be slow. C++14 required.
I believe there is a way to do it with just smart usage of templates. Or at least get rid of type_switch<A>::cast<B> lanes. Anyway, the code:
#include <iostream>
#include <functional>
#include <typeindex>
#include <unordered_map>
// Basic inheritance cases
struct A
{
virtual void foo() = 0;
};
struct B : public A
{
void foo() override { }
void bfoo() {
std::cout << "B specific\n";
}
};
struct C : public A
{
void foo() override { }
};
template <typename T>
struct type_switch
{
using Func = std::function<void(T&)>;
using Pair = std::pair<std::type_index, Func>;
using Map = std::unordered_map<std::type_index, Func>;
Map map;
type_switch(std::initializer_list<Pair> l) : map(l.begin(),l.end())
{
}
void call(T& a)
{
map[typeid(a)](a);
}
// allows for "oneliner", without explicit 'call', but it could end in creation of
// new type_switch on every loop iteration etc.
type_switch(T&a, std::initializer_list<Pair> l) : type_switch(l){
call(a);
}
template <typename T2>
static Func cast(std::function<void(T2&)> f)
{
static_assert(std::is_base_of<T, T2>::value, "Invalid cast operation on functors, T2 is not base of T");
// lot of functor copyings...
return[f = std::move(f)](T& t) {
f(static_cast<T2&>(t));
};
}
};
int main()
{
B b;
C c;
int local = 0;
type_switch<A> sw = {
{ typeid(B), type_switch<A>::cast<B>( [&local](auto& a) { // auto will deduce into B! No explicit casting
std::cout << "Handle b, local value is " << local << '\n';
a.bfoo(); // B specific
local++; // some outer scode operation
}) } ,
{ typeid(C), type_switch<A>::cast<C>([&local](auto& a) { // auto will deduce into C! No explicit casting
std::cout << "Handle c, local value is " << local << '\n';
local++; // some outer scode operation
})
},
/* // this one would trigger static_assert
{ typeid(int), type_switch<A>::cast<int>([&local](auto& a) { // auto will deduce into C! No explicit casting
std::cout << "Handle int, local value is " << local << '\n';
local++; // some outer scode operation
})
},*/
};
sw.call(b);
sw.call(c);
return 0;
}
Suppose there's API like below:
typedef void callback_t(void* data);
void addCallback(handle_t h, callback_t callback, void* data);
I'd like to wrap this API to a higher order C++ interface:
template<class F, bool emplace = IsEmplaceable<F>::value>
struct MakeCallback;
class Handle
{
template<class F>
void addCallback(F f)
{
::addCallback(_h, MakeCallback<F>::f, MakeCallback<F>::create(f));
}
handle_t _h;
};
so that the user can pass any callable object (e.g. lambda function).
I'd like to apply small object optimization to avoid dynamic alloc (e.g. for empty lambdas), the trait IsEmplaceable<F> decides whether F can be emplaced in a void*.
For F that is not emplaceable, MakeCallback can be implemented like below:
template<class F>
struct MakeCallback<F, false>
{
static void f(void* data)
{
auto f = static_cast<F*>(data);
(*f)(status);
delete f;
}
static void* create(F& f)
{
return new F(std::move(f));
}
};
For F that is emplaceable, how could I properly implement the following?
template<class F>
struct MakeCallback<F, true>
{
static void f(void* data)
{
// from void* to F
}
static void* create(F& f)
{
// form F to void*
}
};
More basically, can a void* hold a non-address value if we don't use it as a pointer? will it be UB?
A very big warning should be mentioned before even attempting the code shown in this answer. Doing this is most likely very very undefined and not portable behavior. I would highly suggest not doing this as it could very well break a long time into the future and you will have a very hard time finding out why.
That being said, it appears to work on at least my compiler. Results may vary for other compilers. I use a union to convert between a class instance and a void *, don't know of any other clean way to do this. This should work as long as sizeof( Class ) <= sizeof( void * ), but I make no guarantees on behavior with different compilers or even with the exact same compiler I used on my exact same setup.
#include <iostream>
using namespace std;
class Small {
public:
int i;
};
void *castToVoid( Small& s ) {
union {
Small s;
void *p;
} un;
un.s = s;
return un.p;
}
Small castToSmall( void *p ) {
union {
Small s;
void *p;
} un;
un.p = p;
return un.s;
}
int main( ) {
Small s;
s.i = 100;
void *p = castToVoid( s );
s.i = 200;
cout << p << endl; // Prints 0x64
Small s2 = castToSmall( p );
cout << s2.i << endl; // Prints 100
}
or this example for converting to/from a void *
void *castToVoid( Small& s ) {
void **p = reinterpret_cast< void ** >( &s );
return *p;
}
Small castToSmall( void *p ) {
Small *s = reinterpret_cast< Small * >( &p );
return *s;
}
It's implementation-defined behavior according to the C++ standard (draft N3797).
§ 3.7.4.2/p4
Indirection through an invalid pointer value and passing an invalid
pointer value to a deallocation function have undefined behavior. Any
other use of an invalid pointer value has implementation-defined
behavior.
footnote 38:
Some implementations might define that copying an invalid pointer
value causes a system-generated runtime fault
§ 3.7.4.3/p4
An implementation may have relaxed pointer safety, in which case the
validity of a pointer value does not depend on whether it is a
safely-derived pointer value. Alternatively, an implementation may
have strict pointer safety, in which case a pointer value referring to
an object with dynamic storage duration that is not a safely-derived
pointer value is an invalid pointer value unless the referenced
complete object has previously been declared reachable (20.7.4) [...] It is implementation
defined whether an implementation has relaxed or strict pointer safety.
(emphasis mine)
So it is safe if the implementation has relaxed pointer safety, and we can use the union trick as shown in the answer from #Smith_61 to avoid strict aliasing.
What I am needing can be done by storing this pointer of enclosing class into nested class for example this way:
class CEnclosing {
public:
class CNested : public CSomeGeneric {
public:
CNested(CEnclosing* e) : m_e(e) {}
virtual void operator=(int i) { m_e->SomeMethod(i); }
CEnclosing* m_e;
};
CNested nested;
CEnclosing() : nested(this) {}
virtual void SomeMethod(int i);
};
int main()
{
CEnclosing e;
e.nested = 123;
return 0;
}
This works well, but requires sizeof(void*) bytes of memory more for each nested member class. Exist effective and portable way to do this without need to store pointer to instance of CEnclosing in m_e?
As stated previously, C++ does not provide any way to do this. A nested class has no special way to find its enclosing class. The solution you already have is the recommended way.
If you have an advanced scenario, and if you are prepared to maintain non-portable code, and if the cost of storing an additional pointer is important enough to use a risky solution, then there is a way based on the C++ object model. With a number of provisos I won't go into, you can rely on the enclosing and nested classes being laid out in memory in a predictable order, and there being a fixed offset between the start of the enclosing and nested classes.
The code is something like:
CEnclosing e;
int offset = (char*)&e.nested - (char*)&e;
//... inside nested class
CEnclosing* pencl = (CEnclosing*)((char*)this - offset);
OTOH it's equally possible that the offsetof macro may just do it for you, but I haven't tried it.
If you really want to do this, read about trivially copyable and standard layout in the standard.
I believe the following could be portable; though it is not fool-proof. Specifically, it will not work across virtual inheritance.
Also, I would like to point that it is not safe, in that it will happily compile even if the member you pass does not correspond to the one you compute the offset with:
#include <iostream>
template <typename C, typename T>
std::ptrdiff_t offsetof_impl(T C::* ptr) {
C c; // only works for default constructible classes
T* t = &(c.*ptr);
return reinterpret_cast<char*>(&c) - reinterpret_cast<char*>(t);
}
template <typename C, typename T, T C::* Ptr>
std::ptrdiff_t offsetof() {
static std::ptrdiff_t const Offset = offsetof_impl(Ptr);
return Offset;
}
template <typename C, typename T, T C::* Ptr>
C& get_enclosing(T& t) {
return *reinterpret_cast<C*>(reinterpret_cast<char*>(&t)
+ offsetof<C, T, Ptr>());
}
// Demo
struct E { int i; int j; };
int main() {
E e = { 3, 4 };
//
// BEWARE: get_enclosing<E, int, &E::j>(e.i); compiles ERRONEOUSLY too.
// ^ != ^
//
E& ref = get_enclosing<E, int, &E::j>(e.j);
std::cout << (void const*)&e << " " << (void const*)&ref << "\n";
return 0;
}
Still, it does run on this simplistic example, which allowed me to find 2 bugs in my initial implementation (already). Handle with caution.
The clear and simple answer to your question is no, C++11 doesn't have any special feature to handle your scenario. But there is a trick in C++ to allow you to do this:
If CEnclosing didn't have a virtual function, a pointer to nested would have the same value as a pointer to the containing instance. That is:
(void*)&e == (void*)&e.nested
This is because the variable nested is the first in the class CEnclosing.
However, since you have a virtual function in CEnclosing class, then all you need to do is subtract the vtable size from &e.nested and you should have a pointer to e. Don't forget to cast correctly, though!
EDIT: As Stephane Rolland said, this is a dangerous solution and, honestly, I wouldn't use it, but this is the only way (or trick) I could think of to access the enclosing class from a nested class. Personally, I would probably try to redesign the relation between these two classes if I really want to optimise memory usage up to the level you mentioned.
How about using multiple inheritance like this:
class CNested {
public:
virtual void operator=(int i) { SomeMethod(i); }
virtual void SomeMethod(int i) = 0;
};
class CEnclosing: public CSomeGeneric, public CNested {
int nEncMember;
public:
CNested& nested;
CEnclosing() : nested(*this), nEncMember(456) {}
virtual void SomeMethod(int i) { std:cout << i + nEncMember; }
};
I'm trying to define a good design for my software which implies being careful about read/write access to some variables. Here I simplified the program for the discussion. Hopefully this will be also helpful to others. :-)
Let's say we have a class X as follow:
class X {
int x;
public:
X(int y) : x(y) { }
void print() const { std::cout << "X::" << x << std::endl; }
void foo() { ++x; }
};
Let's also say that in the future this class will be subclassed with X1, X2, ... which can reimplement print() and foo(). (I omitted the required virtual keywords for simplicity here since it's not the actual issue I'm facing.)
Since we will use polymorphisme, let's use (smart) pointers and define a simple factory:
using XPtr = std::shared_ptr<X>;
using ConstXPtr = std::shared_ptr<X const>;
XPtr createX(int x) { return std::make_shared<X>(x); }
Until now, everything is fine: I can define goo(p) which can read and write p and hoo(p) which can only read p.
void goo(XPtr p) {
p->print();
p->foo();
p->print();
}
void hoo(ConstXPtr p) {
p->print();
// p->foo(); // ERROR :-)
}
And the call site looks like this:
XPtr p = createX(42);
goo(p);
hoo(p);
The shared pointer to X (XPtr) is automatically converted to its const version (ConstXPtr). Nice, it's exactly what I want!
Now come the troubles: I need a heterogeneous collection of X. My choice is a std::vector<XPtr>. (It could also be a list, why not.)
The design I have in mind is the following. I have two versions of the container: one with read/write access to its elements, one with read-only access to its elements.
using XsPtr = std::vector<XPtr>;
using ConstXsPtr = std::vector<ConstXPtr>;
I've got a class that handles this data:
class E {
XsPtr xs;
public:
E() {
for (auto i : { 2, 3, 5, 7, 11, 13 }) {
xs.emplace_back(createX(std::move(i)));
}
}
void loo() {
std::cout << "\n\nloo()" << std::endl;
ioo(toConst(xs));
joo(xs);
ioo(toConst(xs));
}
void moo() const {
std::cout << "\n\nmoo()" << std::endl;
ioo(toConst(xs));
joo(xs); // Should not be allowed
ioo(toConst(xs));
}
};
The ioo() and joo() functions are as follow:
void ioo(ConstXsPtr xs) {
for (auto p : xs) {
p->print();
// p->foo(); // ERROR :-)
}
}
void joo(XsPtr xs) {
for (auto p: xs) {
p->foo();
}
}
As you can see, in E::loo() and E::moo() I have to do some conversion with toConst():
ConstXsPtr toConst(XsPtr xs) {
ConstXsPtr cxs(xs.size());
std::copy(std::begin(xs), std::end(xs), std::begin(cxs));
return cxs;
}
But that means copying everything over and over.... :-/
Also, in moo(), which is const, I can call joo() which will modify xs's data. Not what I wanted. Here I would prefer a compilation error.
The full code is available at ideone.com.
The question is: is it possible to do the same but without copying the vector to its const version? Or, more generally, is there a good technique/pattern which is both efficient and easy to understand?
Thank you. :-)
I think the usual answer is that for a class template X<T>, any X<const T> could be specialized and therefore the compiler is not allow to simply assume it can convert a pointer or reference of X<T> to X<const T> and that there is not general way to express that those two actually are convertible. But then I though: Wait, there is a way to say X<T> IS A X<const T>. IS A is expressed via inheritance.
While this will not help you for std::shared_ptr or standard containers, it is a technique that you might want to use when you implement your own classes. In fact, I wonder if std::shared_ptr and the containers could/should be improved to support this. Can anyone see any problem with this?
The technique I have in mind would work like this:
template< typename T > struct my_ptr : my_ptr< const T >
{
using my_ptr< const T >::my_ptr;
T& operator*() const { return *this->p_; }
};
template< typename T > struct my_ptr< const T >
{
protected:
T* p_;
public:
explicit my_ptr( T* p )
: p_(p)
{
}
// just to test nothing is copied
my_ptr( const my_ptr& p ) = delete;
~my_ptr()
{
delete p_;
}
const T& operator*() const { return *p_; }
};
Live example
There is a fundamental issue with what you want to do.
A std::vector<T const*> is not a restriction of a std::vector<T*>, and the same is true of vectors containing smart pointers and their const versions.
Concretely, I can store a pointer to const int foo = 7; in the first container, but not the second. std::vector is both a range and a container. It is similar to the T** vs T const** problem.
Now, technically std::vector<T const*> const is a restriction of std::vector<T>, but that is not supported.
A way around this is to start workimg eith range views: non owning views into other containers. A non owning T const* iterator view into a std::vector<T *> is possible, and can give you the interface you want.
boost::range can do the boilerplate for you, but writing your own contiguous_range_view<T> or random_range_view<RandomAccessIterator> is not hard. It gets fancy ehen you want to auto detect the iterator category and enable capabilities based off that, which is why boost::range contains much more code.
Hiura,
I've tried to compile your code from repo and g++4.8 returned some errors.
changes in main.cpp:97 and the remaining lines calling view::create() with lambda function as the second argument.
+add+
auto f_lambda([](view::ConstRef_t<view::ElementType_t<Element>> const& e) { return ((e.getX() % 2) == 0); });
std::function<bool(view::ConstRef_t<view::ElementType_t<Element>>)> f(std::cref(f_lambda));
+mod+
printDocument(view::create(xs, f));
also View.hpp:185 required additional operator, namely:
+add+
bool operator==(IteratorBase const& a, IteratorBase const& b)
{
return a.self == b.self;
}
BR,
Marek Szews
Based on the comments and answers, I ended up creating a views for containers.
Basically I defined new iterators. I create a project on github here: mantognini/ContainerView.
The code can probably be improved but the main idea is to have two template classes, View and ConstView, on an existing container (e.g. std::vector<T>) that has a begin() and end() method for iterating on the underlying container.
With a little bit of inheritance (View is a ConstView) it helps converting read-write with to read-only view when needed without extra code.
Since I don't like pointers, I used template specialization to hide std::shared_ptr: a view on a container of std::shared_ptr<T> won't required extra dereferencing. (I haven't implemented it yet for raw pointers since I don't use them.)
Here is a basic example of my views in action.