Operator: alignof

  • Thread starter Frederick Gotham
  • Start date
F

Frederick Gotham

It is possible and permissible on a given system, that the alignment
requirements of a type are less than the size of that type. For instance,
here's a hypothetical set-up:

int is 4 bytes, but need only be aligned on a 2 byte boundary.

If we had some sort of "alignof" operator, then we would be able to do
the following:

int main()
{
int array[2];

int *p = array;


/* The following function is defined elsewhere. I use it
here to make this snippet as simple as possible. */

AdvancePointerByBytes( p, alignof(int) );

*p = 5;
}


Over on comp.lang.c, Hallvard B Furuseth devised such an operator, and a
lot of people use it.

I've adapted it to C++, and I've written a sample program consisting of
two headers and one source file. Any thoughts on it?


/* BEGINNING OF advance_ptr.hpp */

#ifndef INCLUDE_ADVANCE_PTR_HPP
#define INCLUDE_ADVANCE_PTR_HPP

#include <cstddef>

template<class T>
inline void AdvancePtr( T * &p, std::size_t const bytes )
{
p = reinterpret_cast< T* >(
const_cast<unsigned char *>(
reinterpret_cast<const unsigned char *>(p) + bytes
)
);
}

template<class T>
inline void RetreatPtr( T * &p, std::size_t const bytes )
{
p = reinterpret_cast< T* >(
const_cast<unsigned char *>(
reinterpret_cast<const unsigned char *>(p) - bytes
)
);
}

#endif

/* END OF advance_ptr.hpp */


/* BEGINNING OF alignof.hpp */

#ifndef INCLUDE_ALIGNOF_HPP
#define INCLUDE_ALIGNOF_HPP

#include <cstddef>

template<class T>
struct StructForAlign {

unsigned char first_byte;
T obj;

};

#define alignof(type) ( offsetof( StructForAlign< type >, obj ) )

#endif

/* END OF alignof.hpp */


/* BEGINNING OF main.cpp */


#include <iostream>

#include "advance_ptr.hpp"

#include "alignof.hpp"

int main()
{
long double array[2];

long double *p = array;

AdvancePtr( p, alignof(long double) );

*p = 23235.2352;

std::cout << *p << '\n';
}


/* END OF main.cpp */
 
R

Ron Natalie

Frederick said:
It is possible and permissible on a given system, that the alignment
requirements of a type are less than the size of that type. For instance,
here's a hypothetical set-up:

I don't understand what you are trying to do. Presuming you
have such an operator what would it be used for?
 
A

Alf P. Steinbach

* Frederick Gotham:
It is possible and permissible on a given system, that the alignment
requirements of a type are less than the size of that type. For instance,
here's a hypothetical set-up:

int is 4 bytes, but need only be aligned on a 2 byte boundary.

Yes, of course: for most types and platforms alignment is less than the
size of an object of the type.

C++ as a language does not address alignment.

Alignment requirements arise from the hardware (system bus, memory
interface) and from the OS (some can fix alignment faults, at cost, and
in Windows this can be configured).

If we had some sort of "alignof" operator, then we would be able to do
the following:

int main()
{
int array[2];

int *p = array;


/* The following function is defined elsewhere. I use it
here to make this snippet as simple as possible. */

AdvancePointerByBytes( p, alignof(int) );

*p = 5;
}

What are you trying to do? 'array' is already suitably aligned for
'int'. Also, advancing a pointer by the alignment value for a type
doesn't make the pointer well-aligned for that type. Instead you need
to treat the address as an integer and advance it to the next multiple
of the alignment value, if not already at such multiple. Like

typedef unsigned long ulong; // Assuming ulong is enough.
char array[1000];
ulong const a = (ulong)(&a[0]) + alignment(int) - 1; // ...
int* p = (int*)(a - a%alignment(int));

which for 2^x alignment is usually done by bit-operations.

Over on comp.lang.c, Hallvard B Furuseth devised such an operator, and a
lot of people use it.

I've adapted it to C++, and I've written a sample program consisting of
two headers and one source file. Any thoughts on it?


/* BEGINNING OF advance_ptr.hpp */

#ifndef INCLUDE_ADVANCE_PTR_HPP
#define INCLUDE_ADVANCE_PTR_HPP

#include <cstddef>

template<class T>
inline void AdvancePtr( T * &p, std::size_t const bytes )
{
p = reinterpret_cast< T* >(
const_cast<unsigned char *>(
reinterpret_cast<const unsigned char *>(p) + bytes
)
);
}

This is IMO mixing incompatible levels of abstraction. Alignment is
part of low-level functionality where you don't really care about the
type T that the pointer will finally end up pointing to. It's IMO
Ungood and almost Evil(TM) to offer client code "easy" functionality to
mix such low-level things with higher level code.

Although I haven't used it, on general grounds I recommend you take a
look at the Boost utility referenced else-thread.
 
F

Frederick Gotham

Alf P. Steinbach posted:
Also, advancing a pointer by the alignment value for a type
doesn't make the pointer well-aligned for that type.


If we advance it in terms of bytes, and if the original pointer value is
the address of a valid object, then I believe that it does.

Instead you need to treat the address as an integer and advance it to
the next multiple of the alignment value, if not already at such
multiple. Like

typedef unsigned long ulong; // Assuming ulong is enough.
char array[1000];
ulong const a = (ulong)(&a[0]) + alignment(int) - 1; // ...
int* p = (int*)(a - a%alignment(int));

which for 2^x alignment is usually done by bit-operations.


I don't see why you think that is necessary.

I'll explain my stance by providing two code snipppets which do exactly
the same thing:

Code Snippet (1)
----------------

int main()
{
double d[2];

double *p = d;

++p;

/* Now points to next element */
}


Code Snippet (2)
----------------

int main()
{
double d[2];

double *p = d;

p = (double*)( (char*)p + sizeof *p );

/* Now points to next element */
}


I want the pointer arithmetic to work in terms of bytes, and thus I use
char* -- there's absolutely nothing wrong with that.

Only when we go converting pointer values to integer types do things
start to become non-portable -- and fortunately, there's no need to do
that.

In my original example: Instead of advancing the pointer by the size of
the object, I advance it by the alignment requirement, and thus it should
be perfectly okay to treat that address as the address of an object of
that type.
 
A

Alf P. Steinbach

* Frederick Gotham:
Alf P. Steinbach posted:


If we advance it in terms of bytes, and if the original pointer value is
the address of a valid object, then I believe that it does.

Nope. Say as integer the pointer value is 1234567. The alignment is 4.
Adding 4 yields 1234571, which is just as unaligned: you want 1234570.

Advancing by the alignment only yields a well-aligned pointer if the
original pointer is well-aligned.

In which case alignment is irrelevant.
 
A

Alf P. Steinbach

* Alf P. Steinbach:
* Frederick Gotham:

Nope. Say as integer the pointer value is 1234567. The alignment is 4.
Adding 4 yields 1234571, which is just as unaligned: you want 1234570.

Urgh. Sorry about that 1234570. I leave it as an exercise for the
reader to deduce the /correct/ value you'd want.
 
F

Frederick Gotham

Alf P. Steinbach posted:
* Frederick Gotham:

Nope. Say as integer the pointer value is 1234567. The alignment is
4.
Adding 4 yields 1234571, which is just as unaligned: you want
1234570.


Please use a mono-width font to view this.

First of all, let's assume that:

sizeof(int) == 4
alignof(int) == 2

Here's a diagram of memory. Each memory block constitutes one byte. Four-
byte boundaries are drawn as a column of [4]. Two-byte boundaries are
drawn as a column of [2]. (Of course, a four-byte boundary also
constitutes a two-byte boundary).

Above each arrow is written the address of the byte at that memory
location.


4 5 6 7 8 9 10 11
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
V V V V V V V V
[4]---------[2]---------[4]---------[2]---------[4]
[4]| | |[2]| | |[4]| | |[2]| | |[4]
[4]| | |[2]| | |[4]| | |[2]| | |[4]
[4]| | |[2]| | |[4]| | |[2]| | |[4]
[4]| | |[2]| | |[4]| | |[2]| | |[4]
[4]| | |[2]| | |[4]| | |[2]| | |[4]
[4]---------[2]---------[4]---------[2]---------[4]


If we were to create an array of two ints, then they could be positioned
as follows:

First byte First byte
of first int of second int

4 8
| |
| |
| |
V V
[4]---------[2]---------[4]---------[2]---------[4]
[4]| | |[2]| | |[4]| | |[2]| | |[4]
[4]| | |[2]| | |[4]| | |[2]| | |[4]
[4]| | |[2]| | |[4]| | |[2]| | |[4]
[4]| | |[2]| | |[4]| | |[2]| | |[4]
[4]| | |[2]| | |[4]| | |[2]| | |[4]
[4]---------[2]---------[4]---------[2]---------[4]


Naturally, if we get a pointer to the first int, and increment it, then
the pointer advances by sizeof(int) bytes, i.e. 4 bytes, and the pointer
then points to the second int.

What I'm proposing however is this:
Get a pointer to the first int, and advance it by alignof(int) bytes,
i.e. 2 bytes. The result can be seen as follows:


First byte Pointer First byte
of first int points of second int
to

4 6 8
| | |
| | |
| | |
V V V
[4]---------[2]---------[4]---------[2]---------[4]
[4]| | |[2]| | |[4]| | |[2]| | |[4]
[4]| | |[2]| | |[4]| | |[2]| | |[4]
[4]| | |[2]| | |[4]| | |[2]| | |[4]
[4]| | |[2]| | |[4]| | |[2]| | |[4]
[4]| | |[2]| | |[4]| | |[2]| | |[4]
[4]---------[2]---------[4]---------[2]---------[4]


From the diagram, we can see that the pointer points to an address which
is aligned on a two-byte boundary. This is sufficient alignment for an
int on this system. Therefore the following code is A-OK:

int main()
{
unsigned array[2];

unsigned *p = array;

p = (unsigned*)( (char*)p + alignof(int) );


*p = 5;
}
 
A

Alf P. Steinbach

* Frederick Gotham:
the following code is A-OK:

int main()
{
unsigned array[2];

unsigned *p = array;

p = (unsigned*)( (char*)p + alignof(int) );


*p = 5;
}

Except for formally being UB, yes, it's OK, but I couldn't believe that
was what you meant to do. Why on earth do you want to do this?
 
F

Frederick Gotham

Alf P. Steinbach posted:
* Frederick Gotham:
the following code is A-OK:

int main()
{
unsigned array[2];

unsigned *p = array;

p = (unsigned*)( (char*)p + alignof(int) );


*p = 5;
}

Except for formally being UB,


I contest that assertion. If the value attained by "alignof" is
legitimate, then it is perfectly legitimate to do what I've been doing.
It is no more outlandish than:

#include <cstdlib>

int main()
{
double *p = static_cast<double*>(
std::malloc( 64 * sizeof(double) )
);

p += 47;

*p = 2452.2352;
}

yes, it's OK, but I couldn't believe that
was what you meant to do. Why on earth do you want to do this?


Just for fun! Although perhaps in the future someone will find an
application for it.
 
A

Alf P. Steinbach

* Frederick Gotham:
Alf P. Steinbach posted:
* Frederick Gotham:
the following code is A-OK:

int main()
{
unsigned array[2];

unsigned *p = array;

p = (unsigned*)( (char*)p + alignof(int) );


*p = 5;
}
Except for formally being UB,

I contest that assertion.

You could, but you forgot to; anyway, it's UB. The current language
spec has no notion of alignment, whatsoever. From the point of view of
the standard the effect could be anything, including nasal demons.
 
R

Ron Natalie

Frederick said:
From the diagram, we can see that the pointer points to an address which
is aligned on a two-byte boundary. This is sufficient alignment for an
int on this system. Therefore the following code is A-OK:
I understand you could do that if you knew the alignment, I'm still
looking for an example of WHAT GOOD IT WILL DO YOU to be able to
do that? What purpose does this exercise serve?
 
F

Frederick Gotham

Ron Natalie posted:

I understand you could do that if you knew the alignment, I'm still
looking for an example of WHAT GOOD IT WILL DO YOU to be able to
do that? What purpose does this exercise serve?


95% of my endevours here serve no purpose -- I program out of hobby, not
out of necessity.
 
H

Howard Hinnant

"Alf P. Steinbach said:
* Frederick Gotham:
Alf P. Steinbach posted:
* Frederick Gotham:
the following code is A-OK:

int main()
{
unsigned array[2];

unsigned *p = array;

p = (unsigned*)( (char*)p + alignof(int) );


*p = 5;
}
Except for formally being UB,

I contest that assertion.

You could, but you forgot to; anyway, it's UB. The current language
spec has no notion of alignment, whatsoever. From the point of view of
the standard the effect could be anything, including nasal demons.

The current language does have a notion of alignment. Specific
alignments are implementation defined (not undefined). E.g. 3.9p5:
Object types have alignment requirements (3.9.1, 3.9.2). The alignment
of a complete object type is an implementation-defined integer value
representing a number of bytes; an object is allocated at an address that
meets the alignment requirements of its object type.

-Howard
 
O

Old Wolf

Alf said:
* Frederick Gotham:
int main()
{
int array[2];
int *p = array;
AdvancePointerByBytes( p, alignof(int) );

*p = 5;
}

What are you trying to do? 'array' is already suitably aligned for
'int'. Also, advancing a pointer by the alignment value for a type
doesn't make the pointer well-aligned for that type. Instead you need
to treat the address as an integer

Addresses need not be treatable as integers -- the memory
model may not be flat, and there may be no defined conversion
from pointer to integer, or the conversion may not be homomorphic.
and advance it to the next multiple of the alignment value, if not
already at such multiple.

There could be a system where objects have to be aligned
to an address of the type 4m+1 (supposing there is a flat
memory model).

typedef unsigned long ulong; // Assuming ulong is enough.
char array[1000];
ulong const a = (ulong)(&a[0]) + alignment(int) - 1; // ...
int* p = (int*)(a - a%alignment(int));

Fred's original code is correct: if a pointer is already correctly
aligned, then adjusting it by the alignment size will result in
another pointer which is correctly aligned. In your later post
you seem to be talking as if the original pointer:
int array[2];
int *p = array;

were somehow not correctly aligned for int.
 
A

Alf P. Steinbach

* Howard Hinnant:
Alf P. Steinbach said:
* Frederick Gotham:
Alf P. Steinbach posted:

* Frederick Gotham:
the following code is A-OK:

int main()
{
unsigned array[2];

unsigned *p = array;

p = (unsigned*)( (char*)p + alignof(int) );


*p = 5;
}
Except for formally being UB,
I contest that assertion.
You could, but you forgot to; anyway, it's UB. The current language
spec has no notion of alignment, whatsoever. From the point of view of
the standard the effect could be anything, including nasal demons.

The current language does have a notion of alignment. Specific
alignments are implementation defined (not undefined). E.g. 3.9p5:
Object types have alignment requirements (3.9.1, 3.9.2). The alignment
of a complete object type is an implementation-defined integer value
representing a number of bytes; an object is allocated at an address that
meets the alignment requirements of its object type.

Yes, you're right; the effect of the program is unspecified (as per
5.2.10/7), not UB.
 
A

Alf P. Steinbach

* Old Wolf:
Alf said:
* Frederick Gotham:
int main()
{
int array[2];
int *p = array;
AdvancePointerByBytes( p, alignof(int) );

*p = 5;
}
What are you trying to do? 'array' is already suitably aligned for
'int'. Also, advancing a pointer by the alignment value for a type
doesn't make the pointer well-aligned for that type. Instead you need
to treat the address as an integer

Addresses need not be treatable as integers -- the memory
model may not be flat, and there may be no defined conversion
from pointer to integer, or the conversion may not be homomorphic.
Yes.

and advance it to the next multiple of the alignment value, if not
already at such multiple.

There could be a system where objects have to be aligned
to an address of the type 4m+1 (supposing there is a flat
memory model).

In principle, yes, but the same technique for obtaining an aligned
pointer applies.

typedef unsigned long ulong; // Assuming ulong is enough.
char array[1000];
ulong const a = (ulong)(&a[0]) + alignment(int) - 1; // ...
int* p = (int*)(a - a%alignment(int));

Fred's original code is correct: if a pointer is already correctly
aligned, then adjusting it by the alignment size will result in
another pointer which is correctly aligned.

Yes, but that's not very useful.

In your later post
you seem to be talking as if the original pointer:
int array[2];
int *p = array;

were somehow not correctly aligned for int.

No, I presented an example that could be useful, with 'array' of 'char'.
 
R

Robbie Hatley

Frederick Gotham said:
It is possible and permissible on a given system, that the alignment
requirements of a type are less than the size of that type. For instance,
here's a hypothetical set-up:

int is 4 bytes, but need only be aligned on a 2 byte boundary.

How on Earth could that work? Assuming a little-endian arrangement
(for sake of argument), if you had an array like this:

int Array[2] = {37, 84};

then the most-significant two bytes of MyArray[0] would overlap
in RAM with the least-significant two bytes of MyArray[1]!!!

byte byte byte byte byte byte
0x3a07 0x3a08 0x3a09 0x3a0a 0x3a0b 0x3a0c
Array[0] Array[0] Array[0] Array[0]
Array[1] Array[1] Array[1] Array[1]

Or am I somehow missing what you mean by "alignment"? By "aligned
on a X-byte boundary", do you mean "the absolute numerical value of
&Array[0] must be zero modulo X"? But why should anyone care? That's
all "behind the scenes". Unless you're writing the lowest-level code
for memory management in some OS kernel, I don't see the point.

--
Curious,
Robbie Hatley
Tustin, CA, USA
lonewolfintj at pacbell dot net
(put "[usenet]" in subject to bypass spam filter)
http://home.pacbell.net/earnur/
 
R

Robbie Hatley

First of all, let's assume that:

sizeof(int) == 4
alignof(int) == 2

Get a pointer to the first int, and advance it by alignof(int) bytes,

Why do I find myself wanting to say a rosary and pray? And I'm not
even religious. In fact, I'm an athiest. But dude! That's EVIL!!!
May the Valar protect us!!!

You could do...

#include <iostream>
int main(void)
{
int EvilArray[666];
EvilArray[0] = 37;
EvilArray[1] = 82;
int* Dagger = &EvilArray[0];
Dagger = (int*)((char*)Dagger + 2);
std::cout << (*Dagger) << std::endl;
return 0;
}

Prints 5373952. Ewww. ::shudders:: It's the work of Morgoth!!!

--
Cheers,
Robbie Hatley
Tustin, CA, USA
lonewolfintj at pacbell dot net
(put "[usenet]" in subject to bypass spam filter)
http://home.pacbell.net/earnur/
 
R

Rolf Magnus

Frederick said:
Ron Natalie posted:




95% of my endevours here serve no purpose -- I program out of hobby, not
out of necessity.

So? Ususally, even when doing it as a hobby, people tend to write programs
that do something useful.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,773
Messages
2,569,594
Members
45,120
Latest member
ShelaWalli
Top