72 Commits

Author SHA1 Message Date
Michael Davidsaver
6da871fa64 7.1.3 2019-04-17 11:48:01 -07:00
Michael Davidsaver
d53cb0cbc9 doc 2019-04-17 11:47:56 -07:00
Michael Davidsaver
8f0111e482 fix ByteBuffer::putArray() and getArray()
Erroneous mixing of byte and element indexing
introduced by a51b308cc8
2019-04-08 09:46:23 -07:00
Michael Davidsaver
5525119778 test ByteBuffer array operations 2019-04-08 09:46:20 -07:00
Michael Davidsaver
6410600205 testByteBuffer cleanup 2019-04-08 09:46:16 -07:00
Michael Davidsaver
a5d44745d1 cleanup testSharedVector
"using namespace" considered harmful...
2019-04-08 09:46:07 -07:00
Michael Davidsaver
8c275cbc1c 7.1.2 2019-03-18 11:07:20 -07:00
Michael Davidsaver
b79f69231c more doxygen 2019-03-18 10:32:50 -07:00
Michael Davidsaver
ff165595c4 minor doc 2019-03-18 09:28:00 -07:00
Michael Davidsaver
12d851dc6f release notes for 7.1.1 2019-03-18 09:08:08 -07:00
Michael Davidsaver
643f289c23 missing include 2019-03-11 14:55:55 -07:00
Michael Davidsaver
68e74ed1d2 update release notes 2019-03-11 14:08:01 -07:00
Michael Davidsaver
f0ef0965c4 AnyScalar doc 2019-03-11 14:05:10 -07:00
Michael Davidsaver
61ce532fdf cleanup test 2019-03-11 14:05:05 -07:00
Michael Davidsaver
d746e1bfb3 minor 2019-03-11 14:04:53 -07:00
MJGaughran
deccc41b9a Disabled NEED_OLL_FUNCS for Visual Studio 2013+ (#64)
* Disabled NEED_OLL_FUNCS for Visual Studio 2013+

Both _MSC_VER (VC++ compiler version) and EPICS base version should be
checked for strtoll, strtoull definitions.

* NEED_OLL_FUNCS now defined only for VS builds
2019-03-02 11:11:42 -08:00
Michael Davidsaver
2814c779bd StandardField::getStandardField safety 2019-01-15 21:01:38 -08:00
Michael Davidsaver
4c73607799 cleanup StandardField 2019-01-15 21:01:38 -08:00
Andrew Johnson
d776f6eaf0 Update and unify README.md 2018-12-17 15:00:28 -06:00
Andrew Johnson
706ef01782 Delete ancient and unused examples.zip file 2018-12-17 14:59:23 -06:00
Michael Davidsaver
00c62cbd67 Fix deprecation message w/ gcc < 4.5
Apparently deprecated(msg) is newer than I thought.
2018-11-26 12:05:34 -08:00
Michael Davidsaver
da8f3d6cc7 minor 2018-11-26 10:25:29 -08:00
Michael Davidsaver
0bc95e51c2 7.1.0 2018-11-26 10:20:31 -08:00
Michael Davidsaver
727153e965 deprecate BoundedString, BoundedScalarArray, and FixedScalarArray
https://github.com/epics-base/pvDataCPP/issues/52
2018-11-26 10:13:08 -08:00
Michael Davidsaver
d00f54228d quiet warning, and possible dllimport/export
Seen as a warning on clang, but class/struct
can result in different mangled symbol names
w/ MSVC.
2018-11-07 09:06:58 -08:00
Michael Davidsaver
77c67802a3 printer.cpp extend precision 2018-10-30 17:09:56 -07:00
Michael Davidsaver
7d68d177d7 printer.cpp update time format 2018-10-30 17:02:22 -07:00
Michael Davidsaver
9b20505dcd stream() remove comma in NTTable 2018-10-30 14:33:15 -07:00
Michael Davidsaver
5f93e292b2 testprinter fix plan 2018-10-29 13:12:01 -07:00
Michael Davidsaver
edd3e20f3c format NTEnum 2018-10-29 13:12:01 -07:00
Michael Davidsaver
90cffa60d6 format NTTable as CSV 2018-10-29 13:12:01 -07:00
Michael Davidsaver
6171cd6867 escape and quote PVString(Array)::dumpValue()
Escaping for both.  quote array values,
but not scalar.

Also move remaining template virtuals out of line.
2018-10-29 13:12:01 -07:00
Michael Davidsaver
fa731bf6c3 helper to escape while printing strings 2018-10-29 13:12:01 -07:00
Michael Davidsaver
818fce324c Add PVStructure::stream() 2018-10-29 13:12:01 -07:00
Michael Davidsaver
1bc867e48d update travis-ci 2018-10-29 13:12:01 -07:00
Michael Davidsaver
434b9f7a9f Timer: simply wakeup condition 2018-10-19 13:35:35 -07:00
Michael Davidsaver
f54602dead update doc 2018-10-18 16:43:56 -07:00
Michael Davidsaver
fb546b41c1 rename documentation/*.h -> .dox 2018-10-18 16:43:56 -07:00
Michael Davidsaver
e400d9f5fd Timer reschedule if earlier timer added 2018-10-18 14:44:51 -07:00
Michael Davidsaver
f0fa8a2481 deprecate pvCopy.h 2018-10-16 20:52:50 -07:00
Michael Davidsaver
c3b0b49e3f use FieldBuilder::begin() and Field::build() 2018-10-16 20:52:38 -07:00
Michael Davidsaver
45265b4f9b Add FieldBuilder::begin() and Field::build()
Boilerplate reduction in structure
definition and instanciation.
2018-10-16 20:52:24 -07:00
Michael Davidsaver
aa87a2a23d json print/parse updates
These functions don't create new refs,
so they don't really need to work with shared_ptr.

Fully support printing.

Add option for maskable PVStructure printing.
2018-09-21 10:28:37 -07:00
Michael Davidsaver
5a59b1da75 Status avoid extra copies 2018-09-21 10:28:37 -07:00
Michael Davidsaver
32aa0dd72f drop extractRequestMask()
superceded by PVRequestMapper
2018-09-21 10:27:43 -07:00
mdavidsaver
c1188b16a1 Merge pull request #56 from mdavidsaver/pvrequestmapper
add PVRequestMapper
2018-09-21 10:27:08 -07:00
Michael Davidsaver
a02a60c658 Field::m_hash friends
Avoid additional access of m_hash by FieldCreate::Helper
which has an unclear (though I think correct) friend
relationship with Field.

Attempt to placate old gcc 3.4.4
2018-09-19 11:04:02 -07:00
c5f9f5a2dc link with rt when using clock_gettime() 2018-09-19 10:49:14 -07:00
Michael Davidsaver
342b1bc8ef add PVRequestMapper
utility to having pvRequest .field mangling

Warn if requesting some non-existant fields.
Error if no requested fields exist.

PVRequestMapper mode enum to select between
two "styles" of interpretation.
2018-09-17 09:11:28 -07:00
Michael Davidsaver
64158376f5 PVStructure inline access to member variables 2018-07-31 16:55:15 -07:00
Michael Davidsaver
850d4ff056 PVField::copyUnchecked assert()
Field comparison is now O(0), so do this anyway
to help catch mistakes.
2018-07-31 16:55:15 -07:00
Michael Davidsaver
f0cfe1c85a PVField::copy() avoid duplication with copyUnchecked() 2018-07-31 16:55:15 -07:00
Michael Davidsaver
c8b615b3ee Field initialize m_hash
valgrind complain (rightly) about use of uninitialized
if Structure ctor throws (eg. duplicate field).
~Field will then try to use m_hash before it has been
initialized.  This don't hurt and the subsequent equality
tests prevent any bad behavior.
2018-07-30 14:50:19 -07:00
Michael Davidsaver
c67fdafb43 add pvRequest -> bitmask processing 2018-07-14 15:10:46 -07:00
Michael Davidsaver
f66d277918 msvc 9 compat 2018-07-12 19:05:12 -07:00
Michael Davidsaver
4ef7db20f8 sharePtr.h: adjust apply/llvm compatibility 2018-07-12 15:38:49 -07:00
Michael Davidsaver
e1216dfa76 move PVValueArray dtor out of line
This class has out of line members, and explicit
instanciations.  Move the dtor out of line as
well to maybe avoid emitting duplicate
typeinfo.
2018-07-12 15:38:49 -07:00
Michael Davidsaver
340fa8a7cb Thread accept non-copyable std::function 2018-07-11 16:30:05 -07:00
Michael Davidsaver
a029455466 Thread reftrack 2018-07-11 16:30:04 -07:00
Michael Davidsaver
27f78c430b move Thread out of line 2018-07-11 16:29:50 -07:00
Michael Davidsaver
3d707e5e95 Field de-duplication performance test 2018-07-06 12:01:15 -07:00
Michael Davidsaver
0406a2f614 Field de-duplication
add a global cache of Field instances to
allow O(0) comparison of types.
2018-07-06 12:01:14 -07:00
Michael Davidsaver
a1c0e432ee fix Timer
use after free
2018-06-19 16:32:39 -07:00
Michael Davidsaver
57e57d9e43 de-virtualize PVValueArray<T> methods append(), remove(), and compress()
These were never members of a common base class,
nor overridden by a sub-class.
2018-06-07 09:53:38 -07:00
Michael Davidsaver
e4e4188eaf some more FINAL 2018-06-07 09:53:38 -07:00
Michael Davidsaver
f1553cc90e Move NOMINMAX to configure/CONFIG_SITE
The macro must be defined before MS system headers are
included.

This rev. remove #define NOMINMAX from public headers,
but no public headers use min()/max() and this was
never the correct way to use this macro as by convention
library headers are included after system headers,
which is too late to have an effect.
2018-05-28 11:24:49 -07:00
Michael Davidsaver
997e68c99a remove usage of min()/max() from public headers
Avoids need for #define NOMINMAX w/ MSVC
2018-05-28 10:14:03 -07:00
Michael Davidsaver
25663d9a7b additional EPICS_NOT_COPYABLE
attempt to pacify, or at least clarify, msvc
link error.
2018-05-21 21:02:32 -07:00
Michael Davidsaver
1e55266396 pvUnitTest.h avoid unintended copies
template argument matching doesn't always preserve
rvalue references, so force use of const ref.
2018-05-21 11:21:01 -07:00
Michael Davidsaver
810ae15991 printer.cpp avoid alloc of spaces
No need to allocate a std::string
just to repeat spaces.
2018-05-21 11:21:01 -07:00
Michael Davidsaver
271fec7f5e printer.cpp: clean whitespace 2018-05-21 11:21:01 -07:00
Michael Davidsaver
c43486791e prevent Field/PVField from being copied
I don't think this was ever intended to be possible,
but it was...
2018-05-21 11:19:42 -07:00
65 changed files with 3259 additions and 1377 deletions

View File

@@ -1,21 +1,10 @@
#!/bin/sh
set -e -x
# set RTEMS to eg. "4.9" or "4.10"
# requires qemu, bison, flex, texinfo, install-info
if [ -n "$RTEMS" ]
then
# find local qemu-system-i386
export PATH="$HOME/.cache/qemu/usr/bin:$PATH"
echo -n "Using QEMU: "
type qemu-system-i386 || echo "Missing qemu"
EXTRA=RTEMS_QEMU_FIXUPS=YES
fi
make -j2 $EXTRA
make -j2
if [ "$TEST" != "NO" ]
then
make tapfiles
make -s test-results
make -j2 tapfiles
make -j2 -s test-results
fi

View File

@@ -1,39 +1,6 @@
#!/bin/sh
set -e -x
CURDIR="$PWD"
QDIR="$HOME/.cache/qemu"
if [ -n "$RTEMS" -a "$TEST" = "YES" ]
then
git clone --quiet --branch vme --depth 10 https://github.com/mdavidsaver/qemu.git "$HOME/.build/qemu"
cd "$HOME/.build/qemu"
HEAD=`git log -n1 --pretty=format:%H`
echo "HEAD revision $HEAD"
[ -e "$HOME/.cache/qemu/built" ] && BUILT=`cat "$HOME/.cache/qemu/built"`
echo "Cached revision $BUILT"
if [ "$HEAD" != "$BUILT" ]
then
echo "Building QEMU"
git submodule --quiet update --init
install -d "$HOME/.build/qemu/build"
cd "$HOME/.build/qemu/build"
"$HOME/.build/qemu/configure" --prefix="$HOME/.cache/qemu/usr" --target-list=i386-softmmu --disable-werror
make -j2
make install
echo "$HEAD" > "$HOME/.cache/qemu/built"
fi
fi
cd "$CURDIR"
cat << EOF > configure/RELEASE.local
EPICS_BASE=$HOME/.source/epics-base
EOF
@@ -41,23 +8,8 @@ EOF
install -d "$HOME/.source"
cd "$HOME/.source"
add_base_module() {
MODULE=$1
BRANCH=$2
( cd epics-base/modules && \
git clone --quiet --depth 5 --branch "$MODULE"/"$BRANCH" https://github.com/${REPOBASE:-epics-base}/epics-base.git "$MODULE" && \
cd "$MODULE" && git log -n1 )
}
if [ "$BRBASE" ]
then
git clone --quiet --depth 5 --branch "$BRBASE" https://github.com/${REPOBASE:-epics-base}/epics-base.git epics-base
(cd epics-base && git log -n1 )
else
git clone --quiet --depth 5 --branch core/"${BRCORE:-master}" https://github.com/${REPOBASE:-epics-base}/epics-base.git epics-base
( cd epics-base && git log -n1 )
add_base_module libcom "${BRLIBCOM:-master}"
fi
git clone --quiet --depth 5 --branch "$BRBASE" https://github.com/${REPOBASE:-epics-base}/epics-base.git epics-base
(cd epics-base && git log -n1 )
EPICS_HOST_ARCH=`sh epics-base/startup/EpicsHostArch`
@@ -115,24 +67,17 @@ EOF
if [ -n "$RTEMS" ]
then
echo "Cross RTEMS${RTEMS} for pc386"
install -d /home/travis/.cache
curl -L "https://github.com/mdavidsaver/rsb/releases/download/travis-20160306-2/rtems${RTEMS}-i386-trusty-20190306-2.tar.gz" \
| tar -C /home/travis/.cache -xj
curl -L "https://github.com/mdavidsaver/rsb/releases/download/20171203-${RTEMS}/i386-rtems${RTEMS}-trusty-20171203-${RTEMS}.tar.bz2" \
| tar -C / -xmj
sed -i -e '/^RTEMS_VERSION/d' -e '/^RTEMS_BASE/d' epics-base/configure/os/CONFIG_SITE.Common.RTEMS
cat << EOF >> epics-base/configure/os/CONFIG_SITE.Common.RTEMS
RTEMS_VERSION=$RTEMS
RTEMS_BASE=/home/travis/.cache/rtems${RTEMS}-i386
RTEMS_BASE=$HOME/.rtems
EOF
cat << EOF >> epics-base/configure/CONFIG_SITE
CROSS_COMPILER_TARGET_ARCHS+=RTEMS-pc386
CROSS_COMPILER_TARGET_ARCHS += RTEMS-pc386-qemu
EOF
# find local qemu-system-i386
export PATH="$HOME/.cache/qemu/usr/bin:$PATH"
echo -n "Using QEMU: "
type qemu-system-i386 || echo "Missing qemu"
EXTRA=RTEMS_QEMU_FIXUPS=YES
fi
make -j2 -C epics-base $EXTRA

View File

@@ -11,19 +11,20 @@ addons:
- perl
- clang
- g++-mingw-w64-i686
- qemu-system-x86
install:
- ./.ci/travis-prepare.sh
script:
- ./.ci/travis-build.sh
env:
- BRCORE=master BRLIBCOM=master
- CMPLR=clang
- USR_CXXFLAGS=-std=c++11
- CMPLR=clang USR_CXXFLAGS=-std=c++11
- WINE=32 TEST=NO STATIC=YES
- WINE=32 TEST=NO STATIC=NO
- RTEMS=4.10 TEST=NO
- RTEMS=4.9 TEST=NO
- BRBASE=7.0 CMPLR=gcc
- BRBASE=7.0 CMPLR=clang
- BRBASE=7.0 USR_CXXFLAGS=-std=c++11
- BRBASE=7.0 CMPLR=clang USR_CXXFLAGS=-std=c++11
- BRBASE=7.0 WINE=32 TEST=NO STATIC=YES
- BRBASE=7.0 WINE=32 TEST=NO STATIC=NO
- BRBASE=7.0 RTEMS=4.10 TEST=NO
- BRBASE=7.0 RTEMS=4.9 TEST=NO
- BRBASE=3.16
- BRBASE=3.15
- BRBASE=3.14

View File

@@ -1,57 +1,16 @@
pvaDataCPP
==========
# pvaDataCPP
pvDataCPP is a set of data types and utilities that form part of the EPICS V4 project.
The EPICS **pvData** API provides a set of classes and utilities that form the core of the EPICS PVA implementation.
The pvDataCPP module is a part of the EPICS software toolkit that implements pvData structures as C++ class objects.
Further Info
------------
## Links
Consult the documents in the documentation directory, in particular
* pvDataCPP.html
* RELEASE_NOTES.md
Also see the [EPICS Version 4 website](http://epics-pvdata.sourceforge.net)
Prerequisites
-------------
The pvDataCPP requires recent versions of the following software:
1. EPICS Base (v3.14.12.3 or later)
2. EPICS4 pvCommonCPP (4.1.0 or later)
(pvCommonCPP may not be needed depending on host/compiler.)
Building
--------
Building uses the make utility and the EPICS base build system.
The build system needs the location of the prerequisites, e.g. by placing the
lines of the form
PVCOMMON = /home/install/epicsV4/pvCommonCPP
EPICS_BASE = /home/install/epics/base
pointing to the locations in a file called RELEASE.local
in the configure directory or the parent directory of pvDataCPP.
With this in place, to build type make
make
To perform a clean build type
make clean uninstall
To run the unit tests type
make runtests
For more information on the EPICS build system consult the
[Application Development guide](http://www.aps.anl.gov/epics/base/R3-14/12-docs/AppDevGuide.pdf).
- General information about EPICS can be found at the
[EPICS Controls website](https://epics-controls.org).
- API documentation for this module can be found on its
[Github Pages website](https://epics-base.github.io/pvDataCPP/).
## Building
This module is included as a submodule of a full EPICS 7 release and will be compiled during builds of that software.

View File

@@ -1,4 +1,4 @@
EPICS_PVD_MAJOR_VERSION = 7
EPICS_PVD_MINOR_VERSION = 0
EPICS_PVD_MAINTENANCE_VERSION = 1
EPICS_PVD_DEVELOPMENT_FLAG = 1
EPICS_PVD_MINOR_VERSION = 1
EPICS_PVD_MAINTENANCE_VERSION = 3
EPICS_PVD_DEVELOPMENT_FLAG = 0

View File

@@ -20,9 +20,14 @@ CHECK_RELEASE = YES
# INSTALL_LOCATION here.
#INSTALL_LOCATION=</path/name/to/install/top>
USR_CPPFLAGS += -DPVD_INTERNAL
-include $(TOP)/../CONFIG_SITE.local
-include $(TOP)/configure/CONFIG_SITE.local
# MSVC - skip defining min()/max() macros
USR_CPPFLAGS_WIN32 += -DNOMINMAX
ifdef WITH_COVERAGE
USR_CPPFLAGS += --coverage
USR_LDFLAGS += --coverage

View File

@@ -26,8 +26,7 @@
#MYMODULE = $(MODULES)/my-module
# If building the EPICS modules individually, set these:
#EPICS_LIBCOM = $(MODULES)/libcom-3.17.0
#EPICS_BASE = $(MODULES)/core-7.0.1
#EPICS_BASE = /path/to/base
# Set RULES here if you want to use build rules from elsewhere:
#RULES = $(MODULES)/build-rules

View File

@@ -38,7 +38,7 @@ PROJECT_NAME = "PVData C++"
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER =
PROJECT_NUMBER = 7.1.3
# 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
@@ -319,7 +319,7 @@ AUTOLINK_SUPPORT = YES
# diagrams that involve STL classes more complete and accurate.
# The default value is: NO.
BUILTIN_STL_SUPPORT = YES
BUILTIN_STL_SUPPORT = NO
# If you use Microsoft's C++/CLI language, you should set this option to YES to
# enable parsing support.
@@ -764,11 +764,11 @@ WARN_LOGFILE =
# Note: If this tag is empty the current directory is searched.
INPUT = ../src/pv \
./mainpage.h \
./mainpage.dox \
../src/copy/pv \
../src/misc/pv \
../src/json/pv \
./release_notes.h
./release_notes.dox
# 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
@@ -1001,7 +1001,7 @@ VERBATIM_HEADERS = YES
# compiled with the --with-libclang option.
# The default value is: NO.
CLANG_ASSISTED_PARSING = NO
CLANG_ASSISTED_PARSING = YES
# If clang assisted parsing is enabled you can provide the compiler with command
# line options that you would normally use when invoking the compiler. Note that
@@ -1948,7 +1948,7 @@ ENABLE_PREPROCESSING = YES
# The default value is: NO.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
MACRO_EXPANSION = NO
MACRO_EXPANSION = YES
# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
# the macro expansion is limited to the macros specified with the PREDEFINED and
@@ -1970,7 +1970,7 @@ SEARCH_INCLUDES = YES
# preprocessor.
# This tag requires that the tag SEARCH_INCLUDES is set to YES.
INCLUDE_PATH =
INCLUDE_PATH = ../src/misc ../src ../../../include/
# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
# patterns (like *.h and *.hpp) to filter out the header-files in the
@@ -1997,7 +1997,7 @@ PREDEFINED =
# definition found in the source code.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
EXPAND_AS_DEFINED =
EXPAND_AS_DEFINED =
# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
# remove all references to function-like macros that are alone on a line, have
@@ -2007,7 +2007,7 @@ EXPAND_AS_DEFINED =
# The default value is: YES.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
SKIP_FUNCTION_MACROS = YES
SKIP_FUNCTION_MACROS = NO
#---------------------------------------------------------------------------
# Configuration options related to external references
@@ -2026,7 +2026,7 @@ SKIP_FUNCTION_MACROS = YES
# the path). If a tag file is not located in the directory in which doxygen is
# run, you must also specify the path to the tagfile here.
TAGFILES = ../../../base-git/documentation/epics-base.tag
TAGFILES = "libstdc++.tag = http://gcc.gnu.org/onlinedocs/libstdc++/latest-doxygen"
# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
# tag file that is based on the input files it reads. See section "Linking to
@@ -2045,14 +2045,14 @@ ALLEXTERNALS = NO
# listed.
# The default value is: YES.
EXTERNAL_GROUPS = YES
EXTERNAL_GROUPS = NO
# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed in
# the related pages index. If set to NO, only the current project's pages will
# be listed.
# The default value is: YES.
EXTERNAL_PAGES = YES
EXTERNAL_PAGES = NO
# The PERL_PATH should be the absolute path and name of the perl script
# interpreter (i.e. the result of 'which perl').

View File

@@ -3,11 +3,14 @@ all: gen
clean:
rm -rf doxygen_sqlite3.db html
gen:
gen: libstdc++.tag
doxygen
commit: gen
touch html/.nojekyll
./commit-gh.sh documentation/html/ html/.nojekyll html/*.* html/search/*.*
libstdc++.tag:
wget -O $@ https://gcc.gnu.org/onlinedocs/libstdc++/latest-doxygen/libstdc++.tag
.PHONY: all clean gen commit

Binary file not shown.

View File

@@ -3,7 +3,8 @@
/**
@mainpage pvDataCPP documentation
- [Download](https://sourceforge.net/projects/epics-pvdata/files/)
- This module is included in [EPICS Base releases](https://epics-controls.org/resources-and-support/base/) beginning with 7.0.1
- It may also be [Downloaded](https://github.com/epics-base/pvDataCPP/releases) and built separately.
- @htmlonly <a href="modules.html">API components</a> @endhtmlonly
- @ref release_notes
@@ -23,33 +24,32 @@ See pv/pvData.h header.
Define a structure type and create a container with default values.
@code
epics::pvData::StructureConstPtr stype; // aka std::tr1::shared_ptr<const epics::pvData::Structure>
stype = epics::pvData::getFieldCreate()->createFieldBuilder()
->add("fld1", epics::pvData::pvInt)
namespace pvd = epics::pvData;
pvd::StructureConstPtr stype(pvd::FieldBuilder::begin()
->add("fld1", pvd::pvInt)
->addNestedStructure("sub")
->add("fld2", epics::pvData::pvString)
->add("fld2", pvd::pvString)
->endNested()
->createStructure();
->createStructure());
epics::pvData::PVStructuretPtr value; // aka std::tr1::shared_ptr<epics::pvData::PVStructure>
value = epics::pvData::getPVDataCreate()->createPVStructure(stype);
pvd::PVStructuretPtr value(stype->build());
value->getSubField<epics::pvData::PVInt>("fld1")->put(4); // store integer 4
value->getSubField<epics::pvData::PVScalar>("sub.fld2")->putFrom(4.2); // convert and store string "4.2"
value->getSubFieldT<pvd::PVInt>("fld1")->put(4); // store integer 4. would throw if not pvInt
value->getSubFieldT<pvd::PVScalar>("sub.fld2")->putFrom(4.2); // convert and store string "4.2"
@endcode
is equivalent to the following pseudo-code.
@code
struct stype {
epics::pvData::int32 fld1;
pvd::int32 fld1;
struct {
std::string fld2;
} sub;
};
stype value;
value.fld1 = 4;
value.fld2 = epics::pvData::castUnsafe<std::string>(4.2);
value.fld2 = pvd::castUnsafe<std::string>(4.2);
@endcode
*/

View File

@@ -2,11 +2,32 @@
@page release_notes Release Notes
Release 7.1.0 (UNRELEASED)
==========================
Release 7.1.3 (Apr 2019)
========================
- Fix for array serialization error to/from big endian.
https://github.com/epics-base/pvDataCPP/issues/65
Release 7.1.2 (Mar 2019)
========================
- 7.1.1 tag pushed prematurely.
Release 7.1.1 (Mar 2019)
========================
- Fixes
- Init order issue with StandardField::getStandardField()
- Build fix for Visual Studio 2013+
Release 7.1.0 (Nov 2018)
========================
- Deprecations
- BoundedString, BoundedScalarArray, and FixedScalarArray will be removed unless they are fixed.
See https://github.com/epics-base/pvDataCPP/issues/52 for discussion.
- pv/localStaticLock.h
- pv/pvCopy.h (see epics::pvData::PVRequestMapper)
- Removals
- Remove previously deprecated executor.h, queue.h and timerFunction.h
- Remove *HashFunction functors to "hash" Field sub-classes which were never fully implemented.
@@ -14,14 +35,19 @@ Release 7.1.0 (UNRELEASED)
- Make thread safe getFieldCreate() and getPVDataCreate()
- Workaround for MSVC pickyness that iterators be non-NULL, even when not de-referenced.
- Fix alignment fault during (de)serialization on RTEMS/vxWorks.
- Fix shared_vector::swap() for void specialization.
- Changes in several Field sub-classes to return const ref. instead of a copy.
- Fix epics::pvData::shared_vector::swap() for void specialization.
- Changes in several epics::pvData::Field sub-classes to return const ref. instead of a copy.
- Additions
- shared_vector add c++11 move and construct for initializer list.
- Add AnyScalar::clear()
- Add ctor AnyScalar(ScalarType, const void*) to allow construction from an untyped buffer.
- Add Timer::close()
- Allow castUnsafe() from const char* without first allocating a std::string.
- epics::pvData::shared_vector add c++11 move and construct for initializer list.
- Add epics::pvData::AnyScalar::clear()
- Add ctor epics::pvData::AnyScalar(ScalarType, const void*) to allow construction from an untyped buffer.
- Add epics::pvData::Timer::close()
- Allow epics::pvData::castUnsafe() from const char* without first allocating a std::string.
- De-duplication of epics::pvData::Field instances is performed using a global hash table.
Identical definitions will share a single instance. Allows O(0) comparision.
- Add epics::pvData::PVRequestMapper to facilitate (partial) copying between PVStructure instances
modified by a pvRequest.
- Add shorthand notations epics::pvData::FieldBuilder::begin() and epics::pvData::Field::build()
Release 7.0.0 (Dec 2017)
========================
@@ -42,6 +68,9 @@ Release 7.0.0 (Dec 2017)
- Can also be constructed using an existing PVStructure to allow "editing".
- Add debugPtr.h wrapper with reference tracking to assist in troubleshooting shared_ptr related ref. loops.
- Add @ref pvjson utilities
- Add reftrack @ref pvd_reftrack
- Add header typemap.h to facilitate boilerplate switch() over ScalarType
- Add epics::auto_ptr typedef in help writing code supporting both c++98 and c++11 w/o copious deprecation warnings.
Release 6.0.1

View File

@@ -6,4 +6,5 @@ INC += pv/createRequest.h
INC += pv/pvCopy.h
LIBSRCS += createRequest.cpp
LIBSRCS += requestmapper.cpp
LIBSRCS += pvCopy.cpp

View File

@@ -318,7 +318,7 @@ struct CreateRequestImpl {
if (!request.empty()) removeBlanks(request);
if (request.empty())
{
return pvDataCreate->createPVStructure(fieldCreate->createStructure());
return fieldCreate->createStructure()->build();
}
size_t offsetRecord = request.find("record[");
size_t offsetField = request.find("field(");
@@ -437,7 +437,7 @@ struct CreateRequestImpl {
}
StructureConstPtr structure = fieldCreate->createStructure(names, fields);
if(!structure) throw std::invalid_argument("bad request " + crequest);
PVStructurePtr pvStructure = pvDataCreate->createPVStructure(structure);
PVStructurePtr pvStructure = structure->build();
for(size_t i=0; i<optionList.size(); ++i) {
OptionPair pair = optionList[i];
string name = pair.name;

View File

@@ -8,14 +8,18 @@
#define CREATEREQUEST_H
#include <string>
#include <sstream>
#include <vector>
#include <pv/pvData.h>
#include <pv/lock.h>
#include <pv/bitSet.h>
#include <shareLib.h>
namespace epics { namespace pvData {
class BitSet;
/**
* @brief Create pvRequest structure for Channel methods.
*
@@ -61,6 +65,168 @@ protected:
epicsShareExtern
PVStructure::shared_pointer createRequest(std::string const & request);
/** Helper for implementations of epics::pvAccess::ChannelProvider in interpreting the
* 'field' substructure of a pvRequest.
* Copies between an internal (base) Structure, and a client/user visible (requested) Structure.
*
* @note PVRequestMapper is not re-entrant. It is copyable and swap()able.
*/
class epicsShareClass PVRequestMapper {
public:
enum mode_t {
/** Masking mode.
*
* Requested Structure is identical to Base.
* The 'field' substructure of the provided pvRequest is used to construct a BitSet
* where the bits corresponding to the "selected" fields are set. This mask can be
* access via. requestedMask(). The copy* and mask* methods operate only
* on "selected" fields.
*/
Mask,
/** Slice mode
*
* The Requested Structure is a strict sub-set of the Base Structure containing
* those fields "selected" by the 'field' substructure of the provided pvRequest.
*/
Slice,
};
PVRequestMapper();
//! @see compute()
PVRequestMapper(const PVStructure& base,
const PVStructure& pvRequest,
mode_t mode = Mask);
//! return to state of default ctor
void reset();
//! @returns the Structure of the PVStructure previously passed to compute(). NULL if never computed()'d
inline const StructureConstPtr& base() const { return typeBase; }
//! @returns the Structure which is the selected sub-set of the base Structure. NULL if never computed()'d
inline const StructureConstPtr& requested() const { return typeRequested; }
/** A mask of all fields in the base structure which are also in the requested structure,
* and any parent/structure "compress" bits. eg. bit 0 is always set.
*
@code
PVRequestMapper mapper(...);
...
BitSet changed = ...; // a base changed mask
bool wouldcopy = changed.logical_and(mapper.requestedMask());
// wouldcopy==false means that copyBaseToRequested(..., changed, ...) would be a no-op
@endcode
*
* eg. allows early detection of empty monitor updates.
*/
inline const BitSet& requestedMask() const { return maskRequested; }
//! @returns A new instance of the requested() Structure
PVStructurePtr buildRequested() const;
//! @returns A new instance of the base() Structure
PVStructurePtr buildBase() const;
/** (re)compute the selected subset of provided base structure.
* @param base A full base structure.
* Must be "top level" (field offset zero).
* @param pvRequest The user/client provided request modifier
* @param mode Control how the mapping is constructed. @see mode_t for a description of mapping modes.
*
* @post Updates warnings()
* @throws std::runtime_error For errors involving invalid pvRequest
* @throws std::logic_error if the provided base is not a "top level" PVStructure.
*/
void compute(const PVStructure& base,
const PVStructure& pvRequest,
mode_t mode = Mask);
//! After compute(), check if !warnings().empty()
inline const std::string& warnings() const { return messages; }
/** Copy field values from Base structure into Requested structure
*
* @param base An instance of the base Structure. Field values are copied from it.
* Need not be the same instance passed to compute().
* @param baseMask A bit mask selecting those base fields to copy.
* @param request An instance of the requested() Structure. Field values are copied to it.
* @param requestMask A bit mask indicating which requested fields were copied.
* BitSet::clear() is not called.
*/
void copyBaseToRequested(
const PVStructure& base,
const BitSet& baseMask,
PVStructure& request,
BitSet& requestMask
) const;
/** Copy field values into Base structure from Requested structure
*
* @param base An instance of the base Structure. Field values are copied into it.
* Need not be the same instance passed to compute().
* @param baseMask A bit mask indicating which base fields were copied.
* BitSet::clear() is not called.
* @param request An instance of the requested() Structure. Field values are copied from it.
* @param requestMask A bit mask selecting those requested fields to copy.
*/
void copyBaseFromRequested(
PVStructure& base,
BitSet& baseMask,
const PVStructure& request,
const BitSet& requestMask
) const;
//! Translate Base bit mask into requested bit mask.
//! BitSet::clear() is not called.
inline void maskBaseToRequested(
const BitSet& baseMask,
BitSet& requestMask
) const
{ _mapMask(baseMask, requestMask, false); }
//! Translate requested bit mask into base bit mask.
//! BitSet::clear() is not called.
inline void maskBaseFromRequested(
BitSet& baseMask,
const BitSet& requestMask
) const
{ _mapMask(requestMask, baseMask, true); }
//! Exchange contents of two mappers. O(0) and never throws.
void swap(PVRequestMapper& other);
private:
bool _compute(const PVStructure& base, const PVStructure& pvReq,
FieldBuilderPtr& builder, bool keepids, unsigned depth);
void _map(const PVStructure& src,
const BitSet& maskSrc,
PVStructure& dest,
BitSet& maskDest,
bool dir_r2b) const;
void _mapMask(const BitSet& maskSrc,
BitSet& maskDest,
bool dir_r2b) const;
StructureConstPtr typeBase, typeRequested;
BitSet maskRequested;
// Map between field offsets of base and requested Structures.
// Include all fields, both leaf and sub-structure.
struct Mapping {
size_t to; // offset in destination Structure
BitSet tomask, // if !leaf these are the other bits in the destination mask to changed
frommask; // if !leaf these are the other bits in the source mask to be copied
bool valid; // only true in (sparse) base -> requested mapping
bool leaf; // not a (sub)Structure?
Mapping() :valid(false) {}
Mapping(size_t to, bool leaf) :to(to), valid(true), leaf(leaf) {}
};
typedef std::vector<Mapping> mapping_t;
mapping_t base2req, req2base;
std::string messages;
mutable BitSet scratch; // avoid temporary allocs. (we aren't re-entrant!)
};
}}
#endif /* CREATEREQUEST_H */

View File

@@ -13,6 +13,7 @@
#include <stdexcept>
#include <memory>
#include <compilerDependencies.h>
#include <shareLib.h>
#include <pv/pvData.h>
@@ -58,7 +59,7 @@ typedef std::tr1::shared_ptr<CopyStructureNode> CopyStructureNodePtr;
* Class that manages one or more PVStructures that holds an arbitrary subset of the fields
* in another PVStructure called master.
*/
class epicsShareClass PVCopy :
class epicsShareClass EPICS_DEPRECATED PVCopy :
public std::tr1::enable_shared_from_this<PVCopy>
{
public:

View File

@@ -13,6 +13,9 @@
#include <sstream>
#include <epicsThread.h>
#include <compilerDependencies.h>
#undef EPICS_DEPRECATED
#define EPICS_DEPRECATED
#define epicsExportSharedSymbols

328
src/copy/requestmapper.cpp Normal file
View File

@@ -0,0 +1,328 @@
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
#include <sstream>
#include <epicsAssert.h>
#include <epicsTypes.h>
#include <epicsVersion.h>
#include <epicsConvert.h>
#include <epicsAssert.h>
#include <epicsMutex.h>
#include <epicsGuard.h>
#define epicsExportSharedSymbols
#include <pv/createRequest.h>
#include <pv/epicsException.h>
#include <pv/bitSet.h>
// Our arbitrary limit on pvRequest structure depth to bound stack usage during recursion
static const unsigned maxDepth = 5;
namespace epics{namespace pvData {
PVRequestMapper::PVRequestMapper() {}
PVRequestMapper::PVRequestMapper(const PVStructure &base,
const PVStructure &pvRequest,
mode_t mode)
{
compute(base, pvRequest, mode);
}
PVStructurePtr PVRequestMapper::buildRequested() const
{
if(!typeRequested)
THROW_EXCEPTION2(std::logic_error, "No mapping compute()d");
return typeRequested->build();
}
PVStructurePtr PVRequestMapper::buildBase() const
{
if(!typeBase)
THROW_EXCEPTION2(std::logic_error, "No mapping compute()d");
return typeBase->build();
}
void PVRequestMapper::compute(const PVStructure &base,
const PVStructure &pvRequest,
mode_t mode)
{
if(base.getFieldOffset()!=0)
THROW_EXCEPTION2(std::logic_error, "Mapper must be used with top level PVStructure");
bool ok = true;
// we want to be transactional, which requires a second copy of everything.
PVRequestMapper temp;
// whether to preserve IDs of partial structures.
bool keepids = false;
PVScalar::const_shared_pointer pbp(pvRequest.getSubField<PVScalar>("record._options.keepIDs"));
try {
if(pbp) keepids = pbp->getAs<boolean>();
}catch(std::runtime_error& e){
std::ostringstream msg;
msg<<"Can't parse keepIDs : '"<<e.what()<<"' ";
temp.messages+=msg.str();
}
PVStructure::const_shared_pointer fields(pvRequest.getSubField<PVStructure>("field"));
if(!fields || fields->getPVFields().empty()) {
// not selection, or empty selection, treated as select all
temp.typeBase = temp.typeRequested = base.getStructure();
for(size_t i=1, N=base.getNextFieldOffset(); i<N; i++)
temp.maskRequested.set(i);
} else {
FieldBuilderPtr builder(getFieldCreate()->createFieldBuilder());
if(keepids)
builder = builder->setId(base.getStructure()->getID());
ok &= temp._compute(base, *fields, builder, keepids, 0); // fills in builder
temp.typeBase = base.getStructure();
temp.typeRequested = builder->createStructure();
// possible that typeBase==typeRequested if all fields explicitly selected
}
if(mode==Mask) {
// short circuit use of masked Structure, but keep maskRequested
temp.typeRequested = temp.typeBase;
}
{
PVStructurePtr proto(temp.typeRequested->build());
// base -> request may be sparce mapping
temp.base2req.resize(base.getNextFieldOffset());
// request -> base is dense mapping
temp.req2base.resize(proto->getNextFieldOffset());
// special handling for whole structure mapping. in part because getSubField(0) isn't allowed
temp.base2req[0] = Mapping(0, false);
temp.req2base[0] = Mapping(0, false);
// Iterate prototype of requested to map with base field offsets.
// which is handled as a special case below.
// We also don't try to prevent redundant copies if both leaf and compress bits are set.
for(size_t r=1, N=proto->getNextFieldOffset(); r<N; r++) {
PVField::const_shared_pointer fld_req(proto->getSubFieldT(r)),
fld_base(base.getSubFieldT(fld_req->getFullName()));
const size_t b = fld_base->getFieldOffset();
if(!temp.requestedMask().get(b))
continue;
bool leaf = fld_base->getField()->getType()!=structure;
// initialize mapping when our bit is set
temp.base2req[b] = Mapping(r, leaf);
temp.req2base[r] = Mapping(b, leaf);
// add ourself to all "compress" bit mappings of enclosing structures
for(const PVStructure *parent = fld_req->getParent(); parent; parent = parent->getParent()) {
temp.req2base[parent->getFieldOffset()].tomask .set(b);
temp.req2base[parent->getFieldOffset()].frommask.set(r);
}
for(const PVStructure *parent = fld_base->getParent(); parent; parent = parent->getParent()) {
temp.base2req[parent->getFieldOffset()].tomask .set(r);
temp.base2req[parent->getFieldOffset()].frommask.set(b);
}
}
}
temp.maskRequested.set(0);
if(temp.maskRequested.nextSetBit(1)==-1) {
ok = false;
temp.messages+="Empty field selection";
}
if(!ok)
throw std::runtime_error(temp.messages);
swap(temp);
}
bool PVRequestMapper::_compute(const PVStructure& base, const PVStructure& pvReq,
FieldBuilderPtr& builder, bool keepids, unsigned depth)
{
bool ok = true;
const StringArray& reqNames = pvReq.getStructure()->getFieldNames();
for(size_t i=0, N=reqNames.size(); i<N; i++) {
// iterate through requested fields
PVField::const_shared_pointer subtype(base.getSubField(reqNames[i]));
const FieldConstPtr& subReq = pvReq.getStructure()->getFields()[i];
if(subReq->getType()!=structure) {
// pvRequest .field was not properly composed
std::ostringstream msg;
// not a great warning message as it doesn't distinguish 'a.value' from 'b.value',
// but getFullName() whould prefix with 'field.', which would probably cause
// more frequent confusion...
msg<<"request invalid '"<<pvReq.getStructure()->getFieldNames()[i]<<"' ";
messages+=msg.str();
ok = false;
} else if(!subtype) {
// requested field does not actually exist in base
std::ostringstream msg;
msg<<"No field '"<<pvReq.getStructure()->getFieldNames()[i]<<"' ";
messages+=msg.str();
} else if(depth>=maxDepth // exceeds max recursion depth
|| subtype->getField()->getType()!=structure // requested field is a leaf
|| static_cast<const Structure&>(*subReq).getFieldNames().empty() // requests all sub-fields
)
{
// just add the whole thing
builder = builder->add(reqNames[i], subtype->getField());
for(size_t j=subtype->getFieldOffset(), N=subtype->getNextFieldOffset(); j<N; j++)
maskRequested.set(j);
if(subtype->getField()->getType()!=structure
&& !static_cast<const Structure&>(*subReq).getFieldNames().empty())
{
// attempt to select below a leaf field
std::ostringstream msg;
msg<<"Leaf field '"<<pvReq.getFullName()<<"' ";
messages+=msg.str();
} else if(depth>=maxDepth) {
std::ostringstream msg;
msg<<"selection truncated at '"<<pvReq.getFullName()<<"' ";
messages+=msg.str();
}
} else {
// recurse into sub-structure
const PVStructure& substruct = static_cast<const PVStructure&>(*subtype);
builder = builder->addNestedStructure(reqNames[i]);
maskRequested.set(substruct.getFieldOffset());
if(keepids)
builder = builder->setId(substruct.getStructure()->getID());
_compute(substruct,
static_cast<const PVStructure&>(*pvReq.getPVFields()[i]),
builder, keepids, depth+1u);
builder = builder->endNested();
}
}
return ok;
}
void PVRequestMapper::copyBaseToRequested(
const PVStructure& base,
const BitSet& baseMask,
PVStructure& request,
BitSet& requestMask
) const {
assert(base.getStructure()==typeBase);
assert(request.getStructure()==typeRequested);
_map(base, baseMask, request, requestMask, false);
}
void PVRequestMapper::copyBaseFromRequested(
PVStructure& base,
BitSet& baseMask,
const PVStructure& request,
const BitSet& requestMask
) const {
assert(base.getStructure()==typeBase);
assert(request.getStructure()==typeRequested);
_map(request, requestMask, base, baseMask, true);
}
void PVRequestMapper::_map(const PVStructure& src, const BitSet& maskSrc,
PVStructure& dest, BitSet& maskDest,
bool dir_r2b) const
{
{
scratch = maskSrc;
const mapping_t& map = dir_r2b ? req2base : base2req;
assert(map.size()==src.getNumberFields());
for(int32 i=scratch.nextSetBit(0), N=map.size(); i>=0 && i<N; i=scratch.nextSetBit(i+1)) {
const Mapping& M = map[i];
if(!M.valid) {
assert(!dir_r2b); // only base -> requested mapping can have holes
} else if(M.leaf) {
// just copy
dest.getSubFieldT(M.to)->copy(*src.getSubFieldT(i));
maskDest.set(M.to);
} else {
// set bits of all sub-fields (in requested structure)
// these indicies are always >i
scratch |= M.frommask;
// we will also set the individual bits, but if a compress bit is set in the input,
// then set the corresponding bit in the output.
maskDest.set(M.to);
}
}
}
}
void PVRequestMapper::_mapMask(const BitSet& maskSrc,
BitSet& maskDest,
bool dir_r2b) const
{
if(maskSrc.isEmpty()) {
// no-op
} else {
const mapping_t& map = dir_r2b ? req2base : base2req;
for(int32 i=maskSrc.nextSetBit(0), N=map.size(); i>=0 && i<N; i=maskSrc.nextSetBit(i+1)) {
const Mapping& M = map[i];
if(!M.valid) {
assert(!dir_r2b); // only base -> requested mapping can have holes
} else {
maskDest.set(M.to);
if(!M.leaf) {
maskDest |= M.tomask;
}
}
}
}
}
void PVRequestMapper::swap(PVRequestMapper& other)
{
typeBase.swap(other.typeBase);
typeRequested.swap(other.typeRequested);
maskRequested.swap(other.maskRequested);
base2req.swap(other.base2req);
req2base.swap(other.req2base);
messages.swap(other.messages);
scratch.swap(other.scratch); // paranoia
}
void PVRequestMapper::reset()
{
typeBase.reset();
typeRequested.reset();
maskRequested.clear();
base2req.clear();
req2base.clear();
messages.clear();
scratch.clear(); // paranoia
}
}} //namespace epics::pvData

View File

@@ -23,7 +23,7 @@ namespace epics { namespace pvData {
* 1) same instance
* 2) same type (field and scalar/element), same name, same subfields (if any)
*/
bool operator==(const Field& a, const Field& b)
bool compare(const Field& a, const Field& b)
{
if(&a==&b)
return true;
@@ -33,53 +33,53 @@ bool operator==(const Field& a, const Field& b)
case scalar: {
const Scalar &A=static_cast<const Scalar&>(a);
const Scalar &B=static_cast<const Scalar&>(b);
return A==B;
return compare(A, B);
}
case scalarArray: {
const ScalarArray &A=static_cast<const ScalarArray&>(a);
const ScalarArray &B=static_cast<const ScalarArray&>(b);
return A==B;
return compare(A, B);
}
case structure: {
const Structure &A=static_cast<const Structure&>(a);
const Structure &B=static_cast<const Structure&>(b);
return A==B;
return compare(A, B);
}
case structureArray: {
const StructureArray &A=static_cast<const StructureArray&>(a);
const StructureArray &B=static_cast<const StructureArray&>(b);
return A==B;
return compare(A, B);
}
case union_: {
const Union &A=static_cast<const Union&>(a);
const Union &B=static_cast<const Union&>(b);
return A==B;
return compare(A, B);
}
case unionArray: {
const UnionArray &A=static_cast<const UnionArray&>(a);
const UnionArray &B=static_cast<const UnionArray&>(b);
return A==B;
return compare(A, B);
}
default:
throw std::logic_error("Invalid Field type in comparison");
}
}
bool operator==(const Scalar& a, const Scalar& b)
bool compare(const Scalar& a, const Scalar& b)
{
if(&a==&b)
return true;
return a.getScalarType()==b.getScalarType();
}
bool operator==(const ScalarArray& a, const ScalarArray& b)
bool compare(const ScalarArray& a, const ScalarArray& b)
{
if(&a==&b)
return true;
return a.getElementType()==b.getElementType();
}
bool operator==(const Structure& a, const Structure& b)
bool compare(const Structure& a, const Structure& b)
{
if(&a==&b)
return true;
@@ -101,12 +101,12 @@ bool operator==(const Structure& a, const Structure& b)
return std::equal( an.begin(), an.end(), bn.begin() );
}
bool operator==(const StructureArray& a, const StructureArray& b)
bool compare(const StructureArray& a, const StructureArray& b)
{
return *(a.getStructure().get())==*(b.getStructure().get());
}
bool operator==(const Union& a, const Union& b)
bool compare(const Union& a, const Union& b)
{
if(&a==&b)
return true;
@@ -128,12 +128,12 @@ bool operator==(const Union& a, const Union& b)
return std::equal( an.begin(), an.end(), bn.begin() );
}
bool operator==(const UnionArray& a, const UnionArray& b)
bool compare(const UnionArray& a, const UnionArray& b)
{
return *(a.getUnion().get())==*(b.getUnion().get());
}
bool operator==(const BoundedString& a, const BoundedString& b)
bool compare(const BoundedString& a, const BoundedString& b)
{
if(&a==&b)
return true;

View File

@@ -7,10 +7,6 @@
* @author mrk
*/
#if defined(_WIN32) && !defined(NOMINMAX)
#define NOMINMAX
#endif
#include <cstddef>
#include <cstdlib>
#include <string>
@@ -18,6 +14,7 @@
#include <stdexcept>
#include <sstream>
#include <epicsString.h>
#include <epicsMutex.h>
#include <epicsThread.h>
@@ -27,6 +24,8 @@
#include <pv/pvIntrospect.h>
#include <pv/factory.h>
#include <pv/serializeHelper.h>
#include <pv/thread.h>
#include <pv/pvData.h>
using std::tr1::static_pointer_cast;
using std::size_t;
@@ -34,21 +33,79 @@ using std::string;
namespace epics { namespace pvData {
static DebugLevel debugLevel = lowDebug;
size_t Field::num_instances;
struct Field::Helper {
static unsigned hash(Field *fld) {
std::ostringstream key;
// hash the output of operator<<()
// not efficient, but stable within this process.
key<<(*fld);
unsigned H = epicsStrHash(key.str().c_str(), 0xbadc0de1);
fld->m_hash = H;
return H;
}
};
struct FieldCreate::Helper {
template<typename FLD>
static void cache(const FieldCreate *create, std::tr1::shared_ptr<FLD>& ent) {
unsigned hash = Field::Helper::hash(ent.get());
Lock G(create->mutex);
// we examine raw pointers stored in create->cache, which is safe under create->mutex
std::pair<cache_t::iterator, cache_t::iterator> itp(create->cache.equal_range(hash));
for(; itp.first!=itp.second; ++itp.first) {
Field* cent(itp.first->second);
FLD* centx(dynamic_cast<FLD*>(cent));
if(centx && compare(*centx, *ent)) {
try{
ent = std::tr1::static_pointer_cast<FLD>(cent->shared_from_this());
return;
}catch(std::tr1::bad_weak_ptr&){
// we're racing destruction.
// add a new entry.
// Field::~Field is in the process of removing this old one.
continue;
}
}
}
create->cache.insert(std::make_pair(hash, ent.get()));
// cache cleaned from Field::~Field
}
};
Field::Field(Type type)
: m_fieldType(type)
, m_hash(0)
{
REFTRACE_INCREMENT(num_instances);
}
Field::~Field() {
REFTRACE_DECREMENT(num_instances);
FieldCreatePtr create(getFieldCreate());
Lock G(create->mutex);
std::pair<FieldCreate::cache_t::iterator, FieldCreate::cache_t::iterator> itp(create->cache.equal_range(m_hash));
for(; itp.first!=itp.second; ++itp.first) {
Field* cent(itp.first->second);
if(cent==this) {
create->cache.erase(itp.first);
return;
}
}
}
std::tr1::shared_ptr<PVField> Field::build() const
{
FieldConstPtr self(shared_from_this());
return getPVDataCreate()->createPVField(self);
}
std::ostream& operator<<(std::ostream& o, const Field& f)
{
@@ -119,6 +176,11 @@ void Scalar::deserialize(ByteBuffer* /*buffer*/, DeserializableControl* /*contro
}
std::tr1::shared_ptr<PVScalar> Scalar::build() const
{
return getPVDataCreate()->createPVScalar(std::tr1::static_pointer_cast<const Scalar>(shared_from_this()));
}
std::string BoundedString::getID() const
@@ -285,6 +347,11 @@ void ScalarArray::deserialize(ByteBuffer* /*buffer*/, DeserializableControl* /*c
throw std::runtime_error("not valid operation, use FieldCreate::deserialize instead");
}
std::tr1::shared_ptr<PVScalarArray> ScalarArray::build() const
{
return getPVDataCreate()->createPVScalarArray(std::tr1::static_pointer_cast<const ScalarArray>(shared_from_this()));
}
BoundedScalarArray::~BoundedScalarArray() {}
@@ -336,9 +403,7 @@ StructureArray::StructureArray(StructureConstPtr const & structure)
{
}
StructureArray::~StructureArray() {
if(debugLevel==highDebug) printf("~StructureArray\n");
}
StructureArray::~StructureArray() {}
string StructureArray::getID() const
{
@@ -365,14 +430,17 @@ void StructureArray::deserialize(ByteBuffer* /*buffer*/, DeserializableControl*
throw std::runtime_error("not valid operation, use FieldCreate::deserialize instead");
}
std::tr1::shared_ptr<PVValueArray<std::tr1::shared_ptr<PVStructure> > > StructureArray::build() const
{
return getPVDataCreate()->createPVStructureArray(std::tr1::static_pointer_cast<const StructureArray>(shared_from_this()));
}
UnionArray::UnionArray(UnionConstPtr const & _punion)
: Array(unionArray),punion(_punion)
{
}
UnionArray::~UnionArray() {
if(debugLevel==highDebug) printf("~UnionArray\n");
}
UnionArray::~UnionArray() {}
string UnionArray::getID() const
{
@@ -407,6 +475,11 @@ void UnionArray::deserialize(ByteBuffer* /*buffer*/, DeserializableControl* /*co
throw std::runtime_error("not valid operation, use FieldCreate::deserialize instead");
}
std::tr1::shared_ptr<PVValueArray<std::tr1::shared_ptr<PVUnion> > > UnionArray::build() const
{
return getPVDataCreate()->createPVUnionArray(std::tr1::static_pointer_cast<const UnionArray>(shared_from_this()));
}
const string Structure::DEFAULT_ID = Structure::defaultId();
const string & Structure::defaultId()
@@ -544,6 +617,11 @@ void Structure::deserialize(ByteBuffer* /*buffer*/, DeserializableControl* /*con
throw std::runtime_error("not valid operation, use FieldCreate::deserialize instead");
}
std::tr1::shared_ptr<PVStructure> Structure::build() const
{
return getPVDataCreate()->createPVStructure(std::tr1::static_pointer_cast<const Structure>(shared_from_this()));
}
const string Union::DEFAULT_ID = Union::defaultId();
const string & Union::defaultId()
@@ -742,6 +820,11 @@ void Union::deserialize(ByteBuffer* /*buffer*/, DeserializableControl* /*control
throw std::runtime_error("not valid operation, use FieldCreate::deserialize instead");
}
std::tr1::shared_ptr<PVUnion> Union::build() const
{
return getPVDataCreate()->createPVUnion(std::tr1::static_pointer_cast<const Union>(shared_from_this()));
}
FieldBuilder::FieldBuilder()
:fieldCreate(getFieldCreate())
,idSet(false)
@@ -843,6 +926,19 @@ void FieldBuilder::reset()
fields.clear();
}
FieldBuilderPtr FieldBuilder::begin()
{
FieldBuilderPtr ret(new FieldBuilder);
return ret;
}
FieldBuilderPtr FieldBuilder::begin(StructureConstPtr S)
{
FieldBuilderPtr ret(new FieldBuilder(S.get()));
return ret;
}
FieldBuilderPtr FieldBuilder::setId(string const & id)
{
this->id = id;
@@ -1079,10 +1175,9 @@ ScalarConstPtr FieldCreate::createScalar(ScalarType scalarType) const
BoundedStringConstPtr FieldCreate::createBoundedString(std::size_t maxLength) const
{
// TODO use std::make_shared
std::tr1::shared_ptr<BoundedString> s(new BoundedString(maxLength), Field::Deleter());
BoundedStringConstPtr sa = s;
return sa;
std::tr1::shared_ptr<BoundedString> s(new BoundedString(maxLength));
Helper::cache(this, s);
return s;
}
ScalarArrayConstPtr FieldCreate::createScalarArray(ScalarType elementType) const
@@ -1104,10 +1199,9 @@ ScalarArrayConstPtr FieldCreate::createFixedScalarArray(ScalarType elementType,
THROW_EXCEPTION2(std::invalid_argument, strm.str());
}
// TODO use std::make_shared
std::tr1::shared_ptr<ScalarArray> s(new FixedScalarArray(elementType, size), Field::Deleter());
ScalarArrayConstPtr sa = s;
return sa;
std::tr1::shared_ptr<ScalarArray> s(new FixedScalarArray(elementType, size));
Helper::cache(this, s);
return s;
}
ScalarArrayConstPtr FieldCreate::createBoundedScalarArray(ScalarType elementType, size_t size) const
@@ -1118,10 +1212,9 @@ ScalarArrayConstPtr FieldCreate::createBoundedScalarArray(ScalarType elementType
THROW_EXCEPTION2(std::invalid_argument, strm.str());
}
// TODO use std::make_shared
std::tr1::shared_ptr<ScalarArray> s(new BoundedScalarArray(elementType, size), Field::Deleter());
ScalarArrayConstPtr sa = s;
return sa;
std::tr1::shared_ptr<ScalarArray> s(new BoundedScalarArray(elementType, size));
Helper::cache(this, s);
return s;
}
StructureConstPtr FieldCreate::createStructure () const
@@ -1178,10 +1271,9 @@ StructureConstPtr FieldCreate::createStructure (
StringArray const & fieldNames,FieldConstPtrArray const & fields) const
{
validateFieldNames(fieldNames);
// TODO use std::make_shared
std::tr1::shared_ptr<Structure> sp(new Structure(fieldNames,fields), Field::Deleter());
StructureConstPtr structure = sp;
return structure;
std::tr1::shared_ptr<Structure> sp(new Structure(fieldNames,fields));
Helper::cache(this, sp);
return sp;
}
StructureConstPtr FieldCreate::createStructure (
@@ -1190,29 +1282,26 @@ StructureConstPtr FieldCreate::createStructure (
FieldConstPtrArray const & fields) const
{
validateFieldNames(fieldNames);
// TODO use std::make_shared
std::tr1::shared_ptr<Structure> sp(new Structure(fieldNames,fields,id), Field::Deleter());
StructureConstPtr structure = sp;
return structure;
std::tr1::shared_ptr<Structure> sp(new Structure(fieldNames,fields,id));
Helper::cache(this, sp);
return sp;
}
StructureArrayConstPtr FieldCreate::createStructureArray(
StructureConstPtr const & structure) const
{
// TODO use std::make_shared
std::tr1::shared_ptr<StructureArray> sp(new StructureArray(structure), Field::Deleter());
StructureArrayConstPtr structureArray = sp;
return structureArray;
std::tr1::shared_ptr<StructureArray> sp(new StructureArray(structure));
Helper::cache(this, sp);
return sp;
}
UnionConstPtr FieldCreate::createUnion (
StringArray const & fieldNames,FieldConstPtrArray const & fields) const
{
validateFieldNames(fieldNames);
// TODO use std::make_shared
std::tr1::shared_ptr<Union> sp(new Union(fieldNames,fields), Field::Deleter());
UnionConstPtr punion = sp;
return punion;
std::tr1::shared_ptr<Union> sp(new Union(fieldNames,fields));
Helper::cache(this, sp);
return sp;
}
UnionConstPtr FieldCreate::createUnion (
@@ -1221,10 +1310,9 @@ UnionConstPtr FieldCreate::createUnion (
FieldConstPtrArray const & fields) const
{
validateFieldNames(fieldNames);
// TODO use std::make_shared
std::tr1::shared_ptr<Union> sp(new Union(fieldNames,fields,id), Field::Deleter());
UnionConstPtr punion = sp;
return punion;
std::tr1::shared_ptr<Union> sp(new Union(fieldNames,fields,id));
Helper::cache(this, sp);
return sp;
}
UnionConstPtr FieldCreate::createVariantUnion () const
@@ -1235,10 +1323,9 @@ UnionConstPtr FieldCreate::createVariantUnion () const
UnionArrayConstPtr FieldCreate::createUnionArray(
UnionConstPtr const & punion) const
{
// TODO use std::make_shared
std::tr1::shared_ptr<UnionArray> sp(new UnionArray(punion), Field::Deleter());
UnionArrayConstPtr unionArray = sp;
return unionArray;
std::tr1::shared_ptr<UnionArray> sp(new UnionArray(punion));
Helper::cache(this, sp);
return sp;
}
UnionArrayConstPtr FieldCreate::createVariantUnionArray () const
@@ -1367,12 +1454,10 @@ FieldConstPtr FieldCreate::deserialize(ByteBuffer* buffer, DeserializableControl
size_t size = SerializeHelper::readSize(buffer, control);
// TODO use std::make_shared
std::tr1::shared_ptr<Field> sp(
new BoundedString(size),
Field::Deleter());
FieldConstPtr p = sp;
return p;
new BoundedString(size));
Helper::cache(this, sp);
return sp;
}
else
throw std::invalid_argument("invalid type encoding");
@@ -1397,19 +1482,17 @@ FieldConstPtr FieldCreate::deserialize(ByteBuffer* buffer, DeserializableControl
{
// TODO use std::make_shared
std::tr1::shared_ptr<Field> sp(
new FixedScalarArray(static_cast<epics::pvData::ScalarType>(scalarType), size),
Field::Deleter());
FieldConstPtr p = sp;
return p;
new FixedScalarArray(static_cast<epics::pvData::ScalarType>(scalarType), size));
Helper::cache(this, sp);
return sp;
}
else
{
// TODO use std::make_shared
std::tr1::shared_ptr<Field> sp(
new BoundedScalarArray(static_cast<epics::pvData::ScalarType>(scalarType), size),
Field::Deleter());
FieldConstPtr p = sp;
return p;
new BoundedScalarArray(static_cast<epics::pvData::ScalarType>(scalarType), size));
Helper::cache(this, sp);
return sp;
}
}
else if (typeCode == 0x80)
@@ -1421,9 +1504,9 @@ FieldConstPtr FieldCreate::deserialize(ByteBuffer* buffer, DeserializableControl
// Type type = Type.structureArray;
StructureConstPtr elementStructure = std::tr1::static_pointer_cast<const Structure>(control->cachedDeserialize(buffer));
// TODO use std::make_shared
std::tr1::shared_ptr<Field> sp(new StructureArray(elementStructure), Field::Deleter());
FieldConstPtr p = sp;
return p;
std::tr1::shared_ptr<Field> sp(new StructureArray(elementStructure));
Helper::cache(this, sp);
return sp;
}
else if (typeCode == 0x81)
{
@@ -1434,9 +1517,9 @@ FieldConstPtr FieldCreate::deserialize(ByteBuffer* buffer, DeserializableControl
// Type type = Type.unionArray;
UnionConstPtr elementUnion = std::tr1::static_pointer_cast<const Union>(control->cachedDeserialize(buffer));
// TODO use std::make_shared
std::tr1::shared_ptr<Field> sp(new UnionArray(elementUnion), Field::Deleter());
FieldConstPtr p = sp;
return p;
std::tr1::shared_ptr<Field> sp(new UnionArray(elementUnion));
Helper::cache(this, sp);
return sp;
}
else if (typeCode == 0x82)
{
@@ -1457,6 +1540,7 @@ struct field_factory {
FieldCreatePtr fieldCreate;
field_factory() :fieldCreate(new FieldCreate()) {
registerRefCounter("Field", &Field::num_instances);
registerRefCounter("Thread", &Thread::num_instances);
}
};
}
@@ -1485,23 +1569,21 @@ FieldCreate::FieldCreate()
{
for (int i = 0; i <= MAX_SCALAR_TYPE; i++)
{
// TODO use std::make_shared
std::tr1::shared_ptr<Scalar> sp(new Scalar(static_cast<ScalarType>(i)), Field::Deleter());
ScalarConstPtr p = sp;
scalars.push_back(p);
std::tr1::shared_ptr<Scalar> sp(new Scalar(static_cast<ScalarType>(i)));
Helper::cache(this, sp);
scalars.push_back(sp);
// TODO use std::make_shared
std::tr1::shared_ptr<ScalarArray> spa(new ScalarArray(static_cast<ScalarType>(i)), Field::Deleter());
ScalarArrayConstPtr pa = spa;
std::tr1::shared_ptr<ScalarArray> spa(new ScalarArray(static_cast<ScalarType>(i)));
Helper::cache(this, spa);
scalarArrays.push_back(spa);
}
// TODO use std::make_shared
std::tr1::shared_ptr<Union> su(new Union(), Field::Deleter());
std::tr1::shared_ptr<Union> su(new Union());
Helper::cache(this, su);
variantUnion = su;
// TODO use std::make_shared
std::tr1::shared_ptr<UnionArray> sua(new UnionArray(variantUnion), Field::Deleter());
std::tr1::shared_ptr<UnionArray> sua(new UnionArray(variantUnion));
Helper::cache(this, sua);
variantUnionArray = sua;
}

View File

@@ -7,10 +7,6 @@
* @author mrk
*/
#if defined(_WIN32) && !defined(NOMINMAX)
#define NOMINMAX
#endif
#include <cstddef>
#include <cstdlib>
#include <string>
@@ -65,6 +61,48 @@ template<> const ScalarType PVStringArray::typeCode = pvString;
template<typename T>
PVScalarValue<T>::~PVScalarValue() {}
template<typename T>
std::ostream& PVScalarValue<T>::dumpValue(std::ostream& o) const
{
return o << get();
}
template<typename T>
void PVScalarValue<T>::operator>>=(T& value) const
{
value = get();
}
template<typename T>
void PVScalarValue<T>::operator<<=(typename storage_t::arg_type value)
{
put(value);
}
template<typename T>
void PVScalarValue<T>::assign(const PVScalar& scalar)
{
if(isImmutable())
throw std::invalid_argument("destination is immutable");
copyUnchecked(scalar);
}
template<typename T>
void PVScalarValue<T>::copy(const PVScalar& from)
{
assign(from);
}
template<typename T>
void PVScalarValue<T>::copyUnchecked(const PVScalar& from)
{
if(this==&from)
return;
T result;
from.getAs((void*)&result, typeCode);
put(result);
}
template<typename T>
void PVScalarValue<T>::serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher) const {
@@ -104,6 +142,13 @@ PVString::PVString(ScalarConstPtr const & scalar)
storage.maxLength = 0;
}
std::ostream& PVString::dumpValue(std::ostream& o) const
{
// we escape, but do not quote, for scalar string
o<<escape(get());
return o;
}
/* mixing overrides (virtual functions) and overloads (different argument lists) is fun...
* we override all overloads to avoid the "hides overloaded virtual function" warning from clang.
* In this case we don't need/want to, so just delegate to the base class.
@@ -160,6 +205,59 @@ PVValueArray<PVUnionPtr>::PVValueArray(UnionArrayConstPtr const & unionArray)
,unionArray(unionArray)
{}
template<typename T>
PVValueArray<T>::~PVValueArray() {}
template<typename T>
ArrayConstPtr PVValueArray<T>::getArray() const
{
return std::tr1::static_pointer_cast<const Array>(this->getField());
}
template<typename T>
std::ostream& PVValueArray<T>::dumpValue(std::ostream& o) const
{
const_svector v(this->view());
typename const_svector::const_iterator it(v.begin()),
end(v.end());
o << '[';
if(it!=end) {
o << print_cast(*it++);
for(; it!=end; ++it)
o << ',' << print_cast(*it);
}
return o << ']';
}
template<>
std::ostream& PVValueArray<std::string>::dumpValue(std::ostream& o, size_t index) const
{
return o << '"' << escape(this->view().at(index)) << '"';
}
template<>
std::ostream& PVValueArray<std::string>::dumpValue(std::ostream& o) const
{
const_svector v(this->view());
const_svector::const_iterator it(v.begin()),
end(v.end());
o << '[';
if(it!=end) {
o << '"' << escape(*it++) << '"';
for(; it!=end; ++it)
o << ", \"" << escape(*it) << '"';
}
return o << ']';
}
template<typename T>
std::ostream& PVValueArray<T>::dumpValue(std::ostream& o, size_t index) const
{
return o << print_cast(this->view().at(index));
}
template<typename T>
void PVValueArray<T>::setCapacity(size_t capacity)
{
@@ -352,6 +450,18 @@ void PVValueArray<string>::serialize(ByteBuffer *pbuffer,
}
}
template<typename T>
void PVValueArray<T>::_getAsVoid(epics::pvData::shared_vector<const void>& out) const
{
out = static_shared_vector_cast<const void>(this->view());
}
template<typename T>
void PVValueArray<T>::_putFromVoid(const epics::pvData::shared_vector<const void>& in)
{
this->replace(shared_vector_convert<const T>(in));
}
// Factory
PVDataCreate::PVDataCreate()

View File

@@ -184,62 +184,16 @@ void PVField::copy(const PVField& from)
if(isImmutable())
throw std::invalid_argument("destination is immutable");
if (getField()->getType() != from.getField()->getType())
if (getField() != from.getField())
throw std::invalid_argument("field types do not match");
switch(getField()->getType())
{
case scalar:
{
const PVScalar* fromS = static_cast<const PVScalar*>(&from);
PVScalar* toS = static_cast<PVScalar*>(this);
toS->copy(*fromS);
break;
}
case scalarArray:
{
const PVScalarArray* fromS = static_cast<const PVScalarArray*>(&from);
PVScalarArray* toS = static_cast<PVScalarArray*>(this);
toS->copy(*fromS);
break;
}
case structure:
{
const PVStructure* fromS = static_cast<const PVStructure*>(&from);
PVStructure* toS = static_cast<PVStructure*>(this);
toS->copy(*fromS);
break;
}
case structureArray:
{
const PVStructureArray* fromS = static_cast<const PVStructureArray*>(&from);
PVStructureArray* toS = static_cast<PVStructureArray*>(this);
toS->copy(*fromS);
break;
}
case union_:
{
const PVUnion* fromS = static_cast<const PVUnion*>(&from);
PVUnion* toS = static_cast<PVUnion*>(this);
toS->copy(*fromS);
break;
}
case unionArray:
{
const PVUnionArray* fromS = static_cast<const PVUnionArray*>(&from);
PVUnionArray* toS = static_cast<PVUnionArray*>(this);
toS->copy(*fromS);
break;
}
default:
{
throw std::logic_error("PVField::copy unknown type");
}
}
copyUnchecked(from);
}
void PVField::copyUnchecked(const PVField& from)
{
assert(getField()==from.getField());
switch(getField()->getType())
{
case scalar:

View File

@@ -60,9 +60,7 @@ PVStructure::PVStructure(StructureConstPtr const & structurePtr,
}
}
PVStructure::~PVStructure()
{
}
PVStructure::~PVStructure() {}
void PVStructure::setImmutable()
{
@@ -74,16 +72,6 @@ void PVStructure::setImmutable()
PVField::setImmutable();
}
const StructureConstPtr& PVStructure::getStructure() const
{
return structurePtr;
}
const PVFieldPtrArray & PVStructure::getPVFields() const
{
return pvFields;
}
PVFieldPtr PVStructure::getSubFieldImpl(size_t fieldOffset, bool throws) const
{
const PVStructure *current = this;

View File

@@ -11,6 +11,7 @@
#include <stdexcept>
#include <epicsMutex.h>
#include <epicsThread.h>
#define epicsExportSharedSymbols
#include <pv/lock.h>
@@ -22,32 +23,85 @@ using std::string;
namespace epics { namespace pvData {
static
StructureConstPtr buildValueAlarm(ScalarType vtype)
{
return FieldBuilder::begin()
->setId("valueAlarm_t")
->add("active", pvBoolean)
->add("lowAlarmLimit", vtype)
->add("lowWarningLimit", vtype)
->add("highWarningLimit", vtype)
->add("highAlarmLimit", vtype)
->add("lowAlarmSeverity", pvInt)
->add("lowWarningSeverity", pvInt)
->add("highWarningSeverity", pvInt)
->add("highAlarmSeverity", pvInt)
->add("hysteresis", pvByte)
->createStructure();
}
StandardField::StandardField()
: fieldCreate(getFieldCreate()),
notImplemented("not implemented"),
valueFieldName("value")
{}
:fieldCreate(getFieldCreate())
,notImplemented("not implemented")
,valueFieldName("value")
void StandardField::init()
{
createAlarm();
createTimeStamp();
createDisplay();
createControl();
createBooleanAlarm();
createByteAlarm();
createShortAlarm();
createIntAlarm();
createLongAlarm();
createUByteAlarm();
createUShortAlarm();
createUIntAlarm();
createULongAlarm();
createFloatAlarm();
createDoubleAlarm();
createEnumeratedAlarm();
}
,alarmField(FieldBuilder::begin()
->setId("alarm_t")
->add("severity", pvInt)
->add("status", pvInt)
->add("message", pvString)
->createStructure())
,timeStampField(FieldBuilder::begin()
->setId("time_t")
->add("secondsPastEpoch", pvLong)
->add("nanoseconds", pvInt)
->add("userTag", pvInt)
->createStructure())
,displayField(FieldBuilder::begin()
->setId("display_t")
->add("limitLow", pvDouble)
->add("limitHigh", pvDouble)
->add("description", pvString)
->add("format", pvString)
->add("units", pvString)
->createStructure())
,controlField(FieldBuilder::begin()
->setId("control_t")
->add("limitLow", pvDouble)
->add("limitHigh", pvDouble)
->add("minStep", pvDouble)
->createStructure())
,booleanAlarmField(FieldBuilder::begin()
->setId("valueAlarm_t")
->add("active", pvBoolean)
->add("falseSeverity", pvInt)
->add("trueSeverity", pvInt)
->add("changeStateSeverity", pvInt)
->createStructure())
,byteAlarmField(buildValueAlarm(pvByte))
,shortAlarmField(buildValueAlarm(pvShort))
,intAlarmField(buildValueAlarm(pvInt))
,longAlarmField(buildValueAlarm(pvLong))
,ubyteAlarmField(buildValueAlarm(pvUByte))
,ushortAlarmField(buildValueAlarm(pvUShort))
,uintAlarmField(buildValueAlarm(pvUInt))
,ulongAlarmField(buildValueAlarm(pvULong))
,floatAlarmField(buildValueAlarm(pvFloat))
,doubleAlarmField(buildValueAlarm(pvDouble))
,enumeratedAlarmField(FieldBuilder::begin()
->setId("valueAlarm_t")
->add("active", pvBoolean)
->add("stateSeverity", pvInt)
->add("changeStateSeverity", pvInt)
->createStructure())
{}
StandardField::~StandardField(){}
@@ -145,361 +199,6 @@ StructureConstPtr StandardField::createProperties(string id,FieldConstPtr field,
return fieldCreate->createStructure(id,names,fields);
}
void StandardField::createAlarm() {
size_t num = 3;
FieldConstPtrArray fields(num);
StringArray names(num);
names[0] = "severity";
names[1] = "status";
names[2] = "message";
fields[0] = fieldCreate->createScalar(pvInt);
fields[1] = fieldCreate->createScalar(pvInt);
fields[2] = fieldCreate->createScalar(pvString);
alarmField = fieldCreate->createStructure("alarm_t",names,fields);
}
void StandardField::createTimeStamp() {
size_t num = 3;
FieldConstPtrArray fields(num);
StringArray names(num);
names[0] = "secondsPastEpoch";
names[1] = "nanoseconds";
names[2] = "userTag";
fields[0] = fieldCreate->createScalar(pvLong);
fields[1] = fieldCreate->createScalar(pvInt);
fields[2] = fieldCreate->createScalar(pvInt);
timeStampField = fieldCreate->createStructure("time_t",names,fields);
}
void StandardField::createDisplay() {
size_t num = 5;
FieldConstPtrArray fields(num);
StringArray names(num);
names[0] = "limitLow";
names[1] = "limitHigh";
names[2] = "description";
names[3] = "format";
names[4] = "units";
fields[0] = fieldCreate->createScalar(pvDouble);
fields[1] = fieldCreate->createScalar(pvDouble);
fields[2] = fieldCreate->createScalar(pvString);
fields[3] = fieldCreate->createScalar(pvString);
fields[4] = fieldCreate->createScalar(pvString);
displayField = fieldCreate->createStructure("display_t",names,fields);
}
void StandardField::createControl() {
size_t num = 3;
FieldConstPtrArray fields(num);
StringArray names(num);
names[0] = "limitLow";
names[1] = "limitHigh";
names[2] = "minStep";
fields[0] = fieldCreate->createScalar(pvDouble);
fields[1] = fieldCreate->createScalar(pvDouble);
fields[2] = fieldCreate->createScalar(pvDouble);
controlField = fieldCreate->createStructure("control_t",names,fields);
}
void StandardField::createBooleanAlarm() {
size_t numFields = 4;
FieldConstPtrArray fields(numFields);
StringArray names(numFields);
names[0] = "active";
names[1] = "falseSeverity";
names[2] = "trueSeverity";
names[3] = "changeStateSeverity";
fields[0] = fieldCreate->createScalar(pvBoolean);
fields[1] = fieldCreate->createScalar(pvInt);
fields[2] = fieldCreate->createScalar(pvInt);
fields[3] = fieldCreate->createScalar(pvInt);
booleanAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
}
void StandardField::createByteAlarm() {
size_t numFields = 10;
FieldConstPtrArray fields(numFields);
StringArray names(numFields);
names[0] = "active";
names[1] = "lowAlarmLimit";
names[2] = "lowWarningLimit";
names[3] = "highWarningLimit";
names[4] = "highAlarmLimit";
names[5] = "lowAlarmSeverity";
names[6] = "lowWarningSeverity";
names[7] = "highWarningSeverity";
names[8] = "highAlarmSeverity";
names[9] = "hysteresis";
fields[0] = fieldCreate->createScalar(pvBoolean);
fields[1] = fieldCreate->createScalar(pvByte);
fields[2] = fieldCreate->createScalar(pvByte);
fields[3] = fieldCreate->createScalar(pvByte);
fields[4] = fieldCreate->createScalar(pvByte);
fields[5] = fieldCreate->createScalar(pvInt);
fields[6] = fieldCreate->createScalar(pvInt);
fields[7] = fieldCreate->createScalar(pvInt);
fields[8] = fieldCreate->createScalar(pvInt);
fields[9] = fieldCreate->createScalar(pvByte);
byteAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
}
void StandardField::createShortAlarm() {
size_t numFields = 10;
FieldConstPtrArray fields(numFields);
StringArray names(numFields);
names[0] = "active";
names[1] = "lowAlarmLimit";
names[2] = "lowWarningLimit";
names[3] = "highWarningLimit";
names[4] = "highAlarmLimit";
names[5] = "lowAlarmSeverity";
names[6] = "lowWarningSeverity";
names[7] = "highWarningSeverity";
names[8] = "highAlarmSeverity";
names[9] = "hysteresis";
fields[0] = fieldCreate->createScalar(pvBoolean);
fields[1] = fieldCreate->createScalar(pvShort);
fields[2] = fieldCreate->createScalar(pvShort);
fields[3] = fieldCreate->createScalar(pvShort);
fields[4] = fieldCreate->createScalar(pvShort);
fields[5] = fieldCreate->createScalar(pvInt);
fields[6] = fieldCreate->createScalar(pvInt);
fields[7] = fieldCreate->createScalar(pvInt);
fields[8] = fieldCreate->createScalar(pvInt);
fields[9] = fieldCreate->createScalar(pvShort);
shortAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
}
void StandardField::createIntAlarm() {
size_t numFields = 10;
FieldConstPtrArray fields(numFields);
StringArray names(numFields);
names[0] = "active";
names[1] = "lowAlarmLimit";
names[2] = "lowWarningLimit";
names[3] = "highWarningLimit";
names[4] = "highAlarmLimit";
names[5] = "lowAlarmSeverity";
names[6] = "lowWarningSeverity";
names[7] = "highWarningSeverity";
names[8] = "highAlarmSeverity";
names[9] = "hysteresis";
fields[0] = fieldCreate->createScalar(pvBoolean);
fields[1] = fieldCreate->createScalar(pvInt);
fields[2] = fieldCreate->createScalar(pvInt);
fields[3] = fieldCreate->createScalar(pvInt);
fields[4] = fieldCreate->createScalar(pvInt);
fields[5] = fieldCreate->createScalar(pvInt);
fields[6] = fieldCreate->createScalar(pvInt);
fields[7] = fieldCreate->createScalar(pvInt);
fields[8] = fieldCreate->createScalar(pvInt);
fields[9] = fieldCreate->createScalar(pvInt);
intAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
}
void StandardField::createLongAlarm() {
size_t numFields = 10;
FieldConstPtrArray fields(numFields);
StringArray names(numFields);
names[0] = "active";
names[1] = "lowAlarmLimit";
names[2] = "lowWarningLimit";
names[3] = "highWarningLimit";
names[4] = "highAlarmLimit";
names[5] = "lowAlarmSeverity";
names[6] = "lowWarningSeverity";
names[7] = "highWarningSeverity";
names[8] = "highAlarmSeverity";
names[9] = "hysteresis";
fields[0] = fieldCreate->createScalar(pvBoolean);
fields[1] = fieldCreate->createScalar(pvLong);
fields[2] = fieldCreate->createScalar(pvLong);
fields[3] = fieldCreate->createScalar(pvLong);
fields[4] = fieldCreate->createScalar(pvLong);
fields[5] = fieldCreate->createScalar(pvInt);
fields[6] = fieldCreate->createScalar(pvInt);
fields[7] = fieldCreate->createScalar(pvInt);
fields[8] = fieldCreate->createScalar(pvInt);
fields[9] = fieldCreate->createScalar(pvLong);
longAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
}
void StandardField::createUByteAlarm() {
size_t numFields = 10;
FieldConstPtrArray fields(numFields);
StringArray names(numFields);
names[0] = "active";
names[1] = "lowAlarmLimit";
names[2] = "lowWarningLimit";
names[3] = "highWarningLimit";
names[4] = "highAlarmLimit";
names[5] = "lowAlarmSeverity";
names[6] = "lowWarningSeverity";
names[7] = "highWarningSeverity";
names[8] = "highAlarmSeverity";
names[9] = "hysteresis";
fields[0] = fieldCreate->createScalar(pvBoolean);
fields[1] = fieldCreate->createScalar(pvUByte);
fields[2] = fieldCreate->createScalar(pvUByte);
fields[3] = fieldCreate->createScalar(pvUByte);
fields[4] = fieldCreate->createScalar(pvUByte);
fields[5] = fieldCreate->createScalar(pvInt);
fields[6] = fieldCreate->createScalar(pvInt);
fields[7] = fieldCreate->createScalar(pvInt);
fields[8] = fieldCreate->createScalar(pvInt);
fields[9] = fieldCreate->createScalar(pvUByte);
ubyteAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
}
void StandardField::createUShortAlarm() {
size_t numFields = 10;
FieldConstPtrArray fields(numFields);
StringArray names(numFields);
names[0] = "active";
names[1] = "lowAlarmLimit";
names[2] = "lowWarningLimit";
names[3] = "highWarningLimit";
names[4] = "highAlarmLimit";
names[5] = "lowAlarmSeverity";
names[6] = "lowWarningSeverity";
names[7] = "highWarningSeverity";
names[8] = "highAlarmSeverity";
names[9] = "hysteresis";
fields[0] = fieldCreate->createScalar(pvBoolean);
fields[1] = fieldCreate->createScalar(pvUShort);
fields[2] = fieldCreate->createScalar(pvUShort);
fields[3] = fieldCreate->createScalar(pvUShort);
fields[4] = fieldCreate->createScalar(pvUShort);
fields[5] = fieldCreate->createScalar(pvInt);
fields[6] = fieldCreate->createScalar(pvInt);
fields[7] = fieldCreate->createScalar(pvInt);
fields[8] = fieldCreate->createScalar(pvInt);
fields[9] = fieldCreate->createScalar(pvUShort);
ushortAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
}
void StandardField::createUIntAlarm() {
size_t numFields = 10;
FieldConstPtrArray fields(numFields);
StringArray names(numFields);
names[0] = "active";
names[1] = "lowAlarmLimit";
names[2] = "lowWarningLimit";
names[3] = "highWarningLimit";
names[4] = "highAlarmLimit";
names[5] = "lowAlarmSeverity";
names[6] = "lowWarningSeverity";
names[7] = "highWarningSeverity";
names[8] = "highAlarmSeverity";
names[9] = "hysteresis";
fields[0] = fieldCreate->createScalar(pvBoolean);
fields[1] = fieldCreate->createScalar(pvUInt);
fields[2] = fieldCreate->createScalar(pvUInt);
fields[3] = fieldCreate->createScalar(pvUInt);
fields[4] = fieldCreate->createScalar(pvUInt);
fields[5] = fieldCreate->createScalar(pvInt);
fields[6] = fieldCreate->createScalar(pvInt);
fields[7] = fieldCreate->createScalar(pvInt);
fields[8] = fieldCreate->createScalar(pvInt);
fields[9] = fieldCreate->createScalar(pvUInt);
uintAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
}
void StandardField::createULongAlarm() {
size_t numFields = 10;
FieldConstPtrArray fields(numFields);
StringArray names(numFields);
names[0] = "active";
names[1] = "lowAlarmLimit";
names[2] = "lowWarningLimit";
names[3] = "highWarningLimit";
names[4] = "highAlarmLimit";
names[5] = "lowAlarmSeverity";
names[6] = "lowWarningSeverity";
names[7] = "highWarningSeverity";
names[8] = "highAlarmSeverity";
names[9] = "hysteresis";
fields[0] = fieldCreate->createScalar(pvBoolean);
fields[1] = fieldCreate->createScalar(pvULong);
fields[2] = fieldCreate->createScalar(pvULong);
fields[3] = fieldCreate->createScalar(pvULong);
fields[4] = fieldCreate->createScalar(pvULong);
fields[5] = fieldCreate->createScalar(pvInt);
fields[6] = fieldCreate->createScalar(pvInt);
fields[7] = fieldCreate->createScalar(pvInt);
fields[8] = fieldCreate->createScalar(pvInt);
fields[9] = fieldCreate->createScalar(pvULong);
ulongAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
}
void StandardField::createFloatAlarm() {
size_t numFields = 10;
FieldConstPtrArray fields(numFields);
StringArray names(numFields);
names[0] = "active";
names[1] = "lowAlarmLimit";
names[2] = "lowWarningLimit";
names[3] = "highWarningLimit";
names[4] = "highAlarmLimit";
names[5] = "lowAlarmSeverity";
names[6] = "lowWarningSeverity";
names[7] = "highWarningSeverity";
names[8] = "highAlarmSeverity";
names[9] = "hysteresis";
fields[0] = fieldCreate->createScalar(pvBoolean);
fields[1] = fieldCreate->createScalar(pvFloat);
fields[2] = fieldCreate->createScalar(pvFloat);
fields[3] = fieldCreate->createScalar(pvFloat);
fields[4] = fieldCreate->createScalar(pvFloat);
fields[5] = fieldCreate->createScalar(pvInt);
fields[6] = fieldCreate->createScalar(pvInt);
fields[7] = fieldCreate->createScalar(pvInt);
fields[8] = fieldCreate->createScalar(pvInt);
fields[9] = fieldCreate->createScalar(pvFloat);
floatAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
}
void StandardField::createDoubleAlarm() {
size_t numFields = 10;
FieldConstPtrArray fields(numFields);
StringArray names(numFields);
names[0] = "active";
names[1] = "lowAlarmLimit";
names[2] = "lowWarningLimit";
names[3] = "highWarningLimit";
names[4] = "highAlarmLimit";
names[5] = "lowAlarmSeverity";
names[6] = "lowWarningSeverity";
names[7] = "highWarningSeverity";
names[8] = "highAlarmSeverity";
names[9] = "hysteresis";
fields[0] = fieldCreate->createScalar(pvBoolean);
fields[1] = fieldCreate->createScalar(pvDouble);
fields[2] = fieldCreate->createScalar(pvDouble);
fields[3] = fieldCreate->createScalar(pvDouble);
fields[4] = fieldCreate->createScalar(pvDouble);
fields[5] = fieldCreate->createScalar(pvInt);
fields[6] = fieldCreate->createScalar(pvInt);
fields[7] = fieldCreate->createScalar(pvInt);
fields[8] = fieldCreate->createScalar(pvInt);
fields[9] = fieldCreate->createScalar(pvDouble);
doubleAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
}
void StandardField::createEnumeratedAlarm() {
size_t numFields = 3;
FieldConstPtrArray fields(numFields);
StringArray names(numFields);
names[0] = "active";
names[1] = "stateSeverity";
names[2] = "changeStateSeverity";
fields[0] = fieldCreate->createScalar(pvBoolean);
fields[1] = fieldCreate->createScalarArray(pvInt);
fields[2] = fieldCreate->createScalar(pvInt);
enumeratedAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
}
StructureConstPtr StandardField::scalar(
ScalarType type,string const &properties)
{
@@ -564,98 +263,21 @@ StructureConstPtr StandardField::enumerated(string const &properties)
return createProperties("epics:nt/NTEnum:1.0",field,properties);
}
StructureConstPtr StandardField::alarm()
{
return alarmField;
}
static StandardFieldPtr *stdFieldGbl;
StructureConstPtr StandardField::timeStamp()
{
return timeStampField;
}
static epicsThreadOnceId stdFieldGblOnce = EPICS_THREAD_ONCE_INIT;
StructureConstPtr StandardField::display()
void StandardField::once(void*)
{
return displayField;
}
StructureConstPtr StandardField::control()
{
return controlField;
}
StructureConstPtr StandardField::booleanAlarm()
{
return booleanAlarmField;
}
StructureConstPtr StandardField::byteAlarm()
{
return byteAlarmField;
}
StructureConstPtr StandardField::ubyteAlarm()
{
return ubyteAlarmField;
}
StructureConstPtr StandardField::shortAlarm()
{
return shortAlarmField;
}
StructureConstPtr StandardField::ushortAlarm()
{
return ushortAlarmField;
}
StructureConstPtr StandardField::intAlarm()
{
return intAlarmField;
}
StructureConstPtr StandardField::uintAlarm()
{
return uintAlarmField;
}
StructureConstPtr StandardField::longAlarm()
{
return longAlarmField;
}
StructureConstPtr StandardField::ulongAlarm()
{
return ulongAlarmField;
}
StructureConstPtr StandardField::floatAlarm()
{
return floatAlarmField;
}
StructureConstPtr StandardField::doubleAlarm()
{
return doubleAlarmField;
}
StructureConstPtr StandardField::enumeratedAlarm()
{
return enumeratedAlarmField;
stdFieldGbl = new StandardFieldPtr;
stdFieldGbl->reset(new StandardField);
}
const StandardFieldPtr &StandardField::getStandardField()
{
static StandardFieldPtr standardFieldCreate;
static Mutex mutex;
Lock xx(mutex);
epicsThreadOnce(&stdFieldGblOnce, &StandardField::once, 0);
if(standardFieldCreate.get()==0)
{
standardFieldCreate = StandardFieldPtr(new StandardField());
standardFieldCreate->init();
}
return standardFieldCreate;
return *stdFieldGbl;
}
}}

View File

@@ -3,42 +3,521 @@
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
#include <stdio.h>
#include <ctype.h>
#if defined(__linux__) || defined(__APPLE__)
#include <unistd.h>
#define HAVE_ISATTY
#endif
#include <deque>
#define epicsExportSharedSymbols
#include <pv/pvIntrospect.h>
#include <epicsTime.h>
#include <epicsString.h>
using std::string;
#define epicsExportSharedSymbols
#include <pv/bitSet.h>
#include <pv/pvData.h>
#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1)
# include <pv/json.h>
#endif
namespace epics { namespace pvData {
namespace format
{
static int indent_index = std::ios_base::xalloc();
static int indent_index = std::ios_base::xalloc();
long& indent_value(std::ios_base& ios)
{
return ios.iword(indent_index);
long& indent_value(std::ios_base& ios)
{
return ios.iword(indent_index);
}
std::ostream& operator<<(std::ostream& os, indent_level const& indent)
{
indent_value(os) = indent.level;
return os;
}
std::ostream& operator<<(std::ostream& os, indent const&)
{
long il = indent_value(os);
for(long i=0, spaces = il * 4; i<spaces; i++)
os.put(' ');
return os;
}
array_at_internal operator<<(std::ostream& str, array_at const& manip)
{
return array_at_internal(manip.index, str);
}
}
}} //epics::pvData
namespace {
using namespace epics::pvData;
bool useEscapes(std::ostream& strm) {
FILE *fp = 0;
// TODO: a better way to do this...
if(&std::cout==&strm)
fp = stdout;
if(&std::cerr==&strm)
fp = stderr;
if(!fp) return false;
#ifdef HAVE_ISATTY
// check $TERM as well?
return isatty(fileno(fp))==1;
#else
// TODO: in theory windows 10 can be made to understand escapes as well
return false;
#endif
}
void printAlarmTx(std::ostream& strm, const PVStructure& sub)
{
PVScalar::const_shared_pointer pvSeverity(sub.getSubField<PVInt>("severity"));
PVScalar::const_shared_pointer pvStatus(sub.getSubField<PVInt>("status"));
PVString::const_shared_pointer pvMessage(sub.getSubField<PVString>("message"));
switch(pvSeverity ? pvSeverity->getAs<int32>() : 0) {
case 0: return; // nothing more to do here...
case 1: strm<<"MINOR "; break;
case 2: strm<<"MAJOR "; break;
case 3: strm<<"INVALID "; break;
case 4: strm<<"UNDEFINED "; break;
default: strm<<pvSeverity->getAs<int32>()<<' ';
}
std::ostream& operator<<(std::ostream& os, indent_level const& indent)
{
indent_value(os) = indent.level;
return os;
}
switch(pvStatus ? pvStatus->getAs<int32>() : 0) {
case 0: break;
case 1: strm<<"DEVICE "; break;
case 2: strm<<"DRIVER "; break;
case 3: strm<<"RECORD "; break;
case 4: strm<<"DB "; break;
case 5: strm<<"CONF "; break;
case 6: strm<<"UNDEFINED "; break;
case 7: strm<<"CLIENT "; break;
default: strm<<pvStatus->getAs<int32>()<<' ';
}
std::ostream& operator<<(std::ostream& os, indent const&)
{
long il = indent_value(os);
std::size_t spaces = static_cast<std::size_t>(il) * 4;
return os << string(spaces, ' ');
}
if(pvMessage && !pvMessage->get().empty())
strm<<pvMessage->get()<<' ';
}
array_at_internal operator<<(std::ostream& str, array_at const& manip)
{
return array_at_internal(manip.index, str);
}
};
}}
void printAlarmT(std::ostream& strm, const PVStructure& top)
{
PVStructure::const_shared_pointer sub(top.getSubField<PVStructure>("alarm"));
if(sub)
printAlarmTx(strm, *sub);
}
void printTimeTx(std::ostream& strm, const PVStructure& tsubop)
{
char timeText[32];
epicsTimeStamp epicsTS;
PVScalar::const_shared_pointer secf(tsubop.getSubField<PVScalar>("secondsPastEpoch")),
nsecf(tsubop.getSubField<PVScalar>("nanoseconds")),
tagf(tsubop.getSubField<PVScalar>("userTag"));
epicsTS.secPastEpoch = secf ? secf->getAs<int64>() : 0;
epicsTS.nsec = nsecf ? nsecf->getAs<int32>() : 0;
if(epicsTS.secPastEpoch > POSIX_TIME_AT_EPICS_EPOCH)
epicsTS.secPastEpoch -= POSIX_TIME_AT_EPICS_EPOCH;
else
epicsTS.secPastEpoch = 0;
epicsTimeToStrftime(timeText, sizeof(timeText), "%Y-%m-%d %H:%M:%S.%03f", &epicsTS);
strm <<std::setw(24) <<std::left <<timeText <<' ';
if (tagf) {
int64 tagv = tagf->getAs<int64>();
if(tagv)
strm << tagv << ' ';
}
}
void printTimeT(std::ostream& strm, const PVStructure& top)
{
PVStructure::const_shared_pointer sub(top.getSubField<PVStructure>("timeStamp"));
if(sub)
printTimeTx(strm, *sub);
}
bool printEnumT(std::ostream& strm, const PVStructure& top, bool fromtop)
{
PVStructure::const_shared_pointer value;
if(fromtop) {
value = top.getSubField<PVStructure>("value");
} else {
value = std::tr1::static_pointer_cast<const PVStructure>(top.shared_from_this());
}
PVScalar::const_shared_pointer idx(value->getSubField<PVScalar>("index"));
PVStringArray::const_shared_pointer choices(value->getSubField<PVStringArray>("choices"));
if(!idx || !choices) return false;
if(fromtop) {
strm<<format::indent();
printTimeT(strm, top);
printAlarmT(strm, top);
}
PVStringArray::const_svector ch(choices->view());
uint32 I = idx->getAs<uint32>();
strm<<'('<<I<<')';
if(I>=ch.size()) {
strm<<" <undefined>";
} else {
strm<<' '<<escape(ch[I]);
}
return true;
}
void csvEscape(std::string& S)
{
// concise, not particularly efficient...
std::string temp(escape(S).style(escape::CSV).str());
if(S.find_first_of(" ,\\")!=S.npos) {// only quote if necessary (stupid Excel)
std::string temp2;
temp2.reserve(temp.size()+2);
temp2.push_back('\"');
temp2 += temp;
temp2.push_back('\"');
temp2.swap(temp);
}
S = temp;
}
bool printTable(std::ostream& strm, const PVStructure& top)
{
PVStructure::const_shared_pointer cf(top.getSubField<PVStructure>("value"));
if(!cf) return false;
{
const FieldConstPtrArray& fields = cf->getStructure()->getFields();
if(fields.empty()) return false;
for(size_t i=0, N=fields.size(); i<N; i++) {
if(fields[i]->getType()!=scalarArray)
return false; // cowardly refusing to handle anything else...
}
}
// maybe output a line with meta-data
{
bool havets = !!top.getSubField("timeStamp");
bool haveal = !!top.getSubField("alarm");
if(havets || haveal)
strm<<format::indent();
if(havets) {
printTimeT(strm, top);
strm<<' ';
}
if(haveal) {
printAlarmT(strm, top);
strm<<' ';
}
strm<<'\n';
}
PVStringArray::svector labels;
{
PVStringArray::const_shared_pointer lf(top.getSubField<PVStringArray>("labels"));
if(lf) {
PVStringArray::const_svector L(lf->view());
labels = thaw(L);
}
}
const PVFieldPtrArray& columns = cf->getPVFields();
std::vector<shared_vector<std::string> > coldat(columns.size());
std::vector<size_t> widths(columns.size());
labels.reserve(columns.size());
size_t nrows = size_t(-1);
for(size_t i=0, N=columns.size(); i<N; i++) {
if(i>=labels.size()) {
labels.push_back(cf->getStructure()->getFieldName(i));
} else {
csvEscape(labels[i]);
}
widths[i] = labels[i].size();
{
shared_vector<const std::string> ro;
static_cast<const PVScalarArray*>(columns[i].get())->getAs(ro);
coldat[i] = thaw(ro);
}
// truncate if some columns are longer than others
nrows = std::min(nrows, coldat[i].size());
for(size_t j=0, M=coldat[i].size(); j<M; j++) {
csvEscape(coldat[i][j]);
widths[i] = std::max(widths[i], coldat[i][j].size());
}
}
// output header line
strm<<format::indent();
for(size_t c=0, N=coldat.size(); c<N; c++) {
strm<<std::setw(widths[c])<<std::right<<labels[c];
if(c+1!=N) {
strm<<' ';
}
}
strm<<'\n';
for(size_t r=0; r<nrows; r++) {
strm<<format::indent();
for(size_t c=0, N=coldat.size(); c<N; c++) {
strm<<std::setw(widths[c])<<std::right<<coldat[c][r];
if(c+1!=N)
strm<<' ';
}
strm<<'\n';
}
return true;
}
void expandBS(const PVStructure& top, BitSet& mask, bool parents) {
if(mask.get(0)) { // special handling because getSubField(0) not allowed
// wildcard
for(size_t idx=1, N=top.getNumberFields(); idx<N; idx++) {
mask.set(idx);
}
} else {
for(int32 idx = mask.nextSetBit(0), N=top.getNumberFields(); idx>=0 && idx<N; idx=mask.nextSetBit(idx+1)) {
PVField::const_shared_pointer fld = top.getSubFieldT(idx);
// look forward and mark all children
for(size_t i=idx+1, N=fld->getNextFieldOffset(); i<N; i++)
mask.set(i);
if(parents) {
// look back and mark all parents
// we've already stepped past all parents so siblings will not be automatically marked
for(const PVStructure *parent = fld->getParent(); parent; parent = parent->getParent()) {
mask.set(parent->getFieldOffset());
}
}
}
}
}
}
namespace epics { namespace pvData {
void printRaw(std::ostream& strm, const PVStructure::Formatter& format, const PVStructure& cur)
{
typedef std::deque<std::pair<const PVField*, size_t> > todo_t;
todo_t todo;
BitSet show, highlight;
const long orig_indent = format::indent_value(strm);
{
if(format.xshow)
show = *format.xshow;
else
show.set(0);
if(format.xhighlight)
highlight = *format.xhighlight;
expandBS(format.xtop, show, true);
expandBS(format.xtop, highlight, false);
highlight &= show; // can't highlight hidden fields (paranoia)
}
if(!show.get(0)) return; // nothing to do here...
todo.push_front(std::make_pair(&format.xtop, orig_indent));
while(!todo.empty()) {
todo_t::value_type cur(todo.front());
todo.pop_front();
format::indent_value(strm) = cur.second;
bool hl = highlight.get(cur.first->getFieldOffset()) && format.xmode==PVStructure::Formatter::ANSI;
if(hl)
strm<<"\x1b[1m"; // Bold
switch(cur.first->getField()->getType()) {
case structure: {
const PVStructure* str = static_cast<const PVStructure*>(cur.first);
const PVFieldPtrArray& flds = str->getPVFields();
for(PVFieldPtrArray::const_reverse_iterator it(flds.rbegin()), end(flds.rend()); it!=end; ++it) {
const PVField *fld = (*it).get();
if(!show.get(fld->getFieldOffset())) continue;
todo.push_front(std::make_pair(fld, cur.second+1));
}
std::string id(cur.first->getField()->getID());
strm<<format::indent()<<id<<' '<<cur.first->getFieldName();
if(id=="alarm_t") {
strm.put(' ');
printAlarmTx(strm, *str);
} else if(id=="time_t") {
strm.put(' ');
printTimeTx(strm, *str);
} else if(id=="enum_t") {
strm.put(' ');
printEnumT(strm, *str, false);
}
strm.put('\n');
}
break;
case scalar:
case scalarArray:
strm<<format::indent()<<cur.first->getField()->getID()<<' '<<cur.first->getFieldName()
<<' '<<*cur.first
<<'\n';
break;
case structureArray:
case union_:
case unionArray:
strm<<*cur.first;
break;
}
if(hl)
strm<<"\x1b[0m"; // reset
}
format::indent_value(strm) = orig_indent;
}
std::ostream& operator<<(std::ostream& strm, const PVStructure::Formatter& format)
{
if(format.xfmt==PVStructure::Formatter::JSON) {
#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1)
JSONPrintOptions opts;
opts.multiLine = false;
printJSON(strm, format.xtop, format.xshow ? *format.xshow : BitSet().set(0), opts);
strm<<'\n';
return strm;
#else
// fall through to Raw
#endif
} else if(format.xfmt==PVStructure::Formatter::NT) {
std::string id(format.xtop.getStructure()->getID()),
idprefix(id.substr(0, id.find_first_of('.')));
// NTTable
if(idprefix=="epics:nt/NTTable:1") {
if(printTable(strm, format.xtop))
return strm;
} else {
//NTScalar, NTScalarArray, NTEnum, or anything with '.value'
PVField::const_shared_pointer value(format.xtop.getSubField("value"));
if(value) {
switch(value->getField()->getType()) {
case scalar:
strm<<format::indent();
printTimeT(strm, format.xtop);
strm<<std::setprecision(6)<<*static_cast<const PVScalar*>(value.get())<<' ';
printAlarmT(strm, format.xtop);
strm<<'\n';
return strm;
case scalarArray:
strm<<format::indent();
printTimeT(strm, format.xtop);
printAlarmT(strm, format.xtop);
strm<<std::setprecision(6)<<*static_cast<const PVScalarArray*>(value.get())<<'\n';
return strm;
case structure:
if(printEnumT(strm, format.xtop, true)) {
strm<<'\n';
return strm;
}
break;
default:
break;
}
}
}
}
// fall through unhandled as Raw
PVStructure::Formatter format2(format);
if(format2.xmode==PVStructure::Formatter::Auto)
format2.xmode = useEscapes(strm) ? PVStructure::Formatter::ANSI : PVStructure::Formatter::Plain;
printRaw(strm, format2, format.xtop);
return strm;
}
static char hexdigit(char c) {
c &= 0xf;
if(c<9)
return '0'+c;
else
return 'A'+c-10;
}
escape::~escape() {}
std::string escape::str() const
{
std::ostringstream strm;
strm<<(*this);
return strm.str();
}
epicsShareFunc
std::ostream& operator<<(std::ostream& strm, const escape& Q)
{
for(size_t pos = 0, len = Q.orig.size(); pos < len; pos++) {
const char C = Q.orig[pos];
char quote = '\\', next;
// compare me with epicsStrnEscapedFromRaw()
switch(C) {
case '\a': next = 'a'; break;
case '\b': next = 'b'; break;
case '\f': next = 'f'; break;
case '\n': next = 'n'; break;
case '\r': next = 'r'; break;
case '\t': next = 't'; break;
case '\v': next = 'v'; break;
case '\\': next = '\\'; break;
case '\'': next = '\''; break;
case '\"': next = '\"'; if(Q.S==escape::CSV) quote = '"'; break;
default:
if(!isprint(C)) {
// print three charator escape
strm<<"\\x"<<hexdigit(C>>4)<<hexdigit(C);
} else {
// literal
strm.put(C);
}
continue;
}
// print two charactor escape
strm.put(quote);
strm.put(next);
}
return strm;
}
}} //epics::pvData

View File

@@ -302,13 +302,17 @@ struct handler {
operator yajl_handle() { return handle; }
};
struct noop {
void operator()(pvd::PVField*) {}
};
} // namespace
namespace epics{namespace pvData{
epicsShareFunc
void parseJSON(std::istream& strm,
const PVField::shared_pointer& dest,
PVField& dest,
BitSet *assigned)
{
#ifndef EPICS_YAJL_VERSION
@@ -318,7 +322,12 @@ void parseJSON(std::istream& strm,
conf.checkUTF8 = 1;
#endif
context ctxt(dest, assigned);
// we won't create refs to 'dest' which presist beyond this call.
// however, it is convienent to treat 'dest' in the same manner as
// any union/structureArray memebers it may contain.
PVFieldPtr fakedest(&dest, noop());
context ctxt(fakedest, assigned);
#ifndef EPICS_YAJL_VERSION
handler handle(yajl_alloc(&jtree_cbs, &conf, NULL, &ctxt));
@@ -334,6 +343,7 @@ void parseJSON(std::istream& strm,
if(!ctxt.stack.empty())
throw std::logic_error("field stack not empty");
assert(fakedest.use_count()==1);
}
}} // namespace epics::pvData

View File

@@ -10,6 +10,7 @@
#include <pv/pvdVersion.h>
#include <pv/pvData.h>
#include <pv/valueBuilder.h>
#include <pv/bitSet.h>
#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1)
@@ -40,9 +41,9 @@ struct args {
}
};
void show_field(args& A, const pvd::PVField* fld);
void show_field(args& A, const pvd::PVField* fld, const pvd::BitSet *mask);
void show_struct(args& A, const pvd::PVStructure* fld)
void show_struct(args& A, const pvd::PVStructure* fld, const pvd::BitSet *mask)
{
const pvd::StructureConstPtr& type = fld->getStructure();
const pvd::PVFieldPtrArray& children = fld->getPVFields();
@@ -52,13 +53,18 @@ void show_struct(args& A, const pvd::PVStructure* fld)
A.strm.put('{');
A.indent++;
bool first = true;
for(size_t i=0, N=names.size(); i<N; i++)
{
if(i!=0)
if(mask && !mask->get(children[i]->getFieldOffset())) continue;
if(first)
first = false;
else
A.strm.put(',');
A.doIntent();
A.strm<<'\"'<<names[i]<<"\": ";
show_field(A, children[i].get());
show_field(A, children[i].get(), mask);
}
A.indent--;
@@ -66,7 +72,7 @@ void show_struct(args& A, const pvd::PVStructure* fld)
A.strm.put('}');
}
void show_field(args& A, const pvd::PVField* fld)
void show_field(args& A, const pvd::PVField* fld, const pvd::BitSet *mask)
{
switch(fld->getField()->getType())
{
@@ -79,7 +85,7 @@ void show_field(args& A, const pvd::PVField* fld)
A.strm<<scalar->getAs<std::string>();
}
}
break;
return;
case pvd::scalarArray:
{
const pvd::PVScalarArray *scalar=static_cast<const pvd::PVScalarArray*>(fld);
@@ -102,10 +108,10 @@ void show_field(args& A, const pvd::PVField* fld)
}
A.strm.put(']');
}
break;
return;
case pvd::structure:
show_struct(A, static_cast<const pvd::PVStructure*>(fld));
break;
show_struct(A, static_cast<const pvd::PVStructure*>(fld), mask);
return;
case pvd::structureArray:
{
pvd::PVStructureArray::const_svector arr(static_cast<const pvd::PVStructureArray*>(fld)->view());
@@ -117,7 +123,7 @@ void show_field(args& A, const pvd::PVField* fld)
A.strm.put(',');
A.doIntent();
if(arr[i])
show_struct(A, arr[i].get());
show_struct(A, arr[i].get(), 0);
else
A.strm<<"NULL";
}
@@ -126,7 +132,7 @@ void show_field(args& A, const pvd::PVField* fld)
A.doIntent();
A.strm.put(']');
}
break;
return;
case pvd::union_:
{
const pvd::PVUnion *U=static_cast<const pvd::PVUnion*>(fld);
@@ -135,15 +141,63 @@ void show_field(args& A, const pvd::PVField* fld)
if(!C) {
A.strm<<"null";
} else {
show_field(A, C.get());
show_field(A, C.get(), 0);
}
}
break;
default:
if(A.opts.ignoreUnprintable)
A.strm<<"// unprintable field type";
else
throw std::runtime_error("Encountered unprintable field type");
return;
case pvd::unionArray: {
const pvd::PVUnionArray *U=static_cast<const pvd::PVUnionArray*>(fld);
pvd::PVUnionArray::const_svector arr(U->view());
A.strm.put('[');
A.indent++;
for(size_t i=0, N=arr.size(); i<N; i++) {
if(i!=0)
A.strm.put(',');
A.doIntent();
if(arr[i])
show_field(A, arr[i].get(), 0);
else
A.strm<<"NULL";
}
A.indent--;
A.doIntent();
A.strm.put(']');
}
return;
}
// should not be reached
if(A.opts.ignoreUnprintable)
A.strm<<"// unprintable field type";
else
throw std::runtime_error("Encountered unprintable field type");
}
void expandBS(const pvd::PVStructure& top, pvd::BitSet& mask, bool parents) {
if(mask.get(0)) { // special handling because getSubField(0) not allowed
// wildcard
for(size_t idx=1, N=top.getNumberFields(); idx<N; idx++) {
mask.set(idx);
}
} else {
for(pvd::int32 idx = mask.nextSetBit(0), N=top.getNumberFields(); idx>=0 && idx<N; idx=mask.nextSetBit(idx+1)) {
pvd::PVField::const_shared_pointer fld = top.getSubFieldT(idx);
// look forward and mark all children
for(size_t i=idx+1, N=fld->getNextFieldOffset(); i<N; i++)
mask.set(i);
if(parents) {
// look back and mark all parents
// we've already stepped past all parents so siblings will not be automatically marked
for(const pvd::PVStructure *parent = fld->getParent(); parent; parent = parent->getParent()) {
mask.set(parent->getFieldOffset());
}
}
}
}
}
@@ -158,11 +212,23 @@ JSONPrintOptions::JSONPrintOptions()
{}
void printJSON(std::ostream& strm,
const PVField::const_shared_pointer& val,
const PVStructure& val,
const BitSet& mask,
const JSONPrintOptions& opts)
{
args A(strm, opts);
show_field(A, val.get());
pvd::BitSet emask(mask);
expandBS(val, emask, true);
if(!emask.get(0)) return;
show_struct(A, &val, &emask);
}
void printJSON(std::ostream& strm,
const PVField& val,
const JSONPrintOptions& opts)
{
args A(strm, opts);
show_field(A, &val, 0);
}
}} // namespace epics::pvData

View File

@@ -51,15 +51,32 @@ struct epicsShareClass JSONPrintOptions
/** Print PVStructure as JSON
*
* Restrictions:
*
* - No support for union or array of union
* 'mask' selects those fields which will be printed.
* @version Overload added after 7.0.0
*/
epicsShareFunc
void printJSON(std::ostream& strm,
const PVField::const_shared_pointer& val,
const PVStructure& val,
const BitSet& mask,
const JSONPrintOptions& opts = JSONPrintOptions());
/** Print PVField as JSON
* @version Overload added after 7.0.0
*/
epicsShareFunc
void printJSON(std::ostream& strm,
const PVField& val,
const JSONPrintOptions& opts = JSONPrintOptions());
// To be deprecated in favor of previous form
FORCE_INLINE
void printJSON(std::ostream& strm,
const PVField::const_shared_pointer& val,
const JSONPrintOptions& opts = JSONPrintOptions())
{
printJSON(strm, *val, opts);
}
/** Parse JSON text into a PVStructure
*
* Restrictions:
@@ -82,12 +99,22 @@ PVStructure::shared_pointer parseJSON(std::istream& strm);
* @param dest Store in fields of this structure
* @param assigned Which fields of _dest_ were assigned. (Optional)
* @throws std::runtime_error on failure. dest and assigned may be modified.
* @version Overload added after 7.0.0
*/
epicsShareFunc
void parseJSON(std::istream& strm,
const PVField::shared_pointer& dest,
PVField& dest,
BitSet *assigned=0);
// To be deprecated in favor of previous form
FORCE_INLINE
void parseJSON(std::istream& strm,
const PVField::shared_pointer& dest,
BitSet *assigned=0)
{
parseJSON(strm, *dest, assigned);
}
/** Wrapper around yajl_parse()
*

View File

@@ -33,6 +33,7 @@ LIBSRCS += timer.cpp
LIBSRCS += status.cpp
LIBSRCS += localStaticLock.cpp
LIBSRCS += typeCast.cpp
LIBSRCS += thread.cpp
LIBSRCS += parseToPOD.cpp
LIBSRCS += pvUnitTest.cpp
LIBSRCS += debugPtr.cpp

View File

@@ -254,7 +254,7 @@ epicsParseFloat(const char *str, float *to, char **units)
#endif
// Sometimes we have to provide our own copy of strtoll()
#if defined(_WIN32) && !defined(_MINGW)
#if defined(_MSC_VER) && _MSC_VER < 1800
// On Windows with MSVC, Base-3.15 provides strtoll()
# define NEED_OLL_FUNCS (EPICS_VERSION_INT < VERSION_INT(3,15,0,1))
#elif defined(vxWorks)

View File

@@ -117,6 +117,7 @@ public:
//! Construct from un-typed pointer.
//! Caller is responsible to ensure that buf actually points to the provided type
//! @version Added after 7.0.0
AnyScalar(ScalarType type, const void *buf);
AnyScalar(const AnyScalar& o);
@@ -147,7 +148,7 @@ public:
#endif
//! Reset internal state.
//! Added after 7.0.0
//! @version Added after 7.0.0
//! @post empty()==true
void clear();
@@ -174,14 +175,16 @@ public:
//! Provide read-only access to underlying buffer.
//! For a string this is std::string::c_str().
//! @version Added after 7.0.0
const void* bufferUnsafe() const;
/** Return typed reference to wrapped value. Non-const reference allows value modification
*
* @throws bad_cast when the requested type does not match the stored type
@code
AnyScalar v(42);
v.ref<uint32>() = 42;
assert(v.ref<uint32>() = 43);
v.ref<uint32>() = 43;
assert(v.ref<uint32>() == 43);
@endcode
*/
template<typename T>
@@ -199,9 +202,10 @@ public:
/** Return typed reference to wrapped value. Const reference does not allow modification.
*
* @throws bad_cast when the requested type does not match the stored type
@code
AnyScalar v(42);
assert(v.ref<uint32>() = 42);
const AnyScalar v(42);
assert(v.ref<uint32>() == 42);
@endcode
*/
template<typename T>
@@ -217,7 +221,10 @@ public:
return reinterpret_cast<typename meta::decorate_const<TT>::type&>(_wrap.blob);
}
/** copy out wrapped value, with a value conversion. */
/** copy out wrapped value, with a value conversion.
*
* @throws bad_cast when empty()==true
*/
template<typename T>
inline
T as() const {

View File

@@ -50,6 +50,8 @@ namespace epics { namespace pvData {
* synchronization.
*
* Based on Java implementation.
*
* @since 7.0.0 Many methods return BitSet& to facilite method chaining.
*/
class epicsShareClass BitSet : public Serializable {
public:

View File

@@ -803,8 +803,8 @@ private:
assert(n<=getRemaining());
if (reverse<T>()) {
for(std::size_t i=0; i<n; i+=sizeof(T)) {
detail::store_unaligned(_position+i, swap<T>(values[i]));
for(std::size_t i=0; i<count; i++) {
detail::store_unaligned(_position+i*sizeof(T), swap<T>(values[i]));
}
} else {
memcpy(_position, values, n);
@@ -819,8 +819,8 @@ private:
assert(n<=getRemaining());
if (reverse<T>()) {
for(std::size_t i=0; i<n; i+=sizeof(T)) {
values[i] = swap<T>(detail::load_unaligned<T>(_position+i));
for(std::size_t i=0; i<count; i++) {
values[i] = swap<T>(detail::load_unaligned<T>(_position+i*sizeof(T)));
}
} else {
memcpy(values, _position, n);

View File

@@ -33,10 +33,6 @@
#ifndef EPICSEXCEPTION_H_
#define EPICSEXCEPTION_H_
#if defined(_WIN32) && !defined(NOMINMAX)
#define NOMINMAX
#endif
#include <stdexcept>
#include <string>
#include <cstdio>

View File

@@ -28,7 +28,7 @@
public:
...
};
@code
@endcode
*
* @note This macro contains 'private:'.
*/

View File

@@ -39,7 +39,7 @@ public:
explicit testPassx(bool r) :dotest(true), pass(r), alive(true) {}
~testPassx();
template<typename T>
inline testPassx& operator<<(T v) {
inline testPassx& operator<<(const T& v) {
strm<<v;
return *this;
}
@@ -52,13 +52,13 @@ private:
};
template<typename LHS, typename RHS>
inline testPassx testEqualx(const char *nLHS, const char *nRHS, LHS l, RHS r)
inline testPassx testEqualx(const char *nLHS, const char *nRHS, const LHS& l, const RHS& r)
{
return testPassx(l==r)<<nLHS<<" ("<<l<<") == "<<nRHS<<" ("<<r<<")";
}
template<typename LHS, typename RHS>
inline testPassx testNotEqualx(const char *nLHS, const char *nRHS, LHS l, RHS r)
inline testPassx testNotEqualx(const char *nLHS, const char *nRHS, const LHS& l, const RHS& r)
{
return testPassx(l!=r)<<nLHS<<" ("<<l<<") != "<<nRHS<<" ("<<r<<")";
}

View File

@@ -5,6 +5,38 @@
#ifndef REFTRACK_H
#define REFTRACK_H
/** @page pvd_reftrack RefTrack
*
* reftrack.h is a utility for listing, finding, and reading global atomic counters.
* By convention used to expose object instance counters as a way of detecting (slow)
* reference/resource leaks before they cause problems.
*
* cf. the IOC shell commands "refshow", "refsave", and "refdiff".
*
* Example usage:
*
* @code
* // my header.h
* struct MyClass {
* MyClass();
* ~MyClass();
* static size_t num_instances;
* ...
* };
* ...
* // my src.cpp
* size_t MyClass::num_instances;
* MyClass::MyClass() {
* REFTRACE_INCREMENT(num_instances);
* }
* MyClass::~MyClass() {
* REFTRACE_DECREMENT(num_instances);
* }
* // in some IOC registrar or global ctor
* registerRefCounter("MyClass", &MyClass::num_instances);
* @endcode
*/
#ifdef __cplusplus
#include <map>

View File

@@ -62,15 +62,21 @@
#if defined(SHARED_FROM_MANUAL)
// define SHARED_FROM_MANUAL if from some reason it is desirable to manually select
// which shared_ptr implementation to use
#elif __cplusplus>=201103L || (defined(_MSC_VER) && (_MSC_VER>=1600)) || (__clang__ && __APPLE__)
// c++11 or MSVC 2010
// clang on linux has tr1/memory, clang on OSX doesn't
#elif __cplusplus>=201103L || (defined(_MSC_VER) && (_MSC_VER>=1600)) || defined(_LIBCPP_VERSION)
// MSVC has been bad about incrementing __cplusplus, even when new features are added. shared_ptr from MSVC 2010
// the llvm libc++ doesn't bother with tr1, and puts shared_ptr in std:: even with -std=c++98
# define SHARED_FROM_STD
#elif defined(__GNUC__) && __GNUC__>=4 && !defined(vxWorks)
// GCC >=4.0.0
# define SHARED_FROM_TR1
#elif defined(_MSC_VER) && _MSC_VER==1500
// MSVC 2009 (eg. Visual C++ for Python 2.7)
// Dinkumware _CPPLIB_VER=505
// Has std::tr1::shared_ptr in <memory>
# define SHARED_TR1_FROM_STD
#elif defined(_MSC_VER) && (_MSC_VER>1500 || defined(_HAS_TR1))
// MSVC > 2008, or 2008 w/ SP1
# define SHARED_FROM_TR1
@@ -120,6 +126,9 @@ namespace std {
#endif // DEBUG_SHARED_PTR
#elif defined(SHARED_TR1_FROM_STD)
# include <memory>
#elif defined(SHARED_FROM_TR1)
# include <tr1/memory>
@@ -192,7 +201,8 @@ inline std::ostream& operator<<(std::ostream& strm, const ::detail::ref_shower<T
typedef std::tr1::weak_ptr<clazz> weak_pointer; \
typedef std::tr1::weak_ptr<const clazz> const_weak_pointer
/* A semi-hack to help with migration from std::auto_ptr to std::unique_ptr,
namespace epics{
/** A semi-hack to help with migration from std::auto_ptr to std::unique_ptr,
* and avoid copious deprecation warning spam
* which may be hiding legitimate issues.
*
@@ -205,7 +215,6 @@ inline std::ostream& operator<<(std::ostream& strm, const ::detail::ref_shower<T
* copy/assignment/return are not supported
* (use auto_ptr or unique_ptr explicitly).
*/
namespace epics{
#if __cplusplus>=201103L
template<typename T>
using auto_ptr = std::unique_ptr<T>;

View File

@@ -6,10 +6,6 @@
#ifndef SHAREDVECTOR_H
#define SHAREDVECTOR_H
#if defined(_WIN32) && !defined(NOMINMAX)
#define NOMINMAX
#endif
#include <ostream>
#include <algorithm>
#include <stdexcept>
@@ -256,7 +252,9 @@ namespace detail {
m_total -= offset;
m_count = std::min(length, max_count);
if(length > max_count)
length = max_count;
m_count = length;
}
// Access to members.
@@ -430,7 +428,9 @@ public:
void reserve(size_t i) {
if(this->unique() && i<=this->m_total)
return;
size_t new_count = std::min(this->m_count, i);
size_t new_count = this->m_count;
if(new_count > i)
new_count = i;
_E_non_const* temp=new _E_non_const[i];
try{
std::copy(begin(), begin()+new_count, temp);
@@ -468,13 +468,18 @@ public:
}
}
// must re-allocate :(
size_t new_total = std::max(this->m_total, i);
size_t new_total = this->m_total;
if(new_total < i)
new_total = i;
_E_non_const* temp=new _E_non_const[new_total];
try{
size_t n = this->size();
if(n > i)
n = i;
// Copy as much as possible from old,
// remaining elements are uninitialized.
std::copy(begin(),
begin()+std::min(i,this->size()),
begin()+n,
temp);
this->m_sdata.reset(temp, detail::default_array_deleter<pointer>());
}catch(...){

View File

@@ -77,13 +77,13 @@ namespace epics { namespace pvData {
* Get error message describing an error. Required if error status.
* @return error message.
*/
inline std::string getMessage() const { return m_message; }
inline const std::string& getMessage() const { return m_message; }
/**
* Get stack dump where error (exception) happened. Optional.
* @return stack dump.
*/
inline std::string getStackDump() const { return m_stackDump; }
inline const std::string& getStackDump() const { return m_stackDump; }
/**
* Convenient OK test. Same as <code>(getType() == StatusType.OK)</code>.

View File

@@ -42,38 +42,7 @@ typedef std::tr1::shared_ptr<epicsThread> EpicsThreadPtr;
typedef epicsThreadRunable Runnable;
//! Helper for those cases where a class should have more than one runnable
template<typename C>
class RunnableMethod : public Runnable
{
EPICS_NOT_COPYABLE(RunnableMethod)
typedef void (C::*meth_t)();
C *inst;
meth_t meth;
virtual void run()
{
(inst->*meth)();
}
public:
RunnableMethod(C* inst, void (C::*meth)())
:inst(inst), meth(meth)
{}
};
namespace detail {
struct FuncRunner : public epicsThreadRunable
{
typedef void (*fn_t)(void*);
fn_t fn;
void *arg;
FuncRunner(fn_t f, void *a) :fn(f), arg(a) {}
virtual ~FuncRunner(){}
virtual void run()
{
(*fn)(arg);
}
};
template<typename C>
struct MethRunner : public epicsThreadRunable
{
@@ -87,26 +56,13 @@ struct MethRunner : public epicsThreadRunable
(inst->*fn)();
}
};
#if __cplusplus>=201103L
struct BindRunner : public epicsThreadRunable
{
typedef std::function<void()> fn_t;
fn_t fn;
BindRunner(const fn_t f) : fn(f) {}
virtual ~BindRunner() {}
virtual void run()
{
fn();
}
};
#endif
} // namespace detail
/**
* @brief C++ wrapper for epicsThread from EPICS base.
*
*/
class Thread : public epicsThread {
class epicsShareClass Thread : public epicsThread {
EPICS_NOT_COPYABLE(Thread)
public:
/** @brief Holds all the configuration necessary to launch a @class Thread
@@ -136,7 +92,7 @@ public:
<<"example"<<1);
@endcode
*/
class Config
class epicsShareClass Config
{
unsigned int p_prio, p_stack;
std::ostringstream p_strm;
@@ -145,49 +101,28 @@ public:
typedef epics::auto_ptr<Runnable> p_owned_runner_t;
p_owned_runner_t p_owned_runner;
friend class Thread;
Runnable& x_getrunner()
{
if(!this->p_runner)
throw std::logic_error("Thread::Config missing run()");
return *this->p_runner;
}
void x_setdefault()
{
this->p_prio = epicsThreadPriorityLow;
this->p_autostart = true;
this->p_runner = NULL;
(*this).stack(epicsThreadStackSmall);
}
Runnable& x_getrunner();
void x_setdefault();
public:
Config() {this->x_setdefault();}
Config(Runnable *r) {this->x_setdefault();this->run(r);}
Config(void(*fn)(void*), void *ptr) {this->x_setdefault();this->run(fn, ptr);}
Config();
Config(Runnable *r);
Config(void(*fn)(void*), void *ptr);
template<typename C>
Config(C* inst, void(C::*meth)()) {this->x_setdefault();this->run(inst, meth);}
#if __cplusplus>=201103L
Config(const std::function<void()>& fn) {this->x_setdefault();this->run(fn);}
Config(std::function<void()>&& fn);
#endif
inline Config& name(const std::string& n)
{ this->p_strm.str(n); return *this; }
inline Config& prio(unsigned int p)
{ this->p_prio = p; return *this; }
inline Config& stack(epicsThreadStackSizeClass s)
{ this->p_stack = epicsThreadGetStackSize(s); return *this; }
inline Config& autostart(bool a)
{ this->p_autostart = a; return *this; }
Config& name(const std::string& n);
Config& prio(unsigned int p);
Config& stack(epicsThreadStackSizeClass s);
Config& autostart(bool a);
//! Thread will execute Runnable::run()
Config& run(Runnable* r)
{ this->p_runner = r; return *this; }
Config& run(Runnable* r);
//! Thread will execute (*fn)(ptr)
Config& run(void(*fn)(void*), void *ptr)
{
this->p_owned_runner.reset(new detail::FuncRunner(fn, ptr));
this->p_runner = this->p_owned_runner.get();
return *this;
}
Config& run(void(*fn)(void*), void *ptr);
//! Thread will execute (inst->*meth)()
template<typename C>
Config& run(C* inst, void(C::*meth)())
@@ -197,12 +132,7 @@ public:
return *this;
}
#if __cplusplus>=201103L
Config& run(const std::function<void()>& fn)
{
this->p_owned_runner.reset(new detail::BindRunner(fn));
this->p_runner = this->p_owned_runner.get();
return *this;
}
Config& run(std::function<void()>&& fn);
#endif
//! Append to thread name string. Argument must be understood by std::ostream::operator<<
@@ -228,14 +158,7 @@ public:
Thread(std::string name,
ThreadPriority priority,
Runnable *runnable,
epicsThreadStackSizeClass stkcls=epicsThreadStackSmall)
:epicsThread(*runnable,
name.c_str(),
epicsThreadGetStackSize(stkcls),
priority)
{
this->start();
}
epicsThreadStackSizeClass stkcls=epicsThreadStackSmall);
/**
*
@@ -255,38 +178,19 @@ public:
Thread(Runnable &runnable,
std::string name,
unsigned int stksize,
unsigned int priority=lowestPriority)
:epicsThread(runnable,
name.c_str(),
stksize,
priority)
{
this->start();
}
unsigned int priority=lowestPriority);
//! @brief Create a new thread using the given @class Config
//! @throws std::logic_error for improper @class Config (ie. missing runner)
Thread(Config& c)
:epicsThread(c.x_getrunner(), c.p_strm.str().c_str(),
c.p_stack, c.p_prio)
{
#if __cplusplus>=201103L
p_owned = std::move(c.p_owned_runner);
#else
p_owned = c.p_owned_runner;
#endif
if(c.p_autostart)
this->start();
}
Thread(Config& c);
/**
* Destructor
*/
~Thread()
{
this->exitWait();
}
~Thread();
static size_t num_instances;
private:
Config::p_owned_runner_t p_owned;
};

View File

@@ -126,6 +126,7 @@ private:
mutable Mutex mutex;
queue_t queue;
Event waitForWork;
bool waiting;
bool alive;
Thread thread;
};

View File

@@ -208,6 +208,35 @@ static FORCE_INLINE
typename detail::print_convolute<T>::return_t
print_cast(const T& v) { return detail::print_convolute<T>::op(v); }
struct escape;
epicsShareFunc
std::ostream& operator<<(std::ostream& strm, const escape& Q);
//! Helper to print a string with escaping
//! @code strm<<'"'<<escape(astring)<<'"' @endcode
struct epicsShareClass escape
{
enum style_t {
C, // default
CSV, // a la RFC4180
};
private:
const std::string& orig;
style_t S;
public:
escape(const std::string& orig) :orig(orig), S(C) {}
~escape();
//! Change escaping style
inline escape& style(style_t s) { S = s; return *this; }
//! print to string and return. (alloc and copy)
std::string str() const;
friend
epicsShareFunc
std::ostream& operator<<(std::ostream& strm, const escape& Q);
};
}} // end namespace
#endif // PVTYPECAST_H

147
src/misc/thread.cpp Normal file
View File

@@ -0,0 +1,147 @@
/* thread.h */
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
#include <epicsThread.h>
#define epicsExportSharedSymbols
#include <pv/thread.h>
#include <pv/reftrack.h>
namespace epics { namespace pvData {
namespace detail {
struct FuncRunner : public epicsThreadRunable
{
typedef void (*fn_t)(void*);
fn_t fn;
void *arg;
FuncRunner(fn_t f, void *a) :fn(f), arg(a) {}
virtual ~FuncRunner(){}
virtual void run()
{
(*fn)(arg);
}
};
#if __cplusplus>=201103L
struct BindRunner : public epicsThreadRunable
{
typedef std::function<void()> fn_t;
fn_t fn;
BindRunner(fn_t&& f) : fn(std::move(f)) {}
virtual ~BindRunner() {}
virtual void run()
{
fn();
}
};
#endif
} // detail
Runnable& Thread::Config::x_getrunner()
{
if(!this->p_runner)
throw std::logic_error("Thread::Config missing run()");
return *this->p_runner;
}
void Thread::Config::x_setdefault()
{
this->p_prio = epicsThreadPriorityLow;
this->p_autostart = true;
this->p_runner = NULL;
(*this).stack(epicsThreadStackSmall);
}
size_t Thread::num_instances;
Thread::Config::Config() {this->x_setdefault();}
Thread::Config::Config(Runnable *r) {this->x_setdefault();this->run(r);}
Thread::Config::Config(void(*fn)(void*), void *ptr) {this->x_setdefault();this->run(fn, ptr);}
#if __cplusplus>=201103L
Thread::Config::Config(std::function<void()>&& fn) {this->x_setdefault();this->run(std::move(fn));}
#endif
Thread::Config& Thread::Config::name(const std::string& n)
{ this->p_strm.str(n); return *this; }
Thread::Config& Thread::Config::prio(unsigned int p)
{ this->p_prio = p; return *this; }
Thread::Config& Thread::Config::stack(epicsThreadStackSizeClass s)
{ this->p_stack = epicsThreadGetStackSize(s); return *this; }
Thread::Config& Thread::Config::autostart(bool a)
{ this->p_autostart = a; return *this; }
Thread::Config& Thread::Config::run(Runnable* r)
{ this->p_runner = r; return *this; }
Thread::Config& Thread::Config::run(void(*fn)(void*), void *ptr)
{
this->p_owned_runner.reset(new detail::FuncRunner(fn, ptr));
this->p_runner = this->p_owned_runner.get();
return *this;
}
#if __cplusplus>=201103L
Thread::Config& Thread::Config::run(std::function<void()> &&fn)
{
this->p_owned_runner.reset(new detail::BindRunner(std::move(fn)));
this->p_runner = this->p_owned_runner.get();
return *this;
}
#endif
Thread::Thread(std::string name,
ThreadPriority priority,
Runnable *runnable,
epicsThreadStackSizeClass stkcls)
:epicsThread(*runnable,
name.c_str(),
epicsThreadGetStackSize(stkcls),
priority)
{
REFTRACE_INCREMENT(num_instances);
this->start();
}
Thread::Thread(Runnable &runnable,
std::string name,
unsigned int stksize,
unsigned int priority)
:epicsThread(runnable,
name.c_str(),
stksize,
priority)
{
REFTRACE_INCREMENT(num_instances);
this->start();
}
Thread::Thread(Config& c)
:epicsThread(c.x_getrunner(), c.p_strm.str().c_str(),
c.p_stack, c.p_prio)
{
REFTRACE_INCREMENT(num_instances);
#if __cplusplus>=201103L
p_owned = std::move(c.p_owned_runner);
#else
p_owned = c.p_owned_runner;
#endif
if(c.p_autostart)
this->start();
}
Thread::~Thread()
{
this->exitWait();
REFTRACE_DECREMENT(num_instances);
}
}} // epics::pvData

View File

@@ -6,10 +6,6 @@
/**
* @author mrk
*/
#if defined(_WIN32) && !defined(NOMINMAX)
#define NOMINMAX
#endif
#include <stdexcept>
#include <string>
@@ -32,9 +28,10 @@ TimerCallback::TimerCallback()
}
Timer::Timer(string threadName,ThreadPriority priority)
: waitForWork(false),
alive(true),
thread(threadName,priority,this)
:waitForWork(false)
,waiting(false)
,alive(true)
,thread(threadName,priority,this)
{}
struct TimerCallback::IncreasingTime {
@@ -68,9 +65,8 @@ bool Timer::cancel(TimerCallbackPtr const &timerCallback)
{
TimerCallbackPtr& cur = *it;
if(cur.get() == timerCallback.get()) {
queue.erase(it);
cur->onList = false;
// iteration now invalid
queue.erase(it); // invalidates cur and it
return true;
}
}
@@ -95,6 +91,7 @@ void Timer::run()
if(queue.empty()) {
// no jobs, just go to sleep
waiting = true;
epicsGuardRelease<epicsMutex> U(G);
waitForWork.wait();
@@ -122,12 +119,14 @@ void Timer::run()
// don't update 'now' until all expired jobs run
} else {
waiting = true;
// wait for first un-expired
epicsGuardRelease<epicsMutex> U(G);
waitForWork.wait(waitfor);
now = epicsTime::getCurrent();
}
waiting = false;
}
}
@@ -169,7 +168,7 @@ void Timer::schedulePeriodic(
{
epicsTime now(epicsTime::getCurrent());
bool wasempty;
bool wakeup;
{
Lock xx(mutex);
if(timerCallback->onList) {
@@ -185,10 +184,10 @@ void Timer::schedulePeriodic(
timerCallback->timeToRun = now + delay;
timerCallback->period = period;
wasempty = queue.empty();
addElement(timerCallback);
wakeup = waiting && queue.front()==timerCallback;
}
if(wasempty) waitForWork.signal();
if(wakeup) waitForWork.signal();
}
void Timer::dump(std::ostream& o) const

View File

@@ -9,10 +9,6 @@
#ifndef PVDATA_H
#define PVDATA_H
#if defined(_WIN32) && !defined(NOMINMAX)
#define NOMINMAX
#endif
#include <string>
#include <map>
#include <stdexcept>
@@ -260,6 +256,7 @@ private:
PostHandlerPtr postHandler;
friend class PVDataCreate;
friend class PVStructure;
EPICS_NOT_COPYABLE(PVField)
};
epicsShareExtern std::ostream& operator<<(std::ostream& o, const PVField& f);
@@ -340,6 +337,7 @@ public:
protected:
explicit PVScalar(ScalarConstPtr const & scalar);
EPICS_NOT_COPYABLE(PVScalar)
};
namespace detail {
@@ -397,31 +395,22 @@ public:
* Put a new value into the PVScalar.
* @param value The value.
*/
void put(typename storage_t::arg_type v) {
inline void put(typename storage_t::arg_type v) {
storage.store(v);
PVField::postPut();
}
std::ostream& dumpValue(std::ostream& o) const OVERRIDE
{
return o << get();
}
virtual std::ostream& dumpValue(std::ostream& o) const OVERRIDE;
// get operator
// double value; doubleField >>= value;
// NOTE: virtual is needed for MS C++ compiler to get this operator exported
virtual void operator>>=(T& value) const
{
value = get();
}
virtual void operator>>=(T& value) const;
// put operator
// double value = 12.8; doubleField <<= value;
// NOTE: virtual is needed for MS C++ compiler to get this operator exported
virtual void operator<<=(typename storage_t::arg_type value)
{
put(value);
}
virtual void operator<<=(typename storage_t::arg_type value);
template<typename T1>
inline T1 getAs() const {
@@ -439,44 +428,29 @@ public:
PVScalar::putFrom(v);
}
virtual void assign(const PVScalar& scalar) OVERRIDE
{
if(isImmutable())
throw std::invalid_argument("destination is immutable");
copyUnchecked(scalar);
}
virtual void copy(const PVScalar& from) OVERRIDE
{
assign(from);
}
virtual void copyUnchecked(const PVScalar& from) OVERRIDE
{
if(this==&from)
return;
T result;
from.getAs((void*)&result, typeCode);
put(result);
}
virtual void assign(const PVScalar& scalar) OVERRIDE FINAL;
virtual void copy(const PVScalar& from) OVERRIDE FINAL;
virtual void copyUnchecked(const PVScalar& from) OVERRIDE FINAL;
virtual void serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher) const OVERRIDE;
virtual void deserialize(ByteBuffer *pbuffer,
DeserializableControl *pflusher) OVERRIDE;
DeserializableControl *pflusher) OVERRIDE FINAL;
protected:
explicit PVScalarValue(ScalarConstPtr const & scalar)
: PVScalar(scalar), storage() {}
virtual void getAs(void * result, ScalarType rtype) const OVERRIDE
virtual void getAs(void * result, ScalarType rtype) const OVERRIDE FINAL
{
const T src = get();
castUnsafeV(1, rtype, result, typeCode, (const void*)&src);
}
public:
virtual void getAs(AnyScalar& v) const
virtual void getAs(AnyScalar& v) const OVERRIDE FINAL
{
v = get();
}
virtual void putFrom(const void *src, ScalarType stype) OVERRIDE
virtual void putFrom(const void *src, ScalarType stype) OVERRIDE FINAL
{
T result;
castUnsafeV(1, typeCode, (void*)&result, stype, src);
@@ -486,6 +460,7 @@ protected:
friend class PVDataCreate;
storage_t storage;
EPICS_NOT_COPYABLE(PVScalarValue)
};
/**
@@ -547,14 +522,17 @@ public:
*/
virtual ~PVString() {}
virtual std::ostream& dumpValue(std::ostream& o) const OVERRIDE FINAL;
virtual void serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher) const OVERRIDE;
SerializableControl *pflusher) const OVERRIDE FINAL;
virtual void serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher, size_t offset, size_t count) const OVERRIDE;
SerializableControl *pflusher, size_t offset, size_t count) const OVERRIDE FINAL;
protected:
explicit PVString(ScalarConstPtr const & scalar);
friend class PVDataCreate;
EPICS_NOT_COPYABLE(PVString)
};
typedef std::tr1::shared_ptr<PVString> PVStringPtr;
@@ -624,6 +602,7 @@ protected:
private:
bool capacityMutable;
friend class PVDataCreate;
EPICS_NOT_COPYABLE(PVArray)
};
epicsShareExtern std::ostream& operator<<(format::array_at_internal const& manip, const PVArray& array);
@@ -718,6 +697,7 @@ protected:
explicit PVScalarArray(ScalarArrayConstPtr const & scalarArray);
private:
friend class PVDataCreate;
EPICS_NOT_COPYABLE(PVScalarArray)
};
@@ -740,17 +720,17 @@ public:
* Set the field to be immutable, i.e. it can no longer be modified.
* This is permanent, i.e. once done the field cannot be made mutable.
*/
virtual void setImmutable() OVERRIDE;
virtual void setImmutable() OVERRIDE FINAL;
/**
* Get the introspection interface
* @return The interface.
*/
const StructureConstPtr &getStructure() const;
inline const StructureConstPtr &getStructure() const { return structurePtr; }
/**
* Get the array of pointers to the subfields in the structure.
* @return The array.
*/
const PVFieldPtrArray & getPVFields() const;
inline const PVFieldPtrArray & getPVFields() const { return pvFields; }
/**
* Get the subfield with the specified offset.
@@ -841,14 +821,14 @@ public:
* @param pflusher Interface to call when buffer is full.
*/
virtual void serialize(
ByteBuffer *pbuffer,SerializableControl *pflusher) const OVERRIDE;
ByteBuffer *pbuffer,SerializableControl *pflusher) const OVERRIDE FINAL;
/**
* Deserialize
* @param pbuffer The byte buffer.
* @param pflusher Interface to call when buffer is empty.
*/
virtual void deserialize(
ByteBuffer *pbuffer,DeserializableControl *pflusher) OVERRIDE;
ByteBuffer *pbuffer,DeserializableControl *pflusher) OVERRIDE FINAL;
/**
* Serialize.
* @param pbuffer The byte buffer.
@@ -856,7 +836,7 @@ public:
* @param pbitSet A bitset the specifies which fields to serialize.
*/
virtual void serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher,BitSet *pbitSet) const OVERRIDE;
SerializableControl *pflusher,BitSet *pbitSet) const OVERRIDE FINAL;
/**
* Deserialize
* @param pbuffer The byte buffer.
@@ -864,7 +844,7 @@ public:
* @param pbitSet A bitset the specifies which fields to deserialize.
*/
virtual void deserialize(ByteBuffer *pbuffer,
DeserializableControl*pflusher,BitSet *pbitSet) OVERRIDE;
DeserializableControl*pflusher,BitSet *pbitSet) OVERRIDE FINAL;
/**
* Constructor
* @param structure The introspection interface.
@@ -877,14 +857,56 @@ public:
*/
PVStructure(StructureConstPtr const & structure,PVFieldPtrArray const & pvFields);
virtual std::ostream& dumpValue(std::ostream& o) const OVERRIDE;
virtual std::ostream& dumpValue(std::ostream& o) const OVERRIDE FINAL;
void copy(const PVStructure& from);
void copyUnchecked(const PVStructure& from);
void copyUnchecked(const PVStructure& from, const BitSet& maskBitSet, bool inverse = false);
struct Formatter {
enum mode_t {
Auto,
Plain,
ANSI,
};
enum format_t {
Raw,
NT,
JSON,
};
private:
const PVStructure& xtop;
const BitSet* xshow;
const BitSet* xhighlight;
mode_t xmode;
format_t xfmt;
public:
explicit Formatter(const PVStructure& top)
:xtop(top)
,xshow(0)
,xhighlight(0)
,xmode(Auto)
,xfmt(NT)
{}
// those fields (and their parents) to be printed. non-NT mode.
FORCE_INLINE Formatter& show(const BitSet& set) { xshow = &set; return *this; }
// those fields (and not their parents) to be specially highlighted. non-NT mode.
FORCE_INLINE Formatter& highlight(const BitSet& set) { xhighlight = &set; return *this; }
FORCE_INLINE Formatter& mode(mode_t m) { xmode = m; return *this; }
FORCE_INLINE Formatter& format(format_t f) { xfmt = f; return *this; }
friend epicsShareFunc std::ostream& operator<<(std::ostream& strm, const Formatter& format);
friend void printRaw(std::ostream& strm, const PVStructure::Formatter& format, const PVStructure& cur);
};
FORCE_INLINE Formatter stream() const { return Formatter(*this); }
private:
inline PVFieldPtr getSubFieldImpl(const std::string& name, bool throws) const {
return getSubFieldImpl(name.c_str(), throws);
}
@@ -895,8 +917,12 @@ private:
StructureConstPtr structurePtr;
std::string extendsStructureName;
friend class PVDataCreate;
EPICS_NOT_COPYABLE(PVStructure)
};
epicsShareFunc
std::ostream& operator<<(std::ostream& strm, const PVStructure::Formatter& format);
/**
* @brief PVUnion has a single subfield.
*
@@ -1020,21 +1046,21 @@ public:
* @param pflusher Interface to call when buffer is full.
*/
virtual void serialize(
ByteBuffer *pbuffer,SerializableControl *pflusher) const OVERRIDE;
ByteBuffer *pbuffer,SerializableControl *pflusher) const OVERRIDE FINAL;
/**
* Deserialize
* @param pbuffer The byte buffer.
* @param pflusher Interface to call when buffer is empty.
*/
virtual void deserialize(
ByteBuffer *pbuffer,DeserializableControl *pflusher) OVERRIDE;
ByteBuffer *pbuffer,DeserializableControl *pflusher) OVERRIDE FINAL;
/**
* Constructor
* @param punion The introspection interface.
*/
explicit PVUnion(UnionConstPtr const & punion);
virtual std::ostream& dumpValue(std::ostream& o) const OVERRIDE;
virtual std::ostream& dumpValue(std::ostream& o) const OVERRIDE FINAL;
void copy(const PVUnion& from);
void copyUnchecked(const PVUnion& from);
@@ -1048,6 +1074,7 @@ private:
int32 selector;
PVFieldPtr value;
bool variant;
EPICS_NOT_COPYABLE(PVUnion)
};
@@ -1122,6 +1149,7 @@ namespace detail {
return thaw(result);
}
EPICS_NOT_COPYABLE(PVVectorStorage)
};
} // namespace detail
@@ -1152,67 +1180,40 @@ public:
/**
* Destructor
*/
virtual ~PVValueArray() {}
virtual ~PVValueArray();
/**
* Get introspection interface.
*/
virtual ArrayConstPtr getArray() const OVERRIDE
{
return std::tr1::static_pointer_cast<const Array>(this->getField());
}
virtual ArrayConstPtr getArray() const OVERRIDE FINAL;
std::ostream& dumpValue(std::ostream& o) const OVERRIDE
{
const_svector v(this->view());
typename const_svector::const_iterator it(v.begin()),
end(v.end());
o << '[';
if(it!=end) {
o << print_cast(*it++);
for(; it!=end; ++it)
o << ',' << print_cast(*it);
virtual std::ostream& dumpValue(std::ostream& o) const OVERRIDE FINAL;
virtual std::ostream& dumpValue(std::ostream& o, size_t index) const OVERRIDE FINAL;
}
return o << ']';
}
virtual size_t getLength() const OVERRIDE FINAL {return value.size();}
virtual size_t getCapacity() const OVERRIDE FINAL {return value.capacity();}
virtual size_t getLength() const OVERRIDE {return value.size();}
virtual size_t getCapacity() const OVERRIDE {return value.capacity();}
virtual void setCapacity(size_t capacity) OVERRIDE FINAL;
virtual void setLength(size_t length) OVERRIDE FINAL;
virtual void setCapacity(size_t capacity) OVERRIDE;
virtual void setLength(size_t length) OVERRIDE;
virtual const_svector view() const OVERRIDE {return value;}
virtual void swap(const_svector &other) OVERRIDE;
virtual void replace(const const_svector& next) OVERRIDE;
virtual const_svector view() const OVERRIDE FINAL {return value;}
virtual void swap(const_svector &other) OVERRIDE FINAL;
virtual void replace(const const_svector& next) OVERRIDE FINAL;
// from Serializable
virtual void serialize(ByteBuffer *pbuffer,SerializableControl *pflusher) const OVERRIDE;
virtual void deserialize(ByteBuffer *pbuffer,DeserializableControl *pflusher) OVERRIDE;
virtual void serialize(ByteBuffer *pbuffer,SerializableControl *pflusher) const OVERRIDE FINAL;
virtual void deserialize(ByteBuffer *pbuffer,DeserializableControl *pflusher) OVERRIDE FINAL;
virtual void serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher, size_t offset, size_t count) const OVERRIDE;
std::ostream& dumpValue(std::ostream& o, size_t index) const OVERRIDE
{
return o << print_cast(this->view().at(index));
}
SerializableControl *pflusher, size_t offset, size_t count) const OVERRIDE FINAL;
protected:
virtual void _getAsVoid(epics::pvData::shared_vector<const void>& out) const OVERRIDE
{
out = static_shared_vector_cast<const void>(this->view());
}
virtual void _putFromVoid(const epics::pvData::shared_vector<const void>& in) OVERRIDE
{
// TODO: try to re-use storage
this->replace(shared_vector_convert<const T>(in));
}
virtual void _getAsVoid(epics::pvData::shared_vector<const void>& out) const OVERRIDE FINAL;
virtual void _putFromVoid(const epics::pvData::shared_vector<const void>& in) OVERRIDE FINAL;
explicit PVValueArray(ScalarArrayConstPtr const & scalar);
const_svector value;
friend class PVDataCreate;
EPICS_NOT_COPYABLE(PVValueArray)
};
@@ -1241,24 +1242,24 @@ public:
*/
virtual ~PVValueArray() {}
virtual ArrayConstPtr getArray() const OVERRIDE
virtual ArrayConstPtr getArray() const OVERRIDE FINAL
{
return std::tr1::static_pointer_cast<const Array>(structureArray);
}
virtual size_t getLength() const OVERRIDE {return value.size();}
virtual size_t getCapacity() const OVERRIDE {return value.capacity();}
virtual size_t getLength() const OVERRIDE FINAL {return value.size();}
virtual size_t getCapacity() const OVERRIDE FINAL {return value.capacity();}
/**
* Set the array capacity.
* @param capacity The length.
*/
virtual void setCapacity(size_t capacity) OVERRIDE;
virtual void setCapacity(size_t capacity) OVERRIDE FINAL;
/**
* Set the array length.
* @param length The length.
*/
virtual void setLength(std::size_t length) OVERRIDE;
virtual void setLength(std::size_t length) OVERRIDE FINAL;
/**
* Get the introspection interface
@@ -1270,36 +1271,36 @@ public:
* @param number The number of elements to add.
* @return the new length of the array.
*/
virtual std::size_t append(std::size_t number) FINAL;
std::size_t append(std::size_t number);
/**
* Remove elements from the array.
* @param offset The offset of the first element to remove.
* @param number The number of elements to remove.
* @return (false,true) if the elements were removed.
*/
virtual bool remove(std::size_t offset,std::size_t number) FINAL;
bool remove(std::size_t offset,std::size_t number);
/**
* Compress. This removes all null elements from the array.
*/
virtual void compress() FINAL;
void compress();
virtual const_svector view() const OVERRIDE { return value; }
virtual void swap(const_svector &other) OVERRIDE;
virtual void replace(const const_svector &other) OVERRIDE {
virtual const_svector view() const OVERRIDE FINAL { return value; }
virtual void swap(const_svector &other) OVERRIDE FINAL;
virtual void replace(const const_svector &other) OVERRIDE FINAL {
checkLength(other.size());
value = other;
PVField::postPut();
}
virtual void serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher) const OVERRIDE;
SerializableControl *pflusher) const OVERRIDE FINAL;
virtual void deserialize(ByteBuffer *buffer,
DeserializableControl *pflusher) OVERRIDE;
DeserializableControl *pflusher) OVERRIDE FINAL;
virtual void serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher, std::size_t offset, std::size_t count) const OVERRIDE;
SerializableControl *pflusher, std::size_t offset, std::size_t count) const OVERRIDE FINAL;
virtual std::ostream& dumpValue(std::ostream& o) const OVERRIDE;
virtual std::ostream& dumpValue(std::ostream& o, std::size_t index) const OVERRIDE;
virtual std::ostream& dumpValue(std::ostream& o) const OVERRIDE FINAL;
virtual std::ostream& dumpValue(std::ostream& o, std::size_t index) const OVERRIDE FINAL;
void copy(const PVStructureArray& from);
void copyUnchecked(const PVStructureArray& from);
@@ -1310,6 +1311,7 @@ private:
StructureArrayConstPtr structureArray;
const_svector value;
friend class PVDataCreate;
EPICS_NOT_COPYABLE(PVValueArray)
};
@@ -1339,24 +1341,24 @@ public:
*/
virtual ~PVValueArray() {}
virtual ArrayConstPtr getArray() const OVERRIDE
virtual ArrayConstPtr getArray() const OVERRIDE FINAL
{
return std::tr1::static_pointer_cast<const Array>(unionArray);
}
virtual size_t getLength() const OVERRIDE {return value.size();}
virtual size_t getCapacity() const OVERRIDE {return value.capacity();}
virtual size_t getLength() const OVERRIDE FINAL {return value.size();}
virtual size_t getCapacity() const OVERRIDE FINAL {return value.capacity();}
/**
* Set the array capacity.
* @param capacity The length.
*/
virtual void setCapacity(size_t capacity) OVERRIDE;
virtual void setCapacity(size_t capacity) OVERRIDE FINAL;
/**
* Set the array length.
* @param length The length.
*/
virtual void setLength(std::size_t length) OVERRIDE;
virtual void setLength(std::size_t length) OVERRIDE FINAL;
/**
* Get the introspection interface
@@ -1368,36 +1370,36 @@ public:
* @param number The number of elements to add.
* @return the new length of the array.
*/
virtual std::size_t append(std::size_t number) FINAL;
std::size_t append(std::size_t number);
/**
* Remove elements from the array.
* @param offset The offset of the first element to remove.
* @param number The number of elements to remove.
* @return (false,true) if the elements were removed.
*/
virtual bool remove(std::size_t offset,std::size_t number) FINAL;
bool remove(std::size_t offset,std::size_t number);
/**
* Compress. This removes all null elements from the array.
*/
virtual void compress() FINAL;
void compress();
virtual const_svector view() const OVERRIDE { return value; }
virtual void swap(const_svector &other) OVERRIDE;
virtual void replace(const const_svector &other) OVERRIDE {
virtual void replace(const const_svector &other) OVERRIDE FINAL {
checkLength(other.size());
value = other;
PVField::postPut();
}
virtual void serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher) const OVERRIDE;
SerializableControl *pflusher) const OVERRIDE FINAL;
virtual void deserialize(ByteBuffer *buffer,
DeserializableControl *pflusher) OVERRIDE;
DeserializableControl *pflusher) OVERRIDE FINAL;
virtual void serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher, std::size_t offset, std::size_t count) const OVERRIDE;
SerializableControl *pflusher, std::size_t offset, std::size_t count) const OVERRIDE FINAL;
virtual std::ostream& dumpValue(std::ostream& o) const OVERRIDE;
virtual std::ostream& dumpValue(std::ostream& o, std::size_t index) const OVERRIDE;
virtual std::ostream& dumpValue(std::ostream& o) const OVERRIDE FINAL;
virtual std::ostream& dumpValue(std::ostream& o, std::size_t index) const OVERRIDE FINAL;
void copy(const PVUnionArray& from);
void copyUnchecked(const PVUnionArray& from);
@@ -1408,6 +1410,7 @@ private:
UnionArrayConstPtr unionArray;
const_svector value;
friend class PVDataCreate;
EPICS_NOT_COPYABLE(PVValueArray)
};
@@ -1623,6 +1626,7 @@ public:
private:
PVDataCreate();
FieldCreatePtr fieldCreate;
EPICS_NOT_COPYABLE(PVDataCreate)
};
/**
@@ -1650,9 +1654,3 @@ namespace std{
}
#endif /* PVDATA_H */
/** @page Overview Documentation
*
* <a href = "pvDataCPP.html">pvData.html</a>
*
*/

View File

@@ -12,7 +12,9 @@
#include <string>
#include <stdexcept>
#include <iostream>
#include <map>
#include <pv/lock.h>
#include <pv/noDefaultMethods.h>
#include <pv/pvType.h>
#include <pv/byteBuffer.h>
@@ -21,6 +23,15 @@
#include <shareLib.h>
#if defined(PVD_INTERNAL)
# define PVD_DEPRECATED(msg)
#elif __GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 5
# define PVD_DEPRECATED(msg) __attribute__((deprecated(msg)))
#else
# define PVD_DEPRECATED(msg) EPICS_DEPRECATED
#endif
#define PVD_DEPRECATED_52 PVD_DEPRECATED("See https://github.com/epics-base/pvDataCPP/issues/52")
/* C++11 keywords
@code
struct Base {
@@ -116,6 +127,13 @@ class UnionArray;
class BoundedString;
class PVField;
class PVScalar;
class PVScalarArray;
class PVStructure;
class PVUnion;
template<typename T> class PVValueArray;
/**
* typedef for a shared pointer to an immutable Field.
*/
@@ -339,6 +357,10 @@ public:
*/
virtual std::ostream& dump(std::ostream& o) const = 0;
//! Allocate a new instance
//! @version Added after 7.0.0
std::tr1::shared_ptr<PVField> build() const;
protected:
/**
* Constructor
@@ -347,6 +369,9 @@ protected:
Field(Type type);
private:
const Type m_fieldType;
unsigned int m_hash;
struct Helper;
friend struct Helper;
friend class StructureArray;
friend class Structure;
@@ -354,8 +379,7 @@ private:
friend class StandardField;
friend class BasePVStructureArray;
friend class FieldCreate;
struct Deleter{void operator()(Field *p){delete p;}};
EPICS_NOT_COPYABLE(Field)
};
epicsShareExtern std::ostream& operator<<(std::ostream& o, const Field& field);
@@ -383,6 +407,10 @@ public:
virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const OVERRIDE;
virtual void deserialize(ByteBuffer *buffer, DeserializableControl *control) OVERRIDE FINAL;
//! Allocate a new instance
//! @version Added after 7.0.0
std::tr1::shared_ptr<PVScalar> build() const;
protected:
Scalar(ScalarType scalarType);
@@ -394,6 +422,7 @@ private:
friend class BoundedScalarArray;
friend class FixedScalarArray;
friend class BoundedString;
EPICS_NOT_COPYABLE(Scalar)
};
/**
@@ -418,6 +447,7 @@ protected:
private:
std::size_t maxLength;
friend class FieldCreate;
EPICS_NOT_COPYABLE(BoundedString)
};
/**
@@ -452,6 +482,7 @@ protected:
*/
Array(Type type);
EPICS_NOT_COPYABLE(Array)
};
/**
@@ -485,13 +516,17 @@ public:
virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const OVERRIDE;
virtual void deserialize(ByteBuffer *buffer, DeserializableControl *control) OVERRIDE FINAL;
//! Allocate a new instance
//! @version Added after 7.0.0
std::tr1::shared_ptr<PVScalarArray> build() const;
protected:
virtual ~ScalarArray();
private:
const std::string getIDScalarArrayLUT() const;
ScalarType elementType;
friend class FieldCreate;
EPICS_NOT_COPYABLE(ScalarArray)
};
@@ -521,11 +556,11 @@ public:
virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const OVERRIDE FINAL;
protected:
virtual ~BoundedScalarArray();
private:
std::size_t size;
friend class FieldCreate;
EPICS_NOT_COPYABLE(BoundedScalarArray)
};
/**
@@ -553,11 +588,11 @@ public:
virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const OVERRIDE FINAL;
protected:
virtual ~FixedScalarArray();
private:
std::size_t size;
friend class FieldCreate;
EPICS_NOT_COPYABLE(FixedScalarArray)
};
/**
@@ -587,16 +622,22 @@ public:
virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const OVERRIDE FINAL;
virtual void deserialize(ByteBuffer *buffer, DeserializableControl *control) OVERRIDE FINAL;
//! Allocate a new instance
//! @version Added after 7.0.0
std::tr1::shared_ptr<PVValueArray<std::tr1::shared_ptr<PVStructure> > > build() const;
protected:
/**
* Constructor.
* @param structure The introspection interface for the elements.
*/
StructureArray(StructureConstPtr const & structure);
public:
virtual ~StructureArray();
private:
StructureConstPtr pstructure;
friend class FieldCreate;
EPICS_NOT_COPYABLE(StructureArray)
};
/**
@@ -626,16 +667,22 @@ public:
virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const OVERRIDE FINAL;
virtual void deserialize(ByteBuffer *buffer, DeserializableControl *control) OVERRIDE FINAL;
//! Allocate a new instance
//! @version Added after 7.0.0
std::tr1::shared_ptr<PVValueArray<std::tr1::shared_ptr<PVUnion> > > build() const;
protected:
/**
* Constructor.
* @param _punion The introspection interface for the elements.
*/
UnionArray(UnionConstPtr const & _punion);
public:
virtual ~UnionArray();
private:
UnionConstPtr punion;
friend class FieldCreate;
EPICS_NOT_COPYABLE(UnionArray)
};
/**
@@ -731,7 +778,11 @@ public:
virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const OVERRIDE FINAL;
virtual void deserialize(ByteBuffer *buffer, DeserializableControl *control) OVERRIDE FINAL;
//! Allocate a new instance
//! @version Added after 7.0.0
std::tr1::shared_ptr<PVStructure> build() const;
protected:
Structure(StringArray const & fieldNames, FieldConstPtrArray const & fields, std::string const & id = defaultId());
private:
@@ -743,6 +794,7 @@ private:
friend class FieldCreate;
friend class Union;
EPICS_NOT_COPYABLE(Structure)
};
/**
@@ -865,6 +917,10 @@ public:
virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const OVERRIDE FINAL;
virtual void deserialize(ByteBuffer *buffer, DeserializableControl *control) OVERRIDE FINAL;
//! Allocate a new instance
//! @version Added after 7.0.0
std::tr1::shared_ptr<PVUnion> build() const;
protected:
Union();
@@ -878,6 +934,7 @@ private:
friend class FieldCreate;
friend class Structure;
EPICS_NOT_COPYABLE(Union)
};
class FieldCreate;
@@ -897,6 +954,12 @@ class epicsShareClass FieldBuilder :
public std::tr1::enable_shared_from_this<FieldBuilder>
{
public:
//! Create a new instance of in-line @c Field builder.
//! @version Added after 7.0.0
static FieldBuilderPtr begin();
//! Create a new instance of in-line @c Field builder pre-initialized with and existing Structure
static FieldBuilderPtr begin(StructureConstPtr S);
/**
* Set ID of an object to be created.
* @param id id to be set.
@@ -918,7 +981,7 @@ public:
* @param maxLength a string maximum length.
* @return this instance of a @c FieldBuilder.
*/
FieldBuilderPtr addBoundedString(std::string const & name, std::size_t maxLength);
FieldBuilderPtr addBoundedString(std::string const & name, std::size_t maxLength) PVD_DEPRECATED_52;
/**
* Add a @c Field (e.g. @c Structure, @c Union).
@@ -943,7 +1006,7 @@ public:
* @param size Array fixed size.
* @return this instance of a @c FieldBuilder.
*/
FieldBuilderPtr addFixedArray(std::string const & name, ScalarType scalarType, std::size_t size);
FieldBuilderPtr addFixedArray(std::string const & name, ScalarType scalarType, std::size_t size) PVD_DEPRECATED_52;
/**
* Add bounded-size array of @c Scalar elements.
@@ -952,7 +1015,7 @@ public:
* @param bound Array maximum capacity (size).
* @return this instance of a @c FieldBuilder.
*/
FieldBuilderPtr addBoundedArray(std::string const & name, ScalarType scalarType, std::size_t bound);
FieldBuilderPtr addBoundedArray(std::string const & name, ScalarType scalarType, std::size_t bound) PVD_DEPRECATED_52;
/**
* Add array of @c Field elements.
@@ -1092,7 +1155,7 @@ public:
* @return a @c BoundedString interface for the newly created object.
* @throws IllegalArgumentException if maxLength == 0.
*/
BoundedStringConstPtr createBoundedString(std::size_t maxLength) const;
BoundedStringConstPtr createBoundedString(std::size_t maxLength) const PVD_DEPRECATED_52;
/**
* Create an @c Array field, variable size array.
* @param elementType The @c ScalarType for array elements
@@ -1105,14 +1168,14 @@ public:
* @param size Fixed array size.
* @return An @c Array Interface for the newly created object.
*/
ScalarArrayConstPtr createFixedScalarArray(ScalarType elementType, std::size_t size) const;
ScalarArrayConstPtr createFixedScalarArray(ScalarType elementType, std::size_t size) const PVD_DEPRECATED_52;
/**
* Create an @c Array field, bounded size array.
* @param elementType The @c ScalarType for array elements
* @param bound Array maximum capacity.
* @return An @c Array Interface for the newly created object.
*/
ScalarArrayConstPtr createBoundedScalarArray(ScalarType elementType, std::size_t bound) const;
ScalarArrayConstPtr createBoundedScalarArray(ScalarType elementType, std::size_t bound) const PVD_DEPRECATED_52;
/**
* Create an @c Array field that is has element type @c Structure
* @param structure The @c Structure for each array element.
@@ -1211,11 +1274,20 @@ public:
private:
FieldCreate();
// const after ctor
std::vector<ScalarConstPtr> scalars;
std::vector<ScalarArrayConstPtr> scalarArrays;
UnionConstPtr variantUnion;
UnionArrayConstPtr variantUnionArray;
mutable Mutex mutex;
typedef std::multimap<unsigned int, Field*> cache_t;
mutable cache_t cache;
struct Helper;
friend class Field;
EPICS_NOT_COPYABLE(FieldCreate)
};
/**
@@ -1267,32 +1339,35 @@ OP(pvDouble, double)
OP(pvString, std::string)
#undef OP
bool epicsShareExtern operator==(const Field&, const Field&);
bool epicsShareExtern operator==(const Scalar&, const Scalar&);
bool epicsShareExtern operator==(const ScalarArray&, const ScalarArray&);
bool epicsShareExtern operator==(const Structure&, const Structure&);
bool epicsShareExtern operator==(const StructureArray&, const StructureArray&);
bool epicsShareExtern operator==(const Union&, const Union&);
bool epicsShareExtern operator==(const UnionArray&, const UnionArray&);
bool epicsShareExtern operator==(const BoundedString&, const BoundedString&);
bool epicsShareExtern compare(const Field&, const Field&);
bool epicsShareExtern compare(const Scalar&, const Scalar&);
bool epicsShareExtern compare(const ScalarArray&, const ScalarArray&);
bool epicsShareExtern compare(const Structure&, const Structure&);
bool epicsShareExtern compare(const StructureArray&, const StructureArray&);
bool epicsShareExtern compare(const Union&, const Union&);
bool epicsShareExtern compare(const UnionArray&, const UnionArray&);
bool epicsShareExtern compare(const BoundedString&, const BoundedString&);
static inline bool operator!=(const Field& a, const Field& b)
{return !(a==b);}
static inline bool operator!=(const Scalar& a, const Scalar& b)
{return !(a==b);}
static inline bool operator!=(const ScalarArray& a, const ScalarArray& b)
{return !(a==b);}
static inline bool operator!=(const Structure& a, const Structure& b)
{return !(a==b);}
static inline bool operator!=(const StructureArray& a, const StructureArray& b)
{return !(a==b);}
static inline bool operator!=(const Union& a, const Union& b)
{return !(a==b);}
static inline bool operator!=(const UnionArray& a, const UnionArray& b)
{return !(a==b);}
static inline bool operator!=(const BoundedString& a, const BoundedString& b)
{return !(a==b);}
/** Equality with other Field
*
* The creation process of class FieldCreate ensures that identical field definitions
* will share the same instance. So pointer equality is sufficient to show defintion
* equality. If in doubt, compare() will do an full test.
*/
#define MAKE_COMPARE(CLASS) \
static FORCE_INLINE bool operator==(const CLASS& a, const CLASS& b) {return (void*)&a==(void*)&b;} \
static FORCE_INLINE bool operator!=(const CLASS& a, const CLASS& b) {return !(a==b);}
MAKE_COMPARE(Field)
MAKE_COMPARE(Scalar)
MAKE_COMPARE(ScalarArray)
MAKE_COMPARE(Structure)
MAKE_COMPARE(StructureArray)
MAKE_COMPARE(Union)
MAKE_COMPARE(UnionArray)
MAKE_COMPARE(BoundedString)
#undef MAKE_COMPARE
}}
/**

View File

@@ -14,10 +14,6 @@
#ifndef PVTYPE_H
#define PVTYPE_H
#if defined(_WIN32) && !defined(NOMINMAX)
#define NOMINMAX
#endif
#if defined(_WIN32) && !defined(_MINGW)
#pragma warning( push )
#pragma warning(disable: 4251)
@@ -34,6 +30,16 @@ typedef unsigned int uintptr_t;
#define INT64_MAX (0x7fffffffffffffffLL)
#define UINT64_MAX (0xffffffffffffffffULL)
#endif
#elif _MSC_VER==1500
#include <epicsTypes.h>
typedef epicsUInt8 uint8_t;
typedef epicsInt8 int8_t;
typedef epicsUInt16 uint16_t;
typedef epicsInt16 int16_t;
typedef epicsUInt32 uint32_t;
typedef epicsInt32 int32_t;
typedef epicsUInt64 uint64_t;
typedef epicsInt64 int64_t;
#else
#include <stdint.h>
#endif

View File

@@ -61,6 +61,7 @@ typedef std::tr1::shared_ptr<StandardField> StandardFieldPtr;
* }
*/
class epicsShareClass StandardField {
static void once(void*);
public:
/**
* getStandardField returns the singleton.
@@ -131,123 +132,105 @@ public:
* create an alarm structure
* @return The const shared pointer to the structure.
*/
StructureConstPtr alarm();
inline const StructureConstPtr& alarm() const { return alarmField; }
/**
* create a timeStamp structure
* @return The const shared pointer to the structure.
*/
StructureConstPtr timeStamp();
inline const StructureConstPtr& timeStamp() const { return timeStampField; }
/**
* create a display structure
* @return The const shared pointer to the structure.
*/
StructureConstPtr display();
inline const StructureConstPtr& display() const { return displayField; }
/**
* create a control structure
* @return The const shared pointer to the structure.
*/
StructureConstPtr control();
inline const StructureConstPtr& control() const { return controlField; }
/**
* create a boolean alarm structure
* @return The const shared pointer to the structure.
*/
StructureConstPtr booleanAlarm();
inline const StructureConstPtr& booleanAlarm() const { return booleanAlarmField; }
/**
* create a byte alarm structure
* @return The const shared pointer to the structure.
*/
StructureConstPtr byteAlarm();
inline const StructureConstPtr& byteAlarm() const { return byteAlarmField; }
/**
* create a unsigned byte alarm structure
* @return The const shared pointer to the structure.
*/
StructureConstPtr ubyteAlarm();
inline const StructureConstPtr& ubyteAlarm() const { return ubyteAlarmField; }
/**
* create a short alarm structure
* @return The const shared pointer to the structure.
*/
StructureConstPtr shortAlarm();
inline const StructureConstPtr& shortAlarm() const { return shortAlarmField; }
/**
* create a unsigned short alarm structure
* @return The const shared pointer to the structure.
*/
StructureConstPtr ushortAlarm();
inline const StructureConstPtr& ushortAlarm() const { return ushortAlarmField; }
/**
* create an int alarm structure
* @return The const shared pointer to the structure.
*/
StructureConstPtr intAlarm();
inline const StructureConstPtr& intAlarm() const { return intAlarmField; }
/**
* create a unsigned int alarm structure
* @return The const shared pointer to the structure.
*/
StructureConstPtr uintAlarm();
inline const StructureConstPtr& uintAlarm() const { return uintAlarmField; }
/**
* create a long alarm structure
* @return The const shared pointer to the structure.
*/
StructureConstPtr longAlarm();
inline const StructureConstPtr& longAlarm() const { return longAlarmField; }
/**
* create a unsigned long alarm structure
* @return The const shared pointer to the structure.
*/
StructureConstPtr ulongAlarm();
inline const StructureConstPtr& ulongAlarm() const { return ulongAlarmField; }
/**
* create a float alarm structure
* @return The const shared pointer to the structure.
*/
StructureConstPtr floatAlarm();
inline const StructureConstPtr& floatAlarm() const { return floatAlarmField; }
/**
* create a double alarm structure
* @return The const shared pointer to the structure.
*/
StructureConstPtr doubleAlarm();
inline const StructureConstPtr& doubleAlarm() const { return doubleAlarmField; }
/**
* create an enumerated alarm structure
* @return The const shared pointer to the structure.
*/
StructureConstPtr enumeratedAlarm();
inline const StructureConstPtr& enumeratedAlarm() const { return enumeratedAlarmField; }
private:
StandardField();
void init();
StructureConstPtr createProperties(
std::string id,FieldConstPtr field,std::string properties);
FieldCreatePtr fieldCreate;
std::string notImplemented;
std::string valueFieldName;
StructureConstPtr alarmField;
StructureConstPtr timeStampField;
StructureConstPtr displayField;
StructureConstPtr controlField;
StructureConstPtr booleanAlarmField;
StructureConstPtr byteAlarmField;
StructureConstPtr shortAlarmField;
StructureConstPtr intAlarmField;
StructureConstPtr longAlarmField;
StructureConstPtr ubyteAlarmField;
StructureConstPtr ushortAlarmField;
StructureConstPtr uintAlarmField;
StructureConstPtr ulongAlarmField;
StructureConstPtr floatAlarmField;
StructureConstPtr doubleAlarmField;
StructureConstPtr enumeratedAlarmField;
void createAlarm();
void createTimeStamp();
void createDisplay();
void createControl();
void createBooleanAlarm();
void createByteAlarm();
void createShortAlarm();
void createIntAlarm();
void createLongAlarm();
void createUByteAlarm();
void createUShortAlarm();
void createUIntAlarm();
void createULongAlarm();
void createFloatAlarm();
void createDoubleAlarm();
void createEnumeratedAlarm();
//friend StandardFieldPtr getStandardField();
const FieldCreatePtr fieldCreate;
const std::string notImplemented;
const std::string valueFieldName;
const StructureConstPtr alarmField;
const StructureConstPtr timeStampField;
const StructureConstPtr displayField;
const StructureConstPtr controlField;
const StructureConstPtr booleanAlarmField;
const StructureConstPtr byteAlarmField;
const StructureConstPtr shortAlarmField;
const StructureConstPtr intAlarmField;
const StructureConstPtr longAlarmField;
const StructureConstPtr ubyteAlarmField;
const StructureConstPtr ushortAlarmField;
const StructureConstPtr uintAlarmField;
const StructureConstPtr ulongAlarmField;
const StructureConstPtr floatAlarmField;
const StructureConstPtr doubleAlarmField;
const StructureConstPtr enumeratedAlarmField;
};
FORCE_INLINE const StandardFieldPtr& getStandardField() {

View File

@@ -285,7 +285,7 @@ PVStructure::shared_pointer ValueBuilder::buildPVStructure() const
type = tbuild->createStructure();
}
PVStructure::shared_pointer root(getPVDataCreate()->createPVStructure(type));
PVStructure::shared_pointer root(type->build());
child_struct::storeStruct(*this, root);

View File

@@ -15,7 +15,9 @@
#include <pv/pvUnitTest.h>
#include <testMain.h>
#include <pv/current_function.h>
#include <pv/createRequest.h>
#include <pv/bitSet.h>
namespace {
@@ -335,15 +337,397 @@ static void testBadRequest()
testOk1(!!C->createRequest("field(value)"));
testOk1(C->getMessage().empty());
// duplicate fieldName C
// correct is: "field(A,C{D,E.F})"
testThrows(std::invalid_argument, createRequest("field(A,C.D,C.E.F)"));
}
static
StructureConstPtr maskingType = getFieldCreate()->createFieldBuilder()
->add("A", pvInt)
->add("B", pvInt)
->addNestedStructure("C")
->add("D", pvInt)
->addNestedStructure("E")
->add("F", pvInt)
->endNested()
->endNested()
->createStructure();
static
void testMapper(PVRequestMapper::mode_t mode)
{
testDiag("=== %s mode==%d", CURRENT_FUNCTION, (int)mode);
{
testDiag("Map full structure");
PVStructurePtr base(getPVDataCreate()->createPVStructure(maskingType));
PVRequestMapper mapper(*base, *createRequest(""), mode);
testEqual(mapper.requested(), maskingType);
testEqual(mapper.requestedMask(), BitSet().set(0)
.set(base->getSubFieldT("A")->getFieldOffset())
.set(base->getSubFieldT("B")->getFieldOffset())
.set(base->getSubFieldT("C")->getFieldOffset())
.set(base->getSubFieldT("C.D")->getFieldOffset())
.set(base->getSubFieldT("C.E")->getFieldOffset())
.set(base->getSubFieldT("C.E.F")->getFieldOffset()));
PVStructurePtr req(getPVDataCreate()->createPVStructure(mapper.requested()));
base->getSubFieldT<PVInt>("A")->put(1);
base->getSubFieldT<PVInt>("B")->put(42);
BitSet output;
mapper.copyBaseToRequested(*base, BitSet().set(0), *req, output);
testFieldEqual<PVInt>(req, "A", 1);
testFieldEqual<PVInt>(req, "B", 42);
req->getSubFieldT<PVInt>("A")->put(2);
req->getSubFieldT<PVInt>("B")->put(43);
mapper.copyBaseFromRequested(*base, output, *req, BitSet().set(0));
testFieldEqual<PVInt>(req, "A", 2);
testFieldEqual<PVInt>(req, "B", 43);
testEqual(mapper.requestedMask(), BitSet().set(0).set(1).set(2).set(3).set(4).set(5).set(6));
}
{
testDiag("Map single leaf field");
PVStructurePtr base(getPVDataCreate()->createPVStructure(maskingType));
PVRequestMapper mapper(*base, *createRequest("field(B)"), mode);
if(mode==PVRequestMapper::Slice)
testNotEqual(mapper.requested(), maskingType);
else
testEqual(mapper.requested(), maskingType);
testEqual(mapper.requestedMask(), BitSet().set(0)
.set(base->getSubFieldT("B")->getFieldOffset()));
PVStructurePtr req(getPVDataCreate()->createPVStructure(mapper.requested()));
if(mode==PVRequestMapper::Slice)
testOk1(!req->getSubField("A"));
base->getSubFieldT<PVScalar>("A")->putFrom<int32>(11);
base->getSubFieldT<PVScalar>("B")->putFrom<int32>(42);
BitSet output;
mapper.copyBaseToRequested(*base, BitSet().set(0), *req, output);
if(mode!=PVRequestMapper::Slice)
testFieldEqual<PVInt>(req, "A", 0);
testFieldEqual<PVInt>(req, "B", 42);
req->getSubFieldT<PVScalar>("B")->putFrom<int32>(43);
mapper.copyBaseFromRequested(*base, output, *req, BitSet().set(0));
testFieldEqual<PVInt>(req, "B", 43);
testEqual(mapper.requestedMask(), BitSet().set(0)
.set(base->getSubFieldT("B")->getFieldOffset()));
BitSet cmp;
mapper.maskBaseToRequested(BitSet().set(base->getSubFieldT("B")->getFieldOffset()), cmp);
testEqual(cmp, BitSet()
.set(req->getSubFieldT("B")->getFieldOffset()));
cmp.clear();
mapper.maskBaseFromRequested(cmp, BitSet()
.set(req->getSubFieldT("B")->getFieldOffset()));
testEqual(cmp, BitSet()
.set(base->getSubFieldT("B")->getFieldOffset()));
}
{
testDiag("Map two sub-fields");
PVStructurePtr base(getPVDataCreate()->createPVStructure(maskingType));
PVRequestMapper mapper(*base, *createRequest("B,C.D"), mode);
if(mode==PVRequestMapper::Slice)
testNotEqual(mapper.requested(), maskingType);
else
testEqual(mapper.requested(), maskingType);
testEqual(mapper.requestedMask(), BitSet().set(0)
.set(base->getSubFieldT("B")->getFieldOffset())
.set(base->getSubFieldT("C")->getFieldOffset())
.set(base->getSubFieldT("C.D")->getFieldOffset()));
PVStructurePtr req(getPVDataCreate()->createPVStructure(mapper.requested()));
if(mode==PVRequestMapper::Slice)
testOk1(!req->getSubField("A"));
base->getSubFieldT<PVScalar>("A")->putFrom<int32>(11);
base->getSubFieldT<PVScalar>("B")->putFrom<int32>(1);
base->getSubFieldT<PVScalar>("C.D")->putFrom<int32>(42);
BitSet output;
mapper.copyBaseToRequested(*base, BitSet().set(0), *req, output);
if(mode!=PVRequestMapper::Slice)
testFieldEqual<PVInt>(req, "A", 0);
testFieldEqual<PVInt>(req, "B", 1);
testFieldEqual<PVInt>(req, "C.D", 42);
req->getSubFieldT<PVScalar>("B")->putFrom<int32>(2);
req->getSubFieldT<PVScalar>("C.D")->putFrom<int32>(43);
mapper.copyBaseFromRequested(*base, output, *req, BitSet().set(0));
testFieldEqual<PVInt>(req, "B", 2);
testFieldEqual<PVInt>(req, "C.D", 43);
testEqual(mapper.requestedMask(), BitSet().set(0)
.set(base->getSubFieldT("B")->getFieldOffset())
.set(base->getSubFieldT("C")->getFieldOffset())
.set(base->getSubFieldT("C.D")->getFieldOffset()));
}
{
testDiag("Map entire sub-structure");
PVStructurePtr base(getPVDataCreate()->createPVStructure(maskingType));
PVRequestMapper mapper(*base, *createRequest("field(C.E)"), mode);
if(mode==PVRequestMapper::Slice)
testNotEqual(mapper.requested(), maskingType);
else
testEqual(mapper.requested(), maskingType);
testEqual(mapper.requestedMask(), BitSet().set(0)
.set(base->getSubFieldT("C")->getFieldOffset())
.set(base->getSubFieldT("C.E")->getFieldOffset())
.set(base->getSubFieldT("C.E.F")->getFieldOffset()));
PVStructurePtr req(getPVDataCreate()->createPVStructure(mapper.requested()));
if(mode==PVRequestMapper::Slice)
testOk1(!req->getSubField("A"));
base->getSubFieldT<PVScalar>("A")->putFrom<int32>(11);
base->getSubFieldT<PVScalar>("C.E.F")->putFrom<int32>(42);
BitSet output;
mapper.copyBaseToRequested(*base, BitSet().set(0), *req, output);
if(mode!=PVRequestMapper::Slice)
testFieldEqual<PVInt>(req, "A", 0);
testFieldEqual<PVInt>(req, "C.E.F", 42);
req->getSubFieldT<PVScalar>("C.E.F")->putFrom<int32>(43);
mapper.copyBaseFromRequested(*base, output, *req, BitSet().set(0));
testFieldEqual<PVInt>(req, "C.E.F", 43);
testEqual(mapper.requestedMask(), BitSet().set(0)
.set(base->getSubFieldT("C")->getFieldOffset())
.set(base->getSubFieldT("C.E")->getFieldOffset())
.set(base->getSubFieldT("C.E.F")->getFieldOffset()));
BitSet cmp;
mapper.maskBaseToRequested(BitSet()
.set(base->getSubFieldT("C")->getFieldOffset()), cmp);
testEqual(cmp, BitSet()
.set(req->getSubFieldT("C")->getFieldOffset())
.set(req->getSubFieldT("C.E")->getFieldOffset())
.set(req->getSubFieldT("C.E.F")->getFieldOffset()));
cmp.clear();
mapper.maskBaseFromRequested(cmp, BitSet()
.set(req->getSubFieldT("C")->getFieldOffset()));
testEqual(cmp, BitSet()
.set(base->getSubFieldT("C")->getFieldOffset())
.set(base->getSubFieldT("C.E")->getFieldOffset())
.set(base->getSubFieldT("C.E.F")->getFieldOffset()));
}
}
struct MapperMask {
PVStructurePtr base, req;
BitSet bmask, rmask;
PVRequestMapper mapper;
MapperMask(PVRequestMapper::mode_t mode) {
base = getPVDataCreate()->createPVStructure(maskingType);
mapper.compute(*base, *createRequest("field(B,C.E)"), mode);
req = getPVDataCreate()->createPVStructure(mapper.requested());
reset();
}
void reset() {
base->getSubFieldT<PVScalar>("B")->putFrom<int32>(1);
base->getSubFieldT<PVScalar>("C.E.F")->putFrom<int32>(3);
req->getSubFieldT<PVScalar>("B")->putFrom<int32>(11);
req->getSubFieldT<PVScalar>("C.E.F")->putFrom<int32>(13);
}
void check(int32 bB, int32 bCEF, int32 rB, int32 rCEF) {
testFieldEqual<PVInt>(base, "B", bB);
testFieldEqual<PVInt>(base, "C.E.F", bCEF);
testFieldEqual<PVInt>(req, "B", rB);
testFieldEqual<PVInt>(req, "C.E.F", rCEF);
}
void testEmptyMaskB2R() {
mapper.copyBaseToRequested(*base, bmask, *req, rmask);
check(1, 3, 11, 13);
testEqual(bmask, BitSet());
testEqual(rmask, BitSet());
}
void testEmptyMaskR2B() {
mapper.copyBaseFromRequested(*base, bmask, *req, rmask);
check(1, 3, 11, 13);
testEqual(bmask, BitSet());
testEqual(rmask, BitSet());
}
void testAllMaskB2R() {
bmask.set(0);
mapper.copyBaseToRequested(*base, bmask, *req, rmask);
check(1, 3, 1, 3);
testEqual(rmask, BitSet()
.set(0)
.set(req->getSubFieldT("B")->getFieldOffset())
.set(req->getSubFieldT("C")->getFieldOffset())
.set(req->getSubFieldT("C.E")->getFieldOffset())
.set(req->getSubFieldT("C.E.F")->getFieldOffset()));
}
void testAllMaskR2B() {
rmask.set(0);
mapper.copyBaseFromRequested(*base, bmask, *req, rmask);
check(11, 13, 11, 13);
testEqual(bmask, BitSet()
.set(0)
.set(base->getSubFieldT("B")->getFieldOffset())
.set(base->getSubFieldT("C")->getFieldOffset())
.set(base->getSubFieldT("C.E")->getFieldOffset())
.set(base->getSubFieldT("C.E.F")->getFieldOffset()));
}
void testMaskOneB2R() {
bmask.set(base->getSubFieldT("B")->getFieldOffset());
mapper.copyBaseToRequested(*base, bmask, *req, rmask);
check(1, 3, 1, 13);
testEqual(rmask, BitSet()
.set(req->getSubFieldT("B")->getFieldOffset()));
}
void testMaskOneR2B() {
rmask.set(req->getSubFieldT("B")->getFieldOffset());
mapper.copyBaseFromRequested(*base, bmask, *req, rmask);
check(11, 3, 11, 13);
testEqual(bmask, BitSet()
.set(base->getSubFieldT("B")->getFieldOffset()));
}
void testMaskOtherB2R() {
bmask.set(base->getSubFieldT("C.E.F")->getFieldOffset());
mapper.copyBaseToRequested(*base, bmask, *req, rmask);
check(1, 3, 11, 3);
testEqual(rmask, BitSet()
.set(req->getSubFieldT("C.E.F")->getFieldOffset()));
}
void testMaskOtherR2B() {
rmask.set(req->getSubFieldT("C.E.F")->getFieldOffset());
mapper.copyBaseFromRequested(*base, bmask, *req, rmask);
check(1, 13, 11, 13);
testEqual(bmask, BitSet()
.set(base->getSubFieldT("C.E.F")->getFieldOffset()));
}
void testMaskSub1B2R() {
bmask.set(base->getSubFieldT("C.E")->getFieldOffset());
mapper.copyBaseToRequested(*base, bmask, *req, rmask);
check(1, 3, 11, 3);
testEqual(rmask, BitSet()
.set(req->getSubFieldT("C.E")->getFieldOffset())
.set(req->getSubFieldT("C.E.F")->getFieldOffset()));
}
void testMaskSub1R2B() {
rmask.set(req->getSubFieldT("C.E")->getFieldOffset());
mapper.copyBaseFromRequested(*base, bmask, *req, rmask);
check(1, 13, 11, 13);
testEqual(bmask, BitSet()
.set(base->getSubFieldT("C.E")->getFieldOffset())
.set(base->getSubFieldT("C.E.F")->getFieldOffset()));
}
void testMaskSub2B2R() {
bmask.set(base->getSubFieldT("C")->getFieldOffset());
mapper.copyBaseToRequested(*base, bmask, *req, rmask);
check(1, 3, 11, 3);
testEqual(rmask, BitSet()
.set(req->getSubFieldT("C")->getFieldOffset())
.set(req->getSubFieldT("C.E")->getFieldOffset())
.set(req->getSubFieldT("C.E.F")->getFieldOffset()));
}
void testMaskSub2R2B() {
rmask.set(req->getSubFieldT("C")->getFieldOffset());
mapper.copyBaseFromRequested(*base, bmask, *req, rmask);
check(1, 13, 11, 13);
testEqual(bmask, BitSet()
.set(base->getSubFieldT("C")->getFieldOffset())
.set(base->getSubFieldT("C.E")->getFieldOffset())
.set(base->getSubFieldT("C.E.F")->getFieldOffset()));
}
};
void testMaskWarn()
{
PVStructurePtr base(getPVDataCreate()->createPVStructure(maskingType));
PVRequestMapper mapper(*base, *createRequest("field(B,invalid)"), PVRequestMapper::Slice);
testEqual(mapper.warnings(), "No field 'invalid' ");
}
void testMaskErr()
{
PVStructurePtr base(getPVDataCreate()->createPVStructure(maskingType));
testThrows(std::runtime_error, PVRequestMapper mapper(*base, *createRequest("field(invalid)"), PVRequestMapper::Slice));
}
} // namespace
MAIN(testCreateRequest)
{
testPlan(126);
testPlan(315);
testCreateRequestInternal();
testBadRequest();
testMapper(PVRequestMapper::Slice);
testMapper(PVRequestMapper::Mask);
#undef TEST_METHOD
#define TEST_METHOD(KLASS, METHOD) \
{ \
testDiag("------- %s::%s Mask --------", #KLASS, #METHOD); \
{ KLASS inst(PVRequestMapper::Mask); inst.METHOD(); } \
testDiag("------- %s::%s Slice --------", #KLASS, #METHOD); \
{ KLASS inst(PVRequestMapper::Slice); inst.METHOD(); } \
}
TEST_METHOD(MapperMask, testEmptyMaskB2R);
TEST_METHOD(MapperMask, testEmptyMaskR2B);
TEST_METHOD(MapperMask, testAllMaskB2R);
TEST_METHOD(MapperMask, testAllMaskR2B);
TEST_METHOD(MapperMask, testMaskOneB2R);
TEST_METHOD(MapperMask, testMaskOneR2B);
TEST_METHOD(MapperMask, testMaskOtherB2R);
TEST_METHOD(MapperMask, testMaskOtherR2B);
TEST_METHOD(MapperMask, testMaskSub1B2R);
TEST_METHOD(MapperMask, testMaskSub1R2B);
TEST_METHOD(MapperMask, testMaskSub2B2R);
TEST_METHOD(MapperMask, testMaskSub2R2B);
testMaskWarn();
testMaskErr();
return testDone();
}

View File

@@ -20,6 +20,10 @@
#include <epicsUnitTest.h>
#include <testMain.h>
#include <compilerDependencies.h>
#undef EPICS_DEPRECATED
#define EPICS_DEPRECATED
#include <pv/standardField.h>
#include <pv/standardPVField.h>
#include <pv/convert.h>

View File

@@ -79,3 +79,7 @@ TESTS += test_reftrack
TESTPROD_HOST += testanyscalar
testanyscalar_SRCS += testanyscalar.cpp
TESTS += testanyscalar
TESTPROD_HOST += testprinter
testprinter_SRCS += testprinter.cpp
TESTS += testprinter

View File

@@ -5,10 +5,6 @@
*/
/* Author: Matej Sekoranja Date: 2010.10.18 */
#if defined(_WIN32) && !defined(NOMINMAX)
#define NOMINMAX
#endif
#include <iostream>
#include <iomanip>
#include <stddef.h>

View File

@@ -14,15 +14,13 @@
#include <cstring>
#include <memory>
#include <epicsUnitTest.h>
#include <testMain.h>
#include <pv/pvUnitTest.h>
#include <pv/byteBuffer.h>
#include <pv/pvIntrospect.h>
using namespace epics::pvData;
using std::string;
using std::cout;
static
void testBasicOperations() {
@@ -175,7 +173,7 @@ void testBasicOperations() {
testOk1(buff->getPosition()==6);
testOk1(strncmp(&src[2],&dst[2],6)==0);
cout<<"# First 10 characters of destination: >>"<<string(dst, 10)<<"<<\n";
testShow()<<"First 10 characters of destination: >>"<<std::string(dst, 10)<<"<<\n";
}
static const char expect_be[] = "abcdef";
@@ -253,14 +251,68 @@ void testUnaligned()
testOk1(memcmp(buf.getBuffer(), "\x42\x12\x34\x56\x78\x90\xab\xcd\xef\x41\x41\x41", 8)==0);
}
static
void testArrayLE()
{
testDiag("testArray() LE");
ByteBuffer buf(8, EPICS_ENDIAN_LITTLE);
std::vector<uint32> vals;
vals.push_back(0x12345678);
vals.push_back(0x01020304);
buf.putArray(&vals[0], vals.size());
testEqual(buf.getPosition(), 8);
testOk1(memcmp(buf.getBuffer(), "\x78\x56\x34\x12\x04\x03\x02\x01", 8)==0);
buf.clear();
buf.put("\x40\x30\x20\x10\xa4\xa3\xa2\xa1", 0, 8);
buf.flip();
buf.getArray(&vals[0], 2);
testEqual(vals[0], 0x10203040);
testEqual(vals[1], 0xa1a2a3a4);
}
static
void testArrayBE()
{
testDiag("testArray() BE");
ByteBuffer buf(8, EPICS_ENDIAN_BIG);
std::vector<uint32> vals;
vals.push_back(0x12345678);
vals.push_back(0x01020304);
buf.putArray(&vals[0], vals.size());
testEqual(buf.getPosition(), 8);
testOk1(memcmp(buf.getBuffer(), "\x12\x34\x56\x78\x01\x02\x03\x04", 8)==0);
buf.clear();
buf.put("\x10\x20\x30\x40\xa1\xa2\xa3\xa4", 0, 8);
buf.flip();
buf.getArray(&vals[0], 2);
testEqual(vals[0], 0x10203040);
testEqual(vals[1], 0xa1a2a3a4);
}
MAIN(testByteBuffer)
{
testPlan(96);
testPlan(104);
testDiag("Tests byteBuffer");
testBasicOperations();
testInverseEndianness(EPICS_ENDIAN_BIG, expect_be);
testInverseEndianness(EPICS_ENDIAN_LITTLE, expect_le);
testSwap();
testUnaligned();
testArrayLE();
testArrayBE();
return testDone();
}

View File

@@ -9,10 +9,6 @@
* Author: Miha Vitorovic
*/
#if defined(_WIN32) && !defined(NOMINMAX)
#define NOMINMAX
#endif
#include <iostream>
#include <fstream>
@@ -28,6 +24,8 @@
#include <pv/noDefaultMethods.h>
#include <pv/byteBuffer.h>
#include <pv/convert.h>
#include <pv/pvUnitTest.h>
#include <pv/current_function.h>
#include <pv/standardField.h>
@@ -633,6 +631,8 @@ void testStructureId() {
void serializationFieldTest(FieldConstPtr const & field)
{
testShow()<<CURRENT_FUNCTION<<"\n"<<field;
buffer->clear();
// serialize
@@ -643,7 +643,7 @@ void serializationFieldTest(FieldConstPtr const & field)
FieldConstPtr deserializedField = getFieldCreate()->deserialize(buffer, control);
// must equal
testShow()<<" after "<<(void*)field.get()<<" == "<<(void*)deserializedField.get();
testOk1(*field == *deserializedField);
}

View File

@@ -17,15 +17,14 @@
#include "pv/sharedVector.h"
using std::string;
using namespace epics::pvData;
namespace {
namespace pvd = epics::pvData;
void testEmpty()
{
testDiag("Test empty vector");
epics::pvData::shared_vector<int32> empty, empty2, empty3(0u);
pvd::shared_vector<pvd::int32> empty, empty2, empty3(0u);
testOk1(empty.size()==0);
testOk1(empty.empty());
@@ -47,7 +46,7 @@ void testInternalAlloc()
{
testDiag("Test vector alloc w/ new[]");
epics::pvData::shared_vector<int32> internal(5);
pvd::shared_vector<pvd::int32> internal(5);
testOk1(internal.size()==5);
testOk1(!internal.empty());
@@ -62,7 +61,7 @@ void testInternalAlloc()
internal[2] = 42;
testOk1(internal[2]==42);
epics::pvData::shared_vector<int32> internal2(15, 500);
pvd::shared_vector<pvd::int32> internal2(15, 500);
testOk1(internal2.size()==15);
testOk1(internal2[1]==500);
@@ -83,8 +82,8 @@ void testInternalAlloc()
//Note: STL shared_ptr requires that deletors be copy constructable
template<typename E>
struct callCounter {
std::tr1::shared_ptr<int32> count;
callCounter():count(new int32){*count=0;}
std::tr1::shared_ptr<pvd::int32> count;
callCounter():count(new pvd::int32){*count=0;}
callCounter(const callCounter& o):count(o.count) {}
callCounter& operator=(const callCounter& o){count=o.count;}
void operator()(E){(*count)++;}
@@ -95,8 +94,8 @@ void testExternalAlloc()
testDiag("Test vector external alloc");
// Simulate a failed malloc() or similar
int32 *oops=0;
epics::pvData::shared_vector<int32> nullPtr(oops, 42, 100);
pvd::int32 *oops=0;
pvd::shared_vector<pvd::int32> nullPtr(oops, 42, 100);
testOk1(nullPtr.size()==0);
testOk1(nullPtr.empty());
@@ -105,8 +104,8 @@ void testExternalAlloc()
testOk1(nullPtr.data()==NULL);
int32 *raw=new int32[5];
epics::pvData::shared_vector<int32> newData(raw, 1, 4);
pvd::int32 *raw=new pvd::int32[5];
pvd::shared_vector<pvd::int32> newData(raw, 1, 4);
testOk1(newData.size()==4);
testOk1(!newData.empty());
@@ -116,11 +115,11 @@ void testExternalAlloc()
testOk1(newData[0]==14);
// Check use of custom deleter
int32 localVar[4] = {1,2,3,4};
callCounter<int32*> tracker;
pvd::int32 localVar[4] = {1,2,3,4};
callCounter<pvd::int32*> tracker;
testOk1(*tracker.count==0);
epics::pvData::shared_vector<int32> locvar(localVar,
pvd::shared_vector<pvd::int32> locvar(localVar,
tracker,
0, 4);
@@ -140,8 +139,8 @@ void testShare()
{
testDiag("Test vector Sharing");
epics::pvData::shared_vector<int32> one, two(15);
epics::pvData::shared_vector<int32> three(two);
pvd::shared_vector<pvd::int32> one, two(15);
pvd::shared_vector<pvd::int32> three(two);
testOk1(one.unique());
testOk1(!two.unique());
@@ -202,22 +201,22 @@ void testConst()
{
testDiag("Test constant vector");
epics::pvData::shared_vector<int32> writable(15, 100);
pvd::shared_vector<pvd::int32> writable(15, 100);
epics::pvData::shared_vector<int32>::reference wr = writable[0];
epics::pvData::shared_vector<int32>::const_reference ror = writable[0];
pvd::shared_vector<pvd::int32>::reference wr = writable[0];
pvd::shared_vector<pvd::int32>::const_reference ror = writable[0];
testOk1(wr==ror);
int32 *compare = writable.data();
pvd::int32 *compare = writable.data();
testOk1(writable.unique());
// can re-target container, but data is R/O
epics::pvData::shared_vector<const int32> rodata(freeze(writable));
pvd::shared_vector<const pvd::int32> rodata(freeze(writable));
epics::pvData::shared_vector<const int32>::reference wcr = rodata[0];
epics::pvData::shared_vector<const int32>::const_reference rocr = rodata[0];
pvd::shared_vector<const pvd::int32>::reference wcr = rodata[0];
pvd::shared_vector<const pvd::int32>::const_reference rocr = rodata[0];
testOk1(wcr==rocr);
@@ -230,7 +229,7 @@ void testConst()
testOk1(rodata.data()==compare);
epics::pvData::shared_vector<const int32> rodata2(rodata);
pvd::shared_vector<const pvd::int32> rodata2(rodata);
testOk1(rodata.data()==rodata2.data());
@@ -243,9 +242,9 @@ void testSlice()
{
testDiag("Test vector slicing");
epics::pvData::shared_vector<int32> original(10, 100);
pvd::shared_vector<pvd::int32> original(10, 100);
epics::pvData::shared_vector<int32> half1(original), half2(original), half2a(original);
pvd::shared_vector<pvd::int32> half1(original), half2(original), half2a(original);
half1.slice(0, 5);
half2.slice(5, 5);
@@ -293,9 +292,9 @@ void testCapacity()
{
testDiag("Test vector capacity");
epics::pvData::shared_vector<int32> vect(10, 100);
pvd::shared_vector<pvd::int32> vect(10, 100);
int32 *peek = vect.dataPtr().get();
pvd::int32 *peek = vect.dataPtr().get();
vect.slice(0, 5);
@@ -333,7 +332,7 @@ void testCapacity()
void testPush()
{
epics::pvData::shared_vector<int32> vect;
pvd::shared_vector<pvd::int32> vect;
testDiag("Test push_back optimizations");
@@ -360,16 +359,16 @@ void testVoid()
{
testDiag("Test vector cast to/from void");
epics::pvData::shared_vector<int32> IV(4);
pvd::shared_vector<pvd::int32> IV(4);
epics::pvData::shared_vector<void> VV(epics::pvData::static_shared_vector_cast<void>(IV));
pvd::shared_vector<void> VV(pvd::static_shared_vector_cast<void>(IV));
testOk1(IV.dataPtr().get()==VV.dataPtr().get());
testOk1(IV.size()*sizeof(int)==VV.size());
testOk1(IV.size()*sizeof(pvd::int32)==VV.size());
VV.slice(sizeof(int), 2*sizeof(int));
VV.slice(sizeof(pvd::int32), 2*sizeof(pvd::int32));
IV = epics::pvData::static_shared_vector_cast<int32>(VV);
IV = pvd::static_shared_vector_cast<pvd::int32>(VV);
testOk1(IV.dataOffset()==1);
testOk1(IV.size()==2);
@@ -380,23 +379,23 @@ void testConstVoid()
{
testDiag("Test vector cast to/from const void");
epics::pvData::shared_vector<const int32> CIV(4);
pvd::shared_vector<const pvd::int32> CIV(4);
epics::pvData::shared_vector<const void> CVV(epics::pvData::static_shared_vector_cast<const void>(CIV));
pvd::shared_vector<const void> CVV(pvd::static_shared_vector_cast<const void>(CIV));
// case const void to const void
epics::pvData::shared_vector<const void> CVV2(epics::pvData::static_shared_vector_cast<const void>(CVV));
pvd::shared_vector<const void> CVV2(pvd::static_shared_vector_cast<const void>(CVV));
testOk1(CIV.dataPtr().get()==CVV2.dataPtr().get());
testOk1(CIV.size()*sizeof(int)==CVV2.size());
CVV2.slice(sizeof(int), 2*sizeof(int));
CIV = epics::pvData::static_shared_vector_cast<const int32>(CVV2);
CIV = pvd::static_shared_vector_cast<const pvd::int32>(CVV2);
testOk1(CIV.dataOffset()==1);
testOk1(CIV.size()==2);
epics::pvData::shared_vector<void> VV;
pvd::shared_vector<void> VV;
// not possible to thaw() void as shared_vector<void> has no make_unique()
//VV = thaw(CVV);
CVV = freeze(VV);
@@ -408,8 +407,8 @@ void testNonPOD()
{
testDiag("Test vector of non-POD types");
epics::pvData::shared_vector<string> strings(6);
epics::pvData::shared_vector<std::tr1::shared_ptr<dummyStruct> > structs(5);
pvd::shared_vector<std::string> strings(6);
pvd::shared_vector<std::tr1::shared_ptr<dummyStruct> > structs(5);
testOk1(strings[0].empty());
testOk1(structs[0].get()==NULL);
@@ -417,7 +416,7 @@ void testNonPOD()
structs[1].reset(new dummyStruct);
dummyStruct *temp = structs[1].get();
epics::pvData::shared_vector<std::tr1::shared_ptr<dummyStruct> > structs2(structs);
pvd::shared_vector<std::tr1::shared_ptr<dummyStruct> > structs2(structs);
testOk1(!structs.unique());
testOk1(structs[1].unique());
@@ -436,22 +435,22 @@ void testVectorConvert()
{
testDiag("Test shared_vector_convert");
epics::pvData::shared_vector<int32> ints(6, 42), moreints;
epics::pvData::shared_vector<float> floats;
epics::pvData::shared_vector<string> strings;
epics::pvData::shared_vector<void> voids;
pvd::shared_vector<pvd::int32> ints(6, 42), moreints;
pvd::shared_vector<float> floats;
pvd::shared_vector<std::string> strings;
pvd::shared_vector<void> voids;
testOk1(ints.unique());
// no-op convert. Just returns another reference
moreints = epics::pvData::shared_vector_convert<int32>(ints);
moreints = pvd::shared_vector_convert<pvd::int32>(ints);
testOk1(!ints.unique());
moreints.clear();
// conversion when both types are known.
// returns a new vector
floats = epics::pvData::shared_vector_convert<float>(ints);
floats = pvd::shared_vector_convert<float>(ints);
testOk1(ints.unique());
testOk1(floats.size()==ints.size());
@@ -459,16 +458,16 @@ void testVectorConvert()
// convert to void is static_shared_vector_cast<void>()
// returns a reference
voids = epics::pvData::shared_vector_convert<void>(ints);
voids = pvd::shared_vector_convert<void>(ints);
testOk1(!ints.unique());
testOk1(voids.size()==ints.size()*sizeof(int32));
testOk1(voids.size()==ints.size()*sizeof(pvd::int32));
// convert from void uses shared_vector<void>::original_type()
// to find that the actual type is 'int32'.
// returns a new vector
testOk1(voids.original_type()==epics::pvData::pvInt);
strings = epics::pvData::shared_vector_convert<string>(voids);
testOk1(voids.original_type()==pvd::pvInt);
strings = pvd::shared_vector_convert<std::string>(voids);
voids.clear();
@@ -481,11 +480,11 @@ void testWeak()
{
testDiag("Test weak_ptr counting");
epics::pvData::shared_vector<int32> data(6);
pvd::shared_vector<pvd::int32> data(6);
testOk1(data.unique());
std::tr1::shared_ptr<int32> pdata(data.dataPtr());
std::tr1::shared_ptr<pvd::int32> pdata(data.dataPtr());
testOk1(!data.unique());
@@ -493,7 +492,7 @@ void testWeak()
testOk1(data.unique());
std::tr1::weak_ptr<int32> wdata(data.dataPtr());
std::tr1::weak_ptr<pvd::int32> wdata(data.dataPtr());
testOk1(data.unique()); // True, but I wish it wasn't!!!
@@ -506,16 +505,16 @@ void testICE()
{
testDiag("Test freeze and thaw");
epics::pvData::shared_vector<int32> A(6, 42), C;
epics::pvData::shared_vector<const int32> B, D;
pvd::shared_vector<pvd::int32> A(6, 42), C;
pvd::shared_vector<const pvd::int32> B, D;
int32 *check = A.data();
pvd::int32 *check = A.data();
// check freeze w/ unique reference
// clears A and moves reference to B
// no copy
B = epics::pvData::freeze(A);
B = pvd::freeze(A);
testOk1(A.unique());
testOk1(B.unique());
@@ -528,7 +527,7 @@ void testICE()
// clears D, but reference to B
// remains, so a copy is made
C = epics::pvData::thaw(D);
C = pvd::thaw(D);
testOk1(B.unique());
testOk1(C.unique());
@@ -542,7 +541,7 @@ void testICE()
// clears B and moves reference to A
// no copy
A = epics::pvData::thaw(B);
A = pvd::thaw(B);
testOk1(A.unique());
testOk1(B.unique());
@@ -559,7 +558,7 @@ void testICE()
// would clear A, but remaining reference C
// fails operation. A not cleared
// and exception thrown
B = epics::pvData::freeze(A);
B = pvd::freeze(A);
testFail("Froze non-unique vector!");
} catch(std::runtime_error& e) {
testPass("freeze of non-unique throws runtime_error as expected");
@@ -568,12 +567,12 @@ void testICE()
void testBad()
{
epics::pvData::shared_vector<int> I;
epics::pvData::shared_vector<const int> CI;
epics::pvData::shared_vector<float> F;
epics::pvData::shared_vector<const float> CF;
epics::pvData::shared_vector<void> V;
epics::pvData::shared_vector<const void> CV;
pvd::shared_vector<int> I;
pvd::shared_vector<const int> CI;
pvd::shared_vector<float> F;
pvd::shared_vector<const float> CF;
pvd::shared_vector<void> V;
pvd::shared_vector<const void> CV;
(void)I;
(void)CI;
(void)F;
@@ -588,38 +587,38 @@ void testBad()
// No copy from const to non-const
//CI = I;
//I = CI;
//epics::pvData::shared_vector<const int> CI2(I);
//epics::pvData::shared_vector<int> I2(CI);
//pvd::shared_vector<const int> CI2(I);
//pvd::shared_vector<int> I2(CI);
// shared_vector_convert can't thaw()
//I = epics::pvData::shared_vector_convert<int>(CI);
//V = epics::pvData::shared_vector_convert<void>(CV);
//I = pvd::shared_vector_convert<int>(CI);
//V = pvd::shared_vector_convert<void>(CV);
// shared_vector_convert can't freeze()
//CI = epics::pvData::shared_vector_convert<const int>(I);
//CV = epics::pvData::shared_vector_convert<const void>(V);
//CI = pvd::shared_vector_convert<const int>(I);
//CV = pvd::shared_vector_convert<const void>(V);
// static_shared_vector_cast can't thaw()
//I = epics::pvData::static_shared_vector_cast<int>(CI);
//V = epics::pvData::static_shared_vector_cast<void>(CV);
//I = pvd::static_shared_vector_cast<int>(CI);
//V = pvd::static_shared_vector_cast<void>(CV);
// static_shared_vector_cast can't freeze()
//CI = epics::pvData::static_shared_vector_cast<const int>(I);
//CV = epics::pvData::static_shared_vector_cast<const void>(V);
//CI = pvd::static_shared_vector_cast<const int>(I);
//CV = pvd::static_shared_vector_cast<const void>(V);
// freeze() can't change type.
// the error here will be with the assignment
//I = epics::pvData::freeze(CV);
//I = epics::pvData::freeze(CF);
//CI = epics::pvData::freeze(V);
//CI = epics::pvData::freeze(F);
//I = pvd::freeze(CV);
//I = pvd::freeze(CF);
//CI = pvd::freeze(V);
//CI = pvd::freeze(F);
// that() can't change type.
// the error here will be with the assignment
//CI = epics::pvData::thaw(V);
//CI = epics::pvData::thaw(F);
//I = epics::pvData::thaw(CV);
//I = epics::pvData::that(CF);
//CI = pvd::thaw(V);
//CI = pvd::thaw(F);
//I = pvd::thaw(CV);
//I = pvd::that(CF);
}
void testAutoSwap()
@@ -636,8 +635,8 @@ void testCXX11Move()
{
#if __cplusplus>=201103L
testDiag("Check std::move()");
shared_vector<int32> A(4, 42),
B(std::move(A));
pvd::shared_vector<pvd::int32> A(4, 42),
B(std::move(A));
testOk1(A.unique());
testOk1(B.unique());
@@ -653,8 +652,8 @@ void testCXX11Move()
testOk1(A.size()==4);
testOk1(!A.empty() && A[0]==42);
shared_vector<void> C(shared_vector_convert<void>(A)),
D(std::move(C));
pvd::shared_vector<void> C(pvd::shared_vector_convert<void>(A)),
D(std::move(C));
A.clear();
testOk1(C.unique());
@@ -678,11 +677,11 @@ void testCXX11Init()
#if __cplusplus>=201103L
testDiag("Check c++11 style array initialization");
shared_vector<const int32> A = {1.0, 2.0, 3.0};
pvd::shared_vector<const pvd::int32> A = {1.0, 2.0, 3.0};
testOk1(A.size()==3);
int32 sum = 0;
pvd::int32 sum = 0;
for (auto V: A) {
sum += V;
}
@@ -699,8 +698,8 @@ MAIN(testSharedVector)
testPlan(191);
testDiag("Tests for shared_vector");
testDiag("sizeof(shared_vector<int32>)=%lu",
(unsigned long)sizeof(epics::pvData::shared_vector<int32>));
testDiag("sizeof(shared_vector<pvd::int32>)=%lu",
(unsigned long)sizeof(pvd::shared_vector<pvd::int32>));
testEmpty();
testInternalAlloc();

View File

@@ -4,10 +4,6 @@
*/
/* Author: Michael Davidsaver */
#if defined(_WIN32) && !defined(NOMINMAX)
#define NOMINMAX
#endif
#include <fstream>
#include <iostream>
#include <algorithm>

View File

@@ -0,0 +1,368 @@
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
#include <sstream>
#include <vector>
#include <testMain.h>
#include <epicsString.h>
#include <pv/pvUnitTest.h>
#include <pv/current_function.h>
#include <pv/bitSet.h>
#include <pv/pvData.h>
#include <pv/standardField.h>
#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1)
# define USE_JSON
#endif
namespace pvd = epics::pvData;
typedef std::vector<std::string> lines_t;
namespace {
struct SB {
std::ostringstream strm;
operator std::string() { return strm.str(); }
template<typename T>
SB& operator<<(const T& v) {
strm<<v;
return *this;
}
};
lines_t lines(const std::string& str)
{
lines_t ret;
size_t p = 0;
while(true) {
size_t next = str.find_first_of('\n', p);
ret.push_back(str.substr(p, next-p)); // exclude trailing '\n'
if(next==str.npos)
break;
else
p = next+1;
}
return ret;
}
std::string print(const pvd::PVStructure::Formatter& fmt)
{
std::ostringstream strm;
strm<<fmt;
return strm.str();
}
struct point {
size_t L, R;
};
// really primative diff
// expect -> actual
::detail::testPassx
testDiff(const std::string& expect, const std::string& actual, const std::string& msg = std::string())
{
bool match = expect==actual;
::detail::testPassx ret(match);
ret<<msg<<'\n';
lines_t lhs(lines(expect)), rhs(lines(actual));
size_t L=0, R=0;
while(L<lhs.size() && R<rhs.size()) {
if(lhs[L]==rhs[R]) {
ret<<" "<<pvd::escape(lhs[L])<<'\n';
L++;
R++;
} else {
// ugly... diagonalization hardcoded...
static const point search[] = {
{1,0},
{0,1},
{1,1},
{2,1},
{1,2},
{2,2},
};
size_t Lp, Rp;
for(size_t n=0, N=sizeof(search)/sizeof(search[0]); n<N; n++) {
Lp = L+search[n].L;
Rp = R+search[n].R;
if(Lp<lhs.size() && Rp<rhs.size() && lhs[Lp]==rhs[Rp])
break;
}
if(Lp>=lhs.size() || Rp>=rhs.size()) {
// reached end without match
Lp = lhs.size();
Rp = rhs.size();
}
for(size_t l=L; l<Lp; l++)
ret<<"- "<<pvd::escape(lhs[l])<<'\n';
for(size_t r=R; r<Rp; r++)
ret<<"+ "<<pvd::escape(rhs[r])<<'\n';
assert(Lp>L); // must make progress
assert(Rp>R);
L = Lp;
R = Rp;
// loop around and print matching line
}
}
for(; L<lhs.size(); L++)
ret<<"- "<<pvd::escape(lhs[L])<<'\n';
for(; R<rhs.size(); R++)
ret<<"+ "<<pvd::escape(rhs[R])<<'\n';
return ret;
}
static const pvd::StructureConstPtr scalarNumeric(pvd::getFieldCreate()->createFieldBuilder()
->setId("epics:nt/NTScalar:1.0")
->add("value", pvd::pvInt)
->add("alarm", pvd::getStandardField()->alarm())
->add("timeStamp", pvd::getStandardField()->timeStamp())
->createStructure());
void showNTScalarNumeric()
{
testDiag("%s", CURRENT_FUNCTION);
pvd::PVStructurePtr input(pvd::getPVDataCreate()->createPVStructure(scalarNumeric));
input->getSubFieldT<pvd::PVScalar>("value")->putFrom(-42);
testDiff("<undefined> -42 \n", print(input->stream()));
input->getSubFieldT<pvd::PVScalar>("alarm.severity")->putFrom(1);
input->getSubFieldT<pvd::PVScalar>("alarm.status")->putFrom(1);
input->getSubFieldT<pvd::PVString>("alarm.message")->put("FOO");
testDiff("<undefined> -42 MINOR DEVICE FOO \n", print(input->stream()));
}
static const pvd::StructureConstPtr scalarString(pvd::getFieldCreate()->createFieldBuilder()
->setId("epics:nt/NTScalar:1.0")
->add("value", pvd::pvString)
->add("alarm", pvd::getStandardField()->alarm())
->add("timeStamp", pvd::getStandardField()->timeStamp())
->createStructure());
void showNTScalarString()
{
testDiag("%s", CURRENT_FUNCTION);
pvd::PVStructurePtr input(pvd::getPVDataCreate()->createPVStructure(scalarString));
testDiff("<undefined> \n", print(input->stream()));
input->getSubFieldT<pvd::PVString>("value")->put("bar");
testDiff("<undefined> bar \n", print(input->stream()));
input->getSubFieldT<pvd::PVScalar>("alarm.severity")->putFrom(1);
input->getSubFieldT<pvd::PVScalar>("alarm.status")->putFrom(1);
input->getSubFieldT<pvd::PVString>("alarm.message")->put("FOO");
testDiff("<undefined> bar MINOR DEVICE FOO \n", print(input->stream()));
}
static const pvd::StructureConstPtr ntenum(pvd::getFieldCreate()->createFieldBuilder()
->setId("epics:nt/NTEnum:1.0")
->addNestedStructure("value")
->setId("enum_t")
->add("index", pvd::pvInt)
->addArray("choices", pvd::pvString)
->endNested()
->add("alarm", pvd::getStandardField()->alarm())
->add("timeStamp", pvd::getStandardField()->timeStamp())
->createStructure());
void showNTEnum()
{
testDiag("%s", CURRENT_FUNCTION);
pvd::PVStructurePtr input(pvd::getPVDataCreate()->createPVStructure(ntenum));
testDiff("<undefined> (0) <undefined>\n", print(input->stream()), "empty");
pvd::PVStringArray::svector sarr;
sarr.push_back("one");
sarr.push_back("a two");
input->getSubFieldT<pvd::PVStringArray>("value.choices")->replace(pvd::freeze(sarr));
input->getSubFieldT<pvd::PVInt>("value.index")->put(0);
testDiff("<undefined> (0) one\n", print(input->stream()), "one");
input->getSubFieldT<pvd::PVInt>("value.index")->put(1);
testDiff("<undefined> (1) a two\n", print(input->stream()), "two");
testDiff("epics:nt/NTEnum:1.0 \n"
" enum_t value (1) a two\n"
" int index 1\n"
" string[] choices [\"one\", \"a two\"]\n"
" alarm_t alarm \n"
" int severity 0\n"
" int status 0\n"
" string message \n"
" time_t timeStamp <undefined> \n"
" long secondsPastEpoch 0\n"
" int nanoseconds 0\n"
" int userTag 0\n",
print(input->stream().format(pvd::PVStructure::Formatter::Raw)), "two raw");
}
static const pvd::StructureConstPtr table(pvd::getFieldCreate()->createFieldBuilder()
->setId("epics:nt/NTTable:1.0")
->addArray("labels", pvd::pvString)
->addNestedStructure("value")
->addArray("colA", pvd::pvInt)
->addArray("colB", pvd::pvString)
->endNested()
->add("alarm", pvd::getStandardField()->alarm())
->add("timeStamp", pvd::getStandardField()->timeStamp())
->createStructure());
void showNTTable()
{
testDiag("%s", CURRENT_FUNCTION);
pvd::PVStructurePtr input(pvd::getPVDataCreate()->createPVStructure(table));
testDiff("<undefined> \n"
"colA colB\n"
, print(input->stream()),
"empty table");
pvd::PVStringArray::svector sarr;
sarr.push_back("labelA");
sarr.push_back("label B");
input->getSubFieldT<pvd::PVStringArray>("labels")->replace(pvd::freeze(sarr));
pvd::PVIntArray::svector iarr;
iarr.push_back(1);
iarr.push_back(2);
iarr.push_back(3);
iarr.push_back(42); // will not be shown
input->getSubFieldT<pvd::PVIntArray>("value.colA")->replace(pvd::freeze(iarr));
sarr.push_back("one\x7f");
sarr.push_back("two words");
sarr.push_back("A '\"'");
input->getSubFieldT<pvd::PVStringArray>("value.colB")->replace(pvd::freeze(sarr));
testDiff("<undefined> \n"
"labelA \"label B\"\n"
" 1 one\\x7F\n"
" 2 \"two words\"\n"
" 3 \"A \\'\"\"\\'\"\n"
, print(input->stream()),
"with data");
}
static const pvd::StructureConstPtr everything(pvd::getFieldCreate()->createFieldBuilder()
->setId("omg")
->add("scalar", pvd::pvString)
->addArray("scalarArray", pvd::pvString)
->addNestedStructure("below")
->add("A", pvd::pvInt)
->addNestedUnion("select")
->add("one", pvd::pvInt)
->add("two", pvd::pvInt)
->endNested()
->addNestedUnionArray("arrselect")
->add("foo", pvd::pvInt)
->add("bar", pvd::pvInt)
->endNested()
->addNestedStructureArray("astruct")
->add("red", pvd::pvInt)
->add("blue", pvd::pvInt)
->endNested()
->endNested()
->add("anything", pvd::getFieldCreate()->createVariantUnion())
->add("arrayany", pvd::getFieldCreate()->createVariantUnionArray())
->createStructure());
void testRaw()
{
testDiag("%s", CURRENT_FUNCTION);
pvd::PVStructurePtr input(pvd::getPVDataCreate()->createPVStructure(everything));
{
pvd::PVStringArray::svector temp;
temp.push_back("hello");
temp.push_back("world\x7f");
input->getSubFieldT<pvd::PVStringArray>("scalarArray")->replace(pvd::freeze(temp));
}
testDiff("omg \n"
" string scalar \n" // bit 1
" string[] scalarArray [\"hello\", \"world\\x7F\"]\n"
" structure below\n"
" int A 0\n" // bit 4
" union select\n"
" (none)\n"
" union[] arrselect\n"
" structure[] astruct\n"
" any anything\n"
" (none)\n"
" any[] arrayany\n"
, print(input->stream()));
testDiff("omg \n"
" string scalar \n"
" structure below\n"
" int A 0\n"
, print(input->stream().show(pvd::BitSet().set(1).set(4))));
testDiff("omg \n"
"\033[1m string scalar \n"
"\033[0m\033[1m string[] scalarArray [\"hello\", \"world\\x7F\"]\n"
"\033[0m structure below\n"
"\033[1m int A 0\n"
"\033[0m union select\n"
" (none)\n"
" union[] arrselect\n"
" structure[] astruct\n"
" any anything\n"
" (none)\n"
" any[] arrayany\n"
, print(input->stream()
.mode(pvd::PVStructure::Formatter::ANSI) // force use of escapes
.highlight(pvd::BitSet().set(1).set(2).set(4))
));
}
void testEscape()
{
testDiag("%s", CURRENT_FUNCTION);
testEqual("hello world", std::string(SB()<<pvd::escape("hello world")));
testEqual("hello\\nworld", std::string(SB()<<pvd::escape("hello\nworld")));
testEqual("hello\\\"world", std::string(SB()<<pvd::escape("hello\"world")));
testEqual("hello\\x7Fworld", std::string(SB()<<pvd::escape("hello\x7Fworld")));
testEqual("hello\"\"world", std::string(SB()<<pvd::escape("hello\"world").style(pvd::escape::CSV)));
testEqual("hello\"\"world", pvd::escape("hello\"world").style(pvd::escape::CSV).str());
}
} // namespace
MAIN(testprinter)
{
testPlan(20);
showNTScalarNumeric();
showNTScalarString();
showNTEnum();
showNTTable();
testRaw();
testEscape();
return testDone();
}

View File

@@ -65,3 +65,7 @@ TESTS += testFieldBuilder
TESTPROD_HOST += testValueBuilder
testValueBuilder_SRCS += testValueBuilder.cpp
TESTS += testValueBuilder
TESTPROD_Linux += performstruct
performstruct_SRCS += performstruct.cpp
performstruct_SYS_LIBS_Linux += rt

View File

@@ -0,0 +1,115 @@
// Attempt to qualtify the effects of de-duplication on the time need to allocate a PVStructure
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <math.h>
#include <testMain.h>
#include <epicsUnitTest.h>
#include <pv/current_function.h>
#include <pv/pvData.h>
#include <pv/standardField.h>
namespace {
namespace pvd = epics::pvData;
struct TimeIt {
struct timespec m_start;
double sum, sum2;
size_t count;
TimeIt() { reset(); }
void reset() {
sum = sum2 = 0.0;
count = 0;
}
void start() {
clock_gettime(CLOCK_MONOTONIC, &m_start);
}
void end() {
struct timespec end;
clock_gettime(CLOCK_MONOTONIC, &end);
double diff = (end.tv_sec-m_start.tv_sec) + (end.tv_nsec-m_start.tv_nsec)*1e-9;
sum += diff;
sum2 += diff*diff;
count++;
}
void report(const char *unit ="s", double mult=1.0) const {
double mean = sum/count;
double mean2 = sum2/count;
double std = sqrt(mean2 - mean*mean);
printf("# %zu sample %f +- %f %s\n", count, mean/mult, std/mult, unit);
}
};
void buildMiss()
{
testDiag("%s", CURRENT_FUNCTION);
TimeIt record;
pvd::FieldCreatePtr create(pvd::getFieldCreate());
pvd::StandardFieldPtr standard(pvd::getStandardField());
for(size_t i=0; i<1000; i++) {
// unique name each time to (partially) defeat caching
char buf[10];
sprintf(buf, "field%zu", i);
record.start();
pvd::FieldConstPtr fld(create->createFieldBuilder()
->setId(buf)
->add("value", pvd::pvInt)
->addNestedStructure(buf)
->add("value", pvd::pvString)
->endNested()
->add("display", standard->display())
->createStructure());
record.end();
}
record.report("us", 1e-6);
}
void buildHit()
{
testDiag("%s", CURRENT_FUNCTION);
TimeIt record;
pvd::FieldCreatePtr create(pvd::getFieldCreate());
pvd::StandardFieldPtr standard(pvd::getStandardField());
pvd::FieldConstPtr fld(create->createFieldBuilder()
->add("value", pvd::pvInt)
->addNestedStructure("foo")
->add("field", pvd::pvString)
->endNested()
->add("display", standard->display())
->createStructure());
for(size_t i=0; i<1000; i++) {
record.start();
pvd::FieldConstPtr fld(create->createFieldBuilder()
->add("value", pvd::pvInt)
->addNestedStructure("foo")
->add("field", pvd::pvString)
->endNested()
->add("display", standard->display())
->createStructure());
record.end();
}
record.report("us", 1e-6);
}
} // namespace
MAIN(performStruct) {
testPlan(0);
buildMiss();
buildHit();
return testDone();
}

View File

@@ -324,8 +324,8 @@ void test_extendStructure()
<<"amended: "<<amended
<<"expected: "<<expected;
testNotEqual(static_cast<const void*>(amended.get()),
static_cast<const void*>(expected.get()));
testEqual(static_cast<const void*>(amended.get()),
static_cast<const void*>(expected.get()));
testEqual(*amended, *expected);
testThrows(std::runtime_error,

View File

@@ -384,7 +384,7 @@ static void testRequest()
StructureConstPtr topStructure = fieldCreate->createStructure(
topNames,topFields);
cout << *topStructure << endl;
PVStructurePtr pvTop = pvDataCreate->createPVStructure(topStructure);
PVStructurePtr pvTop = topStructure->build();
cout << *pvTop << endl;
cout << *pvTop->getStructure() << endl;
PVStructurePtr xxx = pvTop->getSubField<PVStructure>("record");
@@ -522,7 +522,7 @@ static void testFieldAccess()
endNested()->
createStructure();
PVStructurePtr fld = pvDataCreate->createPVStructure(tdef);
PVStructurePtr fld = tdef->build();
PVIntPtr a = fld->getSubField<PVInt>("test");
testOk1(a.get() != NULL);
@@ -628,11 +628,11 @@ static void testFieldAccess()
static void testAnyScalar()
{
PVStructurePtr value(getPVDataCreate()->createPVStructure(getFieldCreate()->createFieldBuilder()
->add("a", pvInt)
->add("b", pvDouble)
->add("c", pvString)
->createStructure()));
PVStructurePtr value(FieldBuilder::begin()
->add("a", pvInt)
->add("b", pvDouble)
->add("c", pvString)
->createStructure()->build());
PVIntPtr a(value->getSubFieldT<PVInt>("a"));
PVDoublePtr b(value->getSubFieldT<PVDouble>("b"));

View File

@@ -38,7 +38,7 @@ static void testBasic()
StructureArrayConstPtr alarmtype(
fieldCreate->createStructureArray(standardField->alarm()));
PVStructureArrayPtr alarmarr(pvDataCreate->createPVStructureArray(alarmtype));
PVStructureArrayPtr alarmarr(alarmtype->build());
testOk1(alarmarr->getLength()==0);
@@ -69,7 +69,7 @@ static void testCompress()
StructureArrayConstPtr alarmtype(
fieldCreate->createStructureArray(standardField->alarm()));
PVStructureArrayPtr alarmarr(pvDataCreate->createPVStructureArray(alarmtype));
PVStructureArrayPtr alarmarr(alarmtype->build());
alarmarr->setLength(5);
@@ -85,10 +85,10 @@ static void testCompress()
PVStructureArray::svector contents(10);
contents[2] = pvDataCreate->createPVStructure(standardField->alarm());
contents[4] = pvDataCreate->createPVStructure(standardField->alarm());
contents[5] = pvDataCreate->createPVStructure(standardField->alarm());
contents[8] = pvDataCreate->createPVStructure(standardField->alarm());
contents[2] = standardField->alarm()->build();
contents[4] = standardField->alarm()->build();
contents[5] = standardField->alarm()->build();
contents[8] = standardField->alarm()->build();
PVStructureArray::const_svector scont(freeze(contents));
@@ -117,11 +117,11 @@ static void testRemove()
PVStructureArray::svector contents(10);
for(size_t i=0; i<contents.size(); i++)
contents[i] = pvDataCreate->createPVStructure(standardField->alarm());
contents[i] = standardField->alarm()->build();
StructureArrayConstPtr alarmtype(
fieldCreate->createStructureArray(standardField->alarm()));
PVStructureArrayPtr alarmarr(pvDataCreate->createPVStructureArray(alarmtype));
PVStructureArrayPtr alarmarr(alarmtype->build());
PVStructureArray::const_svector scont(freeze(contents));
@@ -147,10 +147,10 @@ static void testFromRaw()
testDiag("Test structure array external allocation for shared_vector");
PVStructurePtr* raw = new PVStructurePtr[4];
raw[0] = pvDataCreate->createPVStructure(standardField->alarm());
raw[1] = pvDataCreate->createPVStructure(standardField->alarm());
raw[2] = pvDataCreate->createPVStructure(standardField->alarm());
raw[3] = pvDataCreate->createPVStructure(standardField->alarm());
raw[0] = standardField->alarm()->build();
raw[1] = standardField->alarm()->build();
raw[2] = standardField->alarm()->build();
raw[3] = standardField->alarm()->build();
PVStructureArray::svector cont(raw, 1, 2);
}

View File

@@ -15,6 +15,7 @@
#include <epicsUnitTest.h>
#include <testMain.h>
#include <pv/pvUnitTest.h>
#include <pv/pvIntrospect.h>
#include <pv/pvData.h>
#include <pv/convert.h>
@@ -29,9 +30,7 @@ static StandardFieldPtr standardField = getStandardField();
static void print(const string& name, FieldConstPtr const & f)
{
std::ostringstream strm;
strm << std::endl << name << std::endl << f << std::endl;
testDiag("%s", strm.str().c_str());
testShow()<<name<<'\n'<<format::indent_level(1)<<f;
}
MAIN(testStandardField)