vector and const - c++

Consider this
void f(vector<const T*>& p)
{
}
int main()
{
vector<T*> nonConstVec;
f(nonConstVec);
}
The following does not compile.The thing is that vector<T*> can not be converted to vector <const T*> , and that seems illogically to me , because there exists implicit conversion from T* to const T*. Why is this ?
vector<const T*> can not be converted to vector <T*> too, but that is expected because const T* can not be converted implicitly to T*.

I've added a few lines to your code. That's sufficient to make it clear why this is disallowed:
void f(vector<const T*>& p)
{
static const T ct;
p.push_back(&ct); // adds a const T* to nonConstVec !
}
int main()
{
vector<T*> nonConstVec;
f(nonConstVec);
nonConstVec.back()->nonConstFunction();
}

vector<T> and vector<const T> are unrelated types. The fact that T can be converted to const T doesn't mean a thing here.
You have to think about it from a type system's standpoint. Instantiated vector<int> doesn't have anything in common with vector<const int>.

It may be worth showing why it's a breach of const-correctness to perform the conversion you want:
#include <vector>
const int a = 1;
void addConst(std::vector<const int *> &v) {
v.push_back(&a); // this is OK, adding a const int* to a vector of same
}
int main() {
std::vector<int *> w;
int b = 2;
w.push_back(&b); // this is OK, adding an int* to a vector of same
*(w.back()) = 3; // this is OK, assigning through an int*
addConst(w); // you want this to be OK, but it isn't...
*(w.back()) = 3; // ...because it would make this const-unsafe.
}
The problem is that vector<int*>.push_back takes a pointer-to-non-const (which I'll call a "non-const pointer" from now on). That means, it might modify the pointee of its parameter. Specifically in the case of vector, it might hand the pointer back out to someone else who modifies it. So you can't pass a const pointer to the push_back function of w, and the conversion you want is unsafe even if the template system supported it (which it doesn't). The purpose of const-safety is to stop you passing a const pointer to a function which takes a non-const pointer, and this is how it does its job. C++ requires you to specifically say if you want to do something unsafe, so the conversion certainly can't be implicit. In fact, because of how templates work, it's not possible at all (see later).
I think C++ could in principle preserve const-safety by allowing a conversion from vector<T*>& to const vector<const T*>&, just as int ** to const int *const * is safe. But that's because of the way vector is defined: it wouldn't necessarily be const-safe for other templates.
Likewise, it could in theory allow an explicit conversion. And in fact, it does allow an explicit conversion, but only for objects, not references ;-)
std::vector<const int*> x(w.begin(), w.end()); // conversion
The reason it can't do it for references is because the template system can't support it. Another example that would be broken if the conversion were allowed:
template<typename T>
struct Foo {
void Bar(T &);
};
template<>
struct Foo<const int *> {
void Baz(int *);
};
Now, Foo<int*> doesn't have a Baz function. How on earth could a pointer or reference to Foo<int*> be converted to a pointer or reference to Foo<const int*>?
Foo<int *> f;
Foo<const int *> &g = f; // Not allowed, but suppose it was
int a;
g.Baz(&a); // Um. What happens? Calls Baz on the object f?

Think of like this:
You have two class like this:
class V { T* t;};
class VC { T const* t;};
Do you expect these two classes to be convertible automatically?
This is basically what a template class is. Each variation is a completely new type.
Thus vector<T*> and vector<T const*> are completely different types.
My first question is do you really want to store pointers?
If yes, I would suggest looking at boost::ptr_container. This holds pointers and deletes them when the vector is destroyed. But more importantly it treats the contained pointers as a normal std:vector treats its contained objects. Thus by making the vector const you can only access its members as const
void function(boost::ptr_vector<T> const& x)
{
x.push_back(new T); // Fail x is const.
x[4].plop(); // Will only work if plop() is a const member method.
}
If you don't need to store pointers then store the objects (not the pointers) in the container.
void function(std::vector<T> const& x)
{
x.push_back(T()); // Fail x is const.
x[4].plop(); // Will only work if plop() is a const member method.
}

Others have already given the reason why the code you gave doesn't compile, but I have a different answer on how to deal with it. I don't believe there's any way to teach the compiler how to automatically convert the two (because that would involve changing the definition of std::vector). The only way around this annoyance is to do an explicit conversion.
Converting to a completely different vector is unsatisfying (wastes memory and cycles for something that should be completely identical). I suggest the following:
#include <vector>
#include <iostream>
using namespace std;
typedef int T;
T a = 1;
T b = 2;
void f(vector<const T*>& p)
{
for (vector<const T*>::const_iterator iter = p.begin(); iter != p.end(); ++iter) {
cout << **iter << endl;
}
}
vector<const T*>& constify(vector<T*>& v)
{
// Compiler doesn't know how to automatically convert
// std::vector<T*> to std::vector<T const*> because the way
// the template system works means that in theory the two may
// be specialised differently. This is an explicit conversion.
return reinterpret_cast<vector<const T*>&>(v);
}
int main()
{
vector<T*> nonConstVec;
nonConstVec.push_back(&a);
nonConstVec.push_back(&b);
f(constify(nonConstVec));
}
I'm using reinterpret_cast to declare that the two things are the same. You SHOULD feel dirty after using it, but if you put it in a function by itself with a comment for those following you, then have a wash and try to continue on your way with a good conscience, though you will always (rightly) have that nagging worry about someone pulling the ground out from under you.

As others have said, conversions aren't applied to the template parameters. Put another way,
vector<T>
...and:
vector<const T>
... are completely different types.
If you are trying to implement const-correctness in regard to f() not modifying the contents of the vector, this might be more along the lines of what you're looking for:
void f(vector<T>::const_iterator begin, vector<T>::const_iterator end)
{
for( ; begin != end; ++begin )
{
// do something with *begin
}
}
int main()
{
vector<T> nonConstVec;
f(nonConstVec.begin(), nonConstVec.end());
}

in addition to other answers, it's worth reading C++ FQA Lite where this (and many others C++ features) are discussed from a critical POV:
http://yosefk.com/c++fqa/const.html#fqa-18.1

That's the way templates work - no conversions are applied on the template parameters, so the two vectors are of completely different types.

Both vector<const T*> and vector<T*> are completely different types. Even if you write const T* inside your main(), your code wont compile. You need to provide specialization inside main.
The following compiles:
#include<vector>
using namespace std;
template<typename T>
void f(vector<const T*>& p)
{
}
int main()
{
vector<const int*> nonConstVec;
f(nonConstVec);
}

Templates are a bit strange that way. The fact that there's an implicit conversion from T to U doesn't mean that there's an implicit conversion from XXX to XXX. It can be made to happen, but it takes a fair amount of extra work in the template code to make it happen, and offhand, I doubt the techniques were all known when std::vector was being designed (more accurately, I'm pretty sure they weren't known).
Edit: Issues like this are part of the motivation behind using iterators. Even though a container of X isn't implicitly convertible to a container of const X, a container<X>::iterator is implicitly convertible to a container<X>::const_iterator.
If you replace your:
void f(vector<const T*>& p) {}
with:
template <class const_iter>
void f(const_iter b, const_iter e) {}
Then:
int main() {
vector<T*> nonConstVec;
f(nonConstVec.begin(), nonConstVec.end());
return 0;
}
will be just fine -- and so will:
vector<T const *> constVec;
f(constVec.begin(), constVec.end());

Related

How to write a getter for a container of shared_ptrs which does not allow modification of data

Let's say I have a private variable which is a vector of shared_ptrs to non-const objects.
Is it possible to write a getter method which only allows read access to the data pointed to by the shared pointers?
I want to be able to use range-based loops for elegance, so I want to avoid writing const_iterators.
My understanding is that const shared_ptr<T> makes the pointer itself const, not T. I tried to compile shared_ptr<const T>, but it doesn't compile if T itself is not declared const in the class.
In other words, how could I write something like:
#include <iostream>
#include <vector>
#include <memory>
using std::vector;
using std::shared_ptr;
using std::make_shared;
using std::cout;
using std::endl;
class MyClass{
public:
MyClass(int element1, int element2)
{
myVector_.push_back(std::make_shared<int>(element1));
myVector_.push_back(std::make_shared<int>(element2));
}
// I want something like this, but doesn't compile
// const vector<shared_ptr<const int>> readMyVector() const {return myVector_;}
const vector<shared_ptr<int>> readMyVector() const {return myVector_;}
private:
// Should NOT be <const int>, the class should be able to modify its elements
vector<shared_ptr<int>> myVector_;
};
int main(){
auto testobject = MyClass(1,2);
for (auto my_protected_data : testobject.readMyVector()){
cout<<(*my_protected_data)<<endl;
(*my_protected_data) = 25;
cout<<(*my_protected_data)<<endl; // Should not happen
}
return 0;
}
The correct type to return is std::vector<std::shared_ptr<const int>>, but you'll have to make that vector by hand. std::shared_ptr<T> is convertible to std::shared_ptr<const T>, but the problem is that std::vector<T> isn't implicitly convertible to std::vector<U> simply because T is convertible to U.
The easiest way is to construct a vector from your internal vector's begin and end iterators.
vector<shared_ptr<const int>> readMyVector() const
{
return{ myVector_.begin(), myVector_.end() };
}
Note that adding const to the return type of a function that returns by value is rarely useful.
You should also ask yourself rather it's worth it to copy all of those std::shared_ptr. You may want to consider simply returning a vector of int.
If you want to make your getter to return vector of shared pointers to const data, there is only one way, to return copy of shared pointers to const data.
const vector<shared_ptr<const int>> readMyVector() const
{
vector<shared_ptr<const int>> cdata(myVector_.begin(), myVector_.end());
return cdata;
}

Access c++ struct attribute like in an array [duplicate]

Let have a type T and a struct having ONLY uniform elements of T type.
struct Foo {
T one,
T two,
T three
};
I'd like to access them in fallowing way:
struct Foo {
T one,
T two,
T three
T &operator [] (int i)
{
return *(T*)((size_t)this + i * cpp_offsetof(Foo, two));
}
};
where cpp_offsetof macro (it is considered to be correct) is:
#define cpp_offsetof(s, m) (((size_t)&reinterpret_cast<const volatile char&>((((s*)(char*)8)->m))) - 8)
The C++ standard doesn't guarantee it, but can we assume that members are distanced by a fixed offset and above is correct, cross-platform solution?
100% compatible solution would be:
struct Foo {
T one,
T two,
T three
T &operator [] (int i) {
const size_t offsets[] = { cpp_offsetof(Foo, one), cpp_offsetof(Foo, two), cpp_offsetof(Foo, three) };
return *(T*)((size_t)this + offsets[i]);
}
};
[edit]standard, compliant and faster version was presented by snk_kid using pointers to data members[/edit]
but it requires extra lookup table which I'm trying to avoid.
//EDIT
And one more. I cannot use just an array and constants to index these fields, they have to be named fields of a struct (some macro requires that).
//EDIT2
Why those have to be named fields of a struct? What is the macro? It is settings system of a bigger project. Simplifying it's sth like this:
struct Foo {
int one;
int two;
}
foo;
struct Setting { void *obj, size_t filed_offset, const char *name, FieldType type }
#define SETTING(CLASS, OBJ, FIELD, TYPE) { OBJ, cpp_offsetof(CLASS, FIELD), #OBJ #FIELD, TYPE }
Setting settings[] = {
SETTING(Foo, foo, one, INT_FIELD),
SETTING(Foo, foo, two, INT_FIELD)
};
And once again: I'm not looking form 100% compatible solution but 99%. I'm asking if we can expect that some compilers will put non-uniform padding between uniform fields.
Your code doesn't work with NON-POD types such those which using virtual member functions. There is a standard compliant (and efficient) way to achieve what you're trying to do, using pointer to data members:
template< typename T >
struct Foo {
typedef size_t size_type;
private:
typedef T Foo<T>::* const vec[3];
static const vec v;
public:
T one;
T two;
T three;
const T& operator[](size_type i) const {
return this->*v[i];
}
T& operator[](size_type i) {
return this->*v[i];
}
};
template< typename T >
const typename Foo<T>::vec Foo<T>::v = { &Foo<T>::one, &Foo<T>::two, &Foo<T>::three };
Just make sure you use const every with the table of pointer to data-members to get optimizations. Check here to see what I'm talking about.
Another way is with template specialization if what you are trying to achieve is still a compile time feature.
class Foo {
T one;
T two;
T three;
};
template <int i> T & get(Foo& foo);
template T& get<1>(Foo& foo){ return foo.one;}
template T& get<2>(Foo& foo){ return foo.two;}
template T& get<3>(Foo& foo){ return foo.three;}
It would be nice to define get as a member function but you cannot
specialize template member functions. Now if this is only a compile time
expansion you are looking for then this will avoid the lookup table
issue of one of the previous posts. If you need runtime resolution
then you need a lookup table obviously.
--
Brad Phelan
http://xtargets.heroku.com
You might be able to achieve what you want using an array to hold the data (so you can get indexed access without using a lookup table) and having references to the various array elements (so you can have 'named' elements for use by your macros).
I'm not sure what your macros require, so I'm not 100% sure this will work, but it might. Also, I'm not sure that the slight overhead of the lookup table approach is worth jumping through too many hoops to avoid. On the other hand, I don't think the approach I suggest here is any more complex than the table-of-pointers approach, so here it is for your consideration:
#include <stdio.h>
template< typename T >
struct Foo {
private:
T data_[3];
public:
T& one;
T& two;
T& three;
const T& operator[](size_t i) const {
return data_[i];
}
T& operator[](size_t i) {
return data_[i];
}
Foo() :
one( data_[0]),
two( data_[1]),
three( data_[2])
{};
};
int main()
{
Foo<int> foo;
foo[0] = 11;
foo[1] = 22;
foo[2] = 33;
printf( "%d, %d, %d\n", foo.one, foo.two, foo.three);
Foo<int> const cfoo( foo);
printf( "%d, %d, %d\n", cfoo[0], cfoo[1], cfoo[2]);
return 0;
}
You can't because the compiler can add dead bytes between members to allow padding.
There is two ways to do what you want.
The first is to use your compiler-specific keyword or pragma macro that will force the compiler to not add padding bytes. But that is not portable.
That said it might be the easiest way to do it with your macro requirements, so I suggest you explore this possibility and prepare for adding more pragma when using different compilers.
The other way is to first make sure your members are aligned, then add accessors :
struct Foo {
T members[ 3 ]; // arrays are guarrantied to be contigu
T& one() { return members[0]; }
const T& one() const { return members[0]; }
//etc...
};
If you're sure the compilers you're using are going to generate the right code for this (and I'd imagine they would, assuming T isn't a reference type anyway) the best thing to do is put in some kind of check that the struct is laid out as you think. I can't think of any particular reason to insert non-uniform padding between adjacent members of the same type, but if you check the struct layout by hand then you'll at least know if it happens.
If the struct (S) has exactly N members of type T, for example, you can check at compile time that they are tightly packed simply using sizeof:
struct S {
T a,b,c;
};
extern const char check_S_size[sizeof(S)==3*sizeof(T)?1:-1];
If this compiles, then they're tightly packed, as there's no space for anything else.
If you just happen to have N members, that you want to ensure are placed directly one after the other, you can do something similar using offsetof:
class S {
char x;
T a,b,c;
};
extern const char check_b_offset[offsetof(S,b)==offsetof(S,a)+sizeof(T)?1:-1];
extern const char check_c_offset[offsetof(S,c)==offsetof(S,b)+sizeof(T)?1:-1];
Depending on the compiler, this might have to become a runtime check, possibly not using offsetof -- which you might want to do for non-POD types anyway, because offsetof isn't defined for them.
S tmp;
assert(&tmp.b==&tmp.a+1);
assert(&tmp.c==&tmp.b+1);
This doesn't say anything about what to do if the asserts start failing, but you should at least get some warning that the assumptions aren't true...
(By the way, insert appropriate casts to char references and so on where appropriate. I left them out for brevity.)

Is this a legitimate use of reinterpret_cast and if not how do I do this? [duplicate]

This question already has answers here:
Is casting std::pair<T1, T2> const& to std::pair<T1 const, T2> const& safe?
(3 answers)
Closed 8 years ago.
This code demonstrates the problem I'm trying to solve:
#include <map>
class Point
{
public:
float m_x;
float m_y;
};
typedef std::set<Point *> PointSet;
typedef std::set<const Point * const> ConstPointSet;
float GetMinimumRange(const ConstPointSet &pointSet)
{
float minimumRange(0.0f);
// find the smallest distance between any pair of points in the set
return minimumRange;
}
float GetMinimumRangeWrong(const PointSet &pointSet)
{
PointSet::iterator first(pointSet.begin());
Point * point(*first);
point->m_x = 42.0f; // I want to prevent this
return 0.0f;
}
class PointSet_
{
public:
std::set<Point *> m_pointSet;
float GetMinumumRange() const
{
PointSet::iterator first(m_pointSet.begin());
Point * point(*first);
point->m_x = 42.0f; // I want to prevent this
return 0.0f;
}
};
void test()
{
PointSet myPointSet;
// Add some points to my set
// This fails because the compiler states it can't convert from PointSet to ConstPointSet.
//float minimumRange1(GetMinimumRange(myPointSet));
// reinterpret_cast<> is the only cast that works here, const_cast fails with the same
// complaint as the line above generates
ConstPointSet *myConstPointSet(reinterpret_cast<ConstPointSet *>(&myPointSet));
float minimumRange1(GetMinimumRange(*myConstPointSet));
float minimumRange2(GetMinimumRangeWrong(myPointSet));
}
I want to create a routine that takes a PointSet, evaluates the minimum range between any pair of Points in the set, but that it guarantees that it won't modify the PointSet passed to it in any way at all. It can't modify the members of any referenced Point, it can't change the pointers themselves, nor can it add or remove members from the set
The issue is that the compiler correctly views PointSet and ConstPointSet as different types because of the difference of const qualifiers of the inner type, and therefore refuses to cast between them, even though I'm only adding const qualifiers.
I tried creating a class to contain a PointSet, and creating a const member function, but even in there it allows modification to one of the inner Points. At least MSVC will compile that without complaint. I'll confess I was quite surprised about this.
The only way I've found that works is to use a reinterpret_cast<> to convert a pointer to a PointSet to a pointer to a ConstPointSet. The standard does note that reinterpret_cast<> can be used to add const qualifiers, but does that apply in this case?
If not, is there any way to do what I want? I realize that good code discipline can be used to ensure that GetMinimumRange() doesn't modify the passed PointSet, but I'd like to get those const qualifiers in there for two reasons.
They will ensure that if anyone ever modifies GetMinimumRange() they can't cause it to modify the PointSet.
It will allow the compiler to optimize over the call to GetMinimumRange(). In the absence of the const qualifiers, no assumptions can be made at the calling site regarding values that could be cached across the call, thus possibly leading to redundant fetches of data.
There is no straightforward way, because constness does not propagate through pointers. In a const PointSet, it's the pointers themselves that are const, not the objects they point to. And, like you've discovered, const Point * is a different type from Point *, so std::set<const Point *> is a different type from std::set<Point *>.
I don't like the reinterpret_cast of a STL structure. That is scary to me. STL does all kinds of optimizations based on the type of template parameters. std::vector<bool> being an extreme example. You'd think that std::set<T *> and std::set<const T *> would be laid out the same because they are both pointers, but I wouldn't assume so until I read it in the Standard.
If it were a structure I had written myself, and I could easily verify that the cast would work, it would be less scary but still ugly.
You could write a wrapper class that holds a reference to a std::set<Point *> but only allows const access to its pointed-to Points via iterators. If the pointers are guaranteed to be non-null, your iterator can dereference the points directly. I've written it here as a template:
template <typename T>
class PointerSetViewer
{
public:
PointerSetViewer(std::set<T *> const &set) : set(set) {}
struct iterator : public std::iterator<std::forward_iterator_tag, T const>
{
iterator(typename std::set<T *>::const_iterator it) : it(it) {}
T const &operator*() const { return **it; }
T const *operator->() const { return *it; }
iterator &operator++() { ++it; return *this; }
bool operator==(iterator other) { return it == other.it; }
bool operator!=(iterator other) { return it != other.it; }
private:
typename std::set<T *>::const_iterator it;
};
iterator begin() { return iterator(set.cbegin()); }
iterator end() { return iterator(set.cend()); }
private:
std::set<T *> const &set;
};
It's bulky, but it accomplishes your goals without doing anything risky:
float GetMinimumRangeWrong(PointerSetViewer<Point> &pointSet)
{
PointerSetViewer<Point>::iterator first(pointSet.begin());
first->m_x = 42.0f; // does not compile
}
Also if you're using C++11, you can get some nice range-based for loops:
template <typename T>
PointerSetViewer<T> view_set(std::set<T *> const &set) {
return PointerSetViewer<T>(set);
}
for (Point const &p : view_set(myPointSet)) {
// whatever...
}
Baroque? Yes, but if one piece of baroque library code lets you write 100 pieces of beautiful application code with better type checking, it's probably worth it.
Edit: this doesn't work for set. As pointed out in comments, a non-const set is defined to hold const T, so there is actually nothing we can do.
At this stage I don't see a viable solution other than making PointSet_ actually wrap the set properly, i.e. have the set be private and be careful in your public functions.
Here is a solution I came up with; make the set contain a little wrapper which will propagate the const-ness of itself onto the pointer.
I would have thought there would be a pre-existing class that does this, but none of the std smart pointer classes seem to.
#include <iostream>
#include <set>
template<typename T>
struct qualifier_ptr
{
T *operator->() { return ptr; }
T const *operator->() const { return ptr; }
operator T*() { return ptr; }
operator T const*() const { return ptr; }
qualifier_ptr(T *p): ptr(p) {}
private:
T *ptr;
};
struct Point
{
float m_x;
float m_y;
};
struct PointSet
{
typedef std::set< qualifier_ptr<Point> > SetType;
SetType points;
float foo() const
{
//Point *p = *points.begin(); // error
Point const *p = *points.begin(); // OK
return 0;
}
};
int main()
{
PointSet ps;
PointSet const &cps = ps;
ps.foo(); // OK
cps.foo(); // OK
}
I normally don't like to use conversion operators but it seems appropriate here.
As you stated in the comments that the set is built only once per session, I'd suggest just creating the ConstPointerSet by making a copy:
void test()
{
PointSet myPointSet;
// Add some points to my set
ConstPointSet myConstPointSet{ begin(myPointSet), end(myPointSet) };
float minimumRange1(GetMinimumRange(myConstPointSet));
}
Or wrapp it into a function:
ConstPointSet toConst(const PointSet& pSet){
return ConstPointSet{ cbegin(pSet), cend(pSet) };
}
If you don't need the semantics of a set I'd recommend using a std::vector instead, which is much more efficient to copy or traverse.

Is there any way to call class operators with out using * , in a pointer to class type?

Is it possible to call operator[] with out using * when I have a pointer to the class ?
class MyClass
{
public:
void operator[](int n)
{
cout<<"In []";
}
};
int main()
{
MyClass *a=new MyClass;
(*a)[2];//work
a[2];//It just do some pointer arithmetic ...too bad :((
}
Yes, you should be able to use the -> operator, like this:
a->operator[] (2);
Demo on ideone.
If all you need is eliminating the asterisk, this should do the trick. If you are aiming for a better readability, this isn't of much help - you need to either avoid the pointer, or to use a regular member function:
class MyClass
{
public:
void operator[](int n)
{
cout<<"In []";
}
// Add a regular function for use with pointers
// that forwards the call to the operator[]
void at(int n) { (*this)[n]; }
};
Now you can write a->at(2);
(Demo on ideone).
template<typename LHS> struct at_lhs_t { LHS lhs; };
static struct at_t {} at;
template<typename LHS>
at_lhs_t<LHS> operator%( LHS&& lhs, at_t )
{ return {std::forward<LHS>(lhs)}; }
template<typename LHS, typename RHS>
auto operator%( at_lhs_t<LHS>&& lhs, RHS&& rhs )
->decltype( (std::forward<LHS>(lhs.lhs))->operator[](std::forward<RHS>(rhs)) )
{ return ( (std::forward<LHS>(lhs.lhs))->operator[](std::forward<RHS>(rhs)) ); }
class MyClass
{
public:
void operator[](int n)
{
std::cout<<"In []";
}
};
int main()
{
MyClass *a=new MyClass;
a %at% 2;
}
live example, but this is probably not what you want either.
In practice, just use the *. The * (as well as ->s) help remind you that the left hand side must be first checked for validity before it can be used in most contexts. After you check that your pointer is valid, you can dereference and store it in a reference for the duration of the current scope, and use [] to your heart's content.
The (*a) brackets get to be a bit annoying. On the other hand, possibly you should also avoid using pointers as much: modern C++ has moved away from using pointers, and would rather wrap things in smart pointers or pImpl wrappers and store the data in value-semantic types.
Then you only deal with pointers inside your smart pointer storage types, and when you want reseatable indirection to resources held elsewhere. Your nice and pretty pImpl wrappers are used more often, and they act like values even if most of their state is held dynamically.

Accessing struct members with array subscript operator

Let have a type T and a struct having ONLY uniform elements of T type.
struct Foo {
T one,
T two,
T three
};
I'd like to access them in fallowing way:
struct Foo {
T one,
T two,
T three
T &operator [] (int i)
{
return *(T*)((size_t)this + i * cpp_offsetof(Foo, two));
}
};
where cpp_offsetof macro (it is considered to be correct) is:
#define cpp_offsetof(s, m) (((size_t)&reinterpret_cast<const volatile char&>((((s*)(char*)8)->m))) - 8)
The C++ standard doesn't guarantee it, but can we assume that members are distanced by a fixed offset and above is correct, cross-platform solution?
100% compatible solution would be:
struct Foo {
T one,
T two,
T three
T &operator [] (int i) {
const size_t offsets[] = { cpp_offsetof(Foo, one), cpp_offsetof(Foo, two), cpp_offsetof(Foo, three) };
return *(T*)((size_t)this + offsets[i]);
}
};
[edit]standard, compliant and faster version was presented by snk_kid using pointers to data members[/edit]
but it requires extra lookup table which I'm trying to avoid.
//EDIT
And one more. I cannot use just an array and constants to index these fields, they have to be named fields of a struct (some macro requires that).
//EDIT2
Why those have to be named fields of a struct? What is the macro? It is settings system of a bigger project. Simplifying it's sth like this:
struct Foo {
int one;
int two;
}
foo;
struct Setting { void *obj, size_t filed_offset, const char *name, FieldType type }
#define SETTING(CLASS, OBJ, FIELD, TYPE) { OBJ, cpp_offsetof(CLASS, FIELD), #OBJ #FIELD, TYPE }
Setting settings[] = {
SETTING(Foo, foo, one, INT_FIELD),
SETTING(Foo, foo, two, INT_FIELD)
};
And once again: I'm not looking form 100% compatible solution but 99%. I'm asking if we can expect that some compilers will put non-uniform padding between uniform fields.
Your code doesn't work with NON-POD types such those which using virtual member functions. There is a standard compliant (and efficient) way to achieve what you're trying to do, using pointer to data members:
template< typename T >
struct Foo {
typedef size_t size_type;
private:
typedef T Foo<T>::* const vec[3];
static const vec v;
public:
T one;
T two;
T three;
const T& operator[](size_type i) const {
return this->*v[i];
}
T& operator[](size_type i) {
return this->*v[i];
}
};
template< typename T >
const typename Foo<T>::vec Foo<T>::v = { &Foo<T>::one, &Foo<T>::two, &Foo<T>::three };
Just make sure you use const every with the table of pointer to data-members to get optimizations. Check here to see what I'm talking about.
Another way is with template specialization if what you are trying to achieve is still a compile time feature.
class Foo {
T one;
T two;
T three;
};
template <int i> T & get(Foo& foo);
template T& get<1>(Foo& foo){ return foo.one;}
template T& get<2>(Foo& foo){ return foo.two;}
template T& get<3>(Foo& foo){ return foo.three;}
It would be nice to define get as a member function but you cannot
specialize template member functions. Now if this is only a compile time
expansion you are looking for then this will avoid the lookup table
issue of one of the previous posts. If you need runtime resolution
then you need a lookup table obviously.
--
Brad Phelan
http://xtargets.heroku.com
You might be able to achieve what you want using an array to hold the data (so you can get indexed access without using a lookup table) and having references to the various array elements (so you can have 'named' elements for use by your macros).
I'm not sure what your macros require, so I'm not 100% sure this will work, but it might. Also, I'm not sure that the slight overhead of the lookup table approach is worth jumping through too many hoops to avoid. On the other hand, I don't think the approach I suggest here is any more complex than the table-of-pointers approach, so here it is for your consideration:
#include <stdio.h>
template< typename T >
struct Foo {
private:
T data_[3];
public:
T& one;
T& two;
T& three;
const T& operator[](size_t i) const {
return data_[i];
}
T& operator[](size_t i) {
return data_[i];
}
Foo() :
one( data_[0]),
two( data_[1]),
three( data_[2])
{};
};
int main()
{
Foo<int> foo;
foo[0] = 11;
foo[1] = 22;
foo[2] = 33;
printf( "%d, %d, %d\n", foo.one, foo.two, foo.three);
Foo<int> const cfoo( foo);
printf( "%d, %d, %d\n", cfoo[0], cfoo[1], cfoo[2]);
return 0;
}
You can't because the compiler can add dead bytes between members to allow padding.
There is two ways to do what you want.
The first is to use your compiler-specific keyword or pragma macro that will force the compiler to not add padding bytes. But that is not portable.
That said it might be the easiest way to do it with your macro requirements, so I suggest you explore this possibility and prepare for adding more pragma when using different compilers.
The other way is to first make sure your members are aligned, then add accessors :
struct Foo {
T members[ 3 ]; // arrays are guarrantied to be contigu
T& one() { return members[0]; }
const T& one() const { return members[0]; }
//etc...
};
If you're sure the compilers you're using are going to generate the right code for this (and I'd imagine they would, assuming T isn't a reference type anyway) the best thing to do is put in some kind of check that the struct is laid out as you think. I can't think of any particular reason to insert non-uniform padding between adjacent members of the same type, but if you check the struct layout by hand then you'll at least know if it happens.
If the struct (S) has exactly N members of type T, for example, you can check at compile time that they are tightly packed simply using sizeof:
struct S {
T a,b,c;
};
extern const char check_S_size[sizeof(S)==3*sizeof(T)?1:-1];
If this compiles, then they're tightly packed, as there's no space for anything else.
If you just happen to have N members, that you want to ensure are placed directly one after the other, you can do something similar using offsetof:
class S {
char x;
T a,b,c;
};
extern const char check_b_offset[offsetof(S,b)==offsetof(S,a)+sizeof(T)?1:-1];
extern const char check_c_offset[offsetof(S,c)==offsetof(S,b)+sizeof(T)?1:-1];
Depending on the compiler, this might have to become a runtime check, possibly not using offsetof -- which you might want to do for non-POD types anyway, because offsetof isn't defined for them.
S tmp;
assert(&tmp.b==&tmp.a+1);
assert(&tmp.c==&tmp.b+1);
This doesn't say anything about what to do if the asserts start failing, but you should at least get some warning that the assumptions aren't true...
(By the way, insert appropriate casts to char references and so on where appropriate. I left them out for brevity.)