Alignment of a simple class to allow array access without UB
Asked Answered
O

0

1

Suppose I have the following simple class:

    struct  employee{
        std::string name;
        short salary;
        std::size_t age;
        employee(std::string name, short salary, std::size_t age) : name{name}, salary{salary}, age{age}{}
    };

Since I want array-like access to the name member of employee in an array of employees, I need for example that the offsets are divisible:

    static_assert( sizeof(employee) % sizeof(std::string) == 0, "!" );

In order to ensure that I am using the alignas directive:

    struct alignas(sizeof(std::string)) employee{
        std::string name;
        short salary;
        std::size_t age;
        employee(std::string name, short salary, std::size_t age) : name{name}, salary{salary}, age{age}{}
    };

Which seems to do the job (now the static_assert above passes).

However when I turned on the clang UB (undefined behavior sanitizer) and I try to construct an array of this aligned version of the class clang detects an error:

SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/bits/move.h:139:31 in 
/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/ext/new_allocator.h:153:10: runtime error: member call on misaligned address 0x0000022de1b0 for type 'employee', which requires 32 byte alignment
0x0000022de1b0: note: pointer points here
 00 00 00 00  c0 e1 2d 02 00 00 00 00  05 00 00 00 00 00 00 00  44 61 76 69 64 00 00 00  00 00 00 00

What would be then the correct way to allow a compatible alignment of employee and the name members? (so member of arrays can be accessed by pointer std::string* arithmetic)

BONUS question: How can all the members be aligned to allow array access on all members of an array of employees.

For more details see here: Align/offset specific members of a struct

Basically I am noting that the solutions that worked are UB according to clang and I am looking for alternatives.


By array access of members I mean being able to this:

employee ARRAY[2];
std::string* p = &ARRAY[0].name;
std::string& p2 = *(p + 2); // can be +3 or any necessary integer
assert( &p2 == &ARRAY[1].name );

Note that I found this worked (in my system), does the job wrt to matching strides and clang doesn't say is UB:

    struct employee{
        std::string name;
        short salary;
        std::size_t age;
        char dummy[9];
        employee() = default;
    }

This is the only option I found so far that doesn't produce UB. I wonder if there is a better way still.

The most idiomatic path seems to use alignas but it also triggers UB according to clang.

    struct employee{
        std::string name alignas(32);
        short salary;
        std::size_t age;
        employee(std::string name, short salary, std::size_t age) : name{name}, salary{salary}, age{age}{}
    };
Oddson answered 11/6, 2020 at 5:53 Comment(8)
I have a feeling this is an XY-problem, am I correct? Since, if you use std:string *string your problem is gone.Colchis
Yes, I want to be able to do this employee ARRAY[2]; std::string* p = &ARRAY[0].name; std::string& p2 = *(p + 2); assert( &p2 == &ARRAY[1].name ); and that is why I need employee size to match (be divisible) with the size of std::string.Oddson
Yeah, that won't work. *(p + 2); will offset you halfway the next struct since you only increment sizeof(std:string) omitting salary and age. Just use iterate on the struct.Colchis
That is why I need to customize the padding/alignment etc. I need to interface that work with pointers (arrays with strides).Oddson
This still feels like an XY problem to me. Nobody should need this. There is an iterator pattern for this.Colchis
Unfortunately one needs to interact with C-functions that process stuff vectorially (as c-arrays). So, yes it is a XY problem, X is the access as strided c-arays (see addition to my question). If iterator pattern solved all the problems we wouldn't need manually controlled alignment in the first place.Oddson
What is C code going to do with a std:string? it can't use it. You need to explain the actual problem you;re trying to solve. Show the C code that is somehow going to need C++ data and how it reads it. There are many options here.Colchis
std::string is just an example for a non-small datatype, I have a data structure that is C and Fortran compatible that counts the strides in the same units as the data type and not bytes. Basically I am asking here why the solutions suggested here #57424650 produce UB when checked by clang.Oddson

© 2022 - 2024 — McMap. All rights reserved.