Victor Bazarov
11/11/2008 1:02:00 AM
Hendrik Schober wrote:
> Hi,
>
> the following code is the boiled-down version of something that
> gives us trouble on GCC. In the real code, 'F' is some fixed-size
> array class, 'Q' and 'V' are derived classes that probably aren't
> very interesting for this question, 'B' is a 4x4 matrix base and
> 'M' is the matrix class in use.
> The code works with VC and several versions of GCC, but in one
> test using GCC it breaks with nonsensical values for the 2nd-4th
> row of the matrix. The trouble is, it only does so when no
> additional statements are added to the function (that's mimicked
> here in 'main()') and not, if the code is run under a debugger.
> The problem doesn't appear with this boiled-down example code, but
> looking at the code and the symptoms, I have the suspicion that
> this invokes UB anyway and would like the group's opinion whether
> my suspicion is right.
> 'M<T>::data()'assumes that all of data members of the inherited
> 'F's are laid out contiguously. Can we assume this regarding to
> the standard?
No. Who knows what kind of padding your compiler sticks in your
'F' class right after the 'val'...
> My suspicion is, we can't. But I don't know why we
> either can and can't. I believe struct layout in C is rather
> strict and reliable, but I don't know what's needed of the C++-
> only features (inheritance, templates etc.) is needed to make
> C++ deviate from that.
>
> TIA,
>
> Schobi
>
>
> --8<----8<----8<----8<----8<----8<----8<----8<----8<----8<----8<----8<----8<--
>
> #include <iostream>
> #include <string>
>
> template< typename T >
> struct F {
> T val[4];
> F()
> {val[0]=T();val[1]=T();val[2]=T();val[3]=T();} F(T a0, T a1, T
> a2, T a3) {val[0]=a0 ;val[1]=a1
> ;val[2]=a2 ;val[3]=a3 ;} T& operator[](std::size_t i) {return
> val[i];} const T& operator[](std::size_t i) const {return val[i];} T*
> begin() {return
> &val[0];} const T* begin() const {return
> &val[0];} T* end () {return
> &val[4];} const T* end () const {return
> &val[4];} };
Your use of 'flowed' format is apparent here... :-)
But let me just say, ugh! Why couldn't you initialise the 'val' at
least in the default constructor? You could do
F() : val() {}
right?
>
> template< typename T >
> struct Q : public F<T> {
> Q() : F() {}
> Q(T a0, T a1, T a2, T a3) : F(a0,a1,a2,a3) {}
> };
>
> template< typename T >
> struct V : public Q<T> {
> V() : Q() {}
> V(T a0, T a1, T a2, T a3) : Q(a0,a1,a2,a3) {}
> };
>
> template< typename T >
> struct B : public V< V<T> > {
> typedef V<T> Row;
I don't think you're using this typedef. Are you?
>
> B() {assign( 0, 0, 0, 0
> , 0, 0, 0, 0
> , 0, 0, 0, 0
> , 0, 0, 0, 0
> );}
> B( T a00, T a01, T a02, T a03
> , T a10, T a11, T a12, T a13
> , T a20, T a21, T a22, T a23
> , T a30, T a31, T a32, T a33 ) {assign( a00, a01,
> a02, a03 ,
> a10, a11, a12,
> a13 , a20,
> a21, a22, a23 , a30, a31, a32, a33 );}
> void assign( T a00, T a01, T a02, T a03
> , T a10, T a11, T a12, T a13
> , T a20, T a21, T a22, T a23
> , T a30, T a31, T a32, T a33 )
> {
>
> this->val[0][0]=a00;this->val[0][1]=a01;this->val[0][2]=a02;this->val[0][3]=a03;
> this->val[1][0]=a10;this->val[1][1]=a11;this->val[1][2]=a12;this->val[1][3]=a13;
> this->val[2][0]=a20;this->val[2][1]=a21;this->val[2][2]=a22;this->val[2][3]=a23;
> this->val[3][0]=a30;this->val[3][1]=a31;this->val[3][2]=a32;this->val[3][3]=a33;
> }
> };
>
> template< typename T >
> struct M : public B<T> {
> M() : B<T>(), i() {}
> const T* data() const {return
> B<T>::begin()->begin();} int i;
> };
>
> int main()
> {
> M<float> matrix;
> matrix.assign(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15);
> const float* data = matrix.data();
>
> for( std::size_t i = 0; i < 16; ++i ) {
> std::cout << data[i] << '\n';
> }
>
> return 0;
> }
You could simply verify that the layout is like you expect. In the
constructor of the 'V' class, for example, you could simply do
if (sizeof(this) != sizeof(T) * 4)
throw "A-a-a-a-a-a-a";
V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask