Skip to content

Commit 7b1d4ae

Browse files
committed
Add test for user-defined classes
Closes #10
1 parent 84876ff commit 7b1d4ae

9 files changed

Lines changed: 266 additions & 0 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@
55
*.pcm
66
*.so
77
Nested*.cxx
8+
User*.cxx

types/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
* [`unique_ptr`](unique_ptr): `std::unique_ptr` with different element types
1212
* [`unordered_multiset`](unordered_multiset): `std::unordered_multiset` with all `[Split]Index{32,64}` column types
1313
* [`unordered_set`](unordered_set): `std::unordered_set` with all `[Split]Index{32,64}` column types
14+
* [`user`](user): user-defined types, such as classes and enums
1415
* [`variant`](variant): `std::variant` with `Switch` column type
1516
* [`vector`](vector): `std::vector` with all `[Split]Index{32,64}` column types
1617

types/user/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# User-defined Types
2+
3+
* [`class`]: user-defined class(es)

types/user/class/LinkDef.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#include <cstdint>
2+
#include <string>
3+
#include <variant>
4+
5+
#ifdef __CLING__
6+
#pragma link C++ class Base+;
7+
#pragma link C++ class Nested+;
8+
// ROOT does not (yet) support std::variant, but this line silences a warning
9+
// that fVariant would not be saved (which is not true for RNTuple).
10+
#pragma link C++ class std::variant<std::int32_t, std::string>;
11+
#pragma link C++ class User+;
12+
#endif

types/user/class/Makefile

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
CXX=g++
2+
CXXFLAGS_ROOT=$(shell root-config --cflags)
3+
ifeq ($(CXXFLAGS_ROOT),)
4+
$(error cannot find root-config: make sure to source thisroot.sh)
5+
endif
6+
CXXFLAGS=-Wall $(CXXFLAGS_ROOT)
7+
LDFLAGS=$(shell root-config --libs)
8+
9+
.PHONY: all clean
10+
11+
all: UserClass.cxx libUserClass.so
12+
13+
UserClass.cxx: UserClass.hxx LinkDef.h
14+
rootcling -f $@ $^
15+
16+
libUserClass.so: UserClass.cxx
17+
$(CXX) -shared -fPIC -o $@ $^ $(CXXFLAGS) $(LDFLAGS)
18+
19+
clean:
20+
rm -f UserClass.cxx UserClass_rdict.pcm libUserClass.so

types/user/class/README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# User-defined class(es)
2+
3+
## Fields
4+
5+
* `f` of type `User` with the following subfields:
6+
* `:_0` of type `Base`
7+
* `fInt` of type `std::int32_t`
8+
* `fString` of type `std::string`
9+
* `fVariant` of type `std::variant<std::int32_t, std::string>`
10+
* `fVector` of type `std::vector<std::int32_t>`
11+
* `fNested` of type `Nested`
12+
* `fInt` of type `std::int32_t`
13+
* `VectorUser` of type `std::vector<User>`
14+
15+
with the default column types.
16+
17+
## Entries
18+
19+
1. Simple values
20+
2. Zero / empty values

types/user/class/UserClass.hxx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#pragma once
2+
3+
#include <cstdint>
4+
#include <string>
5+
#include <variant>
6+
#include <vector>
7+
8+
using Variant = std::variant<std::int32_t, std::string>;
9+
using Vector = std::vector<std::int32_t>;
10+
11+
class Base {
12+
public:
13+
std::int32_t fInt;
14+
};
15+
16+
class Nested {
17+
public:
18+
std::int32_t fInt;
19+
};
20+
21+
class User : public Base {
22+
public:
23+
std::string fString;
24+
Variant fVariant;
25+
Vector fVector;
26+
Nested fNested;
27+
};

types/user/class/read.C

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
#include <ROOT/REntry.hxx>
2+
#include <ROOT/RNTupleReader.hxx>
3+
4+
using ROOT::Experimental::REntry;
5+
using ROOT::Experimental::RNTupleReader;
6+
7+
#include <TSystem.h>
8+
9+
#include <cstdint>
10+
#include <fstream>
11+
#include <ostream>
12+
#include <string>
13+
#include <string_view>
14+
#include <variant>
15+
#include <vector>
16+
17+
#include "UserClass.hxx"
18+
19+
static void PrintUserMembers(const User &value, std::ostream &os,
20+
std::string_view indent) {
21+
os << indent << "\":_0\": {\n";
22+
os << indent << " \"fInt\": " << value.fInt << "\n";
23+
os << indent << "},\n";
24+
os << indent << "\"fString\": \"" << value.fString << "\",\n";
25+
26+
os << indent << "\"fVariant\": ";
27+
if (value.fVariant.index() == 0) {
28+
os << std::get<std::int32_t>(value.fVariant);
29+
} else if (value.fVariant.index() == 1) {
30+
os << "\"" << std::get<std::string>(value.fVariant) << "\"";
31+
}
32+
os << ",\n";
33+
34+
os << indent << "\"fVector\": [";
35+
bool first = true;
36+
for (auto element : value.fVector) {
37+
if (first) {
38+
first = false;
39+
} else {
40+
os << ",";
41+
}
42+
os << "\n" << indent << " " << element;
43+
}
44+
if (!value.fVector.empty()) {
45+
os << "\n" << indent;
46+
}
47+
os << "],\n";
48+
49+
os << indent << "\"fNested\": {\n";
50+
os << indent << " \"fInt\": " << value.fNested.fInt << "\n";
51+
os << indent << "}\n";
52+
}
53+
54+
static void PrintUserValue(const REntry &entry, std::string_view name,
55+
std::ostream &os, bool last = false) {
56+
auto &value = *entry.GetPtr<User>(name);
57+
os << " \"" << name << "\": {\n";
58+
PrintUserMembers(value, os, " ");
59+
os << " }";
60+
if (!last) {
61+
os << ",";
62+
}
63+
os << "\n";
64+
}
65+
66+
static void PrintUserVector(const REntry &entry, std::string_view name,
67+
std::ostream &os, bool last = false) {
68+
auto &vector = *entry.GetPtr<std::vector<User>>(name);
69+
os << " \"" << name << "\": [";
70+
bool first = true;
71+
for (const auto &element : vector) {
72+
if (first) {
73+
first = false;
74+
} else {
75+
os << ",";
76+
}
77+
os << "\n {\n";
78+
PrintUserMembers(element, os, " ");
79+
os << " }";
80+
}
81+
if (!vector.empty()) {
82+
os << "\n ";
83+
}
84+
os << "]";
85+
if (!last) {
86+
os << ",";
87+
}
88+
os << "\n";
89+
}
90+
91+
void read(std::string_view input = "types.user.class.root",
92+
std::string_view output = "types.user.class.json") {
93+
if (gSystem->Load("libUserClass") == -1)
94+
throw std::runtime_error("could not find the required ROOT dictionaries, "
95+
"please make sure to run `make` first");
96+
97+
std::ofstream os(std::string{output});
98+
os << "[\n";
99+
100+
auto reader = RNTupleReader::Open("ntpl", input);
101+
auto &entry = reader->GetModel().GetDefaultEntry();
102+
bool first = true;
103+
for (auto index : *reader) {
104+
reader->LoadEntry(index);
105+
106+
if (first) {
107+
first = false;
108+
} else {
109+
os << ",\n";
110+
}
111+
os << " {\n";
112+
113+
PrintUserValue(entry, "f", os);
114+
PrintUserVector(entry, "Vector", os, /*last=*/true);
115+
116+
os << " }";
117+
// Newline is intentionally missing, may need to print a comma before the
118+
// next entry.
119+
}
120+
os << "\n";
121+
os << "]\n";
122+
}

types/user/class/write.C

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#include <ROOT/RNTupleModel.hxx>
2+
#include <ROOT/RNTupleWriteOptions.hxx>
3+
#include <ROOT/RNTupleWriter.hxx>
4+
5+
using ROOT::Experimental::RNTupleModel;
6+
using ROOT::Experimental::RNTupleWriteOptions;
7+
using ROOT::Experimental::RNTupleWriter;
8+
9+
#include <TSystem.h>
10+
11+
#include <string_view>
12+
13+
#include "UserClass.hxx"
14+
15+
void write(std::string_view filename = "types.user.class.root") {
16+
if (gSystem->Load("libUserClass") == -1)
17+
throw std::runtime_error("could not find the required ROOT dictionaries, "
18+
"please make sure to run `make` first");
19+
20+
auto model = RNTupleModel::Create();
21+
22+
auto value = model->MakeField<User>("f");
23+
auto vector = model->MakeField<std::vector<User>>("Vector");
24+
25+
RNTupleWriteOptions options;
26+
options.SetCompression(0);
27+
auto writer =
28+
RNTupleWriter::Recreate(std::move(model), "ntpl", filename, options);
29+
30+
// First entry: simple values
31+
value->fInt = 1;
32+
value->fString = "b";
33+
value->fVariant = 3;
34+
value->fVector = {4};
35+
value->fNested.fInt = 5;
36+
37+
vector->resize(2);
38+
vector->at(0).fInt = 6;
39+
vector->at(0).fString = "g";
40+
vector->at(0).fVariant = "h";
41+
vector->at(0).fVector = {9, 10};
42+
vector->at(0).fNested.fInt = 11;
43+
44+
vector->at(1).fInt = 12;
45+
vector->at(1).fString = "m";
46+
vector->at(1).fVariant = 14;
47+
vector->at(1).fVector = {15};
48+
vector->at(1).fNested.fInt = 16;
49+
50+
writer->Fill();
51+
52+
// Second entry: zero / empty values
53+
value->fInt = 0;
54+
value->fString = "";
55+
value->fVariant = 0;
56+
value->fVector.clear();
57+
value->fNested.fInt = 0;
58+
vector->clear();
59+
writer->Fill();
60+
}

0 commit comments

Comments
 (0)