Skip to content

Commit 5bab5d0

Browse files
committed
Safeguard: No scalar and vector components side by side
"A scalar component can not be contained at the same time as one or more regular components."
1 parent 641bf9a commit 5bab5d0

File tree

5 files changed

+76
-9
lines changed

5 files changed

+76
-9
lines changed

include/openPMD/RecordComponent.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ class RecordComponent : public BaseRecordComponent
154154
*
155155
* @return RecordComponent&
156156
*/
157-
RecordComponent &resetDataset(Dataset);
157+
virtual RecordComponent &resetDataset(Dataset);
158158

159159
uint8_t getDimensionality() const;
160160
Extent getExtent() const;

include/openPMD/backend/BaseRecord.hpp

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@
2020
*/
2121
#pragma once
2222

23+
#include "openPMD/Error.hpp"
2324
#include "openPMD/RecordComponent.hpp"
2425
#include "openPMD/UnitDimension.hpp"
2526
#include "openPMD/auxiliary/Variant.hpp"
27+
#include "openPMD/backend/BaseRecordComponent.hpp"
2628
#include "openPMD/backend/Container.hpp"
2729

2830
#include <array>
@@ -454,6 +456,17 @@ class BaseRecord
454456
*/
455457
std::array<double, 7> unitDimension() const;
456458

459+
void setDatasetDefined(BaseRecordComponent::Data_t &data) override
460+
{
461+
if (!T_Container::empty())
462+
{
463+
throw error::WrongAPIUsage(
464+
"A scalar component can not be contained at the same time as "
465+
"one or more regular components.");
466+
}
467+
T_RecordComponent::setDatasetDefined(data);
468+
}
469+
457470
/** Returns true if this record only contains a single component
458471
*
459472
* @return true if a record with only a single component
@@ -528,9 +541,9 @@ auto BaseRecord<T_elem>::operator[](key_type const &key) -> mapped_type &
528541
bool const keyScalar = (key == RecordComponent::SCALAR);
529542
if ((keyScalar && !Container<T_elem>::empty() && !scalar()) ||
530543
(scalar() && !keyScalar))
531-
throw std::runtime_error(
532-
"A scalar component can not be contained at "
533-
"the same time as one or more regular components.");
544+
throw error::WrongAPIUsage(
545+
"A scalar component can not be contained at the same time as "
546+
"one or more regular components.");
534547

535548
if (keyScalar)
536549
{
@@ -570,9 +583,9 @@ auto BaseRecord<T_elem>::operator[](key_type &&key) -> mapped_type &
570583
bool const keyScalar = (key == RecordComponent::SCALAR);
571584
if ((keyScalar && !Container<T_elem>::empty() && !scalar()) ||
572585
(scalar() && !keyScalar))
573-
throw std::runtime_error(
574-
"A scalar component can not be contained at "
575-
"the same time as one or more regular components.");
586+
throw error::WrongAPIUsage(
587+
"A scalar component can not be contained at the same time as "
588+
"one or more regular components.");
576589

577590
if (keyScalar)
578591
{
@@ -961,6 +974,17 @@ inline void BaseRecord<T_elem>::flush(
961974
"RecordComponents: " +
962975
name);
963976

977+
/*
978+
* Defensive programming. Normally, this error should yield as soon as
979+
* possible.
980+
*/
981+
if (scalar() && !T_Container::empty())
982+
{
983+
throw error::WrongAPIUsage(
984+
"A scalar component can not be contained at the same time as "
985+
"one or more regular components.");
986+
}
987+
964988
this->flush_impl(name, flushParams);
965989
// flush_impl must take care to correctly set the dirty() flag so this
966990
// method doesn't do it

include/openPMD/backend/BaseRecordComponent.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ class BaseRecordComponent : virtual public Attributable
179179
Attributable::setData(m_baseRecordComponentData);
180180
}
181181

182-
void setDatasetDefined(Data_t &);
182+
virtual void setDatasetDefined(Data_t &);
183183

184184
bool datasetDefined() const;
185185

include/openPMD/backend/PatchRecordComponent.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ class PatchRecordComponent : public BaseRecordComponent
8686
public:
8787
PatchRecordComponent &setUnitSI(double);
8888

89-
PatchRecordComponent &resetDataset(Dataset);
89+
virtual PatchRecordComponent &resetDataset(Dataset);
9090

9191
uint8_t getDimensionality() const;
9292
Extent getExtent() const;

test/CoreTest.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
// expose private and protected members for invasive testing
2+
#include "openPMD/Datatype.hpp"
3+
#include "openPMD/Error.hpp"
24
#if openPMD_USE_INVASIVE_TESTS
35
#define OPENPMD_private public:
46
#define OPENPMD_protected public:
@@ -1379,3 +1381,44 @@ TEST_CASE("unique_ptr", "[core]")
13791381
UniquePtrWithLambda<int[]> arrptrFilledCustom{
13801382
new int[5]{}, [](int const *p) { delete[] p; }};
13811383
}
1384+
1385+
TEST_CASE("scalar_and_vector", "[core]")
1386+
{
1387+
{
1388+
Series series("../samples/scalar_and_vector.json", Access::CREATE);
1389+
auto E = series.iterations[0].meshes["E"];
1390+
E["x"].makeEmpty(Datatype::FLOAT, 3);
1391+
REQUIRE_THROWS_AS(
1392+
E.makeEmpty(Datatype::FLOAT, 3), error::WrongAPIUsage);
1393+
}
1394+
{
1395+
Series series("scalar_and_vector.json", Access::CREATE);
1396+
auto E = series.iterations[0].meshes["E"];
1397+
E.makeEmpty(Datatype::FLOAT, 3);
1398+
REQUIRE_THROWS_AS(E["x"], error::WrongAPIUsage);
1399+
}
1400+
{
1401+
Series series("../samples/scalar_and_vector.json", Access::CREATE);
1402+
auto E = series.iterations[0].meshes["E"];
1403+
E["x"].makeEmpty(Datatype::FLOAT, 3);
1404+
}
1405+
{
1406+
Series read("../samples/scalar_and_vector.json", Access::READ_ONLY);
1407+
auto E = read.iterations[0].meshes["E"];
1408+
REQUIRE(E.size() == 1);
1409+
REQUIRE(!E.scalar());
1410+
REQUIRE(E.contains("x"));
1411+
}
1412+
{
1413+
Series series("../samples/scalar_and_vector.json", Access::CREATE);
1414+
auto E = series.iterations[0].meshes["E"];
1415+
E.makeEmpty(Datatype::FLOAT, 3);
1416+
}
1417+
{
1418+
Series read("../samples/scalar_and_vector.json", Access::READ_ONLY);
1419+
auto E = read.iterations[0].meshes["E"];
1420+
REQUIRE(E.size() == 1);
1421+
REQUIRE(E.scalar());
1422+
REQUIRE(!E.contains("x"));
1423+
}
1424+
}

0 commit comments

Comments
 (0)