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.
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 an immediately-invoked 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 lambda is not strictly necessary. If you prefer this can be a constexpr
named function to be called in the initializer as well.
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. In particular, if you don't use a lambda, you are then responsible for assuring that the function that you call is actually marked constexpr
.
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 or a header shared with C sources).
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()
, &tmp_pgdir[0]
or std::to_address(std::begin(tmp_pgdir))
instead of just std::begin(tmp_pgdir)
, &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. std::begin(tmp_pgdir)
directly may or may not work depending on implementation details.
To get a pointer to one-past-the end use std::to_address(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 or implementation-dependent.