Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Non-trivial designated initializers

I am currently working on an OS and working on the utils section. Specifically, on the tmp dir. I have came up with this code,


#include <stdint.h>
#define HIGH_KERNEL   0xc0000000
#define PAGE_SIZE     4096
#define PAGE_PRESENT  (1 << 0)
#define PAGE_WRITE    (1 << 1)
#define PAGE_HUGE     (1 << 7)

__attribute__((__aligned__(PAGE_SIZE)))
uint32_t tmp_pgdir[1024] = {
  [0] = 0x0 | PAGE_PRESENT | PAGE_WRITE | PAGE_HUGE,
  [HIGH_KERNEL >> 22] = 0x0 | PAGE_PRESENT | PAGE_WRITE | PAGE_HUGE,
};

Now, afaik non-trivial designated initializers are not supported in C++, so one option I thought of is compiling the file as a C file. Now, I wonder, is there a workaround? Is it actually the ideal solution?

Thanks in advance.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

>Solution :

The [/*...*/] = initializer syntax for arrays isn’t standard C++ at all.

But you don’t need it in C++. You can just initialize the array in the same way that you would do it at block scope with the help of a lambda and std::array:

alignas(PAGE_SIZE) constinit auto tmp_pgdir = []{
  std::array<std::uint32_t, 1024> ret{}; // {} initializes to zero
  ret[0] = 0x0 | PAGE_PRESENT | PAGE_WRITE | PAGE_HUGE;
  ret[HIGH_KERNEL >> 22] = 0x0 | PAGE_PRESENT | PAGE_WRITE | PAGE_HUGE;
  return ret;
}();

alignas(PAGE_SIZE) is the standard-C++ form for the alignment attribute.

constinit assures that the initialization happens at compile-time, not runtime. It makes the compiler complain if initialization at compile-time is not possible.

std::array has the benefit of having the normal function passing behavior of non-array object types, so it can be returned from the function directly.

The above requires C++20 for constinit, otherwise only C++14. But without constinit you are yourself responsible for making sure that the initialization can be done at compile-time, i.e. is a constant expression.

All of the #define constants can be constexpr auto variables in C++ instead as well. There is no good reason to #define such constants in C++11 and later (except if you need them in the preprocessor).

Also, if you don’t intent to modify tmp_pgdir, then declare it constexpr instead of constinit. This, in addition to assuring compile-time initialization, will make the variable non-modifiable as well and gets rid of the C++20 requirement for constinit.

In order to obtain a pointer to the beginning of the array use tmp_pgdir.data() or &tmp_pgdir[0] instead of just &tmp_pgdir or tmp_pgdir. &tmp_pgdir will probably also work if the target isn’t aware of the pointer type, but it is formally wrong. To get a pointer to one-past-the end use &*std::end(tmp_pgdir) or tmp_pgdir.data() + std::size(tmp_pgdir). Again, just &tmp_pgdir + 1 or std::end(tmp_pgdir) or &tmp_pgdir[std::size(tmp_pgdir)] will probably work as well, but are formally incorrect.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading