11 Commits
7.0.0 ... 6.0.1

Author SHA1 Message Date
Andrew Johnson
e0367a2dd7 Update release notes and release date. 2016-09-14 17:01:51 -05:00
Andrew Johnson
1e0f1119e2 testTypeCast: mangle values to be printable
print  (u)int8 as integer instead of character

(cherry picked from commit da8ba56dd1)
2016-09-14 16:43:53 -05:00
Andrew Johnson
6d6314a924 #include <algorithm> required for MSVS 2015
(cherry picked from commit 4555f69733)
2016-09-14 16:33:37 -05:00
Dave Hickin
e0b881d036 Correction to documentation for 6.0.1 2016-09-14 11:25:30 +01:00
Dave Hickin
d24ff6c3d2 Update documentation for 6.0.1 2016-09-13 22:44:11 +01:00
Dave Hickin
ef17aac046 Update release notes for 6.0.1 2016-09-13 22:33:28 +01:00
Dave Hickin
83b63a9138 Fix for MinGW
Fixes #42
2016-09-07 15:14:17 +01:00
Dave Hickin
0a5ab8625d Update documentation for 6.0.0 2016-08-01 17:25:12 +01:00
Dave Hickin
a115c9cd15 Add release notes for 6.0.0 2016-08-01 17:23:11 +01:00
Dave Hickin
6ba40803d8 Add 5.0.x release notes 2016-08-01 15:00:36 +01:00
Dave Hickin
8d6094c55a jenkins: Remove COPYRIGHT from tar 2016-07-22 16:08:27 +01:00
129 changed files with 10278 additions and 8588 deletions

View File

@@ -1,21 +0,0 @@
#!/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
if [ "$TEST" != "NO" ]
then
make tapfiles
make -s test-results
fi

View File

@@ -1,138 +0,0 @@
#!/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
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
EPICS_HOST_ARCH=`sh epics-base/startup/EpicsHostArch`
# requires wine and g++-mingw-w64-i686
if [ "$WINE" = "32" ]
then
echo "Cross mingw32"
sed -i -e '/CMPLR_PREFIX/d' epics-base/configure/os/CONFIG_SITE.linux-x86.win32-x86-mingw
cat << EOF >> epics-base/configure/os/CONFIG_SITE.linux-x86.win32-x86-mingw
CMPLR_PREFIX=i686-w64-mingw32-
EOF
cat << EOF >> epics-base/configure/CONFIG_SITE
CROSS_COMPILER_TARGET_ARCHS+=win32-x86-mingw
EOF
fi
if [ "$STATIC" = "YES" ]
then
echo "Build static libraries/executables"
cat << EOF >> epics-base/configure/CONFIG_SITE
SHARED_LIBRARIES=NO
STATIC_BUILD=YES
EOF
fi
case "$CMPLR" in
clang)
echo "Host compiler is clang"
cat << EOF >> epics-base/configure/os/CONFIG_SITE.Common.$EPICS_HOST_ARCH
GNU = NO
CMPLR_CLASS = clang
CC = clang
CCC = clang++
EOF
# hack
sed -i -e 's/CMPLR_CLASS = gcc/CMPLR_CLASS = clang/' epics-base/configure/CONFIG.gnuCommon
clang --version
;;
*)
echo "Host compiler is default"
gcc --version
;;
esac
cat <<EOF >> epics-base/configure/CONFIG_SITE
USR_CPPFLAGS += $USR_CPPFLAGS
USR_CFLAGS += $USR_CFLAGS
USR_CXXFLAGS += $USR_CXXFLAGS
EOF
# set RTEMS to eg. "4.9" or "4.10"
# requires qemu, bison, flex, texinfo, install-info
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
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
EOF
cat << EOF >> epics-base/configure/CONFIG_SITE
CROSS_COMPILER_TARGET_ARCHS+=RTEMS-pc386
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

30
.gitignore vendored
View File

@@ -1,17 +1,15 @@
/cfg/
/bin/
/lib/
/db/
/dbd/
/html/
/include/
/templates/
/configure/*.local
/configure/RELEASE.*
/configure/CONFIG_SITE.*
O.*/
/QtC-*
bin/
lib/
doc/
include/
db/
dbd/
documentation/html
documentation/*.tag
envPaths
*.orig
*.log
.*.swp
configure/*.local
configure/RELEASE.*
configure/CONFIG_SITE.*
!configure/ExampleRELEASE.local
**/O.*
QtC-*

View File

@@ -1,29 +0,0 @@
sudo: false
dist: trusty
language: c++
compiler:
- gcc
addons:
apt:
packages:
- libreadline6-dev
- libncurses5-dev
- perl
- clang
- g++-mingw-w64-i686
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=3.16
- BRBASE=3.15
- BRBASE=3.14

View File

@@ -11,7 +11,4 @@ src_DEPEND_DIRS = configure
DIRS += testApp
testApp_DEPEND_DIRS = src
DIRS += examples
examples_DEPEND_DIRS = src
include $(TOP)/configure/RULES_TOP

View File

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

View File

@@ -2,8 +2,6 @@ TOP=..
include $(TOP)/configure/CONFIG
CFG += CONFIG_PVDATA_VERSION
TARGETS = $(CONFIG_TARGETS)
CONFIGS += $(subst ../,,$(wildcard $(CONFIG_INSTALLS)))

View File

@@ -1,39 +1,25 @@
# RELEASE - Location of external support modules
# pvDataCPP RELEASE - Location of external support modules
#
# IF YOU CHANGE ANY PATHS in this file or make API changes to
# any modules it refers to, you should do a "make rebuild" in
# this application's top level directory.
# IF YOU CHANGE this file or any file it includes you must
# subsequently do a "gnumake rebuild" in the application's
# top level directory.
#
# The EPICS build process does not check dependencies against
# any files from outside the application, so it is safest to
# rebuild it completely if any modules it depends on change.
# The build process does not check dependencies against files
# that are outside this application, thus you should also do a
# "gnumake rebuild" in the top level directory after EPICS_BASE
# or any other external module pointed to below is rebuilt.
#
# Host- or target-specific settings can be given in files named
# RELEASE.$(EPICS_HOST_ARCH).Common
# RELEASE.Common.$(T_A)
# RELEASE.$(EPICS_HOST_ARCH).$(T_A)
# EPICS V4 Developers: Do not edit the locations in this file!
#
# This file is parsed by both GNUmake and an EPICS Perl script,
# so it may ONLY contain definititions of paths to other support
# modules, variable definitions that are used in module paths,
# and include statements that pull in other RELEASE files.
# Variables may be used before their values have been set.
# Build variables that are NOT used in paths should be set in
# the CONFIG_SITE file.
# Create a file RELEASE.local pointing to your PVCOMMON
# and EPICS_BASE build directories, e.g.
# PVCOMMON = /home/install/epicsV4/pvCommonCPP
# EPICS_BASE = /home/install/epics/base
# Variables and paths to dependent modules:
#MODULES = /path/to/modules
#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
# Set RULES here if you want to use build rules from elsewhere:
#RULES = $(MODULES)/build-rules
# These allow developers to override the RELEASE variable settings
# without having to modify the configure/RELEASE file itself.
-include $(TOP)/../RELEASE.local
-include $(TOP)/../RELEASE.$(EPICS_HOST_ARCH).local
-include $(TOP)/configure/RELEASE.local

View File

@@ -1,3 +0,0 @@
*.tag
*.db
html/

File diff suppressed because it is too large Load Diff

View File

@@ -1,13 +0,0 @@
all: gen
clean:
rm -rf doxygen_sqlite3.db html
gen:
doxygen
commit: gen
touch html/.nojekyll
./commit-gh.sh documentation/html/ html/.nojekyll html/*.* html/search/*.*
.PHONY: all clean gen commit

View File

@@ -0,0 +1,345 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html;" />
<meta name="keywords" content="EPICS, EPICSv4" />
<title>EPICS V4 pvData C++ Release Notes</title>
<link rel="stylesheet" type="text/css" href="../../base.css" />
<link rel="stylesheet" type="text/css" href="../../epicsv4.css" />
</head>
<body>
<h1>Release 6.0.1</h1>
<p>The changes since release 6.0.0 are:</p>
<ul>
<li>Fix "Problem building pvDataCPP for win32-x86-mingw" (issue #42)</li>
<li>In src/misc/bitSet.cpp #include <algorithm> required for MSVS 2015</li>
<li>In testApp/misc/testTypeCast.cpp print (u)int8 values as integers</li>
<li>Minor documentation updates</li>
</ul>
<h1>Release 6.0.0</h1>
<p>The main changes since release 5.0.4 are:</p>
<ul>
<li>Linux shared library version added</li>
<li>Headers have been moved into pv directories</li>
<li>Bitset functions declared const where possible</li>
<li>Bitset::swap added</li>
<li>Requester::message has default implementation</li>
<li>Serialization/deserialization helpers added</li>
<li>Non-template getSubField char* overload added</li>
<li>MonitorPlugin deprecated</li>
<li>Field name validation performed</li>
<li>Now builds for Cygwin and MinGW targets</li>
<li>Fix for debug build issue.</li>
<li>New license file replaces LICENSE and COPYRIGHT</li>
</ul>
<h2>Shared library version added</h2>
<p>Linux shared library version numbers have been added by setting SHRLIB_VERSION
(to 6.0 in this case). So shared object will be libpvData.so.6.0 instead of
libpvData.so.</p>
<h2>Headers have been moved into pv directories</h2>
<p>E.g. src/property/alarm.h -> src/property/pv/alarm.h</p>
<p>This facilitates using some IDEs such as Qt Creator.</p>
<h2>Requester::message has default implementation</h2>
<p>Requester::message is no longer pure virtual. Default implementation sends
string to std::cerr.</p>
<h2>Serialization/deserialization helpers added</h2>
<p>A helper function, serializeToVector, has been added which serializes a
Serializable object into a standard vector of UInt8s.</p>
<p>Similarly a function deserializeFromVector deserializes a standard vector into
a Deserializable object.</p>
<p>A function deserializeFromBuffer deserializes a ByteBuffer into a
Deserializable object.</p>
<h2>Field name validation performed</h2>
<p>On creating a Structure or Union the field names are now validated.</p>
<p>Valid characters for a field name are upper or lowercase letters, numbers and
underscores and intial numbers are invalid, i.e. names must be of the form
[A-Za-z<em>][A-Za-z0-9</em>]*.</p>
<h2>Now builds for Cygwin and MinGW targets</h2>
<p>Includes cross-compiling MinGW on Linux.</p>
<h1>Release 5.0.4</h1>
<p>The changes since release 5.0.3 are:</p>
<ul>
<li>Fixed bitset serialization (issue #24)</li>
<li>Fixed truncation in BitSet::or_and (issue #27)</li>
</ul>
<h2>Fixed bitset serialization (issue #24)</h2>
<p>C++ bitset serialization was not consistent with the C++ deserialization and
Java code in some instances (depending on the endianness of the serializer and
deserializer) when the number of bits was 56-63 modulo 64. C++ serialization
has been fixed.</p>
<p>Fix exposed issue in deserialization on 32-bit platforms which
has also been corrected. </p>
<h2>Fixed truncation in BitSet::or_and (issue #27)</h2>
<p>If n, n1 and n2 words are used to store the values of the bitsets bitset,
bitset1 and bitset2 respectively then max(n, min(n1,n2)) words are needed
to store bitset.or_(bitset1, bitset2).</p>
<p>Previously min(n1,n2) words were used and the result would be truncated in
some instances. This has been fixed.</p>
<h1>Release 5.0.3</h1>
<p>The only change since release 5.0.2 is:</p>
<h2>Fixed buffer overflow in PVUnion::serialize() (issue #20)</h2>
<p>A PVUnion whose stored value was null was serialized without checking
whether the buffer had sufficient capacity. This has been fixed by calling
ensureBuffer().</p>
<h1>Release 5.0.2</h1>
<p>The main changes since release 4.0.3 are:</p>
<ul>
<li>Deprecated getXXXField() methods have been removed from PVStructure</li>
<li>Convert copy methods and equals operators (re)moved</li>
<li>Convert::copyUnion now always copies between subfields.</li>
<li>New method getSubFieldT, like getSubField except it throws an exception</li>
<li>findSubField method removed from PVStructure</li>
<li>New stream operators for Field and PVField are provided</li>
<li>New template versions of Structure::getField</li>
<li>Fixes for static initialisation order issues</li>
<li>CreateRequest prevents a possible SEGFAULT</li>
</ul>
<h2>Deprecated getXXXField methods have been removed from PVStructure</h2>
<p>The following methods have been removed from PVStructure</p>
<ul>
<li>getBooleanField</li>
<li>getByteField, getShortField, getIntField, getLongField</li>
<li>getUByteField, getUShortField, getUIntField, getULongField</li>
<li>getStringField</li>
<li>getStructureField, getUnionField</li>
<li>getScalarArrayField, getStructureArrayField, getUnionArrayField</li>
</ul>
<p>Use template getSubField instead, e.g. use</p>
<pre><code>getSubField&lt; PVInt &gt;(fieldName)
</code></pre>
<p>in place of</p>
<pre><code>getIntField(fieldName)
</code></pre>
<h2>Convert copy methods and equals operators</h2>
<p>Convert copy methods where moved and replaced with methods
on PVField classes, i.e.</p>
<pre><code>PVField::copy(const PVField&amp; from)
</code></pre>
<p>Methods</p>
<pre><code>PVField::copyUnchecked(const PVField&amp; from)
</code></pre>
<p>were added to allow unchecked copies, to gain performance
where checked are not needed (anymore).</p>
<p>In addition:
- isCompatibleXXX methods were removed in favour of Field::operator==.
- equals methods were remove in favour of PVField::operator==.
- operator== methods where moved to pvIntrospect.h and pvData.h</p>
<h2>Convert::copyUnion</h2>
<p>Before this method, depending on types for to and from,
sometimes did a shallow copy, i.e. just made to shared_ptr for to
share the same data as from.
Now it always copies between the subfield of to and from.</p>
<h2>New method getSubFieldT, like getSubField except it throws an exception</h2>
<p>PVStructure has a new template member</p>
<pre><code>getSubFieldT(std::string const &amp;fieldName)
</code></pre>
<p>that is like <b>getSubField</b> except that it throws a runtime_error
instead of returning null.</p>
<h2>findSubField method removed from PVStructure</h2>
<p>This was mainly used in the implementation of getSubField. With a change to
the latter, findSubField was removed.</p>
<h2>New stream operators</h2>
<p>New steam operators are available for Field and PVField.
Before to print a Field (or any extension) or a PVField (or any extension)
it was necessary to have code like:</p>
<pre><code> void print(StructureConstPtr struc, PVStructurePtr pv)
{
if(struc) {
cout &lt;&lt; *struc &lt;&lt; endl;
} else {
cout &lt;&lt; "nullptr\n"
}
if(pv) {
cout &lt;&lt; *.struc &lt;&lt; endl;
} else {
cout &lt;&lt; "nullptr\n"
}
}
</code></pre>
<p>Now it can be done as follows:</p>
<pre><code> void print(StructureConstPtr struc, PVStructurePtr pv)
{
cout &lt;&lt; struc &lt;&lt; endl;
cout &lt;&lt; pv &lt;&lt; endl;
}
</code></pre>
<h2>New template version of Structure::getField</h2>
<p>A new template getField method has been added to Structure</p>
<p>template&lt;typename FT-&gt;
std::tr1::shared_ptr&lt; const FT-&gt; getField(std::string const &amp;fieldName) const </p>
<p>Can be used, for example, as follows:</p>
<pre><code>StructurePtr tsStruc = struc-&gt;getField&lt;Structure&gt;("timeStamp");
</code></pre>
<h2>Fixes for static initialisation order issues</h2>
<p>Certain static builds (in particular Windows builds) of applications using
pvData had issues due to PVStructure::DEFAULT_ID being used before being initialised. This has been fixed.</p>
<h2>CreateRequest change</h2>
<p>createRequest could cause a SEGFAULT if passed a bad argument.
This has been changed so the it returns a null pvStructure
and provides an error.</p>
<h1>Release 4.0.3</h1>
<p>The main changes since release 3.0.2 are:</p>
<ul>
<li>array semantics now enforce Copy On Write.</li>
<li>String no longer defined.</li>
<li>timeStamp and valueAlarm name changes</li>
<li>toString replaced by stream I/O </li>
<li>union is new type.</li>
<li>copy is new.</li>
<li>monitorPlugin is new.</li>
</ul>
<h2>New Semantics for Arrays</h2>
<p>PVScalarArray, PVStructureArray, and PVUnionArray all enforce COW (Copy On Write) Semantics.
In order to limit memory usage the storage for raw data is managed via a new shared<em>vector facility.
This allows multiple instances of array data to use the shared raw data.
COW is implemented via shared</em>vectors of const data, i. e. data that can not be modified.</p>
<h2>String no longer defined</h2>
<p>This is replaced by std::string.</p>
<h2>timeStamp and valueAlarm name changes</h2>
<p>In timeStamp nanoSeconds is changed to nanoseconds.</p>
<p>In valueAlarm hysteresis is changed to hysteresis</p>
<h2>toString replaced by stream I/O</h2>
<p>pvData.h and pvIntrospect no longer defines toString
Instead they have stream support.
pvIntrospect uses method dump and pvData uses dumpValue.
For example:</p>
<pre><code> PVDoublePtr pvValue;
String buffer;
pvValue-&gt;toString(&amp;buffer);
cout &lt;&lt; buffer &lt;&lt; endl;
buffer.clear();
pvValue-&gt;getField()-&gt;toString(&amp;buffer);
cout &lt;&lt; buffer &lt;&lt; evdl;
</code></pre>
<p>is replaced by</p>
<pre><code> PVDoublePtr pvValue;
cout &lt;&lt; *pvValue &lt;&lt; endl
cout &lt;&lt; *pvValue-&gt;getField() &lt;&lt; endl;
</code></pre>
<h2>union is a new basic type.</h2>
<p>There are two new basic types: union_t and unionArray.</p>
<p>A union is like a structure that has a single subfield.
There are two flavors:</p>
<ul>
<li><b>variant union</b> The field can have any type.</li>
<li><b>union</b> The field can any of specified set of types.</li>
</ul>
<p>The field type can be dynamically changed.</p>
<h2>copy </h2>
<p>This consists of createRequest and pvCopy.
createRequest was moved from pvAccess to here.
pvCopy is moved from pvDatabaseCPP and now depends
only on pvData, i.e. it no longer has any knowledge of PVRecord.</p>
<h2>monitorPlugin</h2>
<p>This is for is for use by code that implements pvAccess monitors.
This is prototype and is subject to debate.</p>
<h1>Release 3.0.2</h1>
<p>This was the starting point for RELEASE_NOTES</p>
</body>
</html>

View File

@@ -1,41 +1,15 @@
/**
@page release_notes Release Notes
Release 7.0 (XYZ 2017)
======================
- Removals
- Remove requester.h, monitor.h, and destroyable.h.. Migrated to the pvAccessCPP module.
- Previously deprecated monitorPlugin.h is removed.
- Remove pv/messageQueue.h and epics::pvData::MessageQueue
- Deprecate the following utility classes, to be removed in 8.0.
- epics::pvData::Queue
- epics::pvData::Executor
- epics::pvData::TimeFunction
- Additions
- Add pv/pvdVersion.h which is included by pv/pvIntrospect.h
- Add epics::pvData::createRequest() function. Alternative to epics::pvData::CreateRequest class which throws on error.
- epics::pvData::FieldBuilder allow Structure defintion to be changed/appended
- Add epics::pvData::ValueBuilder like FieldBuilder also sets initial values.
- 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
Release 6.0.1
=============
The changes since release 6.0.0 are:
* Fix "Problem building pvDataCPP for win32-x86-mingw" (issue #42)
* In src/misc/bitSet.cpp #include "algorithm" required for MSVS 2015
* In src/misc/bitSet.cpp #include <algorithm> required for MSVS 2015
* In testApp/misc/testTypeCast.cpp print (u)int8 values as integers
* Minor documentation updates
Release 6.0.0 (Aug. 2016)
=======================
Release 6.0.0
=============
The main changes since release 5.0.4 are:
@@ -75,13 +49,13 @@ string to std::cerr.
Serialization/deserialization helpers added
-------------------------------------------
A helper function, serializeToVector, has been added which serializes a
A helper function, serializeToVector, has been added which serializes a
Serializable object into a standard vector of UInt8s.
Similarly a function deserializeFromVector deserializes a standard vector into
a Deserializable object.
A function deserializeFromBuffer deserializes a ByteBuffer into a
A function deserializeFromBuffer deserializes a ByteBuffer into a
Deserializable object.
Field name validation performed
@@ -116,7 +90,7 @@ deserializer) when the number of bits was 56-63 modulo 64. C++ serialization
has been fixed.
Fix exposed issue in deserialization on 32-bit platforms which
has also been corrected.
has also been corrected.
Fixed truncation in BitSet::or_and (issue #27)
----------------------------------------------
@@ -137,38 +111,38 @@ The only change since release 5.0.2 is:
Fixed buffer overflow in PVUnion::serialize() (issue #20)
---------------------------------------------------------
A PVUnion whose stored value was null was serialized without checking
A PVUnion whose stored value was null was serialized without checking
whether the buffer had sufficient capacity. This has been fixed by calling
ensureBuffer().
Release 5.0.2 (Sep. 2015)
=========================
Release 5.0.2
=============
The main changes since release 4.0 are:
The main changes since release 4.0.3 are:
- Deprecated getXXXField() methods have been removed from PVStructure
- Convert copy methods and equals operators (re)moved
- Convert::copyUnion now always copies between subfields.
- New method getSubFieldT, like getSubField except it throws an exception
- findSubField method removed from PVStructure
- New stream operators for Field and PVField are provided
- New template versions of Structure::getField
- Fixes for static initialisation order issues
- CreateRequest prevents a possible SEGFAULT
* Deprecated getXXXField() methods have been removed from PVStructure
* Convert copy methods and equals operators (re)moved
* Convert::copyUnion now always copies between subfields.
* New method getSubFieldT, like getSubField except it throws an exception
* findSubField method removed from PVStructure
* New stream operators for Field and PVField are provided
* New template versions of Structure::getField
* Fixes for static initialisation order issues
* CreateRequest prevents a possible SEGFAULT
Deprecated getXXXField methods have been removed from PVStructure
-------------------------------------------------------------------
-----------------------------------------------------------------
The following methods have been removed from PVStructure
- getBooleanField
- getByteField, getShortField, getIntField, getLongField
- getUByteField, getUShortField, getUIntField, getULongField
- getStringField
- getStructureField, getUnionField
- getScalarArrayField, getStructureArrayField, getUnionArrayField
* getBooleanField
* getByteField, getShortField, getIntField, getLongField
* getUByteField, getUShortField, getUIntField, getULongField
* getStringField
* getStructureField, getUnionField
* getScalarArrayField, getStructureArrayField, getUnionArrayField
Use template getSubField instead, e.g. use
@@ -216,7 +190,7 @@ PVStructure has a new template member
getSubFieldT(std::string const &fieldName)
that is like `getSubField()` except that it throws a runtime_error
that is like <b>getSubField</b> except that it throws a runtime_error
instead of returning null.
@@ -262,8 +236,8 @@ New template version of Structure::getField
A new template getField method has been added to Structure
template<typename FT >
std::tr1::shared_ptr< const FT > getField(std::string const &fieldName) const
template<typename FT >
std::tr1::shared_ptr< const FT > getField(std::string const &fieldName) const
Can be used, for example, as follows:
@@ -290,13 +264,13 @@ Release 4.0.3
The main changes since release 3.0.2 are:
- array semantics now enforce Copy On Write.
- String no longer defined.
- timeStamp and valueAlarm name changes
- toString replaced by stream I/O
- union is new type.
- copy is new.
- monitorPlugin is new.
* array semantics now enforce Copy On Write.
* String no longer defined.
* timeStamp and valueAlarm name changes
* toString replaced by stream I/O
* union is new type.
* copy is new.
* monitorPlugin is new.
New Semantics for Arrays
--------
@@ -352,8 +326,8 @@ There are two new basic types: union_t and unionArray.
A union is like a structure that has a single subfield.
There are two flavors:
- *variant union* The field can have any type.
- *union* The field can any of specified set of types.
* <b>variant union</b> The field can have any type.
* <b>union</b> The field can any of specified set of types.
The field type can be dynamically changed.
@@ -374,5 +348,3 @@ This is prototype and is subject to debate.
Release 3.0.2
==========
This was the starting point for RELEASE_NOTES
*/

View File

@@ -12,3 +12,9 @@ valueAlarm
normativeTypes.html describes valueAlarm only for a value field that has type
double.
The implementation also supports all the numeric scalar types.
monitorPlugin
-------------
A debate is on-going about what semantics should be.

View File

@@ -1,45 +0,0 @@
#!/bin/sh
set -e -x
# Usage: commit-gh <sub-directory-prefix> <files...>
#
# Creates a commit containing only the files in the sub-directory provided as an argument
#
# Does not disturb the working copy or index
prefix="$1"
shift
# Commit to this branch
BRANCH=refs/heads/gh-pages
# Use the main branch description as the gh-pages commit message
MSG=`git describe --tags --always`
# Scratch space
TDIR=`mktemp -d -p $PWD`
# Automatic cleanup of scratch space
trap 'rm -rf $TDIR' INT TERM QUIT EXIT
export GIT_INDEX_FILE="$TDIR/index"
# Add listed files to a new (empty) index
git update-index --add "$@"
# Write the index into the repo, get tree hash
TREE=`git write-tree --prefix="$prefix"`
echo "TREE $TREE"
git cat-file -p $TREE
# Create a commit with our new tree
# Reference current branch head as parent (if any)
CMT=`git commit-tree -m "$MSG" $TREE`
echo "COMMIT $CMT"
git cat-file -p $CMT
# Update the branch with the new commit tree hash
git update-ref $BRANCH $CMT
echo "Done"

View File

@@ -0,0 +1,679 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
<title>EPICS pvDataCPP: copy and monitor</title>
<link rel="stylesheet" type="text/css"
href="http://epics-pvdata.sourceforge.net/base.css" />
<link rel="stylesheet" type="text/css"
href="http://epics-pvdata.sourceforge.net/epicsv4.css" />
<style type="text/css">
/*<![CDATA[*/
.about { margin-left: 3em; margin-right: 3em; font-size: .83em}
table { margin-left: auto; margin-right: auto }
.diagram { text-align: center; margin: 2.5em 0 }
span.opt { color: grey }
span.nterm { font-style:italic }
span.term { font-family:courier }
span.user { font-family:courier }
span.user:before { content:"<" }
span.user:after { content:">" }
.nonnorm { font-style:italic }
p.ed { color: #AA0000 }
span.ed { color: #AA0000 }
p.ed.priv { display: inline; }
span.ed.priv { display: inline; }
/*]]>*/</style>
<!-- Script that generates the Table of Contents -->
<script type="text/javascript"
src="http://epics-pvdata.sourceforge.net/script/tocgen.js">
</script>
</head>
<body>
<div id="toc">
<h2 class="nocount" style="page-break-before: always">Table of Contents</h2>
</div>
<div id="contents" class="contents">
<h2>support for copy and monitor</h2>
<p><b>copy</b> and <b>monitor</b> are not used in this project.
They are intended for use by pvAccess and by pvAccess servers.
They are provided with this project because the code depends only on
pvData itself.
</p>
<p>This document describes C++ specific code.
<a href="http://epics-pvdata.sourceforge.net/informative/pvRequest.html">
pvRequest.html</a>
provides a language independent overview of <b>copy</b> and <b>monitor</b>.
</p>
<p>
<b>NOTE:pvRequest.html</b> must be updated since it is based on an earlier version of pvCopy that
had knowledge of PVRecord. The C++ version was implemented in pvDatabaseCPP
and the Java version on pvIOCJava.
At present only the C++ version of the new API for pvCopy is implemented.
</p>
<p>Copy provides:
<dl>
<dt>createRequest</dt>
<dd>
The Channel create methods in pvAccess all have an argument
<b>PVStructure pvRequest</b>.<br />
Given an ascii string createRequest creates a PVStructure that provides
a pvData representation of the information from the ascii string.
It is this structure that can be passed to the channel create methods.<br />
The information in a pvRequest selects an arbitrary subset of the
fields in a top level structure that resides in the server.
In addition options can be specified. Both global and field specific
options can be specified.
</dd>
<dt>pvCopy</dt>
<dd>This is a facility used by channel providers.
It provides client specific code that manages a copy of an arbitrary
subset of the fields in a top level structure that resides in the
provider. It also allows provider access to options specified
by the client.
</dd>
</dl>
Monitor provides:
<dl>
<dt>monitor</dt>
<dd>This is support code for channel providers that implement channel
monitor. It, together with the queue facility, provides support for
monitor queues.
</dd>
<dt>monitorPlugin</dt>
<dd>This is support for implementing monitor plugins.
A monitor plugin can be developed that has no knowledge
of pvAccess but only pvData.
</dd>
</dl>
</p>
<h2>support for copy</h2>
<p><b>copy</b> provides the ability to create a structure that has
a copy of an arbitrary subset of the fields in an existing top level
structure. In addition it allows global options and field specific options.
It has two main components: <b>createRequest</b> and <b>pvCopy</b>.
Given a string createRequest creates a pvRequest, which is a PVStructure
that has the format expected by <b>pvCopy</b>.
</p>
<h3>createRequest</h3>
<p>This is mainly used by pvAccess clients. Given a request string it creates
a pvRequest structure that can be passed to the pvAccess create methods.
In turn pvAccess passes the pvRequest to a local channel provider which
then passes it to pvCopy.
</p>
<p>The definition of the public members is:</p>
<pre>
class CreateRequest {
...
static CreateRequestPtr create();
virtual PVStructurePtr createRequest(std::string const &amp;request);
std::string getMessage();
};
</pre>
<p>An example of how it is used is:</p>
<pre>
CreateRequestPtr createRequest = CreateRequest::create();
PVStructurePtr pvRequest = createRequest-&gt;createRequest(request);
if(pvRequest==NULL) {
std::string error = createRequest-&gt;getMessage();
// take some action
} else {
//success do something
}
</pre>
<h3>pvCopy</h3>
<p>The definition of the public members is:</p>
<pre>
class epicsShareClass PVCopyTraverseMasterCallback
{
...
virtual void nextMasterPVField(PVFieldPtr const &amp;pvField);
};
class class epicsShareClass PVCopy
{
...
static PVCopyPtr create(
PVStructurePtr const &amp;pvMaster,
PVStructurePtr const &amp;pvRequest,
std::string const &amp; structureName);
PVStructurePtr getPVMaster();
void traverseMaster(PVCopyTraverseMasterCallbackPtr const &amp; callback);
StructureConstPtr getStructure();
PVStructurePtr createPVStructure();
size_t getCopyOffset(PVFieldPtr const &amp;masterPVField);
size_t getCopyOffset(
PVStructurePtr const &amp;masterPVStructure,
PVFieldPtr const &amp;masterPVField);
PVFieldPtr getMasterPVField(std::size_t structureOffset);
void initCopy(
PVStructurePtr const &amp;copyPVStructure,
BitSetPtr const &amp;bitSet);
void updateCopySetBitSet(
PVStructurePtr const &amp;copyPVStructure,
BitSetPtr const &amp;bitSet);
void updateCopyFromBitSet(
PVStructurePtr const &amp;copyPVStructure,
BitSetPtr const &amp;bitSet);
void updateMaster(
PVStructurePtr const &amp;copyPVStructure,
BitSetPtr const &amp;bitSet);
PVStructurePtr getOptions(std::size_t fieldOffset);
...
};
</pre>
where
<dl>
<dt>PVCopyTraverseMasterCallback::nextMasterPVField</dt>
<dd>
<b>PVCopyTraverseMasterCallback</b> is a callback which must
be implemented by the code that uses pvCopy, normally
the channel provider. It has the single method <b>nextMasterPVField</b>
<br />
<b>nextMasterPVField</b> is called for each field in the master
as a result of a call to <b>traverseMaster</b>.
</dd>
<dt>create</dt>
<dd>
This is the method for creating a PVCopy instance.<br/>
<dl>
<dt>pvMaster</dt>
<dd>the top level structure managed by the server.</dd>
<dt>pvRequest</dt>
<dd>selects the set of subfields desired
and options for each field.</dd>
<dt>structureName</dt>
<dd>the name for the top level of any PVStructure created.
</dd>
</dl>
</dd>
<dt>getPVMaster</dt>
<dd>
Gets the top level structure from pvMaster.
</dd>
<dt>traverseMaster</dt>
<dd>
Traverse all fields of the top level structure of pvMaster.
For each field the callback is called.
</dd>
<dt>getStructure</dt>
<dd>
Get the introspection interface for a PVStructure for e copy.
</dd>
<dt>createPVStructure</dt>
<dd>Create a copy instance.
Monitors keep a queue of monitor elements.
Since each element needs a PVStructure, multiple top level structures
will be created.
</dd>
<dt>getCopyOffset</dt>
<dd>Given a field in pvMaster.
return the offset in copy for the same field.
A value of std::string::npos means that the copy does not have this field.
Two overloaded methods are provided. The first is called if
the field of master is not a structure. The second is for
subfields of a structure.
</dd>
<dt>getMasterPVField</dt>
<dd>
Given a offset in the copy get the corresponding field in pvMaster.
</dd>
<dt>initCopy</dt>
<dd>
Initialize the fields in copyPVStructure
by giving each field the value from the corresponding field in pvMaster.
bitSet will be set to show that all fields are changed.
This means that bit set will have the value <b>{0}</b>.
</dd>
<dt>updateCopySetBitSet</dt>
<dd>
Set all fields in copyPVStructure to the value of the corresponding field
in pvMaster. Each field that is changed has it's corresponding
bit set in bitSet.
</dd>
<dt>updateCopyFromBitSet</dt>
<dd>
For each set bit in bitSet set the field in copyPVStructure to the value
of the corresponding field in pvMaster.
</dd>
<dt>updateMaster</dt>
<dd>
For each set bit in bitSet set the field in pvMaster to the value
of the corresponding field in copyPVStructure.
</dd>
<dt>getOptions</dt>
<dd>
Get the options for the field at the specified offset.
A NULL is returned if no options were specified for the field.
If options were specified,PVStructurePtr is
a structure with a set of PVString subfields that specify name,value
pairs. name is the subField name and value is the subField value.
</dd>
</dl>
<h2>support for monitor</h2>
<p>This consists of two components:
<dl>
<dt>monitor</dt>
<dd>Used by code that implements pvAccess monitors.</dd>
<dt>monitorPlugin</dt>
<dd>Code that provides special semantics for monitors.</dd>
</dl>
</p>
<h3>monitor</h3>
<pre>
class MonitorElement {
MonitorElement(PVStructurePtr const &amp; pvStructurePtr);
PVStructurePtr pvStructurePtr;
BitSetPtr changedBitSet;
BitSetPtr overrunBitSet;
};
class Monitor {
virtual Status start() = 0;
virtual Status stop() = 0;
virtual MonitorElementPtr poll() = 0;
virtual void release(MonitorElementPtr const &amp; monitorElement) = 0;
};
class MonitorRequester : public virtual Requester {
virtual void monitorConnect(Status const &amp; status,
MonitorPtr const &amp; monitor, StructureConstPtr const &amp; structure) = 0;
virtual void monitorEvent(MonitorPtr const &amp; monitor) = 0;
virtual void unlisten(MonitorPtr const &amp; monitor) = 0;
};
</pre>
<h4>monitorElement</h4>
<p><b>MonitorElement</b> holds the data for one element of a monitor queue.
It has the fields:
<dl>
<dt>pvStructurePtr</dt>
<dd>A top level structure with data values at the time the monitors occurs.</dd>
<dt>changedBitSet</dt>
<dd>Shows which fields have changed since the previous monitor.</dd>
<dt>overrunBitSet</dt>
<dd>Shows which fields have changed more han once since the previous monitor.</dd>
</dl>
</p>
<h4>monitorElement queue</h4>
<p>
A queue of monitor elements must be implemented by any channel provider that implements
<b>Channel::createMonitor</b>.
For an example implementation look at pvDatabaseCPP.
It has the following:
<pre>
typedef Queue&lt;MonitorElement&gt; MonitorElementQueue;
typedef std::tr1::shared_ptr&lt;MonitorElementQueue&gt; MonitorElementQueuePtr;
class MultipleElementQueue :
public ElementQueue
{
public:
POINTER_DEFINITIONS(MultipleElementQueue);
virtual ~MultipleElementQueue(){}
MultipleElementQueue(
MonitorLocalPtr const &amp;monitorLocal,
MonitorElementQueuePtr const &amp;queue,
size_t nfields);
virtual void destroy(){}
virtual Status start();
virtual Status stop();
virtual bool dataChanged();
virtual MonitorElementPtr poll();
virtual void release(MonitorElementPtr const &amp;monitorElement);
...
};
</pre>
<h4>Monitor</h4>
<p><b>Monitor</b> must be implemented by any channel provider that implements
<b>Channel::createMonitor</b>.
Remote PVAccess also implements Monitor on the client side.
Note that each client has it's own queue that is not shared with other client.
</p>
<p>Monitor has the following methods:</p>
<dl>
<dt>start</dt>
<dd>
Start monitoring.
This will result in a an initial monitor that has the current value
of all fields.
</dd>
<dt>stop</dt>
<dd>
Stop monitoring.
</dd>
<dt>poll</dt>
<dd>
Called to get a monitor element.
If no new elements are available then a null pointer is returned.
</dd>
<dt>release</dt>
<dd>
Release the monitor element.
The caller owns the monitor element between the calls to poll and release.
</dd>
<dl>
</dl>
<h4>MonitorRequester</h4>
<p>This must be implemented by a pvAccess client.
It has the methods:</p>
<dl>
<dt>monitorConnect</dt>
<dd>
A monitor has either connected of disconnected.
</dd>
<dt>monitorEvent</dt>
<dd>
A new monitor element is available.
</dd>
<dt>unlisten</dt>
<dd>
The channel is going away. The client cam no longer access the monitor.
</dd>
</dl>
<h3>monitorPlugin</h3>
<pre>
class MonitorPlugin
{
virtual std::string const &amp; getName() = 0;
virtual bool causeMonitor(
PVFieldPtr const &amp;pvField,
PVStructurePtr const &amp;pvTop,
MonitorElementPtr const &amp;monitorElement) = 0;
virtual void monitorDone(
MonitorElementPtr const &amp;monitorElement);
virtual void startMonitoring();
virtual void stopMonitoring();
virtual void beginGroupPut();
virtual void endGroupPut();
};
class MonitorPluginCreator
{
virtual MonitorPluginPtr create(
FieldConstPtr const &amp;field,
StructureConstPtr const &amp;top,
PVStructurePtr const &amp;pvFieldOptions) = 0;
virtual std::string const &amp; getName() = 0;
}
class MonitorPluginManager
{
static MonitorPluginManagerPtr get();
bool addPlugin(
std::string const &amp;pluginName,
MonitorPluginCreatorPtr const &amp;creator);
MonitorPluginCreatorPtr findPlugin(std::string const &amp;pluginName);
void showNames();
};
</pre>
<h4>MonitorPlugin</h4>
<p><b>MonitorPlugin</b> must be implemented by the plugin implementation.
It has methods:</p>
<dl>
<dt>getName</dt>
<dd>Get the name of the plugin.</dd>
<dt>causeMonitor</dt>
<dd>
Should the value of pvField cause a monitor to be raised.
pvField and pvTop are fields in the top level structure
being monitored. monitorElement has the top level structure
for the copy</b>.
The implementation should <b>not</b> modify the fields in the structure
being monitored.
Called with pvTop locked.
</dd>
<dt>monitorDone</dt>
<dd>
Called just before monitorElement will be given to client.
The plugin can change the data values and bitSets in monitorElement.
Called with pvTop unlocked.
</dd>
<dt>startMonitoring</dt>
<dd>
Monitoring is starting.
</dd>
<dt>stopMonitoring</dt>
<dd>
Monitoring is being stopped.
</dd>
<dt>beginGroupPut</dt>
<dd>
A set of puts is starting.
Called with pvTop locked.
</dd>
<dt>endGroupPut</dt>
<dd>
The set of puts is complete.
Called with pvTop locked.
</dd>
</dl>
<h4>MonitorPluginCreator</h4>
<p><b>MonitorPluginCreator</b> must also be implemented by the plugin implementation.
It is called for each field instance that has options of the from
<b>[plugin=name...]</b> where <b>name</b> is the name of the plugin.
Note that a plugin instance will belong to a single client.
It has methods:</p>
<dl>
<dt>getName</dt>
<dd>Get the name of the plugin.</dd>
<dt>create</dt>
<dd>
Create a new plugin instance.
If the arguments are not compatible with the plugin a NULL shared pointer is
returned.<br/>
pvFieldOptions is
a structure with a set of PVString subfields that specify <b>name,value</b>
pairs. name is the subField name and value is the subField value.<br/>
Note that a plugin will below to a single client.
</dd>
<dl>
<h4>MonitorPluginManager</h4>
<p><b>MonitorPluginManager</b> has the methods:</p>
<dl>
<dt>get</dt>
<dd>
MonitorPluginManager is a singleton.
The first call to get will create the single instance.
Further calls will return the single instance.
</dd>
<dt>addPlugin</dt>
<dd>
Add a new plugin.
</dd>
<dt>findPlugin</dt>
<dd>
Find a plugin. A NULL shared pointer is returned if it has not been added.
</dd>
<dt>showNames</dt>
<dd>
Show the names of all plugins that have been added.
</dd>
</dl>
<p><b>NOTE:</b>
Should the method <b>causeMonitor</b>
have arguments <b>pvField</b> and <b>pvTop</b>
be defined so that they can not be modified.
This would be possible if the following was defined:
<pre>
typedef std::tr1::shared_ptr&lt;const PVField&gt; PVFieldConstPtr;
typedef std::tr1::shared_ptr&lt;const PVStructure&gt; PVStructureConstPtr;
</pre>
then the definition for causeMonitor could be:
<pre>
virtual bool causeMonitor(
PVFieldConstPtr const &amp;pvField,
PVStructureConstPtr const &amp;pvTop,
MonitorElementPtr const &amp;monitorElement) = 0;
</pre>
But just adding these definitions is not sufficient.
In addition all methods defined in pvDataCPP must be checked.
In particular many of the methods in <b>Convert</b> must have
their arguments modified.
Big job.
</p>
<h2>monitorPlugin example</h2>
<h3>Example Plugin Overview</h3>
<p>This section describes an example plugin that:</p>
<ul>
<li>Only raises monitors when a field changes value.<br />
If no plugin is provided
the default is to raise a monitor when a put is issued to a field.</li>
<li>Optionally a change will not raise a monitor.<br />
The change will, however,
appear if a put to another field raise a monitor.</li>
</ul>
<p>As an example assume that a channel provided by pvAccess has a top level structure
that represents a power supply.</p>
<pre>
structure powerSupply
structure alarm
structure timeStamp
structure power
double value
structure alarm
structure display
structure voltage
double value
structure alarm
structure display
structure current
double value
structure alarm
structure display
</pre>
<p>A pvAccess client wants to create a monitor on the powerSupply as follows:
The client wants a top level structure that looks like:
<pre>
structure powerSupply
structure alarm
structure timeStamp
structure power
double value
structure voltage
double value
structure current
double value
</pre>
In addition the client wants monitors to occur only when one of the monitored
fields changes value but not just because a put occured.
Also if only the timeStamp changes value then that should not cause a monitor.
</p>
<p>The example monitor plugin implements the semantics the
client wants. It can be attached to any field via the following options:
<pre>
[plugin=onChange,raiseMonitor=value]
</pre>
This plugin will trigger a monitor for the field only if the field changes
value. In addition <b>value</b> equals <b>false</b> means do not raise a monitor
for changes to this field.
But if a change to another field does cause a monitor the change to this field
will be passed to the client.
</p>
<p>
Assume that the client has already connected to the channel.
The client can then issue the commands:</p>
<pre>
std::string request("field(alarm[plugin=onChange]");
request += ",timeStamp[plugin=onChange,raiseMonitor=false]";
request += ",power.value[plugin=onChange";
request += ",voltage.value[plugin=onChange";
request += ",current.value[plugin=onChange";
PVStructurePtr pvRequest = createRequest-&gt;createRequest(request);
MonitorPtr monitor = channel-&gt;createMonitor(monitorRequester,pvRequest);
</pre>
<h3>Example Plugin Code</h3>
<p>The header file to create the example has the definition:</p>
<pre>
class ExampleMonitorPlugin{
public:
static void create();
};
</pre>
<p>The implementation is:</p>
<pre>
class OnChangePlugin : public MonitorPlugin
{
public:
virtual ~OnChangePlugin(){}
OnChangePlugin() {}
bool init(
FieldConstPtr const &amp;field,
StructureConstPtr const &amp;top,
PVStructurePtr const &amp;pvFieldOptions)
{
pvField = getPVDataCreate()-&gt;createPVField(field);
raiseMonitor = true;
if(pvFieldOptions!=NULL) {
PVStringPtr pvString =
pvFieldOptions-&gt;getSubField&lt;PVString&gt;("raiseMonitor");
if(pvString!=NULL) {
std::string value = pvString-&gt;get();
if(value.compare("false")==0) raiseMonitor = false;
}
}
return true;
}
virtual std::string &amp;getName(){return pluginName;}
virtual bool causeMonitor(
PVFieldPtr const &amp;pvNew,
PVStructurePtr const &amp;pvTop,
MonitorElementPtr const &amp;monitorElement)
{
bool isSame = convert-&gt;equals(pvNew,pvField);
if(isSame) return false;
convert-&gt;copy(pvNew,pvField);
return raiseMonitor;
}
private:
PVFieldPtr pvField;
bool raiseMonitor;
};
class OnChangePluginCreator : public MonitorPluginCreator
{
public:
virtual std::string &amp;getName(){return pluginName;}
virtual MonitorPluginPtr create(
FieldConstPtr const &amp;field,
StructureConstPtr const &amp;top,
PVStructurePtr const &amp;pvFieldOptions)
{
OnChangePluginPtr plugin(new OnChangePlugin());
bool result = plugin-&gt;init(field,top,pvFieldOptions);
if(!result) return MonitorPluginPtr();
return plugin;
}
};
void ExampleMonitorPlugin::create()
{
static OnChangePluginCreatorPtr plugin;
static Mutex mutex;
Lock xx(mutex);
if(plugin==NULL) {
plugin = OnChangePluginCreatorPtr(new OnChangePluginCreator());
MonitorPluginManager::get()-&gt;addPlugin(pluginName,plugin);
}
}
</pre>
</div>
</body>
</html>

View File

@@ -1,58 +0,0 @@
#ifndef MAINPAGE_H
#define MAINPAGE_H
/**
@mainpage pvDataCPP documentation
- [Download](https://sourceforge.net/projects/epics-pvdata/files/)
- @htmlonly <a href="modules.html">API components</a> @endhtmlonly
- @ref release_notes
The epics::pvData namespace.
See pv/pvData.h header.
@code
#include <pv/pvData.h>
#include <pv/createRequest.h>
@endcode
- Type description epics::pvData::Field and sub-classes
- Value container epics::pvData::PVField and sub-classes
- POD array handling epics::pvData::shared_vector
- pvRequest parsing epics::pvData::createRequest()
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)
->addNestedStructure("sub")
->add("fld2", epics::pvData::pvString)
->endNested()
->createStructure();
epics::pvData::PVStructuretPtr value; // aka std::tr1::shared_ptr<epics::pvData::PVStructure>
value = epics::pvData::getPVDataCreate()->createPVStructure(stype);
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"
@endcode
is equivalent to the following pseudo-code.
@code
struct stype {
epics::pvData::int32 fld1;
struct {
std::string fld2;
} sub;
};
stype value;
value.fld1 = 4;
value.fld2 = epics::pvData::castUnsafe<std::string>(4.2);
@endcode
*/
#endif /* MAINPAGE_H */

5466
documentation/pvDataCPP.html Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,179 @@
pvDataCPP cookbook
------------------
Creating introspection data interfaces
// create a scalar
getFieldCreate()->createScalar(pvDouble);
// create a scalar array
getFieldCreate()->createScalarArray(pvDouble);
// create a structure
getFieldCreate()->createFieldBuilder()->
setId("enum_t")->
add("index", pvDouble)->
addArray("choices", pvString)->
createStructure();
// create a structure (cntd.)
StructureConstPtr enum_t =
getFieldCreate()->createFieldBuilder()->
setId("enum_t")->
add("index", pvInt)->
addArray("choices", pvString)->
createStructure();
// create a structure (cntd.)
StructureConstPtr ntEnum =
getFieldCreate()->createFieldBuilder()->
setId("epics:nt/NTEnum:1.0")->
add("value", enum_t)->
addNestedStructure("timeStamp")->
setId("time_t")->
add("secondsPastEpoch", pvLong)->
add("nanoseconds", pvInt)->
add("userTag", pvInt)->
endNested()->
createStructure();
// create an union == same as structure
---
Creating data containers
// create a scalar
PVDouble::shared_pointer doubleValue = getPVDataCreate()->createPVScalar<PVDouble>();
// create a scalar array
PVDoubleArray::shared_pointer doubleArrayValue = getPVDataCreate()->createPVScalarArray<PVDouble>();
// create a structure
PVStructure::shared_pointer struct = getPVDataCreate()->createPVStructure(ntEnum);
// create an union
PVUnion::shared_pointer pvUnion = getPVDataCreate()->createPVUnion(unionIF);
// create a structure array
PVStructureArray::shared_pointer structArray = getPVDataCreate()->createPVStructureArray(ntEnum);
// scalar usage
PVInt::shared_pointer index = struct->getSubField<PVInt>("value.index");
int32 ix = index->get();
index->put(3);
std::cout << *index << std::endl;
// using <<=, >>= operators to get/set
*doubleValue <<= 12.3;
double val;
*doubleValue >>= val;
// array usage
PVStringArray::shared_pointer choices = struct->getSubField<PVStringArray>("value.choices");
// use view() to access read-only data
PVStringArray::const_svector data(choices->view());
for (std::size_t i = 0; i < data.size(); i++)
std::cout << data[i] << std::endl;
// use replace() to put new data
PVStringArray::svector newdata;
newdata.push_back("zero");
newdata.push_back("one");
newdata.push_back("two");
choices->replace(freeze(newdata));
// (add more use-cases) here
// print entire array
std::cout << *choices << std::endl;
// print elmenet at index == 1
std::cout << format::array_at(1) << *choices << std::endl;
----
Union handling
Union::const_shared_pointer punion =
getFieldCreate()->createFieldBuilder()->
add("doubleValue", pvDouble)->
add("intValue", pvInt)->
createUnion();
PVUnion::shared_pointer u = getPVDataCreate()->createPVUnion(punion);
// select and put
// this create a new instance of PVDouble (everytime select() is called)
PVDouble::shared_pointer doubleValue = u->select<PVDouble>("doubleValue");
doubeValue->put(12);
// select using index (and direct put)
u->select<PVDouble>(0)->put(12);
// select using existing PVField (PVUnion stores by-reference)
u->set("doubleValue", doubleValue);
// get selected field name or index
std::string selectedFN = u->getSelectedFieldName();
int32 selectedIndex = u->getSelectedIndex();
// get currently selected (knowing it's PVDouble)
PVDouble value = u->get<PVDouble>();
Variant Union handling
PVUnion::shared_pointer any = getPVDataCreate()->createPVVariantUnion();
PVDouble::shared_pointer doubleValue = getPVDataCreate()->createPVScalar<PVDouble>();
doubleValue->put(12.8);
any->set(doubleValue);
PVDouble::shared_pointer doubleValue2 = any->get<PVDouble>();
// variant union work by-reference (pointers match also)
// doubleValue.get() == doubleValue2.get()
------
Convert
// convert to int
int32 i = doubleValue->getAs<int32>();
// from int
doubleValue->putFrom<int32>(i);
// from string
doubleValue->putFrom<std::string>("12.3");
// from scalar field
doubleValue->assign(pvScalar);
// convert to int array
PVIntArray::const_svector intData;
doubleArrayValue->getAs<int32>(intData);
// from string array
PVStringArray::svector labels;
labels.push_back("zero");
labels.push_back("one");
labels.push_back("two");
doubleArrayValue->putFrom<std::string>(labels);
// from scalar array
doubleArrayValue->assign(pvScalarArray);

View File

@@ -1,14 +0,0 @@
# Makefile for the examples
# make sure they compile
TOP = ..
include $(TOP)/configure/CONFIG
PROD_LIBS += pvData Com
TESTPROD_HOST += unittest
unittest_SRCS += unittest.cpp
include $(TOP)/configure/RULES

View File

@@ -1,30 +0,0 @@
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
/* c++ unittest skeleton */
#include <stdexcept>
#include <testMain.h>
#include <pv/pvUnitTest.h>
#include <pv/epicsException.h>
namespace {
void testCase1() {
testEqual(1+1, 2);
}
} // namespace
MAIN(testUnitTest)
{
testPlan(1);
try {
testCase1();
}catch(std::exception& e){
PRINT_EXCEPTION(e); // print stack trace if thrown with THROW_EXCEPTION()
testAbort("Unhandled exception: %s", e.what());
}
return testDone();
}

View File

@@ -21,15 +21,23 @@ installE4 () {
local module=$1
local branch=$2
wget -nv https://openepics.ci.cloudbees.com/job/e4-cpp-${module}-${branch}-build/BASE=${BASE}/lastSuccessfulBuild/artifact/${module}.CB-dist.tar.gz
# If microbench version does not exist, try without
if [ "${MB}" = "WITH_MICROBENCH" ]; then
if ! wget -nv https://openepics.ci.cloudbees.com/job/e4-cpp-${module}-${branch}-build/BASE=${BASE},MB=WITH_MICROBENCH/lastSuccessfulBuild/artifact/${module}.CB-dist.tar.gz; then
wget -nv https://openepics.ci.cloudbees.com/job/e4-cpp-${module}-${branch}-build/BASE=${BASE},MB=NO_MICROBENCH/lastSuccessfulBuild/artifact/${module}.CB-dist.tar.gz
fi
else
wget -nv https://openepics.ci.cloudbees.com/job/e4-cpp-${module}-${branch}-build/BASE=${BASE},MB=NO_MICROBENCH/lastSuccessfulBuild/artifact/${module}.CB-dist.tar.gz
fi
tar -xzf ${module}.CB-dist.tar.gz
}
###########################################
# Defaults for EPICS Base
# Defaults for EPICS Base and MB
DEFAULT_BASE=3.15.4
BASE=${BASE:-${DEFAULT_BASE}}
MB=${MB:-"NO_MICROBENCH"}
###########################################
# Fetch and unpack dependencies

View File

@@ -11,26 +11,14 @@ include $(PVDATA_SRC)/factory/Makefile
include $(PVDATA_SRC)/property/Makefile
include $(PVDATA_SRC)/copy/Makefile
include $(PVDATA_SRC)/pvMisc/Makefile
include $(PVDATA_SRC)/json/Makefile
include $(PVDATA_SRC)/monitor/Makefile
LIBRARY = pvData
pvData_LIBS += Com
EXPANDVARS += EPICS_PVD_MAJOR_VERSION
EXPANDVARS += EPICS_PVD_MINOR_VERSION
EXPANDVARS += EPICS_PVD_MAINTENANCE_VERSION
EXPANDVARS += EPICS_PVD_DEVELOPMENT_FLAG
EXPANDFLAGS += $(foreach var,$(EXPANDVARS),-D$(var)="$(strip $($(var)))")
# shared library ABI version.
SHRLIB_VERSION = $(EPICS_PVD_MAJOR_VERSION).$(EPICS_PVD_MINOR_VERSION).$(EPICS_PVD_MAINTENANCE_VERSION)
SHRLIB_VERSION ?= 6.0
include $(TOP)/configure/RULES
# Can't use EXPAND as generated headers must appear
# in O.Common, but EXPAND emits rules for O.$(T_A)
../O.Common/pv/pvdVersionNum.h: ../pv/pvdVersionNum.h@
$(MKDIR) $(COMMON_DIR)/pv
$(EXPAND_TOOL) $(EXPANDFLAGS) $($@_EXPANDFLAGS) $< $@

View File

@@ -22,13 +22,13 @@ using std::endl;
using std::string;
using std::vector;
namespace {
using namespace epics::pvData;
namespace epics { namespace pvData {
static PVDataCreatePtr pvDataCreate = getPVDataCreate();
static FieldCreatePtr fieldCreate = getFieldCreate();
struct CreateRequestImpl {
class CreateRequestImpl : public CreateRequest {
private:
struct Node
{
@@ -54,7 +54,12 @@ struct CreateRequestImpl {
string fullFieldName;
CreateRequestImpl() {}
public:
CreateRequestImpl()
{
fullFieldName = "";
}
private:
void removeBlanks(string& str)
@@ -70,7 +75,8 @@ struct CreateRequestImpl {
size_t openBrace = request.find('{', index+1);
size_t closeBrace = request.find('}', index+1);
if(openBrace == string::npos && closeBrace == string::npos){
throw std::runtime_error(request + " mismatched {}");
message = request + " mismatched {}";
throw std::logic_error("message");
}
if (openBrace != string::npos && openBrace!=0) {
if(openBrace<closeBrace) return findMatchingBrace(request,openBrace,numOpen+1);
@@ -85,12 +91,14 @@ struct CreateRequestImpl {
for(size_t i=index+1; i< request.size(); ++i) {
if(request[i] == ']') {
if(i==index+1) {
throw std::runtime_error(request + " mismatched []");
message = request + " mismatched []";
throw std::logic_error("message");
}
return i;
}
}
throw std::runtime_error(request + " missing ]");
message = request + " missing ]";
throw std::logic_error("message");
}
size_t findEndField(string& request) {
@@ -145,7 +153,7 @@ struct CreateRequestImpl {
string const & request)
{
if(request.length()<=1) {
throw std::runtime_error("logic error empty options");
throw std::logic_error("logic error empty options");
}
vector<Node> top;
vector<string> items = split(request);
@@ -155,7 +163,8 @@ struct CreateRequestImpl {
string item = items[j];
size_t equals = item.find('=');
if(equals==string::npos || equals==0) {
throw std::runtime_error(item + " illegal option " + request);
message = item + " illegal option " + request;
throw std::logic_error("message");
}
top.push_back(Node(item.substr(0,equals)));
string name = fullFieldName + "._options." + item.substr(0,equals);
@@ -204,7 +213,7 @@ struct CreateRequestImpl {
if(end==0) end = request.size();
string name = request.substr(0,end);
if(name.size()<1) {
throw std::runtime_error("null field name " + request);
throw std::logic_error("null field name " + request);
}
string saveFullName = fullFieldName;
fullFieldName += "." + name;
@@ -227,7 +236,7 @@ struct CreateRequestImpl {
if(chr=='.') {
request = request.substr(end+1);
if(request.size()==string::npos || request.size()<1) {
throw std::runtime_error("null field name " + request);
throw std::logic_error("null field name " + request);
}
Node subNode(name);
if(optionNode.name.size()>0) subNode.nodes.push_back(optionNode);
@@ -248,11 +257,11 @@ struct CreateRequestImpl {
if(chr=='{') {
size_t endBrace = findEndField(request);
if((end+1)>=(endBrace-1)) {
throw std::runtime_error("illegal syntax " + request);
throw std::logic_error("illegal syntax " + request);
}
string subRequest = request.substr(end+1,endBrace-1 -end -1);
if(subRequest.size()<1) {
throw std::runtime_error("empty {} " + request);
throw std::logic_error("empty {} " + request);
}
Node subNode(name);
if(optionNode.name.size()>0) subNode.nodes.push_back(optionNode);
@@ -268,7 +277,7 @@ struct CreateRequestImpl {
createSubNode(node,request);
return;
}
throw std::runtime_error("logic error");
throw std::logic_error("logic error");
}
FieldConstPtr createSubStructure(vector<Node> & nodes)
@@ -309,11 +318,12 @@ struct CreateRequestImpl {
return structure;
}
public:
PVStructurePtr createRequest(
virtual PVStructurePtr createRequest(
string const & crequest)
{
{
try {
string request = crequest;
if (!request.empty()) removeBlanks(request);
if (request.empty())
@@ -347,17 +357,20 @@ struct CreateRequestImpl {
if(numParan!=0) {
ostringstream oss;
oss << "mismatched () " << numParan;
throw std::runtime_error(oss.str());
message = oss.str();
return PVStructurePtr();
}
if(numBrace!=0) {
ostringstream oss;
oss << "mismatched {} " << numBrace;
throw std::runtime_error(oss.str());
message = oss.str();
return PVStructurePtr();
}
if(numBracket!=0) {
ostringstream oss;
oss << "mismatched [] " << numBracket;
throw std::runtime_error(oss.str());
message = oss.str();
return PVStructurePtr();
}
vector<Node> top;
try {
@@ -366,8 +379,9 @@ struct CreateRequestImpl {
size_t openBracket = request.find('[', offsetRecord);
size_t closeBracket = request.find(']', openBracket);
if(closeBracket==string::npos) {
throw std::runtime_error(request.substr(offsetRecord) +
"record[ does not have matching ]");
message = request.substr(offsetRecord) +
"record[ does not have matching ]";
return PVStructurePtr();
}
if(closeBracket-openBracket > 3) {
Node node("record");
@@ -383,8 +397,9 @@ struct CreateRequestImpl {
size_t openParan = request.find('(', offsetField);
size_t closeParan = request.find(')', openParan);
if(closeParan==string::npos) {
throw std::runtime_error(request.substr(offsetField)
+ " field( does not have matching )");
message = request.substr(offsetField)
+ " field( does not have matching )";
return PVStructurePtr();
}
if(closeParan>openParan+1) {
createSubNode(node,request.substr(openParan+1,closeParan-openParan-1));
@@ -397,8 +412,9 @@ struct CreateRequestImpl {
size_t openParan = request.find('(', offsetGetField);
size_t closeParan = request.find(')', openParan);
if(closeParan==string::npos) {
throw std::runtime_error(request.substr(offsetField)
+ " getField( does not have matching )");
message = request.substr(offsetField)
+ " getField( does not have matching )";
return PVStructurePtr();
}
if(closeParan>openParan+1) {
createSubNode(node,request.substr(openParan+1,closeParan-openParan-1));
@@ -411,8 +427,9 @@ struct CreateRequestImpl {
size_t openParan = request.find('(', offsetPutField);
size_t closeParan = request.find(')', openParan);
if(closeParan==string::npos) {
throw std::runtime_error(request.substr(offsetField)
+ " putField( does not have matching )");
message = request.substr(offsetField)
+ " putField( does not have matching )";
return PVStructurePtr();
}
if(closeParan>openParan+1) {
createSubNode(node,request.substr(openParan+1,closeParan-openParan-1));
@@ -420,7 +437,9 @@ struct CreateRequestImpl {
top.push_back(node);
}
} catch (std::exception &e) {
throw std::runtime_error(std::string("while creating Structure exception ")+e.what());
string xxx = e.what();
message = "while creating Structure exception " + xxx;
return PVStructurePtr();
}
size_t num = top.size();
StringArray names(num);
@@ -448,39 +467,20 @@ struct CreateRequestImpl {
}
optionList.clear();
return pvStructure;
} catch (std::exception &e) {
message = e.what();
return PVStructurePtr();
}
}
};
} // namespace
namespace epics {namespace pvData {
CreateRequest::shared_pointer CreateRequest::create()
{
CreateRequest::shared_pointer createRequest(new CreateRequest());
CreateRequest::shared_pointer createRequest(new CreateRequestImpl());
return createRequest;
}
PVStructure::shared_pointer CreateRequest::createRequest(std::string const & request)
{
message.clear();
try {
return ::createRequest(request);
} catch(std::exception& e) {
message = e.what();
return PVStructure::shared_pointer();
}
}
PVStructure::shared_pointer createRequest(std::string const & request)
{
CreateRequestImpl I;
return I.createRequest(request);
}
}} // namespace
}}

View File

@@ -32,7 +32,7 @@ class epicsShareClass CreateRequest {
* @returns A shared pointer to the new instance.
*/
static CreateRequest::shared_pointer create();
~CreateRequest() {};
virtual ~CreateRequest() {};
/**
* Create a request structure for the create calls in Channel.
* See the package overview documentation for details.
@@ -41,7 +41,7 @@ class epicsShareClass CreateRequest {
* If a NULL PVStructure is returned then getMessage will return
* the reason.
*/
PVStructure::shared_pointer createRequest(std::string const & request);
virtual PVStructure::shared_pointer createRequest(std::string const & request) = 0;
/**
* Get the error message of createRequest returns NULL
* return the error message
@@ -52,14 +52,6 @@ protected:
std::string message;
};
/** Parse and build pvRequest structure.
*
@params request the Request string to be parsed. eg. "field(value)"
@returns The resulting strucuture. Never NULL
@throws std::exception for various parsing errors
*/
epicsShareExtern
PVStructure::shared_pointer createRequest(std::string const & request);
}}

View File

@@ -75,7 +75,7 @@ public:
PVStructurePtr const &pvRequest,
std::string const & structureName);
virtual ~PVCopy(){}
void destroy();
virtual void destroy();
/**
* Get the top-level structure of master
* @returns The master top-level structure.

View File

@@ -41,6 +41,13 @@ static void newLine(string *buffer, int indentLevel)
*buffer += string(indentLevel*4, ' ');
}
static PVCopyPtr NULLPVCopy;
static FieldConstPtr NULLField;
static StructureConstPtr NULLStructure;
static PVStructurePtr NULLPVStructure;
static CopyNodePtr NULLCopyNode;
static CopyMasterNodePtr NULLCopyMasterNode;
struct CopyNode {
CopyNode()
: isStructure(false),
@@ -73,12 +80,12 @@ PVCopyPtr PVCopy::create(
if(structureName.size()>0) {
if(pvRequest->getStructure()->getNumberFields()>0) {
pvStructure = pvRequest->getSubField<PVStructure>(structureName);
if(!pvStructure) return PVCopyPtr();
if(!pvStructure) return NULLPVCopy;
}
} else if(pvStructure->getSubField<PVStructure>("field")) {
pvStructure = pvRequest->getSubField<PVStructure>("field");
}
PVCopyPtr pvCopy(new PVCopy(pvMaster));
PVCopyPtr pvCopy = PVCopyPtr(new PVCopy(pvMaster));
bool result = pvCopy->init(pvStructure);
if(!result) pvCopy.reset();
return pvCopy;
@@ -140,7 +147,7 @@ PVStructurePtr PVCopy::getOptions(std::size_t fieldOffset)
while(true) {
if(!node->isStructure) {
if(node->structureOffset==fieldOffset) return node->options;
return PVStructurePtr();
return NULLPVStructure;
}
CopyStructureNodePtr structNode = static_pointer_cast<CopyStructureNode>(node);
CopyNodePtrArrayPtr nodes = structNode->nodes;
@@ -151,7 +158,7 @@ PVStructurePtr PVCopy::getOptions(std::size_t fieldOffset)
if(fieldOffset>=soff && fieldOffset<soff+node->nfields) {
if(fieldOffset==soff) return node->options;
if(!node->isStructure) {
return PVStructurePtr();
return NULLPVStructure;
}
okToContinue = true;
break;
@@ -385,7 +392,7 @@ StructureConstPtr PVCopy::createStructure(
PVFieldPtrArray const &pvFromRequestFields = pvFromRequest->getPVFields();
StringArray const &fromRequestFieldNames = pvFromRequest->getStructure()->getFieldNames();
size_t length = pvFromRequestFields.size();
if(length==0) return StructureConstPtr();
if(length==0) return NULLStructure;
FieldConstPtrArray fields; fields.reserve(length);
StringArray fieldNames; fields.reserve(length);
for(size_t i=0; i<length; ++i) {
@@ -415,7 +422,7 @@ StructureConstPtr PVCopy::createStructure(
fields.push_back(field);
}
size_t numsubfields = fields.size();
if(numsubfields==0) return StructureConstPtr();
if(numsubfields==0) return NULLStructure;
return getFieldCreate()->createStructure(fieldNames, fields);
}
@@ -623,7 +630,7 @@ CopyMasterNodePtr PVCopy::getCopyOffset(
if(masterNode) return masterNode;
}
}
return CopyMasterNodePtr();
return NULLCopyMasterNode;
}
CopyMasterNodePtr PVCopy::getMasterNode(
@@ -643,7 +650,7 @@ CopyMasterNodePtr PVCopy::getMasterNode(
static_pointer_cast<CopyStructureNode>(node);
return getMasterNode(subNode,structureOffset);
}
return CopyMasterNodePtr();
return NULLCopyMasterNode;
}
}}

View File

@@ -271,7 +271,7 @@ bool compareField(const PVUnion* left, const PVUnion* right)
if (ls->isVariant())
{
const PVField::const_shared_pointer& lval = left->get();
PVFieldPtr lval = left->get();
if (lval.get() == 0)
return right->get().get() == 0;
else

View File

@@ -21,12 +21,10 @@
#include <epicsMutex.h>
#define epicsExportSharedSymbols
#include <pv/reftrack.h>
#include <pv/lock.h>
#include <pv/pvIntrospect.h>
#include <pv/factory.h>
#include <pv/serializeHelper.h>
#include <pv/localStaticLock.h>
using std::tr1::static_pointer_cast;
using std::size_t;
@@ -36,17 +34,12 @@ namespace epics { namespace pvData {
static DebugLevel debugLevel = lowDebug;
size_t Field::num_instances;
Field::Field(Type type)
: m_fieldType(type)
{
REFTRACE_INCREMENT(num_instances);
}
Field::~Field() {
REFTRACE_DECREMENT(num_instances);
}
@@ -612,41 +605,6 @@ Union::Union (
Union::~Union() { }
int32 Union::guess(Type t, ScalarType s) const
{
if(t!=scalar && t!=scalarArray)
THROW_EXCEPTION2(std::logic_error, "PVUnion::guess() only support scalar and scalarArray");
int32 ret = -1;
for(size_t i=0, N=fields.size(); i<N; i++)
{
if(fields[i]->getType()!=t)
continue;
ScalarType type;
switch(fields[i]->getType()) {
case scalar:
type = static_cast<const Scalar*>(fields[i].get())->getScalarType();
break;
case scalarArray:
type = static_cast<const ScalarArray*>(fields[i].get())->getElementType();
break;
default:
continue;
}
if(type==s) {
// exact match
ret = i;
break; // we're done
} else if(ret==-1) {
// first partial match
ret = i;
}
}
return ret;
}
string Union::getID() const
{
@@ -743,97 +701,18 @@ void Union::deserialize(ByteBuffer* /*buffer*/, DeserializableControl* /*control
throw std::runtime_error("not valid operation, use FieldCreate::deserialize instead");
}
FieldBuilder::FieldBuilder()
:fieldCreate(getFieldCreate())
,idSet(false)
,nestedClassToBuild(structure)
,nestedArray(false)
,createNested(true)
{}
FieldBuilder::FieldBuilder(const Structure* S)
:fieldCreate(getFieldCreate())
,id(S->getID())
,idSet(!id.empty())
,fieldNames(S->getFieldNames())
,fields(S->getFields())
,parentBuilder()
,nestedClassToBuild(structure)
,nestedName()
,nestedArray(false)
,createNested(false)
{}
FieldBuilder::FieldBuilder(const FieldBuilderPtr & _parentBuilder,
const std::string& name,
const Structure* S)
:fieldCreate(_parentBuilder->fieldCreate)
,id(S->getID())
,idSet(!id.empty())
,fieldNames(S->getFieldNames())
,fields(S->getFields())
,parentBuilder(_parentBuilder)
,nestedClassToBuild(structure)
,nestedName(name)
,nestedArray(false)
,createNested(false)
{}
FieldBuilder::FieldBuilder(const FieldBuilderPtr & _parentBuilder,
const std::string& name,
const StructureArray* S)
:fieldCreate(getFieldCreate())
,id(S->getStructure()->getID())
,idSet(!id.empty())
,fieldNames(S->getStructure()->getFieldNames())
,fields(S->getStructure()->getFields())
,parentBuilder(_parentBuilder)
,nestedClassToBuild(structure)
,nestedName(name)
,nestedArray(true)
,createNested(false)
{}
FieldBuilder::FieldBuilder(const FieldBuilderPtr & _parentBuilder,
const std::string& name,
const Union* S)
:fieldCreate(getFieldCreate())
,id(S->getID())
,idSet(!id.empty())
,fieldNames(S->getFieldNames())
,fields(S->getFields())
,parentBuilder(_parentBuilder)
,nestedClassToBuild(union_)
,nestedName(name)
,nestedArray(false)
,createNested(false)
{}
FieldBuilder::FieldBuilder(const FieldBuilderPtr & _parentBuilder,
const std::string& name,
const UnionArray* S)
:fieldCreate(getFieldCreate())
,id(S->getUnion()->getID())
,idSet(!id.empty())
,fieldNames(S->getUnion()->getFieldNames())
,fields(S->getUnion()->getFields())
,parentBuilder(_parentBuilder)
,nestedClassToBuild(union_)
,nestedName(name)
,nestedArray(true)
,createNested(false)
{}
FieldBuilder::FieldBuilder() : fieldCreate(getFieldCreate()), idSet(false) {}
FieldBuilder::FieldBuilder(FieldBuilderPtr const & _parentBuilder,
string const & _nestedName,
Type _nestedClassToBuild, bool _nestedArray)
:fieldCreate(_parentBuilder->fieldCreate)
,idSet(false)
,parentBuilder(_parentBuilder)
,nestedClassToBuild(_nestedClassToBuild)
,nestedName(_nestedName)
,nestedArray(_nestedArray)
,createNested(true)
Type _nestedClassToBuild, bool _nestedArray) :
fieldCreate(getFieldCreate()),
idSet(false),
parentBuilder(_parentBuilder),
nestedClassToBuild(_nestedClassToBuild),
nestedName(_nestedName),
nestedArray(_nestedArray)
{}
void FieldBuilder::reset()
@@ -853,57 +732,56 @@ FieldBuilderPtr FieldBuilder::setId(string const & id)
FieldBuilderPtr FieldBuilder::add(string const & name, ScalarType scalarType)
{
return add(name, fieldCreate->createScalar(scalarType));
fields.push_back(fieldCreate->createScalar(scalarType)); fieldNames.push_back(name);
return shared_from_this();
}
FieldBuilderPtr FieldBuilder::addBoundedString(std::string const & name, std::size_t maxLength)
{
return add(name, fieldCreate->createBoundedString(maxLength));
fields.push_back(fieldCreate->createBoundedString(maxLength)); fieldNames.push_back(name);
return shared_from_this();
}
FieldBuilderPtr FieldBuilder::add(string const & name, FieldConstPtr const & field)
{
const Field* cur = findField(name, field->getType());
if(!cur) {
fields.push_back(field); fieldNames.push_back(name);
} else if(*cur!=*field) {
THROW_EXCEPTION2(std::runtime_error, "duplicate field name w/ different type : "+name);
} // else exact duplicate is silently ignored
fields.push_back(field); fieldNames.push_back(name);
return shared_from_this();
}
FieldBuilderPtr FieldBuilder::addArray(string const & name, ScalarType scalarType)
{
return add(name, fieldCreate->createScalarArray(scalarType));
fields.push_back(fieldCreate->createScalarArray(scalarType)); fieldNames.push_back(name);
return shared_from_this();
}
FieldBuilderPtr FieldBuilder::addFixedArray(string const & name, ScalarType scalarType, size_t size)
{
return add(name, fieldCreate->createFixedScalarArray(scalarType, size));
fields.push_back(fieldCreate->createFixedScalarArray(scalarType, size)); fieldNames.push_back(name);
return shared_from_this();
}
FieldBuilderPtr FieldBuilder::addBoundedArray(string const & name, ScalarType scalarType, size_t size)
{
return add(name, fieldCreate->createBoundedScalarArray(scalarType, size));
fields.push_back(fieldCreate->createBoundedScalarArray(scalarType, size)); fieldNames.push_back(name);
return shared_from_this();
}
FieldBuilderPtr FieldBuilder::addArray(string const & name, FieldConstPtr const & element)
{
FieldConstPtr fld;
switch (element->getType())
{
case structure:
fld = fieldCreate->createStructureArray(static_pointer_cast<const Structure>(element));
fields.push_back(fieldCreate->createStructureArray(static_pointer_cast<const Structure>(element)));
break;
case union_:
fld = fieldCreate->createUnionArray(static_pointer_cast<const Union>(element));
fields.push_back(fieldCreate->createUnionArray(static_pointer_cast<const Union>(element)));
break;
case scalar:
if (std::tr1::dynamic_pointer_cast<const BoundedString>(element).get())
THROW_EXCEPTION2(std::invalid_argument, "bounded string arrays are not supported");
fld = fieldCreate->createScalarArray(static_pointer_cast<const Scalar>(element)->getScalarType());
fields.push_back(fieldCreate->createScalarArray(static_pointer_cast<const Scalar>(element)->getScalarType()));
break;
// scalarArray?
default:
@@ -912,7 +790,8 @@ FieldBuilderPtr FieldBuilder::addArray(string const & name, FieldConstPtr const
THROW_EXCEPTION2(std::invalid_argument, msg.str());
}
return add(name, fld);
fieldNames.push_back(name);
return shared_from_this();
}
FieldConstPtr FieldBuilder::createFieldInternal(Type type)
@@ -962,111 +841,48 @@ UnionConstPtr FieldBuilder::createUnion()
return field;
}
const Field* FieldBuilder::findField(const std::string& name, Type ftype)
{
// linear search on the theory that the number of fields is small
for(size_t i=0; i<fieldNames.size(); i++)
{
if(name!=fieldNames[i])
continue;
if(fields[i]->getType()!=ftype)
THROW_EXCEPTION2(std::invalid_argument, "nested field not required type: "+name);
return fields[i].get();
}
return 0;
}
FieldBuilderPtr FieldBuilder::addNestedStructure(string const & name)
{
const Field *cur = findField(name, structure);
if(cur) {
return FieldBuilderPtr(new FieldBuilder(shared_from_this(), name,
static_cast<const Structure*>(cur)));
}
return FieldBuilderPtr(new FieldBuilder(shared_from_this(), name, structure, false));
}
FieldBuilderPtr FieldBuilder::addNestedUnion(string const & name)
{
const Field *cur = findField(name, union_);
if(cur) {
return FieldBuilderPtr(new FieldBuilder(shared_from_this(), name,
static_cast<const Union*>(cur)));
}
return FieldBuilderPtr(new FieldBuilder(shared_from_this(), name, union_, false));
}
FieldBuilderPtr FieldBuilder::addNestedStructureArray(string const & name)
{
const Field *cur = findField(name, structureArray);
if(cur) {
return FieldBuilderPtr(new FieldBuilder(shared_from_this(), name,
static_cast<const StructureArray*>(cur)));
}
return FieldBuilderPtr(new FieldBuilder(shared_from_this(), name, structure, true));
}
FieldBuilderPtr FieldBuilder::addNestedUnionArray(string const & name)
{
const Field *cur = findField(name, unionArray);
if(cur) {
return FieldBuilderPtr(new FieldBuilder(shared_from_this(), name,
static_cast<const UnionArray*>(cur)));
}
return FieldBuilderPtr(new FieldBuilder(shared_from_this(), name, union_, true));
}
FieldBuilderPtr FieldBuilder::endNested()
{
if (!parentBuilder)
THROW_EXCEPTION2(std::runtime_error, "FieldBuilder::endNested() can only be called to create nested fields");
if (!parentBuilder.get())
THROW_EXCEPTION2(std::runtime_error, "this method can only be called to create nested fields");
FieldConstPtr nestedField = createFieldInternal(nestedClassToBuild);
if(createNested) {
if (nestedArray)
parentBuilder->addArray(nestedName, nestedField);
else
parentBuilder->add(nestedName, nestedField);
return parentBuilder;
} else {
for(size_t i=0, N = parentBuilder->fieldNames.size(); i<N; i++)
{
if(nestedName!=parentBuilder->fieldNames[i])
continue;
if(nestedArray) {
if(nestedClassToBuild==structure)
parentBuilder->fields[i] = fieldCreate->createStructureArray(std::tr1::static_pointer_cast<const Structure>(nestedField));
else if(nestedClassToBuild==union_)
parentBuilder->fields[i] = fieldCreate->createUnionArray(std::tr1::static_pointer_cast<const Union>(nestedField));
else
throw std::logic_error("bad nested class");
} else {
parentBuilder->fields[i] = nestedField;
}
return parentBuilder;
}
// this only reached if bug in ctor
THROW_EXCEPTION2(std::logic_error, "no nested field field?");
}
if (nestedArray)
parentBuilder->addArray(nestedName, nestedField);
else
parentBuilder->add(nestedName, nestedField);
return parentBuilder;
}
FieldBuilderPtr FieldCreate::createFieldBuilder() const
{
return FieldBuilderPtr(new FieldBuilder());
}
FieldBuilderPtr FieldCreate::createFieldBuilder(StructureConstPtr S) const
{
FieldBuilderPtr ret(new FieldBuilder(S.get()));
return ret;
}
ScalarConstPtr FieldCreate::createScalar(ScalarType scalarType) const
{
if(scalarType<0 || scalarType>MAX_SCALAR_TYPE) {
@@ -1454,17 +1270,14 @@ FieldConstPtr FieldCreate::deserialize(ByteBuffer* buffer, DeserializableControl
}
// TODO replace with non-locking singleton pattern
const FieldCreatePtr& FieldCreate::getFieldCreate()
FieldCreatePtr FieldCreate::getFieldCreate()
{
LOCAL_STATIC_LOCK;
static FieldCreatePtr fieldCreate;
static Mutex mutex;
Lock xx(mutex);
if(fieldCreate.get()==0) {
fieldCreate = FieldCreatePtr(new FieldCreate());
registerRefCounter("Field", &Field::num_instances);
}
if(fieldCreate.get()==0) fieldCreate = FieldCreatePtr(new FieldCreate());
return fieldCreate;
}
@@ -1492,6 +1305,10 @@ FieldCreate::FieldCreate()
variantUnionArray = sua;
}
FieldCreatePtr getFieldCreate() {
return FieldCreate::getFieldCreate();
}
}}
namespace std{

View File

@@ -24,7 +24,6 @@
#include <pv/pvData.h>
#include <pv/factory.h>
#include <pv/serializeHelper.h>
#include <pv/reftrack.h>
using std::tr1::static_pointer_cast;
using std::size_t;
@@ -60,62 +59,135 @@ template<> const ScalarType PVFloatArray::typeCode = pvFloat;
template<> const ScalarType PVDoubleArray::typeCode = pvDouble;
template<> const ScalarType PVStringArray::typeCode = pvString;
/** Default storage for scalar values
*/
template<typename T>
class BasePVScalar : public PVScalarValue<T> {
public:
typedef T value_type;
typedef T* pointer;
typedef const T* const_pointer;
BasePVScalar(ScalarConstPtr const & scalar);
virtual ~BasePVScalar();
virtual T get() const ;
virtual void put(T val);
virtual void serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher) const;
virtual void deserialize(ByteBuffer *pbuffer,
DeserializableControl *pflusher);
private:
T value;
};
template<typename T>
PVScalarValue<T>::~PVScalarValue() {}
BasePVScalar<T>::BasePVScalar(ScalarConstPtr const & scalar)
: PVScalarValue<T>(scalar),value(0)
{}
//Note: '0' is a suitable default for all POD types (not string)
template<typename T>
void PVScalarValue<T>::serialize(ByteBuffer *pbuffer,
BasePVScalar<T>::~BasePVScalar() {}
template<typename T>
T BasePVScalar<T>::get() const { return value;}
template<typename T>
void BasePVScalar<T>::put(T val)
{
value = val;
PVField::postPut();
}
template<typename T>
void BasePVScalar<T>::serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher) const {
pflusher->ensureBuffer(sizeof(T));
pbuffer->put(storage.value);
}
template<>
void PVScalarValue<std::string>::serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher) const {
SerializeHelper::serializeString(storage.value, pbuffer, pflusher);
pbuffer->put(value);
}
template<typename T>
void PVScalarValue<T>::deserialize(ByteBuffer *pbuffer,
void BasePVScalar<T>::deserialize(ByteBuffer *pbuffer,
DeserializableControl *pflusher)
{
pflusher->ensureData(sizeof(T));
storage.value = pbuffer->GET(T);
value = pbuffer->GET(T);
}
template<>
void PVScalarValue<std::string>::deserialize(ByteBuffer *pbuffer,
DeserializableControl *pflusher)
{
storage.value = SerializeHelper::deserializeString(pbuffer, pflusher);
// TODO: check for violations of maxLength?
}
typedef BasePVScalar<boolean> BasePVBoolean;
typedef BasePVScalar<int8> BasePVByte;
typedef BasePVScalar<int16> BasePVShort;
typedef BasePVScalar<int32> BasePVInt;
typedef BasePVScalar<int64> BasePVLong;
typedef BasePVScalar<uint8> BasePVUByte;
typedef BasePVScalar<uint16> BasePVUShort;
typedef BasePVScalar<uint32> BasePVUInt;
typedef BasePVScalar<uint64> BasePVULong;
typedef BasePVScalar<float> BasePVFloat;
typedef BasePVScalar<double> BasePVDouble;
PVString::PVString(ScalarConstPtr const & scalar)
: PVScalarValue<std::string>(scalar)
// BasePVString is special case, since it implements SerializableArray
class BasePVString : public PVString {
public:
typedef string value_type;
typedef string* pointer;
typedef const string* const_pointer;
BasePVString(ScalarConstPtr const & scalar);
virtual ~BasePVString();
virtual string get() const ;
virtual void put(string val);
virtual void serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher) const;
virtual void deserialize(ByteBuffer *pbuffer,
DeserializableControl *pflusher);
virtual void serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher, size_t offset, size_t count) const;
private:
string value;
std::size_t maxLength;
};
BasePVString::BasePVString(ScalarConstPtr const & scalar)
: PVString(scalar),value()
{
BoundedStringConstPtr boundedString = std::tr1::dynamic_pointer_cast<const BoundedString>(scalar);
if (boundedString.get())
storage.maxLength = boundedString->getMaximumLength();
maxLength = boundedString->getMaximumLength();
else
storage.maxLength = 0;
maxLength = 0;
}
/* 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.
*/
void PVString::serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher) const
{PVScalarValue<std::string>::serialize(pbuffer, pflusher);}
BasePVString::~BasePVString() {}
void PVString::serialize(ByteBuffer *pbuffer,
string BasePVString::get() const { return value;}
void BasePVString::put(string val)
{
if (maxLength > 0 && val.length() > maxLength)
throw std::overflow_error("string too long");
value = val;
postPut();
}
void BasePVString::serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher) const
{
SerializeHelper::serializeString(value, pbuffer, pflusher);
}
void BasePVString::deserialize(ByteBuffer *pbuffer,
DeserializableControl *pflusher)
{
value = SerializeHelper::deserializeString(pbuffer, pflusher);
}
void BasePVString::serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher, size_t offset, size_t count) const
{
// check bounds
const size_t length = storage.value.length();
const size_t length = /*(value == null) ? 0 :*/ value.length();
/*if (offset < 0) offset = 0;
else*/ if (offset > length) offset = length;
//if (count < 0) count = length;
@@ -125,10 +197,10 @@ void PVString::serialize(ByteBuffer *pbuffer,
count = maxCount;
// write
SerializeHelper::serializeSubstring(storage.value, offset, count, pbuffer, pflusher);
SerializeHelper::serializeSubstring(value, offset, count, pbuffer, pflusher);
}
void PVArray::checkLength(size_t len) const
void PVArray::checkLength(size_t len)
{
Array::ArraySizeType type = getArray()->getArraySizeType();
if (type != Array::variable)
@@ -141,26 +213,61 @@ void PVArray::checkLength(size_t len) const
}
}
/** Default storage for arrays
*/
template<typename T>
PVValueArray<T>::PVValueArray(ScalarArrayConstPtr const & scalarArray)
:base_t(scalarArray)
,value()
class DefaultPVArray : public PVValueArray<T> {
public:
typedef T* pointer;
typedef const T* const_pointer;
typedef std::vector<T> vector;
typedef const std::vector<T> const_vector;
typedef std::tr1::shared_ptr<vector> shared_vector;
typedef ::epics::pvData::shared_vector<T> svector;
typedef ::epics::pvData::shared_vector<const T> const_svector;
DefaultPVArray(ScalarArrayConstPtr const & scalarArray);
virtual ~DefaultPVArray();
virtual size_t getLength() const {return value.size();}
virtual size_t getCapacity() const {return value.capacity();}
virtual void setCapacity(size_t capacity);
virtual void setLength(size_t length);
virtual const_svector view() const {return value;}
virtual void swap(const_svector &other);
virtual void replace(const const_svector& next);
// from Serializable
virtual void serialize(ByteBuffer *pbuffer,SerializableControl *pflusher) const;
virtual void deserialize(ByteBuffer *pbuffer,DeserializableControl *pflusher);
virtual void serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher, size_t offset, size_t count) const;
private:
const_svector value;
};
template<typename T>
DefaultPVArray<T>::DefaultPVArray(ScalarArrayConstPtr const & scalarArray)
: PVValueArray<T>(scalarArray),
value()
{}
PVValueArray<PVStructurePtr>::PVValueArray(StructureArrayConstPtr const & structureArray)
:base_t(structureArray)
,structureArray(structureArray)
{}
PVValueArray<PVUnionPtr>::PVValueArray(UnionArrayConstPtr const & unionArray)
:base_t(unionArray)
,unionArray(unionArray)
{}
{
ArrayConstPtr array = this->getArray();
if (array->getArraySizeType() == Array::fixed)
{
// this->setLength(array->getMaximumCapacity());
this->setCapacityMutable(false);
}
}
template<typename T>
void PVValueArray<T>::setCapacity(size_t capacity)
DefaultPVArray<T>::~DefaultPVArray()
{ }
template<typename T>
void DefaultPVArray<T>::setCapacity(size_t capacity)
{
if(this->isCapacityMutable()) {
this->checkLength(capacity);
@@ -171,7 +278,7 @@ void PVValueArray<T>::setCapacity(size_t capacity)
}
template<typename T>
void PVValueArray<T>::setLength(size_t length)
void DefaultPVArray<T>::setLength(size_t length)
{
if(this->isImmutable())
THROW_EXCEPTION2(std::logic_error, "immutable");
@@ -188,7 +295,7 @@ void PVValueArray<T>::setLength(size_t length)
}
template<typename T>
void PVValueArray<T>::replace(const const_svector& next)
void DefaultPVArray<T>::replace(const const_svector& next)
{
this->checkLength(next.size());
@@ -197,7 +304,7 @@ void PVValueArray<T>::replace(const const_svector& next)
}
template<typename T>
void PVValueArray<T>::swap(const_svector &other)
void DefaultPVArray<T>::swap(const_svector &other)
{
if (this->isImmutable())
THROW_EXCEPTION2(std::logic_error, "immutable");
@@ -209,13 +316,13 @@ void PVValueArray<T>::swap(const_svector &other)
template<typename T>
void PVValueArray<T>::serialize(ByteBuffer *pbuffer,
void DefaultPVArray<T>::serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher) const {
serialize(pbuffer, pflusher, 0, this->getLength());
}
template<typename T>
void PVValueArray<T>::deserialize(ByteBuffer *pbuffer,
void DefaultPVArray<T>::deserialize(ByteBuffer *pbuffer,
DeserializableControl *pcontrol) {
size_t size = this->getArray()->getArraySizeType() == Array::fixed ?
@@ -264,7 +371,7 @@ void PVValueArray<T>::deserialize(ByteBuffer *pbuffer,
}
template<typename T>
void PVValueArray<T>::serialize(ByteBuffer *pbuffer,
void DefaultPVArray<T>::serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher, size_t offset, size_t count) const
{
//TODO: avoid incrementing the ref counter...
@@ -308,7 +415,7 @@ void PVValueArray<T>::serialize(ByteBuffer *pbuffer,
// specializations for string
template<>
void PVValueArray<string>::deserialize(ByteBuffer *pbuffer,
void DefaultPVArray<string>::deserialize(ByteBuffer *pbuffer,
DeserializableControl *pcontrol) {
size_t size = this->getArray()->getArraySizeType() == Array::fixed ?
@@ -335,7 +442,7 @@ void PVValueArray<string>::deserialize(ByteBuffer *pbuffer,
}
template<>
void PVValueArray<string>::serialize(ByteBuffer *pbuffer,
void DefaultPVArray<string>::serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher, size_t offset, size_t count) const {
const_svector temp(value);
@@ -351,6 +458,19 @@ void PVValueArray<string>::serialize(ByteBuffer *pbuffer,
}
}
typedef DefaultPVArray<boolean> DefaultPVBooleanArray;
typedef DefaultPVArray<int8> BasePVByteArray;
typedef DefaultPVArray<int16> BasePVShortArray;
typedef DefaultPVArray<int32> BasePVIntArray;
typedef DefaultPVArray<int64> BasePVLongArray;
typedef DefaultPVArray<uint8> BasePVUByteArray;
typedef DefaultPVArray<uint16> BasePVUShortArray;
typedef DefaultPVArray<uint32> BasePVUIntArray;
typedef DefaultPVArray<uint64> BasePVULongArray;
typedef DefaultPVArray<float> BasePVFloatArray;
typedef DefaultPVArray<double> BasePVDoubleArray;
typedef DefaultPVArray<string> BasePVStringArray;
// Factory
PVDataCreate::PVDataCreate()
@@ -444,29 +564,29 @@ PVScalarPtr PVDataCreate::createPVScalar(ScalarConstPtr const & scalar)
ScalarType scalarType = scalar->getScalarType();
switch(scalarType) {
case pvBoolean:
return PVScalarPtr(new PVBoolean(scalar));
return PVScalarPtr(new BasePVBoolean(scalar));
case pvByte:
return PVScalarPtr(new PVByte(scalar));
return PVScalarPtr(new BasePVByte(scalar));
case pvShort:
return PVScalarPtr(new PVShort(scalar));
return PVScalarPtr(new BasePVShort(scalar));
case pvInt:
return PVScalarPtr(new PVInt(scalar));
return PVScalarPtr(new BasePVInt(scalar));
case pvLong:
return PVScalarPtr(new PVLong(scalar));
return PVScalarPtr(new BasePVLong(scalar));
case pvUByte:
return PVScalarPtr(new PVUByte(scalar));
return PVScalarPtr(new BasePVUByte(scalar));
case pvUShort:
return PVScalarPtr(new PVUShort(scalar));
return PVScalarPtr(new BasePVUShort(scalar));
case pvUInt:
return PVScalarPtr(new PVUInt(scalar));
return PVScalarPtr(new BasePVUInt(scalar));
case pvULong:
return PVScalarPtr(new PVULong(scalar));
return PVScalarPtr(new BasePVULong(scalar));
case pvFloat:
return PVScalarPtr(new PVFloat(scalar));
return PVScalarPtr(new BasePVFloat(scalar));
case pvDouble:
return PVScalarPtr(new PVDouble(scalar));
return PVScalarPtr(new BasePVDouble(scalar));
case pvString:
return PVScalarPtr(new PVString(scalar));
return PVScalarPtr(new BasePVString(scalar));
}
throw std::logic_error("PVDataCreate::createPVScalar should never get here");
}
@@ -491,29 +611,29 @@ PVScalarArrayPtr PVDataCreate::createPVScalarArray(
{
switch(scalarArray->getElementType()) {
case pvBoolean:
return PVScalarArrayPtr(new PVBooleanArray(scalarArray));
return PVScalarArrayPtr(new DefaultPVBooleanArray(scalarArray));
case pvByte:
return PVScalarArrayPtr(new PVByteArray(scalarArray));
return PVScalarArrayPtr(new BasePVByteArray(scalarArray));
case pvShort:
return PVScalarArrayPtr(new PVShortArray(scalarArray));
return PVScalarArrayPtr(new BasePVShortArray(scalarArray));
case pvInt:
return PVScalarArrayPtr(new PVIntArray(scalarArray));
return PVScalarArrayPtr(new BasePVIntArray(scalarArray));
case pvLong:
return PVScalarArrayPtr(new PVLongArray(scalarArray));
return PVScalarArrayPtr(new BasePVLongArray(scalarArray));
case pvUByte:
return PVScalarArrayPtr(new PVUByteArray(scalarArray));
return PVScalarArrayPtr(new BasePVUByteArray(scalarArray));
case pvUShort:
return PVScalarArrayPtr(new PVUShortArray(scalarArray));
return PVScalarArrayPtr(new BasePVUShortArray(scalarArray));
case pvUInt:
return PVScalarArrayPtr(new PVUIntArray(scalarArray));
return PVScalarArrayPtr(new BasePVUIntArray(scalarArray));
case pvULong:
return PVScalarArrayPtr(new PVULongArray(scalarArray));
return PVScalarArrayPtr(new BasePVULongArray(scalarArray));
case pvFloat:
return PVScalarArrayPtr(new PVFloatArray(scalarArray));
return PVScalarArrayPtr(new BasePVFloatArray(scalarArray));
case pvDouble:
return PVScalarArrayPtr(new PVDoubleArray(scalarArray));
return PVScalarArrayPtr(new BasePVDoubleArray(scalarArray));
case pvString:
return PVScalarArrayPtr(new PVStringArray(scalarArray));
return PVScalarArrayPtr(new BasePVStringArray(scalarArray));
}
throw std::logic_error("PVDataCreate::createPVScalarArray should never get here");
@@ -606,35 +726,21 @@ PVUnionPtr PVDataCreate::createPVUnion(PVUnionPtr const & unionToClone)
// TODO not thread-safe (local static initializers)
// TODO replace with non-locking singleton pattern
const PVDataCreatePtr& PVDataCreate::getPVDataCreate()
PVDataCreatePtr PVDataCreate::getPVDataCreate()
{
static PVDataCreatePtr pvDataCreate;
static Mutex mutex;
Lock xx(mutex);
if(pvDataCreate.get()==0) {
registerRefCounter("PVField", &PVField::num_instances);
pvDataCreate = PVDataCreatePtr(new PVDataCreate());
}
if(pvDataCreate.get()==0) pvDataCreate = PVDataCreatePtr(new PVDataCreate());
return pvDataCreate;
}
// explicitly instanciate to ensure that windows
// builds emit exported symbols for inline'd methods
template class PVScalarValue<boolean>;
template class PVScalarValue<int8>;
template class PVScalarValue<uint8>;
template class PVScalarValue<int16>;
template class PVScalarValue<uint16>;
template class PVScalarValue<int32>;
template class PVScalarValue<uint32>;
template class PVScalarValue<int64>;
template class PVScalarValue<uint64>;
template class PVScalarValue<float>;
template class PVScalarValue<double>;
template class PVScalarValue<std::string>;
PVDataCreatePtr getPVDataCreate() {
return PVDataCreate::getPVDataCreate();
}
}} // namespace epics::pvData
}}
namespace std{
std::ostream& operator<<(std::ostream& o, const epics::pvData::PVField *ptr)

View File

@@ -17,7 +17,6 @@
#include <pv/lock.h>
#include <pv/pvData.h>
#include <pv/factory.h>
#include <pv/reftrack.h>
using std::tr1::const_pointer_cast;
using std::size_t;
@@ -25,20 +24,15 @@ using std::string;
namespace epics { namespace pvData {
size_t PVField::num_instances;
PVField::PVField(FieldConstPtr field)
: parent(NULL),field(field),
fieldOffset(0), nextFieldOffset(0),
immutable(false)
{
REFTRACE_INCREMENT(num_instances);
}
PVField::~PVField()
{
REFTRACE_DECREMENT(num_instances);
}
{ }
size_t PVField::getFieldOffset() const
@@ -60,8 +54,14 @@ size_t PVField::getNumberFields() const
}
bool PVField::isImmutable() const {return immutable;}
void PVField::setImmutable() {immutable = true;}
const FieldConstPtr & PVField::getField() const {return field;}
PVStructure *PVField::getParent() const {return parent;}
void PVField::postPut()
{
if(postHandler.get()!=NULL) postHandler->postPut();
@@ -97,7 +97,7 @@ std::ostream& operator<<(std::ostream& o, const PVField& f)
string PVField::getFullName() const
{
string ret(fieldName);
for(const PVField *fld=getParent(); fld; fld=fld->getParent())
for(PVField *fld=getParent(); fld; fld=fld->getParent())
{
if(fld->getFieldName().size()==0) break;
ret = fld->getFieldName() + '.' + ret;

View File

@@ -24,6 +24,25 @@ using std::string;
namespace epics { namespace pvData {
PVFieldPtr PVStructure::nullPVField;
PVBooleanPtr PVStructure::nullPVBoolean;
PVBytePtr PVStructure::nullPVByte;
PVShortPtr PVStructure::nullPVShort;
PVIntPtr PVStructure::nullPVInt;
PVLongPtr PVStructure::nullPVLong;
PVUBytePtr PVStructure::nullPVUByte;
PVUShortPtr PVStructure::nullPVUShort;
PVUIntPtr PVStructure::nullPVUInt;
PVULongPtr PVStructure::nullPVULong;
PVFloatPtr PVStructure::nullPVFloat;
PVDoublePtr PVStructure::nullPVDouble;
PVStringPtr PVStructure::nullPVString;
PVStructurePtr PVStructure::nullPVStructure;
PVStructureArrayPtr PVStructure::nullPVStructureArray;
PVUnionPtr PVStructure::nullPVUnion;
PVUnionArrayPtr PVStructure::nullPVUnionArray;
PVScalarArrayPtr PVStructure::nullPVScalarArray;
PVStructure::PVStructure(StructureConstPtr const & structurePtr)
: PVField(structurePtr),
structurePtr(structurePtr),
@@ -74,7 +93,7 @@ void PVStructure::setImmutable()
PVField::setImmutable();
}
const StructureConstPtr& PVStructure::getStructure() const
StructureConstPtr PVStructure::getStructure() const
{
return structurePtr;
}
@@ -84,42 +103,50 @@ const PVFieldPtrArray & PVStructure::getPVFields() const
return pvFields;
}
PVFieldPtr PVStructure::getSubFieldImpl(size_t fieldOffset, bool throws) const
PVFieldPtr PVStructure::getSubField(const char * fieldName) const
{
const PVStructure *current = this;
PVField * field = getSubFieldImpl(fieldName, false);
if (field)
return field->shared_from_this();
else
return PVFieldPtr();
}
recurse:
// we don't permit self lookup
if(fieldOffset<=current->getFieldOffset() || fieldOffset>=current->getNextFieldOffset()) {
if(throws) {
std::stringstream ss;
ss << "Failed to get field with offset "
<< fieldOffset << " (Invalid offset)" ;
throw std::runtime_error(ss.str());
} else {
return PVFieldPtr();
PVFieldPtr PVStructure::getSubField(size_t fieldOffset) const
{
if(fieldOffset<=getFieldOffset()) {
return nullPVField;
}
if(fieldOffset>getNextFieldOffset()) return nullPVField;
size_t numFields = pvFields.size();
for(size_t i=0; i<numFields; i++) {
PVFieldPtr pvField = pvFields[i];
if(pvField->getFieldOffset()==fieldOffset) return pvFields[i];
if(pvField->getNextFieldOffset()<=fieldOffset) continue;
if(pvField->getField()->getType()==structure) {
PVStructure *pvStructure = static_cast<PVStructure *>(pvField.get());
return pvStructure->getSubField(fieldOffset);
}
}
for(size_t i=0, numFields = current->pvFields.size(); i<numFields; i++) {
const PVFieldPtr& pvField = current->pvFields[i];
if(pvField->getFieldOffset()==fieldOffset) {
return pvField;
} else if(pvField->getNextFieldOffset()<=fieldOffset) {
continue;
} else if(pvField->getField()->getType()==structure) {
current = static_cast<PVStructure *>(pvField.get());
goto recurse;
}
}
// the first test against current->getNextFieldOffset() would avoid this
throw std::logic_error("PVStructure.getSubField: Logic error");
}
PVFieldPtr PVStructure::getSubFieldImpl(const char *name, bool throws) const
PVFieldPtr PVStructure::getSubFieldT(std::size_t fieldOffset) const
{
PVFieldPtr pvField = getSubField(fieldOffset);
if (pvField.get())
return pvField;
else
{
std::stringstream ss;
ss << "Failed to get field with offset "
<< fieldOffset << "(Invalid offset)" ;
throw std::runtime_error(ss.str());
}
}
PVField* PVStructure::getSubFieldImpl(const char *name, bool throws) const
{
const PVStructure *parent = this;
if(!name)
@@ -127,7 +154,7 @@ PVFieldPtr PVStructure::getSubFieldImpl(const char *name, bool throws) const
if (throws)
throw std::invalid_argument("Failed to get field: (Field name is NULL string)");
else
return PVFieldPtr();
return NULL;
}
const char *fullName = name;
while(true) {
@@ -143,7 +170,7 @@ PVFieldPtr PVStructure::getSubFieldImpl(const char *name, bool throws) const
throw std::runtime_error(ss.str());
}
else
return PVFieldPtr();
return NULL;
}
size_t N = sep-name;
if(N==0)
@@ -156,7 +183,7 @@ PVFieldPtr PVStructure::getSubFieldImpl(const char *name, bool throws) const
throw std::runtime_error(ss.str());
}
else
return PVFieldPtr();
return NULL;
}
const PVFieldPtrArray& pvFields = parent->getPVFields();
@@ -183,7 +210,7 @@ PVFieldPtr PVStructure::getSubFieldImpl(const char *name, bool throws) const
throw std::runtime_error(ss.str());
}
else
return PVFieldPtr();
return NULL;
}
if(*sep) {
@@ -200,32 +227,18 @@ PVFieldPtr PVStructure::getSubFieldImpl(const char *name, bool throws) const
throw std::runtime_error(ss.str());
}
else
return PVFieldPtr();
return NULL;
}
child = NULL;
name = sep+1; // skip past '.'
// loop around to new parent
} else {
return child->shared_from_this();
return child;
}
}
}
void PVStructure::throwBadFieldType(const char *name)
{
std::ostringstream ss;
ss << "Failed to get field: " << name << " (Field has wrong type)";
throw std::runtime_error(ss.str());
}
void PVStructure::throwBadFieldType(std::size_t fieldOffset)
{
std::stringstream ss;
ss << "Failed to get field with offset "
<< fieldOffset << " (Field has wrong type)";
throw std::runtime_error(ss.str());
}
void PVStructure::serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher) const {
@@ -259,7 +272,7 @@ void PVStructure::serialize(ByteBuffer *pbuffer,
size_t fieldsSize = pvFields.size();
for(size_t i = 0; i<fieldsSize; i++) {
PVField* pvField = pvFields[i].get();
PVFieldPtr pvField = pvFields[i];
offset = pvField->getFieldOffset();
int32 inumberFields = static_cast<int32>(pvField->getNumberFields());
next = pbitSet->nextSetBit(static_cast<uint32>(offset));
@@ -273,7 +286,8 @@ void PVStructure::serialize(ByteBuffer *pbuffer,
if(inumberFields==1) {
pvField->serialize(pbuffer, pflusher);
} else {
static_cast<PVStructure*>(pvField)->serialize(pbuffer, pflusher, pbitSet);
PVStructurePtr pvStructure = std::tr1::static_pointer_cast<PVStructure>(pvField);
pvStructure->serialize(pbuffer, pflusher, pbitSet);
}
}
}

View File

@@ -25,7 +25,7 @@ using std::string;
namespace epics { namespace pvData {
#define PVUNION_UNDEFINED_INDEX -1
const int32 PVUnion::UNDEFINED_INDEX = PVUNION_UNDEFINED_INDEX;
int32 PVUnion::UNDEFINED_INDEX = PVUNION_UNDEFINED_INDEX;
PVDataCreatePtr PVUnion::pvDataCreate(getPVDataCreate());
@@ -40,7 +40,24 @@ PVUnion::PVUnion(UnionConstPtr const & unionPtr)
#undef PVUNION_UNDEFINED_INDEX
PVUnion::~PVUnion() {}
PVUnion::~PVUnion()
{
}
UnionConstPtr PVUnion::getUnion() const
{
return unionPtr;
}
PVFieldPtr PVUnion::get() const
{
return value;
}
int32 PVUnion::getSelectedIndex() const
{
return selector;
}
string PVUnion::getSelectedFieldName() const
{
@@ -53,11 +70,8 @@ string PVUnion::getSelectedFieldName() const
PVFieldPtr PVUnion::select(int32 index)
{
if (variant && index != UNDEFINED_INDEX)
throw std::invalid_argument("index out of bounds");
// no change
if (selector == index && !variant)
if (selector == index)
return value;
if (index == UNDEFINED_INDEX)
@@ -66,7 +80,9 @@ PVFieldPtr PVUnion::select(int32 index)
value.reset();
return value;
}
else if (index < 0 || size_t(index) >= unionPtr->getFields().size())
else if (variant)
throw std::invalid_argument("index out of bounds");
else if (index < 0 || index > static_cast<int32>(unionPtr->getFields().size()))
throw std::invalid_argument("index out of bounds");
FieldConstPtr field = unionPtr->getField(index);
@@ -83,6 +99,11 @@ PVFieldPtr PVUnion::select(string const & fieldName)
throw std::invalid_argument("no such fieldName");
return select(index);
}
void PVUnion::set(PVFieldPtr const & value)
{
set(selector, value);
}
void PVUnion::set(int32 index, PVFieldPtr const & value)
{
@@ -93,14 +114,14 @@ void PVUnion::set(int32 index, PVFieldPtr const & value)
if (index == UNDEFINED_INDEX)
{
// for undefined index we accept only null values
if (value)
if (value.get())
throw std::invalid_argument("non-null value for index == UNDEFINED_INDEX");
}
else if (index < 0 || size_t(index) >= unionPtr->getFields().size())
else if (index < 0 || index > static_cast<int32>(unionPtr->getFields().size()))
throw std::invalid_argument("index out of bounds");
else if (!value)
throw std::invalid_argument("Can't set defined index w/ NULL");
else if (value->getField() != unionPtr->getField(index))
// value type must match
if (value->getField() != unionPtr->getField(index))
throw std::invalid_argument("selected field and its introspection data do not match");
}
@@ -115,7 +136,7 @@ void PVUnion::set(string const & fieldName, PVFieldPtr const & value)
if (index == -1)
throw std::invalid_argument("no such fieldName");
set(index, value);
set(index, value);
}
void PVUnion::serialize(ByteBuffer *pbuffer, SerializableControl *pflusher) const
@@ -181,7 +202,7 @@ std::ostream& PVUnion::dumpValue(std::ostream& o) const
o << format::indent() << getUnion()->getID() << ' ' << getFieldName() << std::endl;
{
format::indent_scope s(o);
const PVField::const_shared_pointer& fieldField = get();
PVFieldPtr fieldField = get();
if (fieldField.get() == NULL)
o << format::indent() << "(none)" << std::endl;
else
@@ -210,7 +231,7 @@ void PVUnion::copy(const PVUnion& from)
void PVUnion::copyUnchecked(const PVUnion& from)
{
const PVField::const_shared_pointer& fromValue = from.get();
PVFieldPtr fromValue = from.get();
if (from.getUnion()->isVariant())
{
if (fromValue.get() == 0)

View File

@@ -644,7 +644,7 @@ StructureConstPtr StandardField::enumeratedAlarm()
return enumeratedAlarmField;
}
const StandardFieldPtr &StandardField::getStandardField()
StandardFieldPtr StandardField::getStandardField()
{
static StandardFieldPtr standardFieldCreate;
static Mutex mutex;
@@ -658,4 +658,10 @@ const StandardFieldPtr &StandardField::getStandardField()
return standardFieldCreate;
}
StandardFieldPtr getStandardField() {
return StandardField::getStandardField();
}
}}

View File

@@ -1,10 +0,0 @@
# This is a Makefile fragment, see ../Makefile
SRC_DIRS += $(PVDATA_SRC)/json
INC += pv/json.h
LIBSRCS += parsehelper.cpp
LIBSRCS += parseany.cpp
LIBSRCS += parseinto.cpp
LIBSRCS += print.cpp

View File

@@ -1,284 +0,0 @@
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
#include <sstream>
#define epicsExportSharedSymbols
#include <pv/pvdVersion.h>
#include <pv/pvData.h>
#include <pv/valueBuilder.h>
#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1)
#include "pv/json.h"
namespace pvd = epics::pvData;
using pvd::yajl::integer_arg;
using pvd::yajl::size_arg;
namespace {
struct context {
unsigned depth;
enum state_t {
Undefined,
Key,
Array,
} state;
pvd::shared_vector<void> arr;
pvd::ValueBuilder root,
*cur;
std::string msg,
key;
context() :depth(0u), state(Undefined), cur(&root) {}
};
#define TRY context *self = (context*)ctx; try
#define CATCH() catch(std::exception& e) { self->msg = e.what(); return 0; }
int jtree_null(void * ctx)
{
TRY {
self->msg = "NULL value not permitted";
return 0;
}CATCH()
}
int jtree_boolean(void * ctx, int boolVal)
{
TRY {
if(self->depth==0) throw std::runtime_error("Bare value not supported");
switch(self->state) {
case context::Key:
self->cur = &self->cur->add<pvd::pvBoolean>(self->key, boolVal);
self->key.clear();
self->state = context::Undefined;
break;
case context::Array:
{
if(self->arr.size()>0 && self->arr.original_type()!=pvd::pvBoolean)
throw std::runtime_error("Mixed type array not supported");
pvd::shared_vector<pvd::boolean> arr(pvd::static_shared_vector_cast<pvd::boolean>(self->arr));
arr.push_back(boolVal);
self->arr = pvd::static_shared_vector_cast<void>(arr);
break;
}
default:
throw std::logic_error("boolean in bad state");
}
return 1;
}CATCH()
}
int jtree_integer(void * ctx, integer_arg integerVal)
{
TRY {
if(self->depth==0) throw std::runtime_error("Bare value not supported");
switch(self->state) {
case context::Key:
self->cur = &self->cur->add<pvd::pvLong>(self->key, integerVal);
self->key.clear();
self->state = context::Undefined;
break;
case context::Array:
{
if(self->arr.size()>0 && self->arr.original_type()!=pvd::pvLong)
throw std::runtime_error("Mixed type array not supported");
pvd::shared_vector<pvd::int64> arr(pvd::static_shared_vector_cast<pvd::int64>(self->arr));
arr.push_back(integerVal);
self->arr = pvd::static_shared_vector_cast<void>(arr);
break;
}
default:
throw std::logic_error("int64 in bad state");
}
return 1;
}CATCH()
}
int jtree_double(void * ctx, double doubleVal)
{
TRY {
if(self->depth==0) throw std::runtime_error("Bare value not supported");
switch(self->state) {
case context::Key:
self->cur = &self->cur->add<pvd::pvDouble>(self->key, doubleVal);
self->key.clear();
self->state = context::Undefined;
break;
case context::Array:
{
if(self->arr.size()>0 && self->arr.original_type()!=pvd::pvDouble)
throw std::runtime_error("Mixed type array not supported");
pvd::shared_vector<double> arr(pvd::static_shared_vector_cast<double>(self->arr));
arr.push_back(doubleVal);
self->arr = pvd::static_shared_vector_cast<void>(arr);
break;
}
default:
throw std::logic_error("double in bad state");
}
return 1;
}CATCH()
}
int jtree_string(void * ctx, const unsigned char * stringVal,
size_arg stringLen)
{
TRY {
if(self->depth==0) throw std::runtime_error("Bare value not supported");
std::string sval((const char*)stringVal, stringLen);
switch(self->state) {
case context::Key:
self->cur = &self->cur->add<pvd::pvString>(self->key, sval);
self->key.clear();
self->state = context::Undefined;
break;
case context::Array:
{
if(self->arr.size()>0 && self->arr.original_type()!=pvd::pvString)
throw std::runtime_error("Mixed type array not supported");
pvd::shared_vector<std::string> arr(pvd::static_shared_vector_cast<std::string>(self->arr));
arr.push_back(sval);
self->arr = pvd::static_shared_vector_cast<void>(arr);
break;
}
default:
throw std::logic_error("double in bad state");
}
return 1;
}CATCH()
}
int jtree_start_map(void * ctx)
{
TRY {
if(self->depth>0) {
if(self->key.empty())
throw std::logic_error("anonymous dict not top level?");
self->cur = &self->cur->addNested(self->key);
self->key.clear();
}
self->depth++;
return 1;
}CATCH()
}
int jtree_map_key(void * ctx, const unsigned char * key,
size_arg stringLen)
{
TRY {
if(!self->key.empty())
throw std::logic_error("double key?");
if(stringLen==0)
throw std::runtime_error("empty key not allowed");
self->key = std::string((const char*)key, stringLen);
self->state = context::Key;
return 1;
}CATCH()
}
int jtree_end_map(void * ctx)
{
TRY {
if(self->depth>1)
self->cur = &self->cur->endNested();
else if(self->depth==0)
throw std::logic_error("Unbalenced dict");
self->depth--;
return 1;
}CATCH()
}
int jtree_start_array(void * ctx)
{
TRY {
if(self->depth==0) throw std::runtime_error("Bare array not supported");
if(self->state!=context::Key)
throw std::logic_error("bare array not supported");
self->state = context::Array;
return 1;
}CATCH()
}
int jtree_end_array(void * ctx)
{
TRY {
if(self->state!=context::Array)
throw std::logic_error("Bad array parse");
self->cur = &self->cur->add(self->key, pvd::freeze(self->arr));
self->key.clear();
self->state = context::Undefined;
return 1;
}CATCH()
}
yajl_callbacks jtree_cbs = {
&jtree_null,
&jtree_boolean,
&jtree_integer,
&jtree_double,
NULL, // number
&jtree_string,
&jtree_start_map,
&jtree_map_key,
&jtree_end_map,
&jtree_start_array,
&jtree_end_array,
};
struct handler {
yajl_handle handle;
handler(yajl_handle handle) :handle(handle)
{
if(!handle)
throw std::runtime_error("Failed to allocate yajl handle");
}
~handler() {
yajl_free(handle);
}
operator yajl_handle() { return handle; }
};
} // namespace
namespace epics{namespace pvData{
epics::pvData::PVStructure::shared_pointer
parseJSON(std::istream& strm)
{
#ifndef EPICS_YAJL_VERSION
yajl_parser_config conf;
memset(&conf, 0, sizeof(conf));
conf.allowComments = 1;
conf.checkUTF8 = 1;
#endif
context ctxt;
#ifndef EPICS_YAJL_VERSION
handler handle(yajl_alloc(&jtree_cbs, &conf, NULL, &ctxt));
#else
handler handle(yajl_alloc(&jtree_cbs, NULL, &ctxt));
yajl_config(handle, yajl_allow_comments, 1);
#endif
if(!yajl_parse_helper(strm, handle))
throw std::runtime_error(ctxt.msg);
return ctxt.cur->buildPVStructure();
}
}} // namespace epics::pvData
#endif // EPICS_VERSION_INT

View File

@@ -1,122 +0,0 @@
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
#include <stdexcept>
#include <sstream>
#define epicsExportSharedSymbols
#include <pv/pvdVersion.h>
#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1)
#include "pv/json.h"
namespace {
void check_trailing(const std::string& line)
{
size_t idx = line.find_first_not_of(" \t\n\r");
if(idx==line.npos) return;
// TODO: detect the end of potentially multi-line comments...
// for now trailing comments not allowed
throw std::runtime_error("Trailing junk");
}
} // namespace
namespace epics{namespace pvData{
bool yajl_parse_helper(std::istream& src,
yajl_handle handle)
{
unsigned linenum=0;
#ifndef EPICS_YAJL_VERSION
bool done = false;
#endif
std::string line;
while(std::getline(src, line)) {
linenum++;
#ifndef EPICS_YAJL_VERSION
if(done) {
check_trailing(line);
continue;
}
#endif
yajl_status sts = yajl_parse(handle, (const unsigned char*)line.c_str(), line.size());
switch(sts) {
case yajl_status_ok: {
size_t consumed = yajl_get_bytes_consumed(handle);
if(consumed<line.size()) {
check_trailing(line.substr(consumed));
}
#ifndef EPICS_YAJL_VERSION
done = true;
#endif
break;
}
case yajl_status_client_canceled:
return false;
#ifndef EPICS_YAJL_VERSION
case yajl_status_insufficient_data:
// continue with next line
break;
#endif
case yajl_status_error:
{
std::ostringstream msg;
unsigned char *raw = yajl_get_error(handle, 1, (const unsigned char*)line.c_str(), line.size());
if(!raw) {
msg<<"Unknown error on line "<<linenum;
} else {
try {
msg<<"Error on line "<<linenum<<" : "<<(const char*)raw;
}catch(...){
yajl_free_error(handle, raw);
throw;
}
yajl_free_error(handle, raw);
}
throw std::runtime_error(msg.str());
}
}
}
if(!src.eof() || src.bad()) {
std::ostringstream msg;
msg<<"I/O error after line "<<linenum;
throw std::runtime_error(msg.str());
#ifndef EPICS_YAJL_VERSION
} else if(!done) {
switch(yajl_parse_complete(handle)) {
#else
} else {
switch(yajl_complete_parse(handle)) {
#endif
case yajl_status_ok:
break;
case yajl_status_client_canceled:
return false;
#ifndef EPICS_YAJL_VERSION
case yajl_status_insufficient_data:
throw std::runtime_error("unexpected end of input");
#endif
case yajl_status_error:
throw std::runtime_error("Error while completing parsing");
}
}
return true;
}
}} // namespace epics::pvData
#endif // EPICS_VERSION_INT

View File

@@ -1,342 +0,0 @@
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
#include <vector>
#include <sstream>
#define epicsExportSharedSymbols
#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)
#include "pv/json.h"
namespace pvd = epics::pvData;
using pvd::yajl::integer_arg;
using pvd::yajl::size_arg;
namespace {
struct context {
std::string msg;
struct frame {
pvd::PVFieldPtr fld;
pvd::BitSet *assigned;
frame(const pvd::PVFieldPtr& fld, pvd::BitSet *assigned)
:fld(fld), assigned(assigned)
{}
};
typedef std::vector<frame> stack_t;
stack_t stack;
context(const pvd::PVFieldPtr& root, pvd::BitSet *assigned)
{
stack.push_back(frame(root, assigned));
}
};
#define TRY context *self = (context*)ctx; assert(!self->stack.empty()); try
#define CATCH() catch(std::exception& e) { if(self->msg.empty()) self->msg = e.what(); return 0; }
int jtree_null(void * ctx)
{
TRY {
self->msg = "NULL value not permitted";
return 0;
}CATCH()
}
template<typename PVScalarT, typename PVArrayT>
void valueAssign(context *self, typename PVScalarT::value_type val)
{
assert(!self->stack.empty());
context::frame& back = self->stack.back();
pvd::Type type(back.fld->getField()->getType());
if(type==pvd::scalar) {
pvd::PVScalar* fld(static_cast<pvd::PVScalar*>(back.fld.get()));
fld->putFrom(val);
if(back.assigned)
back.assigned->set(fld->getFieldOffset());
self->stack.pop_back();
// structure at the top of the stack
} else if(type==pvd::scalarArray) {
pvd::PVScalarArray *fld(static_cast<pvd::PVScalarArray*>(back.fld.get()));
pvd::shared_vector<const void> carr;
fld->getAs(carr);
switch(carr.original_type())
{
#define CASE_STRING
#define CASE_REAL_INT64
#define CASE(BASETYPE, PVATYPE, DBFTYPE, PVACODE) case epics::pvData::pv##PVACODE: { \
pvd::shared_vector<const PVATYPE> arr(pvd::static_shared_vector_cast<const PVATYPE>(carr)); \
pvd::shared_vector<PVATYPE> tarr(pvd::thaw(arr)); \
tarr.push_back(pvd::castUnsafe<PVATYPE>(val)); \
carr = pvd::static_shared_vector_cast<const void>(pvd::freeze(tarr)); \
} break;
#include <pv/typemap.h>
#undef CASE
#undef CASE_REAL_INT64
#undef CASE_STRING
}
fld->putFrom(carr);
// leave array field at top of stack
} else if(type==pvd::union_) {
pvd::PVUnion* fld(static_cast<pvd::PVUnion*>(back.fld.get()));
pvd::UnionConstPtr utype(fld->getUnion());
if(utype->isVariant()) {
typename PVScalarT::shared_pointer elem(pvd::getPVDataCreate()->createPVScalar<PVScalarT>());
elem->put(val);
fld->set(elem);
} else {
// attempt automagic assignment
const pvd::StringArray& names = utype->getFieldNames();
const pvd::FieldConstPtrArray types = utype->getFields();
assert(names.size()==types.size());
bool assigned = false;
for(size_t i=0, N=names.size(); i<N; i++) {
if(types[i]->getType()!=pvd::scalar) continue;
pvd::PVScalarPtr ufld(fld->select<pvd::PVScalar>(i));
try{
ufld->putFrom(val);
assigned = true;
}catch(std::runtime_error&){
if(i==N-1)
throw;
continue;
}
break;
}
if(!assigned)
throw std::runtime_error("Unable to select union member");
}
if(back.assigned)
back.assigned->set(fld->getFieldOffset());
self->stack.pop_back();
// structure back at the top of the stack
} else {
throw std::invalid_argument("Can't assign value");
}
}
int jtree_boolean(void * ctx, int boolVal)
{
TRY {
valueAssign<pvd::PVBoolean, pvd::PVBooleanArray>(self, !!boolVal);
return 1;
}CATCH()
}
int jtree_integer(void * ctx, integer_arg integerVal)
{
TRY {
valueAssign<pvd::PVLong, pvd::PVLongArray>(self, integerVal);
return 1;
}CATCH()
}
int jtree_double(void * ctx, double doubleVal)
{
TRY {
valueAssign<pvd::PVDouble, pvd::PVDoubleArray>(self, doubleVal);
return 1;
}CATCH()
}
int jtree_string(void * ctx, const unsigned char * stringVal,
size_arg stringLen)
{
TRY {
std::string val((const char*)stringVal, stringLen);
valueAssign<pvd::PVString, pvd::PVStringArray>(self, val);
return 1;
}CATCH()
}
int jtree_start_map(void * ctx)
{
TRY {
assert(!self->stack.empty());
context::frame& back = self->stack.back();
pvd::Type type = back.fld->getField()->getType();
if(type==pvd::structure) {
// will fill in
} else if(type==pvd::structureArray) {
// starting new element in structure array
pvd::PVStructureArray* sarr(static_cast<pvd::PVStructureArray*>(back.fld.get()));
pvd::PVStructurePtr elem(pvd::getPVDataCreate()->createPVStructure(sarr->getStructureArray()->getStructure()));
self->stack.push_back(context::frame(elem, 0));
} else {
throw std::runtime_error("Can't map (sub)structure");
}
assert(self->stack.back().fld->getField()->getType()==pvd::structure);
return 1;
}CATCH()
}
int jtree_map_key(void * ctx, const unsigned char * key,
size_arg stringLen)
{
TRY {
assert(!self->stack.empty());
std::string name((const char*)key, stringLen);
// start_map() ensures we have a structure at the top of the stack
pvd::PVStructure *fld = static_cast<pvd::PVStructure*>(self->stack.back().fld.get());
try {
self->stack.push_back(context::frame(fld->getSubFieldT(name), self->stack.back().assigned));
}catch(std::runtime_error& e){
std::ostringstream strm;
strm<<"At "<<fld->getFullName()<<" : "<<e.what()<<"\n";
throw std::runtime_error(strm.str());
}
return 1;
}CATCH()
}
int jtree_end_map(void * ctx)
{
TRY {
assert(!self->stack.empty());
assert(self->stack.back().fld->getField()->getType()==pvd::structure);
context::frame elem(self->stack.back());
self->stack.pop_back();
if(!self->stack.empty() && self->stack.back().fld->getField()->getType()==pvd::structureArray) {
// append element to struct array
pvd::PVStructureArray *sarr = static_cast<pvd::PVStructureArray*>(self->stack.back().fld.get());
pvd::PVStructureArray::const_svector cval;
sarr->swap(cval);
pvd::PVStructureArray::svector val(pvd::thaw(cval));
val.push_back(std::tr1::static_pointer_cast<pvd::PVStructure>(elem.fld));
sarr->replace(pvd::freeze(val));
}
return 1;
}CATCH()
}
int jtree_start_array(void * ctx)
{
TRY {
assert(!self->stack.empty());
pvd::PVFieldPtr& back(self->stack.back().fld);
pvd::Type type = back->getField()->getType();
if(type!=pvd::structureArray && type!=pvd::scalarArray)
throw std::runtime_error("Can't assign array");
return 1;
}CATCH()
}
int jtree_end_array(void * ctx)
{
TRY {
assert(!self->stack.empty());
if(self->stack.back().assigned)
self->stack.back().assigned->set(self->stack.back().fld->getFieldOffset());
self->stack.pop_back();
return 1;
}CATCH()
}
yajl_callbacks jtree_cbs = {
&jtree_null,
&jtree_boolean,
&jtree_integer,
&jtree_double,
NULL, // number
&jtree_string,
&jtree_start_map,
&jtree_map_key,
&jtree_end_map,
&jtree_start_array,
&jtree_end_array,
};
struct handler {
yajl_handle handle;
handler(yajl_handle handle) :handle(handle)
{
if(!handle)
throw std::runtime_error("Failed to allocate yajl handle");
}
~handler() {
yajl_free(handle);
}
operator yajl_handle() { return handle; }
};
} // namespace
namespace epics{namespace pvData{
epicsShareFunc
void parseJSON(std::istream& strm,
const PVField::shared_pointer& dest,
BitSet *assigned)
{
#ifndef EPICS_YAJL_VERSION
yajl_parser_config conf;
memset(&conf, 0, sizeof(conf));
conf.allowComments = 1;
conf.checkUTF8 = 1;
#endif
context ctxt(dest, assigned);
#ifndef EPICS_YAJL_VERSION
handler handle(yajl_alloc(&jtree_cbs, &conf, NULL, &ctxt));
#else
handler handle(yajl_alloc(&jtree_cbs, NULL, &ctxt));
yajl_config(handle, yajl_allow_comments, 1);
#endif
if(!yajl_parse_helper(strm, handle))
throw std::runtime_error(ctxt.msg);
if(!ctxt.stack.empty())
throw std::logic_error("field stack not empty");
}
}} // namespace epics::pvData
#endif // EPICS_VERSION_INT

View File

@@ -1,170 +0,0 @@
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
#include <vector>
#include <sstream>
#define epicsExportSharedSymbols
#include <pv/pvdVersion.h>
#include <pv/pvData.h>
#include <pv/valueBuilder.h>
#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1)
#include "pv/json.h"
namespace pvd = epics::pvData;
namespace {
struct args {
std::ostream& strm;
const pvd::JSONPrintOptions& opts;
unsigned indent;
args(std::ostream& strm,
const pvd::JSONPrintOptions& opts)
:strm(strm)
,opts(opts)
,indent(opts.indent)
{}
void doIntent() {
if(!opts.multiLine) return;
strm.put('\n');
unsigned i=indent;
while(i--) strm.put(' ');
}
};
void show_field(args& A, const pvd::PVField* fld);
void show_struct(args& A, const pvd::PVStructure* fld)
{
const pvd::StructureConstPtr& type = fld->getStructure();
const pvd::PVFieldPtrArray& children = fld->getPVFields();
const pvd::StringArray& names = type->getFieldNames();
A.strm.put('{');
A.indent++;
for(size_t i=0, N=names.size(); i<N; i++)
{
if(i!=0)
A.strm.put(',');
A.doIntent();
A.strm<<'\"'<<names[i]<<"\": ";
show_field(A, children[i].get());
}
A.indent--;
A.doIntent();
A.strm.put('}');
}
void show_field(args& A, const pvd::PVField* fld)
{
switch(fld->getField()->getType())
{
case pvd::scalar:
{
const pvd::PVScalar *scalar=static_cast<const pvd::PVScalar*>(fld);
if(scalar->getScalar()->getScalarType()==pvd::pvString) {
A.strm<<'\"'<<scalar->getAs<std::string>()<<'\"';
} else {
A.strm<<scalar->getAs<std::string>();
}
}
break;
case pvd::scalarArray:
{
const pvd::PVScalarArray *scalar=static_cast<const pvd::PVScalarArray*>(fld);
const bool isstring = scalar->getScalarArray()->getElementType()==pvd::pvString;
pvd::shared_vector<const void> arr;
scalar->getAs<void>(arr);
pvd::shared_vector<const std::string> sarr(pvd::shared_vector_convert<const std::string>(arr));
A.strm.put('[');
for(size_t i=0, N=sarr.size(); i<N; i++) {
if(i!=0)
A.strm.put(',');
if(isstring)
A.strm.put('\"');
A.strm<<sarr[i];
if(isstring)
A.strm.put('\"');
}
A.strm.put(']');
}
break;
case pvd::structure:
show_struct(A, static_cast<const pvd::PVStructure*>(fld));
break;
case pvd::structureArray:
{
pvd::PVStructureArray::const_svector arr(static_cast<const pvd::PVStructureArray*>(fld)->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_struct(A, arr[i].get());
else
A.strm<<"NULL";
}
A.indent--;
A.doIntent();
A.strm.put(']');
}
break;
case pvd::union_:
{
const pvd::PVUnion *U=static_cast<const pvd::PVUnion*>(fld);
const pvd::PVField::const_shared_pointer& C(U->get());
if(!C) {
A.strm<<"null";
} else {
show_field(A, C.get());
}
}
break;
default:
if(A.opts.ignoreUnprintable)
A.strm<<"// unprintable field type";
else
throw std::runtime_error("Encountered unprintable field type");
}
}
} // namespace
namespace epics{namespace pvData{
JSONPrintOptions::JSONPrintOptions()
:multiLine(true)
,ignoreUnprintable(true)
,indent(0)
{}
void printJSON(std::ostream& strm,
const PVField::const_shared_pointer& val,
const JSONPrintOptions& opts)
{
args A(strm, opts);
show_field(A, val.get());
}
}} // namespace epics::pvData
#endif // EPICS_VERSION_INT

View File

@@ -1,127 +0,0 @@
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
#ifndef PV_JSON_H
#define PV_JSON_H
#include <istream>
#include <ostream>
#include <string>
#include <map>
#include <pv/pvdVersion.h>
#include <pv/pvData.h>
#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1)
#ifdef epicsExportSharedSymbols
# define pvjson_epicsExportSharedSymbols
# undef epicsExportSharedSymbols
#endif
#include <yajl_parse.h>
#ifdef pvjson_epicsExportSharedSymbols
# define epicsExportSharedSymbols
# include "shareLib.h"
#endif
#include <shareLib.h>
namespace epics{namespace pvData{
class BitSet;
/** @defgroup pvjson JSON print/parse
*
* Printing PVField as JSON and parsing JSON into PVField.
*
* @{
*/
//! Options used during printing
struct epicsShareClass JSONPrintOptions
{
bool multiLine; //!< include new lines
bool ignoreUnprintable;//!< ignore union/union array when encountered
unsigned indent; //!< Initial indentation (# of spaces)
JSONPrintOptions();
};
/** Print PVStructure as JSON
*
* Restrictions:
*
* - No support for union or array of union
*/
epicsShareFunc
void printJSON(std::ostream& strm,
const PVField::const_shared_pointer& val,
const JSONPrintOptions& opts = JSONPrintOptions());
/** Parse JSON text into a PVStructure
*
* Restrictions:
*
* - Top level must be {} dict/object
* - field values must be number, string, array, or dict/object
* - array values must be number or string
*/
epicsShareFunc
PVStructure::shared_pointer parseJSON(std::istream& strm);
/** Parse JSON and store into the provided PVStructure.
*
* Restrictions:
*
* - array of union not supported
* - Only scalar value assigned to union
*
* @param strm Read JSON text from stream
* @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.
*/
epicsShareFunc
void parseJSON(std::istream& strm,
const PVField::shared_pointer& dest,
BitSet *assigned=0);
/** Wrapper around yajl_parse()
*
* Parse entire input stream.
* Errors if extranious non-whitespace found after the point were parsing completes.
*
* @param src The stream from which input charactors are read
* @param handle A parser handle previously allocated with yajl_alloc(). Not free'd on success or failure.
*
* @returns true if parsing completes successfully. false if parsing cancelled by callback. throws other errors
*
* @note The form of this call depends on EPICS_YAJL_VERSION
*/
epicsShareFunc
bool yajl_parse_helper(std::istream& src,
yajl_handle handle);
namespace yajl {
// undef implies API version 0
#ifndef EPICS_YAJL_VERSION
typedef long integer_arg;
typedef unsigned size_arg;
#else
typedef long long integer_arg;
typedef size_t size_arg;
#endif
} // namespace epics::pvData::yajl
/** @} */
}} // namespace epics::pvData
#else
# error JSON parser requires EPICS Base >= 3.15.0.1
#endif // EPICS_VERSION_INT
#endif // PV_JSON_H

View File

@@ -4,6 +4,7 @@ SRC_DIRS += $(PVDATA_SRC)/misc
INC += pv/noDefaultMethods.h
INC += pv/lock.h
INC += pv/requester.h
INC += pv/serialize.h
INC += pv/bitSet.h
INC += pv/byteBuffer.h
@@ -15,30 +16,28 @@ INC += pv/executor.h
INC += pv/timeFunction.h
INC += pv/timer.h
INC += pv/queue.h
INC += pv/messageQueue.h
INC += pv/destroyable.h
INC += pv/status.h
INC += pv/sharedPtr.h
INC += pv/debugPtr.h
INC += pv/localStaticLock.h
INC += pv/typeCast.h
INC += pv/sharedVector.h
INC += pv/templateMeta.h
INC += pv/current_function.h
INC += pv/pvUnitTest.h
INC += pv/reftrack.h
INC += pv/anyscalar.h
LIBSRCS += byteBuffer.cpp
LIBSRCS += bitSet.cpp
LIBSRCS += epicsException.cpp
LIBSRCS += requester.cpp
LIBSRCS += serializeHelper.cpp
LIBSRCS += event.cpp
LIBSRCS += executor.cpp
LIBSRCS += timeFunction.cpp
LIBSRCS += timer.cpp
LIBSRCS += status.cpp
LIBSRCS += messageQueue.cpp
LIBSRCS += localStaticLock.cpp
LIBSRCS += typeCast.cpp
LIBSRCS += parseToPOD.cpp
LIBSRCS += pvUnitTest.cpp
LIBSRCS += debugPtr.cpp
LIBSRCS += reftrack.cpp

View File

@@ -55,19 +55,6 @@ namespace epics { namespace pvData {
words.reserve((nbits == 0) ? 1 : WORD_INDEX(nbits-1) + 1);
}
#if __cplusplus>=201103L
BitSet::BitSet(std::initializer_list<uint32> I)
{
// optimistically guess that highest bit is last (not required)
words.reserve((I.size() == 0) ? 1 : WORD_INDEX(*(I.end()-1)) + 1);
for(uint32 idx : I)
{
set(idx);
}
}
#endif
BitSet::~BitSet() {}
void BitSet::recalculateWordsInUse() {
@@ -89,7 +76,7 @@ namespace epics { namespace pvData {
ensureCapacity(wordIndex+1);
}
BitSet& BitSet::flip(uint32 bitIndex) {
void BitSet::flip(uint32 bitIndex) {
uint32 wordIdx = WORD_INDEX(bitIndex);
expandTo(wordIdx);
@@ -97,27 +84,25 @@ namespace epics { namespace pvData {
words[wordIdx] ^= (((uint64)1) << WORD_OFFSET(bitIndex));
recalculateWordsInUse();
return *this;
}
BitSet& BitSet::set(uint32 bitIndex) {
void BitSet::set(uint32 bitIndex) {
uint32 wordIdx = WORD_INDEX(bitIndex);
expandTo(wordIdx);
words[wordIdx] |= (((uint64)1) << WORD_OFFSET(bitIndex));
return *this;
}
BitSet& BitSet::clear(uint32 bitIndex) {
void BitSet::clear(uint32 bitIndex) {
uint32 wordIdx = WORD_INDEX(bitIndex);
if (wordIdx < words.size()) {
words[wordIdx] &= ~(((uint64)1) << WORD_OFFSET(bitIndex));
if (wordIdx >= words.size())
return;
recalculateWordsInUse();
}
return *this;
words[wordIdx] &= ~(((uint64)1) << WORD_OFFSET(bitIndex));
recalculateWordsInUse();
}
void BitSet::set(uint32 bitIndex, bool value) {
@@ -211,20 +196,6 @@ namespace epics { namespace pvData {
return words.size() * BITS_PER_WORD;
}
bool BitSet::logical_and(const BitSet& set) const
{
size_t nwords = std::min(words.size(), set.words.size());
for(size_t i=0; i<nwords; i++) {
if(words[i] & set.words[i])
return true;
}
return false;
}
bool BitSet::logical_or(const BitSet& set) const
{
return !words.empty() || !set.words.empty();
}
BitSet& BitSet::operator&=(const BitSet& set) {
// Check for self-assignment!
if (this == &set) return *this;

View File

@@ -1,165 +0,0 @@
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
#if __cplusplus>=201103L
#include <epicsMutex.h>
#include <epicsGuard.h>
#define epicsExportSharedSymbols
#include <pv/debugPtr.h>
namespace {
typedef epicsGuard<epicsMutex> Guard;
}
namespace epics {
namespace debug {
// joins together a group of ptr_base instances
// which all have the same dtor
struct tracker {
epicsMutex mutex;
ptr_base::ref_set_t refs;
};
void shared_ptr_base::track_new()
{
if(track) {
Guard G(track->mutex);
track->refs.insert(this);
}
snap_stack();
}
// create new tracker if ptr!=nullptr, otherwise clear
void shared_ptr_base::track_new(void* ptr)
{
track_clear();
if(ptr){
track.reset(new tracker);
Guard G(track->mutex);
track->refs.insert(this);
}
snap_stack();
}
void shared_ptr_base::track_assign(const shared_ptr_base &o)
{
if(track!=o.track) {
track_clear();
track = o.track;
if(track) {
Guard G(track->mutex);
track->refs.insert(this);
}
snap_stack();
}
}
void shared_ptr_base::track_clear()
{
if(track) {
Guard G(track->mutex);
track->refs.erase(this);
}
track.reset();
#ifndef EXCEPT_USE_NONE
m_depth = 0;
#endif
}
void shared_ptr_base::swap(shared_ptr_base &o)
{
// we cheat a bit here to avoid lock order, and to lock only twice
if(track) {
Guard G(track->mutex);
track->refs.insert(&o);
track->refs.erase(this);
}
track.swap(o.track);
if(track) {
Guard G(track->mutex);
track->refs.insert(this);
track->refs.erase(&o);
}
//TODO: keep original somehow???
snap_stack();
o.snap_stack();
}
void shared_ptr_base::snap_stack()
{
if(!track) {
#ifndef EXCEPT_USE_NONE
m_depth = 0;
#endif
return;
}
#if defined(EXCEPT_USE_BACKTRACE)
{
m_depth=backtrace(m_stack,EXCEPT_DEPTH);
}
#else
{}
#endif
}
void shared_ptr_base::show_stack(std::ostream& strm) const
{
strm<<"ptr "<<this;
#ifndef EXCEPT_USE_NONE
if(m_depth<=0) return;
#endif
#if 0 && defined(EXCEPT_USE_BACKTRACE)
{
char **symbols=backtrace_symbols(m_stack, m_depth);
strm<<": ";
for(int i=0; i<m_depth; i++) {
strm<<symbols[i]<<", ";
}
std::free(symbols);
}
#elif !defined(EXCEPT_USE_NONE)
{
strm<<": ";
for(int i=0; i<m_depth; i++) {
strm<<std::hex<<m_stack[i]<<" ";
}
}
#endif
}
void ptr_base::show_refs(std::ostream& strm, bool self, bool weak) const
{
if(!track) {
strm<<"# No refs\n";
} else {
Guard G(track->mutex);
for(auto ref : track->refs) {
if(!self && ref==this) continue;
strm<<'#';
ref->show_stack(strm);
strm<<'\n';
}
}
}
void ptr_base::spy_refs(ref_set_t &refs) const
{
if(track) {
Guard G(track->mutex);
refs.insert(track->refs.begin(), track->refs.end());
}
}
}} // namespace epics::debug
#endif // __cplusplus>=201103L

View File

@@ -16,11 +16,6 @@
#include <epicsMutex.h>
#include <epicsThread.h>
// Suppress deprecation warnings for the implementation
#include <compilerDependencies.h>
#undef EPICS_DEPRECATED
#define EPICS_DEPRECATED
#define epicsExportSharedSymbols
#include <pv/executor.h>

115
src/misc/messageQueue.cpp Normal file
View File

@@ -0,0 +1,115 @@
/* messageQueue.cpp */
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
/**
* @author mrk
*/
#include <string>
#define epicsExportSharedSymbols
#include <pv/messageQueue.h>
using std::string;
namespace epics { namespace pvData {
MessageNode::MessageNode()
: messageType(infoMessage)
{}
string MessageNode::getMessage() const
{
return message;
}
MessageType MessageNode::getMessageType() const
{
return messageType;
}
MessageQueuePtr MessageQueue::create(int size)
{
MessageNodePtrArray nodeArray;
nodeArray.reserve(size);
for(int i=0; i<size; i++) {
nodeArray.push_back(
MessageNodePtr(new MessageNode()));
}
return std::tr1::shared_ptr<MessageQueue>(new MessageQueue(nodeArray));
}
MessageQueue::MessageQueue(MessageNodePtrArray &data)
: Queue<MessageNode>(data),
overrun(0)
{ }
MessageQueue::~MessageQueue()
{
}
MessageNodePtr &MessageQueue::get() {
if(getNumberUsed()==0) return nullNode;
lastGet = getUsed();
return lastGet;
}
void MessageQueue::release() {
if(lastGet.get()==NULL) return;
releaseUsed(lastGet);
lastGet.reset();
}
bool MessageQueue::put(string message,MessageType messageType,bool replaceLast)
{
MessageNodePtr node = getFree();
if(node.get()!= NULL) {
node->message = message;
node->messageType = messageType;
lastPut = node;
setUsed(node);
return true;
}
overrun++;
if(replaceLast) {
node = lastPut;
node->message = message;
node->messageType = messageType;
return true;
}
return false;
}
bool MessageQueue::isEmpty()
{
int free = getNumberFree();
if(free==capacity()) return true;
return false;
}
bool MessageQueue::isFull()
{
if(getNumberFree()==0) return true;
return false;
}
int MessageQueue::getClearOverrun()
{
int num = overrun;
overrun = 0;
return num;
}
MessageQueuePtr createMessageQueue(int size)
{
MessageNodePtrArray nodeArray;
nodeArray.reserve(size);
for(int i=0; i<size; i++) {
nodeArray.push_back(
MessageNodePtr(new MessageNode()));
}
return std::tr1::shared_ptr<MessageQueue>(new MessageQueue(nodeArray));
}
}}

View File

@@ -1,332 +0,0 @@
#ifndef PV_ANYSCALAR_H
#define PV_ANYSCALAR_H
#if __cplusplus>=201103L
# include <type_traits>
#endif
#include <ostream>
#include <exception>
#include <map>
#include <epicsAssert.h>
#include <pv/templateMeta.h>
#include <pv/typeCast.h>
#include <pv/pvIntrospect.h> /* for ScalarType enum */
namespace epics{namespace pvData{
namespace detail {
// special mangling for AnyScalar ctor to map from argument type to storage type.
// allow construction from constants.
template <typename T>
struct any_storage_type { typedef T type; };
template<> struct any_storage_type<int> { typedef int32 type; };
template<> struct any_storage_type<unsigned> { typedef uint32 type; };
template<> struct any_storage_type<char*> { typedef std::string type; };
template<> struct any_storage_type<const char*> { typedef std::string type; };
#if __cplusplus>=201103L
// std::max() isn't constexpr until c++14 :(
constexpr size_t cmax(size_t A, size_t B) {
return A>B ? A : B;
}
#endif
}// namespace detail
/** A type-safe variant union capable of holding
* any of the PVD scalar types (POD or string)
*
@code
AnyScalar A(5);
assert(A.type()==pvInt);
assert(A.ref<int32>()==5);
assert(A.as<int32>()==5);
assert(A.as<double>()==5.0);
assert(A.ref<double>()==5.0); // throws AnyScalar::bad_cast
@endcode
*/
class AnyScalar {
public:
struct bad_cast : public std::exception {
#if __cplusplus>=201103L
bad_cast() noexcept {}
virtual ~bad_cast() noexcept {}
virtual const char* what() const noexcept
#else
bad_cast() throw() {}
virtual ~bad_cast() throw() {}
virtual const char* what() const throw()
#endif
{ return "bad_cast() type mis-match"; }
};
private:
ScalarType _stype;
// always reserve enough storage for std::string or double (assumed worst case)
#if __cplusplus>=201103L
struct wrap_t {
typename std::aligned_storage<detail::cmax(sizeof(std::string), sizeof(double)),
detail::cmax(alignof(std::string), alignof(double))
>::type blob[1];
} _wrap;
#else
struct wrap_t {
union blob_t {
char data[sizeof(std::string)];
double align_f; // assume std::string alignment <= 8
} blob[1];
} _wrap;
#endif
// assumed largest non-string type
typedef double _largest_blob;
template<typename T>
inline T& _as() {
return *reinterpret_cast<T*>(_wrap.blob);
}
template<typename T>
inline const T& _as() const {
return *reinterpret_cast<const T*>(_wrap.blob);
}
public:
AnyScalar() : _stype((ScalarType)-1) {}
template<typename T>
explicit AnyScalar(T v)
{
typedef typename meta::strip_const<T>::type T2;
typedef typename detail::any_storage_type<T2>::type TT;
STATIC_ASSERT(sizeof(TT)<=sizeof(_wrap.blob));
new (_wrap.blob) TT(v);
// this line fails to compile when type T can't be mapped to one of
// the PVD scalar types.
_stype = (ScalarType)ScalarTypeID<TT>::value;
}
AnyScalar(const AnyScalar& o)
:_stype(o._stype)
{
if(o._stype==pvString) {
new (_wrap.blob) std::string(o._as<std::string>());
} else if(o._stype!=(ScalarType)-1) {
memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob));
}
}
#if __cplusplus>=201103L
AnyScalar(AnyScalar&& o)
:_stype(o._stype)
{
typedef std::string string;
if(o._stype==pvString) {
new (_wrap.blob) std::string();
_as<std::string>() = std::move(o._as<std::string>());
o._as<string>().~string();
} else if(o._stype!=(ScalarType)-1) {
memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob));
}
o._stype = (ScalarType)-1;
}
#endif
~AnyScalar() {
if(_stype==pvString) {
typedef std::string string;
_as<string>().~string();
}
// other types need no cleanup
}
AnyScalar& operator=(const AnyScalar& o) {
AnyScalar(o).swap(*this);
return *this;
}
template<typename T>
AnyScalar& operator=(T v) {
AnyScalar(v).swap(*this);
return *this;
}
#if __cplusplus>=201103L
AnyScalar& operator=(AnyScalar&& o) {
if(_stype==pvString) {
typedef std::string string;
_as<string>().~string();
}
_stype = (ScalarType)-1;
swap(o);
return *this;
}
#endif
void swap(AnyScalar& o) {
typedef std::string string;
switch((int)_stype) {
case -1:
switch((int)o._stype) {
case -1:
// nil <-> nil
break;
case pvString:
// nil <-> string
new (_wrap.blob) std::string();
_as<std::string>().swap(o._as<std::string>());
o._as<std::string>().~string();
break;
default:
// nil <-> non-string
memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob));
break;
}
break;
case pvString:
switch((int)o._stype) {
case -1:
// string <-> nil
new (o._wrap.blob) std::string();
_as<std::string>().swap(o._as<std::string>());
_as<std::string>().~string();
break;
case pvString:
// string <-> string
_as<std::string>().swap(o._as<std::string>());
break;
default: {
// string <-> non-string
std::string temp;
temp.swap(_as<std::string>());
_as<std::string>().~string();
memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob));
new (o._wrap.blob) std::string();
temp.swap(o._as<std::string>());
}
break;
}
break;
default:
switch((int)o._stype) {
case -1:
// non-string <-> nil
memcpy(o._wrap.blob, _wrap.blob, sizeof(_largest_blob));
break;
case pvString: {
// non-string <-> string
std::string temp;
temp.swap(o._as<std::string>());
o._as<std::string>().~string();
memcpy(o._wrap.blob, _wrap.blob, sizeof(_largest_blob));
new (_wrap.blob) std::string();
temp.swap(_as<std::string>());
}
break;
default:
// non-string <-> non-string
_largest_blob temp;
memcpy(&temp, _wrap.blob, sizeof(_largest_blob));
memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob));
memcpy(o._wrap.blob, &temp, sizeof(_largest_blob));
// std::swap(o._wrap.blob, _wrap.blob); // gcc <=4.3 doesn't like this
break;
}
break;
}
std::swap(_stype, o._stype);
}
inline ScalarType type() const {
return _stype;
}
void* unsafe() { return _wrap.blob; }
const void* unsafe() const { return _wrap.blob; }
inline bool empty() const { return _stype==(ScalarType)-1; }
#if __cplusplus>=201103L
explicit operator bool() const { return !empty(); }
#else
private:
typedef void (AnyScalar::*bool_type)(AnyScalar&);
public:
operator bool_type() const { return !empty() ? &AnyScalar::swap : 0; }
#endif
/** Return reference to wrapped value */
template<typename T>
// T -> strip_const -> map to storage type -> add reference
typename detail::any_storage_type<typename meta::strip_const<T>::type>::type&
ref() {
typedef typename meta::strip_const<T>::type T2;
typedef typename detail::any_storage_type<T2>::type TT;
if(_stype!=(ScalarType)ScalarTypeID<TT>::value)
throw bad_cast();
return _as<TT>();
}
template<typename T>
// T -> strip_const -> map to storage type -> add const reference
typename meta::decorate_const<typename detail::any_storage_type<typename meta::strip_const<T>::type>::type>::type&
ref() const {
typedef typename meta::strip_const<T>::type T2;
typedef typename detail::any_storage_type<T2>::type TT;
if(_stype!=(ScalarType)ScalarTypeID<TT>::value)
throw bad_cast();
return _as<TT>();
}
/** copy out wrapped value, with a value conversion. */
template<typename T>
T as() const {
typedef typename meta::strip_const<T>::type T2;
typedef typename detail::any_storage_type<T2>::type TT;
if(_stype==(ScalarType)-1)
throw bad_cast();
TT ret;
castUnsafeV(1, (ScalarType)ScalarTypeID<T2>::value, &ret,
_stype, _wrap.blob);
return ret;
}
private:
friend std::ostream& operator<<(std::ostream& strm, const AnyScalar& v);
};
inline
std::ostream& operator<<(std::ostream& strm, const AnyScalar& v)
{
switch(v.type()) {
#define CASE(BASETYPE, PVATYPE, DBFTYPE, PVACODE) case pv ## PVACODE: strm<<v._as<PVATYPE>(); break;
#define CASE_REAL_INT64
#define CASE_STRING
#include "pv/typemap.h"
#undef CASE
#undef CASE_REAL_INT64
#undef CASE_STRING
default:
strm<<"(nil)"; break;
}
return strm;
}
}} // namespace epics::pvData
#endif // PV_ANYSCALAR_H

View File

@@ -9,10 +9,6 @@
#ifndef BITSET_H
#define BITSET_H
#if __cplusplus>=201103L
# include <initializer_list>
#endif
#include <vector>
#include <pv/pvType.h>
@@ -69,16 +65,6 @@ namespace epics { namespace pvData {
*/
BitSet(uint32 nbits);
#if __cplusplus>=201103L
/** Initialize from a list of indicies
@code
BitSet X({1, 5});
assert(X.get(1) && X.get(5));
@endcode
*/
BitSet(std::initializer_list<uint32> I);
#endif
/**
* Destructor.
*/
@@ -90,21 +76,21 @@ namespace epics { namespace pvData {
*
* @param bitIndex the index of the bit to flip
*/
BitSet& flip(uint32 bitIndex);
void flip(uint32 bitIndex);
/**
* Sets the bit at the specified index to @c true.
*
* @param bitIndex a bit index
*/
BitSet& set(uint32 bitIndex);
void set(uint32 bitIndex);
/**
* Sets the bit specified by the index to @c false.
*
* @param bitIndex the index of the bit to be cleared
*/
BitSet& clear(uint32 bitIndex);
void clear(uint32 bitIndex);
/**
* Sets the bit at the specified index to the specified value.
@@ -181,13 +167,8 @@ namespace epics { namespace pvData {
*/
uint32 size() const;
//! Returns true if any bit is set in both *this and other
bool logical_and(const BitSet& other) const;
//! Returns true if any bit is set in both *this or other
bool logical_or(const BitSet& other) const;
/**
* Performs a bitwise <b>AND</b> of this target bit set with the
* Performs a logical <b>AND</b> of this target bit set with the
* argument bit set. This bit set is modified so that each bit in it
* has the value @c true if and only if it both initially
* had the value @c true and the corresponding bit in the
@@ -198,7 +179,7 @@ namespace epics { namespace pvData {
BitSet& operator&=(const BitSet& set);
/**
* Performs a bitwise <b>OR</b> of this bit set with the bit set
* Performs a logical <b>OR</b> of this bit set with the bit set
* argument. This bit set is modified so that a bit in it has the
* value @c true if and only if it either already had the
* value @c true or the corresponding bit in the bit set
@@ -209,7 +190,7 @@ namespace epics { namespace pvData {
BitSet& operator|=(const BitSet& set);
/**
* Performs a bitwise <b>XOR</b> of this bit set with the bit set
* Performs a logical <b>XOR</b> of this bit set with the bit set
* argument. This bit set is modified so that a bit in it has the
* value @c true if and only if one of the following
* statements holds:

View File

@@ -15,153 +15,179 @@
#include <epicsEndian.h>
#include <shareLib.h>
#include <epicsAssert.h>
#include <pv/templateMeta.h>
#include <pv/pvType.h>
#include <pv/epicsException.h>
#ifndef EPICS_ALWAYS_INLINE
# define EPICS_ALWAYS_INLINE inline
#endif
namespace epics {
namespace pvData {
/*
TODO can be used:
/* various compilers provide builtins for byte order swaps.
* conditions based on boost endian library
*/
#if defined(__GNUC__) && ((__GNUC__>4) || (__GNUC__==4 && __GNUC_MINOR__>=3))
MS Visual C++:
#if (__GNUC__>4) || (__GNUC__==4 && __GNUC_MINOR__>=8)
#define _PVA_swap16(X) __builtin_bswap16(X)
#endif
You include intrin.h and call the following functions:
#define _PVA_swap32(X) __builtin_bswap32(X)
#define _PVA_swap64(X) __builtin_bswap64(X)
For 16 bit numbers:
unsigned short _byteswap_ushort(unsigned short value);
#elif defined(__clang__)
For 32 bit numbers:
unsigned long _byteswap_ulong(unsigned long value);
#if __has_builtin(__builtin_bswap16)
#define _PVA_swap16(X) __builtin_bswap16(X)
#endif
#if __has_builtin(__builtin_bswap32)
#define _PVA_swap32(X) __builtin_bswap32(X)
#endif
#if __has_builtin(__builtin_bswap64)
#define _PVA_swap64(X) __builtin_bswap64(X)
#endif
For 64 bit numbers:
unsigned __int64 _byteswap_uint64(unsigned __int64 value);
*/
#elif defined(_MSC_VER)
/*
For floats and doubles it's more difficult as with plain integers as these may or not may be in the host machines byte-order.
You can get little-endian floats on big-endian machines and vice versa.
*/
#define _PVA_swap16(X) _byteswap_ushort(X)
#define _PVA_swap32(X) _byteswap_ulong(X)
#define _PVA_swap64(X) _byteswap_uint64(X)
#endif
namespace epics {namespace pvData {
#define GCC_VERSION_SINCE(major, minor, patchlevel) \
(defined(__GNUC__) && !defined(__INTEL_COMPILER) && \
((__GNUC__ > (major)) || \
(__GNUC__ == (major) && __GNUC_MINOR__ > (minor)) || \
(__GNUC__ == (major) && __GNUC_MINOR__ == (minor) && __GNUC_PATCHLEVEL__ >= (patchlevel))))
#if GCC_VERSION_SINCE(4,3,0)
namespace detail {
template<typename T>
struct asInt {
static EPICS_ALWAYS_INLINE T from(T v) { return v; }
static EPICS_ALWAYS_INLINE T to(T v) { return v; }
};
template<>
struct asInt<float> {
union pun {float f; uint32 i;};
static EPICS_ALWAYS_INLINE float from(uint32 v) {
pun P;
P.i = v;
return P.f;
}
static EPICS_ALWAYS_INLINE uint32 to(float v) {
pun P;
P.f = v;
return P.i;
}
};
template<>
struct asInt<double> {
union pun {double f; uint64 i;};
static EPICS_ALWAYS_INLINE double from(uint64 v) {
pun P;
P.i = v;
return P.f;
}
static EPICS_ALWAYS_INLINE uint64 to(double v) {
pun P;
P.f = v;
return P.i;
}
};
#define swap32(x) __builtin_bswap32(x)
#define swap64(x) __builtin_bswap64(x)
template<int N>
struct swap; // no default
template<>
struct swap<1> {
static EPICS_ALWAYS_INLINE uint8 op(uint8 v) { return v; }
};
template<>
struct swap<2> {
static EPICS_ALWAYS_INLINE uint16 op(uint16 v) {
#ifdef _PVA_swap16
return _PVA_swap16(v);
#else
return (((v) >> 8) | ((v) << 8));
#endif
}
};
template<>
struct swap<4> {
static EPICS_ALWAYS_INLINE uint32 op(uint32 v) {
#ifdef _PVA_swap32
return _PVA_swap32(v);
#else
return ((((v) & 0xff000000) >> 24) |
(((v) & 0x00ff0000) >> 8) |
(((v) & 0x0000ff00) << 8) |
(((v) & 0x000000ff) << 24));
#endif
}
};
template<>
struct swap<8> {
#ifdef _PVA_swap64
static EPICS_ALWAYS_INLINE uint64 op(uint64 v) {
return _PVA_swap64(v);
}
#else
static inline uint64 op(uint64 v) {
return (((v) >> 56) | \
(((v) >> 40) & 0x0000ff00) | \
(((v) >> 24) & 0x00ff0000) | \
(((v) >> 8) & 0xff000000) | \
(((v) << 8) & ((uint64_t)0xff << 32)) | \
(((v) << 24) & ((uint64_t)0xff << 40)) | \
(((v) << 40) & ((uint64_t)0xff << 48)) | \
(((v) << 56)));
}
#endif
};
#define __byte_swap16(x) \
(((x) >> 8) | \
((x) << 8))
#undef _PVA_swap16
#undef _PVA_swap32
#undef _PVA_swap64
} // namespace detail
//! Unconditional byte order swap.
//! defined for integer and floating point types
template<typename T>
EPICS_ALWAYS_INLINE T swap(T val)
static inline uint16_t
swap16(uint16_t _x)
{
return detail::asInt<T>::from(
detail::swap<sizeof(T)>::op(
detail::asInt<T>::to(val)));
return (__byte_swap16(_x));
}
#else
#define __byte_swap16(x) \
(((x) >> 8) | \
((x) << 8))
#define __byte_swap32(x) \
((((x) & 0xff000000) >> 24) | \
(((x) & 0x00ff0000) >> 8) | \
(((x) & 0x0000ff00) << 8) | \
(((x) & 0x000000ff) << 24))
#define __byte_swap64(x) \
(((x) >> 56) | \
(((x) >> 40) & 0xff00) | \
(((x) >> 24) & 0xff0000) | \
(((x) >> 8) & 0xff000000) | \
(((x) << 8) & ((uint64_t)0xff << 32)) | \
(((x) << 24) & ((uint64_t)0xff << 40)) | \
(((x) << 40) & ((uint64_t)0xff << 48)) | \
(((x) << 56)))
static inline uint16_t
swap16(uint16_t _x)
{
return (__byte_swap16(_x));
}
static inline uint32_t
swap32(uint32_t _x)
{
return (__byte_swap32(_x));
}
static inline uint64_t
swap64(uint64_t _x)
{
return (__byte_swap64(_x));
}
#endif
template<typename T>
inline T swap(T val) { return val; } // not valid
template<>
inline int16 swap(int16 val)
{
return swap16(val);
}
template<>
inline int32 swap(int32 val)
{
return swap32(val);
}
template<>
inline int64 swap(int64 val)
{
return swap64(val);
}
template<>
inline uint16 swap(uint16 val)
{
return swap16(val);
}
template<>
inline uint32 swap(uint32 val)
{
return swap32(val);
}
template<>
inline uint64 swap(uint64 val)
{
return swap64(val);
}
template<>
inline float swap(float val)
{
union {
int32 i;
float f;
} conv;
conv.f = val;
conv.i = swap32(conv.i);
return conv.f;
}
template<>
inline double swap(double val)
{
union {
int64 i;
double d;
} conv;
conv.d = val;
conv.i = swap64(conv.i);
return conv.d;
}
#define is_aligned(POINTER, BYTE_COUNT) \
(((std::size_t)(POINTER)) % (BYTE_COUNT) == 0)
(((std::ptrdiff_t)(const void *)(POINTER)) % (BYTE_COUNT) == 0)
/*template <bool ENDIANESS_SUPPORT = false,
bool UNALIGNED_ACCESS = false,
bool ADAPTIVE_ACCESS = true,
bool USE_INLINE_MEMCPY = true>*/
#define ENDIANESS_SUPPORT true
#define UNALIGNED_ACCESS true
#define ADAPTIVE_ACCESS true
#define USE_INLINE_MEMCPY true
#if defined (__GNUC__) && (__GNUC__ < 3)
#define GET(T) get((T*)0)
@@ -252,18 +278,8 @@ public:
_limit = _buffer + _size;
}
/**
* Makes a buffer ready to read out previously written values.
*
* Typically _limit==_buffer+_size is the initial state, but this is not
* required.
*
* V _buffer V _position V _limit V _buffer+_size
* |_______written_______|____uninitialized___|____allocated___|
*
* becomes
*
* V _buffer/_position V _limit V _buffer+size
* |_______written_______|________________allocated____________|
* Makes a buffer ready for a new sequence of channel-write or relative get operations:
* It sets the limit to the current position and then sets the position to zero.
*/
inline void flip() {
_limit = _position;
@@ -272,16 +288,6 @@ public:
/**
* Makes a buffer ready for re-reading the data that it already contains:
* It leaves the limit unchanged and sets the position to zero.
*
* Note that this may allow reading of uninitialized values. flip() should be considered
*
* V _buffer V _position V _limit V _buffer+_size
* |_______written_______|____uninitialized___|____allocated___|
*
* becomes
*
* V _buffer/_position V _limit V _buffer+size
* |_______written_______|____uninitialized___|____allocated___|
*/
inline void rewind() {
_position = _buffer;
@@ -292,7 +298,7 @@ public:
*/
inline std::size_t getPosition() const
{
return _position - _buffer;
return (std::size_t)(((std::ptrdiff_t)(const void *)_position) - ((std::ptrdiff_t)(const void *)_buffer));
}
/**
* Sets the buffer position.
@@ -303,9 +309,7 @@ public:
*/
inline void setPosition(std::size_t pos)
{
assert(pos<=_size);
_position = _buffer + pos;
assert(_position<=_limit);
}
/**
* Returns this buffer's limit.
@@ -314,7 +318,7 @@ public:
*/
inline std::size_t getLimit() const
{
return _limit - _buffer;
return (std::size_t)(((std::ptrdiff_t)(const void *)_limit) - ((std::ptrdiff_t)(const void *)_buffer));
}
/**
* Sets this buffer's limit.
@@ -326,9 +330,7 @@ public:
*/
inline void setLimit(std::size_t limit)
{
assert(limit<=_size);
_limit = _buffer + limit;
assert(_position<=_limit);
}
/**
* Returns the number of elements between the current position and the limit.
@@ -337,7 +339,7 @@ public:
*/
inline std::size_t getRemaining() const
{
return _limit - _position;
return (std::size_t)(((std::ptrdiff_t)(const void *)_limit) - ((std::ptrdiff_t)(const void *)_position));
}
/**
* Returns The size, i.e. capacity of the raw data buffer in bytes.
@@ -362,7 +364,7 @@ public:
* @param value The value to be put into the byte buffer.
*/
template<typename T>
inline void put(std::size_t index, T value) const;
inline void put(std::size_t index, T value);
/**
* Get the new object from the byte buffer. The item MUST have type @c T.
* The position is adjusted based on the type.
@@ -385,18 +387,17 @@ public:
* @return The object.
*/
template<typename T>
inline T get(std::size_t index) const;
inline T get(std::size_t index);
/**
* Put a sub-array of bytes into the byte buffer.
* The position is increased by the count.
*
* @param src The source array.
* @param src_offset The starting position within src.
* @param count The number of bytes to put into the byte buffer.
* Must be less than getRemaining()
* @param count The number of bytes to put into the byte buffer,
*/
inline void put(const char* src, std::size_t src_offset, std::size_t count) {
assert(count<=getRemaining());
//if(count>getRemaining()) THROW_BASE_EXCEPTION("buffer overflow");
memcpy(_position, src + src_offset, count);
_position += count;
}
@@ -407,10 +408,9 @@ public:
* @param dest The destination array.
* @param dest_offset The starting position within src.
* @param count The number of bytes to put into the byte buffer.
* Must be less than getRemaining()
*/
inline void get(char* dest, std::size_t dest_offset, std::size_t count) {
assert(count<=getRemaining());
//if(count>getRemaining()) THROW_BASE_EXCEPTION("buffer overflow");
memcpy(dest + dest_offset, _position, count);
_position += count;
}
@@ -437,26 +437,19 @@ public:
* @return (false,true) if (is, is not) the EPICS_BYTE_ORDER
*/
template<typename T>
EPICS_ALWAYS_INLINE bool reverse() const
inline bool reverse() const
{
return sizeof(T)>1 && _reverseEndianess;
return _reverseEndianess;
}
/**
* Adjust position to the next multiple of 'size.
* @param size The alignment requirement, must be a power of 2. (unchecked)
* @param fill value to use for padding bytes (default '\0').
*
* @note This alignment is absolute, not necessarily with respect to _buffer.
* Adjust position so that it is aligned to the specified size.
* Size MUST be a power of 2.
* @param size The alignment requirement.
*/
inline void align(std::size_t size, char fill='\0')
inline void align(std::size_t size)
{
const std::size_t k = size - 1, bufidx = (std::size_t)_position;
if(bufidx&k) {
std::size_t npad = size-(bufidx&k);
assert(npad<=getRemaining());
std::fill(_position, _position+npad, fill);
_position += npad;
}
const std::size_t k = size - 1;
_position = (char*)((((std::ptrdiff_t)(const void *)_position) + k) & ~(k));
}
/**
* Put a boolean value into the byte buffer.
@@ -694,26 +687,96 @@ private:
template<typename T>
inline void ByteBuffer::put(T value)
{
assert(sizeof(T)<=getRemaining());
// this avoids int8 specialization, compiler will take care if optimization, -O2 or more
if (sizeof(T) == 1)
{
*(_position++) = (int8)value;
return;
}
if(reverse<T>())
if (ENDIANESS_SUPPORT && reverse<T>())
{
value = swap<T>(value);
}
if (UNALIGNED_ACCESS)
{
// NOTE: some CPU handle unaligned access pretty good (e.g. x86)
*((T*)_position) = value;
_position += sizeof(T);
}
else
{
// NOTE: this check and branching does not always pay off
if (ADAPTIVE_ACCESS && is_aligned(_position, sizeof(T)))
{
*((T*)_position) = value;
_position += sizeof(T);
}
else
{
if (USE_INLINE_MEMCPY)
{
// NOTE: it turns out that this compiler can optimize this with inline code, e.g. gcc
memcpy(_position, &value, sizeof(T));
_position += sizeof(T);
}
else
{
// NOTE: compiler should optimize this and unroll the loop
for (size_t i = 0; i < sizeof(T); i++)
_position[i] = ((char*)&value)[i];
_position += sizeof(T);
}
}
}
//assert(is_aligned(_position, sizeof(T)));
*((T*)_position) = value;
_position += sizeof(T);
}
template<typename T>
inline void ByteBuffer::put(std::size_t index, T value) const
inline void ByteBuffer::put(std::size_t index, T value)
{
assert(_buffer+index<=_limit);
// this avoids int8 specialization, compiler will take care if optimization, -O2 or more
if (sizeof(T) == 1)
{
*(_buffer + index) = (int8)value;
return;
}
if(reverse<T>())
if (ENDIANESS_SUPPORT && reverse<T>())
{
value = swap<T>(value);
}
if (UNALIGNED_ACCESS)
{
// NOTE: some CPU handle unaligned access pretty good (e.g. x86)
*((T*)(_buffer + index)) = value;
}
else
{
// NOTE: this check and branching does not always pay off
if (ADAPTIVE_ACCESS && is_aligned(_position, sizeof(T)))
{
*((T*)(_buffer + index)) = value;
}
else
{
if (USE_INLINE_MEMCPY)
{
// NOTE: it turns out that this compiler can optimize this with inline code, e.g. gcc
memcpy(_buffer + index, &value, sizeof(T));
}
else
{
// NOTE: compiler should optimize this and unroll the loop
char *p = _buffer + index;
for (size_t i = 0; i < sizeof(T); i++)
p[i] = ((char*)&value)[i];
}
}
}
//assert(is_aligned(_buffer+index, sizeof(T))); //TODO: special case for targets which support unaligned access
*((T*)(_buffer+index)) = value;
}
#if defined (__GNUC__) && (__GNUC__ < 3)
@@ -724,65 +787,160 @@ private:
inline T ByteBuffer::get()
#endif
{
assert(sizeof(T)<=getRemaining());
// this avoids int8 specialization, compiler will take care if optimization, -O2 or more
if (sizeof(T) == 1)
{
return (int8)(*(_position++));
}
//assert(is_aligned(_position, sizeof(T)));
T value = *((T*)_position);
_position += sizeof(T);
if(reverse<T>())
T value;
if (UNALIGNED_ACCESS)
{
// NOTE: some CPU handle unaligned access pretty good (e.g. x86)
value = *((T*)_position);
_position += sizeof(T);
}
else
{
// NOTE: this check and branching does not always pay off
if (ADAPTIVE_ACCESS && is_aligned(_position, sizeof(T)))
{
value = *((T*)_position);
_position += sizeof(T);
}
else
{
if (USE_INLINE_MEMCPY)
{
// NOTE: it turns out that this compiler can optimize this with inline code, e.g. gcc
memcpy(&value, _position, sizeof(T));
_position += sizeof(T);
}
else
{
// NOTE: compiler should optimize this and unroll the loop
for (size_t i = 0; i < sizeof(T); i++)
((char*)&value)[i] = _position[i];
_position += sizeof(T);
}
}
}
if (ENDIANESS_SUPPORT && reverse<T>())
{
value = swap<T>(value);
}
return value;
}
template<typename T>
inline T ByteBuffer::get(std::size_t index) const
inline T ByteBuffer::get(std::size_t index)
{
assert(_buffer+index<=_limit);
// this avoids int8 specialization, compiler will take care if optimization, -O2 or more
if (sizeof(T) == 1)
{
return (int8)(*(_buffer + index));
}
//assert(is_aligned(_position, sizeof(T)));
T value = *((T*)(_buffer + index));
if(reverse<T>())
T value;
if (UNALIGNED_ACCESS)
{
// NOTE: some CPU handle unaligned access pretty good (e.g. x86)
value = *((T*)(_buffer + index));
}
else
{
// NOTE: this check and branching does not always pay off
if (ADAPTIVE_ACCESS && is_aligned(_position, sizeof(T)))
{
value = *((T*)(_buffer + index));
}
else
{
if (USE_INLINE_MEMCPY)
{
// NOTE: it turns out that this compiler can optimize this with inline code, e.g. gcc
memcpy(&value, _buffer + index, sizeof(T));
}
else
{
// NOTE: compiler should optimize this and unroll the loop
char* p = _buffer + index;
for (size_t i = 0; i < sizeof(T); i++)
((char*)&value)[i] = p[i];
}
}
}
if (ENDIANESS_SUPPORT && reverse<T>())
{
value = swap<T>(value);
}
return value;
}
template<typename T>
inline void ByteBuffer::putArray(const T* values, std::size_t count)
{
// we require aligned arrays...
//assert(is_aligned(_position, sizeof(T)));
T* start = (T*)_position;
size_t n = sizeof(T)*count; // bytes
assert(n<=getRemaining());
if (reverse<T>()) {
for(std::size_t i=0; i<count; i++)
start[i] = swap<T>(values[i]);
} else {
memcpy(start, values, n);
// this avoids int8 specialization, compiler will take care if optimization, -O2 or more
if (sizeof(T) == 1)
{
put((const char*)values, 0, count);
return;
}
T* start = (T*)_position;
size_t n = sizeof(T)*count;
// we require aligned arrays...
memcpy(_position, values, n);
_position += n;
// ... so that we can be fast changing endianness
if (ENDIANESS_SUPPORT && reverse<T>())
{
for (std::size_t i = 0; i < count; i++)
{
*start = swap<T>(*start);
start++;
}
}
}
template<typename T>
inline void ByteBuffer::getArray(T* values, std::size_t count)
{
// we require aligned arrays...
//assert(is_aligned(_position, sizeof(T)));
const T* start = (T*)_position;
size_t n = sizeof(T)*count; // bytes
assert(n<=getRemaining());
if (reverse<T>()) {
for(std::size_t i=0; i<count; i++)
values[i] = swap<T>(start[i]);
} else {
memcpy(values, start, n);
// this avoids int8 specialization, compiler will take care if optimization, -O2 or more
if (sizeof(T) == 1)
{
get((char*)values, 0, count);
return;
}
T* start = (T*)values;
size_t n = sizeof(T)*count;
// we require aligned arrays...
memcpy(values, _position, n);
_position += n;
// ... so that we can be fast changing endianness
if (ENDIANESS_SUPPORT && reverse<T>())
{
for (std::size_t i = 0; i < count; i++)
{
*start = swap<T>(*start);
start++;
}
}
}
}}
}
}
#endif /* BYTEBUFFER_H */

View File

@@ -1,328 +0,0 @@
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
/* Author: Michael Davidsaver */
/* wrapper around shared_ptr which tracks backwards references.
* Can help to find ref. leaks, loops, and other exciting bugs.
* See comments in sharedPtr.h
*/
#ifndef DEBUGPTR_H
#define DEBUGPTR_H
#if __cplusplus<201103L
# error c++11 required
#endif
#include <ostream>
#include <memory>
#include <set>
#include <pv/epicsException.h>
#include <shareLib.h>
//! User code should test this macro
//! before calling epics::debug::shared_ptr::show_refs()
#define HAVE_SHOW_REFS
namespace epics {
namespace debug {
struct tracker;
class shared_ptr_base;
class epicsShareClass ptr_base {
friend class shared_ptr_base;
template<typename A>
friend class shared_ptr;
template<typename A>
friend class weak_ptr;
protected:
typedef std::shared_ptr<tracker> track_t;
track_t track;
ptr_base() noexcept : track() {}
ptr_base(const track_t& track) :track(track) {}
ptr_base(const ptr_base&) = delete;
ptr_base(ptr_base&&) = delete;
ptr_base& operator=(const ptr_base&) = delete;
public:
typedef std::set<const shared_ptr_base *> ref_set_t;
void show_refs(std::ostream&, bool self=true, bool weak=false) const;
void spy_refs(ref_set_t&) const;
};
class epicsShareClass weak_ptr_base : public ptr_base {
protected:
weak_ptr_base() {}
weak_ptr_base(const track_t& track) :ptr_base(track) {}
};
class epicsShareClass shared_ptr_base : public ptr_base {
protected:
shared_ptr_base() noexcept
#ifndef EXCEPT_USE_NONE
:m_stack(), m_depth(0)
#endif
{}
shared_ptr_base(const track_t& track) :ptr_base(track)
#ifndef EXCEPT_USE_NONE
,m_stack(), m_depth(0)
#endif
{}
~shared_ptr_base() {track_clear();}
// add ourselves to tracker
void track_new();
// create new tracker if ptr!=nullptr, otherwise clear
void track_new(void* ptr);
// copy tracker and add ourself
void track_assign(const shared_ptr_base& o);
void track_clear();
void swap(shared_ptr_base& o);
void snap_stack();
#ifndef EXCEPT_USE_NONE
void *m_stack[EXCEPT_DEPTH];
int m_depth; // always <= EXCEPT_DEPTH
#endif
public:
void show_stack(std::ostream&) const;
};
template<typename T>
class shared_ptr;
template<typename T>
class weak_ptr;
template<class Base>
class enable_shared_from_this;
template<typename Store, typename Actual>
inline void
do_enable_shared_from_this(const shared_ptr<Store>& dest,
enable_shared_from_this<Actual>* self
);
template<typename T>
inline void
do_enable_shared_from_this(const shared_ptr<T>&, ...) {}
template<typename T>
class shared_ptr : public shared_ptr_base {
typedef ::std::shared_ptr<T> real_type;
real_type real;
template<typename A>
friend class shared_ptr;
template<typename A>
friend class weak_ptr;
// ctor for casts
shared_ptr(const real_type& r, const ptr_base::track_t& t)
:shared_ptr_base(t), real(r)
{track_new();}
public:
typedef typename real_type::element_type element_type;
typedef weak_ptr<T> weak_type;
// new NULL
shared_ptr() noexcept {}
// copy existing same type
shared_ptr(const shared_ptr& o) :shared_ptr_base(o.track), real(o.real) {track_new();}
// copy existing of implicitly castable type
template<typename A>
shared_ptr(const shared_ptr<A>& o) :shared_ptr_base(o.track), real(o.real) {track_new();}
// construct around new pointer
template<typename A, class ... Args>
explicit shared_ptr(A* a, Args ... args) : shared_ptr_base(), real(a, args...) {
track_new(a);
do_enable_shared_from_this(*this, a);
}
// make strong ref from weak
template<typename A>
shared_ptr(const weak_ptr<A>& o) :shared_ptr_base(o.track), real(o.real) {track_new();}
// takeover from unique_ptr
template<typename A>
shared_ptr(std::unique_ptr<A>&& a) : shared_ptr_base(), real(a.release()) {track_new();}
~shared_ptr() {}
shared_ptr& operator=(const shared_ptr& o) {
if(this!=&o) {
real = o.real;
track_assign(o);
}
return *this;
}
template<typename A>
shared_ptr& operator=(const shared_ptr<A>& o) {
if(get()!=o.get()) {
real = o.real;
track_assign(o);
}
return *this;
}
void reset() noexcept { real.reset(); track_clear(); }
template<typename A, class ... Args>
void reset(A* a, Args ... args)
{
real.reset(a, args...);
track_new(a);
do_enable_shared_from_this(*this, a);
}
void swap(shared_ptr &o) noexcept
{
if(this!=&o) {
real.swap(o.real);
shared_ptr_base::swap(o);
}
}
// proxy remaining to underlying shared_ptr
T* get() const noexcept { return real.get(); }
typename std::add_lvalue_reference<T>::type operator*() const noexcept { return *real; }
T* operator->() const noexcept { return real.get(); }
long use_count() const noexcept { return real.use_count(); }
bool unique() const noexcept { return real.unique(); }
explicit operator bool() const noexcept { return bool(real); }
bool operator==(const shared_ptr<T>& o) const { return real==o.real; }
bool operator!=(const shared_ptr<T>& o) const { return real!=o.real; }
bool operator<(const shared_ptr<T>& o) const { return real<o.real; }
template<typename A>
bool owner_before(const shared_ptr<A>& o) { return real.owner_before(o); }
template<typename A>
bool owner_before(const weak_ptr<A>& o) { return real.owner_before(o); }
template<typename TO, typename FROM>
friend
shared_ptr<TO> static_pointer_cast(const shared_ptr<FROM>& src);
template<typename TO, typename FROM>
friend
shared_ptr<TO> const_pointer_cast(const shared_ptr<FROM>& src);
template<typename TO, typename FROM>
friend
shared_ptr<TO> dynamic_pointer_cast(const shared_ptr<FROM>& src);
template<typename Store, typename Actual>
friend void
do_enable_shared_from_this(const shared_ptr<Store>& dest,
enable_shared_from_this<Actual>* self
);
};
template<typename TO, typename FROM>
shared_ptr<TO> static_pointer_cast(const shared_ptr<FROM>& src) {
return shared_ptr<TO>(std::static_pointer_cast<TO>(src.real), src.track);
}
template<typename TO, typename FROM>
shared_ptr<TO> const_pointer_cast(const shared_ptr<FROM>& src) {
return shared_ptr<TO>(std::const_pointer_cast<TO>(src.real), src.track);
}
template<typename TO, typename FROM>
shared_ptr<TO> dynamic_pointer_cast(const shared_ptr<FROM>& src) {
return shared_ptr<TO>(std::dynamic_pointer_cast<TO>(src.real), src.track);
}
template<typename T>
class weak_ptr : public weak_ptr_base {
typedef ::std::weak_ptr<T> real_type;
real_type real;
template<typename A>
friend class shared_ptr;
template<typename A>
friend class weak_ptr;
public:
typedef typename real_type::element_type element_type;
typedef weak_ptr<T> weak_type;
// new NULL
weak_ptr() noexcept {}
// copy existing same type
weak_ptr(const weak_ptr& o) :weak_ptr_base(o.track), real(o.real) {}
// copy existing of similar type
template<typename A>
weak_ptr(const weak_ptr<A>& o) :weak_ptr_base(o.track), real(o.real) {}
// create week ref from strong ref
template<typename A>
weak_ptr(const shared_ptr<A>& o) :weak_ptr_base(o.track), real(o.real) {}
~weak_ptr() {}
weak_ptr& operator=(const weak_ptr& o) {
if(this!=&o) {
real = o.real;
track = o.track;
}
return *this;
}
template<typename A>
weak_ptr& operator=(const shared_ptr<A>& o) {
real = o.real;
track = o.track;
return *this;
}
shared_ptr<T> lock() const noexcept { return shared_ptr<T>(real.lock(), track); }
void reset() noexcept { track.reset(); real.reset(); }
long use_count() const noexcept { return real.use_count(); }
bool unique() const noexcept { return real.unique(); }
};
template<class Base>
class enable_shared_from_this {
mutable weak_ptr<Base> xxInternalSelf;
template<typename Store, typename Actual>
friend
void
do_enable_shared_from_this(const shared_ptr<Store>& dest,
enable_shared_from_this<Actual>* self
);
public:
shared_ptr<Base> shared_from_this() const {
return shared_ptr<Base>(xxInternalSelf);
}
};
template<typename Store, typename Actual>
inline void
do_enable_shared_from_this(const shared_ptr<Store>& dest,
enable_shared_from_this<Actual>* self
)
{
shared_ptr<Actual> actual(dynamic_pointer_cast<Actual>(dest));
if(!actual)
throw std::logic_error("epics::debug::enabled_shared_from_this fails");
self->xxInternalSelf = actual;
}
}} // namespace epics::debug
template<typename T>
inline std::ostream& operator<<(std::ostream& strm, const epics::debug::shared_ptr<T>& ptr)
{
strm<<ptr.get();
return strm;
}
#endif // DEBUGPTR_H

40
src/misc/pv/destroyable.h Normal file
View File

@@ -0,0 +1,40 @@
/* destroyable.h */
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
/**
* @author mse
*/
#ifndef DESTROYABLE_H
#define DESTROYABLE_H
#include <pv/sharedPtr.h>
#include <shareLib.h>
namespace epics { namespace pvData {
/**
* @brief Instance declaring destroy method.
*
* @author mse
*/
class epicsShareClass Destroyable {
public:
POINTER_DEFINITIONS(Destroyable);
/**
* Destroy this instance.
*/
virtual void destroy() = 0;
protected:
/**
* Do not allow delete on this instance and derived classes, destroy() must be used instead.
*/
virtual ~Destroyable() {};
};
}}
#endif /* DESTROYABLE_H */

View File

@@ -181,7 +181,7 @@ do { \
#define PRINT_EXCEPTION(EI) PRINT_EXCEPTION2(EI,stderr)
#if !defined(__GNUC__) || __GNUC__ < 4
#ifndef __GNUC__
# define SHOW_EXCEPTION(EI) ::epics::pvData::detail::showException(EI)
#else
# define SHOW_EXCEPTION(EI) \

View File

@@ -11,8 +11,6 @@
#include <memory>
#include <compilerDependencies.h>
#include <pv/pvType.h>
#include <pv/lock.h>
#include <pv/event.h>
@@ -32,7 +30,7 @@ typedef std::tr1::shared_ptr<Executor> ExecutorPtr;
* @brief A command to be called by Executor
*
*/
class epicsShareClass EPICS_DEPRECATED Command {
class epicsShareClass Command {
public:
POINTER_DEFINITIONS(Command);
/**
@@ -54,7 +52,7 @@ private:
* @brief A class that executes commands.
*
*/
class epicsShareClass EPICS_DEPRECATED Executor : public Runnable{
class epicsShareClass Executor : public Runnable{
public:
POINTER_DEFINITIONS(Executor);
/**

View File

@@ -33,8 +33,7 @@ typedef epicsMutex Mutex;
* This is based on item 14 of
* * Effective C++, Third Edition, Scott Meyers
*/
class Lock {
EPICS_NOT_COPYABLE(Lock)
class epicsShareClass Lock : private NoDefaultMethods {
public:
/**
* Constructor

128
src/misc/pv/messageQueue.h Normal file
View File

@@ -0,0 +1,128 @@
/* messageQueue.h */
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
/**
* @author mrk
*/
#ifndef MESSAGEQUEUE_H
#define MESSAGEQUEUE_H
#include <memory>
#include <vector>
#include <cstddef>
#include <stdexcept>
#include <pv/pvType.h>
#include <pv/requester.h>
#include <pv/queue.h>
#include <shareLib.h>
namespace epics { namespace pvData {
class MessageNode;
class MessageQueue;
typedef std::tr1::shared_ptr<MessageNode> MessageNodePtr;
typedef std::vector<MessageNodePtr> MessageNodePtrArray;
typedef std::tr1::shared_ptr<MessageQueue> MessageQueuePtr;
/**
* @brief A node that can be put on a MessageQueue.
*
*/
class epicsShareClass MessageNode {
public:
/**
* Constructor
*/
MessageNode();
/**
*
* Get the message value.
* @return The message value.
*/
std::string getMessage() const;
/**
* Get the message type.
* @return The message type which is defined in Requester.
*/
MessageType getMessageType() const;
private:
std::string message;
MessageType messageType;
friend class MessageQueue;
};
/**
* @brief A bounded queue for messages.
*
*
*/
class epicsShareClass MessageQueue : public Queue<MessageNode> {
public:
POINTER_DEFINITIONS(MessageQueue);
/**
* Factory method to create a MessageQueue.
* @param size The number of MessageNodes in the queue.
* @return shared_ptr to MessageQueue.
*/
static MessageQueuePtr create(int size);
/**
* Constructor
* @param nodeArray an array of shared_ptr to MessageNodes,
*/
MessageQueue(MessageNodePtrArray &nodeArray);
/**
* Destructor
*/
virtual ~MessageQueue();
/**
* get the next MessageNode of the queue.
* @return A shared_ptr to the MessageNode.
* This will be a null pointer if queue is empty.
* If get is successful then release for this MessageNode
* must be called before another get can be issued.
*/
MessageNodePtr &get();
/**
* Release the MessageNode that was returned by the previous call to get.
*/
void release();
/**
*
* put a message into the message queue
* @param message The message string.
* @param messageType The message type as defined in Requester,
* @param replaceLast If true and queue is full then replace.
* @return (false,true) if a message (was not, was) put in queiue.
*/
bool put(std::string message,MessageType messageType,bool replaceLast);
/**
* Is queue empty?
* @return (false,true) if (is not, is) empty.
*/
bool isEmpty() ;
/**
* Is queue full?
* @return (false,true) if (is not, is) full.
*/
bool isFull() ;
/**
*
* Clear number of times queue was overrun and return the number
* of times the queue was overrun.
*/
int getClearOverrun();
private:
MessageNodePtr nullNode;
MessageNodePtr lastGet;
MessageNodePtr lastPut;
uint32 overrun;
};
}}
#endif /* MESSAGEQUEUE_H */

View File

@@ -11,54 +11,30 @@
#include <shareLib.h>
/** @macro EPICS_NOT_COPYABLE(CLASS)
* @brief Disable implicit copyable
*
* Prevent the default copy constructor and assignment
* operator from being usable.
*
* For >= C++11 explicitly disable. Attempts to copy/assign will
* fail to compile.
*
* For C++98 make these private, and don't implement them.
* User code will fail to compile, implementation code will fail to link.
@code
struct MyClass {
EPICS_NOT_COPYABLE(MyClass)
public:
...
};
@code
*
* @note This macro contains 'private:'.
namespace epics { namespace pvData {
/* This is based on Item 6 of
* Effective C++, Third Edition, Scott Meyers
*/
#if __cplusplus>=201103L
# define EPICS_NOT_COPYABLE(CLASS) private: CLASS(const CLASS&) = delete; CLASS& operator=(const CLASS&) = delete;
#else
# define EPICS_NOT_COPYABLE(CLASS) private: CLASS(const CLASS&); CLASS& operator=(const CLASS&);
#endif
namespace epics { namespace pvData {
/**
* @brief Base class for not allowing default methods.
*
* Note that copy constructor a copy methods are declared private.
*
* @deprecated Deprecated in favor of EPICS_NOT_COPYABLE() pvDataCPP 7.0.0
*/
class NoDefaultMethods {
public:
NoDefaultMethods() {}
private:
#if __cplusplus>=201103L
NoDefaultMethods(const NoDefaultMethods&) = delete;
NoDefaultMethods & operator=(const NoDefaultMethods &) = delete;
#else
class epicsShareClass NoDefaultMethods {
protected:
/**
* Constructor
*/
NoDefaultMethods(){};
/**
* Destructor
*/
~NoDefaultMethods(){}
private:
// do not implement
NoDefaultMethods(const NoDefaultMethods&);
NoDefaultMethods & operator=(const NoDefaultMethods &);
#endif
};
}}

View File

@@ -1,188 +0,0 @@
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
#ifndef PVUNITTEST_H
#define PVUNITTEST_H
#include <sstream>
#include <typeinfo>
#include <epicsUnitTest.h>
#include <pv/sharedPtr.h>
#include <pv/epicsException.h>
#include <pv/pvData.h>
namespace detail {
template<class C, void (C::*M)()>
void test_method(const char *kname, const char *mname)
{
try {
testDiag("------- %s::%s --------", kname, mname);
C inst;
(inst.*M)();
} catch(std::exception& e) {
PRINT_EXCEPTION(e);
testAbort("unexpected exception: %s", e.what());
}
}
class epicsShareClass testPassx
{
std::ostringstream strm;
const bool dotest, pass;
bool alive;
public:
testPassx() :dotest(false), pass(false), alive(true) {}
explicit testPassx(bool r) :dotest(true), pass(r), alive(true) {}
~testPassx();
template<typename T>
inline testPassx& operator<<(T v) {
strm<<v;
return *this;
}
// allow testPassx to be returned
// move ctor masquerading as copy ctor
testPassx(testPassx& o);
private:
testPassx& operator=(const testPassx&);
};
template<typename LHS, typename RHS>
inline testPassx testEqualx(const char *nLHS, const char *nRHS, LHS l, 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)
{
return testPassx(l!=r)<<nLHS<<" ("<<l<<") != "<<nRHS<<" ("<<r<<")";
}
}//namespace detail
/** @defgroup testhelpers Unit testing helpers
*
* Helper functions for writing unit tests.
*
@include unittest.cpp
*
* @{
*/
/** Run a class method as a test.
*
* Each invocation of TEST_METHOD() constructs a new instance of 'klass' on the stack.
* Thus constructor and destructor can be used for common test setup and tear down.
@code
namespace { // anon
struct MyTest {
MyTest() { } // setup
~MyTest() { } // tear down
void test1() {}
void test2() {}
};
} // namespace anon
MAIN(somename) {
testPlan(0);
TEST_METHOD(MyTest, test1)
TEST_METHOD(MyTest, test2)
return testDone();
}
@endcode
*/
#define TEST_METHOD(klass, method) ::detail::test_method<klass, &klass::method>(#klass, #method)
/** Compare equality. print left and right hand values and expression strings
*
@code
int x=5;
testEqual(x, 5);
// prints "ok 1 - x (5) == 5 (5)\n"
testEqual(x, 6)<<" oops";
// prints "not ok 1 - x (5) == 6 (6) oops\n"
@endcode
*/
#define testEqual(LHS, RHS) ::detail::testEqualx(#LHS, #RHS, LHS, RHS)
#define testNotEqual(LHS, RHS) ::detail::testNotEqualx(#LHS, #RHS, LHS, RHS)
/** Pass/fail from boolean
*
@code
bool y=true;
testTrue(y);
// prints "ok 1 - y\n"
testTrue(!y)<<" oops";
// prints "not ok 1 - !y oops\n"
@endcode
*/
#define testTrue(B) ::detail::testPassx(!!(B))<<#B
/** Test that a given block throws an exception
*
@code
testThrows(std::runtime_error, somefunc(5))
@endcode
*/
#define testThrows(EXC, CODE) try{ CODE; testFail("unexpected success of " #CODE); }catch(EXC& e){testPass("catch expected exception: %s", e.what());}
/** Print test output w/o testing
*
@code
testShow()<<"Foo";
@endcode
*/
#define testShow() ::detail::testPassx()
/** Compare value of PVStructure field
*
@code
PVStructurePtr x(.....);
testFieldEqual<epics::pvData::PVInt>(x, "alarm.severity", 1);
@endcode
*/
template<typename PVD>
::detail::testPassx
testFieldEqual(const std::tr1::shared_ptr<const epics::pvData::PVStructure>& val, const char *name, typename PVD::value_type expect)
{
if(!val) {
return ::detail::testPassx(false)<<" null structure pointer";
}
typename PVD::const_shared_pointer fval(val->getSubField<PVD>(name));
if(!fval) {
epics::pvData::PVUnion::const_shared_pointer uval(val->getSubField<epics::pvData::PVUnion>(name));
if(uval)
fval = uval->get<PVD>();
}
if(!fval) {
return ::detail::testPassx(false)<<" field '"<<name<<"' with type "<<typeid(PVD).name()<<" does not exist";
} else {
typename PVD::value_type actual(fval->get());
return ::detail::testPassx(actual==expect)<<name<<" ("<<actual<<") == "<<expect;
}
}
template<typename PVD>
::detail::testPassx
testFieldEqual(const std::tr1::shared_ptr<const epics::pvData::PVStructure>& val, const char *name, typename PVD::const_svector expect)
{
if(!val) {
return ::detail::testPassx(false)<<" null structure pointer";
}
typename PVD::const_shared_pointer fval(val->getSubField<PVD>(name));
if(!fval) {
return ::detail::testPassx(false)<<" field '"<<name<<"' with type "<<typeid(PVD).name()<<" does not exist";
} else {
typename PVD::const_svector actual(fval->view());
return ::detail::testPassx(actual==expect)<<name<<" ("<<actual<<") == "<<expect;
}
}
/** @} */
#endif // PVUNITTEST_H

View File

@@ -13,7 +13,6 @@
#include <cstddef>
#include <stdexcept>
#include <compilerDependencies.h>
#include <pv/sharedPtr.h>
namespace epics { namespace pvData {
@@ -25,7 +24,7 @@ namespace epics { namespace pvData {
* but must create a std::vector of shared_ptr to queueElements.
*/
template <typename T>
class EPICS_DEPRECATED Queue
class Queue
{
public:
POINTER_DEFINITIONS(Queue);
@@ -65,7 +64,7 @@ public:
* @return a shared_ptr to the queue element.
* This is null if queue was full.
*/
queueElementPtr getFree();
queueElementPtr & getFree();
/**
* Set the element returned by getFree as used.
* Until this is called getUsed will not return it.
@@ -78,13 +77,14 @@ public:
* @return a shared_ptr to the queue element.
* This is null if no used element is available.`
*/
queueElementPtr getUsed();
queueElementPtr & getUsed();
/**
* Release the element obtained by the most recent call to getUsed.
* @param element The element.
*/
void releaseUsed(queueElementPtr const &element);
private:
queueElementPtr nullElement;
queueElementPtrArray elements;
// TODO use size_t instead
int size;
@@ -133,9 +133,9 @@ void Queue<T>::clear()
}
template <typename T>
std::tr1::shared_ptr<T> Queue<T>::getFree()
std::tr1::shared_ptr<T> & Queue<T>::getFree()
{
if(numberFree==0) return queueElementPtr();
if(numberFree==0) return nullElement;
numberFree--;
int ind = nextGetFree;
std::tr1::shared_ptr<T> queueElement = elements[nextGetFree++];
@@ -154,9 +154,9 @@ void Queue<T>::setUsed(std::tr1::shared_ptr<T> const &element)
}
template <typename T>
std::tr1::shared_ptr<T> Queue<T>::getUsed()
std::tr1::shared_ptr<T> & Queue<T>::getUsed()
{
if(numberUsed==0) return queueElementPtr();
if(numberUsed==0) return nullElement;
int ind = nextGetUsed;
std::tr1::shared_ptr<T> queueElement = elements[nextGetUsed++];
if(nextGetUsed>=size) nextGetUsed = 0;

View File

@@ -1,141 +0,0 @@
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
#ifndef REFTRACK_H
#define REFTRACK_H
#ifdef __cplusplus
#include <map>
#include <string>
#include <ostream>
#include <stdlib.h>
#include <epicsVersion.h>
#ifndef VERSION_INT
# define VERSION_INT(V,R,M,P) ( ((V)<<24) | ((R)<<16) | ((M)<<8) | (P))
#endif
#ifndef EPICS_VERSION_INT
# define EPICS_VERSION_INT VERSION_INT(EPICS_VERSION, EPICS_REVISION, EPICS_MODIFICATION, EPICS_PATCH_LEVEL)
#endif
#if EPICS_VERSION_INT>=VERSION_INT(3,15,1,0)
# include <epicsAtomic.h>
# define REFTRACK_USE_ATOMIC
#endif
#ifdef REFTRACK_USE_ATOMIC
# define REFTRACE_INCREMENT(counter) ::epics::atomic::increment(counter)
# define REFTRACE_DECREMENT(counter) ::epics::atomic::decrement(counter)
#else
# define REFTRACE_INCREMENT(counter) do{}while(0)
# define REFTRACE_DECREMENT(counter) do{}while(0)
#endif
#include <shareLib.h>
namespace epics {
//! Register new global reference counter
epicsShareFunc
void registerRefCounter(const char *name, const size_t* counter);
//! Remove registration of global reference counter (if dynamically allocated)
epicsShareFunc
void unregisterRefCounter(const char *name, const size_t* counter);
//! Fetch current value of single reference counter
epicsShareFunc
size_t readRefCounter(const char *name);
//! Represent a snapshot of many reference counters
class epicsShareClass RefSnapshot
{
public:
//! A single count
struct Count {
size_t current;
long delta; //!< current - previous
Count() :current(0u), delta(0) {}
explicit Count(size_t c, long d) :current(c), delta(d) {}
bool operator==(const Count& o) const
{ return current==o.current && delta==o.delta; }
};
private:
typedef std::map<std::string, Count> cnt_map_t;
cnt_map_t counts;
public:
typedef cnt_map_t::const_iterator iterator;
typedef cnt_map_t::const_iterator const_iterator;
/** Fetch values of all reference counters.
*
* This involves many atomic reads, not a single operation.
*/
void update();
const Count& operator[](const std::string& name) const;
iterator begin() const { return counts.begin(); }
iterator end() const { return counts.end(); }
size_t size() const { return counts.size(); }
inline void swap(RefSnapshot& o)
{
counts.swap(o.counts);
}
/** Compute the difference lhs - rhs
*
* Returned RefSnapshot has Count::current=lhs.current
* and Count::delta= lhs.current - rhs.current
*/
RefSnapshot operator-(const RefSnapshot& rhs) const;
};
//! Print all counters with a non-zero delta
epicsShareFunc
std::ostream& operator<<(std::ostream& strm, const RefSnapshot& snap);
//! Helper to run a thread which periodically prints (via show() )
//! global reference counter deltas.
class epicsShareClass RefMonitor
{
struct Impl;
Impl *impl;
public:
RefMonitor();
virtual ~RefMonitor();
void start(double period=10.0);
void stop();
bool running() const;
//! call show() with current snapshot
void current();
protected:
//! Default prints to stderr
//! @param complete when false show only non-zero delta, when true show non-zero count or delta
virtual void show(const RefSnapshot& snap, bool complete=false);
};
} // namespace epics
extern "C" {
#endif /* __cplusplus */
/** Fetch and print current snapshot
* @return NULL or a char* which must be free()'d
*/
char* epicsRefSnapshotCurrent();
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif // REFTRACK_H

71
src/misc/pv/requester.h Normal file
View File

@@ -0,0 +1,71 @@
/* requester.h */
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
/**
* @author mrk
*/
#ifndef REQUESTER_H
#define REQUESTER_H
#include <string>
#include <pv/pvType.h>
#include <pv/sharedPtr.h>
#include <shareLib.h>
namespace epics { namespace pvData {
class Requester;
typedef std::tr1::shared_ptr<Requester> RequesterPtr;
enum MessageType {
infoMessage,warningMessage,errorMessage,fatalErrorMessage
};
#define MESSAGE_TYPE_COUNT 4
epicsShareExtern std::string getMessageTypeName(MessageType messageType);
/**
* @brief Callback class for passing messages to a requester.
*
* This is used by many other classes and also extended by other classes.
* The request is passed a message and a messageType.
* A message is just a string and a messageType is:
@code
enum MessageType {
infoMessage,warningMessage,errorMessage,fatalErrorMessage
};
@endcode
*
*/
class epicsShareClass Requester {
public:
POINTER_DEFINITIONS(Requester);
/**
* Destructor
*/
virtual ~Requester(){}
/**
* The requester must have a name.
* @return The requester's name.
*/
virtual std::string getRequesterName() = 0;
/**
*
* A message for the requester.
* @param message The message.
* @param messageType The type of message:
@code
enum MessageType {
infoMessage,warningMessage,errorMessage,fatalErrorMessage
};
@endcode
*/
virtual void message(std::string const & message,MessageType messageType);
};
}}
#endif /* REQUESTER_H */

View File

@@ -26,8 +26,7 @@ namespace epics {
* @brief Serialization helper.
*
*/
class epicsShareClass SerializeHelper {
EPICS_NOT_COPYABLE(SerializeHelper)
class epicsShareClass SerializeHelper : public NoDefaultMethods {
public:
/**

View File

@@ -9,9 +9,7 @@
#ifndef SHAREDPTR_H
#define SHAREDPTR_H
#include <memory> /* for auto_ptr */
/** @file sharedPtr.h
/*
* Pulls in the std::tr1 namespace with the following names
*
* class shared_ptr
@@ -25,49 +23,9 @@
* function enable_shared_from_this
*/
/* where should we look? (In decending order of preference)
*
* # manual (per source file) selection
* # c++11 version of <memory>, then alias into tr1
* # <tr1/memory>
* # boost version of tr1/memory
*/
// where should we look?
/* Debugging shared_ptr with debugPtr.h requires >= c++11
*
* Define DEBUG_SHARED_PTR globally to cause epics::debug::shared_ptr
* to be injected as std::tr1::shared_ptr and the macro
* HAVE_SHOW_REFS will be defined.
*
* epics::debug::shared_ptr wraps std::shared_ptr with additional
* tracking of backwards references.
* std::shared_ptr::use_count() gives the number of shared_ptr
* (strong refs) to the referenced object.
*
* If use_count()==5 then epics::debug::shared_ptr::show_refs() will print
* 5 lines of the format
*
* # <addr>: <IP0> <IP1> ...
*
* Given the numberic address of each shared_ptr as well as the call stack
* at the point where it was initialized.
* Use the 'addr2line' utility to interpret the stack addresses.
*
* On linux w/ ASLR it is necessary to turn on static linking to meaningfully
* interpret call stack addresses.
* Append "STATIC_BUILD=YES" to configure/CONFIG_SITE
*/
//#define DEBUG_SHARED_PTR
#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
# define SHARED_FROM_STD
#elif defined(__GNUC__) && __GNUC__>=4 && !defined(vxWorks)
#if defined(__GNUC__) && __GNUC__>=4 && !defined(vxWorks)
// GCC >=4.0.0
# define SHARED_FROM_TR1
@@ -79,48 +37,34 @@
# define SHARED_FROM_BOOST
#endif
// go and get it
#if defined(_MSC_VER) && (_MSC_VER>=1600)
// MSVC 2010 has it in <memory>
# undef SHARED_FROM_BOOST
# undef SHARED_FROM_TR1
#endif
#if defined(SHARED_FROM_MANUAL)
// no-op
#elif defined(SHARED_FROM_STD)
#if defined(__clang__)
# undef SHARED_FROM_BOOST
# undef SHARED_FROM_TR1
#include <memory>
#ifndef DEBUG_SHARED_PTR
namespace std {
// import std classes into std::tr1
namespace std {
namespace tr1 {
using ::std::shared_ptr;
using ::std::weak_ptr;
using ::std::static_pointer_cast;
using ::std::dynamic_pointer_cast;
using ::std::const_pointer_cast;
using ::std::enable_shared_from_this;
using ::std::bad_weak_ptr;
using std::shared_ptr;
using std::weak_ptr;
using std::static_pointer_cast;
using std::dynamic_pointer_cast;
using std::const_pointer_cast;
using std::enable_shared_from_this;
}
}
#endif
#else // DEBUG_SHARED_PTR
// go and get it
#include "debugPtr.h"
namespace std {
namespace tr1 {
using ::epics::debug::shared_ptr;
using ::epics::debug::weak_ptr;
using ::epics::debug::static_pointer_cast;
using ::epics::debug::dynamic_pointer_cast;
using ::epics::debug::const_pointer_cast;
using ::epics::debug::enable_shared_from_this;
using ::std::bad_weak_ptr;
}
}
#endif // DEBUG_SHARED_PTR
#elif defined(SHARED_FROM_TR1)
#if defined(SHARED_FROM_TR1)
# include <tr1/memory>
#elif defined(SHARED_FROM_BOOST)
@@ -133,15 +77,12 @@ namespace std {
# include <boost/tr1/memory.hpp>
#else
# error No shared_ptr selection
// eventually...
# include <memory>
#endif
// cleanup
#ifdef SHARED_FROM_STD
# undef SHARED_FROM_STD
#endif
#ifdef SHARED_FROM_TR1
# undef SHARED_FROM_TR1
#endif
@@ -150,78 +91,10 @@ namespace std {
# undef SHARED_FROM_BOOST
#endif
namespace detail {
template<typename T>
struct ref_shower {
const std::tr1::shared_ptr<T>& ptr;
bool self, weak;
ref_shower(const std::tr1::shared_ptr<T>& ptr, bool self, bool weak) :ptr(ptr),self(self),weak(weak) {}
};
}
/** Print a list (one per line) of shared_ptr which refer to the same object
*
* @param ptr Use the object pointed to by this shared_ptr
* @param self include or omit a line for this shared_ptr
* @param weak include a line for each weak_ptr (not implemented)
@code
shared_ptr<int> x;
std::cout << show_referrers(x);
@endcode
*/
template<typename T>
inline ::detail::ref_shower<T> show_referrers(const std::tr1::shared_ptr<T>& ptr, bool self=true, bool weak=false)
{
return ::detail::ref_shower<T>(ptr, self, weak);
}
namespace std{
template<typename T>
inline std::ostream& operator<<(std::ostream& strm, const ::detail::ref_shower<T>& refs)
{
#ifdef HAVE_SHOW_REFS
refs.ptr.show_refs(strm, refs.self, refs.weak);
#endif // HAVE_SHOW_REFS
return strm;
}
}//namespace std
#define POINTER_DEFINITIONS(clazz) \
typedef std::tr1::shared_ptr<clazz> shared_pointer; \
typedef std::tr1::shared_ptr<const clazz> const_shared_pointer; \
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,
* and avoid copious deprecation warning spam
* which may be hiding legitimate issues.
*
* Provides epics::auto_ptr<T> and epics::swap()
*
* epics::auto_ptr<T> is std::auto_ptr<T> for c++98
* and std::unique_ptr<T> for >= c++11.
*
* epics::swap() is the only supported operation.
* 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>;
template<typename T>
static inline void swap(auto_ptr<T>& lhs, auto_ptr<T>& rhs) {
lhs.swap(rhs);
}
#else
using std::auto_ptr;
template<typename T>
static inline void swap(auto_ptr<T>& lhs, auto_ptr<T>& rhs) {
auto_ptr<T> temp(lhs);
lhs = rhs;
rhs = temp;
}
#endif
}
#endif // SHAREDPTR_H

View File

@@ -623,21 +623,33 @@ namespace detail {
template<typename TO, typename FROM, class Enable = void>
struct static_shared_vector_caster { /* no default */ };
// from void to non-void with same const-ness
template<typename TO, typename FROM>
struct static_shared_vector_caster<TO, FROM,
typename meta::_and<meta::_and<meta::is_not_void<TO>, meta::is_void<FROM> >,
meta::same_const<TO,FROM> >::type> {
static inline shared_vector<TO> op(const shared_vector<FROM>& src) {
template<typename TO>
struct static_shared_vector_caster<TO, void,
typename meta::_and<meta::same_const<TO,void>, meta::is_not_void<TO> >::type> {
static inline shared_vector<TO> op(const shared_vector<void>& src) {
return shared_vector<TO>(src, detail::_shared_vector_cast_tag());
}
};
template<typename TO>
struct static_shared_vector_caster<TO, const void,
typename meta::_and<meta::same_const<TO,const void>, meta::is_not_void<TO> >::type> {
static inline shared_vector<TO> op(const shared_vector<const void>& src) {
return shared_vector<TO>(src, detail::_shared_vector_cast_tag());
}
};
// from non-void to void with same const-ness
template<typename TO, typename FROM>
struct static_shared_vector_caster<TO, FROM,
typename meta::_and<meta::_and<meta::is_void<TO>, meta::is_not_void<FROM> >,
meta::same_const<TO,FROM> >::type> {
static FORCE_INLINE shared_vector<TO> op(const shared_vector<FROM>& src) {
return shared_vector<TO>(src, detail::_shared_vector_cast_tag());
template<typename FROM>
struct static_shared_vector_caster<void, FROM,
typename meta::_and<meta::same_const<void,FROM>, meta::is_not_void<FROM> >::type> {
static FORCE_INLINE shared_vector<void> op(const shared_vector<FROM>& src) {
return shared_vector<void>(src, detail::_shared_vector_cast_tag());
}
};
template<typename FROM>
struct static_shared_vector_caster<const void, FROM,
typename meta::_and<meta::same_const<const void,FROM>, meta::is_not_void<FROM> >::type> {
static FORCE_INLINE shared_vector<const void> op(const shared_vector<FROM>& src) {
return shared_vector<const void>(src, detail::_shared_vector_cast_tag());
}
};
@@ -817,7 +829,7 @@ const_shared_vector_cast(shared_vector<FROM>& src)
namespace ScalarTypeFunc {
//! Allocate an untyped array based on ScalarType
epicsShareFunc shared_vector<void> allocArray(ScalarType id, size_t len);
shared_vector<void> allocArray(ScalarType id, size_t len);
//! Allocate an untyped array based on ScalarType
template<ScalarType ID>

View File

@@ -46,14 +46,10 @@ namespace epics { namespace pvData {
static Status Ok;
static inline Status warn(const std::string& m) { return Status(STATUSTYPE_WARNING, m); }
static inline Status error(const std::string& m) { return Status(STATUSTYPE_ERROR, m); }
static inline Status fatal(const std::string& m) { return Status(STATUSTYPE_FATAL, m); }
/**
* Creates OK status; STATUSTYPE_OK, empty message and stackDump.
*/
Status() :m_statusType(STATUSTYPE_OK) {}
Status();
/**
* Create non-OK status.
@@ -65,25 +61,25 @@ namespace epics { namespace pvData {
*/
Status(StatusType type, std::string const & message, std::string const & stackDump);
virtual ~Status() {}
~Status();
/**
* Get status type.
* @return status type, non-<code>null</code>.
*/
inline StatusType getType() const { return m_statusType; }
StatusType getType() const;
/**
* Get error message describing an error. Required if error status.
* @return error message.
*/
inline std::string getMessage() const { return m_message; }
std::string getMessage() const;
/**
* Get stack dump where error (exception) happened. Optional.
* @return stack dump.
*/
inline std::string getStackDump() const { return m_stackDump; }
std::string getStackDump() const;
/**
* Convenient OK test. Same as <code>(getType() == StatusType.OK)</code>.
@@ -92,47 +88,13 @@ namespace epics { namespace pvData {
* @return OK status.
* @see #isSuccess()
*/
inline bool isOK() const {
return (m_statusType == STATUSTYPE_OK);
}
bool isOK() const;
/**
* Check if operation succeeded (OK or WARNING).
* Check if operation succeeded.
* @return operation success status.
*/
inline bool isSuccess() const {
return (m_statusType == STATUSTYPE_OK || m_statusType == STATUSTYPE_WARNING);
}
#if __cplusplus>=201103L
FORCE_INLINE explicit operator bool() const {
return isSuccess();
}
#else
private:
typedef bool (Status::*truth_type)() const;
public:
FORCE_INLINE operator truth_type() const {
return isSuccess() ? &Status::isSuccess : 0;
}
#endif
/** override this Status if the other has higher StatusType
@code
Status ret;
ret |= call1();
if(ret)
ret |= call2();
return ret;
@endcode
*/
void maximize(const Status& o);
//! short hand for "this->maximize(o)"
FORCE_INLINE Status& operator|=(const Status& o) {
maximize(o);
return *this;
}
bool isSuccess() const;
void serialize(ByteBuffer *buffer, SerializableControl *flusher) const;
void deserialize(ByteBuffer *buffer, DeserializableControl *flusher);
@@ -147,15 +109,8 @@ namespace epics { namespace pvData {
};
FORCE_INLINE std::ostream& operator<<(std::ostream& o, const Status& status) {
status.dump(o);
return o;
}
FORCE_INLINE std::ostream& operator<<(std::ostream& o, const Status::StatusType& statusType) {
o << Status::StatusTypeName[statusType];
return o;
}
epicsShareExtern std::ostream& operator<<(std::ostream& o, const Status& status);
epicsShareExtern std::ostream& operator<<(std::ostream& o, const Status::StatusType& statusType);
}}
#endif /* STATUS_H */

View File

@@ -10,9 +10,9 @@
// gently nudge the compiler to inline our wrappers
// Warning: Only use this when the template body is *small*.
// You have been warned!
#if defined(__MINGW32__)
#if defined(__MINGW32__)
# define FORCE_INLINE inline
#elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 402)
#elif defined(__GNUC__) && __GNUC__>=3
# define FORCE_INLINE __attribute__((always_inline)) inline
#elif defined(_MSC_VER)
# define FORCE_INLINE __forceinline
@@ -116,31 +116,6 @@ struct _and {};
template<typename A, typename B, class R>
struct _and<A,B, typename A::type, typename B::type, R> { typedef R type; };
/** Mangle type to best pass as an argument.
*
* POD types passed by value.
* All others by const reference.
*/
template<typename T>
struct arg_type {typedef const T& type;};
#define SIMPLE_ARG_TYPE(TYPE) template<> struct arg_type<TYPE> { typedef TYPE type; };
SIMPLE_ARG_TYPE(bool)
SIMPLE_ARG_TYPE(char)
SIMPLE_ARG_TYPE(signed char)
SIMPLE_ARG_TYPE(unsigned char)
SIMPLE_ARG_TYPE(short)
SIMPLE_ARG_TYPE(unsigned short)
SIMPLE_ARG_TYPE(int)
SIMPLE_ARG_TYPE(unsigned int)
SIMPLE_ARG_TYPE(long)
SIMPLE_ARG_TYPE(unsigned long)
SIMPLE_ARG_TYPE(long long)
SIMPLE_ARG_TYPE(unsigned long long)
SIMPLE_ARG_TYPE(float)
SIMPLE_ARG_TYPE(double)
SIMPLE_ARG_TYPE(long double)
#undef SIMPLE_ARG_TYPE
}}}
#endif // TEMPLATEMETA_H

View File

@@ -11,7 +11,6 @@
#include <memory>
#include <sstream>
#include <stdexcept>
#if __cplusplus>=201103L
#include <functional>
@@ -44,9 +43,8 @@ typedef epicsThreadRunable Runnable;
//! Helper for those cases where a class should have more than one runnable
template<typename C>
class RunnableMethod : public Runnable
class epicsShareClass RunnableMethod : public Runnable, private NoDefaultMethods
{
EPICS_NOT_COPYABLE(RunnableMethod)
typedef void (C::*meth_t)();
C *inst;
meth_t meth;
@@ -106,8 +104,7 @@ struct BindRunner : public epicsThreadRunable
* @brief C++ wrapper for epicsThread from EPICS base.
*
*/
class Thread : public epicsThread {
EPICS_NOT_COPYABLE(Thread)
class epicsShareClass Thread : public epicsThread, private NoDefaultMethods {
public:
/** @brief Holds all the configuration necessary to launch a @class Thread
*
@@ -136,13 +133,17 @@ public:
<<"example"<<1);
@endcode
*/
class Config
class epicsShareClass Config
{
unsigned int p_prio, p_stack;
std::ostringstream p_strm;
bool p_autostart;
Runnable *p_runner;
typedef epics::auto_ptr<Runnable> p_owned_runner_t;
#if __cplusplus>=201103L
typedef std::unique_ptr<Runnable> p_owned_runner_t;
#else
typedef std::auto_ptr<Runnable> p_owned_runner_t;
#endif
p_owned_runner_t p_owned_runner;
friend class Thread;
Runnable& x_getrunner()

View File

@@ -9,8 +9,6 @@
#ifndef TIMEFUNCTION_H
#define TIMEFUNCTION_H
#include <compilerDependencies.h>
#include <pv/sharedPtr.h>
#include <shareLib.h>
@@ -26,7 +24,7 @@ typedef std::tr1::shared_ptr<TimeFunction> TimeFunctionPtr;
* @brief Class that must be implemented by timeFunction requester.
*
*/
class epicsShareClass EPICS_DEPRECATED TimeFunctionRequester {
class epicsShareClass TimeFunctionRequester {
public:
POINTER_DEFINITIONS(TimeFunctionRequester);
/**
@@ -45,7 +43,7 @@ public:
* @brief Class for measuring time it takes to execute a function.
*
*/
class epicsShareClass EPICS_DEPRECATED TimeFunction {
class epicsShareClass TimeFunction {
public:
POINTER_DEFINITIONS(TimeFunction);
/**

View File

@@ -123,6 +123,7 @@ private:
TimerCallbackPtr head;
Mutex mutex;
Event waitForWork;
Event waitForDone;
bool alive;
Thread thread;
};

View File

@@ -1,53 +0,0 @@
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
#include <string>
#include <vector>
#include <epicsUnitTest.h>
#define epicsExportSharedSymbols
#include <pv/pvUnitTest.h>
namespace detail {
testPassx::~testPassx() {
if(!alive) return;
std::string msg(strm.str());
size_t nl = msg.find_first_of('\n');
if(nl==msg.npos) {
// single-line output
if(dotest)
testOk(pass, "%s", msg.c_str());
else
testDiag("%s", msg.c_str());
} else {
// multi-line output
std::istringstream lines(msg);
std::string line;
bool first = true;
while(std::getline(lines ,line)) {
if(dotest && first) {
first = false;
testOk(pass, "%s", line.c_str());
} else {
testDiag("%s", line.c_str());
}
}
}
}
testPassx::testPassx(testPassx& o)
:strm(o.strm.str())
,dotest(o.dotest)
,pass(o.pass)
,alive(o.alive)
{
strm.seekp(0, std::ios_base::end);
o.alive = false;
}
} // namespace detail

View File

@@ -1,300 +0,0 @@
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <memory>
#include <stdlib.h>
#include <string.h>
#include <epicsString.h>
#include <epicsGuard.h>
#include <epicsMutex.h>
#include <epicsEvent.h>
#include <epicsThread.h>
#include <epicsTime.h>
#define epicsExportSharedSymbols
#include <pv/pvdVersion.h>
#include <pv/sharedPtr.h>
#include "pv/reftrack.h"
namespace {
#ifndef REFTRACK_USE_ATOMIC
static inline
size_t readref(const size_t *ref)
{
volatile const size_t *vref = ref;
return *vref;
}
#endif
typedef epicsGuard<epicsMutex> Guard;
typedef epicsGuardRelease<epicsMutex> UnGuard;
struct refgbl_t {
epicsMutex lock;
typedef std::map<std::string, const size_t*> counters_t;
counters_t counters;
} *refgbl;
void refgbl_init(void *)
{
try {
refgbl = new refgbl_t;
} catch(std::exception& e) {
std::cerr<<"Failed to initialize global ref. counter registry :"<<e.what()<<"\n";
}
}
epicsThreadOnceId refgbl_once = EPICS_THREAD_ONCE_INIT;
void refgbl_setup()
{
epicsThreadOnce(&refgbl_once, &refgbl_init, 0);
if(!refgbl)
throw std::runtime_error("Failed to initialize global ref. counter registry");
}
} // namespace
namespace epics {
void registerRefCounter(const char *name, const size_t* counter)
{
refgbl_setup();
Guard G(refgbl->lock);
refgbl->counters[name] = counter;
}
void unregisterRefCounter(const char *name, const size_t* counter)
{
refgbl_setup();
Guard G(refgbl->lock);
refgbl_t::counters_t::iterator it(refgbl->counters.find(name));
if(it!=refgbl->counters.end() && it->second==counter)
refgbl->counters.erase(it);
}
size_t readRefCounter(const char *name)
{
refgbl_setup();
Guard G(refgbl->lock);
refgbl_t::counters_t::iterator it(refgbl->counters.find(name));
if(it==refgbl->counters.end())
return 0;
#ifdef REFTRACK_USE_ATOMIC
return atomic::get(*it->second);
#else
return readref(it->second);
#endif
}
const RefSnapshot::Count&
RefSnapshot::operator[](const std::string& name) const
{
static const Count zero;
cnt_map_t::const_iterator it(counts.find(name));
return it==counts.end() ? zero : it->second;
}
void RefSnapshot::update()
{
refgbl_t::counters_t counters;
{
refgbl_setup();
Guard G(refgbl->lock);
counters = refgbl->counters; // copy
}
counts.clear();
for(refgbl_t::counters_t::const_iterator it=counters.begin(),
end=counters.end();
it!=end; ++it)
{
#ifdef REFTRACK_USE_ATOMIC
size_t cnt = atomic::get(*it->second);
#else
size_t cnt = readref(it->second);
#endif
counts[it->first] = Count(cnt, 0);
}
}
RefSnapshot RefSnapshot::operator-(const RefSnapshot& rhs) const
{
RefSnapshot ret;
RefSnapshot::cnt_map_t::const_iterator lit = counts.begin(),
lend= counts.end(),
rit = rhs.counts.begin(),
rend= rhs.counts.end();
while(lit!=lend || rit!=rend)
{
if(lit==lend || (rit!=rend && lit->first > rit->first)) {
ret.counts[rit->first] = RefSnapshot::Count(0, -long(rit->second.current));
++rit;
} else if(rit==rend || lit->first < rit->first) {
ret.counts[lit->first] = RefSnapshot::Count(lit->second.current, long(lit->second.current));
++lit;
} else { // !end and lit->first == rit->first
ret.counts[lit->first] = RefSnapshot::Count(lit->second.current,
long(lit->second.current) - long(rit->second.current));
++lit;
++rit;
}
}
return ret;
}
std::ostream& operator<<(std::ostream& strm, const RefSnapshot& snap)
{
for(RefSnapshot::const_iterator it = snap.begin(), end = snap.end(); it!=end; ++it)
{
if(it->second.delta==0) continue;
strm<<it->first<<":\t"<<it->second.current<<" (delta "<<it->second.delta<<")\n";
}
return strm;
}
struct RefMonitor::Impl : public epicsThreadRunable
{
RefMonitor& owner;
epics::auto_ptr<epicsThread> worker;
epicsMutex lock;
epicsEvent wakeup;
RefSnapshot prev;
bool done;
double period;
Impl(RefMonitor* owner) :owner(*owner), done(false), period(10.0) {}
virtual ~Impl() {}
virtual void run()
{
Guard G(lock);
while(!done) {
RefSnapshot current, P;
P = prev; // copy
{
UnGuard U(G);
current.update();
owner.show(current-P);
}
prev.swap(current);
{
UnGuard U(G);
wakeup.wait(period);
}
}
}
};
RefMonitor::RefMonitor()
:impl(new Impl(this))
{}
RefMonitor::~RefMonitor()
{
stop();
delete impl;
}
void RefMonitor::start(double period)
{
Guard G(impl->lock);
if(impl->worker.get()) return;
impl->done = false;
impl->period = period;
impl->worker.reset(new epicsThread(*impl,
"RefMonitor",
epicsThreadGetStackSize(epicsThreadStackSmall),
epicsThreadPriorityMin));
impl->worker->start();
}
void RefMonitor::stop()
{
epics::auto_ptr<epicsThread> W;
{
Guard G(impl->lock);
if(!impl->worker.get()) return;
epics::swap(W, impl->worker);
impl->done = true;
}
impl->wakeup.signal();
W->exitWait();
W.reset();
}
bool RefMonitor::running() const
{
Guard G(impl->lock);
return !!impl->worker.get();
}
void RefMonitor::current()
{
RefSnapshot current, P;
current.update();
{
Guard G(impl->lock);
P = impl->prev; // copy
}
show(current-P, true);
}
void RefMonitor::show(const RefSnapshot &snap, bool complete)
{
epicsTime now(epicsTime::getCurrent());
char buf[80];
epicsTime::getCurrent().strftime(buf, sizeof(buf), "%a %b %d %Y %H:%M:%S.%f");
buf[sizeof(buf)-1] = '\0';
std::cerr<<buf<<" : References\n";
for(RefSnapshot::const_iterator it = snap.begin(), end = snap.end(); it!=end; ++it)
{
// print if delta!=0 or (complete && current!=0)
if(it->second.delta==0 && (!complete || it->second.current==0)) continue;
std::cerr<<it->first<<":\t"<<it->second.current<<" (delta "<<it->second.delta<<")\n";
}
}
} // namespace epics
char* epicsRefSnapshotCurrent()
{
try {
epics::RefSnapshot snap;
snap.update();
std::ostringstream strm;
strm<<snap;
const char *str = strm.str().c_str();
char *ret = (char*)malloc(strlen(str)+1);
if(ret)
strcpy(ret, str);
return ret;
}catch(std::exception& e){
return epicsStrDup(e.what());
}
}

44
src/misc/requester.cpp Normal file
View File

@@ -0,0 +1,44 @@
/* requester.cpp */
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
/**
* @author mrk
*/
#include <string>
#include <cstdio>
#include <iostream>
#include <epicsMutex.h>
#define epicsExportSharedSymbols
#include <pv/lock.h>
#include <pv/requester.h>
using std::string;
namespace epics { namespace pvData {
static StringArray messageTypeName(MESSAGE_TYPE_COUNT);
string getMessageTypeName(MessageType messageType)
{
// TODO not thread-safe
static Mutex mutex;
Lock xx(mutex);
if(messageTypeName[0].size()==0) {
messageTypeName[0] = "info";
messageTypeName[1] = "warning";
messageTypeName[2] = "error";
messageTypeName[3] = "fatalError";
}
return messageTypeName[messageType];
}
void Requester::message(std::string const & message,MessageType messageType)
{
std::cerr << "[" << getRequesterName() << "] message(" << message << ", " << getMessageTypeName(messageType) << ")\n";
}
}}

View File

@@ -19,11 +19,20 @@ const char* Status::StatusTypeName[] = { "OK", "WARNING", "ERROR", "FATAL" };
Status Status::Ok;
//PVDATA_REFCOUNT_MONITOR_DEFINE(status);
Status::Status() :
m_statusType(STATUSTYPE_OK)
{
}
Status::Status(StatusType type, string const & message) :
m_statusType(type), m_message(message)
{
if (type == STATUSTYPE_OK)
throw std::invalid_argument("type == STATUSTYPE_OK");
//PVDATA_REFCOUNT_MONITOR_CONSTRUCT(status);
}
Status::Status(StatusType type, string const & message, string const & stackDump) :
@@ -31,15 +40,38 @@ Status::Status(StatusType type, string const & message, string const & stackDump
{
if (type == STATUSTYPE_OK)
throw std::invalid_argument("type == STATUSTYPE_OK");
//PVDATA_REFCOUNT_MONITOR_CONSTRUCT(status);
}
void Status::maximize(const Status& o)
Status::~Status() {
//PVDATA_REFCOUNT_MONITOR_DESTRUCT(status);
}
Status::StatusType Status::getType() const
{
if(m_statusType < o.m_statusType) {
m_statusType = o.m_statusType;
m_message = o.m_message;
m_stackDump = o.m_stackDump;
}
return m_statusType;
}
string Status::getMessage() const
{
return m_message;
}
string Status::getStackDump() const
{
return m_stackDump;
}
bool Status::isOK() const
{
return (m_statusType == STATUSTYPE_OK);
}
bool Status::isSuccess() const
{
return (m_statusType == STATUSTYPE_OK || m_statusType == STATUSTYPE_WARNING);
}
void Status::serialize(ByteBuffer *buffer, SerializableControl *flusher) const
@@ -88,6 +120,18 @@ void Status::dump(std::ostream& o) const
if (!m_stackDump.empty())
o << ", stackDump=" << std::endl << m_stackDump;
o << ']';
}
std::ostream& operator<<(std::ostream& o, const Status& status)
{
status.dump(o);
return o;
}
std::ostream& operator<<(std::ostream& o, const Status::StatusType& statusType)
{
o << Status::StatusTypeName[statusType];
return o;
}
}}

View File

@@ -14,17 +14,12 @@
#include <epicsTime.h>
// Suppress deprecation warnings for the implementation
#include <compilerDependencies.h>
#undef EPICS_DEPRECATED
#define EPICS_DEPRECATED
#define epicsExportSharedSymbols
#include <pv/pvType.h>
#include <pv/timeStamp.h>
#include <pv/timeFunction.h>
namespace epics { namespace pvData {
namespace epics { namespace pvData {
TimeFunction::TimeFunction(TimeFunctionRequesterPtr const &requester)
: requester(requester) {}

View File

@@ -32,6 +32,7 @@ TimerCallback::TimerCallback()
Timer::Timer(string threadName,ThreadPriority priority)
: waitForWork(false),
waitForDone(false),
alive(true),
thread(threadName,priority,this)
{}
@@ -138,6 +139,7 @@ void Timer::run()
waitForWork.wait(delay);
}
}
waitForDone.signal();
}
Timer::~Timer() {
@@ -146,7 +148,7 @@ Timer::~Timer() {
alive = false;
}
waitForWork.signal();
thread.exitWait();
waitForDone.wait();
TimerCallbackPtr timerCallback;
while(true) {
timerCallback = head;

View File

@@ -55,7 +55,7 @@ template<typename T>
static void copyV(size_t count, void *draw, const void *sraw)
{
T *dest=(T*)draw;
const T *src=(const T*)sraw;
const T *src=(T*)sraw;
std::copy(src, src+count, dest);
}

9
src/monitor/Makefile Normal file
View File

@@ -0,0 +1,9 @@
# This is a Makefile fragment, see ../Makefile
SRC_DIRS += $(PVDATA_SRC)/monitor
INC += pv/monitor.h
INC += pv/monitorPlugin.h
LIBSRCS += monitor.cpp
LIBSRCS += monitorPlugin.cpp

13
src/monitor/monitor.cpp Normal file
View File

@@ -0,0 +1,13 @@
/*monitor.cpp*/
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
/**
* @author mse
*/
#define epicsExportSharedSymbols
// needed to get interfaces exported
#include <pv/monitor.h>

View File

@@ -0,0 +1,86 @@
/* monitorPlugin.cpp */
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
/**
* @author mrk
*/
#define IN_MONITORPLUGIN_CPP
#define epicsExportSharedSymbols
#include <pv/monitorPlugin.h>
using std::string;
using std::cout;
using std::endl;
namespace epics { namespace pvData {
MonitorPluginManagerPtr MonitorPluginManager::get()
{
static MonitorPluginManagerPtr pluginManager;
static Mutex mutex;
Lock xx(mutex);
if(!pluginManager) {
pluginManager = MonitorPluginManagerPtr(new MonitorPluginManager());
}
return pluginManager;
}
bool MonitorPluginManager::addPlugin(
string const &pluginName,
MonitorPluginCreatorPtr const &creator)
{
mutex.lock();
std::list<MonitorPluginCreatorPtr>::iterator iter;
for (iter = monitorPluginList.begin();iter!=monitorPluginList.end();iter++)
{
if(*iter==creator)
{
mutex.unlock();
return false;
}
if(((*iter)->getName().compare(pluginName))==0)
{
mutex.unlock();
return false;
}
}
monitorPluginList.push_back(creator);
mutex.unlock();
return true;
}
MonitorPluginCreatorPtr MonitorPluginManager::findPlugin(
string const &pluginName)
{
mutex.lock();
std::list<MonitorPluginCreatorPtr>::iterator iter;
for (iter = monitorPluginList.begin();iter!=monitorPluginList.end();++iter)
{
if(((*iter)->getName().compare(pluginName))==0)
{
mutex.unlock();
return *iter;
}
}
mutex.unlock();
return MonitorPluginCreatorPtr();
}
void MonitorPluginManager::showNames()
{
mutex.lock();
std::list<MonitorPluginCreatorPtr>::iterator iter;
for (iter = monitorPluginList.begin();iter!=monitorPluginList.end();++iter)
{
std::cout << (*iter)->getName() << std::endl;
}
mutex.unlock();
}
}}

118
src/monitor/pv/monitor.h Normal file
View File

@@ -0,0 +1,118 @@
/* monitor.h */
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
/**
* @author mrk
*/
#ifndef MONITOR_H
#define MONITOR_H
#include <pv/status.h>
#include <pv/destroyable.h>
#include <pv/pvData.h>
#include <pv/sharedPtr.h>
#include <pv/bitSet.h>
#include <pv/requester.h>
#include <shareLib.h>
namespace epics { namespace pvData {
class MonitorElement;
typedef std::tr1::shared_ptr<MonitorElement> MonitorElementPtr;
typedef std::vector<MonitorElementPtr> MonitorElementPtrArray;
class Monitor;
typedef std::tr1::shared_ptr<Monitor> MonitorPtr;
/**
* @brief An element for a monitorQueue.
*
* Class instance representing monitor element.
* @author mrk
*/
class epicsShareClass MonitorElement {
public:
POINTER_DEFINITIONS(MonitorElement);
MonitorElement(){}
MonitorElement(PVStructurePtr const & pvStructurePtr)
: pvStructurePtr(pvStructurePtr),
changedBitSet(BitSet::create(static_cast<uint32>(pvStructurePtr->getNumberFields()))),
overrunBitSet(BitSet::create(static_cast<uint32>(pvStructurePtr->getNumberFields())))
{}
PVStructurePtr pvStructurePtr;
BitSet::shared_pointer changedBitSet;
BitSet::shared_pointer overrunBitSet;
};
/**
* @brief Monitor changes to fields of a pvStructure.
*
* This is used by pvAccess to implement monitors.
* @author mrk
*/
class epicsShareClass Monitor : public Destroyable{
public:
POINTER_DEFINITIONS(Monitor);
virtual ~Monitor(){}
/**
* Start monitoring.
* @return completion status.
*/
virtual Status start() = 0;
/**
* Stop Monitoring.
* @return completion status.
*/
virtual Status stop() = 0;
/**
* If monitor has occurred return data.
* @return monitorElement for modified data.
* Must call get to determine if data is available.
*/
virtual MonitorElementPtr poll() = 0;
/**
* Release a MonitorElement that was returned by poll.
* A poll() must be called after the release() to check the presence of any modified data.
* @param monitorElement
*/
virtual void release(MonitorElementPtr const & monitorElement) = 0;
};
/**
* @brief Callback implemented by monitor clients.
*
* Requester for ChannelMonitor.
* @author mrk
*/
class epicsShareClass MonitorRequester : public virtual Requester {
public:
POINTER_DEFINITIONS(MonitorRequester);
virtual ~MonitorRequester(){}
/**
* The client and server have both completed the createMonitor request.
* @param status Completion status.
* @param monitor The monitor
* @param structure The structure defining the data.
*/
virtual void monitorConnect(Status const & status,
MonitorPtr const & monitor, StructureConstPtr const & structure) = 0;
/**
* A monitor event has occurred.
* The requester must call Monitor.poll to get data.
* @param monitor The monitor.
*/
virtual void monitorEvent(MonitorPtr const & monitor) = 0;
/**
* The data source is no longer available.
* @param monitor The monitor.
*/
virtual void unlisten(MonitorPtr const & monitor) = 0;
};
}}
#endif /* MONITOR_H */

View File

@@ -0,0 +1,180 @@
/* monitorPlugin.h */
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
/**
* @author mrk
*/
#ifndef MONITORPLUGIN_H
#define MONITORPLUGIN_H
#include <list>
#include <pv/pvData.h>
#include <pv/sharedPtr.h>
#include <pv/bitSet.h>
#include <pv/monitor.h>
#include <shareLib.h>
#if !defined(IN_MONITORPLUGIN_CPP)
#warning monitorPlugin.h is deprecated
#endif
#if !defined(IN_MONITORPLUGIN_CPP) && defined(__GNUC__) && !(defined(__vxworks) && !defined(_WRS_VXWORKS_MAJOR))
#define USAGE_DEPRECATED __attribute__((deprecated))
#else
#define USAGE_DEPRECATED
#endif
namespace epics { namespace pvData {
class MonitorPlugin;
typedef std::tr1::shared_ptr<MonitorPlugin> MonitorPluginPtr;
class MonitorPluginCreator;
typedef std::tr1::shared_ptr<MonitorPluginCreator> MonitorPluginCreatorPtr;
class MonitorPluginManager;
typedef std::tr1::shared_ptr<MonitorPluginManager> MonitorPluginManagerPtr;
/**
* @brief A plugin for raising monitors;
*
* This is for use by pvAccess servers that support monitors.
* Since the interface has only a dependence on pvData it
* can be used for other purposes.
* A monitor is assumed to be associated with a field of a top-level
* structure.
*/
class epicsShareClass USAGE_DEPRECATED MonitorPlugin
{
public:
virtual ~MonitorPlugin(){}
/**
* getName
* @returns The name of the plugin
*/
virtual std::string const & getName() = 0;
/**
* Should a monitor be raised?
* @param pvField The field being monitored.
* @param pvTop The top-level structure in which the field resides.
* @param monitorElement The client data and bitsets.
* @returns true or false.
* True is returned if the change to this field should cause a monitor.
* False is returned in a change only to this field should not cause a
* monitor.
*/
virtual bool causeMonitor(
PVFieldPtr const &pvField,
PVStructurePtr const &pvTop,
MonitorElementPtr const &monitorElement) = 0;
/**
* A monitor will be sent to the client.
* @param monitorElement The data for the client.
* The plugin is allowed to change the data values.
*/
virtual void monitorDone(
MonitorElementPtr const &monitorElement)
{}
/**
* Begin monitoring
*/
virtual void startMonitoring(){}
/**
* Stop monitoring
*/
virtual void stopMonitoring(){}
/**
* Begin a set of puts.
*/
virtual void beginGroupPut() {};
/**
* End a set of puts.
*/
virtual void endGroupPut() {};
};
/**
* @brief A class that creates a plugin.
*
* Normlly a plugin is created for a single client.
*/
class epicsShareClass USAGE_DEPRECATED MonitorPluginCreator
{
public:
virtual ~MonitorPluginCreator() {}
/**
* Create a monitor plugin.
* @param field The introspection interface for the field monitored.
* @param top The introspection interface for the client structure.
* @param pvFieldOptions The options the client requested.
* The structure has a set of PVString subfields.
* The options are a set of name,value pairs.
* The subfield name is the name and the subfield value is the value.
* @returns shared pointer to a MonitorPluginCreator.
*/
virtual MonitorPluginPtr create(
FieldConstPtr const &field,
StructureConstPtr const &top,
PVStructurePtr const &pvFieldOptions) = 0;
/**
* getName
* @returns The name of the plugin
*/
virtual std::string const & getName() = 0;
};
/**
* @brief Manager for plugins.
*
* This manages a set of monitor plugins.
* @author mrk
*/
class epicsShareClass USAGE_DEPRECATED MonitorPluginManager
{
public:
POINTER_DEFINITIONS(MonitorPluginManager);
/**
* Factory to get the manager.
* @return shared pointer to manager.
*/
static MonitorPluginManagerPtr get();
/** destructor
*/
~MonitorPluginManager(){}
/* add plugin
* @param pluginName The name of the plugin.
* @param creator The creator.
* @returns true or false
* false is returned if a plugin with that name is already present
*/
bool addPlugin(
std::string const &pluginName,
MonitorPluginCreatorPtr const &creator);
/* find plugin
*
* @param plugin name
* @returns share pointer to plugin creator.
* If a plugin with that name is not found NULL is returned.
*/
MonitorPluginCreatorPtr findPlugin(std::string const &pluginName);
/* showNames
*
*/
void showNames();
private:
MonitorPluginManager(){}
std::list<MonitorPluginCreatorPtr> monitorPluginList;
epics::pvData::Mutex mutex;
};
#undef USAGE_DEPRECATED
}}
#endif /* MONITORPLUGIN_H */

View File

@@ -19,10 +19,10 @@
namespace epics { namespace pvData {
epicsShareExtern const int32 milliSecPerSec;
epicsShareExtern const int32 microSecPerSec;
epicsShareExtern const int32 nanoSecPerSec;
epicsShareExtern const int64 posixEpochAtEpicsEpoch;
epicsShareExtern int32 milliSecPerSec;
epicsShareExtern int32 microSecPerSec;
epicsShareExtern int32 nanoSecPerSec;
epicsShareExtern int64 posixEpochAtEpicsEpoch;
/** @brief Methods for manipulating timeStamp.
*
@@ -98,7 +98,7 @@ public:
* Set userTag.
* @param userTag application specific.
*/
void setUserTag(int32 userTag) {this->userTag = userTag;}
void setUserTag(int userTag) {this->userTag = userTag;}
/**
* Set time fields in timeStamp.
* Result will be normalized.

View File

@@ -72,25 +72,10 @@ bool PVAlarm::set(Alarm const & alarm)
throw std::logic_error(notAttached);
}
if(pvSeverity->isImmutable() || pvMessage->isImmutable()) return false;
Alarm current;
get(current);
bool returnValue = false;
if(current.getSeverity()!=alarm.getSeverity())
{
pvSeverity->put(alarm.getSeverity());
returnValue = true;
}
if(current.getStatus()!=alarm.getStatus())
{
pvStatus->put(alarm.getStatus());
returnValue = true;
}
if(current.getMessage()!=alarm.getMessage())
{
pvMessage->put(alarm.getMessage());
returnValue = true;
}
return returnValue;
pvSeverity->put(alarm.getSeverity());
pvStatus->put(alarm.getStatus());
pvMessage->put(alarm.getMessage());
return true;
}
}}

View File

@@ -70,25 +70,10 @@ bool PVControl::set(Control const & control)
throw std::logic_error(notAttached);
}
if(pvLow->isImmutable() || pvHigh->isImmutable() || pvMinStep->isImmutable()) return false;
Control current;
get(current);
bool returnValue = false;
if(current.getLow()!=control.getLow())
{
pvLow->put(control.getLow());
returnValue = true;
}
if(current.getHigh()!=control.getHigh())
{
pvHigh->put(control.getHigh());
returnValue = true;
}
if(current.getMinStep()!=control.getMinStep())
{
pvMinStep->put(control.getMinStep());
returnValue = true;
}
return returnValue;
pvLow->put(control.getLow());
pvHigh->put(control.getHigh());
pvMinStep->put(control.getMinStep());
return true;
}
}}

View File

@@ -85,38 +85,13 @@ bool PVDisplay::set(Display const & display)
}
if(pvDescription->isImmutable() || pvFormat->isImmutable()) return false;
if(pvUnits->isImmutable() || pvLow->isImmutable() || pvHigh->isImmutable())
{
return false;
}
Display current;
get(current);
bool returnValue = false;
if(current.getDescription()!=display.getDescription())
{
pvDescription->put(display.getDescription());
returnValue = true;
}
if(current.getFormat()!=display.getFormat())
{
pvFormat->put(display.getFormat());
returnValue = true;
}
if(current.getUnits()!=display.getUnits())
{
pvUnits->put(display.getUnits());
returnValue = true;
}
if(current.getLow()!=display.getLow())
{
pvLow->put(display.getLow());
returnValue = true;
}
if(current.getHigh()!=display.getHigh())
{
pvHigh->put(display.getHigh());
returnValue = true;
}
return returnValue;
pvDescription->put(display.getDescription());
pvFormat->put(display.getFormat());
pvUnits->put(display.getUnits());
pvLow->put(display.getLow());
pvHigh->put(display.getHigh());
return true;
}
}}

View File

@@ -73,25 +73,10 @@ bool PVTimeStamp::set(TimeStamp const & timeStamp)
throw std::logic_error(notAttached);
}
if(pvSecs->isImmutable() || pvNano->isImmutable()) return false;
TimeStamp current;
get(current);
bool returnValue = false;
if(current.getSecondsPastEpoch()!=timeStamp.getSecondsPastEpoch())
{
pvSecs->put(timeStamp.getSecondsPastEpoch());
returnValue = true;
}
if(current.getNanoseconds()!=timeStamp.getNanoseconds())
{
pvNano->put(timeStamp.getNanoseconds());
returnValue = true;
}
if(current.getUserTag()!=timeStamp.getUserTag())
{
pvUserTag->put(timeStamp.getUserTag());
returnValue = true;
}
return returnValue;
pvSecs->put(timeStamp.getSecondsPastEpoch());
pvUserTag->put(timeStamp.getUserTag());
pvNano->put(timeStamp.getNanoseconds());
return true;
}
}}

View File

@@ -21,10 +21,10 @@
namespace epics { namespace pvData {
const int32 milliSecPerSec = 1000;
const int32 microSecPerSec = 1000000;
const int32 nanoSecPerSec = 1000000000;
const int64 posixEpochAtEpicsEpoch = POSIX_TIME_AT_EPICS_EPOCH;
int32 milliSecPerSec = 1000;
int32 microSecPerSec = milliSecPerSec*milliSecPerSec;
int32 nanoSecPerSec = milliSecPerSec*microSecPerSec;
int64 posixEpochAtEpicsEpoch = POSIX_TIME_AT_EPICS_EPOCH;
TimeStamp::TimeStamp(int64 secondsPastEpoch,int32 nanoseconds,int32 userTag)
: secondsPastEpoch(secondsPastEpoch),nanoseconds(nanoseconds),userTag(userTag)

View File

@@ -4,15 +4,9 @@ SRC_DIRS += $(PVDATA_SRC)/pv
INC += pv/pvType.h
INC += pv/pvIntrospect.h
INC += pv/valueBuilder.h
INC += pv/pvData.h
INC += pv/convert.h
INC += pv/standardField.h
INC += pv/standardPVField.h
INC += pv/pvSubArrayCopy.h
INC += pv/typemap.h
INC += pv/pvdVersion.h
INC += pv/pvdVersionNum.h
LIBSRCS += pvdVersion.cpp
LIBSRCS += valueBuilder.cpp

File diff suppressed because it is too large Load Diff

View File

@@ -17,7 +17,6 @@
#include <pv/pvType.h>
#include <pv/byteBuffer.h>
#include <pv/serialize.h>
#include <pv/pvdVersion.h>
#include <shareLib.h>
@@ -292,8 +291,6 @@ class epicsShareClass Field :
virtual public Serializable,
public std::tr1::enable_shared_from_this<Field> {
public:
static size_t num_instances;
POINTER_DEFINITIONS(Field);
/**
* Destructor.
@@ -324,7 +321,7 @@ protected:
*/
Field(Type type);
private:
const Type m_fieldType;
Type m_fieldType;
friend class StructureArray;
friend class Structure;
@@ -856,17 +853,6 @@ public:
*/
bool isVariant() const {return (fieldNames.size() == 0);}
/** Attempt to find an suitable member to stored the specified type.
*
* Returned index is guerenteed to by of specified Type (either scalar or scalarArray).
* Provided ScalarType is taken as a hint.
*
@param t Must be either scalar or scalarArray
@param s The preferred ScalarType
@returns A valid index or -1
*/
int32 guess(Type t, ScalarType s) const;
virtual std::string getID() const;
virtual std::ostream& dump(std::ostream& o) const;
@@ -1034,35 +1020,29 @@ public:
private:
FieldBuilder();
FieldBuilder(const Structure*);
FieldBuilder(const FieldBuilderPtr & _parentBuilder, const std::string& name, const Structure*);
FieldBuilder(const FieldBuilderPtr & _parentBuilder, const std::string& name, const StructureArray*);
FieldBuilder(const FieldBuilderPtr & _parentBuilder, const std::string& name, const Union*);
FieldBuilder(const FieldBuilderPtr & _parentBuilder, const std::string& name, const UnionArray*);
FieldBuilder(FieldBuilderPtr const & parentBuilder,
std::string const & nestedName,
Type nestedClassToBuild, bool nestedArray);
const Field *findField(const std::string& name, Type ftype);
void reset();
FieldConstPtr createFieldInternal(Type type);
friend class FieldCreate;
const FieldCreatePtr fieldCreate;
FieldCreatePtr fieldCreate;
std::string id;
bool idSet;
// NOTE: this preserves order, however it does not handle duplicates
StringArray fieldNames;
FieldConstPtrArray fields;
const FieldBuilderPtr parentBuilder;
const Type nestedClassToBuild;
const std::string nestedName;
const bool nestedArray;
const bool createNested; // true - endNested() creates in parent, false - endNested() appends to parent
FieldBuilderPtr parentBuilder;
Type nestedClassToBuild;
std::string nestedName;
bool nestedArray;
};
/**
@@ -1071,17 +1051,12 @@ private:
*/
class epicsShareClass FieldCreate {
public:
static const FieldCreatePtr &getFieldCreate();
static FieldCreatePtr getFieldCreate();
/**
* Create a new instance of in-line @c Field builder.
* @return a new instance of a @c FieldBuilder.
*/
FieldBuilderPtr createFieldBuilder() const;
/**
* Create a new instance of in-line @c Field builder pre-initialized with and existing Structure
* @return a new instance of a @c FieldBuilder.
*/
FieldBuilderPtr createFieldBuilder(StructureConstPtr S) const;
/**
* Create a @c ScalarField.
* @param scalarType The scalar type.
@@ -1225,9 +1200,7 @@ private:
* Get the single class that implements FieldCreate,
* @return The fieldCreate factory.
*/
FORCE_INLINE const FieldCreatePtr& getFieldCreate() {
return FieldCreate::getFieldCreate();
}
epicsShareExtern FieldCreatePtr getFieldCreate();
/** Define a compile time mapping from
* type to enum value.

View File

@@ -39,11 +39,9 @@ typedef unsigned int uintptr_t;
#endif
#include <pv/sharedPtr.h>
#include <pv/localStaticLock.h>
//! epics
namespace epics {
//! pvData
namespace pvData {
namespace epics { namespace pvData {
namespace detail {
// Pick either type If or type Else to not be Cond

View File

@@ -1,34 +0,0 @@
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
#include <stdlib.h>
#define epicsExportSharedSymbols
#include <pv/pvdVersion.h>
namespace epics{namespace pvData{
void getVersion(epics::pvData::PVDataVersion *ptr)
{
ptr->major = EPICS_PVD_MAJOR_VERSION;
ptr->minor = EPICS_PVD_MINOR_VERSION;
ptr->maint = EPICS_PVD_MAINTENANCE_VERSION;
ptr->devel = EPICS_PVD_DEVELOPMENT_FLAG;
}
}} // namespace epics::pvData
// perhaps helpful in a loadable module
extern "C" {
epicsShareExtern void getPVDataVersion(epics::pvData::PVDataVersion *ptr, size_t len);
void getPVDataVersion(epics::pvData::PVDataVersion *ptr, size_t len)
{
if(len>=sizeof(*ptr)) {
epics::pvData::getVersion(ptr);
}
}
}

View File

@@ -1,46 +0,0 @@
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
/* pvdVersion.h did not exist prior to 7.0.0
* from which time it is included by pv/pvIntrospect.h
*/
#ifndef PVDVERSION_H
#define PVDVERSION_H
#include <epicsVersion.h>
#include <shareLib.h>
#ifndef VERSION_INT
# define VERSION_INT(V,R,M,P) ( ((V)<<24) | ((R)<<16) | ((M)<<8) | (P))
#endif
#ifndef EPICS_VERSION_INT
# define EPICS_VERSION_INT VERSION_INT(EPICS_VERSION, EPICS_REVISION, EPICS_MODIFICATION, EPICS_PATCH_LEVEL)
#endif
/* include generated headers with:
* EPICS_PVD_MAJOR_VERSION
* EPICS_PVD_MINOR_VERSION
* EPICS_PVD_MAINTENANCE_VERSION
* EPICS_PVD_DEVELOPMENT_FLAG
*/
#include <pv/pvdVersionNum.h>
#define PVDATA_VERSION_INT VERSION_INT(EPICS_PVD_MAJOR_VERSION, EPICS_PVD_MINOR_VERSION, EPICS_PVD_MAINTENANCE_VERSION, 0)
namespace epics{namespace pvData{
struct PVDataVersion {
unsigned major;
unsigned minor;
unsigned maint;
unsigned devel;
};
epicsShareExtern void getVersion(PVDataVersion *ptr);
}} // namespace epics::pvData
#endif // PVDVERSION_H

View File

@@ -1,7 +0,0 @@
#ifndef PVDVERSION_H
# error include pvdVersion.h, not this header
#endif
#define EPICS_PVD_MAJOR_VERSION @EPICS_PVD_MAJOR_VERSION@
#define EPICS_PVD_MINOR_VERSION @EPICS_PVD_MINOR_VERSION@
#define EPICS_PVD_MAINTENANCE_VERSION @EPICS_PVD_MAINTENANCE_VERSION@
#define EPICS_PVD_DEVELOPMENT_FLAG @EPICS_PVD_DEVELOPMENT_FLAG@

View File

@@ -66,7 +66,7 @@ public:
* getStandardField returns the singleton.
* @return Shared pointer to StandardField.
*/
static const StandardFieldPtr& getStandardField();
static StandardFieldPtr getStandardField();
~StandardField();
/** Create a structure that has a scalar value field.
* @param type The type.
@@ -250,9 +250,7 @@ private:
//friend StandardFieldPtr getStandardField();
};
FORCE_INLINE const StandardFieldPtr& getStandardField() {
return StandardField::getStandardField();
}
epicsShareExtern StandardFieldPtr getStandardField();
}}
#endif /* STANDARDFIELD_H */

View File

@@ -35,8 +35,7 @@ typedef std::tr1::shared_ptr<StandardPVField> StandardPVFieldPtr;
StandardPVField *standardPVField = getStandardPVField();
* }
*/
class epicsShareClass StandardPVField {
EPICS_NOT_COPYABLE(StandardPVField)
class epicsShareClass StandardPVField : private NoDefaultMethods {
public:
/**
* getStandardPVField returns the singleton.

View File

@@ -1,41 +0,0 @@
/* #define CASE(BASETYPE, PVATYPE, DBFTYPE, PVACODE)
*
* Helper for switch() on type code
*
* BASETYPE is suffix of type from epicsTypes.h
* epics ## BASETYPE -> eg. epicsUInt8
* PVATYPE is storage type
* PVATYPE -> eg epics::pvData::uint8
* DBFTYPE is suffix of DBF_*
* DBF_ ## DBFTYPE -> eg. DBF_UCHAR
* PVACODE is suffix of ScalarType enum
* epics::pvData::pv ## PVACODE -> epics::pvData::pvUByte
*/
CASE(UInt8, epics::pvData::uint8, UCHAR, UByte)
CASE(Int8, epics::pvData::int8, CHAR, Byte)
CASE(UInt16, epics::pvData::uint16, USHORT, UShort)
CASE(Int16, epics::pvData::int16, SHORT, Short)
CASE(UInt32, epics::pvData::uint32, ULONG, UInt)
CASE(Int32, epics::pvData::int32, LONG, Int)
CASE(Float32, float, FLOAT, Float)
CASE(Float64, double, DOUBLE, Double)
#ifndef CASE_SKIP_BOOL
CASE(UInt8, epics::pvData::boolean, UCHAR, Boolean)
#endif
#ifdef CASE_REAL_INT64
CASE(UInt64, epics::pvData::uint64, UINT64, ULong)
CASE(Int64, epics::pvData::int64, INT64, Long)
#elif defined(CASE_SQUEEZE_INT64)
CASE(UInt32, epics::pvData::uint64, ULONG, ULong)
CASE(Int32, epics::pvData::int64, LONG, Long)
#else
/* nothing */
#endif
#ifdef CASE_ENUM
// yes really, Base uses SHORT (16-bit) while PVD uses Int (32-bit)
CASE(Enum16, epics::pvData::int32, ENUM, Int)
#endif
#ifdef CASE_STRING
CASE(Int8, std::string, CHAR, String)
#endif
/* #undef CASE */

View File

@@ -1,295 +0,0 @@
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
#include <memory>
#define epicsExportSharedSymbols
#include <pv/pvData.h>
#include <pv/valueBuilder.h>
namespace epics{namespace pvData{
struct ValueBuilder::child {
virtual ~child() {}
Type type;
child(Type t) : type(t) {}
virtual void build(const std::string& name, FieldBuilderPtr& builder)=0;
virtual void store(const PVFieldPtr& val)=0;
};
struct ValueBuilder::child_struct : public ValueBuilder::child
{
virtual ~child_struct() {}
child_struct(ValueBuilder *par, const std::string& id)
:child(structure)
,builder(par, id)
{}
ValueBuilder builder;
virtual void build(const std::string& name, FieldBuilderPtr& builder) OVERRIDE FINAL
{
FieldBuilderPtr nest(builder->addNestedStructure(name));
buildStruct(this->builder, nest);
builder = nest->endNested();
}
virtual void store(const PVFieldPtr& val) OVERRIDE FINAL
{
if(val->getField()->getType()!=structure)
THROW_EXCEPTION2(std::logic_error, "Structure type mis-match");
PVStructurePtr str(std::tr1::static_pointer_cast<PVStructure>(val));
storeStruct(builder, str);
}
static
void buildStruct(const ValueBuilder& self, FieldBuilderPtr& builder);
static
void storeStruct(const ValueBuilder& self, const PVStructurePtr& val);
static
void fillStruct(ValueBuilder& self, const PVStructure& val);
};
struct ValueBuilder::child_scalar_base : public ValueBuilder::child
{
virtual ~child_scalar_base() {}
ScalarType stype;
child_scalar_base(ScalarType s) : child(scalar), stype(s) {}
virtual void build(const std::string& name, FieldBuilderPtr& builder) OVERRIDE FINAL
{
builder->add(name, stype);
}
};
struct ValueBuilder::child_scalar_array : public ValueBuilder::child
{
virtual ~child_scalar_array() {}
shared_vector<const void> array;
child_scalar_array(const shared_vector<const void>& v) : child(scalarArray), array(v) {}
virtual void build(const std::string& name, FieldBuilderPtr& builder) OVERRIDE FINAL
{
builder->addArray(name, array.original_type());
}
virtual void store(const PVFieldPtr& val)
{
if(val->getField()->getType()!=scalarArray)
THROW_EXCEPTION2(std::logic_error, "Scalar Array type mis-match");
PVScalarArrayPtr arr(std::tr1::static_pointer_cast<PVScalarArray>(val));
arr->putFrom(array);
}
};
template <typename T>
struct ValueBuilder::child_scalar : public ValueBuilder::child_scalar_base
{
virtual ~child_scalar() {}
T value;
child_scalar(const void* v) : child_scalar_base(static_cast<ScalarType>(ScalarTypeID<T>::value)), value(*static_cast<const T*>(v)) {}
virtual void store(const PVFieldPtr& val) OVERRIDE FINAL
{
if(val->getField()->getType()!=scalar)
THROW_EXCEPTION2(std::logic_error, "Scalar type mis-match");
PVScalarPtr scalar(std::tr1::static_pointer_cast<PVScalar>(val));
scalar->putFrom(value);
}
};
ValueBuilder::ValueBuilder(const std::string &id) :parent(0),id(id) {}
void ValueBuilder::child_struct::fillStruct(ValueBuilder& self, const PVStructure& val)
{
StructureConstPtr type(val.getStructure());
const StringArray& field = type->getFieldNames();
for(StringArray::const_iterator it=field.begin(), end=field.end(); it!=end; ++it)
{
PVField::const_shared_pointer sub(val.getSubField(*it));
assert(sub);
FieldConstPtr subtype(sub->getField());
switch(subtype->getType()) {
case scalar:
{
const PVScalar* subs(static_cast<const PVScalar*>(sub.get()));
ScalarType stype = subs->getScalar()->getScalarType();
switch(stype) {
#define STYPE(stype) case pv##stype: { const PV ##stype* ptr(static_cast<const PV##stype*>(subs)); PV##stype::value_type temp(ptr->get()); self._add(*it, pv##stype, &temp); } break
STYPE(Boolean);
STYPE(Byte);
STYPE(Short);
STYPE(Int);
STYPE(Long);
STYPE(UByte);
STYPE(UShort);
STYPE(UInt);
STYPE(ULong);
STYPE(Float);
STYPE(Double);
STYPE(String);
#undef STYPE
}
}
break;
case structure:
self._add(*it, *static_cast<const PVStructure*>(sub.get()));
break;
default:
THROW_EXCEPTION2(std::runtime_error, "ValueBuilder can only clone scalar and structure");
}
}
}
ValueBuilder::ValueBuilder(const PVStructure& clone) :parent(0)
{
StructureConstPtr ctype(clone.getStructure());
id = ctype->getID();
child_struct::fillStruct(*this, clone);
}
ValueBuilder::ValueBuilder(ValueBuilder* par, const std::string &id)
:parent(par)
,id(id)
{}
ValueBuilder::~ValueBuilder()
{
for(children_t::const_iterator it=children.begin(), end=children.end(); it!=end; ++it)
delete it->second;
children.clear();
}
void ValueBuilder::_add(const std::string& name, const PVStructure& V)
{
StructureConstPtr T(V.getStructure());
ValueBuilder& self = addNested(name, structure, T->getID());
child_struct::fillStruct(self, V);
self.endNested();
}
void ValueBuilder::_add(const std::string& name, ScalarType stype, const void *V)
{
const children_t::iterator it(children.find(name));
if(it!=children.end()) {
if(it->second->type!=scalar && it->second->type!=scalarArray)
THROW_EXCEPTION2(std::logic_error, "Not allowed to replace field. wrong type");
}
epics::auto_ptr<child> store;
switch(stype) {
#define STYPE(stype) case stype: store.reset(new child_scalar<ScalarTypeTraits<stype>::type>(V)); break
STYPE(pvBoolean);
STYPE(pvByte);
STYPE(pvShort);
STYPE(pvInt);
STYPE(pvLong);
STYPE(pvUByte);
STYPE(pvUShort);
STYPE(pvUInt);
STYPE(pvULong);
STYPE(pvFloat);
STYPE(pvDouble);
STYPE(pvString);
#undef STYPE
}
if(!store.get())
THROW_EXCEPTION2(std::logic_error, "Unhandled ScalarType");
if(it!=children.end()) {
delete it->second;
children.erase(it);
}
children[name] = store.get();
store.release();
}
void ValueBuilder::_add(const std::string& name, const shared_vector<const void>& V)
{
const children_t::iterator it(children.find(name));
if(it!=children.end()) {
if(it->second->type!=scalar && it->second->type!=scalarArray)
THROW_EXCEPTION2(std::logic_error, "Not allowed to replace field. wrong type");
}
epics::auto_ptr<child> store(new child_scalar_array(V));
children[name] = store.get();
store.release();
}
ValueBuilder& ValueBuilder::addNested(const std::string& name, Type type, const std::string &id)
{
if(type!=structure)
THROW_EXCEPTION2(std::invalid_argument, "addNested() only supports structure");
child_struct *sub;
children_t::const_iterator it(children.find(name));
if(it==children.end()) {
epics::auto_ptr<child_struct> store(new child_struct(this, id));
sub = store.get();
children[name] = store.get();
store.release();
} else if(it->second->type==structure) {
sub = static_cast<child_struct*>(it->second);
} else {
std::ostringstream msg;
msg<<"Can't replace non-struct field '"<<name<<"' with struct";
THROW_EXCEPTION2(std::invalid_argument, msg.str());
}
sub->builder.id = id;
return sub->builder;
}
ValueBuilder& ValueBuilder::endNested()
{
if(!parent) {
THROW_EXCEPTION2(std::logic_error, "Can't end top of structure");
}
return *parent;
}
void ValueBuilder::child_struct::buildStruct(const ValueBuilder& self, FieldBuilderPtr& builder)
{
if(!self.id.empty())
builder->setId(self.id);
for(children_t::const_iterator it=self.children.begin(), end=self.children.end(); it!=end; ++it)
{
it->second->build(it->first, builder);
}
}
void ValueBuilder::child_struct::storeStruct(const ValueBuilder& self, const PVStructurePtr& val)
{
for(children_t::const_iterator it=self.children.begin(), end=self.children.end(); it!=end; ++it)
{
it->second->store(val->getSubFieldT(it->first));
}
}
PVStructure::shared_pointer ValueBuilder::buildPVStructure() const
{
if(parent)
THROW_EXCEPTION2(std::logic_error, "Only top level structure may be built. Missing endNested() ?");
StructureConstPtr type;
{
FieldBuilderPtr tbuild(getFieldCreate()->createFieldBuilder());
child_struct::buildStruct(*this, tbuild);
type = tbuild->createStructure();
}
PVStructure::shared_pointer root(getPVDataCreate()->createPVStructure(type));
child_struct::storeStruct(*this, root);
return root;
}
}}// namespace epics::pvData

View File

@@ -1,101 +0,0 @@
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
#ifndef VALUEBUILDER_H
#define VALUEBUILDER_H
#include <map>
#include <pv/templateMeta.h>
#include <pv/pvIntrospect.h>
#include <pv/sharedVector.h>
namespace epics{namespace pvData{
class PVStructure;
/** Incrementally define and initialize a PVStructure
*
* Equivalent to FieldBuilder with the added ability to assign initial values.
*
@code
epics::pvData::PVStructurePtr val(epics::pvData::ValueBuilder()
.add<pvInt>("intfld", 42)
.addNested("sub")
.add<pvString>("strfld", "testing")
.endNested()
.buildPVStructure());
@endcode
*/
class epicsShareClass ValueBuilder
{
public:
//! empty structure
explicit ValueBuilder(const std::string& id=std::string());
//! Clone existing definition and value
explicit ValueBuilder(const PVStructure&);
~ValueBuilder();
//! Add a scalar field with a given name and initial value
template<ScalarType ENUM>
FORCE_INLINE ValueBuilder& add(const std::string& name, typename meta::arg_type<typename ScalarTypeTraits<ENUM>::type>::type V)
{
_add(name, ENUM, &V);
return *this;
}
//! Add a scalar array field
template<class T>
FORCE_INLINE ValueBuilder& add(const std::string& name, const shared_vector<const T>& V)
{
_add(name, V);
return *this;
}
FORCE_INLINE ValueBuilder& add(const std::string& name, const PVStructure& V) {
_add(name, V);
return *this;
}
//! Start a sub-structure
ValueBuilder& addNested(const std::string& name, Type type=structure, const std::string& id = std::string());
//! End a sub-structure
ValueBuilder& endNested();
/** Complete building structure
*
* @note ValueBuilder may be re-used after calling buildPVStructure()
*/
std::tr1::shared_ptr<PVStructure> buildPVStructure() const;
private:
void _add(const std::string& name, ScalarType stype, const void *V);
void _add(const std::string& name, const shared_vector<const void> &V);
void _add(const std::string& name, const PVStructure& V);
ValueBuilder(ValueBuilder*, const std::string &id = std::string());
ValueBuilder * const parent;
struct child;
friend struct child;
struct child_struct;
friend struct child_struct;
struct child_scalar_base;
friend struct child_scalar_base;
template <typename T> struct child_scalar;
template <typename T> friend struct child_scalar;
struct child_scalar_array;
friend struct child_scalar_array;
typedef std::map<std::string, child*> children_t;
children_t children;
std::string id;
ValueBuilder(const ValueBuilder&);
ValueBuilder& operator=(const ValueBuilder&);
};
}}// namespace epics::pvData
#endif // VALUEBUILDER_H

View File

@@ -9,6 +9,7 @@
#ifndef BITSETUTIL_H
#define BITSETUTIL_H
#include <pv/noDefaultMethods.h>
#include <pv/pvData.h>
#include <pv/bitSet.h>
@@ -20,7 +21,7 @@ namespace epics { namespace pvData {
* @brief Compress a bitSet.
*
*/
class epicsShareClass BitSetUtil {
class epicsShareClass BitSetUtil : private NoDefaultMethods {
public:
/**
* compress the bitSet for a pvStructure.

View File

@@ -12,18 +12,18 @@
#include <stdio.h>
#include <iostream>
#include <pv/pvUnitTest.h>
#include <epicsUnitTest.h>
#include <testMain.h>
#include <pv/createRequest.h>
namespace {
using namespace epics::pvData;
using std::string;
using std::cout;
using std::endl;
static bool debug = false;
static void testCreateRequestInternal() {
printf("testCreateRequest... \n");
CreateRequest::shared_pointer createRequest = CreateRequest::create();
@@ -32,19 +32,19 @@ static void testCreateRequestInternal() {
string out;
string request = "";
if(debug) { cout << "request " << request <<endl;}
PVStructurePtr pvRequest = createRequest->createRequest(request);
if(!pvRequest) { cout<< createRequest->getMessage() << endl;}
if(debug) { cout << pvRequest << endl;}
testOk1(pvRequest.get()!=NULL);
testOk1(pvRequest->getStructure()->getNumberFields()==0);
testPass("request %s",request.c_str());
request = "record[]field()getField()putField()";
if(debug) { cout << "request " << request <<endl;}
pvRequest = createRequest->createRequest(request);
if(!pvRequest) { cout<< createRequest->getMessage() << endl;}
if(debug) { cout << pvRequest << endl;}
testOk1(pvRequest.get()!=NULL);
testOk1(pvRequest->getSubField<PVStructure>("field").get()!=NULL);
testOk1(pvRequest->getSubField<PVStructure>("putField").get()!=NULL);
@@ -52,10 +52,10 @@ static void testCreateRequestInternal() {
testPass("request %s",request.c_str());
request = "record[a=b,x=y]field(a) putField(a),getField(a)";
if(debug) { cout << "request " << request <<endl;}
pvRequest = createRequest->createRequest(request);
if(!pvRequest) { cout<< createRequest->getMessage() << endl;}
if(debug) { cout << pvRequest << endl;}
testOk1(pvRequest.get()!=NULL);
pvString = pvRequest->getSubField<PVString>("record._options.a");
sval = pvString->get();
@@ -69,10 +69,10 @@ static void testCreateRequestInternal() {
testPass("request %s",request.c_str());
request = "field(a.b[x=y])";
if(debug) { cout << "request " << request <<endl;}
pvRequest = createRequest->createRequest(request);
if(!pvRequest) { cout<< createRequest->getMessage() << endl;}
if(debug) { cout << pvRequest << endl;}
testOk1(pvRequest.get()!=NULL);
pvString = pvRequest->getSubField<PVString>("field.a.b._options.x");
sval = pvString->get();
@@ -80,19 +80,19 @@ static void testCreateRequestInternal() {
testPass("request %s",request.c_str());
request = "field(a.b{c.d})";
if(debug) { cout << "request " << request <<endl;}
pvRequest = createRequest->createRequest(request);
if(!pvRequest) { cout<< createRequest->getMessage() << endl;}
if(debug) { cout << pvRequest << endl;}
testOk1(pvRequest.get()!=NULL);
testOk1(pvRequest->getSubField("field.a.b.c.d").get()!=NULL);
testPass("request %s",request.c_str());
request = "field(a.b[x=y]{c.d})";
if(debug) { cout << "request " << request <<endl;}
pvRequest = createRequest->createRequest(request);
if(!pvRequest) { cout<< createRequest->getMessage() << endl;}
if(debug) { cout << pvRequest << endl;}
testOk1(pvRequest.get()!=NULL);
pvString = pvRequest->getSubField<PVString>("field.a.b._options.x");
sval = pvString->get();
@@ -101,10 +101,10 @@ static void testCreateRequestInternal() {
testPass("request %s",request.c_str());
request = "field(a.b[x=y]{c.d[x=y]})";
if(debug) { cout << "request " << request <<endl;}
pvRequest = createRequest->createRequest(request);
if(!pvRequest) { cout<< createRequest->getMessage() << endl;}
if(debug) { cout << pvRequest << endl;}
testOk1(pvRequest.get()!=NULL);
pvString = pvRequest->getSubField<PVString>("field.a.b._options.x");
sval = pvString->get();
@@ -115,10 +115,10 @@ static void testCreateRequestInternal() {
testPass("request %s",request.c_str());
request = "record[a=b,c=d] field(a.a[a=b]{a.a[a=b]},b.a[a=b]{a,b})";
if(debug) { cout << "request " << request <<endl;}
pvRequest = createRequest->createRequest(request);
if(!pvRequest) { cout<< createRequest->getMessage() << endl;}
if(debug) { cout << pvRequest << endl;}
testOk1(pvRequest.get()!=NULL);
pvString = pvRequest->getSubField<PVString>("field.a.a._options.a");
sval = pvString->get();
@@ -135,10 +135,10 @@ static void testCreateRequestInternal() {
request = "alarm,timeStamp,power.value";
if(debug) { cout << "request " << request <<endl;}
pvRequest = createRequest->createRequest(request);
if(!pvRequest) { cout<< createRequest->getMessage() << endl;}
if(debug) { cout << pvRequest << endl;}
testOk1(pvRequest.get()!=NULL);
testOk1(pvRequest->getSubField<PVStructure>("field.alarm").get()!=NULL);
testOk1(pvRequest->getSubField<PVStructure>("field.timeStamp").get()!=NULL);
@@ -146,10 +146,10 @@ static void testCreateRequestInternal() {
testPass("request %s",request.c_str());
request = "record[process=true]field(alarm,timeStamp,power.value)";
if(debug) { cout << "request " << request <<endl;}
pvRequest = createRequest->createRequest(request);
if(!pvRequest) { cout<< createRequest->getMessage() << endl;}
if(debug) { cout << pvRequest << endl;}
testOk1(pvRequest.get()!=NULL);
pvString = pvRequest->getSubField<PVString>("record._options.process");
sval = pvString->get();
@@ -160,10 +160,10 @@ static void testCreateRequestInternal() {
testPass("request %s",request.c_str());
request = "record[process=true]field(alarm,timeStamp[algorithm=onChange,causeMonitor=false],power{value,alarm})";
if(debug) { cout << "request " << request <<endl;}
pvRequest = createRequest->createRequest(request);
if(!pvRequest) { cout<< createRequest->getMessage() << endl;}
if(debug) { cout << pvRequest << endl;}
testOk1(pvRequest.get()!=NULL);
pvString = pvRequest->getSubField<PVString>("record._options.process");
sval = pvString->get();
@@ -181,10 +181,10 @@ static void testCreateRequestInternal() {
testPass("request %s",request.c_str());
request = "record[int=2,float=3.14159]field(alarm,timeStamp[shareData=true],power.value)";
if(debug) { cout << "request " << request <<endl;}
pvRequest = createRequest->createRequest(request);
if(!pvRequest) { cout<< createRequest->getMessage() << endl;}
if(debug) { cout << pvRequest << endl;}
testOk1(pvRequest.get()!=NULL);
pvString = pvRequest->getSubField<PVString>("record._options.int");
sval = pvString->get();
@@ -203,10 +203,10 @@ static void testCreateRequestInternal() {
+ "putField(power.value)"
+ "getField(alarm,timeStamp,power{value,alarm},"
+ "current{value,alarm},voltage{value,alarm})";
if(debug) { cout << "request " << request <<endl;}
pvRequest = createRequest->createRequest(request);
if(!pvRequest) { cout<< createRequest->getMessage() << endl;}
if(debug) { cout << pvRequest << endl;}
testOk1(pvRequest.get()!=NULL);
testOk1(pvRequest->getSubField<PVStructure>("putField.power.value").get()!=NULL);
testOk1(pvRequest->getSubField<PVStructure>("getField.alarm").get()!=NULL);
@@ -223,10 +223,10 @@ static void testCreateRequestInternal() {
+ "zero{voltage.value,current.value,power.value},"
+ "one{voltage.value,current.value,power.value}"
+ "})";
if(debug) { cout << "request " << request <<endl;}
pvRequest = createRequest->createRequest(request);
if(!pvRequest) { cout<< createRequest->getMessage() << endl;}
if(debug) { cout << pvRequest << endl;}
testOk1(pvRequest.get()!=NULL);
testOk1(pvRequest->getSubField<PVStructure>("field.alarm").get()!=NULL);
testOk1(pvRequest->getSubField<PVStructure>("field.timeStamp").get()!=NULL);
@@ -245,10 +245,10 @@ static void testCreateRequestInternal() {
+ "ps0{alarm,timeStamp,power{value,alarm},current{value,alarm},voltage{value,alarm}},"
+ "ps1{alarm,timeStamp,power{value,alarm},current{value,alarm},voltage{value,alarm}}"
+ ")";
if(debug) { cout << "request " << request <<endl;}
pvRequest = createRequest->createRequest(request);
if(!pvRequest) { cout<< createRequest->getMessage() << endl;}
if(debug) { cout << pvRequest << endl;}
testOk1(pvRequest.get()!=NULL);
testOk1(pvRequest->getSubField<PVStructure>("putField.power.value").get()!=NULL);
testOk1(pvRequest->getSubField<PVStructure>("getField.alarm").get()!=NULL);
@@ -278,10 +278,10 @@ static void testCreateRequestInternal() {
testPass("request %s",request.c_str());
request = "a{b{c{d}}}";
if(debug) { cout << "request " << request <<endl;}
pvRequest = createRequest->createRequest(request);
if(!pvRequest) { cout<< createRequest->getMessage() << endl;}
if(debug) { cout << pvRequest << endl;}
testOk1(pvRequest.get()!=NULL);
testOk1(pvRequest->getSubField<PVStructure>("field.a.b.c.d").get()!=NULL);
testPass("request %s",request.c_str());
@@ -293,14 +293,14 @@ static void testCreateRequestInternal() {
+ "ps0{alarm,timeStamp,power{value,alarm},current{value,alarm},voltage{value,alarm}},"
+ "ps1{alarm,timeStamp,power{value,alarm},current{value,alarm},voltage{value,alarm}"
+ ")";
if(debug) { cout << "request " << request <<endl;}
cout << endl << "Error Expected for next call!!" << endl;
pvRequest = createRequest->createRequest(request);
cout << "reason " << createRequest->getMessage() << endl;
testPass("request %s",request.c_str());
request = "record[process=true,power.value";
if(debug) { cout << "request " << request <<endl;}
cout << endl << "Error Expected for next call!!" << endl;
pvRequest = createRequest->createRequest(request);
cout << "reason " << createRequest->getMessage() << endl;
@@ -308,7 +308,7 @@ static void testCreateRequestInternal() {
testPass("request %s",request.c_str());
request = "field(alarm.status,alarm.severity)";
if(debug) { cout << "request " << request <<endl;}
cout << endl << "Error Expected for next call!!" << endl;
pvRequest = createRequest->createRequest(request);
cout << "reason " << createRequest->getMessage() << endl;
@@ -316,7 +316,7 @@ static void testCreateRequestInternal() {
testPass("request %s",request.c_str());
request = ":field(record[process=false]power.value)";
if(debug) { cout << "request " << request <<endl;}
cout << endl << "Error Expected for next call!!" << endl;
pvRequest = createRequest->createRequest(request);
cout << "reason " << createRequest->getMessage() << endl;
@@ -324,26 +324,10 @@ static void testCreateRequestInternal() {
testPass("request %s",request.c_str());
}
static void testBadRequest()
{
testThrows(std::runtime_error, createRequest("field("));
testThrows(std::runtime_error, createRequest("record[field()"));
CreateRequest::shared_pointer C(CreateRequest::create());
testOk1(!C->createRequest("field("));
testDiag("message %s", C->getMessage().c_str());
testOk1(!!C->createRequest("field(value)"));
testOk1(C->getMessage().empty());
}
} // namespace
MAIN(testCreateRequest)
{
testPlan(126);
testPlan(121);
testCreateRequestInternal();
testBadRequest();
return testDone();
}

View File

@@ -31,6 +31,8 @@ using namespace std;
using std::tr1::static_pointer_cast;
using namespace epics::pvData;
static bool debug = true;
static void testPVScalar(
string const & valueNameMaster,
string const & valueNameCopy,
@@ -47,34 +49,58 @@ static void testPVScalar(
pvValueMaster = pvMaster->getSubField<PVScalar>(valueNameMaster);
convert->fromDouble(pvValueMaster,.04);
StructureConstPtr structure = pvCopy->getStructure();
if(debug) { cout << "structure from copy" << endl << *structure << endl; }
pvStructureCopy = pvCopy->createPVStructure();
pvValueCopy = pvStructureCopy->getSubField<PVScalar>(valueNameCopy);
bitSet = BitSetPtr(new BitSet(pvStructureCopy->getNumberFields()));
pvCopy->initCopy(pvStructureCopy, bitSet);
if(debug) { cout << "after initCopy pvValueCopy " << convert->toDouble(pvValueCopy); }
if(debug) { cout << endl; }
convert->fromDouble(pvValueMaster,.06);
testOk1(convert->toDouble(pvValueCopy)==.04);
pvCopy->updateCopySetBitSet(pvStructureCopy,bitSet);
testOk1(convert->toDouble(pvValueCopy)==.06);
testOk1(bitSet->get(pvValueCopy->getFieldOffset()));
if(debug) { cout << "after put(.06) pvValueCopy " << convert->toDouble(pvValueCopy); }
if(debug) { cout << " bitSet " << *bitSet; }
if(debug) { cout << endl; }
offset = pvCopy->getCopyOffset(pvValueMaster);
if(debug) { cout << "getCopyOffset() " << offset; }
if(debug) { cout << " pvValueCopy->getOffset() " << pvValueCopy->getFieldOffset(); }
if(debug) { cout << " pvValueMaster->getOffset() " << pvValueMaster->getFieldOffset(); }
if(debug) { cout << " bitSet " << *bitSet; }
if(debug) { cout << endl; }
bitSet->clear();
convert->fromDouble(pvValueMaster,1.0);
if(debug) { cout << "before updateCopyFromBitSet"; }
if(debug) { cout << " masterValue " << convert->toDouble(pvValueMaster); }
if(debug) { cout << " copyValue " << convert->toDouble(pvValueCopy); }
if(debug) { cout << " bitSet " << *bitSet; }
if(debug) { cout << endl; }
bitSet->set(0);
testOk1(convert->toDouble(pvValueCopy)==0.06);
pvCopy->updateCopyFromBitSet(pvStructureCopy,bitSet);
testOk1(convert->toDouble(pvValueCopy)==1.0);
if(debug) { cout << "after updateCopyFromBitSet"; }
if(debug) { cout << " masterValue " << convert->toDouble(pvValueMaster); }
if(debug) { cout << " copyValue " << convert->toDouble(pvValueCopy); }
if(debug) { cout << " bitSet " << *bitSet; }
if(debug) { cout << endl; }
convert->fromDouble(pvValueCopy,2.0);
bitSet->set(0);
if(debug) { cout << "before updateMaster"; }
if(debug) { cout << " masterValue " << convert->toDouble(pvValueMaster); }
if(debug) { cout << " copyValue " << convert->toDouble(pvValueCopy); }
if(debug) { cout << " bitSet " << *bitSet; }
if(debug) { cout << endl; }
testOk1(convert->toDouble(pvValueMaster)==1.0);
pvCopy->updateMaster(pvStructureCopy,bitSet);
testOk1(convert->toDouble(pvValueMaster)==2.0);
if(debug) { cout << "after updateMaster"; }
if(debug) { cout << " masterValue " << convert->toDouble(pvValueMaster); }
if(debug) { cout << " copyValue " << convert->toDouble(pvValueCopy); }
if(debug) { cout << " bitSet " << *bitSet; }
if(debug) { cout << endl; }
}
static void testPVScalarArray(
@@ -97,12 +123,13 @@ static void testPVScalarArray(
const shared_vector<const double> xxx(freeze(values));
pvValueMaster->putFrom(xxx);
StructureConstPtr structure = pvCopy->getStructure();
if(debug) { cout << "structure from copy" << endl << *structure << endl;}
pvStructureCopy = pvCopy->createPVStructure();
pvValueCopy = pvStructureCopy->getSubField<PVScalarArray>(valueNameCopy);
bitSet = BitSetPtr(new BitSet(pvStructureCopy->getNumberFields()));
pvCopy->initCopy(pvStructureCopy, bitSet);
if(debug) { cout << "after initCopy pvValueCopy " << *pvValueCopy << endl; }
if(debug) { cout << endl; }
values.resize(n);
for(size_t i=0; i<n; i++) values[i] = i + .06;
const shared_vector<const double> yyy(freeze(values));
@@ -112,38 +139,61 @@ static void testPVScalarArray(
pvCopy->updateCopySetBitSet(pvStructureCopy,bitSet);
pvValueCopy->getAs(cvalues);
testOk1(cvalues[0]==0.06);
if(debug) { cout << "after put(i+ .06) pvValueCopy " << *pvValueCopy << endl; }
if(debug) { cout << " bitSet " << *bitSet; }
if(debug) { cout << endl; }
offset = pvCopy->getCopyOffset(pvValueMaster);
if(debug) { cout << "getCopyOffset() " << offset; }
if(debug) { cout << " pvValueCopy->getOffset() " << pvValueCopy->getFieldOffset(); }
if(debug) { cout << " pvValueMaster->getOffset() " << pvValueMaster->getFieldOffset(); }
if(debug) { cout << " bitSet " << *bitSet; }
if(debug) { cout << endl; }
bitSet->clear();
values.resize(n);
for(size_t i=0; i<n; i++) values[i] = i + 1.0;
const shared_vector<const double> zzz(freeze(values));
pvValueMaster->putFrom(zzz);
if(debug) { cout << "before updateCopyFromBitSet"; }
if(debug) { cout << " masterValue " << *pvValueMaster << endl; }
if(debug) { cout << " copyValue " << *pvValueCopy << endl; }
if(debug) { cout << " bitSet " << *bitSet; }
if(debug) { cout << endl; }
bitSet->set(0);
pvValueCopy->getAs(cvalues);
testOk1(cvalues[0]==0.06);
pvCopy->updateCopyFromBitSet(pvStructureCopy,bitSet);
pvValueCopy->getAs(cvalues);
testOk1(cvalues[0]==1.0);
if(debug) { cout << "after updateCopyFromBitSet"; }
if(debug) { cout << " masterValue " << *pvValueMaster << endl; }
if(debug) { cout << " copyValue " << *pvValueCopy << endl; }
if(debug) { cout << " bitSet " << *bitSet; }
if(debug) { cout << endl; }
values.resize(n);
for(size_t i=0; i<n; i++) values[i] = i + 2.0;
const shared_vector<const double> ttt(freeze(values));
pvValueMaster->putFrom(ttt);
bitSet->set(0);
if(debug) { cout << "before updateMaster"; }
if(debug) { cout << " masterValue " << *pvValueMaster << endl; }
if(debug) { cout << " copyValue " << *pvValueCopy << endl; }
if(debug) { cout << " bitSet " << *bitSet; }
if(debug) { cout << endl; }
pvValueMaster->getAs(cvalues);
testOk1(cvalues[0]==2.0);
pvCopy->updateMaster(pvStructureCopy,bitSet);
pvValueMaster->getAs(cvalues);
testOk1(cvalues[0]==1.0);
if(debug) { cout << "before updateMaster"; }
if(debug) { cout << " masterValue " << *pvValueMaster << endl; }
if(debug) { cout << " copyValue " << *pvValueCopy << endl; }
if(debug) { cout << " bitSet " << *bitSet; }
if(debug) { cout << endl; }
}
static void scalarTest()
{
testDiag("scalarTest()");
if(debug) { cout << endl << endl << "****scalarTest****" << endl; }
PVStructurePtr pvMaster;
string request;
PVStructurePtr pvRequest;
@@ -158,20 +208,24 @@ static void scalarTest()
valueNameMaster = request = "value";
CreateRequest::shared_pointer createRequest = CreateRequest::create();
pvRequest = createRequest->createRequest(request);
if(debug) { cout << "request " << request << endl; }
if(debug) { cout << "pvRequest\n" << *pvRequest << endl; }
pvCopy = PVCopy::create(pvMaster,pvRequest,"");
valueNameCopy = "value";
testPVScalar(valueNameMaster,valueNameCopy,pvMaster,pvCopy);
request = "";
valueNameMaster = "value";
pvRequest = createRequest->createRequest(request);
if(debug) { cout << "request " << request << endl; }
if(debug) { cout << "pvRequest\n" << *pvRequest << endl; }
pvCopy = PVCopy::create(pvMaster,pvRequest,"");
valueNameCopy = "value";
testPVScalar(valueNameMaster,valueNameCopy,pvMaster,pvCopy);
request = "alarm,timeStamp,value";
valueNameMaster = "value";
pvRequest = createRequest->createRequest(request);
if(debug) { cout << "request " << request << endl; }
if(debug) { cout << "pvRequest\n" << *pvRequest << endl; }
pvCopy = PVCopy::create(pvMaster,pvRequest,"");
valueNameCopy = "value";
testPVScalar(valueNameMaster,valueNameCopy,pvMaster,pvCopy);
@@ -179,8 +233,7 @@ static void scalarTest()
static void arrayTest()
{
testDiag("arrayTest");
if(debug) { cout << endl << endl << "****arrayTest****" << endl; }
PVStructurePtr pvMaster;
string request;
PVStructurePtr pvRequest;
@@ -194,21 +247,24 @@ static void arrayTest()
pvMaster = standardPVField->scalarArray(pvDouble,"alarm,timeStamp");
valueNameMaster = request = "value";
pvRequest = createRequest->createRequest(request);
if(debug) { cout << "request " << request << endl; }
if(debug) { cout << "pvRequest\n" << *pvRequest << endl; }
pvCopy = PVCopy::create(pvMaster,pvRequest,"");
valueNameCopy = "value";
testPVScalarArray(valueNameMaster,valueNameCopy,pvMaster,pvCopy);
request = "";
valueNameMaster = "value";
pvRequest = createRequest->createRequest(request);
if(debug) { cout << "request " << request << endl; }
if(debug) { cout << "pvRequest\n" << *pvRequest << endl; }
pvCopy = PVCopy::create(pvMaster,pvRequest,"");
valueNameCopy = "value";
testPVScalarArray(valueNameMaster,valueNameCopy,pvMaster,pvCopy);
request = "alarm,timeStamp,value";
valueNameMaster = "value";
pvRequest = createRequest->createRequest(request);
if(debug) { cout << "request " << request << endl; }
if(debug) { cout << "pvRequest\n" << *pvRequest << endl; }
pvCopy = PVCopy::create(pvMaster,pvRequest,"");
valueNameCopy = "value";
testPVScalarArray(valueNameMaster,valueNameCopy,pvMaster,pvCopy);
@@ -244,8 +300,7 @@ static PVStructurePtr createPowerSupply()
static void powerSupplyTest()
{
testDiag("powerSupplyTest");
if(debug) { cout << endl << endl << "****powerSupplyTest****" << endl; }
PVStructurePtr pvMaster;
string request;
PVStructurePtr pvRequest;
@@ -259,28 +314,32 @@ static void powerSupplyTest()
pvMaster = createPowerSupply();
valueNameMaster = request = "power.value";
pvRequest = createRequest->createRequest(request);
if(debug) { cout << "request " << request << endl; }
if(debug) { cout << "pvRequest\n" << *pvRequest << endl; }
pvCopy = PVCopy::create(pvMaster,pvRequest,"");
valueNameCopy = "power.value";
testPVScalar(valueNameMaster,valueNameCopy,pvMaster,pvCopy);
request = "";
valueNameMaster = "power.value";
pvRequest = createRequest->createRequest(request);
if(debug) { cout << "request " << request << endl; }
if(debug) { cout << "pvRequest\n" << *pvRequest << endl; }
pvCopy = PVCopy::create(pvMaster,pvRequest,"");
valueNameCopy = "power.value";
testPVScalar(valueNameMaster,valueNameCopy,pvMaster,pvCopy);
request = "alarm,timeStamp,voltage.value,power.value,current.value";
valueNameMaster = "power.value";
pvRequest = createRequest->createRequest(request);
if(debug) { cout << "request " << request << endl; }
if(debug) { cout << "pvRequest\n" << *pvRequest << endl; }
pvCopy = PVCopy::create(pvMaster,pvRequest,"");
valueNameCopy = "power.value";
testPVScalar(valueNameMaster,valueNameCopy,pvMaster,pvCopy);
request = "alarm,timeStamp,voltage{value,alarm},power{value,alarm,display},current.value";
valueNameMaster = "power.value";
pvRequest = createRequest->createRequest(request);
if(debug) { cout << "request " << request << endl; }
if(debug) { cout << "pvRequest\n" << *pvRequest << endl; }
pvCopy = PVCopy::create(pvMaster,pvRequest,"");
valueNameCopy = "power.value";
testPVScalar(valueNameMaster,valueNameCopy,pvMaster,pvCopy);

Some files were not shown because too many files have changed in this diff Show More