Skip to content

Commit 3ba05ff

Browse files
committed
meta: Add test of alignment recording
1 parent 3959804 commit 3ba05ff

3 files changed

Lines changed: 241 additions & 0 deletions

File tree

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
ROOTTEST_ADD_TEST(evolution
2+
MACRO evolution.C+
3+
FIXTURES_SETUP root-meta-alignment-evolution-fixture)
4+
5+
ROOTTEST_ADD_TEST(emulation
6+
MACRO emulation.C+
7+
FIXTURES_REQUIRED root-meta-alignment-evolution-fixture)
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// File: emulation.C
2+
3+
4+
#include <iostream>
5+
#include <cstdint> // uintptr_t
6+
#include <cassert>
7+
#include "TError.h"
8+
#include "TFile.h"
9+
10+
// Containee type with a custom alignment (over-aligned)
11+
struct alignas(256) Containee {
12+
int id;
13+
// some payload to make sizeof non-trivial
14+
double payload[7];
15+
16+
Containee(int i = -1) : id(i) {}
17+
~Containee() = default;
18+
19+
void Streamer(TBuffer &b) {
20+
if (b.IsReading()) {
21+
std::cout << "Streaming Containee\n";
22+
// Guess the effective alignment of c's address: largest power-of-two
23+
// that divides the address.
24+
uintptr_t addr = reinterpret_cast<uintptr_t>(this);
25+
uintptr_t guessed = 1;
26+
if (addr != 0) {
27+
guessed = addr & (~addr + 1); // isolate lowest set bit
28+
}
29+
std::cout << " address: " << static_cast<const void*>(this)
30+
<< " (guessed alignment: " << guessed << " bytes)\n";
31+
if (reinterpret_cast<uintptr_t>(this) % alignof(Containee) != 0) {
32+
Fatal("copyContainer", "Containee object at %p does not satisfy alignment requirement of %zu\n",
33+
this, alignof(Containee));
34+
}
35+
36+
b.ReadClassBuffer(TClass::GetClass("Containee"), this);
37+
} else {
38+
b.WriteClassBuffer(TClass::GetClass("Containee"), this);
39+
}
40+
}
41+
};
42+
43+
static_assert(alignof(Containee) == 256, "Containee must be 256-byte aligned");
44+
45+
#ifdef __ROOTCLING__
46+
#pragma link C++ class Containee-;
47+
#endif
48+
49+
int readfile(const char *filename)
50+
{
51+
TFile file(filename, "READ");
52+
if (file.IsZombie())
53+
return 1;
54+
auto c = file.Get("origContainer");
55+
TClass::GetClass("Container")->GetStreamerInfos()->ls();
56+
if (!c) {
57+
Error("readfile", "Could not read object from file");
58+
return 2;
59+
}
60+
return 0;
61+
}
62+
63+
int emulation()
64+
{
65+
return readfile("alignment_evolution.root");
66+
}
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
// File: evolution.C
2+
// Demonstrates a containee with custom alignment embedded directly inside a
3+
// container (no heap allocation). The compiler is responsible for satisfying
4+
// the alignment requirement of the embedded object, inserting padding before it
5+
// if necessary.
6+
7+
#include <iostream>
8+
#include <cstdint> // uintptr_t
9+
#include <cassert>
10+
#include "TError.h"
11+
#include "TFile.h"
12+
13+
// Containee type with a custom alignment (over-aligned)
14+
struct alignas(256) Containee {
15+
int id;
16+
// some payload to make sizeof non-trivial
17+
double payload[7];
18+
19+
Containee(int i = -1) : id(i) {}
20+
~Containee() = default;
21+
};
22+
23+
struct Wrapper {
24+
Containee c;
25+
Wrapper(int i = -1) : c(i) {}
26+
~Wrapper() = default;
27+
};
28+
29+
static_assert(alignof(Containee) == 256, "Containee must be 256-byte aligned");
30+
31+
// Container that embeds a single Containee object directly as a data member.
32+
// The compiler guarantees the member satisfies alignof(Containee) because the
33+
// member type carries the alignas specifier; it inserts padding before m_data
34+
// as needed.
35+
class Container {
36+
private:
37+
using pair_t = std::pair<int, Containee>;
38+
using coll_t = std::map<int, Containee>;
39+
using nested_coll_t = std::map<int, Wrapper>;
40+
41+
char m_misalign; // dummy member to show the compiler inserts padding
42+
Containee m_data; // embedded – no manual aligned allocation
43+
//pair_t m_pair_data; // check on the run-time dictionary generated for pairs
44+
coll_t m_collection; // check on the run-time dictionary generated for collections
45+
nested_coll_t m_nested_collection;
46+
47+
public:
48+
Container() = default;
49+
explicit Container(int id) : m_misalign(0), m_data(id) {
50+
m_collection.emplace(id, Containee(id + 1));
51+
m_nested_collection.emplace(id, Wrapper(id + 2));
52+
}
53+
~Container() = default; // embedded object is destroyed automatically
54+
55+
// Demonstrate and verify alignment of the embedded element
56+
void showAlignment() const {
57+
const void* addr = static_cast<const void*>(&m_data);
58+
uintptr_t v = reinterpret_cast<uintptr_t>(addr);
59+
std::cout << "Containee alignment requirement: " << alignof(Containee) << '\n';
60+
std::cout << "m_data at " << addr
61+
<< " (addr % align = " << (v % alignof(Containee)) << ")\n";
62+
// runtime check
63+
assert((v % alignof(Containee)) == 0 && "m_data not correctly aligned");
64+
}
65+
66+
Containee& get() { return m_data; }
67+
const Containee& get() const { return m_data; }
68+
69+
ClassDef(Container, 2) // Container with embedded Containee
70+
};
71+
72+
// AlternateContainer: same layout and behaviour as Container.
73+
class AlternateContainer {
74+
private:
75+
double padding; // dummy member to show the compiler inserts padding
76+
char m_misalign; // dummy member to show the compiler inserts padding
77+
Containee m_alt_data; // embedded – no manual aligned allocation
78+
79+
public:
80+
AlternateContainer() = default;
81+
explicit AlternateContainer(int id) : m_misalign(0), m_alt_data(id) {}
82+
~AlternateContainer() = default; // embedded object is destroyed automatically
83+
84+
// Demonstrate and verify alignment of the embedded element
85+
void showAlignment() const {
86+
const void* addr = static_cast<const void*>(&m_alt_data);
87+
uintptr_t v = reinterpret_cast<uintptr_t>(addr);
88+
std::cout << "Containee alignment requirement: " << alignof(Containee) << '\n';
89+
std::cout << "m_alt_data at " << addr
90+
<< " (addr % align = " << (v % alignof(Containee)) << ")\n";
91+
// runtime check
92+
assert((v % alignof(Containee)) == 0 && "m_data not correctly aligned");
93+
}
94+
95+
void copyContainee(const Containee& c) {
96+
std::cout << "Copying Containee with id = " << c.id << " into AlternateContainer\n";
97+
// Guess the effective alignment of c's address: largest power-of-two
98+
// that divides the address.
99+
uintptr_t addr = reinterpret_cast<uintptr_t>(&c);
100+
uintptr_t guessed = 1;
101+
if (addr != 0) {
102+
guessed = addr & (~addr + 1); // isolate lowest set bit
103+
}
104+
std::cout << " address of c: " << static_cast<const void*>(&c)
105+
<< " (guessed alignment: " << guessed << " bytes)\n";
106+
if (reinterpret_cast<uintptr_t>(&c) % alignof(Containee) != 0) {
107+
Error("copyContainer", "Containee object at %p does not satisfy alignment requirement of %zu\n",
108+
&c, alignof(Containee));
109+
}
110+
m_alt_data = c;
111+
}
112+
113+
Containee& get() { return m_alt_data; }
114+
const Containee& get() const { return m_alt_data; }
115+
116+
ClassDef(AlternateContainer, 2) // Alternate container with embedded Containee
117+
};
118+
119+
120+
#ifdef __ROOTCLING__
121+
#pragma link C++ class Container+;
122+
#pragma link C++ class Containee+;
123+
#pragma link C++ class Wrapper+;
124+
#pragma link C++ class AlternateContainer+;
125+
#pragma read sourceClass="Container" source="char m_misalign" \
126+
targetClass="AlternateContainer" target="m_misalign" \
127+
code="{ m_misalign = onfile.m_misalign; }";
128+
#pragma read sourceClass="Container" source="Containee m_data" \
129+
targetClass="AlternateContainer" target="m_alt_data" \
130+
code="{ newObj->copyContainee(onfile.m_data); }";
131+
#endif
132+
133+
134+
void writefile(const char *filename)
135+
{
136+
Container c(42);
137+
c.showAlignment();
138+
std::cout << "c.get().id = " << c.get().id << '\n';
139+
140+
TFile file(filename, "RECREATE");
141+
file.WriteObject(&c, "origContainer");
142+
file.Write();
143+
};
144+
145+
void readfile(const char *filename)
146+
{
147+
TFile file(filename, "READ");
148+
AlternateContainer* alt = file.Get<AlternateContainer>("origContainer");
149+
if (alt) {
150+
std::cout << "Successfully read AlternateContainer from file:\n";
151+
alt->showAlignment();
152+
std::cout << "Containee id = " << alt->get().id << '\n';
153+
} else {
154+
Fatal("readfile", "Failed to read AlternateContainer from file");
155+
}
156+
};
157+
158+
int evolution() {
159+
const char* filename = "alignment_evolution.root";
160+
writefile(filename);
161+
readfile(filename);
162+
return 0;
163+
}
164+
165+
int main()
166+
{
167+
return evolution();
168+
}

0 commit comments

Comments
 (0)