0

If I have an structure (e.g. employee below), an array of such structure induces strided arrays of all the members of the structure only if the size of the structure is a (least) common multiple (LCM) of the size all members.

Otherwise there will be pointers to specific member instances in the array that will not have integral pointer distances (measured in the size of the member class).

So, for example, given this structure:

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

A std::vector<employee> v (or an array employee[N] for that matter) induces a strided array of salary members (with stride sizeof(employee)/sizeof(short)) and also a strided arrays of age.

That is, an array of salaries is random accessed by &(v.data()->salary) + sizeof(employee)/sizeof(short)* n.

However it doesn't induce a stride array of names, because sizeof(employee) (=48) is not a multiple of sizeof(std::string) (32) (in my system).

Of course, I could define the struct in this other way to allow this:

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

I am wondering if finding this proper alignas argument is the only way to achieve this. Also, if there is an automatic way to obtain that number with out manually having to find the common multiple.

I seems that the most general way to do this, without reflection, is to do something like:

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

That is, I have to enumerate all the members in advance. I could use constexpr std::lcm by chaining it several times).

Is this the proper way to do it?

Also, one can always find pathological cases in which this doesn't even work because there are extra restrictions that the alignment needs to be a power of 2 (in some systems). In which case the common multiple needs to be also a power of 2 and can that be a huge number:

using password_type = std::array<char, 103>; // suppose I cannot change the 103

struct alignas(std::lcm(std::lcm(std::lcm(sizeof(std::string), sizeof(short)), sizeof(std::size_t)), sizeof(password_type))) employee{ // bad alignment
    password_type password;
    std::string name;
    short salary;
    std::size_t age;
    employee(short salary, std::size_t age) : salary{salary}, age{age}{}
};

...error: requested alignment ‘3296’ is not a positive power of 2

For the alignment LCM to work I have to manually change the alignment of the specific member or add constexpr ceiling to the nearest power of 2.

struct alignas(std::lcm(std::lcm(std::lcm(sizeof(std::string), sizeof(short)), sizeof(std::size_t)), 128)) employee{ // bad alignment
    alignas(128) password_type password;
    std::string name;
    short salary;
    std::size_t age;
    employee(short salary, std::size_t age) : salary{salary}, age{age}{}
};

However that still doesn't solve the problem because alignas is not part of the password_type, it seems that the only solution then is to have a version of std::array that also takes an internal alignment argument! std::aligned_array<char, 103, 128>.

Ok, I still could do this, but at the cost of modifying other classes that where not coupled to employee initially.

struct alignas(128) password_type : std::array<char, 103>{};

and it might end up working. But it is a lot of manual work, and it could change when I change the system, or add new members, etc.

Is there a more automatic way to do this? or some conventions to follow to make this problem less painful

alfC
  • 14,261
  • 4
  • 67
  • 118
  • Why do you insist that your "strided array" include this division: `sizeof(employee)/sizeof(short)`? That is, why can't you convert to a byte pointer, offset by X bytes, and then convert back? – Nicol Bolas Jul 01 '19 at 00:59
  • @NicolBolas, I could allow "fractional" strides effective (that in itself forces a more complicated arithmetic that for random access could be too expensive) and also a weird conversion to `char*` to do arithmetic. Then I will end up with two types of arrays, one with integer strides and another with fractional strides. – alfC Jul 01 '19 at 02:03
  • @NicolBolas, Not impossible, the problem is that the resulting object is not something that I could feed to a C-function that expects integer strides. I know, I know, C would only deal with basic types, so I could have all alignments to `std::max_align_t` and forget about this, but there are still C-structs that can have this problem. I was curious if there is something better I insists that the strides are integer and there are no weird conversions with pointer arithmetic. – alfC Jul 01 '19 at 02:03
  • "*I could allow "fractional" strides*" It doesn't require fractions; just counting in *bytes* rather than multiples of `sizeof`. "*the problem is that the resulting object is not something that I could feed to a C-function that expects integer strides.*" What API are you trying to feed it into that doesn't take strides in bytes rather than indices for strided arrays? – Nicol Bolas Jul 01 '19 at 02:35
  • @NicolBolas Well, effectively it will will be like having fractions to convert back and forth from address `double`s (for example) as `char` some extra divisions and multiplications will have to take place. Not sure if this is hidden in the pointer conversion (?) these divisions and multiplications can't free operations. Most (all?) numerical libraries (blas, lapack, fftw) have "typed" interfaces, so they will never process numerical elements that are not separated by an integer number of `sizeof(element)`. – alfC Jul 01 '19 at 02:54
  • @NicolBolas For example http://www.netlib.org/lapack/explore-html/d0/d16/sdot_8f.html (incx and incy are effective strides, but not measured in bytes but in "number of elements") – alfC Jul 01 '19 at 03:02
  • @NicolBolas, I think this a historical accident that have to do with that these functions were implemented in fortran, which where "typed" arrays (and there were no pointers). – alfC Jul 01 '19 at 22:36

0 Answers0