17 Commits
6.0.0 ... 6.0.2

Author SHA1 Message Date
Andrew Johnson
e67486c319 Final commit for 6.0.2 2025-12-15 10:18:01 -06:00
Andrew Johnson
4f1da262d2 Replease UNRELEASED for EPICS 7.0.10 2025-12-15 10:11:49 -06:00
Andrew Johnson
65c31491d9 Updates to Release Notes
Document the NTNDArray::getValueSize() fix.
Fix markdown errors, back-quoting identifiers with "_" chars.
Reformat the text to use semantic line breaks: https://sembr.org/
2025-11-05 12:10:27 -06:00
Andrew Johnson
5450f564dc Merge pull request #20 from JJL772/pr-fix-possible-nullptr-deref
Fix possible NULL deref in NTNDArray::getValueSize()
2025-04-04 09:53:04 -05:00
JJL772
453c267c9b Fix possible NULL deref in NTNDArray::getValueSize() 2024-06-21 16:47:54 -07:00
7a2d264f2c removed empty lines at end of file 2020-04-15 08:01:07 -07:00
03f9c1ab07 removed spaces at end of line 2020-04-15 08:01:07 -07:00
Andrew Johnson
6d41566b40 Incr version and set development flag after release 2019-10-31 16:34:40 -05:00
Andrew Johnson
1250a3c236 Reset development flag for 6.0.1 release 2019-10-31 16:30:33 -05:00
Andrew Johnson
28e5defc9b Document read-the-docs in Release Notes for 6.0.1 2019-10-31 16:29:32 -05:00
Ralph Lange
fbe1a1135f doc: fix link to reference manual 2019-09-06 10:42:53 +02:00
Ralph Lange
b75f942d5a doc: update/improve ntCPP.rst
- update Sourceforge links
- rename V4 -> 7
- shorten section names
2019-09-06 10:42:53 +02:00
Ralph Lange
e83ce61bf6 rtd-ci: add read-the-docs integration 2019-09-06 10:42:53 +02:00
Ralph Lange
86d8b5deec doc: change doxygen HTML_OUTPUT to html/doxygen 2019-09-05 14:52:59 +02:00
Ralph Lange
abbcf486b8 doc: make doxygen search src (not include) 2019-09-05 13:05:18 +02:00
Ralph Lange
e286d32716 doc: convert ntCPP.html to ntCPP.rst 2019-09-05 11:51:17 +02:00
Andrew Johnson
ba33c7443c Update version number to 6.0.1 DEVELOPMENT 2019-08-13 11:09:49 -05:00
35 changed files with 3516 additions and 3121 deletions

17
.readthedocs.yml Normal file
View File

@@ -0,0 +1,17 @@
# .readthedocs.yml
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
# Required
version: 2
# Build documentation in the documentation/ directory with Sphinx
sphinx:
configuration: documentation/conf.py
# Build documentation with MkDocs
#mkdocs:
# configuration: mkdocs.yml
# Optionally build your docs in additional formats such as PDF and ePub
formats: all

View File

@@ -38,7 +38,7 @@ PROJECT_NAME = normativeTypesCPP
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER =
PROJECT_NUMBER = 6.0.2
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
@@ -743,7 +743,7 @@ WARN_LOGFILE =
# spaces.
# Note: If this tag is empty the current directory is searched.
INPUT = include
INPUT = src
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
@@ -1008,7 +1008,7 @@ GENERATE_HTML = YES
# The default directory is: html.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_OUTPUT = documentation/html
HTML_OUTPUT = html/doxygen
# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
# generated HTML page (for example: .htm, .php, .asp).

View File

@@ -1,3 +1,12 @@
# Version number for the Normative Types API and shared library
EPICS_NTYPES_MAJOR_VERSION = 6
EPICS_NTYPES_MINOR_VERSION = 0
EPICS_NTYPES_MAINTENANCE_VERSION = 0
EPICS_NTYPES_MAINTENANCE_VERSION = 2
# Development flag, set to zero for release versions
EPICS_NTYPES_DEVELOPMENT_FLAG = 0
# Immediately after a release the MAINTENANCE_VERSION
# will be incremented and the DEVELOPMENT_FLAG set to 1

View File

@@ -2,24 +2,42 @@
This document summarizes the changes to the module between releases.
## Release 6.0.2 (EPICS 7.0.10, December 2025)
- Fix potential NULL pointer dereference in `NTNDArray::getValueSize()`
## Release 6.0.1 (EPICS 7.0.3.1, October 2019)
- Doxygen updates and read-the-docs integration.
## Release 6.0.0 (EPICS 7.0.3, July 2019)
* Reimplement `isCompatible` methods to use a new internal validation API in order to make the implementation uniform, less repetitive and less strict.
- Reimplement `isCompatible()` methods to use a new internal validation API in
order to make the implementation uniform, less repetitive and less strict.
The new implementation is less strict in the sense that it considers types that can be converted into one another compatible with each other. For example, any `Scalar` is considered compatible with any other `Scalar`, regardless of the underlying type. Normative Types users are advised to use `getAs` and `putFrom` when getting/putting data from/into `PVScalar`s and `PVScalarArray`s.
The new implementation is less strict in the sense that it considers types that
can be converted into one another compatible with each other.
For example, any `Scalar` is considered compatible with any other `Scalar`,
regardless of the underlying type.
Normative Types users are advised to use `getAs()` and `putFrom()` when
getting/putting data from/into `PVScalar`s and `PVScalarArray`s.
Also, `isCompatible` methods now disregard field order and extra fields that are not part of the specification.
Also, `isCompatible()` methods now disregard field order and extra fields that
are not part of the specification.
This change is not expected to break any current server or client, but it will break existing clients that rely on the previous `isCompatible` strictness once servers start to take advantage of `isCompatible` now being less strict.
This change is not expected to break any current server or client, but it will
break existing clients that rely on the previous `isCompatible()` strictness
once servers start to take advantage of `isCompatible()` now being less strict.
## Release 5.2.2
- Fix NTTable::getColumnNames().
- Fix `NTTable::getColumnNames()`.
## Release 5.2.1 (EPICS 7.0.2, Dec 2018)
* No functional changes.
* Removal of declaration for unimplemented PVNTField::createAlarmLimit() and elimination of unused variables.
- No functional changes.
- Removal of declaration for unimplemented `PVNTField::createAlarmLimit()` and
elimination of unused variables.
## Release 5.2.0 (EPICS 7.0.1, Dec 2017)
@@ -32,39 +50,39 @@ build against the latest version of pvData.
The main changes since release 5.1.1 are:
* NTUnionBuilder: Add missing value() function
* Updated document: Now document all Normative Types
- NTUnionBuilder: Add missing `value()` function
- Updated document: Now document all Normative Types
## Release 5.1.1
The main changes since release 5.0 are:
* Linux shared library version added
* Headers and source locations have changed
* Missing is_a implementations added
* NTAttribute::addTags() is now non-virtual
* New license file replaces LICENSE and COPYRIGHT
- Linux shared library version added
- Headers and source locations have changed
- Missing `is_a()` implementations added
- `NTAttribute::addTags()` is now non-virtual
- New license file replaces LICENSE and COPYRIGHT
### Shared library version added
Linux shared library version numbers have been added by setting SHRLIB_VERSION
(to 5.1 in this case). So shared object will be libnt.so.5.1 instead of
libpvData.so.
Linux shared library version numbers have been added by setting `SHRLIB_VERSION`
(to 5.1 in this case).
The shared object will be `libnt.so.5.1` instead of `libnt.so`.
### Headers and source locations have changed
Source has moved out of nt directory directly into src.
Headers have been moved into a pv directory. This facilitates using some IDEs
such as Qt Creator.
Headers have been moved into a pv directory.
This facilitates using some IDEs such as Qt Creator.
src/nt/ntscalar.cpp -> src/ntscalar.cpp
src/nt/ntscalar.h -> src/pv/ntscalar.h
- `src/nt/ntscalar.cpp -> src/ntscalar.cpp`
- `src/nt/ntscalar.h -> src/pv/ntscalar.h`
### Missing is_a implementations added
### Missing `is_a()` implementations added
is_a(PVStructurePtr const &) implementation has been added for each type.
`is_a(PVStructurePtr const &)` implementation has been added for each type.
## Release 5.0 (EPICS V4.5, Oct 2015)
@@ -72,41 +90,44 @@ is_a(PVStructurePtr const &) implementation has been added for each type.
This release adds support through wrapper classes and builders for the
remaining Normative Types:
* NTEnum
* NTMatrix
* NTURI
* NTAttribute
* NTContinuum
* NTHistogram
* NTAggregate
* NTUnion
* NTScalarMultiChannel
- NTEnum
- NTMatrix
- NTURI
- NTAttribute
- NTContinuum
- NTHistogram
- NTAggregate
- NTUnion
- NTScalarMultiChannel
Release 5.0 therefore implements fully the
[16 Mar 2015 version](http://epics-pvdata.sourceforge.net/alpha/normativeTypes/normativeTypes_20150316.html)
of the normativeTypes specification.
Release 5.0 therefore fully implements the 16 Mar 2015 version of the
normativeTypes specification.
Each wrapper class has an extended API:
* is_a now has a convenience overload taking a PVStructure.
* isCompatible, reporting introspection type compatibility, now has an overload
taking a Structure. The PVStructure version is retained as a convenience
method and for backwards compatibility.
* An isValid function now reports validity of a compatible PVStructure's data
with respect to the specification.
- `is_a()` now has a convenience overload taking a PVStructure.
- `isCompatible()`, reporting introspection type compatibility, now has an
overload taking a Structure.
The PVStructure version is retained as a convenience method and for backwards
compatibility.
- An `isValid()` function now reports validity of a compatible PVStructure's
data with respect to the specification.
Other changes are:
* Support for NTAttributes extended as required by NTNDArray
- Support for NTAttributes extended as required by NTNDArray
(NTNDArrayAttributes).
* A new class for parsing NT IDs (NTID).
* Resolution of the confusion between column names and labels in NTTable and
improved API. Function for adding columns is now addColumn rather than add.
New getColumnNames function provided.
* isConnected is treated as an optional rather than a required field in
NTMultiChannelArray. isConnected() and addIsConnected() functions added to
wrapper and builder respectively.
* Unit tests for all new classes.
- A new class for parsing NT IDs (NTID).
- Resolution of the confusion between column names and labels in NTTable and
improved API.
Function for adding columns is now `addColumn()` rather than `add()`.
A new `getColumnNames()` function provided.
- `isConnected()` is treated as an optional rather than a required field in
NTMultiChannelArray.
`isConnected()` and `addIsConnected()` functions added to wrapper and builder
respectively.
- Unit tests for all new classes.
## Release 4.0 (EPICS V4.4, Dec 2014)
@@ -117,26 +138,27 @@ It is a major rewrite of the previous versions of normativeTypesCPP.
This release provides support through wrapper classes and builders for the
following Normative Types:
* NTScalar
* NTScalarArray
* NTNameValue
* NTTable
* NTMultiChannel
* NTNDArray
- NTScalar
- NTScalarArray
- NTNameValue
- NTTable
- NTMultiChannel
- NTNDArray
Each type has a wrapper class of the same name which has functions for checking
compatibility of existing PVStructures (isCompatible) and the reported types of
Structures (is_a), wraps existing PVStructures (wrap, wrapUnsafe) and provides
a convenient interface to all required and optional fields.
compatibility of existing PVStructures (`isCompatible()`) and the reported types
of Structures (`is_a()`),
wraps existing PVStructures (`wrap()`, `wrapUnsafe()`) and provides a convenient
interface to all required and optional fields.
Each type has a builder which can create a Structure, a PVStructure or a
wrapper around a new PVStructure. In each case optional or extra fields can be
added and options such as choice of scalar type can be made.
wrapper around a new PVStructure.
In each case optional or extra fields can be added and options such as choice of
scalar type can be made.
Additional features are:
* Utility classes NTField and NTPVField for standard structure fields and
- Utility classes NTField and NTPVField for standard structure fields and
NTUtils for type IDs.
* Unit tests for the implemented classes.
- Unit tests for the implemented classes.

View File

@@ -0,0 +1,7 @@
.wy-side-nav-search {
background-color: #18334B;
}
.wy-side-nav-search input[type="text"] {
border-color: #18334b;
}

77
documentation/conf.py Normal file
View File

@@ -0,0 +1,77 @@
# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# http://www.sphinx-doc.org/en/master/config
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
# -- Project information -----------------------------------------------------
project = 'normativeTypes (C++)'
copyright = '2019, EPICS Controls.'
author = 'EPICS'
# -- General configuration ---------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.intersphinx',
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
# Intersphinx links to subprojects
intersphinx_mapping = {
}
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'sphinx_rtd_theme'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
html_css_files = [
'css/custom.css',
]
master_doc = 'index'
html_theme_options = {
'logo_only': True,
}
html_logo = "images/EPICS_white_logo_v02.png"
html_extra_path = ['../html']
# -- Run Doxygen ------------------------------------------------------------
import subprocess
subprocess.call('cd ..; mkdir -p html/doxygen; doxygen', shell=True)

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

17
documentation/index.rst Normal file
View File

@@ -0,0 +1,17 @@
NormativeTypes (C++) Library
============================
.. toctree::
:hidden:
EPICS Website <https://epics-controls.org>
EPICS Documentation Home <https://docs.epics-controls.org>
.. toctree::
:maxdepth: 1
:caption: normativeTypesCPP
Reference Manual <ntCPP>
API Documentation <https://docs.epics-controls.org/projects/normativetypes-cpp/en/latest/doxygen>
Source Code Repository on GitHub <https://github.com/epics-base/normativeTypesCPP>

File diff suppressed because it is too large Load Diff

3270
documentation/ntCPP.rst Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -239,4 +239,3 @@ PVStructureArrayPtr PVNTField::createAlarmArray()
}
}}

View File

@@ -7,11 +7,11 @@
#include <pv/ntid.h>
#include <pv/typeCast.h>
namespace epics {
namespace epics {
namespace nt {
const static std::string BAD_NAME = "?";
const static std::string BAD_NAME = "?";
NTID::NTID(const std::string & id)
: fullName(id),
@@ -81,7 +81,7 @@ namespace nt {
else
{
name = fullName;
}
}
}
return name;
}
@@ -105,7 +105,7 @@ namespace nt {
{
endMajorIndex = fullName.find('.', versionSepIndex+1);
majorVersionStr = (endMajorIndex != std::string::npos)
? fullName.substr(versionSepIndex+1, endMajorIndex-(versionSepIndex+1)) :
? fullName.substr(versionSepIndex+1, endMajorIndex-(versionSepIndex+1)) :
fullName.substr(versionSepIndex+1);
}
else
@@ -150,7 +150,7 @@ namespace nt {
{
endMinorIndex = fullName.find('.', endMajorIndex+1);
minorVersionStr = (endMinorIndex != std::string::npos)
? fullName.substr(endMajorIndex+1, endMinorIndex-(endMajorIndex+1)) :
? fullName.substr(endMajorIndex+1, endMinorIndex-(endMajorIndex+1)) :
fullName.substr(endMajorIndex+1);
}
else
@@ -186,5 +186,3 @@ namespace nt {
}}

View File

@@ -15,7 +15,7 @@
using namespace std;
using namespace epics::pvData;
namespace epics { namespace nt {
namespace epics { namespace nt {
static FieldCreatePtr fieldCreate = getFieldCreate();
@@ -269,7 +269,7 @@ bool NTMultiChannel::isValid()
if (getChannelName()->getLength() != valueLength) return false;
PVScalarArrayPtr arrayFields[] = {
getSeverity(), getStatus(), getMessage(),
getSeverity(), getStatus(), getMessage(),
getSecondsPastEpoch(), getNanoseconds(), getUserTag()
};
size_t N = sizeof(arrayFields)/sizeof(arrayFields[0]);
@@ -281,7 +281,7 @@ bool NTMultiChannel::isValid()
if (arrayField.get() && arrayField->getLength() != valueLength)
return false;
}
return true;
return true;
}

View File

@@ -140,7 +140,7 @@ bool NTNameValue::isCompatible(StructureConstPtr const & structure)
return false;
Result result(structure);
return result
.is<Structure>()
.has<ScalarArray>("name")

View File

@@ -71,7 +71,7 @@ StructureConstPtr NTNDArrayBuilder::createStructure()
ScalarType st = static_cast<ScalarType>(i);
fb->addArray(std::string(ScalarTypeFunc::name(st)) + "Value", st);
}
valueType = fb->createUnion();
valueType = fb->createUnion();
}
if (!codecStruc)
@@ -127,7 +127,7 @@ StructureConstPtr NTNDArrayBuilder::createStructure()
returnedStruc = fb->createStructure();
if (!isExtended)
ntndarrayStruc[index] = returnedStruc;
ntndarrayStruc[index] = returnedStruc;
}
else
{
@@ -327,10 +327,8 @@ int64 NTNDArray::getValueSize()
{
int64 size = 0;
PVScalarArrayPtr storedValue = getValue()->get<PVScalarArray>();
if (!storedValue.get())
{
if (storedValue.get())
size = storedValue->getLength()*getValueTypeSize();
}
return size;
}

View File

@@ -13,7 +13,7 @@
using namespace std;
using namespace epics::pvData;
namespace epics { namespace nt {
namespace epics { namespace nt {
static FieldCreatePtr fieldCreate = getFieldCreate();
@@ -263,7 +263,7 @@ bool NTScalarMultiChannel::isValid()
if (getChannelName()->getLength() != valueLength) return false;
PVScalarArrayPtr arrayFields[] = {
getSeverity(), getStatus(), getMessage(),
getSeverity(), getStatus(), getMessage(),
getSecondsPastEpoch(), getNanoseconds(), getUserTag()
};
size_t N = sizeof(arrayFields)/sizeof(arrayFields[0]);
@@ -275,7 +275,7 @@ bool NTScalarMultiChannel::isValid()
if (arrayField.get() && arrayField->getLength() != valueLength)
return false;
}
return true;
return true;
}
NTScalarMultiChannelBuilderPtr NTScalarMultiChannel::createBuilder()

View File

@@ -188,7 +188,7 @@ bool NTTable::isCompatible(PVStructurePtr const & pvStructure)
bool NTTable::isValid()
{
PVFieldPtrArray const & columns = pvValue->getPVFields();
if (getLabels()->getLength() != columns.size()) return false;
bool first = true;
int length = 0;

View File

@@ -31,7 +31,7 @@
/** @page Overview Documentation
*
* <a href = "ntCPP.html">ntCPP.html</a>
* <a href = "../ntCPP.html">Normative Types (C++) Reference</a>
*
*/

View File

@@ -78,7 +78,7 @@ void test_ntaggregate()
// example how to set a value
//
ntAggregate->getValue()->put(1.0);
//
// example how to get a value
//
@@ -173,5 +173,3 @@ MAIN(testNTAggregate) {
test_wrap();
return testDone();
}

View File

@@ -173,5 +173,3 @@ MAIN(testNTAttribute) {
test_wrap();
return testDone();
}

View File

@@ -274,5 +274,3 @@ MAIN(testNTContinuum) {
test_extra();
return testDone();
}

View File

@@ -84,7 +84,7 @@ void test_ntenum()
choices[1] = "On";
pvValue->getSubField<PVStringArray>("choices")->replace(freeze(choices));
pvValue->getSubField<PVInt>("index")->put(1);
//
// example how to get a value
//
@@ -185,5 +185,3 @@ MAIN(testNTEnum) {
test_wrap();
return testDone();
}

View File

@@ -259,5 +259,3 @@ MAIN(testNTHistogram) {
test_extra();
return testDone();
}

View File

@@ -207,5 +207,3 @@ MAIN(testNTMatrix) {
test_wrap();
return testDone();
}

View File

@@ -193,4 +193,3 @@ MAIN(testCreateRequest)
test_wrap();
return testDone();
}

View File

@@ -254,5 +254,3 @@ MAIN(testNTNameValue) {
test_extra();
return testDone();
}

View File

@@ -175,5 +175,3 @@ MAIN(testNTNDArrayAttribute) {
test_wrap();
return testDone();
}

View File

@@ -21,8 +21,8 @@ void test_builder(bool extraFields)
testOk(builder.get() != 0, "Got builder");
builder->addDescriptor()->
addTimeStamp()->
addAlarm()->
addTimeStamp()->
addAlarm()->
addDisplay();
if (extraFields)
@@ -68,9 +68,9 @@ void test_all()
PVStructurePtr pvStructure = builder->
addDescriptor()->
addTimeStamp()->
addAlarm()->
addDisplay()->
addTimeStamp()->
addAlarm()->
addDisplay()->
add("extra1",fieldCreate->createScalar(pvString)) ->
add("extra2",fieldCreate->createScalarArray(pvString)) ->
createPVStructure();
@@ -120,5 +120,3 @@ MAIN(testNTNDArray) {
test_wrap();
return testDone();
}

View File

@@ -242,5 +242,3 @@ MAIN(testNTScalarArray) {
test_wrap();
return testDone();
}

View File

@@ -180,4 +180,3 @@ MAIN(testCreateRequest)
test_wrap();
return testDone();
}

View File

@@ -233,5 +233,3 @@ MAIN(testNTScalar) {
test_wrap();
return testDone();
}

View File

@@ -245,5 +245,3 @@ MAIN(testNTTable) {
test_wrap();
return testDone();
}

View File

@@ -196,5 +196,3 @@ MAIN(testNTUnion) {
test_regular_union();
return testDone();
}

View File

@@ -33,5 +33,3 @@ MAIN(testNTUtils) {
test_is_a();
return testDone();
}

View File

@@ -90,7 +90,7 @@ void test_is_id()
testOk(!result.valid(), "!Result(Union['TEST_ID']).is<Structure>('TEST_ID').valid()");
testOk1(result.errors.at(0) == Result::Error("", Result::Error::IncorrectType));
}
{
// Type matches, ID doesn't
Result result(FB->setId("WRONG_ID")->createStructure());
@@ -140,7 +140,7 @@ void test_has()
result
.has<Scalar>("A")
.has<ScalarArray>("B");
testOk(!result.valid(),
testOk(!result.valid(),
"!Result(%s).has<Scalar>('A').has<ScalarArray>('B').valid()",
strucRepr.c_str());
testOk1(result.errors.at(0) == Result::Error("B", Result::Error::IncorrectType));
@@ -152,7 +152,7 @@ void test_has()
result
.has<Scalar>("A")
.has<Scalar>("C");
testOk(!result.valid(),
testOk(!result.valid(),
"!Result(%s).has<Scalar>('A').has<Scalar>('C').valid()",
strucRepr.c_str());
testOk1(result.errors.at(0) == Result::Error("C", Result::Error::MissingField));
@@ -199,7 +199,7 @@ void test_maybe_has()
result
.maybeHas<Scalar>("A")
.maybeHas<ScalarArray>("B");
testOk(!result.valid(),
testOk(!result.valid(),
"!Result(%s).maybeHas<Scalar>('A').maybeHas<ScalarArray>('B').valid()",
strucRepr.c_str());
testOk1(result.errors.at(0) == Result::Error("B", Result::Error::IncorrectType));
@@ -211,7 +211,7 @@ void test_maybe_has()
result
.maybeHas<Scalar>("A")
.maybeHas<Scalar>("C");
testOk(result.valid(),
testOk(result.valid(),
"Result(%s).maybeHas<Scalar>('A').maybeHas<Scalar>('C').valid()",
strucRepr.c_str());
}