A pointer to an item in an array will become dangling if you move the array.
However, an index would still work, because it's relative to the start of the
array. index_ptr<&Header::array, uint32_t>
is an array index into
Header::array
that behaves like a pointer. An advantage is you can't
accidentally index the wrong array. Another is marginaly shorter code than real
indices to traverse complex data structures.
Caution
index_ptr
is more of an exploration of an idea than a properly useful
object. You probably want
offset_pointer instead.
I was so preoccupied with whether or not I could, I didn't stop to think if I should.
Use cases:
- Zero overhead when loading a structure-of-arrays memory mapped file
- Sharing data between address spaces, e.g. shared memory or CUDA
- Compatible with limited languages such as GLSL
Header* header = (Header*)mmap("file"); // pseudocode
// no deserialization work, simply start accessing relational data
header->foos[3]->bar->baz...;
Limitations:
- No nice way to implicitly know the target array instance
- I haven't figured out const
- Really just syntactical salt
Example 1
TEST(IndexPtr, Readme) {
struct Header {
std::string letters = "Hello World!";
nodecode::index_ptr<&Header::letters> important;
};
Header header;
// Use this instance for future indexing
nodecode::bound_header bound(header);
// "Dereference" to get the object pointed to
EXPECT_EQ(*header.important, 'H');
// Still acts like a regular integer index
header.important = 6;
EXPECT_EQ(*header.important, 'W');
// The scoped nodecode::bound_header is a shortcut and entirely optional
header.important += 5;
EXPECT_EQ(*header.important.bind(header), '!');
}
Example 2
TEST(IndexPtr, Chain) {
struct Foo;
struct Bar;
struct Header {
std::span<Foo> foos;
std::span<Bar> bars;
};
// Foo has an index_ptr to a Bar
struct Foo {
std::string data;
index_ptr<&Header::bars> bar;
};
// Bar has an index_ptr to a Foo
struct Bar {
std::string data;
index_ptr<&Header::foos> foo;
};
// Make some concrete data for a Header
std::vector<Foo> foos{
{"foo0", 0},
{"foo1", 1}
};
std::vector<Bar> bars{
{"bar0", 1},
{"bar1", 0}
};
Header header{foos, bars};
// Bind it and start traversing the data structure
bound_header bound(header);
EXPECT_EQ(header.foos[0].data, "foo0");
EXPECT_EQ(header.foos[0].bar->data, "bar0");
EXPECT_EQ(header.foos[0].bar->foo->data, "foo1");
EXPECT_EQ(header.foos[0].bar->foo->bar->data, "bar1");
EXPECT_EQ(header.foos[0].bar->foo->bar->foo->data, "foo0");
}
Issues and pull requests are most welcome, thank you! Note the DCO and MIT LICENSE.