Compare commits
177 Commits
7.1.0-pre1
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f66492ce4b | ||
|
|
e069c699e9 | ||
|
|
8963b2dba1 | ||
|
|
9ec9a526f5 | ||
|
|
b4114589ff | ||
| 6689c9ce1f | |||
|
|
0c1773f25d | ||
|
|
d0ff9a9592 | ||
|
|
bf36070860 | ||
|
|
5552773e5e | ||
|
|
b3a1077270 | ||
|
|
1c5f75bcd6 | ||
|
|
2455039594 | ||
|
|
f47676c225 | ||
|
|
0881e0ed86 | ||
|
|
e457b60b30 | ||
|
|
a6746f8161 | ||
|
|
144f0228cc | ||
|
|
7300e6b0bd | ||
|
|
0ef8a36172 | ||
|
|
c0be043aaf | ||
|
|
dd74289eaf | ||
|
|
3da69257a0 | ||
|
|
13e4e577bb | ||
|
|
5387face45 | ||
|
|
eac2a8e70f | ||
|
|
04fcb7e38f | ||
|
|
45018a2163 | ||
|
|
b7ad4478a4 | ||
|
|
c16f19c80e | ||
|
|
87018882d1 | ||
| 2547514abb | |||
|
|
9447eacbd2 | ||
|
|
0b424a71ec | ||
|
|
45671faaea | ||
|
|
016d1154fc | ||
|
|
f3911d5831 | ||
|
|
93e90c5a04 | ||
|
|
8039c75b3e | ||
|
|
a647c8b174 | ||
|
|
d3b4976ea2 | ||
|
|
b1c8303870 | ||
|
|
a3ef984f4f | ||
|
|
ca86a63180 | ||
|
|
07b79693af | ||
|
|
4ef9e18ac6 | ||
|
|
320cc3c60b | ||
|
|
45d0d76a7f | ||
|
|
b2b42d5f8c | ||
|
|
90b1f2f6da | ||
|
|
2e775ef2fb | ||
|
|
f17d2bbca1 | ||
|
|
79b02254c4 | ||
|
|
19245ce805 | ||
|
|
0fa927afa7 | ||
|
|
d9b3f98e35 | ||
|
|
81e7968230 | ||
|
|
06f3b96992 | ||
| 1038182a16 | |||
| 60091bfe56 | |||
| f4de6dd9b1 | |||
|
|
3d93a80cce | ||
|
|
94eff54fa9 | ||
|
|
e9dde4d2f8 | ||
|
|
0a5e22e5b0 | ||
|
|
3a5cfba4e1 | ||
|
|
07b75e4543 | ||
|
|
e4f150b34f | ||
|
|
ca2ae0d0e7 | ||
|
|
828506720c | ||
|
|
ad712b63f9 | ||
|
|
4c3d5a788d | ||
|
|
df89135455 | ||
|
|
95d452870c | ||
|
|
8a82ff9fe4 | ||
|
|
dd24b2ad75 | ||
|
|
17fa7a7724 | ||
|
|
d01ba94ed7 | ||
|
|
c7c7585950 | ||
|
|
f869936cfd | ||
|
|
0ad253c6f0 | ||
|
|
52d04d5044 | ||
|
|
bff2dc9cd2 | ||
|
|
b903df5d0d | ||
|
|
416d910577 | ||
|
|
37b7a0708f | ||
|
|
6ceaa6adb0 | ||
|
|
0447826e7c | ||
|
|
3ef60a61a2 | ||
|
|
31802a8bde | ||
|
|
c2bc77a649 | ||
|
|
4dd7a18301 | ||
|
|
a29894ee2b | ||
|
|
2f8ac7f673 | ||
|
|
b050fbbcbe | ||
|
|
caa11605fc | ||
|
|
cfcdd1a3f9 | ||
|
|
95ff606ba1 | ||
|
|
4cc9b650c5 | ||
|
|
cd2436342d | ||
|
|
3ae2d09fe3 | ||
|
|
35b3403de6 | ||
|
|
f780ebdf76 | ||
|
|
93f0518b4b | ||
|
|
6da871fa64 | ||
|
|
d53cb0cbc9 | ||
|
|
8f0111e482 | ||
|
|
5525119778 | ||
|
|
6410600205 | ||
|
|
a5d44745d1 | ||
|
|
8c275cbc1c | ||
|
|
b79f69231c | ||
|
|
ff165595c4 | ||
|
|
12d851dc6f | ||
|
|
643f289c23 | ||
|
|
68e74ed1d2 | ||
|
|
f0ef0965c4 | ||
|
|
61ce532fdf | ||
|
|
d746e1bfb3 | ||
|
|
deccc41b9a | ||
|
|
4ffddfa2f6 | ||
|
|
2814c779bd | ||
|
|
4c73607799 | ||
|
|
d776f6eaf0 | ||
|
|
706ef01782 | ||
|
|
00c62cbd67 | ||
|
|
da8f3d6cc7 | ||
|
|
0bc95e51c2 | ||
|
|
727153e965 | ||
|
|
d00f54228d | ||
|
|
77c67802a3 | ||
|
|
7d68d177d7 | ||
|
|
9b20505dcd | ||
|
|
5f93e292b2 | ||
|
|
edd3e20f3c | ||
|
|
90cffa60d6 | ||
|
|
6171cd6867 | ||
|
|
fa731bf6c3 | ||
|
|
818fce324c | ||
|
|
1bc867e48d | ||
|
|
434b9f7a9f | ||
|
|
f54602dead | ||
|
|
fb546b41c1 | ||
|
|
e400d9f5fd | ||
|
|
f0fa8a2481 | ||
|
|
c3b0b49e3f | ||
|
|
45265b4f9b | ||
|
|
aa87a2a23d | ||
|
|
5a59b1da75 | ||
|
|
32aa0dd72f | ||
|
|
c1188b16a1 | ||
|
|
a02a60c658 | ||
| c5f9f5a2dc | |||
|
|
342b1bc8ef | ||
|
|
64158376f5 | ||
|
|
850d4ff056 | ||
|
|
f0cfe1c85a | ||
|
|
c8b615b3ee | ||
|
|
c67fdafb43 | ||
|
|
f66d277918 | ||
|
|
4ef7db20f8 | ||
|
|
e1216dfa76 | ||
|
|
340fa8a7cb | ||
|
|
a029455466 | ||
|
|
27f78c430b | ||
|
|
3d707e5e95 | ||
|
|
0406a2f614 | ||
|
|
a1c0e432ee | ||
|
|
57e57d9e43 | ||
|
|
e4e4188eaf | ||
|
|
f1553cc90e | ||
|
|
997e68c99a | ||
|
|
25663d9a7b | ||
|
|
1e55266396 | ||
|
|
810ae15991 | ||
|
|
271fec7f5e | ||
|
|
c43486791e |
113
.appveyor.yml
Normal file
113
.appveyor.yml
Normal file
@@ -0,0 +1,113 @@
|
||||
# .appveyor.yml for use with EPICS Base ci-scripts
|
||||
# (see: https://github.com/epics-base/ci-scripts)
|
||||
|
||||
# This is YAML - indentation levels are crucial
|
||||
|
||||
#---------------------------------#
|
||||
# build cache #
|
||||
#---------------------------------#
|
||||
# The AppVeyor cache allowance is way too small (1GB per account across all
|
||||
# projects, branches and jobs) to be used for the dependency builds.
|
||||
|
||||
cache:
|
||||
- C:\Users\appveyor\.tools
|
||||
|
||||
#---------------------------------#
|
||||
# repository cloning #
|
||||
#---------------------------------#
|
||||
|
||||
init:
|
||||
# Set autocrlf to make batch files work
|
||||
- cmd: git config --global core.autocrlf true
|
||||
|
||||
clone_depth: 5
|
||||
|
||||
#---------------------------------#
|
||||
# build matrix configuration #
|
||||
#---------------------------------#
|
||||
|
||||
# Build Configurations: dll/static, regular/debug
|
||||
configuration:
|
||||
- dynamic
|
||||
- static
|
||||
- dynamic-debug
|
||||
- static-debug
|
||||
|
||||
# Environment variables: compiler toolchain, base version, setup file, ...
|
||||
environment:
|
||||
# common / default variables for all jobs
|
||||
SETUP_PATH: .ci-local
|
||||
EPICS_TEST_IMPRECISE_TIMING: YES
|
||||
BASE: 7.0
|
||||
|
||||
matrix:
|
||||
- CMP: vs2019
|
||||
BASE: 3.15
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
|
||||
- CMP: vs2019
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
|
||||
- CMP: vs2017
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
- CMP: vs2015
|
||||
- CMP: gcc
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
|
||||
# TODO: static linking w/ readline isn't working. Bypass auto-detect
|
||||
COMMANDLINE_LIBRARY: EPICS
|
||||
|
||||
# Platform: processor architecture
|
||||
platform: x64
|
||||
|
||||
# Matrix configuration: exclude sets of jobs
|
||||
matrix:
|
||||
exclude:
|
||||
# MinGW debug builds use the same libraries, unlike VS
|
||||
- configuration: dynamic-debug
|
||||
CMP: gcc
|
||||
- configuration: static-debug
|
||||
CMP: gcc
|
||||
|
||||
#---------------------------------#
|
||||
# building & testing #
|
||||
#---------------------------------#
|
||||
|
||||
install:
|
||||
- cmd: git submodule update --init --recursive
|
||||
- cmd: pip install git+https://github.com/mdavidsaver/ci-core-dumper#egg=ci-core-dumper
|
||||
- cmd: python .ci/cue.py prepare
|
||||
|
||||
build_script:
|
||||
- cmd: python .ci/cue.py build
|
||||
|
||||
test_script:
|
||||
- cmd: python -m ci_core_dumper install
|
||||
- cmd: python .ci/cue.py -T 20M test
|
||||
|
||||
on_finish:
|
||||
- ps: Get-ChildItem *.tap -Recurse -Force | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name }
|
||||
- cmd: python .ci/cue.py -T 5M test-results
|
||||
|
||||
on_failure:
|
||||
- cmd: python -m ci_core_dumper report
|
||||
|
||||
#---------------------------------#
|
||||
# debugging #
|
||||
#---------------------------------#
|
||||
|
||||
## To connect by remote desktop to a failed build, uncomment the lines below.
|
||||
## You must connect within the usual build timeout limit (60 minutes),
|
||||
## so adjust the build matrix above to just build the config of interest.
|
||||
|
||||
#on_failure:
|
||||
# - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
|
||||
# - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
|
||||
|
||||
#---------------------------------#
|
||||
# notifications #
|
||||
#---------------------------------#
|
||||
|
||||
notifications:
|
||||
- provider: Email
|
||||
to:
|
||||
- core-talk@aps.anl.gov
|
||||
on_build_success: false
|
||||
- provider: GitHubPullRequest
|
||||
1
.ci
Submodule
1
.ci
Submodule
Submodule .ci added at 20f8e05393
6
.ci-local/defaults.set
Normal file
6
.ci-local/defaults.set
Normal file
@@ -0,0 +1,6 @@
|
||||
# EPICS Base
|
||||
BASE_DIRNAME=base
|
||||
BASE_REPONAME=epics-base
|
||||
BASE_REPOOWNER=epics-base
|
||||
BASE_VARNAME=EPICS_BASE
|
||||
BASE_RECURSIVE=NO
|
||||
@@ -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
|
||||
@@ -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
|
||||
301
.github/workflows/ci-scripts-build.yml
vendored
Normal file
301
.github/workflows/ci-scripts-build.yml
vendored
Normal file
@@ -0,0 +1,301 @@
|
||||
# .github/workflows/ci-scripts-build.yml for use with EPICS Base ci-scripts
|
||||
# (see: https://github.com/epics-base/ci-scripts)
|
||||
|
||||
# This is YAML - indentation levels are crucial
|
||||
|
||||
# Workflow name, shared by all branches
|
||||
|
||||
name: pvData
|
||||
|
||||
# Trigger on pushes and PRs to any branch
|
||||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- .appveyor.yml
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
SETUP_PATH: .ci-local:.ci
|
||||
EPICS_TEST_IMPRECISE_TIMING: YES
|
||||
EPICS_TEST_TIMEOUT: 300 # 5 min
|
||||
|
||||
jobs:
|
||||
native:
|
||||
name: ${{ matrix.name }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
# Set environment variables from matrix parameters
|
||||
env:
|
||||
# NB: PVA modules build against both BASE 7.0 and 3.15
|
||||
BASE: ${{ matrix.base }}
|
||||
CMP: ${{ matrix.cmp }}
|
||||
BCFG: ${{ matrix.configuration }}
|
||||
CI_CROSS_TARGETS: ${{ matrix.cross }}
|
||||
EXTRA: ${{ matrix.extra }}
|
||||
TEST: ${{ matrix.test }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# Job names also name artifacts, character limitations apply
|
||||
include:
|
||||
- name: "7.0 Ub gcc c++20 Werror"
|
||||
base: "7.0"
|
||||
os: ubuntu-latest
|
||||
cmp: gcc
|
||||
configuration: default
|
||||
# Turn all warnings into errors,
|
||||
# except for those we could not fix (yet).
|
||||
# Remove respective -Wno-error=... flag once it is fixed.
|
||||
extra: "CMD_CXXFLAGS=-std=c++20
|
||||
CMD_CPPFLAGS='-fdiagnostics-color
|
||||
-fstack-protector-strong
|
||||
-Wformat
|
||||
-Werror
|
||||
-Werror=format-security
|
||||
-Wno-error=deprecated-declarations
|
||||
-Wno-error=stringop-truncation
|
||||
-Wno-error=restrict
|
||||
-Wno-error=sizeof-pointer-memaccess
|
||||
-Wno-error=nonnull
|
||||
-Wno-error=dangling-pointer
|
||||
-Wno-error=format-overflow
|
||||
-Wno-error=stringop-overread
|
||||
-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3'
|
||||
CMD_LDFLAGS=-Wl,-z,relro"
|
||||
|
||||
- name: "7.0 Ub gcc C++11, static"
|
||||
base: "7.0"
|
||||
os: ubuntu-latest
|
||||
cmp: gcc
|
||||
configuration: static
|
||||
extra: "CMD_CXXFLAGS=-std=c++11"
|
||||
|
||||
- name: "7.0 Ub gcc u-char"
|
||||
base: "7.0"
|
||||
os: ubuntu-latest
|
||||
cmp: gcc
|
||||
configuration: static
|
||||
extra: "CMD_CFLAGS=-funsigned-char CMD_CXXFLAGS=-funsigned-char"
|
||||
|
||||
- name: "7.0 Ub clang"
|
||||
base: "7.0"
|
||||
os: ubuntu-latest
|
||||
cmp: clang
|
||||
configuration: default
|
||||
|
||||
- name: "7.0 Ub clang C++11"
|
||||
base: "7.0"
|
||||
os: ubuntu-latest
|
||||
cmp: clang
|
||||
configuration: default
|
||||
extra: "CMD_CXXFLAGS=-std=c++11"
|
||||
|
||||
- name: "7.0 MacOS clang"
|
||||
base: "7.0"
|
||||
os: macos-latest
|
||||
cmp: clang
|
||||
configuration: default
|
||||
|
||||
# Cross builds
|
||||
|
||||
- name: "3.15 Ub-22 gcc + MinGW"
|
||||
base: "3.15"
|
||||
os: ubuntu-22.04
|
||||
cmp: gcc
|
||||
configuration: default
|
||||
cross: "windows-x64-mingw"
|
||||
|
||||
- name: "7.0 Ub gcc + linux-aarch64"
|
||||
base: "7.0"
|
||||
os: ubuntu-latest
|
||||
cmp: gcc
|
||||
configuration: default
|
||||
cross: "linux-aarch64"
|
||||
|
||||
- name: "7.0 Ub gcc + linux-arm gnueabi"
|
||||
base: "7.0"
|
||||
os: ubuntu-latest
|
||||
cmp: gcc
|
||||
configuration: default
|
||||
cross: "linux-arm@arm-linux-gnueabi"
|
||||
|
||||
- name: "7.0 Ub gcc + linux-arm gnueabihf"
|
||||
base: "7.0"
|
||||
os: ubuntu-latest
|
||||
cmp: gcc
|
||||
configuration: default
|
||||
cross: "linux-arm@arm-linux-gnueabihf"
|
||||
|
||||
- name: "7.0 Ub gcc + MinGW"
|
||||
base: "7.0"
|
||||
os: ubuntu-latest
|
||||
cmp: gcc
|
||||
configuration: default
|
||||
cross: "windows-x64-mingw"
|
||||
|
||||
- name: "7.0 Ub gcc + MinGW, static"
|
||||
base: "7.0"
|
||||
os: ubuntu-latest
|
||||
cmp: gcc
|
||||
configuration: static
|
||||
cross: "windows-x64-mingw"
|
||||
|
||||
- name: "7.0 Ub-22 gcc + RT-4.9 pc386"
|
||||
base: "7.0"
|
||||
os: ubuntu-22.04
|
||||
cmp: gcc
|
||||
configuration: default
|
||||
cross: "RTEMS-pc386-qemu@4.9"
|
||||
|
||||
- name: "7.0 Ub-22 gcc + RT-4.10 pc386"
|
||||
base: "7.0"
|
||||
os: ubuntu-22.04
|
||||
cmp: gcc
|
||||
configuration: default
|
||||
cross: "RTEMS-pc386-qemu@4.10"
|
||||
test: NO
|
||||
|
||||
- name: "7.0 Ub-22 gcc + RT-5.1 pc686"
|
||||
base: "7.0"
|
||||
os: ubuntu-22.04
|
||||
cmp: gcc
|
||||
configuration: default
|
||||
cross: "RTEMS-pc686-qemu@5"
|
||||
|
||||
- name: "7.0 Ub-22 gcc + RT-5.1 beatnik,zynq_a9,uC5282"
|
||||
base: "7.0"
|
||||
os: ubuntu-22.04
|
||||
cmp: gcc
|
||||
configuration: default
|
||||
cross: "RTEMS-beatnik@5:RTEMS-xilinx_zynq_a9_qemu@5:RTEMS-uC5282@5"
|
||||
test: NO
|
||||
|
||||
# Windows builds
|
||||
|
||||
- name: "7.0 Win-22 MSC-22"
|
||||
base: "7.0"
|
||||
os: windows-2022
|
||||
cmp: vs2022
|
||||
configuration: default
|
||||
|
||||
- name: "7.0 Win-22 MSC-22 static"
|
||||
base: "7.0"
|
||||
os: windows-2022
|
||||
cmp: vs2022
|
||||
configuration: static
|
||||
|
||||
- name: "7.0 Win-22 MSC-22 debug"
|
||||
base: "7.0"
|
||||
os: windows-2022
|
||||
cmp: vs2022
|
||||
configuration: debug
|
||||
extra: "CMD_CXXFLAGS=-analyze"
|
||||
|
||||
- name: "7.0 Win-22 MinGW"
|
||||
base: "7.0"
|
||||
os: windows-2022
|
||||
cmp: gcc
|
||||
configuration: default
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Automatic core dumper analysis
|
||||
uses: mdavidsaver/ci-core-dumper@master
|
||||
- name: "apt-get install"
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install qemu-system-x86 g++-mingw-w64-x86-64 gdb
|
||||
if: runner.os == 'Linux'
|
||||
- name: Prepare and compile dependencies
|
||||
run: python .ci/cue.py prepare
|
||||
- name: Build main module
|
||||
run: python .ci/cue.py build
|
||||
- name: Run main module tests
|
||||
run: python .ci/cue.py -T 60M test
|
||||
- name: Upload tapfiles Artifact
|
||||
if: ${{ always() }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: tapfiles ${{ matrix.name }}
|
||||
path: '**/O.*/*.tap'
|
||||
if-no-files-found: ignore
|
||||
- name: Collect and show test results
|
||||
if: ${{ always() }}
|
||||
run: python .ci/cue.py -T 5M test-results
|
||||
|
||||
docker:
|
||||
name: ${{ matrix.name }}
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: ${{ matrix.image }}
|
||||
# Set environment variables from matrix parameters
|
||||
env:
|
||||
BASE: ${{ matrix.base }}
|
||||
CMP: ${{ matrix.cmp }}
|
||||
BCFG: ${{ matrix.configuration }}
|
||||
EXTRA: ${{ matrix.extra }}
|
||||
TEST: ${{ matrix.test }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# Job names also name artifacts, character limitations apply
|
||||
include:
|
||||
- name: "7.0 CentOS-8 gcc"
|
||||
base: "7.0"
|
||||
image: centos:8
|
||||
cmp: gcc
|
||||
configuration: default
|
||||
|
||||
- name: "7.0 Rocky-9 gcc"
|
||||
base: "7.0"
|
||||
image: rockylinux:9
|
||||
cmp: gcc
|
||||
configuration: default
|
||||
|
||||
- name: "7.0 Fedora-33 gcc"
|
||||
base: "7.0"
|
||||
image: fedora:33
|
||||
cmp: gcc
|
||||
configuration: default
|
||||
|
||||
- name: "7.0 Fedora gcc"
|
||||
base: "7.0"
|
||||
image: fedora:latest
|
||||
cmp: gcc
|
||||
configuration: default
|
||||
|
||||
steps:
|
||||
- name: "Fix repo URLs on CentOS-8"
|
||||
# centos:8 is frozen, repos are in the vault
|
||||
if: matrix.image=='centos:8'
|
||||
run: |
|
||||
sed -i -e "s|mirrorlist=|#mirrorlist=|" \
|
||||
-e "s|#baseurl=http://mirror|baseurl=http://vault|" \
|
||||
/etc/yum.repos.d/CentOS-Linux-{BaseOS,AppStream,Extras,Plus}.repo
|
||||
- name: "Redhat setup"
|
||||
run: |
|
||||
dnf -y install python3 gdb make perl gcc-c++ glibc-devel readline-devel ncurses-devel perl-devel perl-Test-Simple
|
||||
git --version || dnf -y install git
|
||||
python3 --version
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Automatic core dumper analysis
|
||||
uses: mdavidsaver/ci-core-dumper@master
|
||||
- name: Prepare and compile dependencies
|
||||
run: python3 .ci/cue.py prepare
|
||||
- name: Build main module
|
||||
run: python3 .ci/cue.py build
|
||||
- name: Run main module tests
|
||||
run: python3 .ci/cue.py -T 20M test
|
||||
- name: Upload tapfiles Artifact
|
||||
if: ${{ always() }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: tapfiles ${{ matrix.name }}
|
||||
path: '**/O.*/*.tap'
|
||||
if-no-files-found: ignore
|
||||
- name: Collect and show test results
|
||||
if: ${{ always() }}
|
||||
run: python3 .ci/cue.py -T 5M test-results
|
||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule ".ci"]
|
||||
path = .ci
|
||||
url = https://github.com/epics-base/ci-scripts
|
||||
17
.readthedocs.yml
Normal file
17
.readthedocs.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
# .readthedocs.yml
|
||||
# Read the Docs configuration file
|
||||
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
||||
|
||||
# Required
|
||||
version: 2
|
||||
|
||||
# Build documentation in the documentation/ directory with Sphinx
|
||||
sphinx:
|
||||
configuration: documentation/conf.py
|
||||
|
||||
# Build documentation with MkDocs
|
||||
#mkdocs:
|
||||
# configuration: mkdocs.yml
|
||||
|
||||
# Optionally build your docs in additional formats such as PDF and ePub
|
||||
formats: all
|
||||
29
.travis.yml
29
.travis.yml
@@ -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
|
||||
316
Doxyfile
316
Doxyfile
@@ -1,4 +1,4 @@
|
||||
# Doxyfile 1.8.6
|
||||
# Doxyfile 1.8.8
|
||||
|
||||
# This file describes the settings to be used by the documentation system
|
||||
# doxygen (www.doxygen.org) for a project.
|
||||
@@ -32,33 +32,33 @@ DOXYFILE_ENCODING = UTF-8
|
||||
# title of most generated pages and in a few other places.
|
||||
# The default value is: My Project.
|
||||
|
||||
PROJECT_NAME = pvDataCPP
|
||||
PROJECT_NAME = "PVData C++"
|
||||
|
||||
# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
|
||||
# could be handy for archiving the generated documentation or if some version
|
||||
# control system is used.
|
||||
|
||||
PROJECT_NUMBER =
|
||||
PROJECT_NUMBER = 8.0.8-dev
|
||||
|
||||
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
||||
# for a project that appears at the top of each page and should give viewer a
|
||||
# quick idea about the purpose of the project. Keep the description short.
|
||||
|
||||
PROJECT_BRIEF =
|
||||
PROJECT_BRIEF =
|
||||
|
||||
# With the PROJECT_LOGO tag one can specify an logo or icon that is included in
|
||||
# the documentation. The maximum height of the logo should not exceed 55 pixels
|
||||
# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo
|
||||
# to the output directory.
|
||||
|
||||
PROJECT_LOGO =
|
||||
PROJECT_LOGO =
|
||||
|
||||
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
|
||||
# into which the generated documentation will be written. If a relative path is
|
||||
# entered, it will be relative to the location where doxygen was started. If
|
||||
# left blank the current directory will be used.
|
||||
|
||||
OUTPUT_DIRECTORY =
|
||||
OUTPUT_DIRECTORY = .
|
||||
|
||||
# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub-
|
||||
# directories (in 2 levels) under the output directory of each output format and
|
||||
@@ -70,6 +70,14 @@ OUTPUT_DIRECTORY =
|
||||
|
||||
CREATE_SUBDIRS = NO
|
||||
|
||||
# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
|
||||
# characters to appear in the names of generated files. If set to NO, non-ASCII
|
||||
# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
|
||||
# U+3044.
|
||||
# The default value is: NO.
|
||||
|
||||
ALLOW_UNICODE_NAMES = NO
|
||||
|
||||
# The OUTPUT_LANGUAGE tag is used to specify the language in which all
|
||||
# documentation generated by doxygen is written. Doxygen will use this
|
||||
# information to generate all constant output in the proper language.
|
||||
@@ -110,7 +118,17 @@ REPEAT_BRIEF = YES
|
||||
# the entity):The $name class, The $name widget, The $name file, is, provides,
|
||||
# specifies, contains, represents, a, an and the.
|
||||
|
||||
ABBREVIATE_BRIEF =
|
||||
ABBREVIATE_BRIEF = "The $name class" \
|
||||
"The $name widget" \
|
||||
"The $name file" \
|
||||
is \
|
||||
provides \
|
||||
specifies \
|
||||
contains \
|
||||
represents \
|
||||
a \
|
||||
an \
|
||||
the
|
||||
|
||||
# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
|
||||
# doxygen will generate a detailed section even if there is only a brief
|
||||
@@ -125,7 +143,7 @@ ALWAYS_DETAILED_SEC = NO
|
||||
# operators of the base classes will not be shown.
|
||||
# The default value is: NO.
|
||||
|
||||
INLINE_INHERITED_MEMB = YES
|
||||
INLINE_INHERITED_MEMB = NO
|
||||
|
||||
# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path
|
||||
# before files name in the file list and in the header files. If set to NO the
|
||||
@@ -144,7 +162,7 @@ FULL_PATH_NAMES = YES
|
||||
# will be relative from the directory where doxygen is started.
|
||||
# This tag requires that the tag FULL_PATH_NAMES is set to YES.
|
||||
|
||||
STRIP_FROM_PATH =
|
||||
STRIP_FROM_PATH = src
|
||||
|
||||
# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
|
||||
# path mentioned in the documentation of a class, which tells the reader which
|
||||
@@ -153,7 +171,7 @@ STRIP_FROM_PATH =
|
||||
# specify the list of include paths that are normally passed to the compiler
|
||||
# using the -I flag.
|
||||
|
||||
STRIP_FROM_INC_PATH =
|
||||
STRIP_FROM_INC_PATH = src
|
||||
|
||||
# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
|
||||
# less readable) file names. This can be useful is your file systems doesn't
|
||||
@@ -220,13 +238,13 @@ TAB_SIZE = 4
|
||||
# "Side Effects:". You can put \n's in the value part of an alias to insert
|
||||
# newlines.
|
||||
|
||||
ALIASES =
|
||||
ALIASES =
|
||||
|
||||
# This tag can be used to specify a number of word-keyword mappings (TCL only).
|
||||
# A mapping has the form "name=value". For example adding "class=itcl::class"
|
||||
# will allow you to use the command class in the itcl::class meaning.
|
||||
|
||||
TCL_SUBST =
|
||||
TCL_SUBST =
|
||||
|
||||
# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
|
||||
# only. Doxygen will then generate output that is more tailored for C. For
|
||||
@@ -261,16 +279,19 @@ OPTIMIZE_OUTPUT_VHDL = NO
|
||||
# extension. Doxygen has a built-in mapping, but you can override or extend it
|
||||
# using this tag. The format is ext=language, where ext is a file extension, and
|
||||
# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
|
||||
# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make
|
||||
# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
|
||||
# (default is Fortran), use: inc=Fortran f=C.
|
||||
# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:
|
||||
# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
|
||||
# Fortran. In the later case the parser tries to guess whether the code is fixed
|
||||
# or free formatted code, this is the default for Fortran type files), VHDL. For
|
||||
# instance to make doxygen treat .inc files as Fortran files (default is PHP),
|
||||
# and .f files as C (default is Fortran), use: inc=Fortran f=C.
|
||||
#
|
||||
# Note For files without extension you can use no_extension as a placeholder.
|
||||
#
|
||||
# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
|
||||
# the files are not read by doxygen.
|
||||
|
||||
EXTENSION_MAPPING =
|
||||
EXTENSION_MAPPING =
|
||||
|
||||
# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
|
||||
# according to the Markdown format, which allows for more readable
|
||||
@@ -298,7 +319,7 @@ AUTOLINK_SUPPORT = YES
|
||||
# diagrams that involve STL classes more complete and accurate.
|
||||
# The default value is: NO.
|
||||
|
||||
BUILTIN_STL_SUPPORT = YES
|
||||
BUILTIN_STL_SUPPORT = NO
|
||||
|
||||
# If you use Microsoft's C++/CLI language, you should set this option to YES to
|
||||
# enable parsing support.
|
||||
@@ -398,7 +419,7 @@ LOOKUP_CACHE_SIZE = 0
|
||||
# normally produced when WARNINGS is set to YES.
|
||||
# The default value is: NO.
|
||||
|
||||
EXTRACT_ALL = YES
|
||||
EXTRACT_ALL = NO
|
||||
|
||||
# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will
|
||||
# be included in the documentation.
|
||||
@@ -487,7 +508,7 @@ INTERNAL_DOCS = NO
|
||||
# and Mac users are advised to set this option to NO.
|
||||
# The default value is: system dependent.
|
||||
|
||||
CASE_SENSE_NAMES = YES
|
||||
CASE_SENSE_NAMES = NO
|
||||
|
||||
# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
|
||||
# their full class and namespace scopes in the documentation. If set to YES the
|
||||
@@ -606,7 +627,7 @@ GENERATE_DEPRECATEDLIST= YES
|
||||
# sections, marked by \if <section_label> ... \endif and \cond <section_label>
|
||||
# ... \endcond blocks.
|
||||
|
||||
ENABLED_SECTIONS =
|
||||
ENABLED_SECTIONS =
|
||||
|
||||
# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
|
||||
# initial value of a variable or macro / define can have for it to appear in the
|
||||
@@ -648,7 +669,7 @@ SHOW_NAMESPACES = YES
|
||||
# by doxygen. Whatever the program writes to standard output is used as the file
|
||||
# version. For an example see the documentation.
|
||||
|
||||
FILE_VERSION_FILTER =
|
||||
FILE_VERSION_FILTER =
|
||||
|
||||
# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
|
||||
# by doxygen. The layout file controls the global structure of the generated
|
||||
@@ -661,7 +682,7 @@ FILE_VERSION_FILTER =
|
||||
# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
|
||||
# tag is left empty.
|
||||
|
||||
LAYOUT_FILE =
|
||||
LAYOUT_FILE =
|
||||
|
||||
# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
|
||||
# the reference definitions. This must be a list of .bib files. The .bib
|
||||
@@ -669,10 +690,9 @@ LAYOUT_FILE =
|
||||
# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
|
||||
# For LaTeX the style of the bibliography can be controlled using
|
||||
# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
|
||||
# search path. Do not use file names with spaces, bibtex cannot handle them. See
|
||||
# also \cite for info how to create references.
|
||||
# search path. See also \cite for info how to create references.
|
||||
|
||||
CITE_BIB_FILES =
|
||||
CITE_BIB_FILES =
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to warning and progress messages
|
||||
@@ -699,7 +719,7 @@ WARNINGS = YES
|
||||
# will automatically be disabled.
|
||||
# The default value is: YES.
|
||||
|
||||
WARN_IF_UNDOCUMENTED = YES
|
||||
WARN_IF_UNDOCUMENTED = NO
|
||||
|
||||
# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
|
||||
# potential errors in the documentation, such as not documenting some parameters
|
||||
@@ -731,7 +751,7 @@ WARN_FORMAT = "$file:$line: $text"
|
||||
# messages should be written. If left blank the output is written to standard
|
||||
# error (stderr).
|
||||
|
||||
WARN_LOGFILE =
|
||||
WARN_LOGFILE =
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the input files
|
||||
@@ -743,7 +763,12 @@ WARN_LOGFILE =
|
||||
# spaces.
|
||||
# Note: If this tag is empty the current directory is searched.
|
||||
|
||||
INPUT = include
|
||||
INPUT = src/pv \
|
||||
documentation/mainpage.dox \
|
||||
src/copy/pv \
|
||||
src/misc/pv \
|
||||
src/json/pv \
|
||||
documentation/release_notes.dox
|
||||
|
||||
# This tag can be used to specify the character encoding of the source files
|
||||
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
|
||||
@@ -763,13 +788,13 @@ INPUT_ENCODING = UTF-8
|
||||
# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf,
|
||||
# *.qsf, *.as and *.js.
|
||||
|
||||
FILE_PATTERNS =
|
||||
FILE_PATTERNS =
|
||||
|
||||
# The RECURSIVE tag can be used to specify whether or not subdirectories should
|
||||
# be searched for input files as well.
|
||||
# The default value is: NO.
|
||||
|
||||
RECURSIVE = YES
|
||||
RECURSIVE = NO
|
||||
|
||||
# The EXCLUDE tag can be used to specify files and/or directories that should be
|
||||
# excluded from the INPUT source files. This way you can easily exclude a
|
||||
@@ -778,7 +803,7 @@ RECURSIVE = YES
|
||||
# Note that relative paths are relative to the directory from which doxygen is
|
||||
# run.
|
||||
|
||||
EXCLUDE =
|
||||
EXCLUDE =
|
||||
|
||||
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
|
||||
# directories that are symbolic links (a Unix file system feature) are excluded
|
||||
@@ -794,7 +819,7 @@ EXCLUDE_SYMLINKS = NO
|
||||
# Note that the wildcards are matched against the file with absolute path, so to
|
||||
# exclude all test directories for example use the pattern */test/*
|
||||
|
||||
EXCLUDE_PATTERNS =
|
||||
EXCLUDE_PATTERNS =
|
||||
|
||||
# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
|
||||
# (namespaces, classes, functions, etc.) that should be excluded from the
|
||||
@@ -805,20 +830,20 @@ EXCLUDE_PATTERNS =
|
||||
# Note that the wildcards are matched against the file with absolute path, so to
|
||||
# exclude all test directories use the pattern */test/*
|
||||
|
||||
EXCLUDE_SYMBOLS =
|
||||
EXCLUDE_SYMBOLS =
|
||||
|
||||
# The EXAMPLE_PATH tag can be used to specify one or more files or directories
|
||||
# that contain example code fragments that are included (see the \include
|
||||
# command).
|
||||
|
||||
EXAMPLE_PATH =
|
||||
EXAMPLE_PATH = examples
|
||||
|
||||
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
|
||||
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
|
||||
# *.h) to filter out the source-files in the directories. If left blank all
|
||||
# files are included.
|
||||
|
||||
EXAMPLE_PATTERNS =
|
||||
EXAMPLE_PATTERNS = *.cpp
|
||||
|
||||
# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
|
||||
# searched for input files to be used with the \include or \dontinclude commands
|
||||
@@ -831,7 +856,7 @@ EXAMPLE_RECURSIVE = NO
|
||||
# that contain images that are to be included in the documentation (see the
|
||||
# \image command).
|
||||
|
||||
IMAGE_PATH =
|
||||
IMAGE_PATH =
|
||||
|
||||
# The INPUT_FILTER tag can be used to specify a program that doxygen should
|
||||
# invoke to filter for each input file. Doxygen will invoke the filter program
|
||||
@@ -848,7 +873,7 @@ IMAGE_PATH =
|
||||
# code is scanned, but not when the output code is generated. If lines are added
|
||||
# or removed, the anchors will not be placed correctly.
|
||||
|
||||
INPUT_FILTER =
|
||||
INPUT_FILTER =
|
||||
|
||||
# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
|
||||
# basis. Doxygen will compare the file name with each pattern and apply the
|
||||
@@ -857,7 +882,7 @@ INPUT_FILTER =
|
||||
# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
|
||||
# patterns match the file name, INPUT_FILTER is applied.
|
||||
|
||||
FILTER_PATTERNS =
|
||||
FILTER_PATTERNS =
|
||||
|
||||
# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
|
||||
# INPUT_FILTER ) will also be used to filter the input files that are used for
|
||||
@@ -872,14 +897,14 @@ FILTER_SOURCE_FILES = NO
|
||||
# *.ext= (so without naming a filter).
|
||||
# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
|
||||
|
||||
FILTER_SOURCE_PATTERNS =
|
||||
FILTER_SOURCE_PATTERNS =
|
||||
|
||||
# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
|
||||
# is part of the input, its contents will be placed on the main page
|
||||
# (index.html). This can be useful if you have a project on for instance GitHub
|
||||
# and want to reuse the introduction page also for the doxygen output.
|
||||
|
||||
USE_MDFILE_AS_MAINPAGE =
|
||||
USE_MDFILE_AS_MAINPAGE =
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to source browsing
|
||||
@@ -911,13 +936,13 @@ STRIP_CODE_COMMENTS = YES
|
||||
# function all documented functions referencing it will be listed.
|
||||
# The default value is: NO.
|
||||
|
||||
REFERENCED_BY_RELATION = YES
|
||||
REFERENCED_BY_RELATION = NO
|
||||
|
||||
# If the REFERENCES_RELATION tag is set to YES then for each documented function
|
||||
# all documented entities called/used by that function will be listed.
|
||||
# The default value is: NO.
|
||||
|
||||
REFERENCES_RELATION = YES
|
||||
REFERENCES_RELATION = NO
|
||||
|
||||
# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
|
||||
# to YES, then the hyperlinks from functions in REFERENCES_RELATION and
|
||||
@@ -967,6 +992,25 @@ USE_HTAGS = NO
|
||||
|
||||
VERBATIM_HEADERS = YES
|
||||
|
||||
# If the CLANG_ASSISTED_PARSING tag is set to YES, then doxygen will use the
|
||||
# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the
|
||||
# cost of reduced performance. This can be particularly helpful with template
|
||||
# rich C++ code for which doxygen's built-in parser lacks the necessary type
|
||||
# information.
|
||||
# Note: The availability of this option depends on whether or not doxygen was
|
||||
# compiled with the --with-libclang option.
|
||||
# The default value is: NO.
|
||||
|
||||
CLANG_ASSISTED_PARSING = YES
|
||||
|
||||
# If clang assisted parsing is enabled you can provide the compiler with command
|
||||
# line options that you would normally use when invoking the compiler. Note that
|
||||
# the include paths will already be set by doxygen for the files and directories
|
||||
# specified with INPUT and INCLUDE_PATH.
|
||||
# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
|
||||
|
||||
CLANG_OPTIONS =
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the alphabetical class index
|
||||
#---------------------------------------------------------------------------
|
||||
@@ -976,7 +1020,7 @@ VERBATIM_HEADERS = YES
|
||||
# classes, structs, unions or interfaces.
|
||||
# The default value is: YES.
|
||||
|
||||
ALPHABETICAL_INDEX = NO
|
||||
ALPHABETICAL_INDEX = YES
|
||||
|
||||
# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
|
||||
# which the alphabetical index list will be split.
|
||||
@@ -991,7 +1035,7 @@ COLS_IN_ALPHA_INDEX = 5
|
||||
# while generating the index headers.
|
||||
# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
|
||||
|
||||
IGNORE_PREFIX =
|
||||
IGNORE_PREFIX =
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the HTML output
|
||||
@@ -1008,7 +1052,7 @@ GENERATE_HTML = YES
|
||||
# The default directory is: html.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_OUTPUT = documentation/html
|
||||
HTML_OUTPUT = html/doxygen
|
||||
|
||||
# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
|
||||
# generated HTML page (for example: .htm, .php, .asp).
|
||||
@@ -1035,7 +1079,7 @@ HTML_FILE_EXTENSION = .html
|
||||
# of the possible markers and block names see the documentation.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_HEADER =
|
||||
HTML_HEADER =
|
||||
|
||||
# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
|
||||
# generated HTML page. If the tag is left blank doxygen will generate a standard
|
||||
@@ -1045,7 +1089,7 @@ HTML_HEADER =
|
||||
# that doxygen normally uses.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_FOOTER =
|
||||
HTML_FOOTER =
|
||||
|
||||
# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
|
||||
# sheet that is used by each HTML page. It can be used to fine-tune the look of
|
||||
@@ -1057,18 +1101,20 @@ HTML_FOOTER =
|
||||
# obsolete.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_STYLESHEET =
|
||||
HTML_STYLESHEET =
|
||||
|
||||
# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user-
|
||||
# defined cascading style sheet that is included after the standard style sheets
|
||||
# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
|
||||
# cascading style sheets that are included after the standard style sheets
|
||||
# created by doxygen. Using this option one can overrule certain style aspects.
|
||||
# This is preferred over using HTML_STYLESHEET since it does not replace the
|
||||
# standard style sheet and is therefor more robust against future updates.
|
||||
# Doxygen will copy the style sheet file to the output directory. For an example
|
||||
# see the documentation.
|
||||
# Doxygen will copy the style sheet files to the output directory.
|
||||
# Note: The order of the extra stylesheet files is of importance (e.g. the last
|
||||
# stylesheet in the list overrules the setting of the previous ones in the
|
||||
# list). For an example see the documentation.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_EXTRA_STYLESHEET =
|
||||
HTML_EXTRA_STYLESHEET =
|
||||
|
||||
# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
|
||||
# other source files which should be copied to the HTML output directory. Note
|
||||
@@ -1078,7 +1124,7 @@ HTML_EXTRA_STYLESHEET =
|
||||
# files will be copied as-is; there are no commands or markers available.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_EXTRA_FILES = documentation/pvDataCPP.html
|
||||
HTML_EXTRA_FILES =
|
||||
|
||||
# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
|
||||
# will adjust the colors in the stylesheet and background images according to
|
||||
@@ -1206,7 +1252,7 @@ GENERATE_HTMLHELP = NO
|
||||
# written to the html output directory.
|
||||
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
|
||||
|
||||
CHM_FILE =
|
||||
CHM_FILE =
|
||||
|
||||
# The HHC_LOCATION tag can be used to specify the location (absolute path
|
||||
# including file name) of the HTML help compiler ( hhc.exe). If non-empty
|
||||
@@ -1214,7 +1260,7 @@ CHM_FILE =
|
||||
# The file has to be specified with full path.
|
||||
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
|
||||
|
||||
HHC_LOCATION =
|
||||
HHC_LOCATION =
|
||||
|
||||
# The GENERATE_CHI flag controls if a separate .chi index file is generated (
|
||||
# YES) or that it should be included in the master .chm file ( NO).
|
||||
@@ -1227,10 +1273,11 @@ GENERATE_CHI = NO
|
||||
# and project file content.
|
||||
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
|
||||
|
||||
CHM_INDEX_ENCODING =
|
||||
CHM_INDEX_ENCODING =
|
||||
|
||||
# The BINARY_TOC flag controls whether a binary table of contents is generated (
|
||||
# YES) or a normal table of contents ( NO) in the .chm file.
|
||||
# YES) or a normal table of contents ( NO) in the .chm file. Furthermore it
|
||||
# enables the Previous and Next buttons.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
|
||||
|
||||
@@ -1257,7 +1304,7 @@ GENERATE_QHP = NO
|
||||
# the HTML output folder.
|
||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||
|
||||
QCH_FILE =
|
||||
QCH_FILE =
|
||||
|
||||
# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
|
||||
# Project output. For more information please see Qt Help Project / Namespace
|
||||
@@ -1282,7 +1329,7 @@ QHP_VIRTUAL_FOLDER = doc
|
||||
# filters).
|
||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||
|
||||
QHP_CUST_FILTER_NAME =
|
||||
QHP_CUST_FILTER_NAME =
|
||||
|
||||
# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
|
||||
# custom filter to add. For more information please see Qt Help Project / Custom
|
||||
@@ -1290,21 +1337,21 @@ QHP_CUST_FILTER_NAME =
|
||||
# filters).
|
||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||
|
||||
QHP_CUST_FILTER_ATTRS =
|
||||
QHP_CUST_FILTER_ATTRS =
|
||||
|
||||
# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
|
||||
# project's filter section matches. Qt Help Project / Filter Attributes (see:
|
||||
# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
|
||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||
|
||||
QHP_SECT_FILTER_ATTRS =
|
||||
QHP_SECT_FILTER_ATTRS =
|
||||
|
||||
# The QHG_LOCATION tag can be used to specify the location of Qt's
|
||||
# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
|
||||
# generated .qhp file.
|
||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||
|
||||
QHG_LOCATION =
|
||||
QHG_LOCATION =
|
||||
|
||||
# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
|
||||
# generated, together with the HTML files, they form an Eclipse help plugin. To
|
||||
@@ -1437,7 +1484,7 @@ MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
|
||||
# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
|
||||
# This tag requires that the tag USE_MATHJAX is set to YES.
|
||||
|
||||
MATHJAX_EXTENSIONS =
|
||||
MATHJAX_EXTENSIONS =
|
||||
|
||||
# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
|
||||
# of code that will be used on startup of the MathJax code. See the MathJax site
|
||||
@@ -1445,7 +1492,7 @@ MATHJAX_EXTENSIONS =
|
||||
# example see the documentation.
|
||||
# This tag requires that the tag USE_MATHJAX is set to YES.
|
||||
|
||||
MATHJAX_CODEFILE =
|
||||
MATHJAX_CODEFILE =
|
||||
|
||||
# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
|
||||
# the HTML output. The underlying search engine uses javascript and DHTML and
|
||||
@@ -1466,15 +1513,15 @@ MATHJAX_CODEFILE =
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
SEARCHENGINE = NO
|
||||
SEARCHENGINE = YES
|
||||
|
||||
# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
|
||||
# implemented using a web server instead of a web client using Javascript. There
|
||||
# are two flavours of web server based searching depending on the
|
||||
# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for
|
||||
# searching and an index file used by the script. When EXTERNAL_SEARCH is
|
||||
# enabled the indexing and searching needs to be provided by external tools. See
|
||||
# the section "External Indexing and Searching" for details.
|
||||
# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
|
||||
# setting. When disabled, doxygen will generate a PHP script for searching and
|
||||
# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
|
||||
# and searching needs to be provided by external tools. See the section
|
||||
# "External Indexing and Searching" for details.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag SEARCHENGINE is set to YES.
|
||||
|
||||
@@ -1505,7 +1552,7 @@ EXTERNAL_SEARCH = NO
|
||||
# Searching" for details.
|
||||
# This tag requires that the tag SEARCHENGINE is set to YES.
|
||||
|
||||
SEARCHENGINE_URL =
|
||||
SEARCHENGINE_URL =
|
||||
|
||||
# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
|
||||
# search data is written to a file for indexing by an external tool. With the
|
||||
@@ -1521,7 +1568,7 @@ SEARCHDATA_FILE = searchdata.xml
|
||||
# projects and redirect the results back to the right project.
|
||||
# This tag requires that the tag SEARCHENGINE is set to YES.
|
||||
|
||||
EXTERNAL_SEARCH_ID =
|
||||
EXTERNAL_SEARCH_ID =
|
||||
|
||||
# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
|
||||
# projects other than the one defined by this configuration file, but that are
|
||||
@@ -1531,7 +1578,7 @@ EXTERNAL_SEARCH_ID =
|
||||
# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
|
||||
# This tag requires that the tag SEARCHENGINE is set to YES.
|
||||
|
||||
EXTRA_SEARCH_MAPPINGS =
|
||||
EXTRA_SEARCH_MAPPINGS =
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the LaTeX output
|
||||
@@ -1583,7 +1630,7 @@ COMPACT_LATEX = NO
|
||||
# The default value is: a4.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
PAPER_TYPE = a4wide
|
||||
PAPER_TYPE = a4
|
||||
|
||||
# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
|
||||
# that should be included in the LaTeX output. To get the times font for
|
||||
@@ -1592,7 +1639,7 @@ PAPER_TYPE = a4wide
|
||||
# If left blank no extra packages will be included.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
EXTRA_PACKAGES =
|
||||
EXTRA_PACKAGES =
|
||||
|
||||
# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
|
||||
# generated LaTeX document. The header should contain everything until the first
|
||||
@@ -1602,22 +1649,24 @@ EXTRA_PACKAGES =
|
||||
#
|
||||
# Note: Only use a user-defined header if you know what you are doing! The
|
||||
# following commands have a special meaning inside the header: $title,
|
||||
# $datetime, $date, $doxygenversion, $projectname, $projectnumber. Doxygen will
|
||||
# replace them by respectively the title of the page, the current date and time,
|
||||
# only the current date, the version number of doxygen, the project name (see
|
||||
# PROJECT_NAME), or the project number (see PROJECT_NUMBER).
|
||||
# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
|
||||
# $projectbrief, $projectlogo. Doxygen will replace $title with the empy string,
|
||||
# for the replacement values of the other commands the user is refered to
|
||||
# HTML_HEADER.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_HEADER =
|
||||
LATEX_HEADER =
|
||||
|
||||
# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
|
||||
# generated LaTeX document. The footer should contain everything after the last
|
||||
# chapter. If it is left blank doxygen will generate a standard footer.
|
||||
# chapter. If it is left blank doxygen will generate a standard footer. See
|
||||
# LATEX_HEADER for more information on how to generate a default footer and what
|
||||
# special commands can be used inside the footer.
|
||||
#
|
||||
# Note: Only use a user-defined footer if you know what you are doing!
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_FOOTER =
|
||||
LATEX_FOOTER =
|
||||
|
||||
# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
|
||||
# other source files which should be copied to the LATEX_OUTPUT output
|
||||
@@ -1625,7 +1674,7 @@ LATEX_FOOTER =
|
||||
# markers available.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_EXTRA_FILES =
|
||||
LATEX_EXTRA_FILES =
|
||||
|
||||
# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
|
||||
# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
|
||||
@@ -1634,15 +1683,15 @@ LATEX_EXTRA_FILES =
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
PDF_HYPERLINKS = NO
|
||||
PDF_HYPERLINKS = YES
|
||||
|
||||
# If the LATEX_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
|
||||
# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
|
||||
# the PDF file directly from the LaTeX files. Set this option to YES to get a
|
||||
# higher quality PDF documentation.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
USE_PDFLATEX = NO
|
||||
USE_PDFLATEX = YES
|
||||
|
||||
# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
|
||||
# command to the generated LaTeX files. This will instruct LaTeX to keep running
|
||||
@@ -1725,14 +1774,14 @@ RTF_HYPERLINKS = NO
|
||||
# default style sheet that doxygen normally uses.
|
||||
# This tag requires that the tag GENERATE_RTF is set to YES.
|
||||
|
||||
RTF_STYLESHEET_FILE =
|
||||
RTF_STYLESHEET_FILE =
|
||||
|
||||
# Set optional variables used in the generation of an RTF document. Syntax is
|
||||
# similar to doxygen's config file. A template extensions file can be generated
|
||||
# using doxygen -e rtf extensionFile.
|
||||
# This tag requires that the tag GENERATE_RTF is set to YES.
|
||||
|
||||
RTF_EXTENSIONS_FILE =
|
||||
RTF_EXTENSIONS_FILE =
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the man page output
|
||||
@@ -1762,6 +1811,13 @@ MAN_OUTPUT = man
|
||||
|
||||
MAN_EXTENSION = .3
|
||||
|
||||
# The MAN_SUBDIR tag determines the name of the directory created within
|
||||
# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
|
||||
# MAN_EXTENSION with the initial . removed.
|
||||
# This tag requires that the tag GENERATE_MAN is set to YES.
|
||||
|
||||
MAN_SUBDIR =
|
||||
|
||||
# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
|
||||
# will generate one additional man file for each entity documented in the real
|
||||
# man page(s). These additional files only source the real man page, but without
|
||||
@@ -1789,18 +1845,6 @@ GENERATE_XML = NO
|
||||
|
||||
XML_OUTPUT = xml
|
||||
|
||||
# The XML_SCHEMA tag can be used to specify a XML schema, which can be used by a
|
||||
# validating XML parser to check the syntax of the XML files.
|
||||
# This tag requires that the tag GENERATE_XML is set to YES.
|
||||
|
||||
XML_SCHEMA =
|
||||
|
||||
# The XML_DTD tag can be used to specify a XML DTD, which can be used by a
|
||||
# validating XML parser to check the syntax of the XML files.
|
||||
# This tag requires that the tag GENERATE_XML is set to YES.
|
||||
|
||||
XML_DTD =
|
||||
|
||||
# If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program
|
||||
# listings (including syntax highlighting and cross-referencing information) to
|
||||
# the XML output. Note that enabling this will significantly increase the size
|
||||
@@ -1828,6 +1872,15 @@ GENERATE_DOCBOOK = NO
|
||||
|
||||
DOCBOOK_OUTPUT = docbook
|
||||
|
||||
# If the DOCBOOK_PROGRAMLISTING tag is set to YES doxygen will include the
|
||||
# program listings (including syntax highlighting and cross-referencing
|
||||
# information) to the DOCBOOK output. Note that enabling this will significantly
|
||||
# increase the size of the DOCBOOK output.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
|
||||
|
||||
DOCBOOK_PROGRAMLISTING = NO
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options for the AutoGen Definitions output
|
||||
#---------------------------------------------------------------------------
|
||||
@@ -1876,7 +1929,7 @@ PERLMOD_PRETTY = YES
|
||||
# overwrite each other's variables.
|
||||
# This tag requires that the tag GENERATE_PERLMOD is set to YES.
|
||||
|
||||
PERLMOD_MAKEVAR_PREFIX =
|
||||
PERLMOD_MAKEVAR_PREFIX =
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the preprocessor
|
||||
@@ -1895,7 +1948,7 @@ ENABLE_PREPROCESSING = YES
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
|
||||
|
||||
MACRO_EXPANSION = NO
|
||||
MACRO_EXPANSION = YES
|
||||
|
||||
# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
|
||||
# the macro expansion is limited to the macros specified with the PREDEFINED and
|
||||
@@ -1917,7 +1970,7 @@ SEARCH_INCLUDES = YES
|
||||
# preprocessor.
|
||||
# This tag requires that the tag SEARCH_INCLUDES is set to YES.
|
||||
|
||||
INCLUDE_PATH =
|
||||
INCLUDE_PATH = src/misc src ../../include/
|
||||
|
||||
# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
|
||||
# patterns (like *.h and *.hpp) to filter out the header-files in the
|
||||
@@ -1925,7 +1978,7 @@ INCLUDE_PATH =
|
||||
# used.
|
||||
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
|
||||
|
||||
INCLUDE_FILE_PATTERNS =
|
||||
INCLUDE_FILE_PATTERNS =
|
||||
|
||||
# The PREDEFINED tag can be used to specify one or more macro names that are
|
||||
# defined before the preprocessor is started (similar to the -D option of e.g.
|
||||
@@ -1935,7 +1988,7 @@ INCLUDE_FILE_PATTERNS =
|
||||
# recursively expanded use the := operator instead of the = operator.
|
||||
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
|
||||
|
||||
PREDEFINED =
|
||||
PREDEFINED =
|
||||
|
||||
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
|
||||
# tag can be used to specify a list of macro names that should be expanded. The
|
||||
@@ -1947,14 +2000,14 @@ PREDEFINED =
|
||||
EXPAND_AS_DEFINED =
|
||||
|
||||
# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
|
||||
# remove all refrences to function-like macros that are alone on a line, have an
|
||||
# all uppercase name, and do not end with a semicolon. Such function macros are
|
||||
# typically used for boiler-plate code, and will confuse the parser if not
|
||||
# remove all references to function-like macros that are alone on a line, have
|
||||
# an all uppercase name, and do not end with a semicolon. Such function macros
|
||||
# are typically used for boiler-plate code, and will confuse the parser if not
|
||||
# removed.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
|
||||
|
||||
SKIP_FUNCTION_MACROS = YES
|
||||
SKIP_FUNCTION_MACROS = NO
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to external references
|
||||
@@ -1969,17 +2022,17 @@ SKIP_FUNCTION_MACROS = YES
|
||||
# where loc1 and loc2 can be relative or absolute paths or URLs. See the
|
||||
# section "Linking to external documentation" for more information about the use
|
||||
# of tag files.
|
||||
# Note: Each tag file must have an unique name (where the name does NOT include
|
||||
# Note: Each tag file must have a unique name (where the name does NOT include
|
||||
# the path). If a tag file is not located in the directory in which doxygen is
|
||||
# run, you must also specify the path to the tagfile here.
|
||||
|
||||
TAGFILES =
|
||||
TAGFILES = "libstdc++.tag = http://gcc.gnu.org/onlinedocs/libstdc++/latest-doxygen"
|
||||
|
||||
# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
|
||||
# tag file that is based on the input files it reads. See section "Linking to
|
||||
# external documentation" for more information about the usage of tag files.
|
||||
|
||||
GENERATE_TAGFILE =
|
||||
GENERATE_TAGFILE = pvdata.tag
|
||||
|
||||
# If the ALLEXTERNALS tag is set to YES all external class will be listed in the
|
||||
# class index. If set to NO only the inherited external classes will be listed.
|
||||
@@ -1992,14 +2045,14 @@ ALLEXTERNALS = NO
|
||||
# listed.
|
||||
# The default value is: YES.
|
||||
|
||||
EXTERNAL_GROUPS = YES
|
||||
EXTERNAL_GROUPS = NO
|
||||
|
||||
# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed in
|
||||
# the related pages index. If set to NO, only the current project's pages will
|
||||
# be listed.
|
||||
# The default value is: YES.
|
||||
|
||||
EXTERNAL_PAGES = YES
|
||||
EXTERNAL_PAGES = NO
|
||||
|
||||
# The PERL_PATH should be the absolute path and name of the perl script
|
||||
# interpreter (i.e. the result of 'which perl').
|
||||
@@ -2027,14 +2080,14 @@ CLASS_DIAGRAMS = YES
|
||||
# the mscgen tool resides. If left empty the tool is assumed to be found in the
|
||||
# default search path.
|
||||
|
||||
MSCGEN_PATH =
|
||||
MSCGEN_PATH =
|
||||
|
||||
# You can include diagrams made with dia in doxygen documentation. Doxygen will
|
||||
# then run dia to produce the diagram and insert it in the documentation. The
|
||||
# DIA_PATH tag allows you to specify the directory where the dia binary resides.
|
||||
# If left empty dia is assumed to be found in the default search path.
|
||||
|
||||
DIA_PATH =
|
||||
DIA_PATH =
|
||||
|
||||
# If set to YES, the inheritance and collaboration graphs will hide inheritance
|
||||
# and usage relations if the target is undocumented or is not a class.
|
||||
@@ -2047,9 +2100,9 @@ HIDE_UNDOC_RELATIONS = YES
|
||||
# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
|
||||
# Bell Labs. The other options in this section have no effect if this option is
|
||||
# set to NO
|
||||
# The default value is: NO.
|
||||
# The default value is: YES.
|
||||
|
||||
HAVE_DOT = NO
|
||||
HAVE_DOT = YES
|
||||
|
||||
# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
|
||||
# to run in parallel. When set to 0 doxygen will base this on the number of
|
||||
@@ -2061,7 +2114,7 @@ HAVE_DOT = NO
|
||||
|
||||
DOT_NUM_THREADS = 0
|
||||
|
||||
# When you want a differently looking font n the dot files that doxygen
|
||||
# When you want a differently looking font in the dot files that doxygen
|
||||
# generates you can specify the font name using DOT_FONTNAME. You need to make
|
||||
# sure dot is able to find the font, which can be done by putting it in a
|
||||
# standard location or by setting the DOTFONTPATH environment variable or by
|
||||
@@ -2083,7 +2136,7 @@ DOT_FONTSIZE = 10
|
||||
# the path where dot can find it using this tag.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
DOT_FONTPATH =
|
||||
DOT_FONTPATH =
|
||||
|
||||
# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
|
||||
# each documented class showing the direct and indirect inheritance relations.
|
||||
@@ -2199,7 +2252,9 @@ DIRECTORY_GRAPH = YES
|
||||
# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
|
||||
# to make the SVG files visible in IE 9+ (other browsers do not have this
|
||||
# requirement).
|
||||
# Possible values are: png, jpg, gif and svg.
|
||||
# Possible values are: png, png:cairo, png:cairo:cairo, png:cairo:gd, png:gd,
|
||||
# png:gd:gd, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, gif, gif:cairo,
|
||||
# gif:cairo:gd, gif:gd, gif:gd:gd and svg.
|
||||
# The default value is: png.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
@@ -2221,26 +2276,35 @@ INTERACTIVE_SVG = NO
|
||||
# found. If left blank, it is assumed the dot tool can be found in the path.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
DOT_PATH =
|
||||
DOT_PATH =
|
||||
|
||||
# The DOTFILE_DIRS tag can be used to specify one or more directories that
|
||||
# contain dot files that are included in the documentation (see the \dotfile
|
||||
# command).
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
DOTFILE_DIRS =
|
||||
DOTFILE_DIRS =
|
||||
|
||||
# The MSCFILE_DIRS tag can be used to specify one or more directories that
|
||||
# contain msc files that are included in the documentation (see the \mscfile
|
||||
# command).
|
||||
|
||||
MSCFILE_DIRS =
|
||||
MSCFILE_DIRS =
|
||||
|
||||
# The DIAFILE_DIRS tag can be used to specify one or more directories that
|
||||
# contain dia files that are included in the documentation (see the \diafile
|
||||
# command).
|
||||
|
||||
DIAFILE_DIRS =
|
||||
DIAFILE_DIRS =
|
||||
|
||||
# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
|
||||
# path where java can find the plantuml.jar file. If left blank, it is assumed
|
||||
# PlantUML is not used or called during a preprocessing step. Doxygen will
|
||||
# generate a warning when it encounters a \startuml command in this case and
|
||||
# will not generate output for the diagram.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
PLANTUML_JAR_PATH =
|
||||
|
||||
# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
|
||||
# that will be shown in the graph. If the number of nodes in a graph becomes
|
||||
|
||||
61
README.md
61
README.md
@@ -1,57 +1,16 @@
|
||||
pvaDataCPP
|
||||
==========
|
||||
# pvaDataCPP
|
||||
|
||||
pvDataCPP is a set of data types and utilities that form part of the EPICS V4 project.
|
||||
The EPICS **pvData** API provides a set of classes and utilities that form the core of the EPICS PVA implementation.
|
||||
|
||||
The pvDataCPP module is a part of the EPICS software toolkit that implements pvData structures as C++ class objects.
|
||||
|
||||
Further Info
|
||||
------------
|
||||
## Links
|
||||
|
||||
Consult the documents in the documentation directory, in particular
|
||||
|
||||
* pvDataCPP.html
|
||||
* RELEASE_NOTES.md
|
||||
|
||||
Also see the [EPICS Version 4 website](http://epics-pvdata.sourceforge.net)
|
||||
|
||||
Prerequisites
|
||||
-------------
|
||||
|
||||
The pvDataCPP requires recent versions of the following software:
|
||||
|
||||
1. EPICS Base (v3.14.12.3 or later)
|
||||
2. EPICS4 pvCommonCPP (4.1.0 or later)
|
||||
|
||||
(pvCommonCPP may not be needed depending on host/compiler.)
|
||||
|
||||
|
||||
Building
|
||||
--------
|
||||
|
||||
Building uses the make utility and the EPICS base build system.
|
||||
|
||||
The build system needs the location of the prerequisites, e.g. by placing the
|
||||
lines of the form
|
||||
|
||||
PVCOMMON = /home/install/epicsV4/pvCommonCPP
|
||||
EPICS_BASE = /home/install/epics/base
|
||||
|
||||
pointing to the locations in a file called RELEASE.local
|
||||
in the configure directory or the parent directory of pvDataCPP.
|
||||
|
||||
With this in place, to build type make
|
||||
|
||||
make
|
||||
|
||||
To perform a clean build type
|
||||
|
||||
make clean uninstall
|
||||
|
||||
To run the unit tests type
|
||||
|
||||
make runtests
|
||||
|
||||
For more information on the EPICS build system consult the
|
||||
[Application Development guide](http://www.aps.anl.gov/epics/base/R3-14/12-docs/AppDevGuide.pdf).
|
||||
- General information about EPICS can be found at the
|
||||
[EPICS Controls website](https://epics-controls.org).
|
||||
- API documentation for this module can be found on its
|
||||
[Github Pages website](https://epics-base.github.io/pvDataCPP/).
|
||||
|
||||
## Building
|
||||
|
||||
This module is included as a submodule of a full EPICS 7 release and will be compiled during builds of that software.
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
EPICS_PVD_MAJOR_VERSION = 7
|
||||
# Version number for the PV Data API and shared library
|
||||
|
||||
EPICS_PVD_MAJOR_VERSION = 8
|
||||
EPICS_PVD_MINOR_VERSION = 0
|
||||
EPICS_PVD_MAINTENANCE_VERSION = 1
|
||||
EPICS_PVD_MAINTENANCE_VERSION = 8
|
||||
|
||||
# Development flag, set to zero for release versions
|
||||
|
||||
EPICS_PVD_DEVELOPMENT_FLAG = 1
|
||||
|
||||
# Immediately after a release the MAINTENANCE_VERSION
|
||||
# will be incremented and the DEVELOPMENT_FLAG set to 1
|
||||
|
||||
@@ -20,9 +20,14 @@ CHECK_RELEASE = YES
|
||||
# INSTALL_LOCATION here.
|
||||
#INSTALL_LOCATION=</path/name/to/install/top>
|
||||
|
||||
USR_CPPFLAGS += -DPVD_INTERNAL
|
||||
|
||||
-include $(TOP)/../CONFIG_SITE.local
|
||||
-include $(TOP)/configure/CONFIG_SITE.local
|
||||
|
||||
# MSVC - skip defining min()/max() macros
|
||||
USR_CPPFLAGS_WIN32 += -DNOMINMAX
|
||||
|
||||
ifdef WITH_COVERAGE
|
||||
USR_CPPFLAGS += --coverage
|
||||
USR_LDFLAGS += --coverage
|
||||
|
||||
@@ -26,8 +26,7 @@
|
||||
#MYMODULE = $(MODULES)/my-module
|
||||
|
||||
# If building the EPICS modules individually, set these:
|
||||
#EPICS_LIBCOM = $(MODULES)/libcom-3.17.0
|
||||
#EPICS_BASE = $(MODULES)/core-7.0.1
|
||||
#EPICS_BASE = /path/to/base
|
||||
|
||||
# Set RULES here if you want to use build rules from elsewhere:
|
||||
#RULES = $(MODULES)/build-rules
|
||||
|
||||
@@ -38,7 +38,7 @@ PROJECT_NAME = "PVData C++"
|
||||
# could be handy for archiving the generated documentation or if some version
|
||||
# control system is used.
|
||||
|
||||
PROJECT_NUMBER =
|
||||
PROJECT_NUMBER = 8.0.8-dev
|
||||
|
||||
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
||||
# for a project that appears at the top of each page and should give viewer a
|
||||
@@ -319,7 +319,7 @@ AUTOLINK_SUPPORT = YES
|
||||
# diagrams that involve STL classes more complete and accurate.
|
||||
# The default value is: NO.
|
||||
|
||||
BUILTIN_STL_SUPPORT = YES
|
||||
BUILTIN_STL_SUPPORT = NO
|
||||
|
||||
# If you use Microsoft's C++/CLI language, you should set this option to YES to
|
||||
# enable parsing support.
|
||||
@@ -764,11 +764,11 @@ WARN_LOGFILE =
|
||||
# Note: If this tag is empty the current directory is searched.
|
||||
|
||||
INPUT = ../src/pv \
|
||||
./mainpage.h \
|
||||
./mainpage.dox \
|
||||
../src/copy/pv \
|
||||
../src/misc/pv \
|
||||
../src/json/pv \
|
||||
./release_notes.h
|
||||
./release_notes.dox
|
||||
|
||||
# This tag can be used to specify the character encoding of the source files
|
||||
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
|
||||
@@ -1001,7 +1001,7 @@ VERBATIM_HEADERS = YES
|
||||
# compiled with the --with-libclang option.
|
||||
# The default value is: NO.
|
||||
|
||||
CLANG_ASSISTED_PARSING = NO
|
||||
CLANG_ASSISTED_PARSING = YES
|
||||
|
||||
# If clang assisted parsing is enabled you can provide the compiler with command
|
||||
# line options that you would normally use when invoking the compiler. Note that
|
||||
@@ -1948,7 +1948,7 @@ ENABLE_PREPROCESSING = YES
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
|
||||
|
||||
MACRO_EXPANSION = NO
|
||||
MACRO_EXPANSION = YES
|
||||
|
||||
# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
|
||||
# the macro expansion is limited to the macros specified with the PREDEFINED and
|
||||
@@ -1970,7 +1970,7 @@ SEARCH_INCLUDES = YES
|
||||
# preprocessor.
|
||||
# This tag requires that the tag SEARCH_INCLUDES is set to YES.
|
||||
|
||||
INCLUDE_PATH =
|
||||
INCLUDE_PATH = ../src/misc ../src ../../../include/
|
||||
|
||||
# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
|
||||
# patterns (like *.h and *.hpp) to filter out the header-files in the
|
||||
@@ -1997,7 +1997,7 @@ PREDEFINED =
|
||||
# definition found in the source code.
|
||||
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
|
||||
|
||||
EXPAND_AS_DEFINED =
|
||||
EXPAND_AS_DEFINED =
|
||||
|
||||
# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
|
||||
# remove all references to function-like macros that are alone on a line, have
|
||||
@@ -2007,7 +2007,7 @@ EXPAND_AS_DEFINED =
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
|
||||
|
||||
SKIP_FUNCTION_MACROS = YES
|
||||
SKIP_FUNCTION_MACROS = NO
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to external references
|
||||
@@ -2026,7 +2026,7 @@ SKIP_FUNCTION_MACROS = YES
|
||||
# the path). If a tag file is not located in the directory in which doxygen is
|
||||
# run, you must also specify the path to the tagfile here.
|
||||
|
||||
TAGFILES = ../../../base-git/documentation/epics-base.tag
|
||||
TAGFILES = "libstdc++.tag = http://gcc.gnu.org/onlinedocs/libstdc++/latest-doxygen"
|
||||
|
||||
# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
|
||||
# tag file that is based on the input files it reads. See section "Linking to
|
||||
@@ -2045,14 +2045,14 @@ ALLEXTERNALS = NO
|
||||
# listed.
|
||||
# The default value is: YES.
|
||||
|
||||
EXTERNAL_GROUPS = YES
|
||||
EXTERNAL_GROUPS = NO
|
||||
|
||||
# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed in
|
||||
# the related pages index. If set to NO, only the current project's pages will
|
||||
# be listed.
|
||||
# The default value is: YES.
|
||||
|
||||
EXTERNAL_PAGES = YES
|
||||
EXTERNAL_PAGES = NO
|
||||
|
||||
# The PERL_PATH should be the absolute path and name of the perl script
|
||||
# interpreter (i.e. the result of 'which perl').
|
||||
|
||||
@@ -3,11 +3,14 @@ all: gen
|
||||
clean:
|
||||
rm -rf doxygen_sqlite3.db html
|
||||
|
||||
gen:
|
||||
gen: libstdc++.tag
|
||||
doxygen
|
||||
|
||||
commit: gen
|
||||
touch html/.nojekyll
|
||||
./commit-gh.sh documentation/html/ html/.nojekyll html/*.* html/search/*.*
|
||||
|
||||
libstdc++.tag:
|
||||
wget -O $@ https://gcc.gnu.org/onlinedocs/libstdc++/latest-doxygen/libstdc++.tag
|
||||
|
||||
.PHONY: all clean gen commit
|
||||
|
||||
7
documentation/_static/css/custom.css
Normal file
7
documentation/_static/css/custom.css
Normal file
@@ -0,0 +1,7 @@
|
||||
.wy-side-nav-search {
|
||||
background-color: #18334B;
|
||||
}
|
||||
|
||||
.wy-side-nav-search input[type="text"] {
|
||||
border-color: #18334b;
|
||||
}
|
||||
78
documentation/conf.py
Normal file
78
documentation/conf.py
Normal file
@@ -0,0 +1,78 @@
|
||||
# Configuration file for the Sphinx documentation builder.
|
||||
#
|
||||
# This file only contains a selection of the most common options. For a full
|
||||
# list see the documentation:
|
||||
# http://www.sphinx-doc.org/en/master/config
|
||||
|
||||
# -- Path setup --------------------------------------------------------------
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#
|
||||
# import os
|
||||
# import sys
|
||||
# sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
|
||||
project = 'EPICS Documentation'
|
||||
copyright = '2019, EPICS Controls.'
|
||||
author = 'EPICS'
|
||||
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
'sphinx.ext.intersphinx',
|
||||
]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This pattern also affects html_static_path and html_extra_path.
|
||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||
|
||||
# Intersphinx links to subprojects
|
||||
intersphinx_mapping = {
|
||||
'how-tos': ('https://docs.epics-controls.org/projects/how-tos/en/latest', None),
|
||||
}
|
||||
|
||||
|
||||
# -- Options for HTML output -------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
#
|
||||
html_theme = 'sphinx_rtd_theme'
|
||||
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
html_css_files = [
|
||||
'css/custom.css',
|
||||
]
|
||||
|
||||
master_doc = 'index'
|
||||
|
||||
html_theme_options = {
|
||||
'logo_only': True,
|
||||
}
|
||||
html_logo = "images/EPICS_white_logo_v02.png"
|
||||
|
||||
html_extra_path = ['../html']
|
||||
|
||||
|
||||
# -- Run Doxygen ------------------------------------------------------------
|
||||
|
||||
import subprocess
|
||||
subprocess.call('cd ..; mkdir -p html/doxygen; doxygen', shell=True)
|
||||
Binary file not shown.
BIN
documentation/images/EPICS_white_logo_v02.png
Normal file
BIN
documentation/images/EPICS_white_logo_v02.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.5 KiB |
16
documentation/index.rst
Normal file
16
documentation/index.rst
Normal file
@@ -0,0 +1,16 @@
|
||||
pvData (C++) Library
|
||||
====================
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
|
||||
EPICS Website <https://epics-controls.org>
|
||||
EPICS Documentation Home <https://docs.epics-controls.org>
|
||||
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: pvDataCPP
|
||||
|
||||
Reference Manual and API Documentation <https://docs.epics-controls.org/projects/pvdata-cpp/en/latest/doxygen>
|
||||
Source Code Repository on GitHub <https://github.com/epics-base/pvDataCPP>
|
||||
@@ -3,7 +3,8 @@
|
||||
/**
|
||||
@mainpage pvDataCPP documentation
|
||||
|
||||
- [Download](https://sourceforge.net/projects/epics-pvdata/files/)
|
||||
- This module is included in [EPICS Base releases](https://epics-controls.org/resources-and-support/base/) beginning with 7.0.1
|
||||
- It may also be [Downloaded](https://github.com/epics-base/pvDataCPP/releases) and built separately.
|
||||
- @htmlonly <a href="modules.html">API components</a> @endhtmlonly
|
||||
- @ref release_notes
|
||||
|
||||
@@ -23,33 +24,32 @@ See pv/pvData.h header.
|
||||
Define a structure type and create a container with default values.
|
||||
|
||||
@code
|
||||
epics::pvData::StructureConstPtr stype; // aka std::tr1::shared_ptr<const epics::pvData::Structure>
|
||||
stype = epics::pvData::getFieldCreate()->createFieldBuilder()
|
||||
->add("fld1", epics::pvData::pvInt)
|
||||
namespace pvd = epics::pvData;
|
||||
pvd::StructureConstPtr stype(pvd::FieldBuilder::begin()
|
||||
->add("fld1", pvd::pvInt)
|
||||
->addNestedStructure("sub")
|
||||
->add("fld2", epics::pvData::pvString)
|
||||
->add("fld2", pvd::pvString)
|
||||
->endNested()
|
||||
->createStructure();
|
||||
->createStructure());
|
||||
|
||||
epics::pvData::PVStructuretPtr value; // aka std::tr1::shared_ptr<epics::pvData::PVStructure>
|
||||
value = epics::pvData::getPVDataCreate()->createPVStructure(stype);
|
||||
pvd::PVStructuretPtr value(stype->build());
|
||||
|
||||
value->getSubField<epics::pvData::PVInt>("fld1")->put(4); // store integer 4
|
||||
value->getSubField<epics::pvData::PVScalar>("sub.fld2")->putFrom(4.2); // convert and store string "4.2"
|
||||
value->getSubFieldT<pvd::PVInt>("fld1")->put(4); // store integer 4. would throw if not pvInt
|
||||
value->getSubFieldT<pvd::PVScalar>("sub.fld2")->putFrom(4.2); // convert and store string "4.2"
|
||||
@endcode
|
||||
|
||||
is equivalent to the following pseudo-code.
|
||||
|
||||
@code
|
||||
struct stype {
|
||||
epics::pvData::int32 fld1;
|
||||
pvd::int32 fld1;
|
||||
struct {
|
||||
std::string fld2;
|
||||
} sub;
|
||||
};
|
||||
stype value;
|
||||
value.fld1 = 4;
|
||||
value.fld2 = epics::pvData::castUnsafe<std::string>(4.2);
|
||||
value.fld2 = pvd::castUnsafe<std::string>(4.2);
|
||||
@endcode
|
||||
|
||||
*/
|
||||
@@ -2,46 +2,138 @@
|
||||
|
||||
@page release_notes Release Notes
|
||||
|
||||
Release 7.1.0 (UNRELEASED)
|
||||
Release 8.0.7 (Dec 2025)
|
||||
========================
|
||||
|
||||
- Compatible changes
|
||||
- Allow epics::pvData::Timer to be cancelled during callback execution.
|
||||
- Clang compiler warnings cleaned up.
|
||||
- Limit periodic timers to one catch-up after missing many events.
|
||||
|
||||
Release 8.0.6 (Dec 2023)
|
||||
========================
|
||||
|
||||
- Compatible changes
|
||||
- Actually enable JSON-5 output in PVStructure::Formatter::JSON when available.
|
||||
- Fix unaligned access issues for some ARM/Linux targets.
|
||||
|
||||
Release 8.0.5 (Sep 2022)
|
||||
========================
|
||||
|
||||
- Compatible changes
|
||||
- Internal changes to use the YAJL API for generating JSON and JSON-5 output.
|
||||
|
||||
Release 8.0.4 (Feb 2021)
|
||||
========================
|
||||
|
||||
- Incompatible changes
|
||||
- Remove ByteBuffer::align()
|
||||
- Compatible changes
|
||||
- Deprecate SerializableControl::alignBuffer() and DeserializableControl::alignData()
|
||||
- shared_vector_convert<>() fix convert of empty, untyped, array
|
||||
|
||||
Release 8.0.3 (July 2020)
|
||||
=========================
|
||||
|
||||
- Incompatible changes
|
||||
- Removed THROW_BASE_EXCEPTION_CAUSE() macro which has long ignored its cause.
|
||||
Any external users should switch to the functionally identical THROW_BASE_EXCEPTION_CAUSE()
|
||||
- Various printing of functions now conditionally escape strings
|
||||
including quote '\"' and similar charactors.
|
||||
|
||||
Release 8.0.2 (May 2020)
|
||||
========================
|
||||
|
||||
- Changes to documentation and unittests.
|
||||
No functional changes to library.
|
||||
|
||||
Release 8.0.1 (Nov 2019)
|
||||
==========================
|
||||
|
||||
- Incompatible changes
|
||||
- Requires Base >= 3.15
|
||||
- Bug fixes
|
||||
|
||||
Release 8.0.0 (July 2019)
|
||||
=========================
|
||||
|
||||
- Deprecations
|
||||
- pv/localStaticLock.h
|
||||
- ByteBuffer::getArray()
|
||||
- Removals
|
||||
- Remove previously deprecated executor.h, queue.h and timerFunction.h
|
||||
- Remove *HashFunction functors to "hash" Field sub-classes which were never fully implemented.
|
||||
- Fixes
|
||||
- Make thread safe getFieldCreate() and getPVDataCreate()
|
||||
- Workaround for MSVC pickyness that iterators be non-NULL, even when not de-referenced.
|
||||
- Fix alignment fault during (de)serialization on RTEMS/vxWorks.
|
||||
- Fix shared_vector::swap() for void specialization.
|
||||
- Changes in several Field sub-classes to return const ref. instead of a copy.
|
||||
- pv/localStaticLock.h
|
||||
- pv/pvCopy.h (see epics::pvData::PVRequestMapper)
|
||||
- Additions
|
||||
- shared_vector add c++11 move and construct for initializer list.
|
||||
- Add AnyScalar::clear()
|
||||
- Add ctor AnyScalar(ScalarType, const void*) to allow construction from an untyped buffer.
|
||||
- Add Timer::close()
|
||||
- Allow castUnsafe() from const char* without first allocating a std::string.
|
||||
- Add {Structure,Union}::getFieldT
|
||||
|
||||
Release 7.1.3 (Apr 2019)
|
||||
========================
|
||||
|
||||
- Fix for array serialization error to/from big endian.
|
||||
https://github.com/epics-base/pvDataCPP/issues/65
|
||||
|
||||
Release 7.1.2 (Mar 2019)
|
||||
========================
|
||||
|
||||
- 7.1.1 tag pushed prematurely.
|
||||
|
||||
Release 7.1.1 (Mar 2019)
|
||||
========================
|
||||
|
||||
- Fixes
|
||||
- Init order issue with StandardField::getStandardField()
|
||||
- Build fix for Visual Studio 2013+
|
||||
|
||||
Release 7.1.0 (Nov 2018)
|
||||
========================
|
||||
|
||||
- Deprecations
|
||||
- BoundedString, BoundedScalarArray, and FixedScalarArray will be removed unless they are fixed.
|
||||
See https://github.com/epics-base/pvDataCPP/issues/52 for discussion.
|
||||
- pv/localStaticLock.h
|
||||
- pv/pvCopy.h (see epics::pvData::PVRequestMapper)
|
||||
- Removals
|
||||
- Remove previously deprecated executor.h, queue.h and timerFunction.h
|
||||
- Remove *HashFunction functors to "hash" Field sub-classes which were never fully implemented.
|
||||
- Fixes
|
||||
- Make thread safe getFieldCreate() and getPVDataCreate()
|
||||
- Workaround for MSVC pickyness that iterators be non-NULL, even when not de-referenced.
|
||||
- Fix alignment fault during (de)serialization on RTEMS/vxWorks.
|
||||
- Fix epics::pvData::shared_vector::swap() for void specialization.
|
||||
- Changes in several epics::pvData::Field sub-classes to return const ref. instead of a copy.
|
||||
- Additions
|
||||
- epics::pvData::shared_vector add c++11 move and construct for initializer list.
|
||||
- Add epics::pvData::AnyScalar::clear()
|
||||
- Add ctor epics::pvData::AnyScalar(ScalarType, const void*) to allow construction from an untyped buffer.
|
||||
- Add epics::pvData::Timer::close()
|
||||
- Allow epics::pvData::castUnsafe() from const char* without first allocating a std::string.
|
||||
- De-duplication of epics::pvData::Field instances is performed using a global hash table.
|
||||
Identical definitions will share a single instance. Allows O(0) comparision.
|
||||
- Add epics::pvData::PVRequestMapper to facilitate (partial) copying between PVStructure instances
|
||||
modified by a pvRequest.
|
||||
- Add shorthand notations epics::pvData::FieldBuilder::begin() and epics::pvData::Field::build()
|
||||
|
||||
Release 7.0.0 (Dec 2017)
|
||||
========================
|
||||
|
||||
- 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
|
||||
- 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
|
||||
- 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
|
||||
- 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
|
||||
- Add reftrack @ref pvd_reftrack
|
||||
- Add header typemap.h to facilitate boilerplate switch() over ScalarType
|
||||
- Add epics::auto_ptr typedef in help writing code supporting both c++98 and c++11 w/o copious deprecation warnings.
|
||||
|
||||
|
||||
Release 6.0.1
|
||||
@@ -3,7 +3,6 @@
|
||||
SRC_DIRS += $(PVDATA_SRC)/copy
|
||||
|
||||
INC += pv/createRequest.h
|
||||
INC += pv/pvCopy.h
|
||||
|
||||
LIBSRCS += createRequest.cpp
|
||||
LIBSRCS += pvCopy.cpp
|
||||
LIBSRCS += requestmapper.cpp
|
||||
|
||||
@@ -30,7 +30,7 @@ static FieldCreatePtr fieldCreate = getFieldCreate();
|
||||
|
||||
struct CreateRequestImpl {
|
||||
|
||||
struct Node
|
||||
struct Node
|
||||
{
|
||||
string name;
|
||||
vector<Node> nodes;
|
||||
@@ -55,7 +55,7 @@ struct CreateRequestImpl {
|
||||
|
||||
|
||||
CreateRequestImpl() {}
|
||||
|
||||
|
||||
|
||||
void removeBlanks(string& str)
|
||||
{
|
||||
@@ -129,7 +129,7 @@ struct CreateRequestImpl {
|
||||
if(pos==string::npos) break;
|
||||
numValues++;
|
||||
index = pos +1;
|
||||
}
|
||||
}
|
||||
vector<string> valueList(numValues,"");
|
||||
index=0;
|
||||
for(size_t i=0; i<numValues; i++) {
|
||||
@@ -318,7 +318,7 @@ struct CreateRequestImpl {
|
||||
if (!request.empty()) removeBlanks(request);
|
||||
if (request.empty())
|
||||
{
|
||||
return pvDataCreate->createPVStructure(fieldCreate->createStructure());
|
||||
return fieldCreate->createStructure()->build();
|
||||
}
|
||||
size_t offsetRecord = request.find("record[");
|
||||
size_t offsetField = request.find("field(");
|
||||
@@ -437,7 +437,7 @@ struct CreateRequestImpl {
|
||||
}
|
||||
StructureConstPtr structure = fieldCreate->createStructure(names, fields);
|
||||
if(!structure) throw std::invalid_argument("bad request " + crequest);
|
||||
PVStructurePtr pvStructure = pvDataCreate->createPVStructure(structure);
|
||||
PVStructurePtr pvStructure = structure->build();
|
||||
for(size_t i=0; i<optionList.size(); ++i) {
|
||||
OptionPair pair = optionList[i];
|
||||
string name = pair.name;
|
||||
@@ -483,4 +483,3 @@ PVStructure::shared_pointer createRequest(std::string const & request)
|
||||
|
||||
|
||||
}} // namespace
|
||||
|
||||
|
||||
@@ -8,14 +8,18 @@
|
||||
#define CREATEREQUEST_H
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/lock.h>
|
||||
#include <pv/bitSet.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
class BitSet;
|
||||
|
||||
/**
|
||||
* @brief Create pvRequest structure for Channel methods.
|
||||
*
|
||||
@@ -61,6 +65,168 @@ protected:
|
||||
epicsShareExtern
|
||||
PVStructure::shared_pointer createRequest(std::string const & request);
|
||||
|
||||
/** Helper for implementations of epics::pvAccess::ChannelProvider in interpreting the
|
||||
* 'field' substructure of a pvRequest.
|
||||
* Copies between an internal (base) Structure, and a client/user visible (requested) Structure.
|
||||
*
|
||||
* @note PVRequestMapper is not re-entrant. It is copyable and swap()able.
|
||||
*/
|
||||
class epicsShareClass PVRequestMapper {
|
||||
public:
|
||||
enum mode_t {
|
||||
/** Masking mode.
|
||||
*
|
||||
* Requested Structure is identical to Base.
|
||||
* The 'field' substructure of the provided pvRequest is used to construct a BitSet
|
||||
* where the bits corresponding to the "selected" fields are set. This mask can be
|
||||
* access via. requestedMask(). The copy* and mask* methods operate only
|
||||
* on "selected" fields.
|
||||
*/
|
||||
Mask,
|
||||
/** Slice mode
|
||||
*
|
||||
* The Requested Structure is a strict sub-set of the Base Structure containing
|
||||
* those fields "selected" by the 'field' substructure of the provided pvRequest.
|
||||
*/
|
||||
Slice,
|
||||
};
|
||||
|
||||
PVRequestMapper();
|
||||
//! @see compute()
|
||||
PVRequestMapper(const PVStructure& base,
|
||||
const PVStructure& pvRequest,
|
||||
mode_t mode = Mask);
|
||||
|
||||
//! return to state of default ctor
|
||||
void reset();
|
||||
|
||||
//! @returns the Structure of the PVStructure previously passed to compute(). NULL if never computed()'d
|
||||
inline const StructureConstPtr& base() const { return typeBase; }
|
||||
//! @returns the Structure which is the selected sub-set of the base Structure. NULL if never computed()'d
|
||||
inline const StructureConstPtr& requested() const { return typeRequested; }
|
||||
|
||||
/** A mask of all fields in the base structure which are also in the requested structure,
|
||||
* and any parent/structure "compress" bits. eg. bit 0 is always set.
|
||||
*
|
||||
@code
|
||||
PVRequestMapper mapper(...);
|
||||
...
|
||||
BitSet changed = ...; // a base changed mask
|
||||
bool wouldcopy = changed.logical_and(mapper.requestedMask());
|
||||
// wouldcopy==false means that copyBaseToRequested(..., changed, ...) would be a no-op
|
||||
@endcode
|
||||
*
|
||||
* eg. allows early detection of empty monitor updates.
|
||||
*/
|
||||
inline const BitSet& requestedMask() const { return maskRequested; }
|
||||
|
||||
//! @returns A new instance of the requested() Structure
|
||||
PVStructurePtr buildRequested() const;
|
||||
//! @returns A new instance of the base() Structure
|
||||
PVStructurePtr buildBase() const;
|
||||
|
||||
/** (re)compute the selected subset of provided base structure.
|
||||
* @param base A full base structure.
|
||||
* Must be "top level" (field offset zero).
|
||||
* @param pvRequest The user/client provided request modifier
|
||||
* @param mode Control how the mapping is constructed. @see mode_t for a description of mapping modes.
|
||||
*
|
||||
* @post Updates warnings()
|
||||
* @throws std::runtime_error For errors involving invalid pvRequest
|
||||
* @throws std::logic_error if the provided base is not a "top level" PVStructure.
|
||||
*/
|
||||
void compute(const PVStructure& base,
|
||||
const PVStructure& pvRequest,
|
||||
mode_t mode = Mask);
|
||||
|
||||
//! After compute(), check if !warnings().empty()
|
||||
inline const std::string& warnings() const { return messages; }
|
||||
|
||||
/** Copy field values from Base structure into Requested structure
|
||||
*
|
||||
* @param base An instance of the base Structure. Field values are copied from it.
|
||||
* Need not be the same instance passed to compute().
|
||||
* @param baseMask A bit mask selecting those base fields to copy.
|
||||
* @param request An instance of the requested() Structure. Field values are copied to it.
|
||||
* @param requestMask A bit mask indicating which requested fields were copied.
|
||||
* BitSet::clear() is not called.
|
||||
*/
|
||||
void copyBaseToRequested(
|
||||
const PVStructure& base,
|
||||
const BitSet& baseMask,
|
||||
PVStructure& request,
|
||||
BitSet& requestMask
|
||||
) const;
|
||||
|
||||
/** Copy field values into Base structure from Requested structure
|
||||
*
|
||||
* @param base An instance of the base Structure. Field values are copied into it.
|
||||
* Need not be the same instance passed to compute().
|
||||
* @param baseMask A bit mask indicating which base fields were copied.
|
||||
* BitSet::clear() is not called.
|
||||
* @param request An instance of the requested() Structure. Field values are copied from it.
|
||||
* @param requestMask A bit mask selecting those requested fields to copy.
|
||||
*/
|
||||
void copyBaseFromRequested(
|
||||
PVStructure& base,
|
||||
BitSet& baseMask,
|
||||
const PVStructure& request,
|
||||
const BitSet& requestMask
|
||||
) const;
|
||||
|
||||
//! Translate Base bit mask into requested bit mask.
|
||||
//! BitSet::clear() is not called.
|
||||
inline void maskBaseToRequested(
|
||||
const BitSet& baseMask,
|
||||
BitSet& requestMask
|
||||
) const
|
||||
{ _mapMask(baseMask, requestMask, false); }
|
||||
|
||||
//! Translate requested bit mask into base bit mask.
|
||||
//! BitSet::clear() is not called.
|
||||
inline void maskBaseFromRequested(
|
||||
BitSet& baseMask,
|
||||
const BitSet& requestMask
|
||||
) const
|
||||
{ _mapMask(requestMask, baseMask, true); }
|
||||
|
||||
//! Exchange contents of two mappers. O(0) and never throws.
|
||||
void swap(PVRequestMapper& other);
|
||||
|
||||
private:
|
||||
bool _compute(const PVStructure& base, const PVStructure& pvReq,
|
||||
FieldBuilderPtr& builder, bool keepids, unsigned depth);
|
||||
|
||||
void _map(const PVStructure& src,
|
||||
const BitSet& maskSrc,
|
||||
PVStructure& dest,
|
||||
BitSet& maskDest,
|
||||
bool dir_r2b) const;
|
||||
void _mapMask(const BitSet& maskSrc,
|
||||
BitSet& maskDest,
|
||||
bool dir_r2b) const;
|
||||
|
||||
StructureConstPtr typeBase, typeRequested;
|
||||
BitSet maskRequested;
|
||||
// Map between field offsets of base and requested Structures.
|
||||
// Include all fields, both leaf and sub-structure.
|
||||
struct Mapping {
|
||||
size_t to; // offset in destination Structure
|
||||
BitSet tomask, // if !leaf these are the other bits in the destination mask to changed
|
||||
frommask; // if !leaf these are the other bits in the source mask to be copied
|
||||
bool valid; // only true in (sparse) base -> requested mapping
|
||||
bool leaf; // not a (sub)Structure?
|
||||
Mapping() :valid(false) {}
|
||||
Mapping(size_t to, bool leaf) :to(to), valid(true), leaf(leaf) {}
|
||||
};
|
||||
typedef std::vector<Mapping> mapping_t;
|
||||
mapping_t base2req, req2base;
|
||||
|
||||
std::string messages;
|
||||
|
||||
mutable BitSet scratch; // avoid temporary allocs. (we aren't re-entrant!)
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif /* CREATEREQUEST_H */
|
||||
|
||||
@@ -1,232 +0,0 @@
|
||||
/* pvCopy.h */
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/**
|
||||
* @author Marty Kraimer
|
||||
* @date 2013.04
|
||||
*/
|
||||
#ifndef PVCOPY_H
|
||||
#define PVCOPY_H
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
#include <memory>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/bitSet.h>
|
||||
|
||||
namespace epics { namespace pvData{
|
||||
|
||||
class PVCopyTraverseMasterCallback;
|
||||
typedef std::tr1::shared_ptr<PVCopyTraverseMasterCallback> PVCopyTraverseMasterCallbackPtr;
|
||||
|
||||
/**
|
||||
* @brief Callback for traversing master structure
|
||||
*
|
||||
* Must be implemented by code that creates pvCopy.
|
||||
*/
|
||||
class epicsShareClass PVCopyTraverseMasterCallback
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(PVCopyTraverseMasterCallback);
|
||||
virtual ~PVCopyTraverseMasterCallback() {}
|
||||
/**
|
||||
* Called once for each field in master.
|
||||
* @param pvField The field in master.
|
||||
*/
|
||||
virtual void nextMasterPVField(epics::pvData::PVFieldPtr const &pvField) = 0;
|
||||
};
|
||||
|
||||
|
||||
class PVCopy;
|
||||
typedef std::tr1::shared_ptr<PVCopy> PVCopyPtr;
|
||||
|
||||
struct CopyNode;
|
||||
typedef std::tr1::shared_ptr<CopyNode> CopyNodePtr;
|
||||
struct CopyMasterNode;
|
||||
typedef std::tr1::shared_ptr<CopyMasterNode> CopyMasterNodePtr;
|
||||
struct CopyStructureNode;
|
||||
typedef std::tr1::shared_ptr<CopyStructureNode> CopyStructureNodePtr;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Support for subset of fields in a pvStructure.
|
||||
*
|
||||
* Class that manages one or more PVStructures that holds an arbitrary subset of the fields
|
||||
* in another PVStructure called master.
|
||||
*/
|
||||
class epicsShareClass PVCopy :
|
||||
public std::tr1::enable_shared_from_this<PVCopy>
|
||||
{
|
||||
public:
|
||||
POINTER_DEFINITIONS(PVCopy);
|
||||
/**
|
||||
* Create a new pvCopy
|
||||
* @param pvMaster The top-level structure for which a copy of
|
||||
* an arbitrary subset of the fields in master will be created and managed.
|
||||
* @param pvRequest Selects the set of subfields desired and options for each field.
|
||||
* @param structureName The name for the top level of any PVStructure created.
|
||||
*/
|
||||
static PVCopyPtr create(
|
||||
PVStructurePtr const &pvMaster,
|
||||
PVStructurePtr const &pvRequest,
|
||||
std::string const & structureName);
|
||||
virtual ~PVCopy(){}
|
||||
void destroy();
|
||||
/**
|
||||
* Get the top-level structure of master
|
||||
* @returns The master top-level structure.
|
||||
* This should not be modified.
|
||||
*/
|
||||
PVStructurePtr getPVMaster();
|
||||
/**
|
||||
* Traverse all the fields in master.
|
||||
* @param callback This is called for each field on master.
|
||||
*/
|
||||
void traverseMaster(PVCopyTraverseMasterCallbackPtr const & callback)
|
||||
{
|
||||
traverseMaster(headNode,callback);
|
||||
}
|
||||
/**
|
||||
* Get the introspection interface for a PVStructure for e copy.
|
||||
*/
|
||||
StructureConstPtr getStructure();
|
||||
/**
|
||||
* Create a copy instance. Monitors keep a queue of monitor elements.
|
||||
* Since each element needs a PVStructure, multiple top-level structures will be created.
|
||||
*/
|
||||
PVStructurePtr createPVStructure();
|
||||
/**
|
||||
* 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.
|
||||
* @param masterPVField The field in master.
|
||||
*/
|
||||
std::size_t getCopyOffset(PVFieldPtr const &masterPVField);
|
||||
/**
|
||||
* 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.
|
||||
* @param masterPVStructure A structure in master that has masterPVField.
|
||||
* @param masterPVField The field in master.
|
||||
*/
|
||||
std::size_t getCopyOffset(
|
||||
PVStructurePtr const &masterPVStructure,
|
||||
PVFieldPtr const &masterPVField);
|
||||
/**
|
||||
* Given an offset in the copy get the corresponding field in pvMaster.
|
||||
* @param structureOffset The offset in the copy.
|
||||
*/
|
||||
PVFieldPtr getMasterPVField(std::size_t structureOffset);
|
||||
/**
|
||||
* 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.
|
||||
* @param copyPVStructure A copy top-level structure.
|
||||
* @param bitSet A bitSet for copyPVStructure.
|
||||
*/
|
||||
void initCopy(
|
||||
PVStructurePtr const ©PVStructure,
|
||||
BitSetPtr const &bitSet);
|
||||
/**
|
||||
* 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.
|
||||
* @param copyPVStructure A copy top-level structure.
|
||||
* @param bitSet A bitSet for copyPVStructure.
|
||||
*/
|
||||
void updateCopySetBitSet(
|
||||
PVStructurePtr const ©PVStructure,
|
||||
BitSetPtr const &bitSet);
|
||||
/**
|
||||
* For each set bit in bitSet
|
||||
* set the field in copyPVStructure to the value of the corresponding field in pvMaster.
|
||||
* @param copyPVStructure A copy top-level structure.
|
||||
* @param bitSet A bitSet for copyPVStructure.
|
||||
*/
|
||||
void updateCopyFromBitSet(
|
||||
PVStructurePtr const ©PVStructure,
|
||||
BitSetPtr const &bitSet);
|
||||
/**
|
||||
* For each set bit in bitSet
|
||||
* set the field in pvMaster to the value of the corresponding field in copyPVStructure
|
||||
* @param copyPVStructure A copy top-level structure.
|
||||
* @param bitSet A bitSet for copyPVStructure.
|
||||
*/
|
||||
void updateMaster(
|
||||
PVStructurePtr const ©PVStructure,
|
||||
BitSetPtr const &bitSet);
|
||||
/**
|
||||
* Get the options for the field at the specified offset.
|
||||
* @param fieldOffset the offset in copy.
|
||||
* @returns A NULL is returned if no options were specified for the field.
|
||||
* If options were specified,PVStructurePtr is a structures
|
||||
* with a set of PVString subfields that specify name,value pairs.s
|
||||
* name is the subField name and value is the subField value.
|
||||
*/
|
||||
PVStructurePtr getOptions(std::size_t fieldOffset);
|
||||
/**
|
||||
* For debugging.
|
||||
*/
|
||||
std::string dump();
|
||||
private:
|
||||
void dump(
|
||||
std::string *builder,
|
||||
CopyNodePtr const &node,
|
||||
int indentLevel);
|
||||
PVCopyPtr getPtrSelf()
|
||||
{
|
||||
return shared_from_this();
|
||||
}
|
||||
void traverseMaster(CopyNodePtr const &node, PVCopyTraverseMasterCallbackPtr const & callback);
|
||||
|
||||
PVStructurePtr pvMaster;
|
||||
StructureConstPtr structure;
|
||||
CopyNodePtr headNode;
|
||||
PVStructurePtr cacheInitStructure;
|
||||
PVCopy(PVStructurePtr const &pvMaster);
|
||||
friend class PVCopyMonitor;
|
||||
bool init(PVStructurePtr const &pvRequest);
|
||||
std::string dump(
|
||||
std::string const &value,
|
||||
CopyNodePtr const &node,
|
||||
int indentLevel);
|
||||
StructureConstPtr createStructure(
|
||||
PVStructurePtr const &pvMaster,
|
||||
PVStructurePtr const &pvFromRequest);
|
||||
CopyNodePtr createStructureNodes(
|
||||
PVStructurePtr const &pvMasterStructure,
|
||||
PVStructurePtr const &pvFromRequest,
|
||||
PVStructurePtr const &pvFromField);
|
||||
void updateStructureNodeSetBitSet(
|
||||
PVStructurePtr const &pvCopy,
|
||||
CopyStructureNodePtr const &structureNode,
|
||||
BitSetPtr const &bitSet);
|
||||
void updateSubFieldSetBitSet(
|
||||
PVFieldPtr const &pvCopy,
|
||||
PVFieldPtr const &pvMaster,
|
||||
BitSetPtr const &bitSet);
|
||||
void updateStructureNodeFromBitSet(
|
||||
PVStructurePtr const &pvCopy,
|
||||
CopyStructureNodePtr const &structureNode,
|
||||
BitSetPtr const &bitSet,
|
||||
bool toCopy,
|
||||
bool doAll);
|
||||
void updateSubFieldFromBitSet(
|
||||
PVFieldPtr const &pvCopy,
|
||||
PVFieldPtr const &pvMasterField,
|
||||
BitSetPtr const &bitSet,
|
||||
bool toCopy,
|
||||
bool doAll);
|
||||
CopyMasterNodePtr getCopyOffset(
|
||||
CopyStructureNodePtr const &structureNode,
|
||||
PVFieldPtr const &masterPVField);
|
||||
CopyMasterNodePtr getMasterNode(
|
||||
CopyStructureNodePtr const &structureNode,
|
||||
std::size_t structureOffset);
|
||||
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif /* PVCOPY_H */
|
||||
@@ -1,649 +0,0 @@
|
||||
/* pvCopy.cpp */
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/**
|
||||
* @author Marty Kraimer
|
||||
* @date 2013.04
|
||||
*/
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
|
||||
#include <epicsThread.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
|
||||
#include <pv/thread.h>
|
||||
|
||||
#include <pv/pvCopy.h>
|
||||
|
||||
using std::tr1::static_pointer_cast;
|
||||
using std::tr1::dynamic_pointer_cast;
|
||||
using std::string;
|
||||
using std::size_t;
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
/**
|
||||
* Convenience method for implementing dump.
|
||||
* It generates a newline and inserts blanks at the beginning of the newline.
|
||||
* @param builder The std::string * being constructed.
|
||||
* @param indentLevel Indent level, Each level is four spaces.
|
||||
*/
|
||||
static void newLine(string *buffer, int indentLevel)
|
||||
{
|
||||
*buffer += "\n";
|
||||
*buffer += string(indentLevel*4, ' ');
|
||||
}
|
||||
|
||||
struct CopyNode {
|
||||
CopyNode()
|
||||
: isStructure(false),
|
||||
structureOffset(0),
|
||||
nfields(0)
|
||||
{}
|
||||
bool isStructure;
|
||||
size_t structureOffset; // In the copy
|
||||
size_t nfields;
|
||||
PVStructurePtr options;
|
||||
};
|
||||
|
||||
struct CopyMasterNode : public CopyNode{
|
||||
PVFieldPtr masterPVField;
|
||||
};
|
||||
|
||||
typedef std::vector<CopyNodePtr> CopyNodePtrArray;
|
||||
typedef std::tr1::shared_ptr<CopyNodePtrArray> CopyNodePtrArrayPtr;
|
||||
|
||||
struct CopyStructureNode : public CopyNode {
|
||||
CopyNodePtrArrayPtr nodes;
|
||||
};
|
||||
|
||||
PVCopyPtr PVCopy::create(
|
||||
PVStructurePtr const &pvMaster,
|
||||
PVStructurePtr const &pvRequest,
|
||||
string const & structureName)
|
||||
{
|
||||
PVStructurePtr pvStructure(pvRequest);
|
||||
if(structureName.size()>0) {
|
||||
if(pvRequest->getStructure()->getNumberFields()>0) {
|
||||
pvStructure = pvRequest->getSubField<PVStructure>(structureName);
|
||||
if(!pvStructure) return PVCopyPtr();
|
||||
}
|
||||
} else if(pvStructure->getSubField<PVStructure>("field")) {
|
||||
pvStructure = pvRequest->getSubField<PVStructure>("field");
|
||||
}
|
||||
PVCopyPtr pvCopy(new PVCopy(pvMaster));
|
||||
bool result = pvCopy->init(pvStructure);
|
||||
if(!result) pvCopy.reset();
|
||||
return pvCopy;
|
||||
}
|
||||
|
||||
PVCopy::PVCopy(
|
||||
PVStructurePtr const &pvMaster)
|
||||
: pvMaster(pvMaster)
|
||||
{
|
||||
}
|
||||
|
||||
void PVCopy::destroy()
|
||||
{
|
||||
headNode.reset();
|
||||
}
|
||||
|
||||
PVStructurePtr PVCopy::getPVMaster()
|
||||
{
|
||||
return pvMaster;
|
||||
}
|
||||
|
||||
void PVCopy::traverseMaster(CopyNodePtr const &innode, PVCopyTraverseMasterCallbackPtr const & callback)
|
||||
{
|
||||
CopyNodePtr node = innode;
|
||||
if(!node->isStructure) {
|
||||
CopyMasterNodePtr masterNode = static_pointer_cast<CopyMasterNode>(node);
|
||||
callback->nextMasterPVField(masterNode->masterPVField);
|
||||
return;
|
||||
}
|
||||
CopyStructureNodePtr structNode = static_pointer_cast<CopyStructureNode>(node);
|
||||
CopyNodePtrArrayPtr nodes = structNode->nodes;
|
||||
for(size_t i=0; i< nodes->size(); i++) {
|
||||
node = (*nodes)[i];
|
||||
traverseMaster(node,callback);
|
||||
}
|
||||
}
|
||||
|
||||
StructureConstPtr PVCopy::getStructure()
|
||||
{
|
||||
return structure;
|
||||
}
|
||||
|
||||
PVStructurePtr PVCopy::createPVStructure()
|
||||
{
|
||||
if(cacheInitStructure) {
|
||||
PVStructurePtr save = cacheInitStructure;
|
||||
cacheInitStructure.reset();
|
||||
return save;
|
||||
}
|
||||
PVStructurePtr pvStructure =
|
||||
getPVDataCreate()->createPVStructure(structure);
|
||||
return pvStructure;
|
||||
}
|
||||
|
||||
PVStructurePtr PVCopy::getOptions(std::size_t fieldOffset)
|
||||
{
|
||||
if(fieldOffset==0) return headNode->options;
|
||||
CopyNodePtr node = headNode;
|
||||
while(true) {
|
||||
if(!node->isStructure) {
|
||||
if(node->structureOffset==fieldOffset) return node->options;
|
||||
return PVStructurePtr();
|
||||
}
|
||||
CopyStructureNodePtr structNode = static_pointer_cast<CopyStructureNode>(node);
|
||||
CopyNodePtrArrayPtr nodes = structNode->nodes;
|
||||
boolean okToContinue = false;
|
||||
for(size_t i=0; i< nodes->size(); i++) {
|
||||
node = (*nodes)[i];
|
||||
size_t soff = node->structureOffset;
|
||||
if(fieldOffset>=soff && fieldOffset<soff+node->nfields) {
|
||||
if(fieldOffset==soff) return node->options;
|
||||
if(!node->isStructure) {
|
||||
return PVStructurePtr();
|
||||
}
|
||||
okToContinue = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(okToContinue) continue;
|
||||
throw std::invalid_argument("fieldOffset not valid");
|
||||
}
|
||||
}
|
||||
|
||||
size_t PVCopy::getCopyOffset(PVFieldPtr const &masterPVField)
|
||||
{
|
||||
if(masterPVField->getFieldOffset()==0) return 0;
|
||||
if(!headNode->isStructure) {
|
||||
CopyMasterNodePtr masterNode = static_pointer_cast<CopyMasterNode>(headNode);
|
||||
if((masterNode->masterPVField.get())==masterPVField.get()) {
|
||||
return headNode->structureOffset;
|
||||
}
|
||||
PVStructure * parent = masterPVField->getParent();
|
||||
size_t offsetParent = parent->getFieldOffset();
|
||||
size_t off = masterPVField->getFieldOffset();
|
||||
size_t offdiff = off -offsetParent;
|
||||
if(offdiff<masterNode->nfields) return headNode->structureOffset + offdiff;
|
||||
return string::npos;
|
||||
}
|
||||
CopyStructureNodePtr node = static_pointer_cast<CopyStructureNode>(headNode);
|
||||
CopyMasterNodePtr masterNode = getCopyOffset(node,masterPVField);
|
||||
if(masterNode) return masterNode->structureOffset;
|
||||
return string::npos;
|
||||
}
|
||||
|
||||
size_t PVCopy::getCopyOffset(
|
||||
PVStructurePtr const &masterPVStructure,
|
||||
PVFieldPtr const &masterPVField)
|
||||
{
|
||||
CopyMasterNodePtr masterNode;
|
||||
if(!headNode->isStructure) {
|
||||
masterNode = static_pointer_cast<CopyMasterNode>(headNode);
|
||||
if(masterNode->masterPVField.get()!=masterPVStructure.get()) return string::npos;
|
||||
} else {
|
||||
CopyStructureNodePtr node = static_pointer_cast<CopyStructureNode>(headNode);
|
||||
masterNode = getCopyOffset(node,masterPVField);
|
||||
}
|
||||
if(!masterNode) return string::npos;
|
||||
size_t diff = masterPVField->getFieldOffset()
|
||||
- masterPVStructure->getFieldOffset();
|
||||
return masterNode->structureOffset + diff;
|
||||
}
|
||||
|
||||
PVFieldPtr PVCopy::getMasterPVField(size_t structureOffset)
|
||||
{
|
||||
CopyMasterNodePtr masterNode;
|
||||
if(!headNode->isStructure) {
|
||||
masterNode = static_pointer_cast<CopyMasterNode>(headNode);
|
||||
} else {
|
||||
CopyStructureNodePtr node = static_pointer_cast<CopyStructureNode>(headNode);
|
||||
masterNode = getMasterNode(node,structureOffset);
|
||||
}
|
||||
if(!masterNode) {
|
||||
throw std::invalid_argument(
|
||||
"PVCopy::getMasterPVField: setstructureOffset not valid");
|
||||
}
|
||||
size_t diff = structureOffset - masterNode->structureOffset;
|
||||
PVFieldPtr pvMasterField = masterNode->masterPVField;
|
||||
if(diff==0) return pvMasterField;
|
||||
PVStructurePtr pvStructure
|
||||
= static_pointer_cast<PVStructure>(pvMasterField);
|
||||
return pvStructure->getSubField(
|
||||
pvMasterField->getFieldOffset() + diff);
|
||||
}
|
||||
|
||||
void PVCopy::initCopy(
|
||||
PVStructurePtr const ©PVStructure,
|
||||
BitSetPtr const &bitSet)
|
||||
{
|
||||
bitSet->clear();
|
||||
bitSet->set(0);
|
||||
updateCopyFromBitSet(copyPVStructure,bitSet);
|
||||
}
|
||||
|
||||
void PVCopy::updateCopySetBitSet(
|
||||
PVStructurePtr const ©PVStructure,
|
||||
BitSetPtr const &bitSet)
|
||||
{
|
||||
if(headNode->isStructure) {
|
||||
CopyStructureNodePtr node = static_pointer_cast<CopyStructureNode>(headNode);
|
||||
updateStructureNodeSetBitSet(copyPVStructure,node,bitSet);
|
||||
} else {
|
||||
CopyMasterNodePtr masterNode = static_pointer_cast<CopyMasterNode>(headNode);
|
||||
PVFieldPtr pvMasterField= masterNode->masterPVField;
|
||||
PVFieldPtr copyPVField = copyPVStructure;
|
||||
PVFieldPtr pvField = pvMasterField;
|
||||
if(pvField->getField()->getType()==epics::pvData::structure) {
|
||||
updateSubFieldSetBitSet(copyPVField,pvMasterField,bitSet);
|
||||
return;
|
||||
}
|
||||
bool isEqual = (*copyPVField == *pvField);
|
||||
if(!isEqual) {
|
||||
copyPVField->copyUnchecked(*pvField);
|
||||
bitSet->set(copyPVField->getFieldOffset());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PVCopy::updateCopyFromBitSet(
|
||||
PVStructurePtr const ©PVStructure,
|
||||
BitSetPtr const &bitSet)
|
||||
{
|
||||
bool doAll = bitSet->get(0);
|
||||
if(headNode->isStructure) {
|
||||
CopyStructureNodePtr node = static_pointer_cast<CopyStructureNode>(headNode);
|
||||
updateStructureNodeFromBitSet(copyPVStructure,node,bitSet,true,doAll);
|
||||
} else {
|
||||
CopyMasterNodePtr masterNode = static_pointer_cast<CopyMasterNode>(headNode);
|
||||
updateSubFieldFromBitSet(copyPVStructure, masterNode->masterPVField,bitSet, true,doAll);
|
||||
}
|
||||
}
|
||||
|
||||
void PVCopy::updateMaster(
|
||||
PVStructurePtr const ©PVStructure,
|
||||
BitSetPtr const &bitSet)
|
||||
{
|
||||
bool doAll = bitSet->get(0);
|
||||
if(headNode->isStructure) {
|
||||
CopyStructureNodePtr node =
|
||||
static_pointer_cast<CopyStructureNode>(headNode);
|
||||
updateStructureNodeFromBitSet(
|
||||
copyPVStructure,node,bitSet,false,doAll);
|
||||
} else {
|
||||
CopyMasterNodePtr masterNode =
|
||||
static_pointer_cast<CopyMasterNode>(headNode);
|
||||
updateSubFieldFromBitSet( copyPVStructure,masterNode->masterPVField,bitSet,false,doAll);
|
||||
}
|
||||
}
|
||||
|
||||
string PVCopy::dump()
|
||||
{
|
||||
string builder;
|
||||
dump(&builder,headNode,0);
|
||||
return builder;
|
||||
}
|
||||
|
||||
void PVCopy::dump(string *builder,CopyNodePtr const &node,int indentLevel)
|
||||
{
|
||||
newLine(builder,indentLevel);
|
||||
std::stringstream ss;
|
||||
ss << (node->isStructure ? "structureNode" : "masterNode");
|
||||
ss << " structureOffset " << node->structureOffset;
|
||||
ss << " nfields " << node->nfields;
|
||||
*builder += ss.str();
|
||||
PVStructurePtr options = node->options;
|
||||
if(options) {
|
||||
newLine(builder,indentLevel +1);
|
||||
|
||||
// TODO !!! ugly
|
||||
std::ostringstream oss;
|
||||
oss << *options;
|
||||
*builder += oss.str();
|
||||
|
||||
newLine(builder,indentLevel);
|
||||
}
|
||||
if(!node->isStructure) {
|
||||
CopyMasterNodePtr masterNode = static_pointer_cast<CopyMasterNode>(node);
|
||||
string name = masterNode->masterPVField->getFullName();
|
||||
*builder += " masterField " + name;
|
||||
return;
|
||||
}
|
||||
CopyStructureNodePtr structureNode =
|
||||
static_pointer_cast<CopyStructureNode>(node);
|
||||
CopyNodePtrArrayPtr nodes = structureNode->nodes;
|
||||
for(size_t i=0; i<nodes->size(); ++i) {
|
||||
if((*nodes)[i].get()==NULL) {
|
||||
newLine(builder,indentLevel +1);
|
||||
ss.str("");
|
||||
ss << "node[" << i << "] is null";
|
||||
*builder += ss.str();
|
||||
continue;
|
||||
}
|
||||
dump(builder,(*nodes)[i],indentLevel+1);
|
||||
}
|
||||
}
|
||||
|
||||
bool PVCopy::init(epics::pvData::PVStructurePtr const &pvRequest)
|
||||
{
|
||||
PVStructurePtr pvMasterStructure = pvMaster;
|
||||
size_t len = pvRequest->getPVFields().size();
|
||||
bool entireMaster = false;
|
||||
if(len==string::npos) entireMaster = true;
|
||||
if(len==0) entireMaster = true;
|
||||
PVStructurePtr pvOptions;
|
||||
if(len==1) {
|
||||
pvOptions = pvRequest->getSubField<PVStructure>("_options");
|
||||
}
|
||||
if(entireMaster) {
|
||||
structure = pvMasterStructure->getStructure();
|
||||
CopyMasterNodePtr masterNode(new CopyMasterNode());
|
||||
headNode = masterNode;
|
||||
masterNode->options = pvOptions;
|
||||
masterNode->isStructure = false;
|
||||
masterNode->structureOffset = 0;
|
||||
masterNode->masterPVField = pvMasterStructure;
|
||||
masterNode->nfields = pvMasterStructure->getNumberFields();
|
||||
return true;
|
||||
}
|
||||
structure = createStructure(pvMasterStructure,pvRequest);
|
||||
if(!structure) return false;
|
||||
cacheInitStructure = createPVStructure();
|
||||
headNode = createStructureNodes(
|
||||
pvMaster,
|
||||
pvRequest,
|
||||
cacheInitStructure);
|
||||
return true;
|
||||
}
|
||||
|
||||
string PVCopy::dump(
|
||||
string const &value,
|
||||
CopyNodePtr const &node,
|
||||
int indentLevel)
|
||||
{
|
||||
throw std::logic_error(string("Not Implemented"));
|
||||
}
|
||||
|
||||
|
||||
StructureConstPtr PVCopy::createStructure(
|
||||
PVStructurePtr const &pvMaster,
|
||||
PVStructurePtr const &pvFromRequest)
|
||||
{
|
||||
if(pvFromRequest->getStructure()->getNumberFields()==0) {
|
||||
return pvMaster->getStructure();
|
||||
}
|
||||
PVFieldPtrArray const &pvFromRequestFields = pvFromRequest->getPVFields();
|
||||
StringArray const &fromRequestFieldNames = pvFromRequest->getStructure()->getFieldNames();
|
||||
size_t length = pvFromRequestFields.size();
|
||||
if(length==0) return StructureConstPtr();
|
||||
FieldConstPtrArray fields; fields.reserve(length);
|
||||
StringArray fieldNames; fields.reserve(length);
|
||||
for(size_t i=0; i<length; ++i) {
|
||||
string const &fieldName = fromRequestFieldNames[i];
|
||||
PVFieldPtr pvMasterField = pvMaster->getSubField(fieldName);
|
||||
if(!pvMasterField) continue;
|
||||
FieldConstPtr field = pvMasterField->getField();
|
||||
if(field->getType()==epics::pvData::structure) {
|
||||
PVStructurePtr pvRequestStructure = static_pointer_cast<PVStructure>(
|
||||
pvFromRequestFields[i]);
|
||||
if(pvRequestStructure->getNumberFields()>0) {
|
||||
StringArray const &names = pvRequestStructure->getStructure()->
|
||||
getFieldNames();
|
||||
size_t num = names.size();
|
||||
if(num>0 && names[0].compare("_options")==0) --num;
|
||||
if(num>0) {
|
||||
if(pvMasterField->getField()->getType()!=epics::pvData::structure) continue;
|
||||
fieldNames.push_back(fieldName);
|
||||
fields.push_back(createStructure(
|
||||
static_pointer_cast<PVStructure>(pvMasterField),
|
||||
pvRequestStructure));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
fieldNames.push_back(fieldName);
|
||||
fields.push_back(field);
|
||||
}
|
||||
size_t numsubfields = fields.size();
|
||||
if(numsubfields==0) return StructureConstPtr();
|
||||
return getFieldCreate()->createStructure(fieldNames, fields);
|
||||
}
|
||||
|
||||
CopyNodePtr PVCopy::createStructureNodes(
|
||||
PVStructurePtr const &pvMasterStructure,
|
||||
PVStructurePtr const &pvFromRequest,
|
||||
PVStructurePtr const &pvFromCopy)
|
||||
{
|
||||
PVFieldPtrArray const & copyPVFields = pvFromCopy->getPVFields();
|
||||
PVStructurePtr pvOptions;
|
||||
PVFieldPtr pvField = pvFromRequest->getSubField("_options");
|
||||
if(pvField) pvOptions = static_pointer_cast<PVStructure>(pvField);
|
||||
size_t number = copyPVFields.size();
|
||||
CopyNodePtrArrayPtr nodes(new CopyNodePtrArray());
|
||||
nodes->reserve(number);
|
||||
for(size_t i=0; i<number; i++) {
|
||||
PVFieldPtr copyPVField = copyPVFields[i];
|
||||
string fieldName = copyPVField->getFieldName();
|
||||
|
||||
PVStructurePtr requestPVStructure = pvFromRequest->getSubField<PVStructure>(fieldName);
|
||||
PVStructurePtr pvSubFieldOptions = requestPVStructure->getSubField<PVStructure>("_options");
|
||||
PVFieldPtr pvMasterField;
|
||||
PVFieldPtrArray const & pvMasterFields = pvMasterStructure->getPVFields();
|
||||
for(size_t j=0; i<pvMasterFields.size(); j++ ) {
|
||||
if(pvMasterFields[j]->getFieldName().compare(fieldName)==0) {
|
||||
pvMasterField = pvMasterFields[j];
|
||||
break;
|
||||
}
|
||||
}
|
||||
size_t numberRequest = requestPVStructure->getPVFields().size();
|
||||
if(pvSubFieldOptions) numberRequest--;
|
||||
if(numberRequest>0) {
|
||||
nodes->push_back(createStructureNodes(
|
||||
static_pointer_cast<PVStructure>(pvMasterField),
|
||||
requestPVStructure,
|
||||
static_pointer_cast<PVStructure>(copyPVField)));
|
||||
continue;
|
||||
}
|
||||
CopyMasterNodePtr masterNode(new CopyMasterNode());
|
||||
masterNode->options = pvSubFieldOptions;
|
||||
masterNode->isStructure = false;
|
||||
masterNode->masterPVField = pvMasterField;
|
||||
masterNode->nfields = copyPVField->getNumberFields();
|
||||
masterNode->structureOffset = copyPVField->getFieldOffset();
|
||||
nodes->push_back(masterNode);
|
||||
}
|
||||
CopyStructureNodePtr structureNode(new CopyStructureNode());
|
||||
structureNode->isStructure = true;
|
||||
structureNode->nodes = nodes;
|
||||
structureNode->structureOffset = pvFromCopy->getFieldOffset();
|
||||
structureNode->nfields = pvFromCopy->getNumberFields();
|
||||
structureNode->options = pvOptions;
|
||||
return structureNode;
|
||||
}
|
||||
|
||||
void PVCopy::updateStructureNodeSetBitSet(
|
||||
PVStructurePtr const &pvCopy,
|
||||
CopyStructureNodePtr const &structureNode,
|
||||
epics::pvData::BitSetPtr const &bitSet)
|
||||
{
|
||||
for(size_t i=0; i<structureNode->nodes->size(); i++) {
|
||||
CopyNodePtr node = (*structureNode->nodes)[i];
|
||||
PVFieldPtr pvField = pvCopy->getSubField(node->structureOffset);
|
||||
if(node->isStructure) {
|
||||
PVStructurePtr xxx = static_pointer_cast<PVStructure>(pvField);
|
||||
CopyStructureNodePtr yyy =
|
||||
static_pointer_cast<CopyStructureNode>(node);
|
||||
updateStructureNodeSetBitSet(xxx,yyy,bitSet);
|
||||
} else {
|
||||
CopyMasterNodePtr masterNode =
|
||||
static_pointer_cast<CopyMasterNode>(node);
|
||||
updateSubFieldSetBitSet(pvField,masterNode->masterPVField,bitSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PVCopy::updateSubFieldSetBitSet(
|
||||
PVFieldPtr const &pvCopy,
|
||||
PVFieldPtr const &pvMaster,
|
||||
BitSetPtr const &bitSet)
|
||||
{
|
||||
FieldConstPtr field = pvCopy->getField();
|
||||
Type type = field->getType();
|
||||
if(type!=epics::pvData::structure) {
|
||||
bool isEqual = (*pvCopy == *pvMaster);
|
||||
if(isEqual) {
|
||||
if(type==structureArray) {
|
||||
// always act as though a change occurred.
|
||||
// Note that array elements are shared.
|
||||
bitSet->set(pvCopy->getFieldOffset());
|
||||
}
|
||||
}
|
||||
if(isEqual) return;
|
||||
pvCopy->copyUnchecked(*pvMaster);
|
||||
bitSet->set(pvCopy->getFieldOffset());
|
||||
return;
|
||||
}
|
||||
PVStructurePtr pvCopyStructure = static_pointer_cast<PVStructure>(pvCopy);
|
||||
PVFieldPtrArray const & pvCopyFields = pvCopyStructure->getPVFields();
|
||||
PVStructurePtr pvMasterStructure =
|
||||
static_pointer_cast<PVStructure>(pvMaster);
|
||||
PVFieldPtrArray const & pvMasterFields =
|
||||
pvMasterStructure->getPVFields();
|
||||
size_t length = pvCopyFields.size();
|
||||
for(size_t i=0; i<length; i++) {
|
||||
updateSubFieldSetBitSet(pvCopyFields[i],pvMasterFields[i],bitSet);
|
||||
}
|
||||
}
|
||||
|
||||
void PVCopy::updateStructureNodeFromBitSet(
|
||||
PVStructurePtr const &pvCopy,
|
||||
CopyStructureNodePtr const &structureNode,
|
||||
BitSetPtr const &bitSet,
|
||||
bool toCopy,
|
||||
bool doAll)
|
||||
{
|
||||
size_t offset = structureNode->structureOffset;
|
||||
if(!doAll) {
|
||||
size_t nextSet = bitSet->nextSetBit(offset);
|
||||
if(nextSet==string::npos) return;
|
||||
}
|
||||
if(offset>=pvCopy->getNextFieldOffset()) return;
|
||||
if(!doAll) doAll = bitSet->get(offset);
|
||||
CopyNodePtrArrayPtr nodes = structureNode->nodes;
|
||||
for(size_t i=0; i<nodes->size(); i++) {
|
||||
CopyNodePtr node = (*nodes)[i];
|
||||
PVFieldPtr pvField = pvCopy->getSubFieldT(node->structureOffset);
|
||||
if(node->isStructure) {
|
||||
PVStructurePtr xxx = static_pointer_cast<PVStructure>(pvField);
|
||||
CopyStructureNodePtr subStructureNode =
|
||||
static_pointer_cast<CopyStructureNode>(node);
|
||||
updateStructureNodeFromBitSet(
|
||||
xxx,subStructureNode,bitSet,toCopy,doAll);
|
||||
} else {
|
||||
CopyMasterNodePtr masterNode =
|
||||
static_pointer_cast<CopyMasterNode>(node);
|
||||
updateSubFieldFromBitSet(
|
||||
pvField,masterNode->masterPVField,bitSet,toCopy,doAll);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PVCopy::updateSubFieldFromBitSet(
|
||||
PVFieldPtr const &pvCopy,
|
||||
PVFieldPtr const &pvMasterField,
|
||||
BitSetPtr const &bitSet,
|
||||
bool toCopy,
|
||||
bool doAll)
|
||||
{
|
||||
if(!doAll) {
|
||||
doAll = bitSet->get(pvCopy->getFieldOffset());
|
||||
}
|
||||
if(!doAll) {
|
||||
size_t offset = pvCopy->getFieldOffset();
|
||||
size_t nextSet = bitSet->nextSetBit(offset);
|
||||
if(nextSet==string::npos) return;
|
||||
if(nextSet>=pvCopy->getNextFieldOffset()) return;
|
||||
}
|
||||
if(pvCopy->getField()->getType()==epics::pvData::structure) {
|
||||
PVStructurePtr pvCopyStructure =
|
||||
static_pointer_cast<PVStructure>(pvCopy);
|
||||
PVFieldPtrArray const & pvCopyFields = pvCopyStructure->getPVFields();
|
||||
if(pvMasterField->getField()->getType() !=epics::pvData::structure)
|
||||
{
|
||||
throw std::logic_error(string("Logic error"));
|
||||
}
|
||||
PVStructurePtr pvMasterStructure =
|
||||
static_pointer_cast<PVStructure>(pvMasterField);
|
||||
PVFieldPtrArray const & pvMasterFields =
|
||||
pvMasterStructure->getPVFields();
|
||||
for(size_t i=0; i<pvCopyFields.size(); i++) {
|
||||
updateSubFieldFromBitSet(
|
||||
pvCopyFields[i],
|
||||
pvMasterFields[i],
|
||||
bitSet,toCopy,doAll);
|
||||
}
|
||||
} else {
|
||||
if(toCopy) {
|
||||
pvCopy->copyUnchecked(*pvMasterField);
|
||||
} else {
|
||||
pvMasterField->copyUnchecked(*pvCopy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CopyMasterNodePtr PVCopy::getCopyOffset(
|
||||
CopyStructureNodePtr const &structureNode,
|
||||
PVFieldPtr const &masterPVField)
|
||||
{
|
||||
size_t offset = masterPVField->getFieldOffset();
|
||||
CopyNodePtrArrayPtr nodes = structureNode->nodes;
|
||||
for(size_t i=0; i< nodes->size(); i++) {
|
||||
CopyNodePtr node = (*nodes)[i];
|
||||
if(!node->isStructure) {
|
||||
CopyMasterNodePtr masterNode =
|
||||
static_pointer_cast<CopyMasterNode>(node);
|
||||
size_t off = masterNode->masterPVField->getFieldOffset();
|
||||
size_t nextOffset = masterNode->masterPVField->getNextFieldOffset();
|
||||
if(offset>= off && offset<nextOffset) return masterNode;
|
||||
} else {
|
||||
CopyStructureNodePtr subNode =
|
||||
static_pointer_cast<CopyStructureNode>(node);
|
||||
CopyMasterNodePtr masterNode =
|
||||
getCopyOffset(subNode,masterPVField);
|
||||
if(masterNode) return masterNode;
|
||||
}
|
||||
}
|
||||
return CopyMasterNodePtr();
|
||||
}
|
||||
|
||||
CopyMasterNodePtr PVCopy::getMasterNode(
|
||||
CopyStructureNodePtr const &structureNode,
|
||||
std::size_t structureOffset)
|
||||
{
|
||||
CopyNodePtrArrayPtr nodes = structureNode->nodes;
|
||||
for(size_t i=0; i< nodes->size(); i++) {
|
||||
CopyNodePtr node = (*nodes)[i];
|
||||
if(structureOffset>=(node->structureOffset + node->nfields)) continue;
|
||||
if(!node->isStructure) {
|
||||
CopyMasterNodePtr masterNode =
|
||||
static_pointer_cast<CopyMasterNode>(node);
|
||||
return masterNode;
|
||||
}
|
||||
CopyStructureNodePtr subNode =
|
||||
static_pointer_cast<CopyStructureNode>(node);
|
||||
return getMasterNode(subNode,structureOffset);
|
||||
}
|
||||
return CopyMasterNodePtr();
|
||||
}
|
||||
|
||||
}}
|
||||
328
src/copy/requestmapper.cpp
Normal file
328
src/copy/requestmapper.cpp
Normal file
@@ -0,0 +1,328 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include <epicsAssert.h>
|
||||
#include <epicsTypes.h>
|
||||
#include <epicsVersion.h>
|
||||
#include <epicsConvert.h>
|
||||
#include <epicsAssert.h>
|
||||
#include <epicsMutex.h>
|
||||
#include <epicsGuard.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/createRequest.h>
|
||||
#include <pv/epicsException.h>
|
||||
#include <pv/bitSet.h>
|
||||
|
||||
// Our arbitrary limit on pvRequest structure depth to bound stack usage during recursion
|
||||
static const unsigned maxDepth = 5;
|
||||
|
||||
namespace epics{namespace pvData {
|
||||
|
||||
PVRequestMapper::PVRequestMapper() {}
|
||||
|
||||
PVRequestMapper::PVRequestMapper(const PVStructure &base,
|
||||
const PVStructure &pvRequest,
|
||||
mode_t mode)
|
||||
{
|
||||
compute(base, pvRequest, mode);
|
||||
}
|
||||
|
||||
PVStructurePtr PVRequestMapper::buildRequested() const
|
||||
{
|
||||
if(!typeRequested)
|
||||
THROW_EXCEPTION2(std::logic_error, "No mapping compute()d");
|
||||
return typeRequested->build();
|
||||
}
|
||||
|
||||
PVStructurePtr PVRequestMapper::buildBase() const
|
||||
{
|
||||
if(!typeBase)
|
||||
THROW_EXCEPTION2(std::logic_error, "No mapping compute()d");
|
||||
return typeBase->build();
|
||||
}
|
||||
|
||||
void PVRequestMapper::compute(const PVStructure &base,
|
||||
const PVStructure &pvRequest,
|
||||
mode_t mode)
|
||||
{
|
||||
if(base.getFieldOffset()!=0)
|
||||
THROW_EXCEPTION2(std::logic_error, "Mapper must be used with top level PVStructure");
|
||||
|
||||
bool ok = true;
|
||||
|
||||
// we want to be transactional, which requires a second copy of everything.
|
||||
PVRequestMapper temp;
|
||||
|
||||
// whether to preserve IDs of partial structures.
|
||||
bool keepids = false;
|
||||
PVScalar::const_shared_pointer pbp(pvRequest.getSubField<PVScalar>("record._options.keepIDs"));
|
||||
try {
|
||||
if(pbp) keepids = pbp->getAs<boolean>();
|
||||
}catch(std::runtime_error& e){
|
||||
std::ostringstream msg;
|
||||
msg<<"Can't parse keepIDs : '"<<e.what()<<"' ";
|
||||
temp.messages+=msg.str();
|
||||
}
|
||||
|
||||
PVStructure::const_shared_pointer fields(pvRequest.getSubField<PVStructure>("field"));
|
||||
if(!fields || fields->getPVFields().empty()) {
|
||||
// not selection, or empty selection, treated as select all
|
||||
temp.typeBase = temp.typeRequested = base.getStructure();
|
||||
|
||||
for(size_t i=1, N=base.getNextFieldOffset(); i<N; i++)
|
||||
temp.maskRequested.set(i);
|
||||
|
||||
} else {
|
||||
FieldBuilderPtr builder(getFieldCreate()->createFieldBuilder());
|
||||
|
||||
if(keepids)
|
||||
builder = builder->setId(base.getStructure()->getID());
|
||||
|
||||
ok &= temp._compute(base, *fields, builder, keepids, 0); // fills in builder
|
||||
|
||||
temp.typeBase = base.getStructure();
|
||||
temp.typeRequested = builder->createStructure();
|
||||
// possible that typeBase==typeRequested if all fields explicitly selected
|
||||
}
|
||||
|
||||
if(mode==Mask) {
|
||||
// short circuit use of masked Structure, but keep maskRequested
|
||||
temp.typeRequested = temp.typeBase;
|
||||
}
|
||||
|
||||
{
|
||||
PVStructurePtr proto(temp.typeRequested->build());
|
||||
|
||||
// base -> request may be sparce mapping
|
||||
temp.base2req.resize(base.getNextFieldOffset());
|
||||
// request -> base is dense mapping
|
||||
temp.req2base.resize(proto->getNextFieldOffset());
|
||||
|
||||
// special handling for whole structure mapping. in part because getSubField(0) isn't allowed
|
||||
temp.base2req[0] = Mapping(0, false);
|
||||
temp.req2base[0] = Mapping(0, false);
|
||||
|
||||
// Iterate prototype of requested to map with base field offsets.
|
||||
// which is handled as a special case below.
|
||||
// We also don't try to prevent redundant copies if both leaf and compress bits are set.
|
||||
for(size_t r=1, N=proto->getNextFieldOffset(); r<N; r++) {
|
||||
PVField::const_shared_pointer fld_req(proto->getSubFieldT(r)),
|
||||
fld_base(base.getSubFieldT(fld_req->getFullName()));
|
||||
const size_t b = fld_base->getFieldOffset();
|
||||
|
||||
if(!temp.requestedMask().get(b))
|
||||
continue;
|
||||
|
||||
bool leaf = fld_base->getField()->getType()!=structure;
|
||||
|
||||
// initialize mapping when our bit is set
|
||||
temp.base2req[b] = Mapping(r, leaf);
|
||||
temp.req2base[r] = Mapping(b, leaf);
|
||||
|
||||
// add ourself to all "compress" bit mappings of enclosing structures
|
||||
for(const PVStructure *parent = fld_req->getParent(); parent; parent = parent->getParent()) {
|
||||
temp.req2base[parent->getFieldOffset()].tomask .set(b);
|
||||
temp.req2base[parent->getFieldOffset()].frommask.set(r);
|
||||
}
|
||||
|
||||
for(const PVStructure *parent = fld_base->getParent(); parent; parent = parent->getParent()) {
|
||||
temp.base2req[parent->getFieldOffset()].tomask .set(r);
|
||||
temp.base2req[parent->getFieldOffset()].frommask.set(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
temp.maskRequested.set(0);
|
||||
|
||||
if(temp.maskRequested.nextSetBit(1)==-1) {
|
||||
ok = false;
|
||||
temp.messages+="Empty field selection";
|
||||
}
|
||||
|
||||
if(!ok)
|
||||
throw std::runtime_error(temp.messages);
|
||||
|
||||
swap(temp);
|
||||
}
|
||||
|
||||
bool PVRequestMapper::_compute(const PVStructure& base, const PVStructure& pvReq,
|
||||
FieldBuilderPtr& builder, bool keepids, unsigned depth)
|
||||
{
|
||||
bool ok = true;
|
||||
const StringArray& reqNames = pvReq.getStructure()->getFieldNames();
|
||||
|
||||
for(size_t i=0, N=reqNames.size(); i<N; i++) {
|
||||
// iterate through requested fields
|
||||
|
||||
PVField::const_shared_pointer subtype(base.getSubField(reqNames[i]));
|
||||
const FieldConstPtr& subReq = pvReq.getStructure()->getFields()[i];
|
||||
|
||||
if(subReq->getType()!=structure) {
|
||||
// pvRequest .field was not properly composed
|
||||
std::ostringstream msg;
|
||||
// not a great warning message as it doesn't distinguish 'a.value' from 'b.value',
|
||||
// but getFullName() whould prefix with 'field.', which would probably cause
|
||||
// more frequent confusion...
|
||||
msg<<"request invalid '"<<pvReq.getStructure()->getFieldNames()[i]<<"' ";
|
||||
messages+=msg.str();
|
||||
ok = false;
|
||||
|
||||
} else if(!subtype) {
|
||||
// requested field does not actually exist in base
|
||||
std::ostringstream msg;
|
||||
msg<<"No field '"<<pvReq.getStructure()->getFieldNames()[i]<<"' ";
|
||||
messages+=msg.str();
|
||||
|
||||
} else if(depth>=maxDepth // exceeds max recursion depth
|
||||
|| subtype->getField()->getType()!=structure // requested field is a leaf
|
||||
|| static_cast<const Structure&>(*subReq).getFieldNames().empty() // requests all sub-fields
|
||||
)
|
||||
{
|
||||
// just add the whole thing
|
||||
builder = builder->add(reqNames[i], subtype->getField());
|
||||
for(size_t j=subtype->getFieldOffset(), N=subtype->getNextFieldOffset(); j<N; j++)
|
||||
maskRequested.set(j);
|
||||
|
||||
if(subtype->getField()->getType()!=structure
|
||||
&& !static_cast<const Structure&>(*subReq).getFieldNames().empty())
|
||||
{
|
||||
// attempt to select below a leaf field
|
||||
std::ostringstream msg;
|
||||
msg<<"Leaf field '"<<pvReq.getFullName()<<"' ";
|
||||
messages+=msg.str();
|
||||
} else if(depth>=maxDepth) {
|
||||
std::ostringstream msg;
|
||||
msg<<"selection truncated at '"<<pvReq.getFullName()<<"' ";
|
||||
messages+=msg.str();
|
||||
}
|
||||
|
||||
} else {
|
||||
// recurse into sub-structure
|
||||
const PVStructure& substruct = static_cast<const PVStructure&>(*subtype);
|
||||
|
||||
builder = builder->addNestedStructure(reqNames[i]);
|
||||
maskRequested.set(substruct.getFieldOffset());
|
||||
|
||||
if(keepids)
|
||||
builder = builder->setId(substruct.getStructure()->getID());
|
||||
|
||||
_compute(substruct,
|
||||
static_cast<const PVStructure&>(*pvReq.getPVFields()[i]),
|
||||
builder, keepids, depth+1u);
|
||||
|
||||
builder = builder->endNested();
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
void PVRequestMapper::copyBaseToRequested(
|
||||
const PVStructure& base,
|
||||
const BitSet& baseMask,
|
||||
PVStructure& request,
|
||||
BitSet& requestMask
|
||||
) const {
|
||||
assert(base.getStructure()==typeBase);
|
||||
assert(request.getStructure()==typeRequested);
|
||||
_map(base, baseMask, request, requestMask, false);
|
||||
}
|
||||
|
||||
void PVRequestMapper::copyBaseFromRequested(
|
||||
PVStructure& base,
|
||||
BitSet& baseMask,
|
||||
const PVStructure& request,
|
||||
const BitSet& requestMask
|
||||
) const {
|
||||
assert(base.getStructure()==typeBase);
|
||||
assert(request.getStructure()==typeRequested);
|
||||
_map(request, requestMask, base, baseMask, true);
|
||||
}
|
||||
|
||||
void PVRequestMapper::_map(const PVStructure& src, const BitSet& maskSrc,
|
||||
PVStructure& dest, BitSet& maskDest,
|
||||
bool dir_r2b) const
|
||||
{
|
||||
{
|
||||
scratch = maskSrc;
|
||||
const mapping_t& map = dir_r2b ? req2base : base2req;
|
||||
|
||||
assert(map.size()==src.getNumberFields());
|
||||
|
||||
for(int32 i=scratch.nextSetBit(0), N=map.size(); i>=0 && i<N; i=scratch.nextSetBit(i+1)) {
|
||||
const Mapping& M = map[i];
|
||||
if(!M.valid) {
|
||||
assert(!dir_r2b); // only base -> requested mapping can have holes
|
||||
|
||||
} else if(M.leaf) {
|
||||
// just copy
|
||||
dest.getSubFieldT(M.to)->copy(*src.getSubFieldT(i));
|
||||
maskDest.set(M.to);
|
||||
|
||||
} else {
|
||||
// set bits of all sub-fields (in requested structure)
|
||||
// these indicies are always >i
|
||||
scratch |= M.frommask;
|
||||
|
||||
// we will also set the individual bits, but if a compress bit is set in the input,
|
||||
// then set the corresponding bit in the output.
|
||||
maskDest.set(M.to);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PVRequestMapper::_mapMask(const BitSet& maskSrc,
|
||||
BitSet& maskDest,
|
||||
bool dir_r2b) const
|
||||
{
|
||||
if(maskSrc.isEmpty()) {
|
||||
// no-op
|
||||
|
||||
} else {
|
||||
const mapping_t& map = dir_r2b ? req2base : base2req;
|
||||
|
||||
for(int32 i=maskSrc.nextSetBit(0), N=map.size(); i>=0 && i<N; i=maskSrc.nextSetBit(i+1)) {
|
||||
const Mapping& M = map[i];
|
||||
if(!M.valid) {
|
||||
assert(!dir_r2b); // only base -> requested mapping can have holes
|
||||
|
||||
} else {
|
||||
maskDest.set(M.to);
|
||||
|
||||
if(!M.leaf) {
|
||||
maskDest |= M.tomask;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void PVRequestMapper::swap(PVRequestMapper& other)
|
||||
{
|
||||
typeBase.swap(other.typeBase);
|
||||
typeRequested.swap(other.typeRequested);
|
||||
maskRequested.swap(other.maskRequested);
|
||||
base2req.swap(other.base2req);
|
||||
req2base.swap(other.req2base);
|
||||
messages.swap(other.messages);
|
||||
scratch.swap(other.scratch); // paranoia
|
||||
}
|
||||
|
||||
void PVRequestMapper::reset()
|
||||
{
|
||||
typeBase.reset();
|
||||
typeRequested.reset();
|
||||
maskRequested.clear();
|
||||
base2req.clear();
|
||||
req2base.clear();
|
||||
messages.clear();
|
||||
scratch.clear(); // paranoia
|
||||
}
|
||||
|
||||
}} //namespace epics::pvData
|
||||
@@ -23,7 +23,7 @@ namespace epics { namespace pvData {
|
||||
* 1) same instance
|
||||
* 2) same type (field and scalar/element), same name, same subfields (if any)
|
||||
*/
|
||||
bool operator==(const Field& a, const Field& b)
|
||||
bool compare(const Field& a, const Field& b)
|
||||
{
|
||||
if(&a==&b)
|
||||
return true;
|
||||
@@ -33,58 +33,58 @@ bool operator==(const Field& a, const Field& b)
|
||||
case scalar: {
|
||||
const Scalar &A=static_cast<const Scalar&>(a);
|
||||
const Scalar &B=static_cast<const Scalar&>(b);
|
||||
return A==B;
|
||||
return compare(A, B);
|
||||
}
|
||||
case scalarArray: {
|
||||
const ScalarArray &A=static_cast<const ScalarArray&>(a);
|
||||
const ScalarArray &B=static_cast<const ScalarArray&>(b);
|
||||
return A==B;
|
||||
return compare(A, B);
|
||||
}
|
||||
case structure: {
|
||||
const Structure &A=static_cast<const Structure&>(a);
|
||||
const Structure &B=static_cast<const Structure&>(b);
|
||||
return A==B;
|
||||
return compare(A, B);
|
||||
}
|
||||
case structureArray: {
|
||||
const StructureArray &A=static_cast<const StructureArray&>(a);
|
||||
const StructureArray &B=static_cast<const StructureArray&>(b);
|
||||
return A==B;
|
||||
return compare(A, B);
|
||||
}
|
||||
case union_: {
|
||||
const Union &A=static_cast<const Union&>(a);
|
||||
const Union &B=static_cast<const Union&>(b);
|
||||
return A==B;
|
||||
return compare(A, B);
|
||||
}
|
||||
case unionArray: {
|
||||
const UnionArray &A=static_cast<const UnionArray&>(a);
|
||||
const UnionArray &B=static_cast<const UnionArray&>(b);
|
||||
return A==B;
|
||||
return compare(A, B);
|
||||
}
|
||||
default:
|
||||
throw std::logic_error("Invalid Field type in comparison");
|
||||
}
|
||||
}
|
||||
|
||||
bool operator==(const Scalar& a, const Scalar& b)
|
||||
bool compare(const Scalar& a, const Scalar& b)
|
||||
{
|
||||
if(&a==&b)
|
||||
return true;
|
||||
return a.getScalarType()==b.getScalarType();
|
||||
}
|
||||
|
||||
bool operator==(const ScalarArray& a, const ScalarArray& b)
|
||||
bool compare(const ScalarArray& a, const ScalarArray& b)
|
||||
{
|
||||
if(&a==&b)
|
||||
return true;
|
||||
return a.getElementType()==b.getElementType();
|
||||
}
|
||||
|
||||
bool operator==(const Structure& a, const Structure& b)
|
||||
bool compare(const Structure& a, const Structure& b)
|
||||
{
|
||||
if(&a==&b)
|
||||
return true;
|
||||
if (a.getID()!=b.getID())
|
||||
return false;
|
||||
return false;
|
||||
size_t nflds=a.getNumberFields();
|
||||
if (b.getNumberFields()!=nflds)
|
||||
return false;
|
||||
@@ -101,17 +101,17 @@ bool operator==(const Structure& a, const Structure& b)
|
||||
return std::equal( an.begin(), an.end(), bn.begin() );
|
||||
}
|
||||
|
||||
bool operator==(const StructureArray& a, const StructureArray& b)
|
||||
bool compare(const StructureArray& a, const StructureArray& b)
|
||||
{
|
||||
return *(a.getStructure().get())==*(b.getStructure().get());
|
||||
}
|
||||
|
||||
bool operator==(const Union& a, const Union& b)
|
||||
bool compare(const Union& a, const Union& b)
|
||||
{
|
||||
if(&a==&b)
|
||||
return true;
|
||||
if (a.getID()!=b.getID())
|
||||
return false;
|
||||
return false;
|
||||
size_t nflds=a.getNumberFields();
|
||||
if (b.getNumberFields()!=nflds)
|
||||
return false;
|
||||
@@ -128,12 +128,12 @@ bool operator==(const Union& a, const Union& b)
|
||||
return std::equal( an.begin(), an.end(), bn.begin() );
|
||||
}
|
||||
|
||||
bool operator==(const UnionArray& a, const UnionArray& b)
|
||||
bool compare(const UnionArray& a, const UnionArray& b)
|
||||
{
|
||||
return *(a.getUnion().get())==*(b.getUnion().get());
|
||||
}
|
||||
|
||||
bool operator==(const BoundedString& a, const BoundedString& b)
|
||||
bool compare(const BoundedString& a, const BoundedString& b)
|
||||
{
|
||||
if(&a==&b)
|
||||
return true;
|
||||
@@ -268,7 +268,7 @@ bool compareField(const PVUnion* left, const PVUnion* right)
|
||||
|
||||
if(*ls!=*right->getUnion())
|
||||
return false;
|
||||
|
||||
|
||||
if (ls->isVariant())
|
||||
{
|
||||
const PVField::const_shared_pointer& lval = left->get();
|
||||
|
||||
@@ -24,10 +24,10 @@ using std::tr1::static_pointer_cast;
|
||||
using std::size_t;
|
||||
using std::string;
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
|
||||
static std::vector<string> split(string commaSeparatedList) {
|
||||
static std::vector<string> split(const string& commaSeparatedList) {
|
||||
string::size_type numValues = 1;
|
||||
string::size_type index=0;
|
||||
while(true) {
|
||||
@@ -60,7 +60,7 @@ void Convert::getString(string *buf,PVField const *pvField,int /*indentLevel*/)
|
||||
size_t Convert::fromString(PVStructurePtr const &pvStructure, StringArray const & from, size_t fromStartIndex)
|
||||
{
|
||||
size_t processed = 0;
|
||||
|
||||
|
||||
PVFieldPtrArray const & fieldsData = pvStructure->getPVFields();
|
||||
if (fieldsData.size() != 0) {
|
||||
size_t length = pvStructure->getStructure()->getNumberFields();
|
||||
@@ -93,7 +93,7 @@ size_t Convert::fromString(PVStructurePtr const &pvStructure, StringArray const
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return processed;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,10 +7,6 @@
|
||||
* @author mrk
|
||||
*/
|
||||
|
||||
#if defined(_WIN32) && !defined(NOMINMAX)
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
@@ -18,6 +14,8 @@
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
|
||||
#include <epicsString.h>
|
||||
#include <epicsStdio.h>
|
||||
#include <epicsMutex.h>
|
||||
#include <epicsThread.h>
|
||||
|
||||
@@ -27,6 +25,8 @@
|
||||
#include <pv/pvIntrospect.h>
|
||||
#include <pv/factory.h>
|
||||
#include <pv/serializeHelper.h>
|
||||
#include <pv/thread.h>
|
||||
#include <pv/pvData.h>
|
||||
|
||||
using std::tr1::static_pointer_cast;
|
||||
using std::size_t;
|
||||
@@ -34,13 +34,54 @@ using std::string;
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
static DebugLevel debugLevel = lowDebug;
|
||||
|
||||
size_t Field::num_instances;
|
||||
|
||||
|
||||
struct Field::Helper {
|
||||
static unsigned hash(Field *fld) {
|
||||
std::ostringstream key;
|
||||
// hash the output of operator<<()
|
||||
// not efficient, but stable within this process.
|
||||
key<<(*fld);
|
||||
unsigned H = epicsStrHash(key.str().c_str(), 0xbadc0de1);
|
||||
fld->m_hash = H;
|
||||
return H;
|
||||
}
|
||||
};
|
||||
|
||||
struct FieldCreate::Helper {
|
||||
template<typename FLD>
|
||||
static void cache(const FieldCreate *create, std::tr1::shared_ptr<FLD>& ent) {
|
||||
unsigned hash = Field::Helper::hash(ent.get());
|
||||
|
||||
Lock G(create->mutex);
|
||||
// we examine raw pointers stored in create->cache, which is safe under create->mutex
|
||||
|
||||
std::pair<cache_t::iterator, cache_t::iterator> itp(create->cache.equal_range(hash));
|
||||
for(; itp.first!=itp.second; ++itp.first) {
|
||||
Field* cent(itp.first->second);
|
||||
FLD* centx(dynamic_cast<FLD*>(cent));
|
||||
if(centx && compare(*centx, *ent)) {
|
||||
try{
|
||||
ent = std::tr1::static_pointer_cast<FLD>(cent->shared_from_this());
|
||||
return;
|
||||
}catch(std::tr1::bad_weak_ptr&){
|
||||
// we're racing destruction.
|
||||
// add a new entry.
|
||||
// Field::~Field is in the process of removing this old one.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
create->cache.insert(std::make_pair(hash, ent.get()));
|
||||
// cache cleaned from Field::~Field
|
||||
}
|
||||
};
|
||||
|
||||
Field::Field(Type type)
|
||||
: m_fieldType(type)
|
||||
, m_hash(0)
|
||||
{
|
||||
REFTRACE_INCREMENT(num_instances);
|
||||
}
|
||||
@@ -49,10 +90,31 @@ Field::~Field() {
|
||||
REFTRACE_DECREMENT(num_instances);
|
||||
}
|
||||
|
||||
void Field::cacheCleanup()
|
||||
{
|
||||
const FieldCreatePtr& create(getFieldCreate());
|
||||
|
||||
Lock G(create->mutex);
|
||||
|
||||
std::pair<FieldCreate::cache_t::iterator, FieldCreate::cache_t::iterator> itp(create->cache.equal_range(m_hash));
|
||||
for(; itp.first!=itp.second; ++itp.first) {
|
||||
Field* cent(itp.first->second);
|
||||
if(cent==this) {
|
||||
create->cache.erase(itp.first);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::tr1::shared_ptr<PVField> Field::build() const
|
||||
{
|
||||
FieldConstPtr self(shared_from_this());
|
||||
return getPVDataCreate()->createPVField(self);
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& o, const Field& f)
|
||||
{
|
||||
return f.dump(o);
|
||||
return f.dump(o);
|
||||
};
|
||||
|
||||
Scalar::Scalar(ScalarType scalarType)
|
||||
@@ -62,7 +124,10 @@ Scalar::Scalar(ScalarType scalarType)
|
||||
THROW_EXCEPTION2(std::invalid_argument, "Can't construct Scalar from invalid ScalarType");
|
||||
}
|
||||
|
||||
Scalar::~Scalar(){}
|
||||
Scalar::~Scalar()
|
||||
{
|
||||
cacheCleanup();
|
||||
}
|
||||
|
||||
std::ostream& Scalar::dump(std::ostream& o) const
|
||||
{
|
||||
@@ -72,18 +137,18 @@ std::ostream& Scalar::dump(std::ostream& o) const
|
||||
string Scalar::getID() const
|
||||
{
|
||||
static const string idScalarLUT[] = {
|
||||
"boolean", // pvBoolean
|
||||
"byte", // pvByte
|
||||
"short", // pvShort
|
||||
"int", // pvInt
|
||||
"long", // pvLong
|
||||
"ubyte", // pvUByte
|
||||
"ushort", // pvUShort
|
||||
"uint", // pvUInt
|
||||
"ulong", // pvULong
|
||||
"float", // pvFloat
|
||||
"double", // pvDouble
|
||||
"string" // pvString
|
||||
"boolean", // pvBoolean
|
||||
"byte", // pvByte
|
||||
"short", // pvShort
|
||||
"int", // pvInt
|
||||
"long", // pvLong
|
||||
"ubyte", // pvUByte
|
||||
"ushort", // pvUShort
|
||||
"uint", // pvUInt
|
||||
"ulong", // pvULong
|
||||
"float", // pvFloat
|
||||
"double", // pvDouble
|
||||
"string" // pvString
|
||||
};
|
||||
return idScalarLUT[scalarType];
|
||||
}
|
||||
@@ -119,6 +184,11 @@ void Scalar::deserialize(ByteBuffer* /*buffer*/, DeserializableControl* /*contro
|
||||
}
|
||||
|
||||
|
||||
std::tr1::shared_ptr<PVScalar> Scalar::build() const
|
||||
{
|
||||
return getPVDataCreate()->createPVScalar(std::tr1::static_pointer_cast<const Scalar>(shared_from_this()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::string BoundedString::getID() const
|
||||
@@ -147,20 +217,21 @@ BoundedString::BoundedString(std::size_t maxStringLength) :
|
||||
THROW_EXCEPTION2(std::invalid_argument, "maxLength == 0");
|
||||
}
|
||||
|
||||
BoundedString::~BoundedString() {}
|
||||
BoundedString::~BoundedString()
|
||||
{
|
||||
cacheCleanup();
|
||||
}
|
||||
|
||||
|
||||
static string emptyStringtring;
|
||||
|
||||
static void serializeStructureField(const Structure* structure, ByteBuffer* buffer, SerializableControl* control)
|
||||
{
|
||||
// to optimize default (non-empty) IDs optimization
|
||||
// empty IDs are not allowed
|
||||
string id = structure->getID();
|
||||
if (id == Structure::DEFAULT_ID) // TODO slow comparison
|
||||
SerializeHelper::serializeString(emptyStringtring, buffer, control);
|
||||
else
|
||||
SerializeHelper::serializeString(id, buffer, control);
|
||||
// to optimize default (non-empty) IDs optimization
|
||||
// empty IDs are not allowed
|
||||
string id = structure->getID();
|
||||
if (id == Structure::DEFAULT_ID) // TODO slow comparison
|
||||
SerializeHelper::serializeString(string(), buffer, control);
|
||||
else
|
||||
SerializeHelper::serializeString(id, buffer, control);
|
||||
|
||||
FieldConstPtrArray const & fields = structure->getFields();
|
||||
StringArray const & fieldNames = structure->getFieldNames();
|
||||
@@ -188,18 +259,18 @@ static StructureConstPtr deserializeStructureField(const FieldCreate* fieldCreat
|
||||
if (id.empty())
|
||||
return fieldCreate->createStructure(fieldNames, fields);
|
||||
else
|
||||
return fieldCreate->createStructure(id, fieldNames, fields);
|
||||
return fieldCreate->createStructure(id, fieldNames, fields);
|
||||
}
|
||||
|
||||
static void serializeUnionField(const Union* punion, ByteBuffer* buffer, SerializableControl* control)
|
||||
{
|
||||
// to optimize default (non-empty) IDs optimization
|
||||
// empty IDs are not allowed
|
||||
string id = punion->getID();
|
||||
if (id == Union::DEFAULT_ID) // TODO slow comparison
|
||||
SerializeHelper::serializeString(emptyStringtring, buffer, control);
|
||||
else
|
||||
SerializeHelper::serializeString(id, buffer, control);
|
||||
// to optimize default (non-empty) IDs optimization
|
||||
// empty IDs are not allowed
|
||||
string id = punion->getID();
|
||||
if (id == Union::DEFAULT_ID) // TODO slow comparison
|
||||
SerializeHelper::serializeString(string(), buffer, control);
|
||||
else
|
||||
SerializeHelper::serializeString(id, buffer, control);
|
||||
|
||||
FieldConstPtrArray const & fields = punion->getFields();
|
||||
StringArray const & fieldNames = punion->getFieldNames();
|
||||
@@ -227,7 +298,7 @@ static UnionConstPtr deserializeUnionField(const FieldCreate* fieldCreate, ByteB
|
||||
if (id.empty())
|
||||
return fieldCreate->createUnion(fieldNames, fields);
|
||||
else
|
||||
return fieldCreate->createUnion(id, fieldNames, fields);
|
||||
return fieldCreate->createUnion(id, fieldNames, fields);
|
||||
}
|
||||
|
||||
Array::Array(Type type)
|
||||
@@ -245,23 +316,26 @@ ScalarArray::ScalarArray(ScalarType elementType)
|
||||
throw std::invalid_argument("Can't construct ScalarArray from invalid ScalarType");
|
||||
}
|
||||
|
||||
ScalarArray::~ScalarArray() {}
|
||||
ScalarArray::~ScalarArray()
|
||||
{
|
||||
cacheCleanup();
|
||||
}
|
||||
|
||||
const string ScalarArray::getIDScalarArrayLUT() const
|
||||
{
|
||||
static const string idScalarArrayLUT[] = {
|
||||
"boolean[]", // pvBoolean
|
||||
"byte[]", // pvByte
|
||||
"short[]", // pvShort
|
||||
"int[]", // pvInt
|
||||
"long[]", // pvLong
|
||||
"ubyte[]", // pvUByte
|
||||
"ushort[]", // pvUShort
|
||||
"uint[]", // pvUInt
|
||||
"ulong[]", // pvULong
|
||||
"float[]", // pvFloat
|
||||
"double[]", // pvDouble
|
||||
"string[]" // pvString
|
||||
"boolean[]", // pvBoolean
|
||||
"byte[]", // pvByte
|
||||
"short[]", // pvShort
|
||||
"int[]", // pvInt
|
||||
"long[]", // pvLong
|
||||
"ubyte[]", // pvUByte
|
||||
"ushort[]", // pvUShort
|
||||
"uint[]", // pvUInt
|
||||
"ulong[]", // pvULong
|
||||
"float[]", // pvFloat
|
||||
"double[]", // pvDouble
|
||||
"string[]" // pvString
|
||||
};
|
||||
return idScalarArrayLUT[elementType];
|
||||
}
|
||||
@@ -285,8 +359,16 @@ void ScalarArray::deserialize(ByteBuffer* /*buffer*/, DeserializableControl* /*c
|
||||
throw std::runtime_error("not valid operation, use FieldCreate::deserialize instead");
|
||||
}
|
||||
|
||||
std::tr1::shared_ptr<PVScalarArray> ScalarArray::build() const
|
||||
{
|
||||
return getPVDataCreate()->createPVScalarArray(std::tr1::static_pointer_cast<const ScalarArray>(shared_from_this()));
|
||||
}
|
||||
|
||||
BoundedScalarArray::~BoundedScalarArray() {}
|
||||
|
||||
BoundedScalarArray::~BoundedScalarArray()
|
||||
{
|
||||
cacheCleanup();
|
||||
}
|
||||
|
||||
BoundedScalarArray::BoundedScalarArray(ScalarType elementType, size_t size)
|
||||
: ScalarArray(elementType),
|
||||
@@ -297,7 +379,8 @@ BoundedScalarArray::BoundedScalarArray(ScalarType elementType, size_t size)
|
||||
string BoundedScalarArray::getID() const
|
||||
{
|
||||
char buffer[32];
|
||||
sprintf(buffer, "%s<%zu>", ScalarTypeFunc::name(getElementType()), size);
|
||||
epicsSnprintf(buffer, sizeof(buffer), "%s<%lu>",
|
||||
ScalarTypeFunc::name(getElementType()), (unsigned long) size);
|
||||
return string(buffer);
|
||||
}
|
||||
|
||||
@@ -308,7 +391,10 @@ void BoundedScalarArray::serialize(ByteBuffer *buffer, SerializableControl *cont
|
||||
}
|
||||
|
||||
|
||||
FixedScalarArray::~FixedScalarArray() {}
|
||||
FixedScalarArray::~FixedScalarArray()
|
||||
{
|
||||
cacheCleanup();
|
||||
}
|
||||
|
||||
FixedScalarArray::FixedScalarArray(ScalarType elementType, size_t size)
|
||||
: ScalarArray(elementType),
|
||||
@@ -319,7 +405,8 @@ FixedScalarArray::FixedScalarArray(ScalarType elementType, size_t size)
|
||||
string FixedScalarArray::getID() const
|
||||
{
|
||||
char buffer[32];
|
||||
sprintf(buffer, "%s[%zu]", ScalarTypeFunc::name(getElementType()), size);
|
||||
epicsSnprintf(buffer, sizeof(buffer), "%s[%lu]",
|
||||
ScalarTypeFunc::name(getElementType()), (unsigned long) size);
|
||||
return string(buffer);
|
||||
}
|
||||
|
||||
@@ -336,13 +423,14 @@ StructureArray::StructureArray(StructureConstPtr const & structure)
|
||||
{
|
||||
}
|
||||
|
||||
StructureArray::~StructureArray() {
|
||||
if(debugLevel==highDebug) printf("~StructureArray\n");
|
||||
StructureArray::~StructureArray()
|
||||
{
|
||||
cacheCleanup();
|
||||
}
|
||||
|
||||
string StructureArray::getID() const
|
||||
{
|
||||
return pstructure->getID() + "[]";
|
||||
return pstructure->getID() + "[]";
|
||||
}
|
||||
|
||||
std::ostream& StructureArray::dump(std::ostream& o) const
|
||||
@@ -365,18 +453,24 @@ void StructureArray::deserialize(ByteBuffer* /*buffer*/, DeserializableControl*
|
||||
throw std::runtime_error("not valid operation, use FieldCreate::deserialize instead");
|
||||
}
|
||||
|
||||
std::tr1::shared_ptr<PVValueArray<std::tr1::shared_ptr<PVStructure> > > StructureArray::build() const
|
||||
{
|
||||
return getPVDataCreate()->createPVStructureArray(std::tr1::static_pointer_cast<const StructureArray>(shared_from_this()));
|
||||
}
|
||||
|
||||
UnionArray::UnionArray(UnionConstPtr const & _punion)
|
||||
: Array(unionArray),punion(_punion)
|
||||
{
|
||||
}
|
||||
|
||||
UnionArray::~UnionArray() {
|
||||
if(debugLevel==highDebug) printf("~UnionArray\n");
|
||||
UnionArray::~UnionArray()
|
||||
{
|
||||
cacheCleanup();
|
||||
}
|
||||
|
||||
string UnionArray::getID() const
|
||||
{
|
||||
return punion->getID() + "[]";
|
||||
return punion->getID() + "[]";
|
||||
}
|
||||
|
||||
std::ostream& UnionArray::dump(std::ostream& o) const
|
||||
@@ -407,6 +501,11 @@ void UnionArray::deserialize(ByteBuffer* /*buffer*/, DeserializableControl* /*co
|
||||
throw std::runtime_error("not valid operation, use FieldCreate::deserialize instead");
|
||||
}
|
||||
|
||||
std::tr1::shared_ptr<PVValueArray<std::tr1::shared_ptr<PVUnion> > > UnionArray::build() const
|
||||
{
|
||||
return getPVDataCreate()->createPVUnionArray(std::tr1::static_pointer_cast<const UnionArray>(shared_from_this()));
|
||||
}
|
||||
|
||||
const string Structure::DEFAULT_ID = Structure::defaultId();
|
||||
|
||||
const string & Structure::defaultId()
|
||||
@@ -451,12 +550,15 @@ Structure::Structure (
|
||||
}
|
||||
}
|
||||
|
||||
Structure::~Structure() { }
|
||||
Structure::~Structure()
|
||||
{
|
||||
cacheCleanup();
|
||||
}
|
||||
|
||||
|
||||
string Structure::getID() const
|
||||
{
|
||||
return id;
|
||||
return id;
|
||||
}
|
||||
|
||||
FieldConstPtr Structure::getField(string const & fieldName) const {
|
||||
@@ -478,6 +580,21 @@ size_t Structure::getFieldIndex(string const &fieldName) const {
|
||||
return -1;
|
||||
}
|
||||
|
||||
FieldConstPtr Structure::getFieldImpl(string const & fieldName, bool throws) const {
|
||||
for(size_t i=0, N=fields.size(); i<N; i++)
|
||||
if(fieldName==fieldNames[i])
|
||||
return fields[i];
|
||||
|
||||
if (throws) {
|
||||
std::stringstream ss;
|
||||
ss << "Failed to get field: "
|
||||
<< fieldName << " (not found)";
|
||||
throw std::runtime_error(ss.str());
|
||||
} else {
|
||||
return FieldConstPtr();
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream& Structure::dump(std::ostream& o) const
|
||||
{
|
||||
o << format::indent() << getID() << std::endl;
|
||||
@@ -544,6 +661,11 @@ void Structure::deserialize(ByteBuffer* /*buffer*/, DeserializableControl* /*con
|
||||
throw std::runtime_error("not valid operation, use FieldCreate::deserialize instead");
|
||||
}
|
||||
|
||||
std::tr1::shared_ptr<PVStructure> Structure::build() const
|
||||
{
|
||||
return getPVDataCreate()->createPVStructure(std::tr1::static_pointer_cast<const Structure>(shared_from_this()));
|
||||
}
|
||||
|
||||
const string Union::DEFAULT_ID = Union::defaultId();
|
||||
|
||||
const string & Union::defaultId()
|
||||
@@ -609,7 +731,10 @@ Union::Union (
|
||||
}
|
||||
}
|
||||
|
||||
Union::~Union() { }
|
||||
Union::~Union()
|
||||
{
|
||||
cacheCleanup();
|
||||
}
|
||||
|
||||
int32 Union::guess(Type t, ScalarType s) const
|
||||
{
|
||||
@@ -649,7 +774,7 @@ int32 Union::guess(Type t, ScalarType s) const
|
||||
|
||||
string Union::getID() const
|
||||
{
|
||||
return id;
|
||||
return id;
|
||||
}
|
||||
|
||||
FieldConstPtr Union::getField(string const & fieldName) const {
|
||||
@@ -672,6 +797,21 @@ size_t Union::getFieldIndex(string const &fieldName) const {
|
||||
return -1;
|
||||
}
|
||||
|
||||
FieldConstPtr Union::getFieldImpl(string const & fieldName, bool throws) const {
|
||||
for(size_t i=0, N=fields.size(); i<N; i++)
|
||||
if(fieldName==fieldNames[i])
|
||||
return fields[i];
|
||||
|
||||
if (throws) {
|
||||
std::stringstream ss;
|
||||
ss << "Failed to get field: "
|
||||
<< fieldName << " (not found)";
|
||||
throw std::runtime_error(ss.str());
|
||||
} else {
|
||||
return FieldConstPtr();
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream& Union::dump(std::ostream& o) const
|
||||
{
|
||||
o << format::indent() << getID() << std::endl;
|
||||
@@ -742,6 +882,11 @@ void Union::deserialize(ByteBuffer* /*buffer*/, DeserializableControl* /*control
|
||||
throw std::runtime_error("not valid operation, use FieldCreate::deserialize instead");
|
||||
}
|
||||
|
||||
std::tr1::shared_ptr<PVUnion> Union::build() const
|
||||
{
|
||||
return getPVDataCreate()->createPVUnion(std::tr1::static_pointer_cast<const Union>(shared_from_this()));
|
||||
}
|
||||
|
||||
FieldBuilder::FieldBuilder()
|
||||
:fieldCreate(getFieldCreate())
|
||||
,idSet(false)
|
||||
@@ -824,7 +969,7 @@ FieldBuilder::FieldBuilder(const FieldBuilderPtr & _parentBuilder,
|
||||
{}
|
||||
|
||||
FieldBuilder::FieldBuilder(FieldBuilderPtr const & _parentBuilder,
|
||||
string const & _nestedName,
|
||||
string const & _nestedName,
|
||||
Type _nestedClassToBuild, bool _nestedArray)
|
||||
:fieldCreate(_parentBuilder->fieldCreate)
|
||||
,idSet(false)
|
||||
@@ -837,16 +982,29 @@ FieldBuilder::FieldBuilder(FieldBuilderPtr const & _parentBuilder,
|
||||
|
||||
void FieldBuilder::reset()
|
||||
{
|
||||
id.erase();
|
||||
id.erase();
|
||||
idSet = false;
|
||||
fieldNames.clear();
|
||||
fields.clear();
|
||||
fieldNames.clear();
|
||||
fields.clear();
|
||||
}
|
||||
|
||||
FieldBuilderPtr FieldBuilder::begin()
|
||||
{
|
||||
FieldBuilderPtr ret(new FieldBuilder);
|
||||
return ret;
|
||||
}
|
||||
|
||||
FieldBuilderPtr FieldBuilder::begin(StructureConstPtr S)
|
||||
{
|
||||
FieldBuilderPtr ret(new FieldBuilder(S.get()));
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
FieldBuilderPtr FieldBuilder::setId(string const & id)
|
||||
{
|
||||
this->id = id;
|
||||
idSet = true;
|
||||
idSet = true;
|
||||
return shared_from_this();
|
||||
}
|
||||
|
||||
@@ -868,7 +1026,7 @@ FieldBuilderPtr FieldBuilder::add(string const & name, FieldConstPtr const & fie
|
||||
} else if(*cur!=*field) {
|
||||
THROW_EXCEPTION2(std::runtime_error, "duplicate field name w/ different type : "+name);
|
||||
} // else exact duplicate is silently ignored
|
||||
return shared_from_this();
|
||||
return shared_from_this();
|
||||
}
|
||||
|
||||
FieldBuilderPtr FieldBuilder::addArray(string const & name, ScalarType scalarType)
|
||||
@@ -910,7 +1068,7 @@ FieldBuilderPtr FieldBuilder::addArray(string const & name, FieldConstPtr const
|
||||
msg << element->getType();
|
||||
THROW_EXCEPTION2(std::invalid_argument, msg.str());
|
||||
}
|
||||
|
||||
|
||||
return add(name, fld);
|
||||
}
|
||||
|
||||
@@ -945,7 +1103,7 @@ StructureConstPtr FieldBuilder::createStructure()
|
||||
{
|
||||
if (parentBuilder.get())
|
||||
THROW_EXCEPTION2(std::runtime_error, "createStructure() called in nested FieldBuilder");
|
||||
|
||||
|
||||
StructureConstPtr field(static_pointer_cast<const Structure>(createFieldInternal(structure)));
|
||||
reset();
|
||||
return field;
|
||||
@@ -955,7 +1113,7 @@ UnionConstPtr FieldBuilder::createUnion()
|
||||
{
|
||||
if (parentBuilder.get())
|
||||
THROW_EXCEPTION2(std::runtime_error, "createUnion() called in nested FieldBuilder");
|
||||
|
||||
|
||||
UnionConstPtr field(static_pointer_cast<const Union>(createFieldInternal(union_)));
|
||||
reset();
|
||||
return field;
|
||||
@@ -1023,7 +1181,7 @@ FieldBuilderPtr FieldBuilder::endNested()
|
||||
{
|
||||
if (!parentBuilder)
|
||||
THROW_EXCEPTION2(std::runtime_error, "FieldBuilder::endNested() can only be called to create nested fields");
|
||||
|
||||
|
||||
FieldConstPtr nestedField = createFieldInternal(nestedClassToBuild);
|
||||
|
||||
if(createNested) {
|
||||
@@ -1079,10 +1237,9 @@ ScalarConstPtr FieldCreate::createScalar(ScalarType scalarType) const
|
||||
|
||||
BoundedStringConstPtr FieldCreate::createBoundedString(std::size_t maxLength) const
|
||||
{
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<BoundedString> s(new BoundedString(maxLength), Field::Deleter());
|
||||
BoundedStringConstPtr sa = s;
|
||||
return sa;
|
||||
std::tr1::shared_ptr<BoundedString> s(new BoundedString(maxLength));
|
||||
Helper::cache(this, s);
|
||||
return s;
|
||||
}
|
||||
|
||||
ScalarArrayConstPtr FieldCreate::createScalarArray(ScalarType elementType) const
|
||||
@@ -1092,7 +1249,7 @@ ScalarArrayConstPtr FieldCreate::createScalarArray(ScalarType elementType) const
|
||||
strm << elementType;
|
||||
THROW_EXCEPTION2(std::invalid_argument, strm.str());
|
||||
}
|
||||
|
||||
|
||||
return scalarArrays[elementType];
|
||||
}
|
||||
|
||||
@@ -1104,10 +1261,9 @@ ScalarArrayConstPtr FieldCreate::createFixedScalarArray(ScalarType elementType,
|
||||
THROW_EXCEPTION2(std::invalid_argument, strm.str());
|
||||
}
|
||||
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<ScalarArray> s(new FixedScalarArray(elementType, size), Field::Deleter());
|
||||
ScalarArrayConstPtr sa = s;
|
||||
return sa;
|
||||
std::tr1::shared_ptr<ScalarArray> s(new FixedScalarArray(elementType, size));
|
||||
Helper::cache(this, s);
|
||||
return s;
|
||||
}
|
||||
|
||||
ScalarArrayConstPtr FieldCreate::createBoundedScalarArray(ScalarType elementType, size_t size) const
|
||||
@@ -1118,10 +1274,9 @@ ScalarArrayConstPtr FieldCreate::createBoundedScalarArray(ScalarType elementType
|
||||
THROW_EXCEPTION2(std::invalid_argument, strm.str());
|
||||
}
|
||||
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<ScalarArray> s(new BoundedScalarArray(elementType, size), Field::Deleter());
|
||||
ScalarArrayConstPtr sa = s;
|
||||
return sa;
|
||||
std::tr1::shared_ptr<ScalarArray> s(new BoundedScalarArray(elementType, size));
|
||||
Helper::cache(this, s);
|
||||
return s;
|
||||
}
|
||||
|
||||
StructureConstPtr FieldCreate::createStructure () const
|
||||
@@ -1178,10 +1333,9 @@ StructureConstPtr FieldCreate::createStructure (
|
||||
StringArray const & fieldNames,FieldConstPtrArray const & fields) const
|
||||
{
|
||||
validateFieldNames(fieldNames);
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<Structure> sp(new Structure(fieldNames,fields), Field::Deleter());
|
||||
StructureConstPtr structure = sp;
|
||||
return structure;
|
||||
std::tr1::shared_ptr<Structure> sp(new Structure(fieldNames,fields));
|
||||
Helper::cache(this, sp);
|
||||
return sp;
|
||||
}
|
||||
|
||||
StructureConstPtr FieldCreate::createStructure (
|
||||
@@ -1190,29 +1344,26 @@ StructureConstPtr FieldCreate::createStructure (
|
||||
FieldConstPtrArray const & fields) const
|
||||
{
|
||||
validateFieldNames(fieldNames);
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<Structure> sp(new Structure(fieldNames,fields,id), Field::Deleter());
|
||||
StructureConstPtr structure = sp;
|
||||
return structure;
|
||||
std::tr1::shared_ptr<Structure> sp(new Structure(fieldNames,fields,id));
|
||||
Helper::cache(this, sp);
|
||||
return sp;
|
||||
}
|
||||
|
||||
StructureArrayConstPtr FieldCreate::createStructureArray(
|
||||
StructureConstPtr const & structure) const
|
||||
{
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<StructureArray> sp(new StructureArray(structure), Field::Deleter());
|
||||
StructureArrayConstPtr structureArray = sp;
|
||||
return structureArray;
|
||||
std::tr1::shared_ptr<StructureArray> sp(new StructureArray(structure));
|
||||
Helper::cache(this, sp);
|
||||
return sp;
|
||||
}
|
||||
|
||||
UnionConstPtr FieldCreate::createUnion (
|
||||
StringArray const & fieldNames,FieldConstPtrArray const & fields) const
|
||||
{
|
||||
validateFieldNames(fieldNames);
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<Union> sp(new Union(fieldNames,fields), Field::Deleter());
|
||||
UnionConstPtr punion = sp;
|
||||
return punion;
|
||||
std::tr1::shared_ptr<Union> sp(new Union(fieldNames,fields));
|
||||
Helper::cache(this, sp);
|
||||
return sp;
|
||||
}
|
||||
|
||||
UnionConstPtr FieldCreate::createUnion (
|
||||
@@ -1221,10 +1372,9 @@ UnionConstPtr FieldCreate::createUnion (
|
||||
FieldConstPtrArray const & fields) const
|
||||
{
|
||||
validateFieldNames(fieldNames);
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<Union> sp(new Union(fieldNames,fields,id), Field::Deleter());
|
||||
UnionConstPtr punion = sp;
|
||||
return punion;
|
||||
std::tr1::shared_ptr<Union> sp(new Union(fieldNames,fields,id));
|
||||
Helper::cache(this, sp);
|
||||
return sp;
|
||||
}
|
||||
|
||||
UnionConstPtr FieldCreate::createVariantUnion () const
|
||||
@@ -1235,10 +1385,9 @@ UnionConstPtr FieldCreate::createVariantUnion () const
|
||||
UnionArrayConstPtr FieldCreate::createUnionArray(
|
||||
UnionConstPtr const & punion) const
|
||||
{
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<UnionArray> sp(new UnionArray(punion), Field::Deleter());
|
||||
UnionArrayConstPtr unionArray = sp;
|
||||
return unionArray;
|
||||
std::tr1::shared_ptr<UnionArray> sp(new UnionArray(punion));
|
||||
Helper::cache(this, sp);
|
||||
return sp;
|
||||
}
|
||||
|
||||
UnionArrayConstPtr FieldCreate::createVariantUnionArray () const
|
||||
@@ -1302,7 +1451,7 @@ static int decodeScalar(int8 code)
|
||||
pvUInt, // unsigned 32-bits
|
||||
pvULong // unsigned 64-bits
|
||||
};
|
||||
|
||||
|
||||
static const int floatLUT[] =
|
||||
{
|
||||
-1, // reserved
|
||||
@@ -1367,12 +1516,10 @@ FieldConstPtr FieldCreate::deserialize(ByteBuffer* buffer, DeserializableControl
|
||||
|
||||
size_t size = SerializeHelper::readSize(buffer, control);
|
||||
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<Field> sp(
|
||||
new BoundedString(size),
|
||||
Field::Deleter());
|
||||
FieldConstPtr p = sp;
|
||||
return p;
|
||||
new BoundedString(size));
|
||||
Helper::cache(this, sp);
|
||||
return sp;
|
||||
}
|
||||
else
|
||||
throw std::invalid_argument("invalid type encoding");
|
||||
@@ -1397,19 +1544,17 @@ FieldConstPtr FieldCreate::deserialize(ByteBuffer* buffer, DeserializableControl
|
||||
{
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<Field> sp(
|
||||
new FixedScalarArray(static_cast<epics::pvData::ScalarType>(scalarType), size),
|
||||
Field::Deleter());
|
||||
FieldConstPtr p = sp;
|
||||
return p;
|
||||
new FixedScalarArray(static_cast<epics::pvData::ScalarType>(scalarType), size));
|
||||
Helper::cache(this, sp);
|
||||
return sp;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<Field> sp(
|
||||
new BoundedScalarArray(static_cast<epics::pvData::ScalarType>(scalarType), size),
|
||||
Field::Deleter());
|
||||
FieldConstPtr p = sp;
|
||||
return p;
|
||||
new BoundedScalarArray(static_cast<epics::pvData::ScalarType>(scalarType), size));
|
||||
Helper::cache(this, sp);
|
||||
return sp;
|
||||
}
|
||||
}
|
||||
else if (typeCode == 0x80)
|
||||
@@ -1421,9 +1566,9 @@ FieldConstPtr FieldCreate::deserialize(ByteBuffer* buffer, DeserializableControl
|
||||
// Type type = Type.structureArray;
|
||||
StructureConstPtr elementStructure = std::tr1::static_pointer_cast<const Structure>(control->cachedDeserialize(buffer));
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<Field> sp(new StructureArray(elementStructure), Field::Deleter());
|
||||
FieldConstPtr p = sp;
|
||||
return p;
|
||||
std::tr1::shared_ptr<Field> sp(new StructureArray(elementStructure));
|
||||
Helper::cache(this, sp);
|
||||
return sp;
|
||||
}
|
||||
else if (typeCode == 0x81)
|
||||
{
|
||||
@@ -1434,9 +1579,9 @@ FieldConstPtr FieldCreate::deserialize(ByteBuffer* buffer, DeserializableControl
|
||||
// Type type = Type.unionArray;
|
||||
UnionConstPtr elementUnion = std::tr1::static_pointer_cast<const Union>(control->cachedDeserialize(buffer));
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<Field> sp(new UnionArray(elementUnion), Field::Deleter());
|
||||
FieldConstPtr p = sp;
|
||||
return p;
|
||||
std::tr1::shared_ptr<Field> sp(new UnionArray(elementUnion));
|
||||
Helper::cache(this, sp);
|
||||
return sp;
|
||||
}
|
||||
else if (typeCode == 0x82)
|
||||
{
|
||||
@@ -1457,6 +1602,7 @@ struct field_factory {
|
||||
FieldCreatePtr fieldCreate;
|
||||
field_factory() :fieldCreate(new FieldCreate()) {
|
||||
registerRefCounter("Field", &Field::num_instances);
|
||||
registerRefCounter("Thread", &Thread::num_instances);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1485,23 +1631,21 @@ FieldCreate::FieldCreate()
|
||||
{
|
||||
for (int i = 0; i <= MAX_SCALAR_TYPE; i++)
|
||||
{
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<Scalar> sp(new Scalar(static_cast<ScalarType>(i)), Field::Deleter());
|
||||
ScalarConstPtr p = sp;
|
||||
scalars.push_back(p);
|
||||
std::tr1::shared_ptr<Scalar> sp(new Scalar(static_cast<ScalarType>(i)));
|
||||
Helper::cache(this, sp);
|
||||
scalars.push_back(sp);
|
||||
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<ScalarArray> spa(new ScalarArray(static_cast<ScalarType>(i)), Field::Deleter());
|
||||
ScalarArrayConstPtr pa = spa;
|
||||
std::tr1::shared_ptr<ScalarArray> spa(new ScalarArray(static_cast<ScalarType>(i)));
|
||||
Helper::cache(this, spa);
|
||||
scalarArrays.push_back(spa);
|
||||
}
|
||||
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<Union> su(new Union(), Field::Deleter());
|
||||
std::tr1::shared_ptr<Union> su(new Union());
|
||||
Helper::cache(this, su);
|
||||
variantUnion = su;
|
||||
|
||||
// TODO use std::make_shared
|
||||
std::tr1::shared_ptr<UnionArray> sua(new UnionArray(variantUnion), Field::Deleter());
|
||||
std::tr1::shared_ptr<UnionArray> sua(new UnionArray(variantUnion));
|
||||
Helper::cache(this, sua);
|
||||
variantUnionArray = sua;
|
||||
}
|
||||
|
||||
@@ -1514,4 +1658,3 @@ namespace std{
|
||||
return o << "nullptr";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ PVArray::PVArray(FieldConstPtr const & field)
|
||||
{
|
||||
capacityMutable = false;
|
||||
PVField::setImmutable();
|
||||
}
|
||||
}
|
||||
|
||||
bool PVArray::isCapacityMutable() const
|
||||
{
|
||||
@@ -49,7 +49,7 @@ PVArray::PVArray(FieldConstPtr const & field)
|
||||
|
||||
std::ostream& operator<<(format::array_at_internal const& manip, const PVArray& array)
|
||||
{
|
||||
return array.dumpValue(manip.stream, manip.index);
|
||||
return array.dumpValue(manip.stream, manip.index);
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
@@ -7,10 +7,6 @@
|
||||
* @author mrk
|
||||
*/
|
||||
|
||||
#if defined(_WIN32) && !defined(NOMINMAX)
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
@@ -65,6 +61,48 @@ template<> const ScalarType PVStringArray::typeCode = pvString;
|
||||
template<typename T>
|
||||
PVScalarValue<T>::~PVScalarValue() {}
|
||||
|
||||
template<typename T>
|
||||
std::ostream& PVScalarValue<T>::dumpValue(std::ostream& o) const
|
||||
{
|
||||
return o << get();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVScalarValue<T>::operator>>=(T& value) const
|
||||
{
|
||||
value = get();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVScalarValue<T>::operator<<=(typename storage_t::arg_type value)
|
||||
{
|
||||
put(value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVScalarValue<T>::assign(const PVScalar& scalar)
|
||||
{
|
||||
if(isImmutable())
|
||||
throw std::invalid_argument("destination is immutable");
|
||||
copyUnchecked(scalar);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVScalarValue<T>::copy(const PVScalar& from)
|
||||
{
|
||||
assign(from);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVScalarValue<T>::copyUnchecked(const PVScalar& from)
|
||||
{
|
||||
if(this==&from)
|
||||
return;
|
||||
T result;
|
||||
from.getAs((void*)&result, typeCode);
|
||||
put(result);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVScalarValue<T>::serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher) const {
|
||||
@@ -104,6 +142,12 @@ PVString::PVString(ScalarConstPtr const & scalar)
|
||||
storage.maxLength = 0;
|
||||
}
|
||||
|
||||
std::ostream& PVString::dumpValue(std::ostream& o) const
|
||||
{
|
||||
o<<maybeQuote(get());
|
||||
return o;
|
||||
}
|
||||
|
||||
/* mixing overrides (virtual functions) and overloads (different argument lists) is fun...
|
||||
* we override all overloads to avoid the "hides overloaded virtual function" warning from clang.
|
||||
* In this case we don't need/want to, so just delegate to the base class.
|
||||
@@ -115,17 +159,17 @@ void PVString::serialize(ByteBuffer *pbuffer,
|
||||
void PVString::serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher, size_t offset, size_t count) const
|
||||
{
|
||||
// check bounds
|
||||
// check bounds
|
||||
const size_t length = storage.value.length();
|
||||
/*if (offset < 0) offset = 0;
|
||||
else*/ if (offset > length) offset = length;
|
||||
//if (count < 0) count = length;
|
||||
/*if (offset < 0) offset = 0;
|
||||
else*/ if (offset > length) offset = length;
|
||||
//if (count < 0) count = length;
|
||||
|
||||
const size_t maxCount = length - offset;
|
||||
if (count > maxCount)
|
||||
count = maxCount;
|
||||
|
||||
// write
|
||||
const size_t maxCount = length - offset;
|
||||
if (count > maxCount)
|
||||
count = maxCount;
|
||||
|
||||
// write
|
||||
SerializeHelper::serializeSubstring(storage.value, offset, count, pbuffer, pflusher);
|
||||
}
|
||||
|
||||
@@ -146,7 +190,7 @@ template<typename T>
|
||||
PVValueArray<T>::PVValueArray(ScalarArrayConstPtr const & scalarArray)
|
||||
:base_t(scalarArray)
|
||||
,value()
|
||||
|
||||
|
||||
{}
|
||||
|
||||
PVValueArray<PVStructurePtr>::PVValueArray(StructureArrayConstPtr const & structureArray)
|
||||
@@ -160,6 +204,59 @@ PVValueArray<PVUnionPtr>::PVValueArray(UnionArrayConstPtr const & unionArray)
|
||||
,unionArray(unionArray)
|
||||
{}
|
||||
|
||||
template<typename T>
|
||||
PVValueArray<T>::~PVValueArray() {}
|
||||
|
||||
template<typename T>
|
||||
ArrayConstPtr PVValueArray<T>::getArray() const
|
||||
{
|
||||
return std::tr1::static_pointer_cast<const Array>(this->getField());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::ostream& PVValueArray<T>::dumpValue(std::ostream& o) const
|
||||
{
|
||||
const_svector v(this->view());
|
||||
typename const_svector::const_iterator it(v.begin()),
|
||||
end(v.end());
|
||||
o << '[';
|
||||
if(it!=end) {
|
||||
o << print_cast(*it++);
|
||||
for(; it!=end; ++it)
|
||||
o << ',' << print_cast(*it);
|
||||
|
||||
}
|
||||
return o << ']';
|
||||
}
|
||||
|
||||
template<>
|
||||
std::ostream& PVValueArray<std::string>::dumpValue(std::ostream& o, size_t index) const
|
||||
{
|
||||
return o << maybeQuote(this->view().at(index));
|
||||
}
|
||||
|
||||
template<>
|
||||
std::ostream& PVValueArray<std::string>::dumpValue(std::ostream& o) const
|
||||
{
|
||||
const_svector v(this->view());
|
||||
const_svector::const_iterator it(v.begin()),
|
||||
end(v.end());
|
||||
o << '[';
|
||||
if(it!=end) {
|
||||
o << maybeQuote(*it++);
|
||||
for(; it!=end; ++it)
|
||||
o << ", " << maybeQuote(*it);
|
||||
|
||||
}
|
||||
return o << ']';
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::ostream& PVValueArray<T>::dumpValue(std::ostream& o, size_t index) const
|
||||
{
|
||||
return o << print_cast(this->view().at(index));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVValueArray<T>::setCapacity(size_t capacity)
|
||||
{
|
||||
@@ -352,6 +449,18 @@ void PVValueArray<string>::serialize(ByteBuffer *pbuffer,
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVValueArray<T>::_getAsVoid(epics::pvData::shared_vector<const void>& out) const
|
||||
{
|
||||
out = static_shared_vector_cast<const void>(this->view());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void PVValueArray<T>::_putFromVoid(const epics::pvData::shared_vector<const void>& in)
|
||||
{
|
||||
this->replace(shared_vector_convert<const T>(in));
|
||||
}
|
||||
|
||||
// Factory
|
||||
|
||||
PVDataCreate::PVDataCreate()
|
||||
@@ -517,7 +626,7 @@ PVScalarArrayPtr PVDataCreate::createPVScalarArray(
|
||||
return PVScalarArrayPtr(new PVStringArray(scalarArray));
|
||||
}
|
||||
throw std::logic_error("PVDataCreate::createPVScalarArray should never get here");
|
||||
|
||||
|
||||
}
|
||||
|
||||
PVScalarArrayPtr PVDataCreate::createPVScalarArray(
|
||||
@@ -648,6 +757,18 @@ template class PVScalarValue<uint64>;
|
||||
template class PVScalarValue<float>;
|
||||
template class PVScalarValue<double>;
|
||||
template class PVScalarValue<std::string>;
|
||||
template class PVValueArray<boolean>;
|
||||
template class PVValueArray<int8>;
|
||||
template class PVValueArray<uint8>;
|
||||
template class PVValueArray<int16>;
|
||||
template class PVValueArray<uint16>;
|
||||
template class PVValueArray<int32>;
|
||||
template class PVValueArray<uint32>;
|
||||
template class PVValueArray<int64>;
|
||||
template class PVValueArray<uint64>;
|
||||
template class PVValueArray<float>;
|
||||
template class PVValueArray<double>;
|
||||
template class PVValueArray<std::string>;
|
||||
|
||||
}} // namespace epics::pvData
|
||||
|
||||
@@ -658,4 +779,3 @@ namespace std{
|
||||
return o << "nullptr";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ size_t PVField::getNumberFields() const
|
||||
|
||||
void PVField::setImmutable() {immutable = true;}
|
||||
|
||||
void PVField::postPut()
|
||||
void PVField::postPut()
|
||||
{
|
||||
if(postHandler) postHandler->postPut();
|
||||
}
|
||||
@@ -91,7 +91,7 @@ bool PVField::equals(PVField &pv)
|
||||
|
||||
std::ostream& operator<<(std::ostream& o, const PVField& f)
|
||||
{
|
||||
return f.dumpValue(o);
|
||||
return f.dumpValue(o);
|
||||
};
|
||||
|
||||
string PVField::getFullName() const
|
||||
@@ -184,62 +184,16 @@ void PVField::copy(const PVField& from)
|
||||
if(isImmutable())
|
||||
throw std::invalid_argument("destination is immutable");
|
||||
|
||||
if (getField()->getType() != from.getField()->getType())
|
||||
if (getField() != from.getField())
|
||||
throw std::invalid_argument("field types do not match");
|
||||
|
||||
switch(getField()->getType())
|
||||
{
|
||||
case scalar:
|
||||
{
|
||||
const PVScalar* fromS = static_cast<const PVScalar*>(&from);
|
||||
PVScalar* toS = static_cast<PVScalar*>(this);
|
||||
toS->copy(*fromS);
|
||||
break;
|
||||
}
|
||||
case scalarArray:
|
||||
{
|
||||
const PVScalarArray* fromS = static_cast<const PVScalarArray*>(&from);
|
||||
PVScalarArray* toS = static_cast<PVScalarArray*>(this);
|
||||
toS->copy(*fromS);
|
||||
break;
|
||||
}
|
||||
case structure:
|
||||
{
|
||||
const PVStructure* fromS = static_cast<const PVStructure*>(&from);
|
||||
PVStructure* toS = static_cast<PVStructure*>(this);
|
||||
toS->copy(*fromS);
|
||||
break;
|
||||
}
|
||||
case structureArray:
|
||||
{
|
||||
const PVStructureArray* fromS = static_cast<const PVStructureArray*>(&from);
|
||||
PVStructureArray* toS = static_cast<PVStructureArray*>(this);
|
||||
toS->copy(*fromS);
|
||||
break;
|
||||
}
|
||||
case union_:
|
||||
{
|
||||
const PVUnion* fromS = static_cast<const PVUnion*>(&from);
|
||||
PVUnion* toS = static_cast<PVUnion*>(this);
|
||||
toS->copy(*fromS);
|
||||
break;
|
||||
}
|
||||
case unionArray:
|
||||
{
|
||||
const PVUnionArray* fromS = static_cast<const PVUnionArray*>(&from);
|
||||
PVUnionArray* toS = static_cast<PVUnionArray*>(this);
|
||||
toS->copy(*fromS);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw std::logic_error("PVField::copy unknown type");
|
||||
}
|
||||
}
|
||||
copyUnchecked(from);
|
||||
}
|
||||
|
||||
void PVField::copyUnchecked(const PVField& from)
|
||||
{
|
||||
assert(getField()==from.getField());
|
||||
|
||||
switch(getField()->getType())
|
||||
{
|
||||
case scalar:
|
||||
|
||||
@@ -38,7 +38,7 @@ PVStructure::PVStructure(StructureConstPtr const & structurePtr)
|
||||
pvFields.push_back(pvDataCreate->createPVField(fields[i]));
|
||||
}
|
||||
for(size_t i=0; i<numberFields; i++) {
|
||||
pvFields[i]->setParentAndName(this,fieldNames[i]);
|
||||
pvFields[i]->setParentAndName(this,fieldNames[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,9 +60,7 @@ PVStructure::PVStructure(StructureConstPtr const & structurePtr,
|
||||
}
|
||||
}
|
||||
|
||||
PVStructure::~PVStructure()
|
||||
{
|
||||
}
|
||||
PVStructure::~PVStructure() {}
|
||||
|
||||
void PVStructure::setImmutable()
|
||||
{
|
||||
@@ -74,16 +72,6 @@ void PVStructure::setImmutable()
|
||||
PVField::setImmutable();
|
||||
}
|
||||
|
||||
const StructureConstPtr& PVStructure::getStructure() const
|
||||
{
|
||||
return structurePtr;
|
||||
}
|
||||
|
||||
const PVFieldPtrArray & PVStructure::getPVFields() const
|
||||
{
|
||||
return pvFields;
|
||||
}
|
||||
|
||||
PVFieldPtr PVStructure::getSubFieldImpl(size_t fieldOffset, bool throws) const
|
||||
{
|
||||
const PVStructure *current = this;
|
||||
@@ -180,7 +168,7 @@ PVFieldPtr PVStructure::getSubFieldImpl(const char *name, bool throws) const
|
||||
std::stringstream ss;
|
||||
ss << "Failed to get field: " << fullName << " ("
|
||||
<< std::string(fullName, sep) << " not found)";
|
||||
throw std::runtime_error(ss.str());
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
else
|
||||
return PVFieldPtr();
|
||||
@@ -196,7 +184,7 @@ PVFieldPtr PVStructure::getSubFieldImpl(const char *name, bool throws) const
|
||||
std::stringstream ss;
|
||||
ss << "Failed to get field: " << fullName
|
||||
<< " (" << std::string(fullName, sep)
|
||||
<< " is not a structure)";
|
||||
<< " is not a structure)";
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
else
|
||||
@@ -319,22 +307,22 @@ std::ostream& PVStructure::dumpValue(std::ostream& o) const
|
||||
o << format::indent() << getStructure()->getID() << ' ' << getFieldName();
|
||||
o << std::endl;
|
||||
{
|
||||
format::indent_scope s(o);
|
||||
format::indent_scope s(o);
|
||||
|
||||
PVFieldPtrArray const & fieldsData = getPVFields();
|
||||
if (fieldsData.size() != 0) {
|
||||
size_t length = getStructure()->getNumberFields();
|
||||
for(size_t i=0; i<length; i++) {
|
||||
PVFieldPtr fieldField = fieldsData[i];
|
||||
Type type = fieldField->getField()->getType();
|
||||
if (type == scalar || type == scalarArray)
|
||||
o << format::indent() << fieldField->getField()->getID() << ' ' << fieldField->getFieldName() << ' ' << *(fieldField.get()) << std::endl;
|
||||
else
|
||||
o << *(fieldField.get());
|
||||
}
|
||||
}
|
||||
PVFieldPtrArray const & fieldsData = getPVFields();
|
||||
if (fieldsData.size() != 0) {
|
||||
size_t length = getStructure()->getNumberFields();
|
||||
for(size_t i=0; i<length; i++) {
|
||||
PVFieldPtr fieldField = fieldsData[i];
|
||||
Type type = fieldField->getField()->getType();
|
||||
if (type == scalar || type == scalarArray)
|
||||
o << format::indent() << fieldField->getField()->getID() << ' ' << fieldField->getFieldName() << ' ' << *(fieldField.get()) << std::endl;
|
||||
else
|
||||
o << *(fieldField.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
return o;
|
||||
return o;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -218,10 +218,10 @@ std::ostream& PVStructureArray::dumpValue(std::ostream& o) const
|
||||
size_t length = getLength();
|
||||
if (length > 0)
|
||||
{
|
||||
format::indent_scope s(o);
|
||||
format::indent_scope s(o);
|
||||
|
||||
for (size_t i = 0; i < length; i++)
|
||||
dumpValue(o, i);
|
||||
dumpValue(o, i);
|
||||
}
|
||||
|
||||
return o;
|
||||
|
||||
@@ -23,7 +23,7 @@ using std::size_t;
|
||||
using std::string;
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
|
||||
#define PVUNION_UNDEFINED_INDEX -1
|
||||
const int32 PVUnion::UNDEFINED_INDEX = PVUNION_UNDEFINED_INDEX;
|
||||
|
||||
@@ -75,13 +75,13 @@ PVFieldPtr PVUnion::select(int32 index)
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
PVFieldPtr PVUnion::select(string const & fieldName)
|
||||
{
|
||||
int32 index = variant ? -1 : static_cast<int32>(unionPtr->getFieldIndex(fieldName));
|
||||
if (index == -1)
|
||||
if (index == -1)
|
||||
throw std::invalid_argument("no such fieldName");
|
||||
return select(index);
|
||||
return select(index);
|
||||
}
|
||||
|
||||
void PVUnion::set(int32 index, PVFieldPtr const & value)
|
||||
@@ -94,14 +94,14 @@ void PVUnion::set(int32 index, PVFieldPtr const & value)
|
||||
{
|
||||
// for undefined index we accept only null values
|
||||
if (value)
|
||||
throw std::invalid_argument("non-null value for index == UNDEFINED_INDEX");
|
||||
throw std::invalid_argument("non-null value for index == UNDEFINED_INDEX");
|
||||
}
|
||||
else if (index < 0 || size_t(index) >= 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))
|
||||
throw std::invalid_argument("selected field and its introspection data do not match");
|
||||
throw std::invalid_argument("selected field and its introspection data do not match");
|
||||
}
|
||||
|
||||
selector = index;
|
||||
@@ -136,7 +136,7 @@ void PVUnion::serialize(ByteBuffer *pbuffer, SerializableControl *pflusher) cons
|
||||
// write selector value
|
||||
SerializeHelper::writeSize(selector, pbuffer, pflusher);
|
||||
// write value, no value for UNDEFINED_INDEX
|
||||
if (selector != UNDEFINED_INDEX)
|
||||
if (selector != UNDEFINED_INDEX)
|
||||
value->serialize(pbuffer, pflusher);
|
||||
}
|
||||
}
|
||||
@@ -180,20 +180,20 @@ std::ostream& PVUnion::dumpValue(std::ostream& o) const
|
||||
{
|
||||
o << format::indent() << getUnion()->getID() << ' ' << getFieldName() << std::endl;
|
||||
{
|
||||
format::indent_scope s(o);
|
||||
format::indent_scope s(o);
|
||||
const PVField::const_shared_pointer& fieldField = get();
|
||||
if (fieldField.get() == NULL)
|
||||
o << format::indent() << "(none)" << std::endl;
|
||||
else
|
||||
{
|
||||
Type type = fieldField->getField()->getType();
|
||||
if (type == scalar || type == scalarArray)
|
||||
o << format::indent() << fieldField->getField()->getID() << ' ' << fieldField->getFieldName() << ' ' << *(fieldField.get()) << std::endl;
|
||||
else
|
||||
o << *(fieldField.get());
|
||||
}
|
||||
if (fieldField.get() == NULL)
|
||||
o << format::indent() << "(none)" << std::endl;
|
||||
else
|
||||
{
|
||||
Type type = fieldField->getField()->getType();
|
||||
if (type == scalar || type == scalarArray)
|
||||
o << format::indent() << fieldField->getField()->getID() << ' ' << fieldField->getFieldName() << ' ' << *(fieldField.get()) << std::endl;
|
||||
else
|
||||
o << *(fieldField.get());
|
||||
}
|
||||
}
|
||||
return o;
|
||||
return o;
|
||||
}
|
||||
|
||||
void PVUnion::copy(const PVUnion& from)
|
||||
|
||||
@@ -217,10 +217,10 @@ std::ostream& PVUnionArray::dumpValue(std::ostream& o) const
|
||||
size_t length = getLength();
|
||||
if (length > 0)
|
||||
{
|
||||
format::indent_scope s(o);
|
||||
format::indent_scope s(o);
|
||||
|
||||
for (size_t i = 0; i < length; i++)
|
||||
dumpValue(o, i);
|
||||
dumpValue(o, i);
|
||||
}
|
||||
|
||||
return o;
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <stdexcept>
|
||||
|
||||
#include <epicsMutex.h>
|
||||
#include <epicsThread.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/lock.h>
|
||||
@@ -20,34 +21,87 @@
|
||||
using std::tr1::static_pointer_cast;
|
||||
using std::string;
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
static
|
||||
StructureConstPtr buildValueAlarm(ScalarType vtype)
|
||||
{
|
||||
return FieldBuilder::begin()
|
||||
->setId("valueAlarm_t")
|
||||
->add("active", pvBoolean)
|
||||
->add("lowAlarmLimit", vtype)
|
||||
->add("lowWarningLimit", vtype)
|
||||
->add("highWarningLimit", vtype)
|
||||
->add("highAlarmLimit", vtype)
|
||||
->add("lowAlarmSeverity", pvInt)
|
||||
->add("lowWarningSeverity", pvInt)
|
||||
->add("highWarningSeverity", pvInt)
|
||||
->add("highAlarmSeverity", pvInt)
|
||||
->add("hysteresis", pvByte)
|
||||
->createStructure();
|
||||
}
|
||||
|
||||
StandardField::StandardField()
|
||||
: fieldCreate(getFieldCreate()),
|
||||
notImplemented("not implemented"),
|
||||
valueFieldName("value")
|
||||
{}
|
||||
:fieldCreate(getFieldCreate())
|
||||
,notImplemented("not implemented")
|
||||
,valueFieldName("value")
|
||||
|
||||
void StandardField::init()
|
||||
{
|
||||
createAlarm();
|
||||
createTimeStamp();
|
||||
createDisplay();
|
||||
createControl();
|
||||
createBooleanAlarm();
|
||||
createByteAlarm();
|
||||
createShortAlarm();
|
||||
createIntAlarm();
|
||||
createLongAlarm();
|
||||
createUByteAlarm();
|
||||
createUShortAlarm();
|
||||
createUIntAlarm();
|
||||
createULongAlarm();
|
||||
createFloatAlarm();
|
||||
createDoubleAlarm();
|
||||
createEnumeratedAlarm();
|
||||
}
|
||||
,alarmField(FieldBuilder::begin()
|
||||
->setId("alarm_t")
|
||||
->add("severity", pvInt)
|
||||
->add("status", pvInt)
|
||||
->add("message", pvString)
|
||||
->createStructure())
|
||||
|
||||
,timeStampField(FieldBuilder::begin()
|
||||
->setId("time_t")
|
||||
->add("secondsPastEpoch", pvLong)
|
||||
->add("nanoseconds", pvInt)
|
||||
->add("userTag", pvInt)
|
||||
->createStructure())
|
||||
|
||||
,displayField(FieldBuilder::begin()
|
||||
->setId("display_t")
|
||||
->add("limitLow", pvDouble)
|
||||
->add("limitHigh", pvDouble)
|
||||
->add("description", pvString)
|
||||
->add("format", pvString)
|
||||
->add("units", pvString)
|
||||
->createStructure())
|
||||
|
||||
,controlField(FieldBuilder::begin()
|
||||
->setId("control_t")
|
||||
->add("limitLow", pvDouble)
|
||||
->add("limitHigh", pvDouble)
|
||||
->add("minStep", pvDouble)
|
||||
->createStructure())
|
||||
|
||||
,booleanAlarmField(FieldBuilder::begin()
|
||||
->setId("valueAlarm_t")
|
||||
->add("active", pvBoolean)
|
||||
->add("falseSeverity", pvInt)
|
||||
->add("trueSeverity", pvInt)
|
||||
->add("changeStateSeverity", pvInt)
|
||||
->createStructure())
|
||||
|
||||
,byteAlarmField(buildValueAlarm(pvByte))
|
||||
,shortAlarmField(buildValueAlarm(pvShort))
|
||||
,intAlarmField(buildValueAlarm(pvInt))
|
||||
,longAlarmField(buildValueAlarm(pvLong))
|
||||
,ubyteAlarmField(buildValueAlarm(pvUByte))
|
||||
,ushortAlarmField(buildValueAlarm(pvUShort))
|
||||
,uintAlarmField(buildValueAlarm(pvUInt))
|
||||
,ulongAlarmField(buildValueAlarm(pvULong))
|
||||
,floatAlarmField(buildValueAlarm(pvFloat))
|
||||
,doubleAlarmField(buildValueAlarm(pvDouble))
|
||||
|
||||
,enumeratedAlarmField(FieldBuilder::begin()
|
||||
->setId("valueAlarm_t")
|
||||
->add("active", pvBoolean)
|
||||
->add("stateSeverity", pvInt)
|
||||
->add("changeStateSeverity", pvInt)
|
||||
->createStructure())
|
||||
{}
|
||||
|
||||
StandardField::~StandardField(){}
|
||||
|
||||
@@ -69,8 +123,8 @@ StructureConstPtr StandardField::createProperties(string id,FieldConstPtr field,
|
||||
while(gotValueAlarm) {
|
||||
if(type==epics::pvData::scalar || type==epics::pvData::scalarArray) {
|
||||
ScalarType scalarType = (type==epics::pvData::scalar) ?
|
||||
static_pointer_cast<const Scalar>(field)->getScalarType() :
|
||||
static_pointer_cast<const ScalarArray>(field)->getElementType();
|
||||
static_pointer_cast<const Scalar>(field)->getScalarType() :
|
||||
static_pointer_cast<const ScalarArray>(field)->getElementType();
|
||||
switch(scalarType) {
|
||||
case pvBoolean: valueAlarm = booleanAlarmField; break;
|
||||
case pvByte: valueAlarm = byteAlarmField; break;
|
||||
@@ -103,7 +157,7 @@ StructureConstPtr StandardField::createProperties(string id,FieldConstPtr field,
|
||||
if(first->getType()==epics::pvData::scalar
|
||||
&& second->getType()==epics::pvData::scalarArray) {
|
||||
ScalarConstPtr scalarFirst = static_pointer_cast<const Scalar>(first);
|
||||
ScalarArrayConstPtr scalarArraySecond =
|
||||
ScalarArrayConstPtr scalarArraySecond =
|
||||
static_pointer_cast<const ScalarArray>(second);
|
||||
if(scalarFirst->getScalarType()==pvInt
|
||||
&& scalarArraySecond->getElementType()==pvString) {
|
||||
@@ -145,361 +199,6 @@ StructureConstPtr StandardField::createProperties(string id,FieldConstPtr field,
|
||||
return fieldCreate->createStructure(id,names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createAlarm() {
|
||||
size_t num = 3;
|
||||
FieldConstPtrArray fields(num);
|
||||
StringArray names(num);
|
||||
names[0] = "severity";
|
||||
names[1] = "status";
|
||||
names[2] = "message";
|
||||
fields[0] = fieldCreate->createScalar(pvInt);
|
||||
fields[1] = fieldCreate->createScalar(pvInt);
|
||||
fields[2] = fieldCreate->createScalar(pvString);
|
||||
alarmField = fieldCreate->createStructure("alarm_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createTimeStamp() {
|
||||
size_t num = 3;
|
||||
FieldConstPtrArray fields(num);
|
||||
StringArray names(num);
|
||||
names[0] = "secondsPastEpoch";
|
||||
names[1] = "nanoseconds";
|
||||
names[2] = "userTag";
|
||||
fields[0] = fieldCreate->createScalar(pvLong);
|
||||
fields[1] = fieldCreate->createScalar(pvInt);
|
||||
fields[2] = fieldCreate->createScalar(pvInt);
|
||||
timeStampField = fieldCreate->createStructure("time_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createDisplay() {
|
||||
size_t num = 5;
|
||||
FieldConstPtrArray fields(num);
|
||||
StringArray names(num);
|
||||
names[0] = "limitLow";
|
||||
names[1] = "limitHigh";
|
||||
names[2] = "description";
|
||||
names[3] = "format";
|
||||
names[4] = "units";
|
||||
fields[0] = fieldCreate->createScalar(pvDouble);
|
||||
fields[1] = fieldCreate->createScalar(pvDouble);
|
||||
fields[2] = fieldCreate->createScalar(pvString);
|
||||
fields[3] = fieldCreate->createScalar(pvString);
|
||||
fields[4] = fieldCreate->createScalar(pvString);
|
||||
displayField = fieldCreate->createStructure("display_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createControl() {
|
||||
size_t num = 3;
|
||||
FieldConstPtrArray fields(num);
|
||||
StringArray names(num);
|
||||
names[0] = "limitLow";
|
||||
names[1] = "limitHigh";
|
||||
names[2] = "minStep";
|
||||
fields[0] = fieldCreate->createScalar(pvDouble);
|
||||
fields[1] = fieldCreate->createScalar(pvDouble);
|
||||
fields[2] = fieldCreate->createScalar(pvDouble);
|
||||
controlField = fieldCreate->createStructure("control_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createBooleanAlarm() {
|
||||
size_t numFields = 4;
|
||||
FieldConstPtrArray fields(numFields);
|
||||
StringArray names(numFields);
|
||||
names[0] = "active";
|
||||
names[1] = "falseSeverity";
|
||||
names[2] = "trueSeverity";
|
||||
names[3] = "changeStateSeverity";
|
||||
fields[0] = fieldCreate->createScalar(pvBoolean);
|
||||
fields[1] = fieldCreate->createScalar(pvInt);
|
||||
fields[2] = fieldCreate->createScalar(pvInt);
|
||||
fields[3] = fieldCreate->createScalar(pvInt);
|
||||
booleanAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createByteAlarm() {
|
||||
size_t numFields = 10;
|
||||
FieldConstPtrArray fields(numFields);
|
||||
StringArray names(numFields);
|
||||
names[0] = "active";
|
||||
names[1] = "lowAlarmLimit";
|
||||
names[2] = "lowWarningLimit";
|
||||
names[3] = "highWarningLimit";
|
||||
names[4] = "highAlarmLimit";
|
||||
names[5] = "lowAlarmSeverity";
|
||||
names[6] = "lowWarningSeverity";
|
||||
names[7] = "highWarningSeverity";
|
||||
names[8] = "highAlarmSeverity";
|
||||
names[9] = "hysteresis";
|
||||
fields[0] = fieldCreate->createScalar(pvBoolean);
|
||||
fields[1] = fieldCreate->createScalar(pvByte);
|
||||
fields[2] = fieldCreate->createScalar(pvByte);
|
||||
fields[3] = fieldCreate->createScalar(pvByte);
|
||||
fields[4] = fieldCreate->createScalar(pvByte);
|
||||
fields[5] = fieldCreate->createScalar(pvInt);
|
||||
fields[6] = fieldCreate->createScalar(pvInt);
|
||||
fields[7] = fieldCreate->createScalar(pvInt);
|
||||
fields[8] = fieldCreate->createScalar(pvInt);
|
||||
fields[9] = fieldCreate->createScalar(pvByte);
|
||||
byteAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createShortAlarm() {
|
||||
size_t numFields = 10;
|
||||
FieldConstPtrArray fields(numFields);
|
||||
StringArray names(numFields);
|
||||
names[0] = "active";
|
||||
names[1] = "lowAlarmLimit";
|
||||
names[2] = "lowWarningLimit";
|
||||
names[3] = "highWarningLimit";
|
||||
names[4] = "highAlarmLimit";
|
||||
names[5] = "lowAlarmSeverity";
|
||||
names[6] = "lowWarningSeverity";
|
||||
names[7] = "highWarningSeverity";
|
||||
names[8] = "highAlarmSeverity";
|
||||
names[9] = "hysteresis";
|
||||
fields[0] = fieldCreate->createScalar(pvBoolean);
|
||||
fields[1] = fieldCreate->createScalar(pvShort);
|
||||
fields[2] = fieldCreate->createScalar(pvShort);
|
||||
fields[3] = fieldCreate->createScalar(pvShort);
|
||||
fields[4] = fieldCreate->createScalar(pvShort);
|
||||
fields[5] = fieldCreate->createScalar(pvInt);
|
||||
fields[6] = fieldCreate->createScalar(pvInt);
|
||||
fields[7] = fieldCreate->createScalar(pvInt);
|
||||
fields[8] = fieldCreate->createScalar(pvInt);
|
||||
fields[9] = fieldCreate->createScalar(pvShort);
|
||||
shortAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createIntAlarm() {
|
||||
size_t numFields = 10;
|
||||
FieldConstPtrArray fields(numFields);
|
||||
StringArray names(numFields);
|
||||
names[0] = "active";
|
||||
names[1] = "lowAlarmLimit";
|
||||
names[2] = "lowWarningLimit";
|
||||
names[3] = "highWarningLimit";
|
||||
names[4] = "highAlarmLimit";
|
||||
names[5] = "lowAlarmSeverity";
|
||||
names[6] = "lowWarningSeverity";
|
||||
names[7] = "highWarningSeverity";
|
||||
names[8] = "highAlarmSeverity";
|
||||
names[9] = "hysteresis";
|
||||
fields[0] = fieldCreate->createScalar(pvBoolean);
|
||||
fields[1] = fieldCreate->createScalar(pvInt);
|
||||
fields[2] = fieldCreate->createScalar(pvInt);
|
||||
fields[3] = fieldCreate->createScalar(pvInt);
|
||||
fields[4] = fieldCreate->createScalar(pvInt);
|
||||
fields[5] = fieldCreate->createScalar(pvInt);
|
||||
fields[6] = fieldCreate->createScalar(pvInt);
|
||||
fields[7] = fieldCreate->createScalar(pvInt);
|
||||
fields[8] = fieldCreate->createScalar(pvInt);
|
||||
fields[9] = fieldCreate->createScalar(pvInt);
|
||||
intAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createLongAlarm() {
|
||||
size_t numFields = 10;
|
||||
FieldConstPtrArray fields(numFields);
|
||||
StringArray names(numFields);
|
||||
names[0] = "active";
|
||||
names[1] = "lowAlarmLimit";
|
||||
names[2] = "lowWarningLimit";
|
||||
names[3] = "highWarningLimit";
|
||||
names[4] = "highAlarmLimit";
|
||||
names[5] = "lowAlarmSeverity";
|
||||
names[6] = "lowWarningSeverity";
|
||||
names[7] = "highWarningSeverity";
|
||||
names[8] = "highAlarmSeverity";
|
||||
names[9] = "hysteresis";
|
||||
fields[0] = fieldCreate->createScalar(pvBoolean);
|
||||
fields[1] = fieldCreate->createScalar(pvLong);
|
||||
fields[2] = fieldCreate->createScalar(pvLong);
|
||||
fields[3] = fieldCreate->createScalar(pvLong);
|
||||
fields[4] = fieldCreate->createScalar(pvLong);
|
||||
fields[5] = fieldCreate->createScalar(pvInt);
|
||||
fields[6] = fieldCreate->createScalar(pvInt);
|
||||
fields[7] = fieldCreate->createScalar(pvInt);
|
||||
fields[8] = fieldCreate->createScalar(pvInt);
|
||||
fields[9] = fieldCreate->createScalar(pvLong);
|
||||
longAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createUByteAlarm() {
|
||||
size_t numFields = 10;
|
||||
FieldConstPtrArray fields(numFields);
|
||||
StringArray names(numFields);
|
||||
names[0] = "active";
|
||||
names[1] = "lowAlarmLimit";
|
||||
names[2] = "lowWarningLimit";
|
||||
names[3] = "highWarningLimit";
|
||||
names[4] = "highAlarmLimit";
|
||||
names[5] = "lowAlarmSeverity";
|
||||
names[6] = "lowWarningSeverity";
|
||||
names[7] = "highWarningSeverity";
|
||||
names[8] = "highAlarmSeverity";
|
||||
names[9] = "hysteresis";
|
||||
fields[0] = fieldCreate->createScalar(pvBoolean);
|
||||
fields[1] = fieldCreate->createScalar(pvUByte);
|
||||
fields[2] = fieldCreate->createScalar(pvUByte);
|
||||
fields[3] = fieldCreate->createScalar(pvUByte);
|
||||
fields[4] = fieldCreate->createScalar(pvUByte);
|
||||
fields[5] = fieldCreate->createScalar(pvInt);
|
||||
fields[6] = fieldCreate->createScalar(pvInt);
|
||||
fields[7] = fieldCreate->createScalar(pvInt);
|
||||
fields[8] = fieldCreate->createScalar(pvInt);
|
||||
fields[9] = fieldCreate->createScalar(pvUByte);
|
||||
ubyteAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createUShortAlarm() {
|
||||
size_t numFields = 10;
|
||||
FieldConstPtrArray fields(numFields);
|
||||
StringArray names(numFields);
|
||||
names[0] = "active";
|
||||
names[1] = "lowAlarmLimit";
|
||||
names[2] = "lowWarningLimit";
|
||||
names[3] = "highWarningLimit";
|
||||
names[4] = "highAlarmLimit";
|
||||
names[5] = "lowAlarmSeverity";
|
||||
names[6] = "lowWarningSeverity";
|
||||
names[7] = "highWarningSeverity";
|
||||
names[8] = "highAlarmSeverity";
|
||||
names[9] = "hysteresis";
|
||||
fields[0] = fieldCreate->createScalar(pvBoolean);
|
||||
fields[1] = fieldCreate->createScalar(pvUShort);
|
||||
fields[2] = fieldCreate->createScalar(pvUShort);
|
||||
fields[3] = fieldCreate->createScalar(pvUShort);
|
||||
fields[4] = fieldCreate->createScalar(pvUShort);
|
||||
fields[5] = fieldCreate->createScalar(pvInt);
|
||||
fields[6] = fieldCreate->createScalar(pvInt);
|
||||
fields[7] = fieldCreate->createScalar(pvInt);
|
||||
fields[8] = fieldCreate->createScalar(pvInt);
|
||||
fields[9] = fieldCreate->createScalar(pvUShort);
|
||||
ushortAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createUIntAlarm() {
|
||||
size_t numFields = 10;
|
||||
FieldConstPtrArray fields(numFields);
|
||||
StringArray names(numFields);
|
||||
names[0] = "active";
|
||||
names[1] = "lowAlarmLimit";
|
||||
names[2] = "lowWarningLimit";
|
||||
names[3] = "highWarningLimit";
|
||||
names[4] = "highAlarmLimit";
|
||||
names[5] = "lowAlarmSeverity";
|
||||
names[6] = "lowWarningSeverity";
|
||||
names[7] = "highWarningSeverity";
|
||||
names[8] = "highAlarmSeverity";
|
||||
names[9] = "hysteresis";
|
||||
fields[0] = fieldCreate->createScalar(pvBoolean);
|
||||
fields[1] = fieldCreate->createScalar(pvUInt);
|
||||
fields[2] = fieldCreate->createScalar(pvUInt);
|
||||
fields[3] = fieldCreate->createScalar(pvUInt);
|
||||
fields[4] = fieldCreate->createScalar(pvUInt);
|
||||
fields[5] = fieldCreate->createScalar(pvInt);
|
||||
fields[6] = fieldCreate->createScalar(pvInt);
|
||||
fields[7] = fieldCreate->createScalar(pvInt);
|
||||
fields[8] = fieldCreate->createScalar(pvInt);
|
||||
fields[9] = fieldCreate->createScalar(pvUInt);
|
||||
uintAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createULongAlarm() {
|
||||
size_t numFields = 10;
|
||||
FieldConstPtrArray fields(numFields);
|
||||
StringArray names(numFields);
|
||||
names[0] = "active";
|
||||
names[1] = "lowAlarmLimit";
|
||||
names[2] = "lowWarningLimit";
|
||||
names[3] = "highWarningLimit";
|
||||
names[4] = "highAlarmLimit";
|
||||
names[5] = "lowAlarmSeverity";
|
||||
names[6] = "lowWarningSeverity";
|
||||
names[7] = "highWarningSeverity";
|
||||
names[8] = "highAlarmSeverity";
|
||||
names[9] = "hysteresis";
|
||||
fields[0] = fieldCreate->createScalar(pvBoolean);
|
||||
fields[1] = fieldCreate->createScalar(pvULong);
|
||||
fields[2] = fieldCreate->createScalar(pvULong);
|
||||
fields[3] = fieldCreate->createScalar(pvULong);
|
||||
fields[4] = fieldCreate->createScalar(pvULong);
|
||||
fields[5] = fieldCreate->createScalar(pvInt);
|
||||
fields[6] = fieldCreate->createScalar(pvInt);
|
||||
fields[7] = fieldCreate->createScalar(pvInt);
|
||||
fields[8] = fieldCreate->createScalar(pvInt);
|
||||
fields[9] = fieldCreate->createScalar(pvULong);
|
||||
ulongAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createFloatAlarm() {
|
||||
size_t numFields = 10;
|
||||
FieldConstPtrArray fields(numFields);
|
||||
StringArray names(numFields);
|
||||
names[0] = "active";
|
||||
names[1] = "lowAlarmLimit";
|
||||
names[2] = "lowWarningLimit";
|
||||
names[3] = "highWarningLimit";
|
||||
names[4] = "highAlarmLimit";
|
||||
names[5] = "lowAlarmSeverity";
|
||||
names[6] = "lowWarningSeverity";
|
||||
names[7] = "highWarningSeverity";
|
||||
names[8] = "highAlarmSeverity";
|
||||
names[9] = "hysteresis";
|
||||
fields[0] = fieldCreate->createScalar(pvBoolean);
|
||||
fields[1] = fieldCreate->createScalar(pvFloat);
|
||||
fields[2] = fieldCreate->createScalar(pvFloat);
|
||||
fields[3] = fieldCreate->createScalar(pvFloat);
|
||||
fields[4] = fieldCreate->createScalar(pvFloat);
|
||||
fields[5] = fieldCreate->createScalar(pvInt);
|
||||
fields[6] = fieldCreate->createScalar(pvInt);
|
||||
fields[7] = fieldCreate->createScalar(pvInt);
|
||||
fields[8] = fieldCreate->createScalar(pvInt);
|
||||
fields[9] = fieldCreate->createScalar(pvFloat);
|
||||
floatAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createDoubleAlarm() {
|
||||
size_t numFields = 10;
|
||||
FieldConstPtrArray fields(numFields);
|
||||
StringArray names(numFields);
|
||||
names[0] = "active";
|
||||
names[1] = "lowAlarmLimit";
|
||||
names[2] = "lowWarningLimit";
|
||||
names[3] = "highWarningLimit";
|
||||
names[4] = "highAlarmLimit";
|
||||
names[5] = "lowAlarmSeverity";
|
||||
names[6] = "lowWarningSeverity";
|
||||
names[7] = "highWarningSeverity";
|
||||
names[8] = "highAlarmSeverity";
|
||||
names[9] = "hysteresis";
|
||||
fields[0] = fieldCreate->createScalar(pvBoolean);
|
||||
fields[1] = fieldCreate->createScalar(pvDouble);
|
||||
fields[2] = fieldCreate->createScalar(pvDouble);
|
||||
fields[3] = fieldCreate->createScalar(pvDouble);
|
||||
fields[4] = fieldCreate->createScalar(pvDouble);
|
||||
fields[5] = fieldCreate->createScalar(pvInt);
|
||||
fields[6] = fieldCreate->createScalar(pvInt);
|
||||
fields[7] = fieldCreate->createScalar(pvInt);
|
||||
fields[8] = fieldCreate->createScalar(pvInt);
|
||||
fields[9] = fieldCreate->createScalar(pvDouble);
|
||||
doubleAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
|
||||
}
|
||||
|
||||
void StandardField::createEnumeratedAlarm() {
|
||||
size_t numFields = 3;
|
||||
FieldConstPtrArray fields(numFields);
|
||||
StringArray names(numFields);
|
||||
names[0] = "active";
|
||||
names[1] = "stateSeverity";
|
||||
names[2] = "changeStateSeverity";
|
||||
fields[0] = fieldCreate->createScalar(pvBoolean);
|
||||
fields[1] = fieldCreate->createScalarArray(pvInt);
|
||||
fields[2] = fieldCreate->createScalar(pvInt);
|
||||
enumeratedAlarmField = fieldCreate->createStructure("valueAlarm_t",names,fields);
|
||||
}
|
||||
|
||||
|
||||
StructureConstPtr StandardField::scalar(
|
||||
ScalarType type,string const &properties)
|
||||
{
|
||||
@@ -564,98 +263,21 @@ StructureConstPtr StandardField::enumerated(string const &properties)
|
||||
return createProperties("epics:nt/NTEnum:1.0",field,properties);
|
||||
}
|
||||
|
||||
StructureConstPtr StandardField::alarm()
|
||||
{
|
||||
return alarmField;
|
||||
}
|
||||
static StandardFieldPtr *stdFieldGbl;
|
||||
|
||||
StructureConstPtr StandardField::timeStamp()
|
||||
{
|
||||
return timeStampField;
|
||||
}
|
||||
static epicsThreadOnceId stdFieldGblOnce = EPICS_THREAD_ONCE_INIT;
|
||||
|
||||
StructureConstPtr StandardField::display()
|
||||
void StandardField::once(void*)
|
||||
{
|
||||
return displayField;
|
||||
}
|
||||
|
||||
StructureConstPtr StandardField::control()
|
||||
{
|
||||
return controlField;
|
||||
}
|
||||
|
||||
StructureConstPtr StandardField::booleanAlarm()
|
||||
{
|
||||
return booleanAlarmField;
|
||||
}
|
||||
|
||||
StructureConstPtr StandardField::byteAlarm()
|
||||
{
|
||||
return byteAlarmField;
|
||||
}
|
||||
|
||||
StructureConstPtr StandardField::ubyteAlarm()
|
||||
{
|
||||
return ubyteAlarmField;
|
||||
}
|
||||
|
||||
StructureConstPtr StandardField::shortAlarm()
|
||||
{
|
||||
return shortAlarmField;
|
||||
}
|
||||
|
||||
StructureConstPtr StandardField::ushortAlarm()
|
||||
{
|
||||
return ushortAlarmField;
|
||||
}
|
||||
|
||||
StructureConstPtr StandardField::intAlarm()
|
||||
{
|
||||
return intAlarmField;
|
||||
}
|
||||
|
||||
StructureConstPtr StandardField::uintAlarm()
|
||||
{
|
||||
return uintAlarmField;
|
||||
}
|
||||
|
||||
StructureConstPtr StandardField::longAlarm()
|
||||
{
|
||||
return longAlarmField;
|
||||
}
|
||||
|
||||
StructureConstPtr StandardField::ulongAlarm()
|
||||
{
|
||||
return ulongAlarmField;
|
||||
}
|
||||
|
||||
StructureConstPtr StandardField::floatAlarm()
|
||||
{
|
||||
return floatAlarmField;
|
||||
}
|
||||
|
||||
StructureConstPtr StandardField::doubleAlarm()
|
||||
{
|
||||
return doubleAlarmField;
|
||||
}
|
||||
|
||||
StructureConstPtr StandardField::enumeratedAlarm()
|
||||
{
|
||||
return enumeratedAlarmField;
|
||||
stdFieldGbl = new StandardFieldPtr;
|
||||
stdFieldGbl->reset(new StandardField);
|
||||
}
|
||||
|
||||
const StandardFieldPtr &StandardField::getStandardField()
|
||||
{
|
||||
static StandardFieldPtr standardFieldCreate;
|
||||
static Mutex mutex;
|
||||
Lock xx(mutex);
|
||||
epicsThreadOnce(&stdFieldGblOnce, &StandardField::once, 0);
|
||||
|
||||
if(standardFieldCreate.get()==0)
|
||||
{
|
||||
standardFieldCreate = StandardFieldPtr(new StandardField());
|
||||
standardFieldCreate->init();
|
||||
}
|
||||
return standardFieldCreate;
|
||||
return *stdFieldGbl;
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
StandardPVField::StandardPVField()
|
||||
: standardField(getStandardField()),
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace ScalarTypeFunc {
|
||||
if(type>=pvBoolean && type<=pvDouble) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static const char* names[] = {
|
||||
"boolean",
|
||||
"byte", "short", "int", "long",
|
||||
|
||||
@@ -3,42 +3,552 @@
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#if defined(__linux__) || defined(__APPLE__)
|
||||
#include <unistd.h>
|
||||
#define HAVE_ISATTY
|
||||
#endif
|
||||
|
||||
#include <deque>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/pvIntrospect.h>
|
||||
#include <epicsTime.h>
|
||||
#include <epicsString.h>
|
||||
|
||||
using std::string;
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/bitSet.h>
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/json.h>
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
namespace format
|
||||
{
|
||||
static int indent_index = std::ios_base::xalloc();
|
||||
static int indent_index = std::ios_base::xalloc();
|
||||
|
||||
long& indent_value(std::ios_base& ios)
|
||||
{
|
||||
return ios.iword(indent_index);
|
||||
long& indent_value(std::ios_base& ios)
|
||||
{
|
||||
return ios.iword(indent_index);
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, indent_level const& indent)
|
||||
{
|
||||
indent_value(os) = indent.level;
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, indent const&)
|
||||
{
|
||||
long il = indent_value(os);
|
||||
for(long i=0, spaces = il * 4; i<spaces; i++)
|
||||
os.put(' ');
|
||||
return os;
|
||||
}
|
||||
|
||||
array_at_internal operator<<(std::ostream& str, array_at const& manip)
|
||||
{
|
||||
return array_at_internal(manip.index, str);
|
||||
}
|
||||
}
|
||||
|
||||
}} //epics::pvData
|
||||
namespace {
|
||||
using namespace epics::pvData;
|
||||
|
||||
bool useEscapes(std::ostream& strm) {
|
||||
FILE *fp = 0;
|
||||
// TODO: a better way to do this...
|
||||
if(&std::cout==&strm)
|
||||
fp = stdout;
|
||||
if(&std::cerr==&strm)
|
||||
fp = stderr;
|
||||
if(!fp) return false;
|
||||
#ifdef HAVE_ISATTY
|
||||
// check $TERM as well?
|
||||
return isatty(fileno(fp))==1;
|
||||
#else
|
||||
// TODO: in theory windows 10 can be made to understand escapes as well
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void printAlarmTx(std::ostream& strm, const PVStructure& sub)
|
||||
{
|
||||
PVScalar::const_shared_pointer pvSeverity(sub.getSubField<PVInt>("severity"));
|
||||
PVScalar::const_shared_pointer pvStatus(sub.getSubField<PVInt>("status"));
|
||||
PVString::const_shared_pointer pvMessage(sub.getSubField<PVString>("message"));
|
||||
|
||||
switch(pvSeverity ? pvSeverity->getAs<int32>() : 0) {
|
||||
case 0: return; // nothing more to do here...
|
||||
case 1: strm<<"MINOR "; break;
|
||||
case 2: strm<<"MAJOR "; break;
|
||||
case 3: strm<<"INVALID "; break;
|
||||
case 4: strm<<"UNDEFINED "; break;
|
||||
default: strm<<pvSeverity->getAs<int32>()<<' ';
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, indent_level const& indent)
|
||||
{
|
||||
indent_value(os) = indent.level;
|
||||
return os;
|
||||
}
|
||||
switch(pvStatus ? pvStatus->getAs<int32>() : 0) {
|
||||
case 0: break;
|
||||
case 1: strm<<"DEVICE "; break;
|
||||
case 2: strm<<"DRIVER "; break;
|
||||
case 3: strm<<"RECORD "; break;
|
||||
case 4: strm<<"DB "; break;
|
||||
case 5: strm<<"CONF "; break;
|
||||
case 6: strm<<"UNDEFINED "; break;
|
||||
case 7: strm<<"CLIENT "; break;
|
||||
default: strm<<pvStatus->getAs<int32>()<<' ';
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, indent const&)
|
||||
{
|
||||
long il = indent_value(os);
|
||||
std::size_t spaces = static_cast<std::size_t>(il) * 4;
|
||||
return os << string(spaces, ' ');
|
||||
}
|
||||
if(pvMessage && !pvMessage->get().empty())
|
||||
strm<<pvMessage->get()<<' ';
|
||||
}
|
||||
|
||||
array_at_internal operator<<(std::ostream& str, array_at const& manip)
|
||||
{
|
||||
return array_at_internal(manip.index, str);
|
||||
}
|
||||
};
|
||||
|
||||
}}
|
||||
void printAlarmT(std::ostream& strm, const PVStructure& top)
|
||||
{
|
||||
PVStructure::const_shared_pointer sub(top.getSubField<PVStructure>("alarm"));
|
||||
if(sub)
|
||||
printAlarmTx(strm, *sub);
|
||||
}
|
||||
|
||||
void printTimeTx(std::ostream& strm, const PVStructure& tsubop)
|
||||
{
|
||||
char timeText[32];
|
||||
epicsTimeStamp epicsTS;
|
||||
|
||||
PVScalar::const_shared_pointer secf(tsubop.getSubField<PVScalar>("secondsPastEpoch")),
|
||||
nsecf(tsubop.getSubField<PVScalar>("nanoseconds")),
|
||||
tagf(tsubop.getSubField<PVScalar>("userTag"));
|
||||
|
||||
epicsTS.secPastEpoch = secf ? secf->getAs<int64>() : 0;
|
||||
epicsTS.nsec = nsecf ? nsecf->getAs<int32>() : 0;
|
||||
|
||||
if(epicsTS.secPastEpoch > POSIX_TIME_AT_EPICS_EPOCH)
|
||||
epicsTS.secPastEpoch -= POSIX_TIME_AT_EPICS_EPOCH;
|
||||
else
|
||||
epicsTS.secPastEpoch = 0;
|
||||
|
||||
epicsTimeToStrftime(timeText, sizeof(timeText), "%Y-%m-%d %H:%M:%S.%03f", &epicsTS);
|
||||
strm <<std::setw(24) <<std::left <<timeText <<' ';
|
||||
if (tagf) {
|
||||
int64 tagv = tagf->getAs<int64>();
|
||||
if(tagv)
|
||||
strm << tagv << ' ';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void printTimeT(std::ostream& strm, const PVStructure& top)
|
||||
{
|
||||
PVStructure::const_shared_pointer sub(top.getSubField<PVStructure>("timeStamp"));
|
||||
if(sub)
|
||||
printTimeTx(strm, *sub);
|
||||
}
|
||||
|
||||
bool printEnumT(std::ostream& strm, const PVStructure& top, bool fromtop)
|
||||
{
|
||||
PVStructure::const_shared_pointer value;
|
||||
if(fromtop) {
|
||||
value = top.getSubField<PVStructure>("value");
|
||||
} else {
|
||||
value = std::tr1::static_pointer_cast<const PVStructure>(top.shared_from_this());
|
||||
}
|
||||
PVScalar::const_shared_pointer idx(value->getSubField<PVScalar>("index"));
|
||||
PVStringArray::const_shared_pointer choices(value->getSubField<PVStringArray>("choices"));
|
||||
if(!idx || !choices) return false;
|
||||
|
||||
if(fromtop) {
|
||||
strm<<format::indent();
|
||||
printTimeT(strm, top);
|
||||
printAlarmT(strm, top);
|
||||
}
|
||||
|
||||
PVStringArray::const_svector ch(choices->view());
|
||||
uint32 I = idx->getAs<uint32>();
|
||||
strm<<'('<<I<<')';
|
||||
if(I>=ch.size()) {
|
||||
strm<<" <undefined>";
|
||||
} else {
|
||||
strm<<' '<<maybeQuote(ch[I]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void csvEscape(std::string& S)
|
||||
{
|
||||
// concise, not particularly efficient...
|
||||
std::string temp(escape(S).style(escape::CSV).str());
|
||||
if(S.find_first_of("\" ,\\")!=S.npos) {// only quote if necessary (stupid Excel)
|
||||
std::string temp2;
|
||||
temp2.reserve(temp.size()+2);
|
||||
temp2.push_back('\"');
|
||||
temp2 += temp;
|
||||
temp2.push_back('\"');
|
||||
temp2.swap(temp);
|
||||
}
|
||||
S.swap(temp);
|
||||
}
|
||||
|
||||
bool printTable(std::ostream& strm, const PVStructure& top)
|
||||
{
|
||||
PVStructure::const_shared_pointer cf(top.getSubField<PVStructure>("value"));
|
||||
if(!cf) return false;
|
||||
|
||||
{
|
||||
const FieldConstPtrArray& fields = cf->getStructure()->getFields();
|
||||
if(fields.empty()) return false;
|
||||
for(size_t i=0, N=fields.size(); i<N; i++) {
|
||||
if(fields[i]->getType()!=scalarArray)
|
||||
return false; // cowardly refusing to handle anything else...
|
||||
}
|
||||
}
|
||||
|
||||
// maybe output a line with meta-data
|
||||
{
|
||||
bool havets = !!top.getSubField("timeStamp");
|
||||
bool haveal = !!top.getSubField("alarm");
|
||||
if(havets || haveal)
|
||||
strm<<format::indent();
|
||||
if(havets) {
|
||||
printTimeT(strm, top);
|
||||
strm<<' ';
|
||||
}
|
||||
if(haveal) {
|
||||
printAlarmT(strm, top);
|
||||
strm<<' ';
|
||||
}
|
||||
strm<<'\n';
|
||||
}
|
||||
|
||||
PVStringArray::svector labels;
|
||||
{
|
||||
PVStringArray::const_shared_pointer lf(top.getSubField<PVStringArray>("labels"));
|
||||
if(lf) {
|
||||
PVStringArray::const_svector L(lf->view());
|
||||
labels = thaw(L);
|
||||
}
|
||||
}
|
||||
|
||||
const PVFieldPtrArray& columns = cf->getPVFields();
|
||||
std::vector<shared_vector<std::string> > coldat(columns.size());
|
||||
|
||||
std::vector<size_t> widths(columns.size());
|
||||
labels.reserve(columns.size());
|
||||
|
||||
size_t nrows = size_t(-1);
|
||||
|
||||
for(size_t i=0, N=columns.size(); i<N; i++) {
|
||||
if(i>=labels.size()) {
|
||||
labels.push_back(cf->getStructure()->getFieldName(i));
|
||||
} else {
|
||||
csvEscape(labels[i]);
|
||||
}
|
||||
widths[i] = labels[i].size();
|
||||
|
||||
{
|
||||
shared_vector<const std::string> ro;
|
||||
static_cast<const PVScalarArray*>(columns[i].get())->getAs(ro);
|
||||
coldat[i] = thaw(ro);
|
||||
}
|
||||
|
||||
// truncate if some columns are longer than others
|
||||
nrows = std::min(nrows, coldat[i].size());
|
||||
|
||||
for(size_t j=0, M=coldat[i].size(); j<M; j++) {
|
||||
csvEscape(coldat[i][j]);
|
||||
widths[i] = std::max(widths[i], coldat[i][j].size());
|
||||
}
|
||||
}
|
||||
|
||||
// output header line
|
||||
strm<<format::indent();
|
||||
for(size_t c=0, N=coldat.size(); c<N; c++) {
|
||||
strm<<std::setw(widths[c])<<std::right<<labels[c];
|
||||
if(c+1!=N) {
|
||||
strm<<' ';
|
||||
}
|
||||
}
|
||||
strm<<'\n';
|
||||
|
||||
for(size_t r=0; r<nrows; r++) {
|
||||
strm<<format::indent();
|
||||
for(size_t c=0, N=coldat.size(); c<N; c++) {
|
||||
strm<<std::setw(widths[c])<<std::right<<coldat[c][r];
|
||||
if(c+1!=N)
|
||||
strm<<' ';
|
||||
}
|
||||
strm<<'\n';
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void expandBS(const PVStructure& top, BitSet& mask, bool parents) {
|
||||
if(mask.get(0)) { // special handling because getSubField(0) not allowed
|
||||
// wildcard
|
||||
for(size_t idx=1, N=top.getNumberFields(); idx<N; idx++) {
|
||||
mask.set(idx);
|
||||
}
|
||||
|
||||
} else {
|
||||
for(int32 idx = mask.nextSetBit(0), N=top.getNumberFields(); idx>=0 && idx<N; idx=mask.nextSetBit(idx+1)) {
|
||||
PVField::const_shared_pointer fld = top.getSubFieldT(idx);
|
||||
|
||||
// look forward and mark all children
|
||||
for(size_t i=idx+1, N=fld->getNextFieldOffset(); i<N; i++)
|
||||
mask.set(i);
|
||||
|
||||
if(parents) {
|
||||
// look back and mark all parents
|
||||
// we've already stepped past all parents so siblings will not be automatically marked
|
||||
for(const PVStructure *parent = fld->getParent(); parent; parent = parent->getParent()) {
|
||||
mask.set(parent->getFieldOffset());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
void printRaw(std::ostream& strm, const PVStructure::Formatter& format, const PVStructure& cur)
|
||||
{
|
||||
typedef std::deque<std::pair<const PVField*, size_t> > todo_t;
|
||||
todo_t todo;
|
||||
BitSet show, highlight;
|
||||
|
||||
const long orig_indent = format::indent_value(strm);
|
||||
|
||||
{
|
||||
if(format.xshow)
|
||||
show = *format.xshow;
|
||||
else
|
||||
show.set(0);
|
||||
|
||||
if(format.xhighlight)
|
||||
highlight = *format.xhighlight;
|
||||
|
||||
expandBS(format.xtop, show, true);
|
||||
expandBS(format.xtop, highlight, false);
|
||||
highlight &= show; // can't highlight hidden fields (paranoia)
|
||||
}
|
||||
|
||||
if(!show.get(0)) return; // nothing to do here...
|
||||
|
||||
todo.push_front(std::make_pair(&format.xtop, orig_indent));
|
||||
|
||||
while(!todo.empty()) {
|
||||
todo_t::value_type cur(todo.front());
|
||||
todo.pop_front();
|
||||
|
||||
format::indent_value(strm) = cur.second;
|
||||
|
||||
bool hl = highlight.get(cur.first->getFieldOffset()) && format.xmode==PVStructure::Formatter::ANSI;
|
||||
if(hl)
|
||||
strm<<"\x1b[1m"; // Bold
|
||||
|
||||
switch(cur.first->getField()->getType()) {
|
||||
case structure: {
|
||||
const PVStructure* str = static_cast<const PVStructure*>(cur.first);
|
||||
|
||||
const PVFieldPtrArray& flds = str->getPVFields();
|
||||
|
||||
for(PVFieldPtrArray::const_reverse_iterator it(flds.rbegin()), end(flds.rend()); it!=end; ++it) {
|
||||
const PVField *fld = (*it).get();
|
||||
if(!show.get(fld->getFieldOffset())) continue;
|
||||
|
||||
todo.push_front(std::make_pair(fld, cur.second+1));
|
||||
}
|
||||
|
||||
std::string id(cur.first->getField()->getID());
|
||||
|
||||
strm<<format::indent()<<id<<' '<<cur.first->getFieldName();
|
||||
if(id=="alarm_t") {
|
||||
strm.put(' ');
|
||||
printAlarmTx(strm, *str);
|
||||
} else if(id=="time_t") {
|
||||
strm.put(' ');
|
||||
printTimeTx(strm, *str);
|
||||
} else if(id=="enum_t") {
|
||||
strm.put(' ');
|
||||
printEnumT(strm, *str, false);
|
||||
}
|
||||
strm.put('\n');
|
||||
}
|
||||
break;
|
||||
case scalar:
|
||||
case scalarArray:
|
||||
strm<<format::indent()<<cur.first->getField()->getID()<<' '<<cur.first->getFieldName()
|
||||
<<' '<<*cur.first
|
||||
<<'\n';
|
||||
break;
|
||||
case structureArray:
|
||||
case union_:
|
||||
case unionArray:
|
||||
strm<<*cur.first;
|
||||
break;
|
||||
}
|
||||
|
||||
if(hl)
|
||||
strm<<"\x1b[0m"; // reset
|
||||
}
|
||||
|
||||
format::indent_value(strm) = orig_indent;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& strm, const PVStructure::Formatter& format)
|
||||
{
|
||||
if(format.xfmt==PVStructure::Formatter::JSON) {
|
||||
JSONPrintOptions opts;
|
||||
opts.multiLine = false;
|
||||
#if EPICS_VERSION_INT>=VERSION_INT(7,0,6,1)
|
||||
opts.json5 = true;
|
||||
#endif
|
||||
printJSON(strm, format.xtop, format.xshow ? *format.xshow : BitSet().set(0), opts);
|
||||
strm<<'\n';
|
||||
return strm;
|
||||
|
||||
} else if(format.xfmt==PVStructure::Formatter::NT) {
|
||||
std::string id(format.xtop.getStructure()->getID()),
|
||||
idprefix(id.substr(0, id.find_first_of('.')));
|
||||
|
||||
// NTTable
|
||||
if(idprefix=="epics:nt/NTTable:1") {
|
||||
if(printTable(strm, format.xtop))
|
||||
return strm;
|
||||
} else {
|
||||
//NTScalar, NTScalarArray, NTEnum, or anything with '.value'
|
||||
|
||||
PVField::const_shared_pointer value(format.xtop.getSubField("value"));
|
||||
if(value) {
|
||||
switch(value->getField()->getType()) {
|
||||
case scalar:
|
||||
strm<<format::indent();
|
||||
printTimeT(strm, format.xtop);
|
||||
strm<<std::setprecision(6)<<*static_cast<const PVScalar*>(value.get())<<' ';
|
||||
printAlarmT(strm, format.xtop);
|
||||
strm<<'\n';
|
||||
return strm;
|
||||
|
||||
case scalarArray:
|
||||
strm<<format::indent();
|
||||
printTimeT(strm, format.xtop);
|
||||
printAlarmT(strm, format.xtop);
|
||||
strm<<std::setprecision(6)<<*static_cast<const PVScalarArray*>(value.get())<<'\n';
|
||||
return strm;
|
||||
|
||||
case structure:
|
||||
if(printEnumT(strm, format.xtop, true)) {
|
||||
strm<<'\n';
|
||||
return strm;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// fall through unhandled as Raw
|
||||
|
||||
PVStructure::Formatter format2(format);
|
||||
|
||||
if(format2.xmode==PVStructure::Formatter::Auto)
|
||||
format2.xmode = useEscapes(strm) ? PVStructure::Formatter::ANSI : PVStructure::Formatter::Plain;
|
||||
|
||||
printRaw(strm, format2, format.xtop);
|
||||
|
||||
return strm;
|
||||
}
|
||||
|
||||
static char hexdigit(char c) {
|
||||
c &= 0xf;
|
||||
if(c<9)
|
||||
return '0'+c;
|
||||
else
|
||||
return 'A'+c-10;
|
||||
}
|
||||
|
||||
escape::~escape() {}
|
||||
|
||||
std::string escape::str() const
|
||||
{
|
||||
std::ostringstream strm;
|
||||
strm<<(*this);
|
||||
return strm.str();
|
||||
}
|
||||
|
||||
epicsShareFunc
|
||||
std::ostream& operator<<(std::ostream& strm, const escape& Q)
|
||||
{
|
||||
for(size_t pos = 0, len = Q.orig.size(); pos < len; pos++) {
|
||||
const char C = Q.orig[pos];
|
||||
char quote = '\\', next;
|
||||
// compare me with epicsStrnEscapedFromRaw()
|
||||
switch(C) {
|
||||
case '\a': next = 'a'; break;
|
||||
case '\b': next = 'b'; break;
|
||||
case '\f': next = 'f'; break;
|
||||
case '\n': next = 'n'; break;
|
||||
case '\r': next = 'r'; break;
|
||||
case '\t': next = 't'; break;
|
||||
case '\v': next = 'v'; break;
|
||||
case '\\': next = '\\'; break;
|
||||
case '\'': next = '\''; break;
|
||||
case '\"': next = '\"'; if(Q.S==escape::CSV) quote = '"'; break;
|
||||
default:
|
||||
if(!isprint((unsigned char)C)) {
|
||||
// print three charator escape
|
||||
strm<<"\\x"<<hexdigit(C>>4)<<hexdigit(C);
|
||||
} else {
|
||||
// literal
|
||||
strm.put(C);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// print two charactor escape
|
||||
strm.put(quote);
|
||||
strm.put(next);
|
||||
}
|
||||
|
||||
return strm;
|
||||
}
|
||||
|
||||
|
||||
std::ostream& operator<<(std::ostream& strm, const maybeQuote& q)
|
||||
{
|
||||
bool esc = false;
|
||||
for(size_t i=0, N=q.s.size(); i<N && !esc; i++) {
|
||||
switch(q.s[i]) {
|
||||
case '\a':
|
||||
case '\b':
|
||||
case '\f':
|
||||
case '\n':
|
||||
case '\r':
|
||||
case '\t':
|
||||
case ' ':
|
||||
case '\v':
|
||||
case '\\':
|
||||
case '\'':
|
||||
case '\"':
|
||||
esc = true;
|
||||
break;
|
||||
default:
|
||||
if(!isprint((unsigned char)q.s[i])) {
|
||||
esc = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(esc) {
|
||||
strm<<'"'<<escape(q.s)<<'"';
|
||||
} else {
|
||||
strm<<q.s;
|
||||
}
|
||||
return strm;
|
||||
}
|
||||
|
||||
}} //epics::pvData
|
||||
|
||||
@@ -17,7 +17,7 @@ using std::cout;
|
||||
using std::endl;
|
||||
using std::string;
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
template<typename T>
|
||||
void copy(
|
||||
@@ -272,4 +272,3 @@ void copy(
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
|
||||
@@ -7,4 +7,4 @@ INC += pv/json.h
|
||||
LIBSRCS += parsehelper.cpp
|
||||
LIBSRCS += parseany.cpp
|
||||
LIBSRCS += parseinto.cpp
|
||||
LIBSRCS += print.cpp
|
||||
LIBSRCS += jprint.cpp
|
||||
|
||||
305
src/json/jprint.cpp
Normal file
305
src/json/jprint.cpp
Normal file
@@ -0,0 +1,305 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
#include <errlog.h>
|
||||
#include <yajl_gen.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/pvdVersion.h>
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/valueBuilder.h>
|
||||
#include <pv/bitSet.h>
|
||||
#include "pv/json.h"
|
||||
|
||||
namespace pvd = epics::pvData;
|
||||
|
||||
namespace {
|
||||
using namespace pvd::yajl;
|
||||
|
||||
void yg(yajl_gen_status sts) {
|
||||
const char *msg = "<\?\?\?>";
|
||||
switch(sts) {
|
||||
case yajl_gen_status_ok:
|
||||
case yajl_gen_generation_complete:
|
||||
return;
|
||||
#define CASE(STS) case STS: msg = #STS; break
|
||||
CASE(yajl_gen_keys_must_be_strings);
|
||||
CASE(yajl_gen_in_error_state);
|
||||
CASE(yajl_gen_no_buf);
|
||||
CASE(yajl_gen_invalid_number);
|
||||
CASE(yajl_max_depth_exceeded);
|
||||
#ifdef EPICS_YAJL_VERSION
|
||||
CASE(yajl_gen_invalid_string);
|
||||
#endif
|
||||
#undef CASE
|
||||
}
|
||||
throw std::runtime_error(msg);
|
||||
}
|
||||
|
||||
static
|
||||
void stream_printer(void * ctx,
|
||||
const char * str,
|
||||
size_arg len)
|
||||
{
|
||||
std::ostream *strm = (std::ostream*)ctx;
|
||||
strm->write(str, len);
|
||||
}
|
||||
|
||||
struct args {
|
||||
yajl_gen handle;
|
||||
const pvd::JSONPrintOptions& opts;
|
||||
|
||||
std::string indent;
|
||||
|
||||
args(std::ostream& strm,
|
||||
const pvd::JSONPrintOptions& opts)
|
||||
:opts(opts)
|
||||
,indent(opts.indent, ' ')
|
||||
{
|
||||
#ifndef EPICS_YAJL_VERSION
|
||||
yajl_gen_config conf;
|
||||
conf.beautify = opts.multiLine;
|
||||
conf.indentString = indent.c_str();
|
||||
if(!(handle = yajl_gen_alloc2(stream_printer, NULL, NULL, &strm)))
|
||||
throw std::bad_alloc();
|
||||
|
||||
if(opts.json5) {
|
||||
static bool warned;
|
||||
if(!warned) {
|
||||
warned = true;
|
||||
errlogPrintf("Warning: Ignoring request to print JSON5. Update Base >= 7.0.6.1");
|
||||
}
|
||||
}
|
||||
#else
|
||||
if(!(handle = yajl_gen_alloc(NULL)))
|
||||
throw std::bad_alloc();
|
||||
if(opts.multiLine) {
|
||||
yajl_gen_config(handle, yajl_gen_beautify, 1);
|
||||
yajl_gen_config(handle, yajl_gen_indent_string, indent.c_str());
|
||||
} else {
|
||||
yajl_gen_config(handle, yajl_gen_beautify, 0);
|
||||
}
|
||||
# if EPICS_VERSION_INT>=VERSION_INT(7,0,6,1)
|
||||
yajl_gen_config(handle, yajl_gen_json5, (int)opts.json5);
|
||||
# else
|
||||
if(opts.json5) {
|
||||
static bool warned;
|
||||
if(!warned) {
|
||||
warned = true;
|
||||
errlogPrintf("Warning: Ignoring request to print JSON5. Update Base >= 7.0.6.1");
|
||||
}
|
||||
}
|
||||
# endif
|
||||
yajl_gen_config(handle, yajl_gen_print_callback, stream_printer, &strm);
|
||||
#endif
|
||||
}
|
||||
~args() {
|
||||
yajl_gen_free(handle);
|
||||
}
|
||||
};
|
||||
|
||||
void yg_string(yajl_gen handle, const std::string& s) {
|
||||
yg(yajl_gen_string(handle, (const unsigned char*)s.c_str(), s.size()));
|
||||
}
|
||||
|
||||
void show_field(args& A, const pvd::PVField* fld, const pvd::BitSet *mask);
|
||||
|
||||
void show_struct(args& A, const pvd::PVStructure* fld, const pvd::BitSet *mask)
|
||||
{
|
||||
const pvd::StructureConstPtr& type = fld->getStructure();
|
||||
const pvd::PVFieldPtrArray& children = fld->getPVFields();
|
||||
|
||||
const pvd::StringArray& names = type->getFieldNames();
|
||||
|
||||
yg(yajl_gen_map_open(A.handle));
|
||||
|
||||
for(size_t i=0, N=names.size(); i<N; i++)
|
||||
{
|
||||
if(mask && !mask->get(children[i]->getFieldOffset())) continue;
|
||||
|
||||
yg_string(A.handle, names[i]);
|
||||
show_field(A, children[i].get(), mask);
|
||||
}
|
||||
|
||||
yg(yajl_gen_map_close(A.handle));
|
||||
}
|
||||
|
||||
void show_field(args& A, const pvd::PVField* fld, const pvd::BitSet *mask)
|
||||
{
|
||||
switch(fld->getField()->getType())
|
||||
{
|
||||
case pvd::scalar:
|
||||
{
|
||||
const pvd::PVScalar *scalar=static_cast<const pvd::PVScalar*>(fld);
|
||||
switch(scalar->getScalar()->getScalarType()) {
|
||||
case pvd::pvString: yg_string(A.handle, scalar->getAs<std::string>()); break;
|
||||
case pvd::pvBoolean: yg(yajl_gen_bool(A.handle, scalar->getAs<pvd::boolean>())); break;
|
||||
case pvd::pvDouble:
|
||||
case pvd::pvFloat: yg(yajl_gen_double(A.handle, scalar->getAs<double>())); break;
|
||||
// case pvd::pvULong: // can't always be exactly represented...
|
||||
default:
|
||||
yg(yajl_gen_integer(A.handle, scalar->getAs<pvd::int64>())); break;
|
||||
}
|
||||
}
|
||||
return;
|
||||
case pvd::scalarArray:
|
||||
{
|
||||
const pvd::PVScalarArray *scalar=static_cast<const pvd::PVScalarArray*>(fld);
|
||||
|
||||
pvd::shared_vector<const void> arr;
|
||||
scalar->getAs<void>(arr);
|
||||
|
||||
yg(yajl_gen_array_open(A.handle));
|
||||
|
||||
switch(arr.original_type()) {
|
||||
case pvd::pvString: {
|
||||
pvd::shared_vector<const std::string> sarr(pvd::shared_vector_convert<const std::string>(arr));
|
||||
for(size_t i=0, N=sarr.size(); i<N; i++) {
|
||||
yg_string(A.handle, sarr[i]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case pvd::pvBoolean: {
|
||||
pvd::shared_vector<const pvd::boolean> sarr(pvd::shared_vector_convert<const pvd::boolean>(arr));
|
||||
for(size_t i=0, N=sarr.size(); i<N; i++) {
|
||||
yg(yajl_gen_bool(A.handle, sarr[i]));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case pvd::pvDouble:
|
||||
case pvd::pvFloat: {
|
||||
pvd::shared_vector<const double> sarr(pvd::shared_vector_convert<const double>(arr));
|
||||
for(size_t i=0, N=sarr.size(); i<N; i++) {
|
||||
yg(yajl_gen_double(A.handle, sarr[i]));
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
pvd::shared_vector<const pvd::int64> sarr(pvd::shared_vector_convert<const pvd::int64>(arr));
|
||||
for(size_t i=0, N=sarr.size(); i<N; i++) {
|
||||
yg(yajl_gen_integer(A.handle, sarr[i]));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
yg(yajl_gen_array_close(A.handle));
|
||||
}
|
||||
return;
|
||||
case pvd::structure:
|
||||
show_struct(A, static_cast<const pvd::PVStructure*>(fld), mask);
|
||||
return;
|
||||
case pvd::structureArray:
|
||||
{
|
||||
pvd::PVStructureArray::const_svector arr(static_cast<const pvd::PVStructureArray*>(fld)->view());
|
||||
yg(yajl_gen_array_open(A.handle));
|
||||
|
||||
for(size_t i=0, N=arr.size(); i<N; i++) {
|
||||
if(arr[i])
|
||||
show_struct(A, arr[i].get(), 0);
|
||||
else
|
||||
yg(yajl_gen_null(A.handle));
|
||||
}
|
||||
|
||||
yg(yajl_gen_array_close(A.handle));
|
||||
}
|
||||
return;
|
||||
case pvd::union_:
|
||||
{
|
||||
const pvd::PVUnion *U=static_cast<const pvd::PVUnion*>(fld);
|
||||
const pvd::PVField::const_shared_pointer& C(U->get());
|
||||
|
||||
if(!C) {
|
||||
yg(yajl_gen_null(A.handle));
|
||||
} else {
|
||||
show_field(A, C.get(), 0);
|
||||
}
|
||||
}
|
||||
return;
|
||||
case pvd::unionArray: {
|
||||
const pvd::PVUnionArray *U=static_cast<const pvd::PVUnionArray*>(fld);
|
||||
pvd::PVUnionArray::const_svector arr(U->view());
|
||||
|
||||
yg(yajl_gen_array_open(A.handle));
|
||||
|
||||
for(size_t i=0, N=arr.size(); i<N; i++) {
|
||||
if(arr[i])
|
||||
show_field(A, arr[i].get(), 0);
|
||||
else
|
||||
yg(yajl_gen_null(A.handle));
|
||||
}
|
||||
|
||||
yg(yajl_gen_array_close(A.handle));
|
||||
}
|
||||
return;
|
||||
}
|
||||
// should not be reached
|
||||
if(A.opts.ignoreUnprintable)
|
||||
yg(yajl_gen_null(A.handle));
|
||||
else
|
||||
throw std::runtime_error("Encountered unprintable field type");
|
||||
}
|
||||
|
||||
void expandBS(const pvd::PVStructure& top, pvd::BitSet& mask, bool parents) {
|
||||
if(mask.get(0)) { // special handling because getSubField(0) not allowed
|
||||
// wildcard
|
||||
for(size_t idx=1, N=top.getNumberFields(); idx<N; idx++) {
|
||||
mask.set(idx);
|
||||
}
|
||||
|
||||
} else {
|
||||
for(pvd::int32 idx = mask.nextSetBit(0), N=top.getNumberFields(); idx>=0 && idx<N; idx=mask.nextSetBit(idx+1)) {
|
||||
pvd::PVField::const_shared_pointer fld = top.getSubFieldT(idx);
|
||||
|
||||
// look forward and mark all children
|
||||
for(size_t i=idx+1, N=fld->getNextFieldOffset(); i<N; i++)
|
||||
mask.set(i);
|
||||
|
||||
if(parents) {
|
||||
// look back and mark all parents
|
||||
// we've already stepped past all parents so siblings will not be automatically marked
|
||||
for(const pvd::PVStructure *parent = fld->getParent(); parent; parent = parent->getParent()) {
|
||||
mask.set(parent->getFieldOffset());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace epics{namespace pvData{
|
||||
|
||||
JSONPrintOptions::JSONPrintOptions()
|
||||
:multiLine(true)
|
||||
,ignoreUnprintable(true)
|
||||
,indent(0)
|
||||
,json5(false)
|
||||
{}
|
||||
|
||||
void printJSON(std::ostream& strm,
|
||||
const PVStructure& val,
|
||||
const BitSet& mask,
|
||||
const JSONPrintOptions& opts)
|
||||
{
|
||||
args A(strm, opts);
|
||||
pvd::BitSet emask(mask);
|
||||
expandBS(val, emask, true);
|
||||
if(!emask.get(0)) return;
|
||||
show_struct(A, &val, &emask);
|
||||
}
|
||||
|
||||
void printJSON(std::ostream& strm,
|
||||
const PVField& val,
|
||||
const JSONPrintOptions& opts)
|
||||
{
|
||||
args A(strm, opts);
|
||||
show_field(A, &val, 0);
|
||||
}
|
||||
|
||||
}} // namespace epics::pvData
|
||||
@@ -10,8 +10,6 @@
|
||||
#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;
|
||||
@@ -280,5 +278,3 @@ parseJSON(std::istream& strm)
|
||||
}
|
||||
|
||||
}} // namespace epics::pvData
|
||||
|
||||
#endif // EPICS_VERSION_INT
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/pvdVersion.h>
|
||||
|
||||
#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1)
|
||||
|
||||
#include "pv/json.h"
|
||||
|
||||
namespace {
|
||||
@@ -118,5 +116,3 @@ bool yajl_parse_helper(std::istream& src,
|
||||
}
|
||||
|
||||
}} // namespace epics::pvData
|
||||
|
||||
#endif // EPICS_VERSION_INT
|
||||
|
||||
@@ -11,9 +11,6 @@
|
||||
#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;
|
||||
@@ -302,13 +299,17 @@ struct handler {
|
||||
operator yajl_handle() { return handle; }
|
||||
};
|
||||
|
||||
struct noop {
|
||||
void operator()(pvd::PVField*) {}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace epics{namespace pvData{
|
||||
|
||||
epicsShareFunc
|
||||
void parseJSON(std::istream& strm,
|
||||
const PVField::shared_pointer& dest,
|
||||
PVField& dest,
|
||||
BitSet *assigned)
|
||||
{
|
||||
#ifndef EPICS_YAJL_VERSION
|
||||
@@ -318,7 +319,12 @@ void parseJSON(std::istream& strm,
|
||||
conf.checkUTF8 = 1;
|
||||
#endif
|
||||
|
||||
context ctxt(dest, assigned);
|
||||
// we won't create refs to 'dest' which presist beyond this call.
|
||||
// however, it is convienent to treat 'dest' in the same manner as
|
||||
// any union/structureArray memebers it may contain.
|
||||
PVFieldPtr fakedest(&dest, noop());
|
||||
|
||||
context ctxt(fakedest, assigned);
|
||||
|
||||
#ifndef EPICS_YAJL_VERSION
|
||||
handler handle(yajl_alloc(&jtree_cbs, &conf, NULL, &ctxt));
|
||||
@@ -334,9 +340,7 @@ void parseJSON(std::istream& strm,
|
||||
|
||||
if(!ctxt.stack.empty())
|
||||
throw std::logic_error("field stack not empty");
|
||||
assert(fakedest.use_count()==1);
|
||||
}
|
||||
|
||||
}} // namespace epics::pvData
|
||||
|
||||
#endif // EPICS_VERSION_INT
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -13,8 +13,6 @@
|
||||
#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
|
||||
@@ -46,20 +44,38 @@ struct epicsShareClass JSONPrintOptions
|
||||
bool multiLine; //!< include new lines
|
||||
bool ignoreUnprintable;//!< ignore union/union array when encountered
|
||||
unsigned indent; //!< Initial indentation (# of spaces)
|
||||
bool json5; //!< Output extended JSON (eg. NaN). @since 8.1.0
|
||||
JSONPrintOptions();
|
||||
};
|
||||
|
||||
/** Print PVStructure as JSON
|
||||
*
|
||||
* Restrictions:
|
||||
*
|
||||
* - No support for union or array of union
|
||||
* 'mask' selects those fields which will be printed.
|
||||
* @version Overload added after 7.0.0
|
||||
*/
|
||||
epicsShareFunc
|
||||
void printJSON(std::ostream& strm,
|
||||
const PVField::const_shared_pointer& val,
|
||||
const PVStructure& val,
|
||||
const BitSet& mask,
|
||||
const JSONPrintOptions& opts = JSONPrintOptions());
|
||||
|
||||
/** Print PVField as JSON
|
||||
* @version Overload added after 7.0.0
|
||||
*/
|
||||
epicsShareFunc
|
||||
void printJSON(std::ostream& strm,
|
||||
const PVField& val,
|
||||
const JSONPrintOptions& opts = JSONPrintOptions());
|
||||
|
||||
// To be deprecated in favor of previous form
|
||||
FORCE_INLINE
|
||||
void printJSON(std::ostream& strm,
|
||||
const PVField::const_shared_pointer& val,
|
||||
const JSONPrintOptions& opts = JSONPrintOptions())
|
||||
{
|
||||
printJSON(strm, *val, opts);
|
||||
}
|
||||
|
||||
/** Parse JSON text into a PVStructure
|
||||
*
|
||||
* Restrictions:
|
||||
@@ -82,12 +98,22 @@ PVStructure::shared_pointer parseJSON(std::istream& strm);
|
||||
* @param dest Store in fields of this structure
|
||||
* @param assigned Which fields of _dest_ were assigned. (Optional)
|
||||
* @throws std::runtime_error on failure. dest and assigned may be modified.
|
||||
* @version Overload added after 7.0.0
|
||||
*/
|
||||
epicsShareFunc
|
||||
void parseJSON(std::istream& strm,
|
||||
const PVField::shared_pointer& dest,
|
||||
PVField& dest,
|
||||
BitSet *assigned=0);
|
||||
|
||||
// To be deprecated in favor of previous form
|
||||
FORCE_INLINE
|
||||
void parseJSON(std::istream& strm,
|
||||
const PVField::shared_pointer& dest,
|
||||
BitSet *assigned=0)
|
||||
{
|
||||
parseJSON(strm, *dest, assigned);
|
||||
}
|
||||
|
||||
|
||||
/** Wrapper around yajl_parse()
|
||||
*
|
||||
@@ -120,8 +146,4 @@ typedef size_t size_arg;
|
||||
|
||||
}} // namespace epics::pvData
|
||||
|
||||
#else
|
||||
# error JSON parser requires EPICS Base >= 3.15.0.1
|
||||
#endif // EPICS_VERSION_INT
|
||||
|
||||
#endif // PV_JSON_H
|
||||
|
||||
@@ -15,7 +15,6 @@ INC += pv/timer.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
|
||||
@@ -31,8 +30,8 @@ LIBSRCS += serializeHelper.cpp
|
||||
LIBSRCS += event.cpp
|
||||
LIBSRCS += timer.cpp
|
||||
LIBSRCS += status.cpp
|
||||
LIBSRCS += localStaticLock.cpp
|
||||
LIBSRCS += typeCast.cpp
|
||||
LIBSRCS += thread.cpp
|
||||
LIBSRCS += parseToPOD.cpp
|
||||
LIBSRCS += pvUnitTest.cpp
|
||||
LIBSRCS += debugPtr.cpp
|
||||
|
||||
@@ -31,7 +31,7 @@ AnyScalar::AnyScalar(const AnyScalar& o)
|
||||
}
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
AnyScalar::AnyScalar(AnyScalar&& o)
|
||||
AnyScalar::AnyScalar(AnyScalar&& o) noexcept
|
||||
:_stype(o._stype)
|
||||
{
|
||||
typedef std::string string;
|
||||
@@ -136,7 +136,7 @@ void AnyScalar::swap(AnyScalar& o) {
|
||||
}
|
||||
const void* AnyScalar::bufferUnsafe() const {
|
||||
if(_stype==pvString) {
|
||||
return as<std::string>().c_str();
|
||||
return ref<std::string>().c_str();
|
||||
} else {
|
||||
return _wrap.blob;
|
||||
}
|
||||
|
||||
@@ -42,12 +42,12 @@
|
||||
#define CHECK_POST() assert(words.empty() || words.back()!=0)
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
|
||||
BitSet::shared_pointer BitSet::create(uint32 nbits)
|
||||
{
|
||||
return BitSet::shared_pointer(new BitSet(nbits));
|
||||
}
|
||||
|
||||
|
||||
BitSet::BitSet() {}
|
||||
|
||||
BitSet::BitSet(uint32 nbits)
|
||||
@@ -327,7 +327,7 @@ namespace epics { namespace pvData {
|
||||
SerializeHelper::writeSize(len, buffer, flusher);
|
||||
flusher->ensureBuffer(len);
|
||||
|
||||
n = len / 8;
|
||||
n = len / 8;
|
||||
for (uint32 i = 0; i < n; i++)
|
||||
buffer->putLong(words[i]);
|
||||
|
||||
@@ -338,7 +338,7 @@ namespace epics { namespace pvData {
|
||||
|
||||
void BitSet::deserialize(ByteBuffer* buffer, DeserializableControl* control) {
|
||||
|
||||
uint32 bytes = static_cast<uint32>(SerializeHelper::readSize(buffer, control)); // in bytes
|
||||
uint32 bytes = static_cast<uint32>(SerializeHelper::readSize(buffer, control)); // in bytes
|
||||
|
||||
size_t wordsInUse = (bytes + 7) / BYTES_PER_WORD;
|
||||
words.resize(wordsInUse);
|
||||
|
||||
@@ -35,7 +35,7 @@ void shared_ptr_base::track_new()
|
||||
}
|
||||
|
||||
// create new tracker if ptr!=nullptr, otherwise clear
|
||||
void shared_ptr_base::track_new(void* ptr)
|
||||
void shared_ptr_base::track_new(const void* ptr)
|
||||
{
|
||||
track_clear();
|
||||
if(ptr){
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
|
||||
Event::~Event() {
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
/* localStaticLock.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
|
||||
#include <pv/localStaticLock.h>
|
||||
|
||||
static int nifty_counter;
|
||||
static epics::pvData::Mutex* g_localStaticInitMutex;
|
||||
|
||||
epics::pvData::Mutex& getLocalStaticInitMutex()
|
||||
{
|
||||
return *g_localStaticInitMutex;
|
||||
}
|
||||
|
||||
|
||||
// The counter is initialized at load-time, i.e., before any of the static objects are initialized.
|
||||
MutexInitializer::MutexInitializer ()
|
||||
{
|
||||
if (0 == nifty_counter++)
|
||||
{
|
||||
// Initialize static members.
|
||||
g_localStaticInitMutex = new epics::pvData::Mutex();
|
||||
}
|
||||
}
|
||||
|
||||
MutexInitializer::~MutexInitializer ()
|
||||
{
|
||||
if (0 == --nifty_counter)
|
||||
{
|
||||
// Clean-up.
|
||||
delete g_localStaticInitMutex;
|
||||
}
|
||||
}
|
||||
@@ -25,406 +25,6 @@ using std::string;
|
||||
#define NEED_LONGLONG
|
||||
#endif
|
||||
|
||||
#ifndef EPICS_VERSION_INT
|
||||
#define VERSION_INT(V,R,M,P) ( ((V)<<24) | ((R)<<16) | ((M)<<8) | (P))
|
||||
#define EPICS_VERSION_INT VERSION_INT(EPICS_VERSION, EPICS_REVISION, EPICS_MODIFICATION, EPICS_PATCH_LEVEL)
|
||||
#endif
|
||||
|
||||
#if EPICS_VERSION_INT < VERSION_INT(3,15,0,1)
|
||||
/* These integer conversion primitives added to epicsStdlib.c in 3.15.0.1 */
|
||||
|
||||
#define S_stdlib_noConversion 1 /* No digits to convert */
|
||||
#define S_stdlib_extraneous 2 /* Extraneous characters */
|
||||
#define S_stdlib_underflow 3 /* Too small to represent */
|
||||
#define S_stdlib_overflow 4 /* Too large to represent */
|
||||
#define S_stdlib_badBase 5 /* Number base not supported */
|
||||
|
||||
static int
|
||||
epicsParseLong(const char *str, long *to, int base, char **units)
|
||||
{
|
||||
int c;
|
||||
char *endp;
|
||||
long value;
|
||||
|
||||
while ((c = *str) && isspace(c))
|
||||
++str;
|
||||
|
||||
errno = 0;
|
||||
value = strtol(str, &endp, base);
|
||||
|
||||
if (endp == str)
|
||||
return S_stdlib_noConversion;
|
||||
if (errno == EINVAL) /* Not universally supported */
|
||||
return S_stdlib_badBase;
|
||||
if (errno == ERANGE)
|
||||
return S_stdlib_overflow;
|
||||
|
||||
while ((c = *endp) && isspace(c))
|
||||
++endp;
|
||||
if (c && !units)
|
||||
return S_stdlib_extraneous;
|
||||
|
||||
*to = value;
|
||||
if (units)
|
||||
*units = endp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
epicsParseULong(const char *str, unsigned long *to, int base, char **units)
|
||||
{
|
||||
int c;
|
||||
char *endp;
|
||||
unsigned long value;
|
||||
|
||||
while ((c = *str) && isspace(c))
|
||||
++str;
|
||||
|
||||
errno = 0;
|
||||
value = strtoul(str, &endp, base);
|
||||
|
||||
if (endp == str)
|
||||
return S_stdlib_noConversion;
|
||||
if (errno == EINVAL) /* Not universally supported */
|
||||
return S_stdlib_badBase;
|
||||
if (errno == ERANGE)
|
||||
return S_stdlib_overflow;
|
||||
|
||||
while ((c = *endp) && isspace(c))
|
||||
++endp;
|
||||
if (c && !units)
|
||||
return S_stdlib_extraneous;
|
||||
|
||||
*to = value;
|
||||
if (units)
|
||||
*units = endp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
epicsParseDouble(const char *str, double *to, char **units)
|
||||
{
|
||||
int c;
|
||||
char *endp;
|
||||
double value;
|
||||
|
||||
while ((c = *str) && isspace(c))
|
||||
++str;
|
||||
|
||||
errno = 0;
|
||||
value = epicsStrtod(str, &endp);
|
||||
|
||||
if (endp == str)
|
||||
return S_stdlib_noConversion;
|
||||
if (errno == ERANGE)
|
||||
return (value == 0) ? S_stdlib_underflow : S_stdlib_overflow;
|
||||
|
||||
while ((c = *endp) && isspace(c))
|
||||
++endp;
|
||||
if (c && !units)
|
||||
return S_stdlib_extraneous;
|
||||
|
||||
*to = value;
|
||||
if (units)
|
||||
*units = endp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* These call the primitives */
|
||||
|
||||
static int
|
||||
epicsParseInt8(const char *str, epicsInt8 *to, int base, char **units)
|
||||
{
|
||||
long value;
|
||||
int status = epicsParseLong(str, &value, base, units);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
if (value < -0x80 || value > 0x7f)
|
||||
return S_stdlib_overflow;
|
||||
|
||||
*to = (epicsInt8)value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
epicsParseUInt8(const char *str, epicsUInt8 *to, int base, char **units)
|
||||
{
|
||||
unsigned long value;
|
||||
int status = epicsParseULong(str, &value, base, units);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
if (value > 0xff && value <= ~0xffUL)
|
||||
return S_stdlib_overflow;
|
||||
|
||||
*to = (epicsUInt8)value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
epicsParseInt16(const char *str, epicsInt16 *to, int base, char **units)
|
||||
{
|
||||
long value;
|
||||
int status = epicsParseLong(str, &value, base, units);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
if (value < -0x8000 || value > 0x7fff)
|
||||
return S_stdlib_overflow;
|
||||
|
||||
*to = (epicsInt16)value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
epicsParseUInt16(const char *str, epicsUInt16 *to, int base, char **units)
|
||||
{
|
||||
unsigned long value;
|
||||
int status = epicsParseULong(str, &value, base, units);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
if (value > 0xffff && value <= ~0xffffUL)
|
||||
return S_stdlib_overflow;
|
||||
|
||||
*to = (epicsUInt16)value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
epicsParseInt32(const char *str, epicsInt32 *to, int base, char **units)
|
||||
{
|
||||
long value;
|
||||
int status = epicsParseLong(str, &value, base, units);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
#if (LONG_MAX > 0x7fffffff)
|
||||
if (value < -0x80000000L || value > 0x7fffffffL)
|
||||
return S_stdlib_overflow;
|
||||
#endif
|
||||
|
||||
*to = (epicsInt32)value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
epicsParseUInt32(const char *str, epicsUInt32 *to, int base, char **units)
|
||||
{
|
||||
unsigned long value;
|
||||
int status = epicsParseULong(str, &value, base, units);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
#if (ULONG_MAX > 0xffffffff)
|
||||
if (value > 0xffffffffUL && value <= ~0xffffffffUL)
|
||||
return S_stdlib_overflow;
|
||||
#endif
|
||||
|
||||
*to = (epicsUInt32)value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
epicsParseFloat(const char *str, float *to, char **units)
|
||||
{
|
||||
double value, abs;
|
||||
int status = epicsParseDouble(str, &value, units);
|
||||
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
abs = fabs(value);
|
||||
if (value > 0 && abs <= FLT_MIN)
|
||||
return S_stdlib_underflow;
|
||||
if (finite(value) && abs >= FLT_MAX)
|
||||
return S_stdlib_overflow;
|
||||
|
||||
*to = (float)value;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Sometimes we have to provide our own copy of strtoll()
|
||||
#if defined(_WIN32) && !defined(_MINGW)
|
||||
// On Windows with MSVC, Base-3.15 provides strtoll()
|
||||
# define NEED_OLL_FUNCS (EPICS_VERSION_INT < VERSION_INT(3,15,0,1))
|
||||
#elif defined(vxWorks)
|
||||
// On VxWorks, Base-3.15 provides strtoll()
|
||||
# define NEED_OLL_FUNCS (EPICS_VERSION_INT < VERSION_INT(3,15,0,1))
|
||||
#else
|
||||
// Other architectures all provide strtoll()
|
||||
# define NEED_OLL_FUNCS 0
|
||||
#endif
|
||||
|
||||
#if defined(NEED_LONGLONG) && NEED_OLL_FUNCS
|
||||
static
|
||||
long long strtoll(const char *ptr, char ** endp, int base)
|
||||
{
|
||||
size_t inlen = strlen(ptr);
|
||||
long long result;
|
||||
unsigned char offset=0;
|
||||
|
||||
assert(base==0);
|
||||
|
||||
if(ptr[0]=='-')
|
||||
offset=1;
|
||||
|
||||
try {
|
||||
std::istringstream strm(ptr);
|
||||
|
||||
assert(strm.rdbuf()->in_avail()>=0
|
||||
&& inlen==(size_t)strm.rdbuf()->in_avail());
|
||||
|
||||
if(ptr[offset]=='0') {
|
||||
if(ptr[offset+1]=='x')
|
||||
strm >> std::hex;
|
||||
else
|
||||
strm >> std::oct;
|
||||
}
|
||||
|
||||
strm >> result;
|
||||
if(strm.fail())
|
||||
goto noconvert;
|
||||
|
||||
assert(strm.rdbuf()->in_avail()>=0
|
||||
&& inlen>=(size_t)strm.rdbuf()->in_avail());
|
||||
|
||||
size_t consumed = inlen - strm.rdbuf()->in_avail();
|
||||
*endp = (char*)ptr + consumed;
|
||||
|
||||
return result;
|
||||
|
||||
} catch(...) {
|
||||
goto noconvert;
|
||||
}
|
||||
|
||||
return result;
|
||||
noconvert:
|
||||
*endp = (char*)ptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(vxWorks)
|
||||
/* The VxWorks version of std::istringstream >> uint64_t is buggy,
|
||||
* provide our own implementation
|
||||
*/
|
||||
static
|
||||
unsigned long long strtoull(const char *nptr, char **endptr, int base)
|
||||
{
|
||||
const char *s = nptr;
|
||||
unsigned long long acc;
|
||||
int c;
|
||||
unsigned long long cutoff;
|
||||
int neg = 0, any, cutlim;
|
||||
|
||||
do
|
||||
c = *s++;
|
||||
while (isspace(c));
|
||||
if (c == '-')
|
||||
{
|
||||
neg = 1;
|
||||
c = *s++;
|
||||
}
|
||||
else if (c == '+')
|
||||
c = *s++;
|
||||
if ((base == 0 || base == 16) &&
|
||||
c == '0' && (*s == 'x' || *s == 'X'))
|
||||
{
|
||||
c = s[1];
|
||||
s += 2;
|
||||
base = 16;
|
||||
}
|
||||
if (base == 0)
|
||||
base = c == '0' ? 8 : 10;
|
||||
|
||||
cutoff = (unsigned long long) UINT64_MAX / (unsigned long long) base;
|
||||
cutlim = (unsigned long long) UINT64_MAX % (unsigned long long) base;
|
||||
|
||||
for (acc = 0, any = 0;; c = *s++)
|
||||
{
|
||||
if (isdigit(c))
|
||||
c -= '0';
|
||||
else if (isalpha(c))
|
||||
c -= isupper(c) ? 'A' - 10 : 'a' - 10;
|
||||
else
|
||||
break;
|
||||
if (c >= base)
|
||||
break;
|
||||
if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
|
||||
any = -1;
|
||||
else
|
||||
{
|
||||
any = 1;
|
||||
acc *= base;
|
||||
acc += c;
|
||||
}
|
||||
}
|
||||
if (any < 0)
|
||||
{
|
||||
acc = UINT64_MAX;
|
||||
errno = ERANGE;
|
||||
}
|
||||
else if (neg)
|
||||
acc = -acc;
|
||||
if (endptr != 0)
|
||||
*endptr = any ? (char *) s - 1 : (char *) nptr;
|
||||
return (acc);
|
||||
}
|
||||
#else
|
||||
static
|
||||
unsigned long long strtoull(const char *ptr, char ** endp, int base)
|
||||
{
|
||||
size_t inlen = strlen(ptr);
|
||||
unsigned long long result;
|
||||
|
||||
assert(base==0);
|
||||
|
||||
try {
|
||||
std::istringstream strm(ptr);
|
||||
|
||||
assert(strm.rdbuf()->in_avail()>=0
|
||||
&& inlen==(size_t)strm.rdbuf()->in_avail());
|
||||
|
||||
if(ptr[0]=='0') {
|
||||
if(ptr[1]=='x')
|
||||
strm >> std::hex;
|
||||
else
|
||||
strm >> std::oct;
|
||||
}
|
||||
|
||||
strm >> result;
|
||||
if(strm.fail())
|
||||
goto noconvert;
|
||||
|
||||
assert(strm.rdbuf()->in_avail()>=0
|
||||
&& inlen>=(size_t)strm.rdbuf()->in_avail());
|
||||
|
||||
size_t consumed = inlen - strm.rdbuf()->in_avail();
|
||||
*endp = (char*)ptr + consumed;
|
||||
|
||||
return result;
|
||||
|
||||
} catch(...) {
|
||||
goto noconvert;
|
||||
}
|
||||
|
||||
return result;
|
||||
noconvert:
|
||||
*endp = (char*)ptr;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* do we need long long? */
|
||||
#ifdef NEED_LONGLONG
|
||||
static int
|
||||
|
||||
@@ -117,12 +117,13 @@ public:
|
||||
|
||||
//! Construct from un-typed pointer.
|
||||
//! Caller is responsible to ensure that buf actually points to the provided type
|
||||
//! @version Added after 7.0.0
|
||||
AnyScalar(ScalarType type, const void *buf);
|
||||
|
||||
AnyScalar(const AnyScalar& o);
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
AnyScalar(AnyScalar&& o);
|
||||
AnyScalar(AnyScalar&& o) noexcept;
|
||||
#endif
|
||||
|
||||
inline ~AnyScalar() {clear();}
|
||||
@@ -139,7 +140,7 @@ public:
|
||||
}
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
inline AnyScalar& operator=(AnyScalar&& o) {
|
||||
inline AnyScalar& operator=(AnyScalar&& o) noexcept {
|
||||
clear();
|
||||
swap(o);
|
||||
return *this;
|
||||
@@ -147,7 +148,7 @@ public:
|
||||
#endif
|
||||
|
||||
//! Reset internal state.
|
||||
//! Added after 7.0.0
|
||||
//! @version Added after 7.0.0
|
||||
//! @post empty()==true
|
||||
void clear();
|
||||
|
||||
@@ -174,14 +175,16 @@ public:
|
||||
|
||||
//! Provide read-only access to underlying buffer.
|
||||
//! For a string this is std::string::c_str().
|
||||
//! @version Added after 7.0.0
|
||||
const void* bufferUnsafe() const;
|
||||
|
||||
/** Return typed reference to wrapped value. Non-const reference allows value modification
|
||||
*
|
||||
* @throws bad_cast when the requested type does not match the stored type
|
||||
@code
|
||||
AnyScalar v(42);
|
||||
v.ref<uint32>() = 42;
|
||||
assert(v.ref<uint32>() = 43);
|
||||
v.ref<uint32>() = 43;
|
||||
assert(v.ref<uint32>() == 43);
|
||||
@endcode
|
||||
*/
|
||||
template<typename T>
|
||||
@@ -199,9 +202,10 @@ public:
|
||||
|
||||
/** Return typed reference to wrapped value. Const reference does not allow modification.
|
||||
*
|
||||
* @throws bad_cast when the requested type does not match the stored type
|
||||
@code
|
||||
AnyScalar v(42);
|
||||
assert(v.ref<uint32>() = 42);
|
||||
const AnyScalar v(42);
|
||||
assert(v.ref<uint32>() == 42);
|
||||
@endcode
|
||||
*/
|
||||
template<typename T>
|
||||
@@ -217,7 +221,10 @@ public:
|
||||
return reinterpret_cast<typename meta::decorate_const<TT>::type&>(_wrap.blob);
|
||||
}
|
||||
|
||||
/** copy out wrapped value, with a value conversion. */
|
||||
/** copy out wrapped value, with a value conversion.
|
||||
*
|
||||
* @throws bad_cast when empty()==true
|
||||
*/
|
||||
template<typename T>
|
||||
inline
|
||||
T as() const {
|
||||
|
||||
@@ -50,6 +50,8 @@ namespace epics { namespace pvData {
|
||||
* synchronization.
|
||||
*
|
||||
* Based on Java implementation.
|
||||
*
|
||||
* @since 7.0.0 Many methods return BitSet& to facilite method chaining.
|
||||
*/
|
||||
class epicsShareClass BitSet : public Serializable {
|
||||
public:
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <epicsEndian.h>
|
||||
#include <shareLib.h>
|
||||
#include <epicsAssert.h>
|
||||
#include <compilerDependencies.h>
|
||||
|
||||
#include <pv/templateMeta.h>
|
||||
#include <pv/pvType.h>
|
||||
@@ -149,14 +150,17 @@ struct swap<8> {
|
||||
#undef _PVA_swap64
|
||||
|
||||
/* PVD serialization doesn't pay attention to alignement,
|
||||
* which some targets really care about and treat unaligned
|
||||
* which some targets (ARM and powerpc) really care about and treat unaligned
|
||||
* access as a fault, or with a heavy penalty (~= to a syscall).
|
||||
*
|
||||
* For those targets,, we will have to live with the increase
|
||||
* in execution time and/or object code size of byte-wise copy.
|
||||
*
|
||||
* Treat x86 32/64 as an outlier, and assume all other targets
|
||||
* need, or greatly benefit, from aligned access.
|
||||
*/
|
||||
|
||||
#ifdef _ARCH_PPC
|
||||
#if !(defined(__x86_64__) || defined(_M_AMD64) || defined(__i386__) || defined(_M_IX86))
|
||||
|
||||
template<typename T>
|
||||
union alignu {
|
||||
@@ -493,23 +497,6 @@ public:
|
||||
{
|
||||
return sizeof(T)>1 && _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.
|
||||
*/
|
||||
inline void align(std::size_t size, char fill='\0')
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Put a boolean value into the byte buffer.
|
||||
*
|
||||
@@ -695,7 +682,7 @@ public:
|
||||
EPICS_ALWAYS_INLINE double getDouble (std::size_t index) { return get<double>(index); }
|
||||
|
||||
// TODO remove
|
||||
EPICS_ALWAYS_INLINE const char* getArray() const
|
||||
EPICS_ALWAYS_INLINE const char* getArray() const EPICS_DEPRECATED
|
||||
{
|
||||
return _buffer;
|
||||
}
|
||||
@@ -803,8 +790,8 @@ private:
|
||||
assert(n<=getRemaining());
|
||||
|
||||
if (reverse<T>()) {
|
||||
for(std::size_t i=0; i<n; i+=sizeof(T)) {
|
||||
detail::store_unaligned(_position+i, swap<T>(values[i]));
|
||||
for(std::size_t i=0; i<count; i++) {
|
||||
detail::store_unaligned(_position+i*sizeof(T), swap<T>(values[i]));
|
||||
}
|
||||
} else {
|
||||
memcpy(_position, values, n);
|
||||
@@ -819,8 +806,8 @@ private:
|
||||
assert(n<=getRemaining());
|
||||
|
||||
if (reverse<T>()) {
|
||||
for(std::size_t i=0; i<n; i+=sizeof(T)) {
|
||||
values[i] = swap<T>(detail::load_unaligned<T>(_position+i));
|
||||
for(std::size_t i=0; i<count; i++) {
|
||||
values[i] = swap<T>(detail::load_unaligned<T>(_position+i*sizeof(T)));
|
||||
}
|
||||
} else {
|
||||
memcpy(values, _position, n);
|
||||
|
||||
@@ -78,7 +78,7 @@ protected:
|
||||
// add ourselves to tracker
|
||||
void track_new();
|
||||
// create new tracker if ptr!=nullptr, otherwise clear
|
||||
void track_new(void* ptr);
|
||||
void track_new(const void* ptr);
|
||||
// copy tracker and add ourself
|
||||
void track_assign(const shared_ptr_base& o);
|
||||
void track_clear();
|
||||
@@ -286,6 +286,7 @@ public:
|
||||
|
||||
long use_count() const noexcept { return real.use_count(); }
|
||||
bool unique() const noexcept { return real.unique(); }
|
||||
bool expired() const noexcept { return real.expired(); }
|
||||
};
|
||||
|
||||
template<class Base>
|
||||
@@ -316,13 +317,12 @@ do_enable_shared_from_this(const shared_ptr<Store>& dest,
|
||||
self->xxInternalSelf = actual;
|
||||
}
|
||||
|
||||
}} // namespace epics::debug
|
||||
|
||||
template<typename T>
|
||||
inline std::ostream& operator<<(std::ostream& strm, const epics::debug::shared_ptr<T>& ptr)
|
||||
inline std::ostream& operator<<(std::ostream& strm, const shared_ptr<T>& ptr)
|
||||
{
|
||||
strm<<ptr.get();
|
||||
return strm;
|
||||
}
|
||||
|
||||
}} // namespace epics::debug
|
||||
#endif // DEBUGPTR_H
|
||||
|
||||
@@ -33,10 +33,6 @@
|
||||
#ifndef EPICSEXCEPTION_H_
|
||||
#define EPICSEXCEPTION_H_
|
||||
|
||||
#if defined(_WIN32) && !defined(NOMINMAX)
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
@@ -211,7 +207,7 @@ public:
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
explicit BaseException(const std::string msg) : std::logic_error(msg) {}
|
||||
explicit BaseException(const std::string& msg) : std::logic_error(msg) {}
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
@@ -233,7 +229,6 @@ private:
|
||||
#endif
|
||||
|
||||
#define THROW_BASE_EXCEPTION(msg) THROW_EXCEPTION2(::epics::pvData::BaseException, msg)
|
||||
#define THROW_BASE_EXCEPTION_CAUSE(msg, cause) THROW_EXCEPTION2(::epics::pvData::BaseException, msg)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
/* localStaticLock.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 LOCALSTATICLOCK_H
|
||||
#define LOCALSTATICLOCK_H
|
||||
|
||||
#include <compilerDependencies.h>
|
||||
|
||||
#include <pv/lock.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
epicsShareExtern epics::pvData::Mutex& getLocalStaticInitMutex() EPICS_DEPRECATED;
|
||||
|
||||
#if defined(__GNUC__) && __GNUC__ >= 4
|
||||
// noop
|
||||
#define LOCAL_STATIC_LOCK
|
||||
#else
|
||||
#define LOCAL_STATIC_LOCK epics::pvData::Lock localStaticInitMutexLock(getLocalStaticInitMutex());
|
||||
#endif
|
||||
|
||||
static class epicsShareClass MutexInitializer {
|
||||
public:
|
||||
MutexInitializer ();
|
||||
~MutexInitializer ();
|
||||
} localStaticMutexInitializer; // Note object here in the header.
|
||||
|
||||
|
||||
#endif /* LOCALSTATICLOCK_H */
|
||||
@@ -28,7 +28,7 @@
|
||||
public:
|
||||
...
|
||||
};
|
||||
@code
|
||||
@endcode
|
||||
*
|
||||
* @note This macro contains 'private:'.
|
||||
*/
|
||||
|
||||
@@ -39,7 +39,7 @@ public:
|
||||
explicit testPassx(bool r) :dotest(true), pass(r), alive(true) {}
|
||||
~testPassx();
|
||||
template<typename T>
|
||||
inline testPassx& operator<<(T v) {
|
||||
inline testPassx& operator<<(const T& v) {
|
||||
strm<<v;
|
||||
return *this;
|
||||
}
|
||||
@@ -52,13 +52,13 @@ private:
|
||||
};
|
||||
|
||||
template<typename LHS, typename RHS>
|
||||
inline testPassx testEqualx(const char *nLHS, const char *nRHS, LHS l, RHS r)
|
||||
inline testPassx testEqualx(const char *nLHS, const char *nRHS, const LHS& l, const RHS& r)
|
||||
{
|
||||
return testPassx(l==r)<<nLHS<<" ("<<l<<") == "<<nRHS<<" ("<<r<<")";
|
||||
}
|
||||
|
||||
template<typename LHS, typename RHS>
|
||||
inline testPassx testNotEqualx(const char *nLHS, const char *nRHS, LHS l, RHS r)
|
||||
inline testPassx testNotEqualx(const char *nLHS, const char *nRHS, const LHS& l, const RHS& r)
|
||||
{
|
||||
return testPassx(l!=r)<<nLHS<<" ("<<l<<") != "<<nRHS<<" ("<<r<<")";
|
||||
}
|
||||
|
||||
@@ -5,6 +5,38 @@
|
||||
#ifndef REFTRACK_H
|
||||
#define REFTRACK_H
|
||||
|
||||
/** @page pvd_reftrack RefTrack
|
||||
*
|
||||
* reftrack.h is a utility for listing, finding, and reading global atomic counters.
|
||||
* By convention used to expose object instance counters as a way of detecting (slow)
|
||||
* reference/resource leaks before they cause problems.
|
||||
*
|
||||
* cf. the IOC shell commands "refshow", "refsave", and "refdiff".
|
||||
*
|
||||
* Example usage:
|
||||
*
|
||||
* @code
|
||||
* // my header.h
|
||||
* struct MyClass {
|
||||
* MyClass();
|
||||
* ~MyClass();
|
||||
* static size_t num_instances;
|
||||
* ...
|
||||
* };
|
||||
* ...
|
||||
* // my src.cpp
|
||||
* size_t MyClass::num_instances;
|
||||
* MyClass::MyClass() {
|
||||
* REFTRACE_INCREMENT(num_instances);
|
||||
* }
|
||||
* MyClass::~MyClass() {
|
||||
* REFTRACE_DECREMENT(num_instances);
|
||||
* }
|
||||
* // in some IOC registrar or global ctor
|
||||
* registerRefCounter("MyClass", &MyClass::num_instances);
|
||||
* @endcode
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include <map>
|
||||
@@ -14,27 +46,10 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <epicsVersion.h>
|
||||
#include <epicsAtomic.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
|
||||
#define REFTRACE_INCREMENT(counter) ::epics::atomic::increment(counter)
|
||||
#define REFTRACE_DECREMENT(counter) ::epics::atomic::decrement(counter)
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
|
||||
@@ -16,6 +16,14 @@
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
#if defined(PVD_INTERNAL)
|
||||
# define PVD_DEPRECATED(msg)
|
||||
#elif __GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 5
|
||||
# define PVD_DEPRECATED(msg) __attribute__((deprecated(msg)))
|
||||
#else
|
||||
# define PVD_DEPRECATED(msg) EPICS_DEPRECATED
|
||||
#endif
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
class SerializableControl;
|
||||
@@ -47,11 +55,7 @@ namespace epics { namespace pvData {
|
||||
* @param size The number of bytes.
|
||||
*/
|
||||
virtual void ensureBuffer(std::size_t size) =0;
|
||||
/**
|
||||
* Add pad bytes to buffer.
|
||||
* @param alignment alignment required.
|
||||
*/
|
||||
virtual void alignBuffer(std::size_t alignment) =0;
|
||||
virtual void alignBuffer(std::size_t alignment) PVD_DEPRECATED("Deprecated for lack of use") {}
|
||||
/**
|
||||
* Method for serializing primitive array data.
|
||||
* Hook for supplying custom serialization implementation.
|
||||
@@ -98,14 +102,8 @@ namespace epics { namespace pvData {
|
||||
* @param size The number of bytes.
|
||||
*/
|
||||
virtual void ensureData(std::size_t size) =0;
|
||||
/**
|
||||
* Align buffer.
|
||||
* Note that this takes care only current buffer alignment.
|
||||
* If streaming protocol is used,
|
||||
* care must be taken that entire stream is aligned.
|
||||
* @param alignment size in bytes, must be power of two.
|
||||
*/
|
||||
virtual void alignData(std::size_t alignment) =0;
|
||||
// Deprecated for lack of use
|
||||
virtual void alignData(std::size_t alignment) PVD_DEPRECATED("Deprecated for lack of use") {};
|
||||
/**
|
||||
* Method for deserializing array data.
|
||||
* Hook for supplying custom deserialization implementation.
|
||||
@@ -150,7 +148,7 @@ namespace epics { namespace pvData {
|
||||
*/
|
||||
virtual void serialize(ByteBuffer *buffer,
|
||||
SerializableControl *flusher) const = 0;
|
||||
/**
|
||||
/**
|
||||
* Deserialize buffer.
|
||||
* @param buffer serialization buffer.
|
||||
* @param flusher deserialization control.
|
||||
@@ -213,7 +211,7 @@ namespace epics { namespace pvData {
|
||||
*/
|
||||
virtual void serialize(ByteBuffer *buffer,
|
||||
SerializableControl *flusher,BitSet *bitSet) const = 0;
|
||||
/**
|
||||
/**
|
||||
* Deserialize buffer.
|
||||
* @param buffer serialization buffer.
|
||||
* @param flusher deserialization control.
|
||||
|
||||
@@ -62,15 +62,21 @@
|
||||
#if defined(SHARED_FROM_MANUAL)
|
||||
// define SHARED_FROM_MANUAL if from some reason it is desirable to manually select
|
||||
// which shared_ptr implementation to use
|
||||
#elif __cplusplus>=201103L || (defined(_MSC_VER) && (_MSC_VER>=1600)) || (__clang__ && __APPLE__)
|
||||
// c++11 or MSVC 2010
|
||||
// clang on linux has tr1/memory, clang on OSX doesn't
|
||||
#elif __cplusplus>=201103L || (defined(_MSC_VER) && (_MSC_VER>=1600)) || defined(_LIBCPP_VERSION)
|
||||
// MSVC has been bad about incrementing __cplusplus, even when new features are added. shared_ptr from MSVC 2010
|
||||
// the llvm libc++ doesn't bother with tr1, and puts shared_ptr in std:: even with -std=c++98
|
||||
# define SHARED_FROM_STD
|
||||
|
||||
#elif defined(__GNUC__) && __GNUC__>=4 && !defined(vxWorks)
|
||||
// GCC >=4.0.0
|
||||
# define SHARED_FROM_TR1
|
||||
|
||||
#elif defined(_MSC_VER) && _MSC_VER==1500
|
||||
// MSVC 2009 (eg. Visual C++ for Python 2.7)
|
||||
// Dinkumware _CPPLIB_VER=505
|
||||
// Has std::tr1::shared_ptr in <memory>
|
||||
# define SHARED_TR1_FROM_STD
|
||||
|
||||
#elif defined(_MSC_VER) && (_MSC_VER>1500 || defined(_HAS_TR1))
|
||||
// MSVC > 2008, or 2008 w/ SP1
|
||||
# define SHARED_FROM_TR1
|
||||
@@ -120,6 +126,9 @@ namespace std {
|
||||
|
||||
#endif // DEBUG_SHARED_PTR
|
||||
|
||||
#elif defined(SHARED_TR1_FROM_STD)
|
||||
# include <memory>
|
||||
|
||||
#elif defined(SHARED_FROM_TR1)
|
||||
# include <tr1/memory>
|
||||
|
||||
@@ -192,7 +201,8 @@ inline std::ostream& operator<<(std::ostream& strm, const ::detail::ref_shower<T
|
||||
typedef std::tr1::weak_ptr<clazz> weak_pointer; \
|
||||
typedef std::tr1::weak_ptr<const clazz> const_weak_pointer
|
||||
|
||||
/* A semi-hack to help with migration from std::auto_ptr to std::unique_ptr,
|
||||
namespace epics{
|
||||
/** A semi-hack to help with migration from std::auto_ptr to std::unique_ptr,
|
||||
* and avoid copious deprecation warning spam
|
||||
* which may be hiding legitimate issues.
|
||||
*
|
||||
@@ -205,7 +215,6 @@ inline std::ostream& operator<<(std::ostream& strm, const ::detail::ref_shower<T
|
||||
* copy/assignment/return are not supported
|
||||
* (use auto_ptr or unique_ptr explicitly).
|
||||
*/
|
||||
namespace epics{
|
||||
#if __cplusplus>=201103L
|
||||
template<typename T>
|
||||
using auto_ptr = std::unique_ptr<T>;
|
||||
|
||||
@@ -6,10 +6,6 @@
|
||||
#ifndef SHAREDVECTOR_H
|
||||
#define SHAREDVECTOR_H
|
||||
|
||||
#if defined(_WIN32) && !defined(NOMINMAX)
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include <ostream>
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
@@ -256,7 +252,9 @@ namespace detail {
|
||||
|
||||
m_total -= offset;
|
||||
|
||||
m_count = std::min(length, max_count);
|
||||
if(length > max_count)
|
||||
length = max_count;
|
||||
m_count = length;
|
||||
}
|
||||
|
||||
// Access to members.
|
||||
@@ -430,15 +428,17 @@ public:
|
||||
void reserve(size_t i) {
|
||||
if(this->unique() && i<=this->m_total)
|
||||
return;
|
||||
size_t new_count = std::min(this->m_count, i);
|
||||
size_t new_count = this->m_count;
|
||||
if(new_count > i)
|
||||
new_count = i;
|
||||
_E_non_const* temp=new _E_non_const[i];
|
||||
try{
|
||||
std::copy(begin(), begin()+new_count, temp);
|
||||
this->m_sdata.reset(temp, detail::default_array_deleter<E*>());
|
||||
}catch(...){
|
||||
delete[] temp;
|
||||
throw;
|
||||
}
|
||||
this->m_sdata.reset(temp, detail::default_array_deleter<E*>());
|
||||
this->m_offset = 0;
|
||||
this->m_count = new_count;
|
||||
this->m_total = i;
|
||||
@@ -468,19 +468,24 @@ public:
|
||||
}
|
||||
}
|
||||
// must re-allocate :(
|
||||
size_t new_total = std::max(this->m_total, i);
|
||||
size_t new_total = this->m_total;
|
||||
if(new_total < i)
|
||||
new_total = i;
|
||||
_E_non_const* temp=new _E_non_const[new_total];
|
||||
try{
|
||||
size_t n = this->size();
|
||||
if(n > i)
|
||||
n = i;
|
||||
// Copy as much as possible from old,
|
||||
// remaining elements are uninitialized.
|
||||
std::copy(begin(),
|
||||
begin()+std::min(i,this->size()),
|
||||
begin()+n,
|
||||
temp);
|
||||
this->m_sdata.reset(temp, detail::default_array_deleter<pointer>());
|
||||
}catch(...){
|
||||
delete[] temp;
|
||||
throw;
|
||||
}
|
||||
this->m_sdata.reset(temp, detail::default_array_deleter<pointer>());
|
||||
this->m_offset= 0;
|
||||
this->m_count = i;
|
||||
this->m_total = new_total;
|
||||
@@ -539,11 +544,11 @@ private:
|
||||
/* Hack alert.
|
||||
* For reasons of simplicity and efficiency, we want to use raw pointers for iteration.
|
||||
* However, shared_ptr::get() isn't defined when !m_sdata, although practically it gives NULL.
|
||||
* Unfortunately, many of the MSVC (<= VS 2010) STL methods assert() that iterators are never NULL.
|
||||
* Unfortunately, many of the MSVC (<= VS 2013) STL methods assert() that iterators are never NULL.
|
||||
* So we fudge here by abusing 'this' so that our iterators are always !NULL.
|
||||
*/
|
||||
inline E* base_ptr() const {
|
||||
#if defined(_MSC_VER) && _MSC_VER<=1600
|
||||
#if defined(_MSC_VER) && _MSC_VER<=1800
|
||||
return this->m_count ? this->m_sdata.get() : (E*)(this-1);
|
||||
#else
|
||||
return this->m_sdata.get();
|
||||
@@ -832,9 +837,13 @@ namespace detail {
|
||||
typedef typename meta::strip_const<TO>::type to_t;
|
||||
ScalarType stype = src.original_type(),
|
||||
dtype = (ScalarType)ScalarTypeID<TO>::value;
|
||||
if(stype==dtype) {
|
||||
if(src.empty()) {
|
||||
return shared_vector<TO>();
|
||||
|
||||
} else if(stype==dtype) {
|
||||
// no convert needed
|
||||
return shared_vector<TO>(src, detail::_shared_vector_cast_tag());
|
||||
|
||||
} else {
|
||||
// alloc and convert
|
||||
shared_vector<to_t> ret(src.size()/ScalarTypeFunc::elementSize(stype));
|
||||
|
||||
@@ -28,10 +28,10 @@ namespace epics { namespace pvData {
|
||||
class epicsShareClass Status : public epics::pvData::Serializable {
|
||||
public:
|
||||
POINTER_DEFINITIONS(Status);
|
||||
/**
|
||||
* Status type enum.
|
||||
*/
|
||||
enum StatusType {
|
||||
/**
|
||||
* Status type enum.
|
||||
*/
|
||||
enum StatusType {
|
||||
/** Operation completed successfully. */
|
||||
STATUSTYPE_OK,
|
||||
/** Operation completed successfully, but there is a warning message. */
|
||||
@@ -40,11 +40,11 @@ namespace epics { namespace pvData {
|
||||
STATUSTYPE_ERROR,
|
||||
/** Operation failed due to an unexpected error. */
|
||||
STATUSTYPE_FATAL
|
||||
};
|
||||
|
||||
static const char* StatusTypeName[];
|
||||
|
||||
static Status Ok;
|
||||
};
|
||||
|
||||
static const char* StatusTypeName[];
|
||||
|
||||
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); }
|
||||
@@ -54,10 +54,10 @@ namespace epics { namespace pvData {
|
||||
* Creates OK status; STATUSTYPE_OK, empty message and stackDump.
|
||||
*/
|
||||
Status() :m_statusType(STATUSTYPE_OK) {}
|
||||
|
||||
/**
|
||||
* Create non-OK status.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create non-OK status.
|
||||
*/
|
||||
Status(StatusType type, std::string const & message);
|
||||
|
||||
/**
|
||||
@@ -77,13 +77,13 @@ namespace epics { namespace pvData {
|
||||
* Get error message describing an error. Required if error status.
|
||||
* @return error message.
|
||||
*/
|
||||
inline std::string getMessage() const { return m_message; }
|
||||
inline const std::string& getMessage() const { return m_message; }
|
||||
|
||||
/**
|
||||
* Get stack dump where error (exception) happened. Optional.
|
||||
* @return stack dump.
|
||||
*/
|
||||
inline std::string getStackDump() const { return m_stackDump; }
|
||||
inline const std::string& getStackDump() const { return m_stackDump; }
|
||||
|
||||
/**
|
||||
* Convenient OK test. Same as <code>(getType() == StatusType.OK)</code>.
|
||||
|
||||
@@ -42,38 +42,7 @@ typedef std::tr1::shared_ptr<epicsThread> EpicsThreadPtr;
|
||||
|
||||
typedef epicsThreadRunable Runnable;
|
||||
|
||||
//! Helper for those cases where a class should have more than one runnable
|
||||
template<typename C>
|
||||
class RunnableMethod : public Runnable
|
||||
{
|
||||
EPICS_NOT_COPYABLE(RunnableMethod)
|
||||
typedef void (C::*meth_t)();
|
||||
C *inst;
|
||||
meth_t meth;
|
||||
|
||||
virtual void run()
|
||||
{
|
||||
(inst->*meth)();
|
||||
}
|
||||
public:
|
||||
RunnableMethod(C* inst, void (C::*meth)())
|
||||
:inst(inst), meth(meth)
|
||||
{}
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
struct FuncRunner : public epicsThreadRunable
|
||||
{
|
||||
typedef void (*fn_t)(void*);
|
||||
fn_t fn;
|
||||
void *arg;
|
||||
FuncRunner(fn_t f, void *a) :fn(f), arg(a) {}
|
||||
virtual ~FuncRunner(){}
|
||||
virtual void run()
|
||||
{
|
||||
(*fn)(arg);
|
||||
}
|
||||
};
|
||||
template<typename C>
|
||||
struct MethRunner : public epicsThreadRunable
|
||||
{
|
||||
@@ -87,26 +56,13 @@ struct MethRunner : public epicsThreadRunable
|
||||
(inst->*fn)();
|
||||
}
|
||||
};
|
||||
#if __cplusplus>=201103L
|
||||
struct BindRunner : public epicsThreadRunable
|
||||
{
|
||||
typedef std::function<void()> fn_t;
|
||||
fn_t fn;
|
||||
BindRunner(const fn_t f) : fn(f) {}
|
||||
virtual ~BindRunner() {}
|
||||
virtual void run()
|
||||
{
|
||||
fn();
|
||||
}
|
||||
};
|
||||
#endif
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* @brief C++ wrapper for epicsThread from EPICS base.
|
||||
*
|
||||
*/
|
||||
class Thread : public epicsThread {
|
||||
class epicsShareClass Thread : public epicsThread {
|
||||
EPICS_NOT_COPYABLE(Thread)
|
||||
public:
|
||||
/** @brief Holds all the configuration necessary to launch a @class Thread
|
||||
@@ -136,7 +92,7 @@ public:
|
||||
<<"example"<<1);
|
||||
@endcode
|
||||
*/
|
||||
class Config
|
||||
class epicsShareClass Config
|
||||
{
|
||||
unsigned int p_prio, p_stack;
|
||||
std::ostringstream p_strm;
|
||||
@@ -145,49 +101,28 @@ public:
|
||||
typedef epics::auto_ptr<Runnable> p_owned_runner_t;
|
||||
p_owned_runner_t p_owned_runner;
|
||||
friend class Thread;
|
||||
Runnable& x_getrunner()
|
||||
{
|
||||
if(!this->p_runner)
|
||||
throw std::logic_error("Thread::Config missing run()");
|
||||
return *this->p_runner;
|
||||
}
|
||||
void x_setdefault()
|
||||
{
|
||||
this->p_prio = epicsThreadPriorityLow;
|
||||
this->p_autostart = true;
|
||||
this->p_runner = NULL;
|
||||
(*this).stack(epicsThreadStackSmall);
|
||||
}
|
||||
Runnable& x_getrunner();
|
||||
void x_setdefault();
|
||||
|
||||
public:
|
||||
Config() {this->x_setdefault();}
|
||||
Config(Runnable *r) {this->x_setdefault();this->run(r);}
|
||||
Config(void(*fn)(void*), void *ptr) {this->x_setdefault();this->run(fn, ptr);}
|
||||
Config();
|
||||
Config(Runnable *r);
|
||||
Config(void(*fn)(void*), void *ptr);
|
||||
template<typename C>
|
||||
Config(C* inst, void(C::*meth)()) {this->x_setdefault();this->run(inst, meth);}
|
||||
#if __cplusplus>=201103L
|
||||
Config(const std::function<void()>& fn) {this->x_setdefault();this->run(fn);}
|
||||
Config(std::function<void()>&& fn);
|
||||
#endif
|
||||
|
||||
inline Config& name(const std::string& n)
|
||||
{ this->p_strm.str(n); return *this; }
|
||||
inline Config& prio(unsigned int p)
|
||||
{ this->p_prio = p; return *this; }
|
||||
inline Config& stack(epicsThreadStackSizeClass s)
|
||||
{ this->p_stack = epicsThreadGetStackSize(s); return *this; }
|
||||
inline Config& autostart(bool a)
|
||||
{ this->p_autostart = a; return *this; }
|
||||
Config& name(const std::string& n);
|
||||
Config& prio(unsigned int p);
|
||||
Config& stack(epicsThreadStackSizeClass s);
|
||||
Config& autostart(bool a);
|
||||
|
||||
//! Thread will execute Runnable::run()
|
||||
Config& run(Runnable* r)
|
||||
{ this->p_runner = r; return *this; }
|
||||
Config& run(Runnable* r);
|
||||
//! Thread will execute (*fn)(ptr)
|
||||
Config& run(void(*fn)(void*), void *ptr)
|
||||
{
|
||||
this->p_owned_runner.reset(new detail::FuncRunner(fn, ptr));
|
||||
this->p_runner = this->p_owned_runner.get();
|
||||
return *this;
|
||||
}
|
||||
Config& run(void(*fn)(void*), void *ptr);
|
||||
//! Thread will execute (inst->*meth)()
|
||||
template<typename C>
|
||||
Config& run(C* inst, void(C::*meth)())
|
||||
@@ -197,12 +132,7 @@ public:
|
||||
return *this;
|
||||
}
|
||||
#if __cplusplus>=201103L
|
||||
Config& run(const std::function<void()>& fn)
|
||||
{
|
||||
this->p_owned_runner.reset(new detail::BindRunner(fn));
|
||||
this->p_runner = this->p_owned_runner.get();
|
||||
return *this;
|
||||
}
|
||||
Config& run(std::function<void()>&& fn);
|
||||
#endif
|
||||
|
||||
//! Append to thread name string. Argument must be understood by std::ostream::operator<<
|
||||
@@ -228,14 +158,7 @@ public:
|
||||
Thread(std::string name,
|
||||
ThreadPriority priority,
|
||||
Runnable *runnable,
|
||||
epicsThreadStackSizeClass stkcls=epicsThreadStackSmall)
|
||||
:epicsThread(*runnable,
|
||||
name.c_str(),
|
||||
epicsThreadGetStackSize(stkcls),
|
||||
priority)
|
||||
{
|
||||
this->start();
|
||||
}
|
||||
epicsThreadStackSizeClass stkcls=epicsThreadStackBig);
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -255,38 +178,19 @@ public:
|
||||
Thread(Runnable &runnable,
|
||||
std::string name,
|
||||
unsigned int stksize,
|
||||
unsigned int priority=lowestPriority)
|
||||
:epicsThread(runnable,
|
||||
name.c_str(),
|
||||
stksize,
|
||||
priority)
|
||||
{
|
||||
this->start();
|
||||
}
|
||||
unsigned int priority=lowestPriority);
|
||||
|
||||
//! @brief Create a new thread using the given @class Config
|
||||
//! @throws std::logic_error for improper @class Config (ie. missing runner)
|
||||
Thread(Config& c)
|
||||
:epicsThread(c.x_getrunner(), c.p_strm.str().c_str(),
|
||||
c.p_stack, c.p_prio)
|
||||
{
|
||||
#if __cplusplus>=201103L
|
||||
p_owned = std::move(c.p_owned_runner);
|
||||
#else
|
||||
p_owned = c.p_owned_runner;
|
||||
#endif
|
||||
if(c.p_autostart)
|
||||
this->start();
|
||||
}
|
||||
Thread(Config& c);
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~Thread()
|
||||
{
|
||||
this->exitWait();
|
||||
}
|
||||
~Thread();
|
||||
|
||||
static size_t num_instances;
|
||||
private:
|
||||
Config::p_owned_runner_t p_owned;
|
||||
};
|
||||
|
||||
|
||||
@@ -60,6 +60,8 @@ private:
|
||||
epicsTime timeToRun;
|
||||
double period;
|
||||
bool onList;
|
||||
bool cancelled;
|
||||
bool processing;
|
||||
friend class Timer;
|
||||
struct IncreasingTime;
|
||||
};
|
||||
@@ -126,6 +128,7 @@ private:
|
||||
mutable Mutex mutex;
|
||||
queue_t queue;
|
||||
Event waitForWork;
|
||||
bool waiting;
|
||||
bool alive;
|
||||
Thread thread;
|
||||
};
|
||||
|
||||
@@ -208,6 +208,43 @@ static FORCE_INLINE
|
||||
typename detail::print_convolute<T>::return_t
|
||||
print_cast(const T& v) { return detail::print_convolute<T>::op(v); }
|
||||
|
||||
struct escape;
|
||||
|
||||
epicsShareFunc
|
||||
std::ostream& operator<<(std::ostream& strm, const escape& Q);
|
||||
|
||||
//! Helper to print a string with escaping
|
||||
//! @code strm<<'"'<<escape(astring)<<'"' @endcode
|
||||
struct epicsShareClass escape
|
||||
{
|
||||
enum style_t {
|
||||
C, // default
|
||||
CSV, // a la RFC4180
|
||||
};
|
||||
private:
|
||||
const std::string& orig;
|
||||
style_t S;
|
||||
public:
|
||||
escape(const std::string& orig) :orig(orig), S(C) {}
|
||||
~escape();
|
||||
//! Change escaping style
|
||||
inline escape& style(style_t s) { S = s; return *this; }
|
||||
//! print to string and return. (alloc and copy)
|
||||
std::string str() const;
|
||||
|
||||
friend
|
||||
epicsShareFunc
|
||||
std::ostream& operator<<(std::ostream& strm, const escape& Q);
|
||||
};
|
||||
|
||||
struct maybeQuote {
|
||||
const std::string& s;
|
||||
maybeQuote(const std::string& s) :s(s) {}
|
||||
};
|
||||
|
||||
epicsShareExtern
|
||||
std::ostream& operator<<(std::ostream& strm, const maybeQuote& q);
|
||||
|
||||
}} // end namespace
|
||||
|
||||
#endif // PVTYPECAST_H
|
||||
|
||||
@@ -25,15 +25,6 @@
|
||||
|
||||
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;
|
||||
|
||||
@@ -88,11 +79,7 @@ size_t readRefCounter(const char *name)
|
||||
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&
|
||||
@@ -119,11 +106,7 @@ void RefSnapshot::update()
|
||||
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);
|
||||
}
|
||||
@@ -223,7 +206,7 @@ void RefMonitor::start(double period)
|
||||
impl->period = period;
|
||||
impl->worker.reset(new epicsThread(*impl,
|
||||
"RefMonitor",
|
||||
epicsThreadGetStackSize(epicsThreadStackSmall),
|
||||
epicsThreadGetStackSize(epicsThreadStackBig),
|
||||
epicsThreadPriorityMin));
|
||||
impl->worker->start();
|
||||
}
|
||||
@@ -288,10 +271,10 @@ char* epicsRefSnapshotCurrent()
|
||||
snap.update();
|
||||
std::ostringstream strm;
|
||||
strm<<snap;
|
||||
const char *str = strm.str().c_str();
|
||||
char *ret = (char*)malloc(strlen(str)+1);
|
||||
std::string str = strm.str();
|
||||
char *ret = (char*)malloc(str.length()+1);
|
||||
if(ret)
|
||||
strcpy(ret, str);
|
||||
strcpy(ret, str.c_str());
|
||||
return ret;
|
||||
}catch(std::exception& e){
|
||||
return epicsStrDup(e.what());
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace epics {
|
||||
}
|
||||
|
||||
void SerializeHelper::writeSize(std::size_t s, ByteBuffer* buffer) {
|
||||
if(s==(std::size_t)-1) // null // TODO remove
|
||||
if(s==(std::size_t)-1) // null not a size, but used in PVUnion::serialize()
|
||||
buffer->putByte(-1);
|
||||
else if(s<254)
|
||||
buffer->putByte(static_cast<int8>(s));
|
||||
@@ -103,13 +103,13 @@ namespace epics {
|
||||
DeserializableControl* control) {
|
||||
|
||||
std::size_t size = SerializeHelper::readSize(buffer, control);
|
||||
if(size!=(size_t)-1) // TODO null strings check, to be removed in the future
|
||||
if(size!=(size_t)-1) // TODO null strings check, to be removed in the future
|
||||
{
|
||||
if (buffer->getRemaining()>=size)
|
||||
{
|
||||
// entire string is in buffer, simply create a string out of it (copy)
|
||||
std::size_t pos = buffer->getPosition();
|
||||
string str(buffer->getArray()+pos, size);
|
||||
string str(buffer->getBuffer()+pos, size);
|
||||
buffer->setPosition(pos+size);
|
||||
return str;
|
||||
}
|
||||
@@ -122,7 +122,7 @@ namespace epics {
|
||||
while(true) {
|
||||
std::size_t toRead = min(size-i, buffer->getRemaining());
|
||||
std::size_t pos = buffer->getPosition();
|
||||
str.append(buffer->getArray()+pos, toRead);
|
||||
str.append(buffer->getBuffer()+pos, toRead);
|
||||
buffer->setPosition(pos+toRead);
|
||||
i += toRead;
|
||||
if(i<size)
|
||||
@@ -174,14 +174,6 @@ struct ToString : public epics::pvData::SerializableControl
|
||||
assert(bufwrap.getRemaining()>0);
|
||||
}
|
||||
|
||||
virtual void alignBuffer(std::size_t alignment)
|
||||
{
|
||||
if(bufwrap.getRemaining()<alignment)
|
||||
flushSerializeBuffer();
|
||||
assert(bufwrap.getRemaining()>=alignment);
|
||||
bufwrap.align(alignment);
|
||||
}
|
||||
|
||||
virtual bool directSerialize(
|
||||
ByteBuffer *existingBuffer,
|
||||
const char* toSerialize,
|
||||
@@ -232,16 +224,6 @@ struct FromString : public epics::pvData::DeserializableControl
|
||||
throw std::logic_error("Incomplete buffer");
|
||||
}
|
||||
|
||||
virtual void alignData(std::size_t alignment)
|
||||
{
|
||||
size_t pos = buf.getPosition(), k = alignment-1;
|
||||
if(pos&k) {
|
||||
std::size_t npad = alignment-(pos&k);
|
||||
ensureData(npad);
|
||||
buf.align(alignment);
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool directDeserialize(
|
||||
ByteBuffer *existingBuffer,
|
||||
char* deserializeTo,
|
||||
|
||||
@@ -32,7 +32,7 @@ Status::Status(StatusType type, string const & message, string const & stackDump
|
||||
if (type == STATUSTYPE_OK)
|
||||
throw std::invalid_argument("type == STATUSTYPE_OK");
|
||||
}
|
||||
|
||||
|
||||
void Status::maximize(const Status& o)
|
||||
{
|
||||
if(m_statusType < o.m_statusType) {
|
||||
@@ -44,40 +44,40 @@ void Status::maximize(const Status& o)
|
||||
|
||||
void Status::serialize(ByteBuffer *buffer, SerializableControl *flusher) const
|
||||
{
|
||||
flusher->ensureBuffer(1);
|
||||
if (m_statusType == STATUSTYPE_OK)
|
||||
{
|
||||
// special code for okStatus (optimization)
|
||||
buffer->putByte((int8)-1);
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer->putByte((int8)m_statusType);
|
||||
SerializeHelper::serializeString(m_message, buffer, flusher);
|
||||
SerializeHelper::serializeString(m_stackDump, buffer, flusher);
|
||||
}
|
||||
flusher->ensureBuffer(1);
|
||||
if (m_statusType == STATUSTYPE_OK)
|
||||
{
|
||||
// special code for okStatus (optimization)
|
||||
buffer->putByte((int8)-1);
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer->putByte((int8)m_statusType);
|
||||
SerializeHelper::serializeString(m_message, buffer, flusher);
|
||||
SerializeHelper::serializeString(m_stackDump, buffer, flusher);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Status::deserialize(ByteBuffer *buffer, DeserializableControl *flusher)
|
||||
{
|
||||
flusher->ensureData(1);
|
||||
int8 typeCode = buffer->getByte();
|
||||
if (typeCode == (int8)-1)
|
||||
{
|
||||
// in most of the cases status will be OK, we statistically optimize
|
||||
if (m_statusType != STATUSTYPE_OK)
|
||||
{
|
||||
m_statusType = STATUSTYPE_OK;
|
||||
flusher->ensureData(1);
|
||||
int8 typeCode = buffer->getByte();
|
||||
if (typeCode == (int8)-1)
|
||||
{
|
||||
// in most of the cases status will be OK, we statistically optimize
|
||||
if (m_statusType != STATUSTYPE_OK)
|
||||
{
|
||||
m_statusType = STATUSTYPE_OK;
|
||||
m_message.clear();
|
||||
m_stackDump.clear();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_statusType = (StatusType)typeCode;
|
||||
m_message = SerializeHelper::deserializeString(buffer, flusher);
|
||||
m_stackDump = SerializeHelper::deserializeString(buffer, flusher);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_statusType = (StatusType)typeCode;
|
||||
m_message = SerializeHelper::deserializeString(buffer, flusher);
|
||||
m_stackDump = SerializeHelper::deserializeString(buffer, flusher);
|
||||
}
|
||||
}
|
||||
|
||||
void Status::dump(std::ostream& o) const
|
||||
|
||||
147
src/misc/thread.cpp
Normal file
147
src/misc/thread.cpp
Normal file
@@ -0,0 +1,147 @@
|
||||
/* thread.h */
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
#include <epicsThread.h>
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/thread.h>
|
||||
#include <pv/reftrack.h>
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
namespace detail {
|
||||
struct FuncRunner : public epicsThreadRunable
|
||||
{
|
||||
typedef void (*fn_t)(void*);
|
||||
fn_t fn;
|
||||
void *arg;
|
||||
FuncRunner(fn_t f, void *a) :fn(f), arg(a) {}
|
||||
virtual ~FuncRunner(){}
|
||||
virtual void run()
|
||||
{
|
||||
(*fn)(arg);
|
||||
}
|
||||
};
|
||||
#if __cplusplus>=201103L
|
||||
struct BindRunner : public epicsThreadRunable
|
||||
{
|
||||
typedef std::function<void()> fn_t;
|
||||
fn_t fn;
|
||||
BindRunner(fn_t&& f) : fn(std::move(f)) {}
|
||||
virtual ~BindRunner() {}
|
||||
virtual void run()
|
||||
{
|
||||
fn();
|
||||
}
|
||||
};
|
||||
#endif
|
||||
} // detail
|
||||
|
||||
|
||||
Runnable& Thread::Config::x_getrunner()
|
||||
{
|
||||
if(!this->p_runner)
|
||||
throw std::logic_error("Thread::Config missing run()");
|
||||
return *this->p_runner;
|
||||
}
|
||||
|
||||
void Thread::Config::x_setdefault()
|
||||
{
|
||||
this->p_prio = epicsThreadPriorityLow;
|
||||
this->p_autostart = true;
|
||||
this->p_runner = NULL;
|
||||
(*this).stack(epicsThreadStackBig);
|
||||
}
|
||||
|
||||
size_t Thread::num_instances;
|
||||
|
||||
Thread::Config::Config() {this->x_setdefault();}
|
||||
|
||||
Thread::Config::Config(Runnable *r) {this->x_setdefault();this->run(r);}
|
||||
|
||||
Thread::Config::Config(void(*fn)(void*), void *ptr) {this->x_setdefault();this->run(fn, ptr);}
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
Thread::Config::Config(std::function<void()>&& fn) {this->x_setdefault();this->run(std::move(fn));}
|
||||
#endif
|
||||
|
||||
Thread::Config& Thread::Config::name(const std::string& n)
|
||||
{ this->p_strm.str(n); return *this; }
|
||||
|
||||
Thread::Config& Thread::Config::prio(unsigned int p)
|
||||
{ this->p_prio = p; return *this; }
|
||||
|
||||
Thread::Config& Thread::Config::stack(epicsThreadStackSizeClass s)
|
||||
{ this->p_stack = epicsThreadGetStackSize(s); return *this; }
|
||||
|
||||
Thread::Config& Thread::Config::autostart(bool a)
|
||||
{ this->p_autostart = a; return *this; }
|
||||
|
||||
Thread::Config& Thread::Config::run(Runnable* r)
|
||||
{ this->p_runner = r; return *this; }
|
||||
|
||||
Thread::Config& Thread::Config::run(void(*fn)(void*), void *ptr)
|
||||
{
|
||||
this->p_owned_runner.reset(new detail::FuncRunner(fn, ptr));
|
||||
this->p_runner = this->p_owned_runner.get();
|
||||
return *this;
|
||||
}
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
Thread::Config& Thread::Config::run(std::function<void()> &&fn)
|
||||
{
|
||||
this->p_owned_runner.reset(new detail::BindRunner(std::move(fn)));
|
||||
this->p_runner = this->p_owned_runner.get();
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
Thread::Thread(std::string name,
|
||||
ThreadPriority priority,
|
||||
Runnable *runnable,
|
||||
epicsThreadStackSizeClass stkcls)
|
||||
:epicsThread(*runnable,
|
||||
name.c_str(),
|
||||
epicsThreadGetStackSize(stkcls),
|
||||
priority)
|
||||
{
|
||||
REFTRACE_INCREMENT(num_instances);
|
||||
this->start();
|
||||
}
|
||||
|
||||
Thread::Thread(Runnable &runnable,
|
||||
std::string name,
|
||||
unsigned int stksize,
|
||||
unsigned int priority)
|
||||
:epicsThread(runnable,
|
||||
name.c_str(),
|
||||
stksize,
|
||||
priority)
|
||||
{
|
||||
REFTRACE_INCREMENT(num_instances);
|
||||
this->start();
|
||||
}
|
||||
|
||||
Thread::Thread(Config& c)
|
||||
:epicsThread(c.x_getrunner(), c.p_strm.str().c_str(),
|
||||
c.p_stack, c.p_prio)
|
||||
{
|
||||
REFTRACE_INCREMENT(num_instances);
|
||||
#if __cplusplus>=201103L
|
||||
p_owned = std::move(c.p_owned_runner);
|
||||
#else
|
||||
p_owned = c.p_owned_runner;
|
||||
#endif
|
||||
if(c.p_autostart)
|
||||
this->start();
|
||||
}
|
||||
|
||||
Thread::~Thread()
|
||||
{
|
||||
this->exitWait();
|
||||
REFTRACE_DECREMENT(num_instances);
|
||||
}
|
||||
|
||||
}} // epics::pvData
|
||||
@@ -6,10 +6,6 @@
|
||||
/**
|
||||
* @author mrk
|
||||
*/
|
||||
|
||||
#if defined(_WIN32) && !defined(NOMINMAX)
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
@@ -23,18 +19,21 @@
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
TimerCallback::TimerCallback()
|
||||
: period(0.0),
|
||||
onList(false)
|
||||
onList(false),
|
||||
cancelled(false),
|
||||
processing(false)
|
||||
{
|
||||
}
|
||||
|
||||
Timer::Timer(string threadName,ThreadPriority priority)
|
||||
: waitForWork(false),
|
||||
alive(true),
|
||||
thread(threadName,priority,this)
|
||||
:waitForWork(false)
|
||||
,waiting(false)
|
||||
,alive(true)
|
||||
,thread(threadName,priority,this)
|
||||
{}
|
||||
|
||||
struct TimerCallback::IncreasingTime {
|
||||
@@ -53,6 +52,7 @@ void Timer::addElement(TimerCallbackPtr const & timerCallback)
|
||||
temp.push_back(timerCallback);
|
||||
|
||||
timerCallback->onList = true;
|
||||
timerCallback->cancelled = false;
|
||||
|
||||
// merge sorted lists.
|
||||
// for us effectively insertion sort.
|
||||
@@ -63,14 +63,22 @@ void Timer::addElement(TimerCallbackPtr const & timerCallback)
|
||||
bool Timer::cancel(TimerCallbackPtr const &timerCallback)
|
||||
{
|
||||
Lock xx(mutex);
|
||||
/* If we're processing, just set the cancel flag */
|
||||
if (timerCallback->processing) {
|
||||
timerCallback->cancelled = true;
|
||||
return true;
|
||||
}
|
||||
if(!timerCallback->onList) return false;
|
||||
if(!alive) {
|
||||
timerCallback->onList = false;
|
||||
return true;
|
||||
}
|
||||
for(queue_t::iterator it(queue.begin()), end(queue.end()); it != end; ++it)
|
||||
{
|
||||
TimerCallbackPtr& cur = *it;
|
||||
if(cur.get() == timerCallback.get()) {
|
||||
queue.erase(it);
|
||||
cur->onList = false;
|
||||
// iteration now invalid
|
||||
queue.erase(it); // invalidates cur and it
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -95,6 +103,7 @@ void Timer::run()
|
||||
|
||||
if(queue.empty()) {
|
||||
// no jobs, just go to sleep
|
||||
waiting = true;
|
||||
epicsGuardRelease<epicsMutex> U(G);
|
||||
|
||||
waitForWork.wait();
|
||||
@@ -106,6 +115,7 @@ void Timer::run()
|
||||
TimerCallbackPtr work;
|
||||
work.swap(queue.front());
|
||||
work->onList = false;
|
||||
work->processing = true;
|
||||
queue.pop_front();
|
||||
|
||||
{
|
||||
@@ -114,7 +124,15 @@ void Timer::run()
|
||||
work->callback();
|
||||
}
|
||||
|
||||
if(work->period > 0.0) {
|
||||
work->processing = false;
|
||||
|
||||
if(work->period > 0.0 && alive && !work->cancelled) {
|
||||
if(waitfor <= -work->period) {
|
||||
// Periodic timer phase has fallen behind by at least one full period.
|
||||
// Could be due to previous slow jobs, or a system time jump forward.
|
||||
// Reset the phase.
|
||||
work->timeToRun = now = epicsTime::getCurrent();
|
||||
}
|
||||
work->timeToRun += work->period;
|
||||
addElement(work);
|
||||
}
|
||||
@@ -122,12 +140,14 @@ void Timer::run()
|
||||
// don't update 'now' until all expired jobs run
|
||||
|
||||
} else {
|
||||
waiting = true;
|
||||
// wait for first un-expired
|
||||
epicsGuardRelease<epicsMutex> U(G);
|
||||
|
||||
waitForWork.wait(waitfor);
|
||||
now = epicsTime::getCurrent();
|
||||
}
|
||||
waiting = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,7 +189,7 @@ void Timer::schedulePeriodic(
|
||||
{
|
||||
epicsTime now(epicsTime::getCurrent());
|
||||
|
||||
bool wasempty;
|
||||
bool wakeup;
|
||||
{
|
||||
Lock xx(mutex);
|
||||
if(timerCallback->onList) {
|
||||
@@ -185,10 +205,10 @@ void Timer::schedulePeriodic(
|
||||
timerCallback->timeToRun = now + delay;
|
||||
timerCallback->period = period;
|
||||
|
||||
wasempty = queue.empty();
|
||||
addElement(timerCallback);
|
||||
wakeup = waiting && queue.front()==timerCallback;
|
||||
}
|
||||
if(wasempty) waitForWork.signal();
|
||||
if(wakeup) waitForWork.signal();
|
||||
}
|
||||
|
||||
void Timer::dump(std::ostream& o) const
|
||||
|
||||
@@ -30,9 +30,9 @@ static void castVTyped(size_t count, void *draw, const void *sraw)
|
||||
{
|
||||
TO *dest=(TO*)draw;
|
||||
const FROM *src=(FROM*)sraw;
|
||||
|
||||
|
||||
//std::transform(src, src+count, dest, castUnsafe<TO,FROM>);
|
||||
|
||||
|
||||
try {
|
||||
for(size_t i=0; i<count; i++) {
|
||||
dest[i] = castUnsafe<TO,FROM>(src[i]);
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
AlarmSeverity AlarmSeverityFunc::getSeverity(int value)
|
||||
{
|
||||
|
||||
@@ -44,11 +44,6 @@ public:
|
||||
//default constructors and destructor are OK
|
||||
//returns (false,true) if pvField(is not, is) a valid alarm structure
|
||||
//An automatic detach is issued if already attached.
|
||||
/*
|
||||
* Attach to a field of a PVData object.
|
||||
* @param pvField The pvField.
|
||||
* @return (false,true) if the pvField (is not, is) an alarm structure.
|
||||
*/
|
||||
/*
|
||||
* Attach to a field of a PVData object.
|
||||
* @param pvField The pvField.
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
using std::tr1::static_pointer_cast;
|
||||
using std::string;
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
string PVAlarm::noAlarmFound("No alarm structure found");
|
||||
string PVAlarm::notAttached("Not attached to an alarm structure");
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/pvControl.h>
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
using std::tr1::static_pointer_cast;
|
||||
using std::string;
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
using std::tr1::static_pointer_cast;
|
||||
using std::string;
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
string PVDisplay::noDisplayFound("No display structure found");
|
||||
string PVDisplay::notAttached("Not attached to an display structure");
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
using std::tr1::static_pointer_cast;
|
||||
using std::string;
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
string PVEnumerated::notFound("No enumerated structure found");
|
||||
string PVEnumerated::notAttached("Not attached to an enumerated structure");
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
using std::tr1::static_pointer_cast;
|
||||
using std::string;
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
string PVTimeStamp::noTimeStamp("No timeStamp structure found");
|
||||
string PVTimeStamp::notAttached("Not attached to a timeStamp structure");
|
||||
@@ -93,5 +93,5 @@ bool PVTimeStamp::set(TimeStamp const & timeStamp)
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
|
||||
}}
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#include <pv/pvType.h>
|
||||
#include <pv/timeStamp.h>
|
||||
|
||||
namespace epics { namespace pvData {
|
||||
namespace epics { namespace pvData {
|
||||
|
||||
const int32 milliSecPerSec = 1000;
|
||||
const int32 microSecPerSec = 1000000;
|
||||
|
||||
288
src/pv/pvData.h
288
src/pv/pvData.h
@@ -9,10 +9,6 @@
|
||||
#ifndef PVDATA_H
|
||||
#define PVDATA_H
|
||||
|
||||
#if defined(_WIN32) && !defined(NOMINMAX)
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <stdexcept>
|
||||
@@ -21,6 +17,8 @@
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
#include <epicsAssert.h>
|
||||
|
||||
#include <pv/pvIntrospect.h>
|
||||
#include <pv/typeCast.h>
|
||||
#include <pv/anyscalar.h>
|
||||
@@ -241,6 +239,7 @@ public:
|
||||
void copyUnchecked(const PVField& from);
|
||||
|
||||
static size_t num_instances; // use atomic::get() or volatile* access
|
||||
enum {isPVField=1};
|
||||
protected:
|
||||
PVField::shared_pointer getPtrSelf()
|
||||
{
|
||||
@@ -260,6 +259,7 @@ private:
|
||||
PostHandlerPtr postHandler;
|
||||
friend class PVDataCreate;
|
||||
friend class PVStructure;
|
||||
EPICS_NOT_COPYABLE(PVField)
|
||||
};
|
||||
|
||||
epicsShareExtern std::ostream& operator<<(std::ostream& o, const PVField& f);
|
||||
@@ -340,6 +340,7 @@ public:
|
||||
|
||||
protected:
|
||||
explicit PVScalar(ScalarConstPtr const & scalar);
|
||||
EPICS_NOT_COPYABLE(PVScalar)
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
@@ -397,31 +398,22 @@ public:
|
||||
* Put a new value into the PVScalar.
|
||||
* @param value The value.
|
||||
*/
|
||||
void put(typename storage_t::arg_type v) {
|
||||
inline void put(typename storage_t::arg_type v) {
|
||||
storage.store(v);
|
||||
PVField::postPut();
|
||||
}
|
||||
|
||||
std::ostream& dumpValue(std::ostream& o) const OVERRIDE
|
||||
{
|
||||
return o << get();
|
||||
}
|
||||
virtual std::ostream& dumpValue(std::ostream& o) const OVERRIDE;
|
||||
|
||||
// get operator
|
||||
// double value; doubleField >>= value;
|
||||
// NOTE: virtual is needed for MS C++ compiler to get this operator exported
|
||||
virtual void operator>>=(T& value) const
|
||||
{
|
||||
value = get();
|
||||
}
|
||||
virtual void operator>>=(T& value) const;
|
||||
|
||||
// put operator
|
||||
// double value = 12.8; doubleField <<= value;
|
||||
// NOTE: virtual is needed for MS C++ compiler to get this operator exported
|
||||
virtual void operator<<=(typename storage_t::arg_type value)
|
||||
{
|
||||
put(value);
|
||||
}
|
||||
virtual void operator<<=(typename storage_t::arg_type value);
|
||||
|
||||
template<typename T1>
|
||||
inline T1 getAs() const {
|
||||
@@ -439,44 +431,29 @@ public:
|
||||
PVScalar::putFrom(v);
|
||||
}
|
||||
|
||||
virtual void assign(const PVScalar& scalar) OVERRIDE
|
||||
{
|
||||
if(isImmutable())
|
||||
throw std::invalid_argument("destination is immutable");
|
||||
copyUnchecked(scalar);
|
||||
}
|
||||
virtual void copy(const PVScalar& from) OVERRIDE
|
||||
{
|
||||
assign(from);
|
||||
}
|
||||
virtual void copyUnchecked(const PVScalar& from) OVERRIDE
|
||||
{
|
||||
if(this==&from)
|
||||
return;
|
||||
T result;
|
||||
from.getAs((void*)&result, typeCode);
|
||||
put(result);
|
||||
}
|
||||
virtual void assign(const PVScalar& scalar) OVERRIDE FINAL;
|
||||
virtual void copy(const PVScalar& from) OVERRIDE FINAL;
|
||||
virtual void copyUnchecked(const PVScalar& from) OVERRIDE FINAL;
|
||||
|
||||
virtual void serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher) const OVERRIDE;
|
||||
virtual void deserialize(ByteBuffer *pbuffer,
|
||||
DeserializableControl *pflusher) OVERRIDE;
|
||||
DeserializableControl *pflusher) OVERRIDE FINAL;
|
||||
|
||||
protected:
|
||||
explicit PVScalarValue(ScalarConstPtr const & scalar)
|
||||
: PVScalar(scalar), storage() {}
|
||||
virtual void getAs(void * result, ScalarType rtype) const OVERRIDE
|
||||
virtual void getAs(void * result, ScalarType rtype) const OVERRIDE FINAL
|
||||
{
|
||||
const T src = get();
|
||||
castUnsafeV(1, rtype, result, typeCode, (const void*)&src);
|
||||
}
|
||||
public:
|
||||
virtual void getAs(AnyScalar& v) const
|
||||
virtual void getAs(AnyScalar& v) const OVERRIDE FINAL
|
||||
{
|
||||
v = get();
|
||||
}
|
||||
virtual void putFrom(const void *src, ScalarType stype) OVERRIDE
|
||||
virtual void putFrom(const void *src, ScalarType stype) OVERRIDE FINAL
|
||||
{
|
||||
T result;
|
||||
castUnsafeV(1, typeCode, (void*)&result, stype, src);
|
||||
@@ -486,6 +463,7 @@ protected:
|
||||
|
||||
friend class PVDataCreate;
|
||||
storage_t storage;
|
||||
EPICS_NOT_COPYABLE(PVScalarValue)
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -547,14 +525,17 @@ public:
|
||||
*/
|
||||
virtual ~PVString() {}
|
||||
|
||||
virtual std::ostream& dumpValue(std::ostream& o) const OVERRIDE FINAL;
|
||||
|
||||
virtual void serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher) const OVERRIDE;
|
||||
SerializableControl *pflusher) const OVERRIDE FINAL;
|
||||
virtual void serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher, size_t offset, size_t count) const OVERRIDE;
|
||||
SerializableControl *pflusher, size_t offset, size_t count) const OVERRIDE FINAL;
|
||||
protected:
|
||||
explicit PVString(ScalarConstPtr const & scalar);
|
||||
|
||||
friend class PVDataCreate;
|
||||
EPICS_NOT_COPYABLE(PVString)
|
||||
};
|
||||
typedef std::tr1::shared_ptr<PVString> PVStringPtr;
|
||||
|
||||
@@ -624,6 +605,7 @@ protected:
|
||||
private:
|
||||
bool capacityMutable;
|
||||
friend class PVDataCreate;
|
||||
EPICS_NOT_COPYABLE(PVArray)
|
||||
};
|
||||
|
||||
epicsShareExtern std::ostream& operator<<(format::array_at_internal const& manip, const PVArray& array);
|
||||
@@ -718,6 +700,7 @@ protected:
|
||||
explicit PVScalarArray(ScalarArrayConstPtr const & scalarArray);
|
||||
private:
|
||||
friend class PVDataCreate;
|
||||
EPICS_NOT_COPYABLE(PVScalarArray)
|
||||
};
|
||||
|
||||
|
||||
@@ -740,17 +723,17 @@ public:
|
||||
* Set the field to be immutable, i.e. it can no longer be modified.
|
||||
* This is permanent, i.e. once done the field cannot be made mutable.
|
||||
*/
|
||||
virtual void setImmutable() OVERRIDE;
|
||||
virtual void setImmutable() OVERRIDE FINAL;
|
||||
/**
|
||||
* Get the introspection interface
|
||||
* @return The interface.
|
||||
*/
|
||||
const StructureConstPtr &getStructure() const;
|
||||
inline const StructureConstPtr &getStructure() const { return structurePtr; }
|
||||
/**
|
||||
* Get the array of pointers to the subfields in the structure.
|
||||
* @return The array.
|
||||
*/
|
||||
const PVFieldPtrArray & getPVFields() const;
|
||||
inline const PVFieldPtrArray & getPVFields() const { return pvFields; }
|
||||
|
||||
/**
|
||||
* Get the subfield with the specified offset.
|
||||
@@ -782,12 +765,14 @@ public:
|
||||
template<typename PVD, typename A>
|
||||
inline std::tr1::shared_ptr<PVD> getSubField(A a)
|
||||
{
|
||||
STATIC_ASSERT(PVD::isPVField); // only allow cast from PVField sub-class
|
||||
return std::tr1::dynamic_pointer_cast<PVD>(getSubFieldImpl(a, false));
|
||||
}
|
||||
|
||||
template<typename PVD, typename A>
|
||||
inline std::tr1::shared_ptr<const PVD> getSubField(A a) const
|
||||
{
|
||||
STATIC_ASSERT(PVD::isPVField); // only allow cast from PVField sub-class
|
||||
return std::tr1::dynamic_pointer_cast<const PVD>(getSubFieldImpl(a, false));
|
||||
}
|
||||
|
||||
@@ -820,6 +805,7 @@ public:
|
||||
template<typename PVD, typename A>
|
||||
inline std::tr1::shared_ptr<PVD> getSubFieldT(A a)
|
||||
{
|
||||
STATIC_ASSERT(PVD::isPVField); // only allow cast from PVField sub-class
|
||||
std::tr1::shared_ptr<PVD> ret(std::tr1::dynamic_pointer_cast<PVD>(getSubFieldImpl(a, true)));
|
||||
if(!ret)
|
||||
throwBadFieldType(a);
|
||||
@@ -829,6 +815,7 @@ public:
|
||||
template<typename PVD, typename A>
|
||||
inline std::tr1::shared_ptr<const PVD> getSubFieldT(A a) const
|
||||
{
|
||||
STATIC_ASSERT(PVD::isPVField); // only allow cast from PVField sub-class
|
||||
std::tr1::shared_ptr<const PVD> ret(std::tr1::dynamic_pointer_cast<const PVD>(getSubFieldImpl(a, true)));
|
||||
if(!ret)
|
||||
throwBadFieldType(a);
|
||||
@@ -841,14 +828,14 @@ public:
|
||||
* @param pflusher Interface to call when buffer is full.
|
||||
*/
|
||||
virtual void serialize(
|
||||
ByteBuffer *pbuffer,SerializableControl *pflusher) const OVERRIDE;
|
||||
ByteBuffer *pbuffer,SerializableControl *pflusher) const OVERRIDE FINAL;
|
||||
/**
|
||||
* Deserialize
|
||||
* @param pbuffer The byte buffer.
|
||||
* @param pflusher Interface to call when buffer is empty.
|
||||
*/
|
||||
virtual void deserialize(
|
||||
ByteBuffer *pbuffer,DeserializableControl *pflusher) OVERRIDE;
|
||||
ByteBuffer *pbuffer,DeserializableControl *pflusher) OVERRIDE FINAL;
|
||||
/**
|
||||
* Serialize.
|
||||
* @param pbuffer The byte buffer.
|
||||
@@ -856,7 +843,7 @@ public:
|
||||
* @param pbitSet A bitset the specifies which fields to serialize.
|
||||
*/
|
||||
virtual void serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher,BitSet *pbitSet) const OVERRIDE;
|
||||
SerializableControl *pflusher,BitSet *pbitSet) const OVERRIDE FINAL;
|
||||
/**
|
||||
* Deserialize
|
||||
* @param pbuffer The byte buffer.
|
||||
@@ -864,7 +851,7 @@ public:
|
||||
* @param pbitSet A bitset the specifies which fields to deserialize.
|
||||
*/
|
||||
virtual void deserialize(ByteBuffer *pbuffer,
|
||||
DeserializableControl*pflusher,BitSet *pbitSet) OVERRIDE;
|
||||
DeserializableControl*pflusher,BitSet *pbitSet) OVERRIDE FINAL;
|
||||
/**
|
||||
* Constructor
|
||||
* @param structure The introspection interface.
|
||||
@@ -877,14 +864,56 @@ public:
|
||||
*/
|
||||
PVStructure(StructureConstPtr const & structure,PVFieldPtrArray const & pvFields);
|
||||
|
||||
virtual std::ostream& dumpValue(std::ostream& o) const OVERRIDE;
|
||||
virtual std::ostream& dumpValue(std::ostream& o) const OVERRIDE FINAL;
|
||||
|
||||
void copy(const PVStructure& from);
|
||||
|
||||
void copyUnchecked(const PVStructure& from);
|
||||
void copyUnchecked(const PVStructure& from, const BitSet& maskBitSet, bool inverse = false);
|
||||
|
||||
struct Formatter {
|
||||
enum mode_t {
|
||||
Auto,
|
||||
Plain,
|
||||
ANSI,
|
||||
};
|
||||
enum format_t {
|
||||
Raw,
|
||||
NT,
|
||||
JSON,
|
||||
};
|
||||
private:
|
||||
const PVStructure& xtop;
|
||||
const BitSet* xshow;
|
||||
const BitSet* xhighlight;
|
||||
mode_t xmode;
|
||||
format_t xfmt;
|
||||
public:
|
||||
explicit Formatter(const PVStructure& top)
|
||||
:xtop(top)
|
||||
,xshow(0)
|
||||
,xhighlight(0)
|
||||
,xmode(Auto)
|
||||
,xfmt(NT)
|
||||
{}
|
||||
|
||||
// those fields (and their parents) to be printed. non-NT mode.
|
||||
FORCE_INLINE Formatter& show(const BitSet& set) { xshow = &set; return *this; }
|
||||
// those fields (and not their parents) to be specially highlighted. non-NT mode.
|
||||
FORCE_INLINE Formatter& highlight(const BitSet& set) { xhighlight = &set; return *this; }
|
||||
|
||||
FORCE_INLINE Formatter& mode(mode_t m) { xmode = m; return *this; }
|
||||
|
||||
FORCE_INLINE Formatter& format(format_t f) { xfmt = f; return *this; }
|
||||
|
||||
friend epicsShareFunc std::ostream& operator<<(std::ostream& strm, const Formatter& format);
|
||||
friend void printRaw(std::ostream& strm, const PVStructure::Formatter& format, const PVStructure& cur);
|
||||
};
|
||||
|
||||
FORCE_INLINE Formatter stream() const { return Formatter(*this); }
|
||||
|
||||
private:
|
||||
|
||||
inline PVFieldPtr getSubFieldImpl(const std::string& name, bool throws) const {
|
||||
return getSubFieldImpl(name.c_str(), throws);
|
||||
}
|
||||
@@ -895,8 +924,12 @@ private:
|
||||
StructureConstPtr structurePtr;
|
||||
std::string extendsStructureName;
|
||||
friend class PVDataCreate;
|
||||
EPICS_NOT_COPYABLE(PVStructure)
|
||||
};
|
||||
|
||||
epicsShareFunc
|
||||
std::ostream& operator<<(std::ostream& strm, const PVStructure::Formatter& format);
|
||||
|
||||
/**
|
||||
* @brief PVUnion has a single subfield.
|
||||
*
|
||||
@@ -915,11 +948,11 @@ public:
|
||||
typedef PVUnion & reference;
|
||||
typedef const PVUnion & const_reference;
|
||||
|
||||
/**
|
||||
* Undefined index.
|
||||
* Default value upon PVUnion construction. Can be set by the user.
|
||||
* Corresponds to @c null value.
|
||||
*/
|
||||
/**
|
||||
* Undefined index.
|
||||
* Default value upon PVUnion construction. Can be set by the user.
|
||||
* Corresponds to @c null value.
|
||||
*/
|
||||
static const int32 UNDEFINED_INDEX;
|
||||
|
||||
/**
|
||||
@@ -937,11 +970,13 @@ public:
|
||||
|
||||
template<typename PVT>
|
||||
inline std::tr1::shared_ptr<PVT> get() {
|
||||
STATIC_ASSERT(PVT::isPVField); // only allow cast from PVField sub-class
|
||||
return std::tr1::dynamic_pointer_cast<PVT>(get());
|
||||
}
|
||||
|
||||
template<typename PVT>
|
||||
inline std::tr1::shared_ptr<const PVT> get() const {
|
||||
STATIC_ASSERT(PVT::isPVField); // only allow cast from PVField sub-class
|
||||
return std::tr1::dynamic_pointer_cast<const PVT>(get());
|
||||
}
|
||||
|
||||
@@ -955,6 +990,7 @@ public:
|
||||
|
||||
template<typename PVT>
|
||||
inline std::tr1::shared_ptr<PVT> select(int32 index) {
|
||||
STATIC_ASSERT(PVT::isPVField); // only allow cast from PVField sub-class
|
||||
return std::tr1::dynamic_pointer_cast<PVT>(select(index));
|
||||
}
|
||||
|
||||
@@ -1020,21 +1056,21 @@ public:
|
||||
* @param pflusher Interface to call when buffer is full.
|
||||
*/
|
||||
virtual void serialize(
|
||||
ByteBuffer *pbuffer,SerializableControl *pflusher) const OVERRIDE;
|
||||
ByteBuffer *pbuffer,SerializableControl *pflusher) const OVERRIDE FINAL;
|
||||
/**
|
||||
* Deserialize
|
||||
* @param pbuffer The byte buffer.
|
||||
* @param pflusher Interface to call when buffer is empty.
|
||||
*/
|
||||
virtual void deserialize(
|
||||
ByteBuffer *pbuffer,DeserializableControl *pflusher) OVERRIDE;
|
||||
ByteBuffer *pbuffer,DeserializableControl *pflusher) OVERRIDE FINAL;
|
||||
/**
|
||||
* Constructor
|
||||
* @param punion The introspection interface.
|
||||
*/
|
||||
explicit PVUnion(UnionConstPtr const & punion);
|
||||
|
||||
virtual std::ostream& dumpValue(std::ostream& o) const OVERRIDE;
|
||||
virtual std::ostream& dumpValue(std::ostream& o) const OVERRIDE FINAL;
|
||||
|
||||
void copy(const PVUnion& from);
|
||||
void copyUnchecked(const PVUnion& from);
|
||||
@@ -1045,9 +1081,10 @@ private:
|
||||
friend class PVDataCreate;
|
||||
UnionConstPtr unionPtr; // same as PVField::getField()
|
||||
|
||||
int32 selector;
|
||||
PVFieldPtr value;
|
||||
bool variant;
|
||||
int32 selector;
|
||||
PVFieldPtr value;
|
||||
bool variant;
|
||||
EPICS_NOT_COPYABLE(PVUnion)
|
||||
};
|
||||
|
||||
|
||||
@@ -1122,6 +1159,7 @@ namespace detail {
|
||||
return thaw(result);
|
||||
}
|
||||
|
||||
EPICS_NOT_COPYABLE(PVVectorStorage)
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
@@ -1152,67 +1190,40 @@ public:
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
virtual ~PVValueArray() {}
|
||||
virtual ~PVValueArray();
|
||||
|
||||
/**
|
||||
* Get introspection interface.
|
||||
*/
|
||||
virtual ArrayConstPtr getArray() const OVERRIDE
|
||||
{
|
||||
return std::tr1::static_pointer_cast<const Array>(this->getField());
|
||||
}
|
||||
virtual ArrayConstPtr getArray() const OVERRIDE FINAL;
|
||||
|
||||
std::ostream& dumpValue(std::ostream& o) const OVERRIDE
|
||||
{
|
||||
const_svector v(this->view());
|
||||
typename const_svector::const_iterator it(v.begin()),
|
||||
end(v.end());
|
||||
o << '[';
|
||||
if(it!=end) {
|
||||
o << print_cast(*it++);
|
||||
for(; it!=end; ++it)
|
||||
o << ',' << print_cast(*it);
|
||||
virtual std::ostream& dumpValue(std::ostream& o) const OVERRIDE FINAL;
|
||||
virtual std::ostream& dumpValue(std::ostream& o, size_t index) const OVERRIDE FINAL;
|
||||
|
||||
}
|
||||
return o << ']';
|
||||
}
|
||||
virtual size_t getLength() const OVERRIDE FINAL {return value.size();}
|
||||
virtual size_t getCapacity() const OVERRIDE FINAL {return value.capacity();}
|
||||
|
||||
virtual size_t getLength() const OVERRIDE {return value.size();}
|
||||
virtual size_t getCapacity() const OVERRIDE {return value.capacity();}
|
||||
virtual void setCapacity(size_t capacity) OVERRIDE FINAL;
|
||||
virtual void setLength(size_t length) OVERRIDE FINAL;
|
||||
|
||||
virtual void setCapacity(size_t capacity) OVERRIDE;
|
||||
virtual void setLength(size_t length) OVERRIDE;
|
||||
|
||||
virtual const_svector view() const OVERRIDE {return value;}
|
||||
virtual void swap(const_svector &other) OVERRIDE;
|
||||
virtual void replace(const const_svector& next) OVERRIDE;
|
||||
virtual const_svector view() const OVERRIDE FINAL {return value;}
|
||||
virtual void swap(const_svector &other) OVERRIDE FINAL;
|
||||
virtual void replace(const const_svector& next) OVERRIDE FINAL;
|
||||
|
||||
// from Serializable
|
||||
virtual void serialize(ByteBuffer *pbuffer,SerializableControl *pflusher) const OVERRIDE;
|
||||
virtual void deserialize(ByteBuffer *pbuffer,DeserializableControl *pflusher) OVERRIDE;
|
||||
virtual void serialize(ByteBuffer *pbuffer,SerializableControl *pflusher) const OVERRIDE FINAL;
|
||||
virtual void deserialize(ByteBuffer *pbuffer,DeserializableControl *pflusher) OVERRIDE FINAL;
|
||||
virtual void serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher, size_t offset, size_t count) const OVERRIDE;
|
||||
|
||||
std::ostream& dumpValue(std::ostream& o, size_t index) const OVERRIDE
|
||||
{
|
||||
return o << print_cast(this->view().at(index));
|
||||
}
|
||||
SerializableControl *pflusher, size_t offset, size_t count) const OVERRIDE FINAL;
|
||||
|
||||
protected:
|
||||
virtual void _getAsVoid(epics::pvData::shared_vector<const void>& out) const OVERRIDE
|
||||
{
|
||||
out = static_shared_vector_cast<const void>(this->view());
|
||||
}
|
||||
|
||||
virtual void _putFromVoid(const epics::pvData::shared_vector<const void>& in) OVERRIDE
|
||||
{
|
||||
// TODO: try to re-use storage
|
||||
this->replace(shared_vector_convert<const T>(in));
|
||||
}
|
||||
virtual void _getAsVoid(epics::pvData::shared_vector<const void>& out) const OVERRIDE FINAL;
|
||||
virtual void _putFromVoid(const epics::pvData::shared_vector<const void>& in) OVERRIDE FINAL;
|
||||
|
||||
explicit PVValueArray(ScalarArrayConstPtr const & scalar);
|
||||
const_svector value;
|
||||
friend class PVDataCreate;
|
||||
EPICS_NOT_COPYABLE(PVValueArray)
|
||||
};
|
||||
|
||||
|
||||
@@ -1241,24 +1252,24 @@ public:
|
||||
*/
|
||||
virtual ~PVValueArray() {}
|
||||
|
||||
virtual ArrayConstPtr getArray() const OVERRIDE
|
||||
virtual ArrayConstPtr getArray() const OVERRIDE FINAL
|
||||
{
|
||||
return std::tr1::static_pointer_cast<const Array>(structureArray);
|
||||
}
|
||||
|
||||
virtual size_t getLength() const OVERRIDE {return value.size();}
|
||||
virtual size_t getCapacity() const OVERRIDE {return value.capacity();}
|
||||
virtual size_t getLength() const OVERRIDE FINAL {return value.size();}
|
||||
virtual size_t getCapacity() const OVERRIDE FINAL {return value.capacity();}
|
||||
|
||||
/**
|
||||
* Set the array capacity.
|
||||
* @param capacity The length.
|
||||
*/
|
||||
virtual void setCapacity(size_t capacity) OVERRIDE;
|
||||
virtual void setCapacity(size_t capacity) OVERRIDE FINAL;
|
||||
/**
|
||||
* Set the array length.
|
||||
* @param length The length.
|
||||
*/
|
||||
virtual void setLength(std::size_t length) OVERRIDE;
|
||||
virtual void setLength(std::size_t length) OVERRIDE FINAL;
|
||||
|
||||
/**
|
||||
* Get the introspection interface
|
||||
@@ -1270,36 +1281,36 @@ public:
|
||||
* @param number The number of elements to add.
|
||||
* @return the new length of the array.
|
||||
*/
|
||||
virtual std::size_t append(std::size_t number) FINAL;
|
||||
std::size_t append(std::size_t number);
|
||||
/**
|
||||
* Remove elements from the array.
|
||||
* @param offset The offset of the first element to remove.
|
||||
* @param number The number of elements to remove.
|
||||
* @return (false,true) if the elements were removed.
|
||||
*/
|
||||
virtual bool remove(std::size_t offset,std::size_t number) FINAL;
|
||||
bool remove(std::size_t offset,std::size_t number);
|
||||
/**
|
||||
* Compress. This removes all null elements from the array.
|
||||
*/
|
||||
virtual void compress() FINAL;
|
||||
void compress();
|
||||
|
||||
virtual const_svector view() const OVERRIDE { return value; }
|
||||
virtual void swap(const_svector &other) OVERRIDE;
|
||||
virtual void replace(const const_svector &other) OVERRIDE {
|
||||
virtual const_svector view() const OVERRIDE FINAL { return value; }
|
||||
virtual void swap(const_svector &other) OVERRIDE FINAL;
|
||||
virtual void replace(const const_svector &other) OVERRIDE FINAL {
|
||||
checkLength(other.size());
|
||||
value = other;
|
||||
PVField::postPut();
|
||||
}
|
||||
|
||||
virtual void serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher) const OVERRIDE;
|
||||
SerializableControl *pflusher) const OVERRIDE FINAL;
|
||||
virtual void deserialize(ByteBuffer *buffer,
|
||||
DeserializableControl *pflusher) OVERRIDE;
|
||||
DeserializableControl *pflusher) OVERRIDE FINAL;
|
||||
virtual void serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher, std::size_t offset, std::size_t count) const OVERRIDE;
|
||||
SerializableControl *pflusher, std::size_t offset, std::size_t count) const OVERRIDE FINAL;
|
||||
|
||||
virtual std::ostream& dumpValue(std::ostream& o) const OVERRIDE;
|
||||
virtual std::ostream& dumpValue(std::ostream& o, std::size_t index) const OVERRIDE;
|
||||
virtual std::ostream& dumpValue(std::ostream& o) const OVERRIDE FINAL;
|
||||
virtual std::ostream& dumpValue(std::ostream& o, std::size_t index) const OVERRIDE FINAL;
|
||||
|
||||
void copy(const PVStructureArray& from);
|
||||
void copyUnchecked(const PVStructureArray& from);
|
||||
@@ -1310,6 +1321,7 @@ private:
|
||||
StructureArrayConstPtr structureArray;
|
||||
const_svector value;
|
||||
friend class PVDataCreate;
|
||||
EPICS_NOT_COPYABLE(PVValueArray)
|
||||
};
|
||||
|
||||
|
||||
@@ -1339,24 +1351,24 @@ public:
|
||||
*/
|
||||
virtual ~PVValueArray() {}
|
||||
|
||||
virtual ArrayConstPtr getArray() const OVERRIDE
|
||||
virtual ArrayConstPtr getArray() const OVERRIDE FINAL
|
||||
{
|
||||
return std::tr1::static_pointer_cast<const Array>(unionArray);
|
||||
}
|
||||
|
||||
virtual size_t getLength() const OVERRIDE {return value.size();}
|
||||
virtual size_t getCapacity() const OVERRIDE {return value.capacity();}
|
||||
virtual size_t getLength() const OVERRIDE FINAL {return value.size();}
|
||||
virtual size_t getCapacity() const OVERRIDE FINAL {return value.capacity();}
|
||||
|
||||
/**
|
||||
* Set the array capacity.
|
||||
* @param capacity The length.
|
||||
*/
|
||||
virtual void setCapacity(size_t capacity) OVERRIDE;
|
||||
virtual void setCapacity(size_t capacity) OVERRIDE FINAL;
|
||||
/**
|
||||
* Set the array length.
|
||||
* @param length The length.
|
||||
*/
|
||||
virtual void setLength(std::size_t length) OVERRIDE;
|
||||
virtual void setLength(std::size_t length) OVERRIDE FINAL;
|
||||
|
||||
/**
|
||||
* Get the introspection interface
|
||||
@@ -1368,36 +1380,36 @@ public:
|
||||
* @param number The number of elements to add.
|
||||
* @return the new length of the array.
|
||||
*/
|
||||
virtual std::size_t append(std::size_t number) FINAL;
|
||||
std::size_t append(std::size_t number);
|
||||
/**
|
||||
* Remove elements from the array.
|
||||
* @param offset The offset of the first element to remove.
|
||||
* @param number The number of elements to remove.
|
||||
* @return (false,true) if the elements were removed.
|
||||
*/
|
||||
virtual bool remove(std::size_t offset,std::size_t number) FINAL;
|
||||
bool remove(std::size_t offset,std::size_t number);
|
||||
/**
|
||||
* Compress. This removes all null elements from the array.
|
||||
*/
|
||||
virtual void compress() FINAL;
|
||||
void compress();
|
||||
|
||||
virtual const_svector view() const OVERRIDE { return value; }
|
||||
virtual void swap(const_svector &other) OVERRIDE;
|
||||
virtual void replace(const const_svector &other) OVERRIDE {
|
||||
virtual void replace(const const_svector &other) OVERRIDE FINAL {
|
||||
checkLength(other.size());
|
||||
value = other;
|
||||
PVField::postPut();
|
||||
}
|
||||
|
||||
virtual void serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher) const OVERRIDE;
|
||||
SerializableControl *pflusher) const OVERRIDE FINAL;
|
||||
virtual void deserialize(ByteBuffer *buffer,
|
||||
DeserializableControl *pflusher) OVERRIDE;
|
||||
DeserializableControl *pflusher) OVERRIDE FINAL;
|
||||
virtual void serialize(ByteBuffer *pbuffer,
|
||||
SerializableControl *pflusher, std::size_t offset, std::size_t count) const OVERRIDE;
|
||||
SerializableControl *pflusher, std::size_t offset, std::size_t count) const OVERRIDE FINAL;
|
||||
|
||||
virtual std::ostream& dumpValue(std::ostream& o) const OVERRIDE;
|
||||
virtual std::ostream& dumpValue(std::ostream& o, std::size_t index) const OVERRIDE;
|
||||
virtual std::ostream& dumpValue(std::ostream& o) const OVERRIDE FINAL;
|
||||
virtual std::ostream& dumpValue(std::ostream& o, std::size_t index) const OVERRIDE FINAL;
|
||||
|
||||
void copy(const PVUnionArray& from);
|
||||
void copyUnchecked(const PVUnionArray& from);
|
||||
@@ -1408,6 +1420,7 @@ private:
|
||||
UnionArrayConstPtr unionArray;
|
||||
const_svector value;
|
||||
friend class PVDataCreate;
|
||||
EPICS_NOT_COPYABLE(PVValueArray)
|
||||
};
|
||||
|
||||
|
||||
@@ -1623,6 +1636,7 @@ public:
|
||||
private:
|
||||
PVDataCreate();
|
||||
FieldCreatePtr fieldCreate;
|
||||
EPICS_NOT_COPYABLE(PVDataCreate)
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1650,9 +1664,3 @@ namespace std{
|
||||
}
|
||||
|
||||
#endif /* PVDATA_H */
|
||||
|
||||
/** @page Overview Documentation
|
||||
*
|
||||
* <a href = "pvDataCPP.html">pvData.html</a>
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -12,7 +12,11 @@
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
|
||||
#include <epicsAssert.h>
|
||||
|
||||
#include <pv/lock.h>
|
||||
#include <pv/noDefaultMethods.h>
|
||||
#include <pv/pvType.h>
|
||||
#include <pv/byteBuffer.h>
|
||||
@@ -20,6 +24,7 @@
|
||||
#include <pv/pvdVersion.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
#define PVD_DEPRECATED_52 PVD_DEPRECATED("See https://github.com/epics-base/pvDataCPP/issues/52")
|
||||
|
||||
/* C++11 keywords
|
||||
@code
|
||||
@@ -116,6 +121,13 @@ class UnionArray;
|
||||
|
||||
class BoundedString;
|
||||
|
||||
class PVField;
|
||||
class PVScalar;
|
||||
class PVScalarArray;
|
||||
class PVStructure;
|
||||
class PVUnion;
|
||||
template<typename T> class PVValueArray;
|
||||
|
||||
/**
|
||||
* typedef for a shared pointer to an immutable Field.
|
||||
*/
|
||||
@@ -339,14 +351,24 @@ public:
|
||||
*/
|
||||
virtual std::ostream& dump(std::ostream& o) const = 0;
|
||||
|
||||
//! Allocate a new instance
|
||||
//! @version Added after 7.0.0
|
||||
std::tr1::shared_ptr<PVField> build() const;
|
||||
|
||||
enum {isField=1};
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Constructor
|
||||
* @param type The field type.
|
||||
*/
|
||||
Field(Type type);
|
||||
void cacheCleanup();
|
||||
private:
|
||||
const Type m_fieldType;
|
||||
unsigned int m_hash;
|
||||
struct Helper;
|
||||
friend struct Helper;
|
||||
|
||||
friend class StructureArray;
|
||||
friend class Structure;
|
||||
@@ -354,8 +376,7 @@ private:
|
||||
friend class StandardField;
|
||||
friend class BasePVStructureArray;
|
||||
friend class FieldCreate;
|
||||
|
||||
struct Deleter{void operator()(Field *p){delete p;}};
|
||||
EPICS_NOT_COPYABLE(Field)
|
||||
};
|
||||
|
||||
epicsShareExtern std::ostream& operator<<(std::ostream& o, const Field& field);
|
||||
@@ -383,6 +404,10 @@ public:
|
||||
|
||||
virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const OVERRIDE;
|
||||
virtual void deserialize(ByteBuffer *buffer, DeserializableControl *control) OVERRIDE FINAL;
|
||||
|
||||
//! Allocate a new instance
|
||||
//! @version Added after 7.0.0
|
||||
std::tr1::shared_ptr<PVScalar> build() const;
|
||||
|
||||
protected:
|
||||
Scalar(ScalarType scalarType);
|
||||
@@ -394,6 +419,7 @@ private:
|
||||
friend class BoundedScalarArray;
|
||||
friend class FixedScalarArray;
|
||||
friend class BoundedString;
|
||||
EPICS_NOT_COPYABLE(Scalar)
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -418,6 +444,7 @@ protected:
|
||||
private:
|
||||
std::size_t maxLength;
|
||||
friend class FieldCreate;
|
||||
EPICS_NOT_COPYABLE(BoundedString)
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -452,6 +479,7 @@ protected:
|
||||
*/
|
||||
Array(Type type);
|
||||
|
||||
EPICS_NOT_COPYABLE(Array)
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -485,13 +513,17 @@ public:
|
||||
|
||||
virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const OVERRIDE;
|
||||
virtual void deserialize(ByteBuffer *buffer, DeserializableControl *control) OVERRIDE FINAL;
|
||||
|
||||
//! Allocate a new instance
|
||||
//! @version Added after 7.0.0
|
||||
std::tr1::shared_ptr<PVScalarArray> build() const;
|
||||
|
||||
protected:
|
||||
virtual ~ScalarArray();
|
||||
private:
|
||||
const std::string getIDScalarArrayLUT() const;
|
||||
ScalarType elementType;
|
||||
friend class FieldCreate;
|
||||
EPICS_NOT_COPYABLE(ScalarArray)
|
||||
};
|
||||
|
||||
|
||||
@@ -521,11 +553,11 @@ public:
|
||||
|
||||
virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const OVERRIDE FINAL;
|
||||
|
||||
protected:
|
||||
virtual ~BoundedScalarArray();
|
||||
private:
|
||||
std::size_t size;
|
||||
friend class FieldCreate;
|
||||
EPICS_NOT_COPYABLE(BoundedScalarArray)
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -553,11 +585,11 @@ public:
|
||||
|
||||
virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const OVERRIDE FINAL;
|
||||
|
||||
protected:
|
||||
virtual ~FixedScalarArray();
|
||||
private:
|
||||
std::size_t size;
|
||||
friend class FieldCreate;
|
||||
EPICS_NOT_COPYABLE(FixedScalarArray)
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -587,16 +619,22 @@ public:
|
||||
virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const OVERRIDE FINAL;
|
||||
virtual void deserialize(ByteBuffer *buffer, DeserializableControl *control) OVERRIDE FINAL;
|
||||
|
||||
//! Allocate a new instance
|
||||
//! @version Added after 7.0.0
|
||||
std::tr1::shared_ptr<PVValueArray<std::tr1::shared_ptr<PVStructure> > > build() const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Constructor.
|
||||
* @param structure The introspection interface for the elements.
|
||||
*/
|
||||
StructureArray(StructureConstPtr const & structure);
|
||||
public:
|
||||
virtual ~StructureArray();
|
||||
private:
|
||||
StructureConstPtr pstructure;
|
||||
friend class FieldCreate;
|
||||
EPICS_NOT_COPYABLE(StructureArray)
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -626,16 +664,22 @@ public:
|
||||
virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const OVERRIDE FINAL;
|
||||
virtual void deserialize(ByteBuffer *buffer, DeserializableControl *control) OVERRIDE FINAL;
|
||||
|
||||
//! Allocate a new instance
|
||||
//! @version Added after 7.0.0
|
||||
std::tr1::shared_ptr<PVValueArray<std::tr1::shared_ptr<PVUnion> > > build() const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Constructor.
|
||||
* @param _punion The introspection interface for the elements.
|
||||
*/
|
||||
UnionArray(UnionConstPtr const & _punion);
|
||||
public:
|
||||
virtual ~UnionArray();
|
||||
private:
|
||||
UnionConstPtr punion;
|
||||
friend class FieldCreate;
|
||||
EPICS_NOT_COPYABLE(UnionArray)
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -666,40 +710,96 @@ public:
|
||||
* @return The number of fields.
|
||||
*/
|
||||
std::size_t getNumberFields() const {return fieldNames.size();}
|
||||
|
||||
/**
|
||||
* Get the field for the specified fieldName.
|
||||
* @param fieldName The name of the field to get;
|
||||
* @return The introspection interface.
|
||||
* This will hold a null pointer if the field is not in the structure.
|
||||
* Lookup Field by name
|
||||
* @param fieldName Member field name. May not contain '.'
|
||||
* @return NULL if no member by this name.
|
||||
*/
|
||||
FieldConstPtr getField(std::string const &fieldName) const;
|
||||
|
||||
/** Lookup Field by name and cast to Field sub-class.
|
||||
* @param fieldName Member field name. May not contain '.'
|
||||
* @return NULL If no member by this name, or member exists, but has type other than FT.
|
||||
*/
|
||||
template<typename FT>
|
||||
std::tr1::shared_ptr<const FT> getField(std::string const &fieldName) const
|
||||
{
|
||||
FieldConstPtr field(getField(fieldName));
|
||||
if (field)
|
||||
return std::tr1::dynamic_pointer_cast<const FT>(field);
|
||||
else
|
||||
return std::tr1::shared_ptr<const FT>();
|
||||
STATIC_ASSERT(FT::isField); // only allow cast from Field sub-class
|
||||
return std::tr1::dynamic_pointer_cast<const FT>(getField(fieldName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the field for the specified fieldName.
|
||||
* @param index The index of the field to get;
|
||||
* @return The introspection interface.
|
||||
* This will hold a null pointer if the field is not in the structure.
|
||||
* Lookup Field by name
|
||||
* @param fieldName Member field name. May not contain '.'
|
||||
* @return Field pointer (never NULL)
|
||||
* @throws std::runtime_error If no member by this name
|
||||
*/
|
||||
FieldConstPtr getFieldT(std::string const &fieldName) const {return getFieldImpl(fieldName, true);};
|
||||
|
||||
/** Lookup Field by name and cast to Field sub-class.
|
||||
* @param fieldName Member field name. May not contain '.'
|
||||
* @return Field pointer (never NULL)
|
||||
* @throws std::runtime_error If no member by this name, or member exists, but has type other than FT.
|
||||
*/
|
||||
template<typename FT>
|
||||
std::tr1::shared_ptr<const FT> getFieldT(std::string const &fieldName) const
|
||||
{
|
||||
STATIC_ASSERT(FT::isField); // only allow cast from Field sub-class
|
||||
std::tr1::shared_ptr<const FT> result(
|
||||
std::tr1::dynamic_pointer_cast<const FT>(getFieldT(fieldName))
|
||||
);
|
||||
|
||||
if (!result)
|
||||
throw std::runtime_error("Wrong Field type");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Lookup Field by index, within this Structure.
|
||||
* @param index Index of member in this structure. @code index>=0 && index<getNumberFields() @endcode
|
||||
* @return Field pointer (never NULL)
|
||||
* @throws std::out_of_range If index >= getNumberFields()
|
||||
*/
|
||||
const FieldConstPtr& getField(std::size_t index) const {return fields.at(index);}
|
||||
|
||||
/** Lookup Field by index, within this Structure.
|
||||
* @param index Index of member in this structure. @code index>=0 && index<getNumberFields() @endcode
|
||||
* @return NULL if member is not a sub-class of FT
|
||||
* @throws std::out_of_range If index >= getNumberFields()
|
||||
*/
|
||||
template<typename FT>
|
||||
std::tr1::shared_ptr<const FT> getField(std::size_t index) const
|
||||
{
|
||||
const FieldConstPtr& field(getField(index));
|
||||
if (field)
|
||||
return std::tr1::dynamic_pointer_cast<const FT>(field);
|
||||
else
|
||||
return std::tr1::shared_ptr<const FT>();
|
||||
STATIC_ASSERT(FT::isField); // only allow cast from Field sub-class
|
||||
return std::tr1::dynamic_pointer_cast<const FT>(getField(index));
|
||||
}
|
||||
|
||||
/** Lookup Field by index, within this Structure.
|
||||
* @param index Index of member in this structure. @code index>=0 && index<getNumberFields() @endcode
|
||||
* @return Field pointer (never NULL)
|
||||
* @throws std::out_of_range If index >= getNumberFields()
|
||||
*/
|
||||
FieldConstPtr getFieldT(std::size_t index) const {return fields.at(index);}
|
||||
|
||||
/** Lookup Field by index, within this Structure.
|
||||
* @param index Index of member in this structure. @code index>=0 && index<getNumberFields() @endcode
|
||||
* @return Field pointer (never NULL)
|
||||
* @throws std::out_of_range If index >= getNumberFields()
|
||||
* @throws std::runtime_error If member is not a sub-class of FT
|
||||
*/
|
||||
template<typename FT>
|
||||
std::tr1::shared_ptr<const FT> getFieldT(std::size_t index) const
|
||||
{
|
||||
STATIC_ASSERT(FT::isField); // only allow cast from Field sub-class
|
||||
std::tr1::shared_ptr<const FT> result(
|
||||
std::tr1::dynamic_pointer_cast<const FT>(getFieldT(index))
|
||||
);
|
||||
|
||||
if (!result)
|
||||
throw std::runtime_error("Wrong Field type");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -731,7 +831,11 @@ public:
|
||||
|
||||
virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const OVERRIDE FINAL;
|
||||
virtual void deserialize(ByteBuffer *buffer, DeserializableControl *control) OVERRIDE FINAL;
|
||||
|
||||
|
||||
//! Allocate a new instance
|
||||
//! @version Added after 7.0.0
|
||||
std::tr1::shared_ptr<PVStructure> build() const;
|
||||
|
||||
protected:
|
||||
Structure(StringArray const & fieldNames, FieldConstPtrArray const & fields, std::string const & id = defaultId());
|
||||
private:
|
||||
@@ -739,10 +843,12 @@ private:
|
||||
FieldConstPtrArray fields;
|
||||
std::string id;
|
||||
|
||||
FieldConstPtr getFieldImpl(const std::string& fieldName, bool throws) const;
|
||||
void dumpFields(std::ostream& o) const;
|
||||
|
||||
friend class FieldCreate;
|
||||
friend class Union;
|
||||
EPICS_NOT_COPYABLE(Structure)
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -784,40 +890,96 @@ public:
|
||||
* @return The number of fields.
|
||||
*/
|
||||
std::size_t getNumberFields() const {return fieldNames.size();}
|
||||
|
||||
/**
|
||||
* Get the field for the specified fieldName.
|
||||
* @param fieldName The name of the field to get;
|
||||
* @return The introspection interface.
|
||||
* This will hold a null pointer if the field is not in the union.
|
||||
* Lookup Field by name
|
||||
* @param fieldName Member field name. May not contain '.'
|
||||
* @return NULL if no member by this name.
|
||||
*/
|
||||
FieldConstPtr getField(std::string const &fieldName) const;
|
||||
|
||||
/** Lookup Field by name and cast to Field sub-class.
|
||||
* @param fieldName Member field name. May not contain '.'
|
||||
* @return NULL If no member by this name, or member exists, but has type other than FT.
|
||||
*/
|
||||
template<typename FT>
|
||||
std::tr1::shared_ptr<const FT> getField(std::string const &fieldName) const
|
||||
{
|
||||
FieldConstPtr field = getField(fieldName);
|
||||
if (field)
|
||||
return std::tr1::dynamic_pointer_cast<const FT>(field);
|
||||
else
|
||||
return std::tr1::shared_ptr<const FT>();
|
||||
STATIC_ASSERT(FT::isField); // only allow cast from Field sub-class
|
||||
return std::tr1::dynamic_pointer_cast<const FT>(getField(fieldName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the field for the specified fieldName.
|
||||
* @param index The index of the field to get;
|
||||
* @return The introspection interface.
|
||||
* This will hold a null pointer if the field is not in the union.
|
||||
* Lookup Field by name
|
||||
* @param fieldName Member field name. May not contain '.'
|
||||
* @return Field pointer (never NULL)
|
||||
* @throws std::runtime_error If no member by this name
|
||||
*/
|
||||
FieldConstPtr getFieldT(std::string const &fieldName) const {return getFieldImpl(fieldName, true);};
|
||||
|
||||
/** Lookup Field by name and cast to Field sub-class.
|
||||
* @param fieldName Member field name. May not contain '.'
|
||||
* @return Field pointer (never NULL)
|
||||
* @throws std::runtime_error If no member by this name, or member exists, but has type other than FT.
|
||||
*/
|
||||
template<typename FT>
|
||||
std::tr1::shared_ptr<const FT> getFieldT(std::string const &fieldName) const
|
||||
{
|
||||
STATIC_ASSERT(FT::isField); // only allow cast from Field sub-class
|
||||
std::tr1::shared_ptr<const FT> result(
|
||||
std::tr1::dynamic_pointer_cast<const FT>(getFieldT(fieldName))
|
||||
);
|
||||
|
||||
if (!result)
|
||||
throw std::runtime_error("Wrong Field type");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Lookup Field by index, within this Union.
|
||||
* @param index Index of member in this union. @code index>=0 && index<getNumberFields() @endcode
|
||||
* @return Field pointer (never NULL)
|
||||
* @throws std::out_of_range If index >= getNumberFields()
|
||||
*/
|
||||
FieldConstPtr getField(std::size_t index) const {return fields.at(index);}
|
||||
|
||||
/** Lookup Field by index, within this Union.
|
||||
* @param index Index of member in this union. @code index>=0 && index<getNumberFields() @endcode
|
||||
* @return NULL if member is not a sub-class of FT
|
||||
* @throws std::out_of_range If index >= getNumberFields()
|
||||
*/
|
||||
template<typename FT>
|
||||
std::tr1::shared_ptr<const FT> getField(std::size_t index) const
|
||||
{
|
||||
FieldConstPtr field = getField(index);
|
||||
if (field)
|
||||
return std::tr1::dynamic_pointer_cast<const FT>(field);
|
||||
else
|
||||
return std::tr1::shared_ptr<const FT>();
|
||||
STATIC_ASSERT(FT::isField); // only allow cast from Field sub-class
|
||||
return std::tr1::dynamic_pointer_cast<const FT>(getField(index));
|
||||
}
|
||||
|
||||
/** Lookup Field by index, within this Union.
|
||||
* @param index Index of member in this union. @code index>=0 && index<getNumberFields() @endcode
|
||||
* @return Field pointer (never NULL)
|
||||
* @throws std::out_of_range If index >= getNumberFields()
|
||||
*/
|
||||
FieldConstPtr getFieldT(std::size_t index) const {return fields.at(index);}
|
||||
|
||||
/** Lookup Field by index, within this Structure.
|
||||
* @param index Index of member in this structure. @code index>=0 && index<getNumberFields() @endcode
|
||||
* @return Field pointer (never NULL)
|
||||
* @throws std::out_of_range If index >= getNumberFields()
|
||||
* @throws std::runtime_error If member is not a sub-class of FT
|
||||
*/
|
||||
template<typename FT>
|
||||
std::tr1::shared_ptr<const FT> getFieldT(std::size_t index) const
|
||||
{
|
||||
STATIC_ASSERT(FT::isField); // only allow cast from Field sub-class
|
||||
std::tr1::shared_ptr<const FT> result(
|
||||
std::tr1::dynamic_pointer_cast<const FT>(getFieldT(index))
|
||||
);
|
||||
|
||||
if (!result)
|
||||
throw std::runtime_error("Wrong Field type");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -865,6 +1027,10 @@ public:
|
||||
|
||||
virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const OVERRIDE FINAL;
|
||||
virtual void deserialize(ByteBuffer *buffer, DeserializableControl *control) OVERRIDE FINAL;
|
||||
|
||||
//! Allocate a new instance
|
||||
//! @version Added after 7.0.0
|
||||
std::tr1::shared_ptr<PVUnion> build() const;
|
||||
|
||||
protected:
|
||||
Union();
|
||||
@@ -873,11 +1039,13 @@ private:
|
||||
StringArray fieldNames;
|
||||
FieldConstPtrArray fields;
|
||||
std::string id;
|
||||
|
||||
|
||||
FieldConstPtr getFieldImpl(const std::string& fieldName, bool throws) const;
|
||||
void dumpFields(std::ostream& o) const;
|
||||
|
||||
friend class FieldCreate;
|
||||
friend class Structure;
|
||||
EPICS_NOT_COPYABLE(Union)
|
||||
};
|
||||
|
||||
class FieldCreate;
|
||||
@@ -897,12 +1065,18 @@ class epicsShareClass FieldBuilder :
|
||||
public std::tr1::enable_shared_from_this<FieldBuilder>
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Set ID of an object to be created.
|
||||
* @param id id to be set.
|
||||
//! Create a new instance of in-line @c Field builder.
|
||||
//! @version Added after 7.0.0
|
||||
static FieldBuilderPtr begin();
|
||||
//! Create a new instance of in-line @c Field builder pre-initialized with and existing Structure
|
||||
static FieldBuilderPtr begin(StructureConstPtr S);
|
||||
|
||||
/**
|
||||
* Set ID of an object to be created.
|
||||
* @param id id to be set.
|
||||
* @return this instance of a @c FieldBuilder.
|
||||
*/
|
||||
FieldBuilderPtr setId(std::string const & id);
|
||||
*/
|
||||
FieldBuilderPtr setId(std::string const & id);
|
||||
|
||||
/**
|
||||
* Add a @c Scalar.
|
||||
@@ -918,7 +1092,7 @@ public:
|
||||
* @param maxLength a string maximum length.
|
||||
* @return this instance of a @c FieldBuilder.
|
||||
*/
|
||||
FieldBuilderPtr addBoundedString(std::string const & name, std::size_t maxLength);
|
||||
FieldBuilderPtr addBoundedString(std::string const & name, std::size_t maxLength) PVD_DEPRECATED_52;
|
||||
|
||||
/**
|
||||
* Add a @c Field (e.g. @c Structure, @c Union).
|
||||
@@ -943,7 +1117,7 @@ public:
|
||||
* @param size Array fixed size.
|
||||
* @return this instance of a @c FieldBuilder.
|
||||
*/
|
||||
FieldBuilderPtr addFixedArray(std::string const & name, ScalarType scalarType, std::size_t size);
|
||||
FieldBuilderPtr addFixedArray(std::string const & name, ScalarType scalarType, std::size_t size) PVD_DEPRECATED_52;
|
||||
|
||||
/**
|
||||
* Add bounded-size array of @c Scalar elements.
|
||||
@@ -952,7 +1126,7 @@ public:
|
||||
* @param bound Array maximum capacity (size).
|
||||
* @return this instance of a @c FieldBuilder.
|
||||
*/
|
||||
FieldBuilderPtr addBoundedArray(std::string const & name, ScalarType scalarType, std::size_t bound);
|
||||
FieldBuilderPtr addBoundedArray(std::string const & name, ScalarType scalarType, std::size_t bound) PVD_DEPRECATED_52;
|
||||
|
||||
/**
|
||||
* Add array of @c Field elements.
|
||||
@@ -1032,20 +1206,20 @@ private:
|
||||
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);
|
||||
std::string const & nestedName,
|
||||
Type nestedClassToBuild, bool nestedArray);
|
||||
|
||||
const Field *findField(const std::string& name, Type ftype);
|
||||
|
||||
void reset();
|
||||
FieldConstPtr createFieldInternal(Type type);
|
||||
|
||||
void reset();
|
||||
FieldConstPtr createFieldInternal(Type type);
|
||||
|
||||
friend class FieldCreate;
|
||||
|
||||
const FieldCreatePtr fieldCreate;
|
||||
|
||||
std::string id;
|
||||
bool idSet;
|
||||
std::string id;
|
||||
bool idSet;
|
||||
|
||||
StringArray fieldNames;
|
||||
FieldConstPtrArray fields;
|
||||
@@ -1069,11 +1243,11 @@ class epicsShareClass FieldCreate {
|
||||
friend struct detail::field_factory;
|
||||
public:
|
||||
static const 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.
|
||||
* @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.
|
||||
@@ -1092,7 +1266,7 @@ public:
|
||||
* @return a @c BoundedString interface for the newly created object.
|
||||
* @throws IllegalArgumentException if maxLength == 0.
|
||||
*/
|
||||
BoundedStringConstPtr createBoundedString(std::size_t maxLength) const;
|
||||
BoundedStringConstPtr createBoundedString(std::size_t maxLength) const PVD_DEPRECATED_52;
|
||||
/**
|
||||
* Create an @c Array field, variable size array.
|
||||
* @param elementType The @c ScalarType for array elements
|
||||
@@ -1105,14 +1279,14 @@ public:
|
||||
* @param size Fixed array size.
|
||||
* @return An @c Array Interface for the newly created object.
|
||||
*/
|
||||
ScalarArrayConstPtr createFixedScalarArray(ScalarType elementType, std::size_t size) const;
|
||||
ScalarArrayConstPtr createFixedScalarArray(ScalarType elementType, std::size_t size) const PVD_DEPRECATED_52;
|
||||
/**
|
||||
* Create an @c Array field, bounded size array.
|
||||
* @param elementType The @c ScalarType for array elements
|
||||
* @param bound Array maximum capacity.
|
||||
* @return An @c Array Interface for the newly created object.
|
||||
*/
|
||||
ScalarArrayConstPtr createBoundedScalarArray(ScalarType elementType, std::size_t bound) const;
|
||||
ScalarArrayConstPtr createBoundedScalarArray(ScalarType elementType, std::size_t bound) const PVD_DEPRECATED_52;
|
||||
/**
|
||||
* Create an @c Array field that is has element type @c Structure
|
||||
* @param structure The @c Structure for each array element.
|
||||
@@ -1141,7 +1315,7 @@ public:
|
||||
* @return a @c Structure interface for the newly created object.
|
||||
*/
|
||||
StructureConstPtr createStructure (
|
||||
std::string const & id,
|
||||
std::string const & id,
|
||||
StringArray const & fieldNames,
|
||||
FieldConstPtrArray const & fields) const;
|
||||
/**
|
||||
@@ -1177,7 +1351,7 @@ public:
|
||||
* @return a @c Union interface for the newly created object.
|
||||
*/
|
||||
UnionConstPtr createUnion (
|
||||
std::string const & id,
|
||||
std::string const & id,
|
||||
StringArray const & fieldNames,
|
||||
FieldConstPtrArray const & fields) const;
|
||||
/**
|
||||
@@ -1211,11 +1385,20 @@ public:
|
||||
|
||||
private:
|
||||
FieldCreate();
|
||||
|
||||
|
||||
// const after ctor
|
||||
std::vector<ScalarConstPtr> scalars;
|
||||
std::vector<ScalarArrayConstPtr> scalarArrays;
|
||||
UnionConstPtr variantUnion;
|
||||
UnionArrayConstPtr variantUnionArray;
|
||||
|
||||
mutable Mutex mutex;
|
||||
typedef std::multimap<unsigned int, Field*> cache_t;
|
||||
mutable cache_t cache;
|
||||
|
||||
struct Helper;
|
||||
friend class Field;
|
||||
EPICS_NOT_COPYABLE(FieldCreate)
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1267,32 +1450,35 @@ OP(pvDouble, double)
|
||||
OP(pvString, std::string)
|
||||
#undef OP
|
||||
|
||||
bool epicsShareExtern operator==(const Field&, const Field&);
|
||||
bool epicsShareExtern operator==(const Scalar&, const Scalar&);
|
||||
bool epicsShareExtern operator==(const ScalarArray&, const ScalarArray&);
|
||||
bool epicsShareExtern operator==(const Structure&, const Structure&);
|
||||
bool epicsShareExtern operator==(const StructureArray&, const StructureArray&);
|
||||
bool epicsShareExtern operator==(const Union&, const Union&);
|
||||
bool epicsShareExtern operator==(const UnionArray&, const UnionArray&);
|
||||
bool epicsShareExtern operator==(const BoundedString&, const BoundedString&);
|
||||
bool epicsShareExtern compare(const Field&, const Field&);
|
||||
bool epicsShareExtern compare(const Scalar&, const Scalar&);
|
||||
bool epicsShareExtern compare(const ScalarArray&, const ScalarArray&);
|
||||
bool epicsShareExtern compare(const Structure&, const Structure&);
|
||||
bool epicsShareExtern compare(const StructureArray&, const StructureArray&);
|
||||
bool epicsShareExtern compare(const Union&, const Union&);
|
||||
bool epicsShareExtern compare(const UnionArray&, const UnionArray&);
|
||||
bool epicsShareExtern compare(const BoundedString&, const BoundedString&);
|
||||
|
||||
static inline bool operator!=(const Field& a, const Field& b)
|
||||
{return !(a==b);}
|
||||
static inline bool operator!=(const Scalar& a, const Scalar& b)
|
||||
{return !(a==b);}
|
||||
static inline bool operator!=(const ScalarArray& a, const ScalarArray& b)
|
||||
{return !(a==b);}
|
||||
static inline bool operator!=(const Structure& a, const Structure& b)
|
||||
{return !(a==b);}
|
||||
static inline bool operator!=(const StructureArray& a, const StructureArray& b)
|
||||
{return !(a==b);}
|
||||
static inline bool operator!=(const Union& a, const Union& b)
|
||||
{return !(a==b);}
|
||||
static inline bool operator!=(const UnionArray& a, const UnionArray& b)
|
||||
{return !(a==b);}
|
||||
static inline bool operator!=(const BoundedString& a, const BoundedString& b)
|
||||
{return !(a==b);}
|
||||
/** Equality with other Field
|
||||
*
|
||||
* The creation process of class FieldCreate ensures that identical field definitions
|
||||
* will share the same instance. So pointer equality is sufficient to show defintion
|
||||
* equality. If in doubt, compare() will do an full test.
|
||||
*/
|
||||
#define MAKE_COMPARE(CLASS) \
|
||||
static FORCE_INLINE bool operator==(const CLASS& a, const CLASS& b) {return (void*)&a==(void*)&b;} \
|
||||
static FORCE_INLINE bool operator!=(const CLASS& a, const CLASS& b) {return !(a==b);}
|
||||
|
||||
MAKE_COMPARE(Field)
|
||||
MAKE_COMPARE(Scalar)
|
||||
MAKE_COMPARE(ScalarArray)
|
||||
MAKE_COMPARE(Structure)
|
||||
MAKE_COMPARE(StructureArray)
|
||||
MAKE_COMPARE(Union)
|
||||
MAKE_COMPARE(UnionArray)
|
||||
MAKE_COMPARE(BoundedString)
|
||||
|
||||
#undef MAKE_COMPARE
|
||||
}}
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,10 +14,6 @@
|
||||
#ifndef PVTYPE_H
|
||||
#define PVTYPE_H
|
||||
|
||||
#if defined(_WIN32) && !defined(NOMINMAX)
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) && !defined(_MINGW)
|
||||
#pragma warning( push )
|
||||
#pragma warning(disable: 4251)
|
||||
@@ -34,6 +30,16 @@ typedef unsigned int uintptr_t;
|
||||
#define INT64_MAX (0x7fffffffffffffffLL)
|
||||
#define UINT64_MAX (0xffffffffffffffffULL)
|
||||
#endif
|
||||
#elif _MSC_VER==1500
|
||||
#include <epicsTypes.h>
|
||||
typedef epicsUInt8 uint8_t;
|
||||
typedef epicsInt8 int8_t;
|
||||
typedef epicsUInt16 uint16_t;
|
||||
typedef epicsInt16 int16_t;
|
||||
typedef epicsUInt32 uint32_t;
|
||||
typedef epicsInt32 int32_t;
|
||||
typedef epicsUInt64 uint64_t;
|
||||
typedef epicsInt64 int64_t;
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
@@ -61,6 +61,7 @@ typedef std::tr1::shared_ptr<StandardField> StandardFieldPtr;
|
||||
* }
|
||||
*/
|
||||
class epicsShareClass StandardField {
|
||||
static void once(void*);
|
||||
public:
|
||||
/**
|
||||
* getStandardField returns the singleton.
|
||||
@@ -131,123 +132,105 @@ public:
|
||||
* create an alarm structure
|
||||
* @return The const shared pointer to the structure.
|
||||
*/
|
||||
StructureConstPtr alarm();
|
||||
inline const StructureConstPtr& alarm() const { return alarmField; }
|
||||
/**
|
||||
* create a timeStamp structure
|
||||
* @return The const shared pointer to the structure.
|
||||
*/
|
||||
StructureConstPtr timeStamp();
|
||||
inline const StructureConstPtr& timeStamp() const { return timeStampField; }
|
||||
/**
|
||||
* create a display structure
|
||||
* @return The const shared pointer to the structure.
|
||||
*/
|
||||
StructureConstPtr display();
|
||||
inline const StructureConstPtr& display() const { return displayField; }
|
||||
/**
|
||||
* create a control structure
|
||||
* @return The const shared pointer to the structure.
|
||||
*/
|
||||
StructureConstPtr control();
|
||||
inline const StructureConstPtr& control() const { return controlField; }
|
||||
/**
|
||||
* create a boolean alarm structure
|
||||
* @return The const shared pointer to the structure.
|
||||
*/
|
||||
StructureConstPtr booleanAlarm();
|
||||
inline const StructureConstPtr& booleanAlarm() const { return booleanAlarmField; }
|
||||
/**
|
||||
* create a byte alarm structure
|
||||
* @return The const shared pointer to the structure.
|
||||
*/
|
||||
StructureConstPtr byteAlarm();
|
||||
inline const StructureConstPtr& byteAlarm() const { return byteAlarmField; }
|
||||
/**
|
||||
* create a unsigned byte alarm structure
|
||||
* @return The const shared pointer to the structure.
|
||||
*/
|
||||
StructureConstPtr ubyteAlarm();
|
||||
inline const StructureConstPtr& ubyteAlarm() const { return ubyteAlarmField; }
|
||||
/**
|
||||
* create a short alarm structure
|
||||
* @return The const shared pointer to the structure.
|
||||
*/
|
||||
StructureConstPtr shortAlarm();
|
||||
inline const StructureConstPtr& shortAlarm() const { return shortAlarmField; }
|
||||
/**
|
||||
* create a unsigned short alarm structure
|
||||
* @return The const shared pointer to the structure.
|
||||
*/
|
||||
StructureConstPtr ushortAlarm();
|
||||
inline const StructureConstPtr& ushortAlarm() const { return ushortAlarmField; }
|
||||
/**
|
||||
* create an int alarm structure
|
||||
* @return The const shared pointer to the structure.
|
||||
*/
|
||||
StructureConstPtr intAlarm();
|
||||
inline const StructureConstPtr& intAlarm() const { return intAlarmField; }
|
||||
/**
|
||||
* create a unsigned int alarm structure
|
||||
* @return The const shared pointer to the structure.
|
||||
*/
|
||||
StructureConstPtr uintAlarm();
|
||||
inline const StructureConstPtr& uintAlarm() const { return uintAlarmField; }
|
||||
/**
|
||||
* create a long alarm structure
|
||||
* @return The const shared pointer to the structure.
|
||||
*/
|
||||
StructureConstPtr longAlarm();
|
||||
inline const StructureConstPtr& longAlarm() const { return longAlarmField; }
|
||||
/**
|
||||
* create a unsigned long alarm structure
|
||||
* @return The const shared pointer to the structure.
|
||||
*/
|
||||
StructureConstPtr ulongAlarm();
|
||||
inline const StructureConstPtr& ulongAlarm() const { return ulongAlarmField; }
|
||||
/**
|
||||
* create a float alarm structure
|
||||
* @return The const shared pointer to the structure.
|
||||
*/
|
||||
StructureConstPtr floatAlarm();
|
||||
inline const StructureConstPtr& floatAlarm() const { return floatAlarmField; }
|
||||
/**
|
||||
* create a double alarm structure
|
||||
* @return The const shared pointer to the structure.
|
||||
*/
|
||||
StructureConstPtr doubleAlarm();
|
||||
inline const StructureConstPtr& doubleAlarm() const { return doubleAlarmField; }
|
||||
/**
|
||||
* create an enumerated alarm structure
|
||||
* @return The const shared pointer to the structure.
|
||||
*/
|
||||
StructureConstPtr enumeratedAlarm();
|
||||
inline const StructureConstPtr& enumeratedAlarm() const { return enumeratedAlarmField; }
|
||||
private:
|
||||
StandardField();
|
||||
void init();
|
||||
StructureConstPtr createProperties(
|
||||
std::string id,FieldConstPtr field,std::string properties);
|
||||
FieldCreatePtr fieldCreate;
|
||||
std::string notImplemented;
|
||||
std::string valueFieldName;
|
||||
StructureConstPtr alarmField;
|
||||
StructureConstPtr timeStampField;
|
||||
StructureConstPtr displayField;
|
||||
StructureConstPtr controlField;
|
||||
StructureConstPtr booleanAlarmField;
|
||||
StructureConstPtr byteAlarmField;
|
||||
StructureConstPtr shortAlarmField;
|
||||
StructureConstPtr intAlarmField;
|
||||
StructureConstPtr longAlarmField;
|
||||
StructureConstPtr ubyteAlarmField;
|
||||
StructureConstPtr ushortAlarmField;
|
||||
StructureConstPtr uintAlarmField;
|
||||
StructureConstPtr ulongAlarmField;
|
||||
StructureConstPtr floatAlarmField;
|
||||
StructureConstPtr doubleAlarmField;
|
||||
StructureConstPtr enumeratedAlarmField;
|
||||
void createAlarm();
|
||||
void createTimeStamp();
|
||||
void createDisplay();
|
||||
void createControl();
|
||||
void createBooleanAlarm();
|
||||
void createByteAlarm();
|
||||
void createShortAlarm();
|
||||
void createIntAlarm();
|
||||
void createLongAlarm();
|
||||
void createUByteAlarm();
|
||||
void createUShortAlarm();
|
||||
void createUIntAlarm();
|
||||
void createULongAlarm();
|
||||
void createFloatAlarm();
|
||||
void createDoubleAlarm();
|
||||
void createEnumeratedAlarm();
|
||||
//friend StandardFieldPtr getStandardField();
|
||||
const FieldCreatePtr fieldCreate;
|
||||
const std::string notImplemented;
|
||||
const std::string valueFieldName;
|
||||
const StructureConstPtr alarmField;
|
||||
const StructureConstPtr timeStampField;
|
||||
const StructureConstPtr displayField;
|
||||
const StructureConstPtr controlField;
|
||||
const StructureConstPtr booleanAlarmField;
|
||||
const StructureConstPtr byteAlarmField;
|
||||
const StructureConstPtr shortAlarmField;
|
||||
const StructureConstPtr intAlarmField;
|
||||
const StructureConstPtr longAlarmField;
|
||||
const StructureConstPtr ubyteAlarmField;
|
||||
const StructureConstPtr ushortAlarmField;
|
||||
const StructureConstPtr uintAlarmField;
|
||||
const StructureConstPtr ulongAlarmField;
|
||||
const StructureConstPtr floatAlarmField;
|
||||
const StructureConstPtr doubleAlarmField;
|
||||
const StructureConstPtr enumeratedAlarmField;
|
||||
};
|
||||
|
||||
FORCE_INLINE const StandardFieldPtr& getStandardField() {
|
||||
|
||||
@@ -74,7 +74,7 @@ struct ValueBuilder::child_scalar_array : public ValueBuilder::child
|
||||
{
|
||||
builder->addArray(name, array.original_type());
|
||||
}
|
||||
virtual void store(const PVFieldPtr& val)
|
||||
virtual void store(const PVFieldPtr& val) OVERRIDE FINAL
|
||||
{
|
||||
if(val->getField()->getType()!=scalarArray)
|
||||
THROW_EXCEPTION2(std::logic_error, "Scalar Array type mis-match");
|
||||
@@ -285,7 +285,7 @@ PVStructure::shared_pointer ValueBuilder::buildPVStructure() const
|
||||
type = tbuild->createStructure();
|
||||
}
|
||||
|
||||
PVStructure::shared_pointer root(getPVDataCreate()->createPVStructure(type));
|
||||
PVStructure::shared_pointer root(type->build());
|
||||
|
||||
child_struct::storeStruct(*this, root);
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ static bool checkBitSetPVField(
|
||||
|
||||
bool BitSetUtil::compress(BitSetPtr const &bitSet,PVStructurePtr const &pvStructure)
|
||||
{
|
||||
return checkBitSetPVField(pvStructure,bitSet,0);
|
||||
return checkBitSetPVField(pvStructure,bitSet,0);
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
@@ -12,6 +12,8 @@ include $(PVDATA_TEST)/pv/Makefile
|
||||
include $(PVDATA_TEST)/property/Makefile
|
||||
include $(PVDATA_TEST)/copy/Makefile
|
||||
|
||||
PROD_SRCS_RTEMS += rtemsTestData.c
|
||||
|
||||
# pvDataAllTests runs all the test programs in a known working order.
|
||||
testHarness_SRCS += pvDataAllTests.c
|
||||
|
||||
@@ -30,6 +32,11 @@ TESTSPEC_RTEMS = pvdTestHarness.$(MUNCH_SUFFIX); pvDataAllTests
|
||||
# Build test scripts for hosts
|
||||
TESTSCRIPTS_HOST += $(TESTS:%=%.t)
|
||||
|
||||
ifneq ($(filter $(T_A),$(CROSS_COMPILER_RUNTEST_ARCHS)),)
|
||||
TESTPROD = $(TESTPROD_HOST)
|
||||
TESTSCRIPTS += $(TESTS:%=%.t)
|
||||
endif
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
|
||||
|
||||
|
||||
@@ -6,8 +6,3 @@ TESTPROD_HOST += testCreateRequest
|
||||
testCreateRequest_SRCS = testCreateRequest.cpp
|
||||
testHarness_SRCS += testCreateRequest.cpp
|
||||
TESTS += testCreateRequest
|
||||
|
||||
TESTPROD_HOST += testPVCopy
|
||||
testPVCopy_SRCS += testPVCopy.cpp
|
||||
testHarness_SRCS += testPVCopy.cpp
|
||||
TESTS += testPVCopy
|
||||
|
||||
@@ -15,7 +15,9 @@
|
||||
#include <pv/pvUnitTest.h>
|
||||
#include <testMain.h>
|
||||
|
||||
#include <pv/current_function.h>
|
||||
#include <pv/createRequest.h>
|
||||
#include <pv/bitSet.h>
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -201,8 +203,8 @@ static void testCreateRequestInternal() {
|
||||
|
||||
request = string("record[process=true,xxx=yyy]")
|
||||
+ "putField(power.value)"
|
||||
+ "getField(alarm,timeStamp,power{value,alarm},"
|
||||
+ "current{value,alarm},voltage{value,alarm})";
|
||||
+ "getField(alarm,timeStamp,power{value,alarm},"
|
||||
+ "current{value,alarm},voltage{value,alarm})";
|
||||
|
||||
pvRequest = createRequest->createRequest(request);
|
||||
if(!pvRequest) { cout<< createRequest->getMessage() << endl;}
|
||||
@@ -239,10 +241,10 @@ static void testCreateRequestInternal() {
|
||||
testPass("request %s",request.c_str());
|
||||
|
||||
request = string("record[process=true,xxx=yyy]")
|
||||
+ "putField(power.value)"
|
||||
+ "getField(alarm,timeStamp,power{value,alarm},"
|
||||
+ "current{value,alarm},voltage{value,alarm},"
|
||||
+ "ps0{alarm,timeStamp,power{value,alarm},current{value,alarm},voltage{value,alarm}},"
|
||||
+ "putField(power.value)"
|
||||
+ "getField(alarm,timeStamp,power{value,alarm},"
|
||||
+ "current{value,alarm},voltage{value,alarm},"
|
||||
+ "ps0{alarm,timeStamp,power{value,alarm},current{value,alarm},voltage{value,alarm}},"
|
||||
+ "ps1{alarm,timeStamp,power{value,alarm},current{value,alarm},voltage{value,alarm}}"
|
||||
+ ")";
|
||||
|
||||
@@ -335,16 +337,396 @@ static void testBadRequest()
|
||||
|
||||
testOk1(!!C->createRequest("field(value)"));
|
||||
testOk1(C->getMessage().empty());
|
||||
|
||||
// duplicate fieldName C
|
||||
// correct is: "field(A,C{D,E.F})"
|
||||
testThrows(std::invalid_argument, createRequest("field(A,C.D,C.E.F)"));
|
||||
}
|
||||
|
||||
static
|
||||
StructureConstPtr maskingType = getFieldCreate()->createFieldBuilder()
|
||||
->add("A", pvInt)
|
||||
->add("B", pvInt)
|
||||
->addNestedStructure("C")
|
||||
->add("D", pvInt)
|
||||
->addNestedStructure("E")
|
||||
->add("F", pvInt)
|
||||
->endNested()
|
||||
->endNested()
|
||||
->createStructure();
|
||||
|
||||
static
|
||||
void testMapper(PVRequestMapper::mode_t mode)
|
||||
{
|
||||
testDiag("=== %s mode==%d", CURRENT_FUNCTION, (int)mode);
|
||||
{
|
||||
testDiag("Map full structure");
|
||||
|
||||
PVStructurePtr base(getPVDataCreate()->createPVStructure(maskingType));
|
||||
PVRequestMapper mapper(*base, *createRequest(""), mode);
|
||||
|
||||
testEqual(mapper.requested(), maskingType);
|
||||
|
||||
testEqual(mapper.requestedMask(), BitSet().set(0)
|
||||
.set(base->getSubFieldT("A")->getFieldOffset())
|
||||
.set(base->getSubFieldT("B")->getFieldOffset())
|
||||
.set(base->getSubFieldT("C")->getFieldOffset())
|
||||
.set(base->getSubFieldT("C.D")->getFieldOffset())
|
||||
.set(base->getSubFieldT("C.E")->getFieldOffset())
|
||||
.set(base->getSubFieldT("C.E.F")->getFieldOffset()));
|
||||
|
||||
PVStructurePtr req(getPVDataCreate()->createPVStructure(mapper.requested()));
|
||||
|
||||
base->getSubFieldT<PVInt>("A")->put(1);
|
||||
base->getSubFieldT<PVInt>("B")->put(42);
|
||||
|
||||
BitSet output;
|
||||
mapper.copyBaseToRequested(*base, BitSet().set(0), *req, output);
|
||||
|
||||
testFieldEqual<PVInt>(req, "A", 1);
|
||||
testFieldEqual<PVInt>(req, "B", 42);
|
||||
|
||||
req->getSubFieldT<PVInt>("A")->put(2);
|
||||
req->getSubFieldT<PVInt>("B")->put(43);
|
||||
|
||||
mapper.copyBaseFromRequested(*base, output, *req, BitSet().set(0));
|
||||
|
||||
testFieldEqual<PVInt>(req, "A", 2);
|
||||
testFieldEqual<PVInt>(req, "B", 43);
|
||||
testEqual(mapper.requestedMask(), BitSet().set(0).set(1).set(2).set(3).set(4).set(5).set(6));
|
||||
}
|
||||
{
|
||||
testDiag("Map single leaf field");
|
||||
|
||||
PVStructurePtr base(getPVDataCreate()->createPVStructure(maskingType));
|
||||
PVRequestMapper mapper(*base, *createRequest("field(B)"), mode);
|
||||
|
||||
if(mode==PVRequestMapper::Slice)
|
||||
testNotEqual(mapper.requested(), maskingType);
|
||||
else
|
||||
testEqual(mapper.requested(), maskingType);
|
||||
|
||||
testEqual(mapper.requestedMask(), BitSet().set(0)
|
||||
.set(base->getSubFieldT("B")->getFieldOffset()));
|
||||
|
||||
PVStructurePtr req(getPVDataCreate()->createPVStructure(mapper.requested()));
|
||||
|
||||
if(mode==PVRequestMapper::Slice)
|
||||
testOk1(!req->getSubField("A"));
|
||||
|
||||
base->getSubFieldT<PVScalar>("A")->putFrom<int32>(11);
|
||||
base->getSubFieldT<PVScalar>("B")->putFrom<int32>(42);
|
||||
|
||||
BitSet output;
|
||||
mapper.copyBaseToRequested(*base, BitSet().set(0), *req, output);
|
||||
|
||||
if(mode!=PVRequestMapper::Slice)
|
||||
testFieldEqual<PVInt>(req, "A", 0);
|
||||
testFieldEqual<PVInt>(req, "B", 42);
|
||||
|
||||
req->getSubFieldT<PVScalar>("B")->putFrom<int32>(43);
|
||||
|
||||
mapper.copyBaseFromRequested(*base, output, *req, BitSet().set(0));
|
||||
|
||||
testFieldEqual<PVInt>(req, "B", 43);
|
||||
testEqual(mapper.requestedMask(), BitSet().set(0)
|
||||
.set(base->getSubFieldT("B")->getFieldOffset()));
|
||||
|
||||
BitSet cmp;
|
||||
mapper.maskBaseToRequested(BitSet().set(base->getSubFieldT("B")->getFieldOffset()), cmp);
|
||||
testEqual(cmp, BitSet()
|
||||
.set(req->getSubFieldT("B")->getFieldOffset()));
|
||||
|
||||
cmp.clear();
|
||||
mapper.maskBaseFromRequested(cmp, BitSet()
|
||||
.set(req->getSubFieldT("B")->getFieldOffset()));
|
||||
testEqual(cmp, BitSet()
|
||||
.set(base->getSubFieldT("B")->getFieldOffset()));
|
||||
}
|
||||
{
|
||||
testDiag("Map two sub-fields");
|
||||
|
||||
PVStructurePtr base(getPVDataCreate()->createPVStructure(maskingType));
|
||||
PVRequestMapper mapper(*base, *createRequest("B,C.D"), mode);
|
||||
|
||||
if(mode==PVRequestMapper::Slice)
|
||||
testNotEqual(mapper.requested(), maskingType);
|
||||
else
|
||||
testEqual(mapper.requested(), maskingType);
|
||||
|
||||
testEqual(mapper.requestedMask(), BitSet().set(0)
|
||||
.set(base->getSubFieldT("B")->getFieldOffset())
|
||||
.set(base->getSubFieldT("C")->getFieldOffset())
|
||||
.set(base->getSubFieldT("C.D")->getFieldOffset()));
|
||||
|
||||
PVStructurePtr req(getPVDataCreate()->createPVStructure(mapper.requested()));
|
||||
|
||||
if(mode==PVRequestMapper::Slice)
|
||||
testOk1(!req->getSubField("A"));
|
||||
|
||||
base->getSubFieldT<PVScalar>("A")->putFrom<int32>(11);
|
||||
base->getSubFieldT<PVScalar>("B")->putFrom<int32>(1);
|
||||
base->getSubFieldT<PVScalar>("C.D")->putFrom<int32>(42);
|
||||
|
||||
BitSet output;
|
||||
mapper.copyBaseToRequested(*base, BitSet().set(0), *req, output);
|
||||
|
||||
if(mode!=PVRequestMapper::Slice)
|
||||
testFieldEqual<PVInt>(req, "A", 0);
|
||||
testFieldEqual<PVInt>(req, "B", 1);
|
||||
testFieldEqual<PVInt>(req, "C.D", 42);
|
||||
|
||||
req->getSubFieldT<PVScalar>("B")->putFrom<int32>(2);
|
||||
req->getSubFieldT<PVScalar>("C.D")->putFrom<int32>(43);
|
||||
|
||||
mapper.copyBaseFromRequested(*base, output, *req, BitSet().set(0));
|
||||
|
||||
testFieldEqual<PVInt>(req, "B", 2);
|
||||
testFieldEqual<PVInt>(req, "C.D", 43);
|
||||
testEqual(mapper.requestedMask(), BitSet().set(0)
|
||||
.set(base->getSubFieldT("B")->getFieldOffset())
|
||||
.set(base->getSubFieldT("C")->getFieldOffset())
|
||||
.set(base->getSubFieldT("C.D")->getFieldOffset()));
|
||||
}
|
||||
{
|
||||
testDiag("Map entire sub-structure");
|
||||
|
||||
PVStructurePtr base(getPVDataCreate()->createPVStructure(maskingType));
|
||||
PVRequestMapper mapper(*base, *createRequest("field(C.E)"), mode);
|
||||
|
||||
if(mode==PVRequestMapper::Slice)
|
||||
testNotEqual(mapper.requested(), maskingType);
|
||||
else
|
||||
testEqual(mapper.requested(), maskingType);
|
||||
|
||||
testEqual(mapper.requestedMask(), BitSet().set(0)
|
||||
.set(base->getSubFieldT("C")->getFieldOffset())
|
||||
.set(base->getSubFieldT("C.E")->getFieldOffset())
|
||||
.set(base->getSubFieldT("C.E.F")->getFieldOffset()));
|
||||
|
||||
|
||||
PVStructurePtr req(getPVDataCreate()->createPVStructure(mapper.requested()));
|
||||
|
||||
if(mode==PVRequestMapper::Slice)
|
||||
testOk1(!req->getSubField("A"));
|
||||
|
||||
base->getSubFieldT<PVScalar>("A")->putFrom<int32>(11);
|
||||
base->getSubFieldT<PVScalar>("C.E.F")->putFrom<int32>(42);
|
||||
|
||||
BitSet output;
|
||||
mapper.copyBaseToRequested(*base, BitSet().set(0), *req, output);
|
||||
|
||||
if(mode!=PVRequestMapper::Slice)
|
||||
testFieldEqual<PVInt>(req, "A", 0);
|
||||
testFieldEqual<PVInt>(req, "C.E.F", 42);
|
||||
|
||||
req->getSubFieldT<PVScalar>("C.E.F")->putFrom<int32>(43);
|
||||
|
||||
mapper.copyBaseFromRequested(*base, output, *req, BitSet().set(0));
|
||||
|
||||
testFieldEqual<PVInt>(req, "C.E.F", 43);
|
||||
testEqual(mapper.requestedMask(), BitSet().set(0)
|
||||
.set(base->getSubFieldT("C")->getFieldOffset())
|
||||
.set(base->getSubFieldT("C.E")->getFieldOffset())
|
||||
.set(base->getSubFieldT("C.E.F")->getFieldOffset()));
|
||||
|
||||
BitSet cmp;
|
||||
mapper.maskBaseToRequested(BitSet()
|
||||
.set(base->getSubFieldT("C")->getFieldOffset()), cmp);
|
||||
testEqual(cmp, BitSet()
|
||||
.set(req->getSubFieldT("C")->getFieldOffset())
|
||||
.set(req->getSubFieldT("C.E")->getFieldOffset())
|
||||
.set(req->getSubFieldT("C.E.F")->getFieldOffset()));
|
||||
|
||||
cmp.clear();
|
||||
mapper.maskBaseFromRequested(cmp, BitSet()
|
||||
.set(req->getSubFieldT("C")->getFieldOffset()));
|
||||
testEqual(cmp, BitSet()
|
||||
.set(base->getSubFieldT("C")->getFieldOffset())
|
||||
.set(base->getSubFieldT("C.E")->getFieldOffset())
|
||||
.set(base->getSubFieldT("C.E.F")->getFieldOffset()));
|
||||
}
|
||||
}
|
||||
|
||||
struct MapperMask {
|
||||
PVStructurePtr base, req;
|
||||
BitSet bmask, rmask;
|
||||
PVRequestMapper mapper;
|
||||
|
||||
MapperMask(PVRequestMapper::mode_t mode) {
|
||||
base = getPVDataCreate()->createPVStructure(maskingType);
|
||||
mapper.compute(*base, *createRequest("field(B,C.E)"), mode);
|
||||
req = getPVDataCreate()->createPVStructure(mapper.requested());
|
||||
reset();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
base->getSubFieldT<PVScalar>("B")->putFrom<int32>(1);
|
||||
base->getSubFieldT<PVScalar>("C.E.F")->putFrom<int32>(3);
|
||||
req->getSubFieldT<PVScalar>("B")->putFrom<int32>(11);
|
||||
req->getSubFieldT<PVScalar>("C.E.F")->putFrom<int32>(13);
|
||||
}
|
||||
|
||||
void check(int32 bB, int32 bCEF, int32 rB, int32 rCEF) {
|
||||
testFieldEqual<PVInt>(base, "B", bB);
|
||||
testFieldEqual<PVInt>(base, "C.E.F", bCEF);
|
||||
testFieldEqual<PVInt>(req, "B", rB);
|
||||
testFieldEqual<PVInt>(req, "C.E.F", rCEF);
|
||||
}
|
||||
|
||||
void testEmptyMaskB2R() {
|
||||
mapper.copyBaseToRequested(*base, bmask, *req, rmask);
|
||||
check(1, 3, 11, 13);
|
||||
testEqual(bmask, BitSet());
|
||||
testEqual(rmask, BitSet());
|
||||
}
|
||||
|
||||
void testEmptyMaskR2B() {
|
||||
mapper.copyBaseFromRequested(*base, bmask, *req, rmask);
|
||||
check(1, 3, 11, 13);
|
||||
testEqual(bmask, BitSet());
|
||||
testEqual(rmask, BitSet());
|
||||
}
|
||||
|
||||
void testAllMaskB2R() {
|
||||
bmask.set(0);
|
||||
mapper.copyBaseToRequested(*base, bmask, *req, rmask);
|
||||
check(1, 3, 1, 3);
|
||||
testEqual(rmask, BitSet()
|
||||
.set(0)
|
||||
.set(req->getSubFieldT("B")->getFieldOffset())
|
||||
.set(req->getSubFieldT("C")->getFieldOffset())
|
||||
.set(req->getSubFieldT("C.E")->getFieldOffset())
|
||||
.set(req->getSubFieldT("C.E.F")->getFieldOffset()));
|
||||
}
|
||||
|
||||
void testAllMaskR2B() {
|
||||
rmask.set(0);
|
||||
mapper.copyBaseFromRequested(*base, bmask, *req, rmask);
|
||||
check(11, 13, 11, 13);
|
||||
testEqual(bmask, BitSet()
|
||||
.set(0)
|
||||
.set(base->getSubFieldT("B")->getFieldOffset())
|
||||
.set(base->getSubFieldT("C")->getFieldOffset())
|
||||
.set(base->getSubFieldT("C.E")->getFieldOffset())
|
||||
.set(base->getSubFieldT("C.E.F")->getFieldOffset()));
|
||||
}
|
||||
|
||||
void testMaskOneB2R() {
|
||||
bmask.set(base->getSubFieldT("B")->getFieldOffset());
|
||||
mapper.copyBaseToRequested(*base, bmask, *req, rmask);
|
||||
check(1, 3, 1, 13);
|
||||
testEqual(rmask, BitSet()
|
||||
.set(req->getSubFieldT("B")->getFieldOffset()));
|
||||
}
|
||||
|
||||
void testMaskOneR2B() {
|
||||
rmask.set(req->getSubFieldT("B")->getFieldOffset());
|
||||
mapper.copyBaseFromRequested(*base, bmask, *req, rmask);
|
||||
check(11, 3, 11, 13);
|
||||
testEqual(bmask, BitSet()
|
||||
.set(base->getSubFieldT("B")->getFieldOffset()));
|
||||
}
|
||||
|
||||
void testMaskOtherB2R() {
|
||||
bmask.set(base->getSubFieldT("C.E.F")->getFieldOffset());
|
||||
mapper.copyBaseToRequested(*base, bmask, *req, rmask);
|
||||
check(1, 3, 11, 3);
|
||||
testEqual(rmask, BitSet()
|
||||
.set(req->getSubFieldT("C.E.F")->getFieldOffset()));
|
||||
}
|
||||
|
||||
void testMaskOtherR2B() {
|
||||
rmask.set(req->getSubFieldT("C.E.F")->getFieldOffset());
|
||||
mapper.copyBaseFromRequested(*base, bmask, *req, rmask);
|
||||
check(1, 13, 11, 13);
|
||||
testEqual(bmask, BitSet()
|
||||
.set(base->getSubFieldT("C.E.F")->getFieldOffset()));
|
||||
}
|
||||
|
||||
void testMaskSub1B2R() {
|
||||
bmask.set(base->getSubFieldT("C.E")->getFieldOffset());
|
||||
mapper.copyBaseToRequested(*base, bmask, *req, rmask);
|
||||
check(1, 3, 11, 3);
|
||||
testEqual(rmask, BitSet()
|
||||
.set(req->getSubFieldT("C.E")->getFieldOffset())
|
||||
.set(req->getSubFieldT("C.E.F")->getFieldOffset()));
|
||||
}
|
||||
|
||||
void testMaskSub1R2B() {
|
||||
rmask.set(req->getSubFieldT("C.E")->getFieldOffset());
|
||||
mapper.copyBaseFromRequested(*base, bmask, *req, rmask);
|
||||
check(1, 13, 11, 13);
|
||||
testEqual(bmask, BitSet()
|
||||
.set(base->getSubFieldT("C.E")->getFieldOffset())
|
||||
.set(base->getSubFieldT("C.E.F")->getFieldOffset()));
|
||||
}
|
||||
|
||||
void testMaskSub2B2R() {
|
||||
bmask.set(base->getSubFieldT("C")->getFieldOffset());
|
||||
mapper.copyBaseToRequested(*base, bmask, *req, rmask);
|
||||
check(1, 3, 11, 3);
|
||||
testEqual(rmask, BitSet()
|
||||
.set(req->getSubFieldT("C")->getFieldOffset())
|
||||
.set(req->getSubFieldT("C.E")->getFieldOffset())
|
||||
.set(req->getSubFieldT("C.E.F")->getFieldOffset()));
|
||||
}
|
||||
|
||||
void testMaskSub2R2B() {
|
||||
rmask.set(req->getSubFieldT("C")->getFieldOffset());
|
||||
mapper.copyBaseFromRequested(*base, bmask, *req, rmask);
|
||||
check(1, 13, 11, 13);
|
||||
testEqual(bmask, BitSet()
|
||||
.set(base->getSubFieldT("C")->getFieldOffset())
|
||||
.set(base->getSubFieldT("C.E")->getFieldOffset())
|
||||
.set(base->getSubFieldT("C.E.F")->getFieldOffset()));
|
||||
}
|
||||
};
|
||||
|
||||
void testMaskWarn()
|
||||
{
|
||||
PVStructurePtr base(getPVDataCreate()->createPVStructure(maskingType));
|
||||
PVRequestMapper mapper(*base, *createRequest("field(B,invalid)"), PVRequestMapper::Slice);
|
||||
|
||||
testEqual(mapper.warnings(), "No field 'invalid' ");
|
||||
}
|
||||
|
||||
void testMaskErr()
|
||||
{
|
||||
PVStructurePtr base(getPVDataCreate()->createPVStructure(maskingType));
|
||||
testThrows(std::runtime_error, PVRequestMapper mapper(*base, *createRequest("field(invalid)"), PVRequestMapper::Slice));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
MAIN(testCreateRequest)
|
||||
{
|
||||
testPlan(126);
|
||||
testPlan(315);
|
||||
testCreateRequestInternal();
|
||||
testBadRequest();
|
||||
testMapper(PVRequestMapper::Slice);
|
||||
testMapper(PVRequestMapper::Mask);
|
||||
#undef TEST_METHOD
|
||||
#define TEST_METHOD(KLASS, METHOD) \
|
||||
{ \
|
||||
testDiag("------- %s::%s Mask --------", #KLASS, #METHOD); \
|
||||
{ KLASS inst(PVRequestMapper::Mask); inst.METHOD(); } \
|
||||
testDiag("------- %s::%s Slice --------", #KLASS, #METHOD); \
|
||||
{ KLASS inst(PVRequestMapper::Slice); inst.METHOD(); } \
|
||||
}
|
||||
TEST_METHOD(MapperMask, testEmptyMaskB2R);
|
||||
TEST_METHOD(MapperMask, testEmptyMaskR2B);
|
||||
TEST_METHOD(MapperMask, testAllMaskB2R);
|
||||
TEST_METHOD(MapperMask, testAllMaskR2B);
|
||||
TEST_METHOD(MapperMask, testMaskOneB2R);
|
||||
TEST_METHOD(MapperMask, testMaskOneR2B);
|
||||
TEST_METHOD(MapperMask, testMaskOtherB2R);
|
||||
TEST_METHOD(MapperMask, testMaskOtherR2B);
|
||||
TEST_METHOD(MapperMask, testMaskSub1B2R);
|
||||
TEST_METHOD(MapperMask, testMaskSub1R2B);
|
||||
TEST_METHOD(MapperMask, testMaskSub2B2R);
|
||||
TEST_METHOD(MapperMask, testMaskSub2R2B);
|
||||
testMaskWarn();
|
||||
testMaskErr();
|
||||
return testDone();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,295 +0,0 @@
|
||||
/*testPVCopyMain.cpp */
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
/**
|
||||
* @author mrk
|
||||
*/
|
||||
|
||||
/* Author: Marty Kraimer */
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
|
||||
#include <epicsUnitTest.h>
|
||||
#include <testMain.h>
|
||||
|
||||
#include <pv/standardField.h>
|
||||
#include <pv/standardPVField.h>
|
||||
#include <pv/convert.h>
|
||||
#include <pv/pvCopy.h>
|
||||
#include <pv/createRequest.h>
|
||||
|
||||
|
||||
using namespace std;
|
||||
using std::tr1::static_pointer_cast;
|
||||
using namespace epics::pvData;
|
||||
|
||||
static void testPVScalar(
|
||||
string const & valueNameMaster,
|
||||
string const & valueNameCopy,
|
||||
PVStructurePtr const & pvMaster,
|
||||
PVCopyPtr const & pvCopy)
|
||||
{
|
||||
PVStructurePtr pvStructureCopy;
|
||||
PVScalarPtr pvValueMaster;
|
||||
PVScalarPtr pvValueCopy;
|
||||
BitSetPtr bitSet;
|
||||
ConvertPtr convert = getConvert();
|
||||
|
||||
pvValueMaster = pvMaster->getSubField<PVScalar>(valueNameMaster);
|
||||
convert->fromDouble(pvValueMaster,.04);
|
||||
StructureConstPtr structure = pvCopy->getStructure();
|
||||
pvStructureCopy = pvCopy->createPVStructure();
|
||||
pvValueCopy = pvStructureCopy->getSubField<PVScalar>(valueNameCopy);
|
||||
bitSet = BitSetPtr(new BitSet(pvStructureCopy->getNumberFields()));
|
||||
pvCopy->initCopy(pvStructureCopy, bitSet);
|
||||
|
||||
convert->fromDouble(pvValueMaster,.06);
|
||||
testOk1(convert->toDouble(pvValueCopy)==.04);
|
||||
pvCopy->updateCopySetBitSet(pvStructureCopy,bitSet);
|
||||
testOk1(convert->toDouble(pvValueCopy)==.06);
|
||||
testOk1(bitSet->get(pvValueCopy->getFieldOffset()));
|
||||
|
||||
pvCopy->getCopyOffset(pvValueMaster);
|
||||
|
||||
bitSet->clear();
|
||||
convert->fromDouble(pvValueMaster,1.0);
|
||||
|
||||
bitSet->set(0);
|
||||
testOk1(convert->toDouble(pvValueCopy)==0.06);
|
||||
pvCopy->updateCopyFromBitSet(pvStructureCopy,bitSet);
|
||||
testOk1(convert->toDouble(pvValueCopy)==1.0);
|
||||
|
||||
convert->fromDouble(pvValueCopy,2.0);
|
||||
bitSet->set(0);
|
||||
|
||||
testOk1(convert->toDouble(pvValueMaster)==1.0);
|
||||
pvCopy->updateMaster(pvStructureCopy,bitSet);
|
||||
testOk1(convert->toDouble(pvValueMaster)==2.0);
|
||||
|
||||
}
|
||||
|
||||
static void testPVScalarArray(
|
||||
string const & valueNameMaster,
|
||||
string const & valueNameCopy,
|
||||
PVStructurePtr const & pvMaster,
|
||||
PVCopyPtr const & pvCopy)
|
||||
{
|
||||
PVStructurePtr pvStructureCopy;
|
||||
PVScalarArrayPtr pvValueMaster;
|
||||
PVScalarArrayPtr pvValueCopy;
|
||||
BitSetPtr bitSet;
|
||||
size_t n = 5;
|
||||
shared_vector<double> values(n);
|
||||
shared_vector<const double> cvalues;
|
||||
|
||||
pvValueMaster = pvMaster->getSubField<PVScalarArray>(valueNameMaster);
|
||||
for(size_t i=0; i<n; i++) values[i] = i;
|
||||
const shared_vector<const double> xxx(freeze(values));
|
||||
pvValueMaster->putFrom(xxx);
|
||||
StructureConstPtr structure = pvCopy->getStructure();
|
||||
|
||||
pvStructureCopy = pvCopy->createPVStructure();
|
||||
pvValueCopy = pvStructureCopy->getSubField<PVScalarArray>(valueNameCopy);
|
||||
bitSet = BitSetPtr(new BitSet(pvStructureCopy->getNumberFields()));
|
||||
pvCopy->initCopy(pvStructureCopy, bitSet);
|
||||
|
||||
values.resize(n);
|
||||
for(size_t i=0; i<n; i++) values[i] = i + .06;
|
||||
const shared_vector<const double> yyy(freeze(values));
|
||||
pvValueMaster->putFrom(yyy);
|
||||
pvValueCopy->getAs(cvalues);
|
||||
testOk1(cvalues[0]==0.0);
|
||||
pvCopy->updateCopySetBitSet(pvStructureCopy,bitSet);
|
||||
pvValueCopy->getAs(cvalues);
|
||||
testOk1(cvalues[0]==0.06);
|
||||
|
||||
pvCopy->getCopyOffset(pvValueMaster);
|
||||
|
||||
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);
|
||||
|
||||
bitSet->set(0);
|
||||
pvValueCopy->getAs(cvalues);
|
||||
testOk1(cvalues[0]==0.06);
|
||||
pvCopy->updateCopyFromBitSet(pvStructureCopy,bitSet);
|
||||
pvValueCopy->getAs(cvalues);
|
||||
testOk1(cvalues[0]==1.0);
|
||||
|
||||
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);
|
||||
|
||||
pvValueMaster->getAs(cvalues);
|
||||
testOk1(cvalues[0]==2.0);
|
||||
pvCopy->updateMaster(pvStructureCopy,bitSet);
|
||||
pvValueMaster->getAs(cvalues);
|
||||
testOk1(cvalues[0]==1.0);
|
||||
}
|
||||
|
||||
static void scalarTest()
|
||||
{
|
||||
testDiag("scalarTest()");
|
||||
PVStructurePtr pvMaster;
|
||||
string request;
|
||||
PVStructurePtr pvRequest;
|
||||
PVFieldPtr pvMasterField;
|
||||
PVCopyPtr pvCopy;
|
||||
string valueNameMaster;
|
||||
string valueNameCopy;
|
||||
|
||||
StandardPVFieldPtr standardPVField = getStandardPVField();
|
||||
pvMaster = standardPVField->scalar(pvDouble,"alarm,timeStamp,display");
|
||||
|
||||
valueNameMaster = request = "value";
|
||||
CreateRequest::shared_pointer createRequest = CreateRequest::create();
|
||||
pvRequest = createRequest->createRequest(request);
|
||||
pvCopy = PVCopy::create(pvMaster,pvRequest,"");
|
||||
valueNameCopy = "value";
|
||||
testPVScalar(valueNameMaster,valueNameCopy,pvMaster,pvCopy);
|
||||
request = "";
|
||||
valueNameMaster = "value";
|
||||
pvRequest = createRequest->createRequest(request);
|
||||
|
||||
pvCopy = PVCopy::create(pvMaster,pvRequest,"");
|
||||
valueNameCopy = "value";
|
||||
testPVScalar(valueNameMaster,valueNameCopy,pvMaster,pvCopy);
|
||||
request = "alarm,timeStamp,value";
|
||||
valueNameMaster = "value";
|
||||
pvRequest = createRequest->createRequest(request);
|
||||
|
||||
pvCopy = PVCopy::create(pvMaster,pvRequest,"");
|
||||
valueNameCopy = "value";
|
||||
testPVScalar(valueNameMaster,valueNameCopy,pvMaster,pvCopy);
|
||||
}
|
||||
|
||||
static void arrayTest()
|
||||
{
|
||||
testDiag("arrayTest");
|
||||
|
||||
PVStructurePtr pvMaster;
|
||||
string request;
|
||||
PVStructurePtr pvRequest;
|
||||
PVFieldPtr pvMasterField;
|
||||
PVCopyPtr pvCopy;
|
||||
string valueNameMaster;
|
||||
string valueNameCopy;
|
||||
|
||||
CreateRequest::shared_pointer createRequest = CreateRequest::create();
|
||||
StandardPVFieldPtr standardPVField = getStandardPVField();
|
||||
pvMaster = standardPVField->scalarArray(pvDouble,"alarm,timeStamp");
|
||||
valueNameMaster = request = "value";
|
||||
pvRequest = createRequest->createRequest(request);
|
||||
|
||||
pvCopy = PVCopy::create(pvMaster,pvRequest,"");
|
||||
valueNameCopy = "value";
|
||||
testPVScalarArray(valueNameMaster,valueNameCopy,pvMaster,pvCopy);
|
||||
request = "";
|
||||
valueNameMaster = "value";
|
||||
pvRequest = createRequest->createRequest(request);
|
||||
|
||||
pvCopy = PVCopy::create(pvMaster,pvRequest,"");
|
||||
valueNameCopy = "value";
|
||||
testPVScalarArray(valueNameMaster,valueNameCopy,pvMaster,pvCopy);
|
||||
request = "alarm,timeStamp,value";
|
||||
valueNameMaster = "value";
|
||||
pvRequest = createRequest->createRequest(request);
|
||||
|
||||
pvCopy = PVCopy::create(pvMaster,pvRequest,"");
|
||||
valueNameCopy = "value";
|
||||
testPVScalarArray(valueNameMaster,valueNameCopy,pvMaster,pvCopy);
|
||||
}
|
||||
|
||||
static PVStructurePtr createPowerSupply()
|
||||
{
|
||||
FieldCreatePtr fieldCreate = getFieldCreate();
|
||||
StandardFieldPtr standardField = getStandardField();
|
||||
PVDataCreatePtr pvDataCreate = getPVDataCreate();
|
||||
|
||||
size_t nfields = 5;
|
||||
StringArray names;
|
||||
names.reserve(nfields);
|
||||
FieldConstPtrArray powerSupply;
|
||||
powerSupply.reserve(nfields);
|
||||
names.push_back("alarm");
|
||||
powerSupply.push_back(standardField->alarm());
|
||||
names.push_back("timeStamp");
|
||||
powerSupply.push_back(standardField->timeStamp());
|
||||
string properties("alarm,display");
|
||||
names.push_back("voltage");
|
||||
powerSupply.push_back(standardField->scalar(pvDouble,properties));
|
||||
names.push_back("power");
|
||||
powerSupply.push_back(standardField->scalar(pvDouble,properties));
|
||||
names.push_back("current");
|
||||
powerSupply.push_back(standardField->scalar(pvDouble,properties));
|
||||
return pvDataCreate->createPVStructure(
|
||||
fieldCreate->createStructure(names,powerSupply));
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void powerSupplyTest()
|
||||
{
|
||||
testDiag("powerSupplyTest");
|
||||
|
||||
PVStructurePtr pvMaster;
|
||||
string request;
|
||||
PVStructurePtr pvRequest;
|
||||
PVFieldPtr pvMasterField;
|
||||
PVCopyPtr pvCopy;
|
||||
string builder;
|
||||
string valueNameMaster;
|
||||
string valueNameCopy;
|
||||
|
||||
CreateRequest::shared_pointer createRequest = CreateRequest::create();
|
||||
pvMaster = createPowerSupply();
|
||||
valueNameMaster = request = "power.value";
|
||||
pvRequest = createRequest->createRequest(request);
|
||||
|
||||
pvCopy = PVCopy::create(pvMaster,pvRequest,"");
|
||||
valueNameCopy = "power.value";
|
||||
testPVScalar(valueNameMaster,valueNameCopy,pvMaster,pvCopy);
|
||||
request = "";
|
||||
valueNameMaster = "power.value";
|
||||
pvRequest = createRequest->createRequest(request);
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
pvCopy = PVCopy::create(pvMaster,pvRequest,"");
|
||||
valueNameCopy = "power.value";
|
||||
testPVScalar(valueNameMaster,valueNameCopy,pvMaster,pvCopy);
|
||||
}
|
||||
|
||||
MAIN(testPVCopy)
|
||||
{
|
||||
testPlan(67);
|
||||
scalarTest();
|
||||
arrayTest();
|
||||
powerSupplyTest();
|
||||
return testDone();
|
||||
}
|
||||
|
||||
@@ -79,3 +79,7 @@ TESTS += test_reftrack
|
||||
TESTPROD_HOST += testanyscalar
|
||||
testanyscalar_SRCS += testanyscalar.cpp
|
||||
TESTS += testanyscalar
|
||||
|
||||
TESTPROD_HOST += testprinter
|
||||
testprinter_SRCS += testprinter.cpp
|
||||
TESTS += testprinter
|
||||
|
||||
@@ -31,16 +31,6 @@ void Unroller::unroll<0>(double /*d*/) {
|
||||
THROW_BASE_EXCEPTION("the root cause");
|
||||
}
|
||||
|
||||
void internalTestBaseException(int /*unused*/ = 0)
|
||||
{
|
||||
try {
|
||||
// NOTE: 5, 4, 3, 2, 1 calls will be optimized and not shown
|
||||
Unroller().unroll<5>(42.0);
|
||||
} catch (BaseException& be3) {
|
||||
THROW_BASE_EXCEPTION_CAUSE("exception 1", be3);
|
||||
}
|
||||
}
|
||||
|
||||
void testBaseExceptionTest() {
|
||||
printf("testBaseException... ");
|
||||
|
||||
@@ -50,16 +40,6 @@ void testBaseExceptionTest() {
|
||||
printf("\n\n%s\n\n", be.what());
|
||||
}
|
||||
|
||||
try {
|
||||
try {
|
||||
internalTestBaseException();
|
||||
} catch (BaseException& be2) {
|
||||
THROW_BASE_EXCEPTION_CAUSE("exception 2", be2);
|
||||
}
|
||||
} catch (BaseException& be) {
|
||||
printf("\n\n%s\n\n", be.what());
|
||||
}
|
||||
|
||||
testPass("testBaseException");
|
||||
}
|
||||
|
||||
@@ -88,4 +68,3 @@ MAIN(testBaseException)
|
||||
testBaseExceptionTest();
|
||||
return testDone();
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user