Skip to content

Commit f7271a8

Browse files
committed
[hist] Implement RHistEngine::SetBinContent
This needs one indirection to construct the std::array of indices with the right (number of) elements.
1 parent 42f3188 commit f7271a8

File tree

4 files changed

+147
-3
lines changed

4 files changed

+147
-3
lines changed

hist/histv7/doc/DesignImplementation.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,12 @@ Special arguments are passed last.
5858
Examples include
5959
```cpp
6060
template <typename... A> void Fill(const std::tuple<A...> &args, RWeight w);
61-
template <std::size_t N> void SetBinContent(const std::array<RBinIndex, N> &indices, const BinContentType &content);
61+
template <std::size_t N, typename V> void SetBinContent(const std::array<RBinIndex, N> &indices, const V &value);
6262
```
63-
The same works for the variadic function templates that will check the type of the last argument.
63+
Note that we accept mandatory arguments with a template type as well to allow automatic conversion.
6464
65-
For profiles, we accept the value with a template type as well to allow automatic conversion to `double`, for example from `int`.
65+
Variadic function templates receive all arguments in a single function parameter pack.
66+
For optional arguments, the function will check the type of the last argument to determine if it was passed.
6667
6768
## Miscellaneous
6869

hist/histv7/inc/ROOT/RHistEngine.hxx

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,78 @@ public:
170170
return GetBinContent(indices);
171171
}
172172

173+
/// Set the content of a single bin.
174+
///
175+
/// \code
176+
/// ROOT::Experimental::RHistEngine<int> hist({/* two dimensions */});
177+
/// std::array<ROOT::Experimental::RBinIndex, 2> indices = {3, 5};
178+
/// int value = /* ... */;
179+
/// hist.SetBinContent(indices, value);
180+
/// \endcode
181+
///
182+
/// \note Compared to TH1 conventions, the first normal bin has index 0 and underflow and overflow bins are special
183+
/// values. See also the class documentation of RBinIndex.
184+
///
185+
/// Throws an exception if the number of indices does not match the axis configuration or the bin is not found.
186+
///
187+
/// \param[in] indices the array of indices for each axis
188+
/// \param[in] value the new value of the bin content
189+
/// \par See also
190+
/// the \ref SetBinContent(const A &... args) const "variadic function template overload" accepting arguments
191+
/// directly
192+
template <std::size_t N, typename V>
193+
void SetBinContent(const std::array<RBinIndex, N> &indices, const V &value)
194+
{
195+
// We could rely on RAxes::ComputeGlobalIndex to check the number of arguments, but its exception message might
196+
// be confusing for users.
197+
if (N != GetNDimensions()) {
198+
throw std::invalid_argument("invalid number of indices passed to SetBinContent");
199+
}
200+
RLinearizedIndex index = fAxes.ComputeGlobalIndex(indices);
201+
if (!index.fValid) {
202+
throw std::invalid_argument("bin not found in SetBinContent");
203+
}
204+
assert(index.fIndex < fBinContents.size());
205+
// To allow conversion, we have to accept value with a template type V to capture any argument. Otherwise it would
206+
// select the variadic function template...
207+
fBinContents[index.fIndex] = value;
208+
}
209+
210+
private:
211+
template <typename... A, std::size_t... I>
212+
void SetBinContentImpl(const std::tuple<A...> &args, std::index_sequence<I...>)
213+
{
214+
std::array<RBinIndex, sizeof...(A) - 1> indices{std::get<I>(args)...};
215+
SetBinContent(indices, std::get<sizeof...(A) - 1>(args));
216+
}
217+
218+
public:
219+
/// Set the content of a single bin.
220+
///
221+
/// \code
222+
/// ROOT::Experimental::RHistEngine<int> hist({/* two dimensions */});
223+
/// int value = /* ... */;
224+
/// hist.SetBinContent(ROOT::Experimental::RBinIndex(3), ROOT::Experimental::RBinIndex(5), value);
225+
/// // ... or construct the RBinIndex arguments implicitly from integers:
226+
/// hist.SetBinContent(3, 5, value);
227+
/// \endcode
228+
///
229+
/// \note Compared to TH1 conventions, the first normal bin has index 0 and underflow and overflow bins are special
230+
/// values. See also the class documentation of RBinIndex.
231+
///
232+
/// Throws an exception if the number of arguments does not match the axis configuration or the bin is not found.
233+
///
234+
/// \param[in] args the arguments for each axis and the new value of the bin content
235+
/// \par See also
236+
/// the \ref SetBinContent(const std::array<RBinIndex, N> &indices, const V &value) const "function overload"
237+
/// accepting `std::array`
238+
template <typename... A>
239+
void SetBinContent(const A &...args)
240+
{
241+
auto t = std::forward_as_tuple(args...);
242+
SetBinContentImpl(t, std::make_index_sequence<sizeof...(A) - 1>());
243+
}
244+
173245
/// Add all bin contents of another histogram.
174246
///
175247
/// Throws an exception if the axes configurations are not identical.

hist/histv7/test/hist_engine.cxx

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,55 @@ TEST(RHistEngine, GetBinContentNotFound)
7171
EXPECT_THROW(engine.GetBinContent(Bins), std::invalid_argument);
7272
}
7373

74+
TEST(RHistEngine, SetBinContent)
75+
{
76+
static constexpr std::size_t Bins = 20;
77+
const RRegularAxis axis(Bins, {0, Bins});
78+
RHistEngine<int> engine({axis});
79+
80+
const RBinIndex index(7);
81+
engine.SetBinContent(index, 42);
82+
EXPECT_EQ(engine.GetBinContent(index), 42);
83+
84+
const std::array<RBinIndex, 1> indices = {index};
85+
engine.SetBinContent(indices, 43);
86+
EXPECT_EQ(engine.GetBinContent(indices), 43);
87+
88+
// This also works if the value must be converted to the bin content type.
89+
RHistEngine<float> engineF({axis});
90+
engineF.SetBinContent(index, 42);
91+
EXPECT_EQ(engineF.GetBinContent(index), 42);
92+
93+
engineF.SetBinContent(indices, 43);
94+
EXPECT_EQ(engineF.GetBinContent(indices), 43);
95+
}
96+
97+
TEST(RHistEngine, SetBinContentInvalidNumberOfArguments)
98+
{
99+
static constexpr std::size_t Bins = 20;
100+
const RRegularAxis axis(Bins, {0, Bins});
101+
RHistEngine<int> engine1({axis});
102+
ASSERT_EQ(engine1.GetNDimensions(), 1);
103+
RHistEngine<int> engine2({axis, axis});
104+
ASSERT_EQ(engine2.GetNDimensions(), 2);
105+
106+
EXPECT_NO_THROW(engine1.SetBinContent(1, 0));
107+
EXPECT_THROW(engine1.SetBinContent(1, 2, 0), std::invalid_argument);
108+
109+
EXPECT_THROW(engine2.SetBinContent(1, 0), std::invalid_argument);
110+
EXPECT_NO_THROW(engine2.SetBinContent(1, 2, 0));
111+
EXPECT_THROW(engine2.SetBinContent(1, 2, 3, 0), std::invalid_argument);
112+
}
113+
114+
TEST(RHistEngine, SetBinContentNotFound)
115+
{
116+
static constexpr std::size_t Bins = 20;
117+
const RRegularAxis axis(Bins, {0, Bins});
118+
RHistEngine<int> engine({axis});
119+
120+
EXPECT_THROW(engine.SetBinContent(Bins, 0), std::invalid_argument);
121+
}
122+
74123
TEST(RHistEngine, Add)
75124
{
76125
static constexpr std::size_t Bins = 20;

hist/histv7/test/hist_user.cxx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@
66
struct User {
77
double fValue = 0;
88

9+
User &operator=(double value)
10+
{
11+
fValue = value;
12+
return *this;
13+
}
14+
915
User &operator++()
1016
{
1117
fValue++;
@@ -119,3 +125,19 @@ TEST(RHistEngineUser, FillWeight)
119125
std::array<RBinIndex, 1> indices = {9};
120126
EXPECT_EQ(engine.GetBinContent(indices).fValue, 0.9);
121127
}
128+
129+
TEST(RHistEngineUser, SetBinContent)
130+
{
131+
// Setting uses operator=
132+
static constexpr std::size_t Bins = 20;
133+
const RRegularAxis axis(Bins, {0, Bins});
134+
RHistEngine<User> engine({axis});
135+
136+
const RBinIndex index(7);
137+
engine.SetBinContent(index, 42);
138+
EXPECT_EQ(engine.GetBinContent(index).fValue, 42);
139+
140+
const std::array<RBinIndex, 1> indices = {index};
141+
engine.SetBinContent(indices, 43);
142+
EXPECT_EQ(engine.GetBinContent(indices).fValue, 43);
143+
}

0 commit comments

Comments
 (0)