0

In C++11, we now have the alignas keyword, which can be used to define a new type that is simply an existing type, but with stricter alignment, for example by typedef:

typedef char Maximally_Aligned_Char alignas( max_align_t );

Is there a way programmatically, given a typename T, to determine the original, "natural" alignment of the type? Something like the following conceptual natural_alignment_of type_trait that would compile:

size_t natural_char_alignment = natural_alignment_of< Maximally_Aligned_Char >::value;
static_assert( natural_char_alignment == alignof( char ) );

Background:

I'm writing templated code to act on all scalar-types. Generally with integers, ( sizeof( T ) == alignof( T ) ) is true, but with official alignas support, I don't think I can make this assumption anymore.

Speculation:

Perhaps something like std::decay would work? Testing code, I see that G++4.8 warns about "ignoring attributes on template argument", which sounds nice and dangerous.

Charles L Wilcox
  • 1,126
  • 8
  • 18
  • Just use `alignof`, like you've used it already: `alignof(Maximally_Aligned_Char)`. You seem to think the typedef of `char` will have different alignment, just because you've used `alignas` in its definition. The answer is, you're wrong. – Nawaz Dec 06 '13 at 18:36
  • What does "natural" alignment of the type mean? And why doesn't `static_assert( sizeof(T) == alignof(T) )` where `T==int` not do what you want? – Yakk - Adam Nevraumont Dec 06 '13 at 18:39
  • @Yakk: `sizeof` is a different thing than `alignof` – Nawaz Dec 06 '13 at 18:42
  • 1
    @Nawaz they both return `std::size_t`. And Charles's problem is that he could no longer assume that they are equal. So, assert they are equal to deal with the problem? I guess if you are writing a library that is intended to be hugely portable: but even then, `static_assert`, wait until you find a single platform where it *fails* to work, and *then* work on a solution now that you have a concrete case to work with... – Yakk - Adam Nevraumont Dec 06 '13 at 18:43
  • 1
    @Yakk: just because they both return a *value* of type `std::size_t` does not mean that said values are equal; I seem to recall a 32 bit platform where `sizeof(double)` was 8 but `alignof(double)` was only 4. – Matthieu M. Dec 06 '13 at 18:50
  • @Yakk `natural_char_alignment = alignof( char );` – Charles L Wilcox Dec 06 '13 at 18:51
  • @Yakk `static_assert( sizeof(T) == alignof(T) )` won't usually compile when `T = int alignas( max_align_t )`. – Charles L Wilcox Dec 06 '13 at 18:52
  • @Yakk: `sizeof(T)` doesn't return *type*, it returns *value*, so does `alignof(T)`. The *values* may not be same. The types are same, is irrelevant here. – Nawaz Dec 06 '13 at 18:59
  • @Nawaz yes? Which makes `static_assert( sizeof(T) == alignof(T) )` a statement that would compile for most `T` without `alignas`... – Yakk - Adam Nevraumont Dec 06 '13 at 19:00
  • @Yakk: No. It will NOT compile for most T. – Nawaz Dec 06 '13 at 19:01
  • @CharlesLWilcox You almost certainly had a `T` you augmented with `alignas( max_align_t )` at some point, possibly in your `template` code. Keep track of both that `T` and the `aligned_T`? – Yakk - Adam Nevraumont Dec 06 '13 at 19:02
  • @Nawaz nevermind -- yes, `sizeof(double)` often does not equal `alignof(double)`, let alone for user defined `struct`s. – Yakk - Adam Nevraumont Dec 06 '13 at 19:05
  • @Yakk, in my case, I'm the consumer of user-types, which are simply required to have `( std::is_scalar< T >::value == true )`. – Charles L Wilcox Dec 06 '13 at 19:10
  • 1
    If you want to know the size of an integer type (to a reasonable degree of certainty, subject to differences on unusual implementations), use `(std::numeric_limits::digits + std::numeric_limits::is_signed) / CHAR_BIT`. For floating-point types this doesn't include the bits of exponent, though, I guess you'd have to do something with `max_exponent` and a template logarithm. – Steve Jessop Dec 06 '13 at 19:10
  • @SteveJessop Thanks for the integer-width suggestion; I know how to do this however. As you started to explore, floating-point, pointers to objects & functions, member-pointers to objects & functions are much more tricky. :-) – Charles L Wilcox Dec 06 '13 at 19:17
  • 1
    @CharlesLWilcox: I don't think member pointers typically have equal size and alignment anyway, especially for pointers to members of classes with virtual bases, which get quite large. You say "generally with integers, size equals alignment", but I don't see how you can assume that for all scalar types with a straight face :-) – Steve Jessop Dec 06 '13 at 19:19
  • @SteveJessop I never assumed integers were all scalar types. I stated I'm working on code that works on all scalar types; then I stated that for integers, which are an easy sub-case of scalar-types, that `sizeof(T)==alignof(T)`. – Charles L Wilcox Dec 06 '13 at 19:25

1 Answers1

1

I honestly do not understand what you could do with your natural_alignment trait.

If I define a type with an alignment of 16, then I expect all instances of this type to have an alignment of 16. You are not allowed to break this contract.

Since you already know about alignof, then why don't you simply use it ? Even though in theory one could conceivably use non-power of 2 alignments, in practice it would be extremely brittle, so all you have to do is to take std::max(sizeof(T), alignof(T)) as a basis for your computations and off you go.

Note: it might mean that the data is packed less tightly, and padding is introduced, but why should you care ? You did your best within the user's constraints.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • My question is a 'how can I', not a 'why would I'. – Charles L Wilcox Dec 06 '13 at 19:06
  • But, accessing the value-representation of types is one legitimate area. `std::aligned_storage< sizeof(T), alignof(T) >` is wasteful for types that are over-aligned, say for SIMD, or cache-line friendliness. `std::aligned_storage< sizeof(T), natural_alignment_of::value >` is much better. – Charles L Wilcox Dec 06 '13 at 19:08
  • 1
    @CharlesLWilcox: so what, you don't believe the user really meant to say `Maximally_Aligned_Char`, you're going to use `char` instead with it's normal alignment? What's the reason for not just doing what your template parameter tells you to do? If that's inefficient, well, they shouldn't have added the alignment if they didn't want it. – Steve Jessop Dec 06 '13 at 19:16
  • @SteveJessop Data serialization is a different problem from efficient number-crunching; but both have to happen on the same types' value-representation. `natural_alignment_of` is useful for the former; `alignof` is useful for the latter, and typically the default. – Charles L Wilcox Dec 06 '13 at 19:21
  • @CharlesLWilcox: and my answer is *don't do that*. If all you use are POD types, then you can simply copy it into a raw memory area (using `memcpy`) and not worry about alignments; if you ever present an address/reference to the user though, then you have to respect the alignment **she required**. – Matthieu M. Dec 06 '13 at 19:21