98 Commits
7.0.0 ... 7.1.0

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

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

Fully support printing.

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

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

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

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

This rev. remove #define NOMINMAX from public headers,
but no public headers use min()/max() and this was
never the correct way to use this macro as by convention
library headers are included after system headers,
which is too late to have an effect.
2018-05-28 11:24:49 -07:00
Michael Davidsaver
997e68c99a remove usage of min()/max() from public headers
Avoids need for #define NOMINMAX w/ MSVC
2018-05-28 10:14:03 -07:00
Michael Davidsaver
25663d9a7b additional EPICS_NOT_COPYABLE
attempt to pacify, or at least clarify, msvc
link error.
2018-05-21 21:02:32 -07:00
Michael Davidsaver
1e55266396 pvUnitTest.h avoid unintended copies
template argument matching doesn't always preserve
rvalue references, so force use of const ref.
2018-05-21 11:21:01 -07:00
Michael Davidsaver
810ae15991 printer.cpp avoid alloc of spaces
No need to allocate a std::string
just to repeat spaces.
2018-05-21 11:21:01 -07:00
Michael Davidsaver
271fec7f5e printer.cpp: clean whitespace 2018-05-21 11:21:01 -07:00
Michael Davidsaver
c43486791e prevent Field/PVField from being copied
I don't think this was ever intended to be possible,
but it was...
2018-05-21 11:19:42 -07:00
Michael Davidsaver
499c03265f update release notes 2018-04-24 13:30:15 -07:00
Michael Davidsaver
7eaa613d4d try to fix dllimport error 2018-04-17 21:13:22 -07:00
Michael Davidsaver
19db72031c shared_vector add missing void swap
swap() of void and const void misses vtype
2018-04-13 12:40:22 -07:00
Michael Davidsaver
671f9cca4b anyscalar.h: move out of line
No reason to believe that inline was helping.
2018-04-08 15:53:21 -07:00
Michael Davidsaver
9ecdb80534 anyscalar.h: remove unnecesary vcast
what was I thinking...
2018-04-08 15:53:21 -07:00
Michael Davidsaver
e973422ee1 anyscalar.h: add ctor from type code and void*
also helper bufferUnsafe() to get storage pointer
or c_str().
2018-04-08 15:53:21 -07:00
Michael Davidsaver
8093c25b72 typeCast.h: allow cast from C string to numeric w/o copy
also re-enable compile test of string to int64 which
was disabled for some reason...
2018-04-08 15:53:21 -07:00
Michael Davidsaver
a7c9c620dd Timer avoid deadlock in timerStopped()
No code actually uses this hook, but lets make
sure nothing bad would happen.
2018-04-08 15:53:21 -07:00
Michael Davidsaver
1e1d94ed73 rework Timer
I'm not sure how I broke it, but I know I don't have the patience
to fix it.  So replacing intrusive list and custom sorting with
std::list and std::list::merge().
2018-04-04 21:03:11 -07:00
Michael Davidsaver
7b8ef390ce add Timer::close()
An aid to orderly shutdown
2018-04-02 15:54:48 -07:00
Michael Davidsaver
87ade13234 Timer cleanup and hide run() 2018-04-02 15:54:48 -07:00
Michael Davidsaver
32abde7f19 redo Timer to avoid data race
run() was accessing this->head w/o locking
2018-04-02 10:54:49 -07:00
Michael Davidsaver
fe413af177 timerTest: redo to avoid time related false positives 2018-04-02 09:34:15 -07:00
Michael Davidsaver
b4cd026fe5 byteBuffer cleanup 2018-03-23 10:05:31 -07:00
Michael Davidsaver
a51b308cc8 ByteBuffer avoid PPC alignment fault 2018-03-19 15:26:40 -07:00
Michael Davidsaver
1c09b42951 ByteBuffer change order of tests for optimized byte swap
Check __clang__ before __GNUC__ as clang
also identifies itself as gcc for compatibility.
2018-03-19 15:26:35 -07:00
Michael Davidsaver
e42bb46563 ByteBuffer collapse some trivial indirection 2018-03-19 15:26:35 -07:00
Michael Davidsaver
a7788f9847 remove rtemsConfig.c
no longer needed.
2018-03-19 09:11:37 -07:00
Michael Davidsaver
b597364419 simpler test harness main() 2018-03-13 18:44:54 -07:00
Andrew Johnson
a9a951d970 Move epicsExit() call into pvDataAllTests()
Needed on VxWorks to display the test summary.
2018-03-13 12:24:59 -05:00
Andrew Johnson
6ac879ec6a Rename vxTestHarness -> pvdTestHarness
Integrate with Michael's similar changes for RTEMS.
2018-03-13 12:24:00 -05:00
Michael Davidsaver
2422ef50b6 rename rtemsTestHarness -> pvdTestHarness
avoid name clash with rtemsTestHarness
from pvAccessCPP
2018-03-12 09:11:44 -07:00
Michael Davidsaver
06dbf96b65 missing test count 2018-03-12 09:11:44 -07:00
Michael Davidsaver
172046e78f Add AnyScalar::clear() 2018-02-21 11:17:49 -08:00
Michael Davidsaver
7e8c49f0a0 don't use shared_ptr::get() for null test
unnecesarily verbose
2018-02-21 11:17:49 -08:00
Michael Davidsaver
f2ad6292f5 cleanup 2018-02-06 13:12:06 -08:00
Michael Davidsaver
337e13b72e pvIntrospect.h mark OVERRIDE/FINAL 2018-02-06 10:12:38 -08:00
Michael Davidsaver
2ab2fc62dc move FINAL/OVERRIDE defs to pvIntrospect.h 2018-02-06 10:12:38 -08:00
Michael Davidsaver
786575c3de a little bit of cleanup and minor opt 2018-02-06 10:02:08 -08:00
Michael Davidsaver
4cca194000 drop *HashFunction
not really implemented
2018-02-06 10:02:08 -08:00
Michael Davidsaver
cd3ead0028 testThread drop dead code 2018-01-05 11:14:59 -08:00
Michael Davidsaver
a239b95ca1 remove previously deprecated executor.h, queue.h and timerFunction.h 2018-01-05 11:14:59 -08:00
Michael Davidsaver
09574c0e82 sharedVector more c++11
support std::move() and construct
from initializer list.
2018-01-05 11:14:59 -08:00
Michael Davidsaver
0b6b01ef83 shared_vector limit MSVC workaround
limit 207efca15c
to MSVC <= 2010.
2018-01-05 11:14:59 -08:00
Andrew Johnson
34145e459b Clean up compiler warnings. 2018-01-04 17:59:09 -06:00
Michael Davidsaver
207efca15c workaround for msvc pickyness
The MSVC STL implementation asserts that
pointer/itertors are not null, even
when they would not be dereferenced
(eg. empty input range).
2018-01-04 11:52:19 -08:00
Michael Davidsaver
3e25c2ea46 fix more printf specs 2018-01-04 11:52:19 -08:00
Michael Davidsaver
2046678caa drop emptyStringtring 2018-01-04 11:52:19 -08:00
Michael Davidsaver
cb7e4e858b clear some warnings 2018-01-04 11:52:19 -08:00
Michael Davidsaver
43ee4b9cb6 thread safe getFieldCreate() and getPVDataCreate()
Fully thread safe and ctor order safe on all targets
(not just c++11).  Never destroyed to avoid global
dtor order issues.
2017-12-28 11:52:47 -06:00
Michael Davidsaver
207c24a4fd deprecate LOCAL_STATIC_LOCK
This construct is fairly useless.
Doesn't prevent ctor ordering problems.
2017-12-28 11:52:47 -06:00
Michael Davidsaver
6465ab3b6d caseUnsafeV use switch instead of jump table
Change from jump table to switch.
reduces code size (~1k of 30k for rtems/mvme3100).
use indexed loop to help gcc vectorizer.

Helpfully won't fail to compile w/ gcc 4.1 (vxworks 6.6/6.7)
2017-12-28 11:52:47 -06:00
Ralph Lange
cf624bc679 jenkins-ci: fix/update CloudBees jobs 2017-12-19 09:08:27 +01:00
Andrew Johnson
df55a776c7 Update version number after tagging release 2017-12-14 18:28:03 -06:00
85 changed files with 4302 additions and 2540 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -38,7 +38,7 @@ PROJECT_NAME = "PVData C++"
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER =
PROJECT_NUMBER = 7.1.0
# 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
@@ -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

Binary file not shown.

View File

@@ -23,33 +23,32 @@ See pv/pvData.h header.
Define a structure type and create a container with default values.
@code
epics::pvData::StructureConstPtr stype; // aka std::tr1::shared_ptr<const epics::pvData::Structure>
stype = epics::pvData::getFieldCreate()->createFieldBuilder()
->add("fld1", epics::pvData::pvInt)
namespace pvd = epics::pvData;
pvd::StructureConstPtr stype(pvd::FieldBuilder::begin()
->add("fld1", pvd::pvInt)
->addNestedStructure("sub")
->add("fld2", epics::pvData::pvString)
->add("fld2", pvd::pvString)
->endNested()
->createStructure();
->createStructure());
epics::pvData::PVStructuretPtr value; // aka std::tr1::shared_ptr<epics::pvData::PVStructure>
value = epics::pvData::getPVDataCreate()->createPVStructure(stype);
pvd::PVStructuretPtr value(stype->build());
value->getSubField<epics::pvData::PVInt>("fld1")->put(4); // store integer 4
value->getSubField<epics::pvData::PVScalar>("sub.fld2")->putFrom(4.2); // convert and store string "4.2"
value->getSubFieldT<pvd::PVInt>("fld1")->put(4); // store integer 4. would throw if not pvInt
value->getSubFieldT<pvd::PVScalar>("sub.fld2")->putFrom(4.2); // convert and store string "4.2"
@endcode
is equivalent to the following pseudo-code.
@code
struct stype {
epics::pvData::int32 fld1;
pvd::int32 fld1;
struct {
std::string fld2;
} sub;
};
stype value;
value.fld1 = 4;
value.fld2 = epics::pvData::castUnsafe<std::string>(4.2);
value.fld2 = pvd::castUnsafe<std::string>(4.2);
@endcode
*/

View File

@@ -2,8 +2,37 @@
@page release_notes Release Notes
Release 7.0 (XYZ 2017)
======================
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.
@@ -21,6 +50,9 @@ Release 7.0 (XYZ 2017)
- Can also be constructed using an existing PVStructure to allow "editing".
- Add debugPtr.h wrapper with reference tracking to assist in troubleshooting shared_ptr related ref. loops.
- Add @ref pvjson utilities
- Add reftrack @ref pvd_reftrack
- Add header typemap.h to facilitate boilerplate switch() over ScalarType
- Add epics::auto_ptr typedef in help writing code supporting both c++98 and c++11 w/o copious deprecation warnings.
Release 6.0.1

View File

@@ -65,6 +65,6 @@ make distclean all
make runtests
###########################################
# Create distribution
# Create cache
tar czf pvData.CB-dist.tar.gz lib include LICENSE
tar czf pvData.CB-dist.tar.gz lib include cfg LICENSE

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

View File

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

View File

@@ -7,10 +7,6 @@
* @author mrk
*/
#if defined(_WIN32) && !defined(NOMINMAX)
#define NOMINMAX
#endif
#include <cstddef>
#include <cstdlib>
#include <string>
@@ -18,7 +14,9 @@
#include <stdexcept>
#include <sstream>
#include <epicsString.h>
#include <epicsMutex.h>
#include <epicsThread.h>
#define epicsExportSharedSymbols
#include <pv/reftrack.h>
@@ -26,7 +24,8 @@
#include <pv/pvIntrospect.h>
#include <pv/factory.h>
#include <pv/serializeHelper.h>
#include <pv/localStaticLock.h>
#include <pv/thread.h>
#include <pv/pvData.h>
using std::tr1::static_pointer_cast;
using std::size_t;
@@ -39,16 +38,76 @@ static DebugLevel debugLevel = lowDebug;
size_t Field::num_instances;
struct Field::Helper {
static unsigned hash(Field *fld) {
std::ostringstream key;
// hash the output of operator<<()
// not efficient, but stable within this process.
key<<(*fld);
unsigned H = epicsStrHash(key.str().c_str(), 0xbadc0de1);
fld->m_hash = H;
return H;
}
};
struct FieldCreate::Helper {
template<typename FLD>
static void cache(const FieldCreate *create, std::tr1::shared_ptr<FLD>& ent) {
unsigned hash = Field::Helper::hash(ent.get());
Lock G(create->mutex);
// we examine raw pointers stored in create->cache, which is safe under create->mutex
std::pair<cache_t::iterator, cache_t::iterator> itp(create->cache.equal_range(hash));
for(; itp.first!=itp.second; ++itp.first) {
Field* cent(itp.first->second);
FLD* centx(dynamic_cast<FLD*>(cent));
if(centx && compare(*centx, *ent)) {
try{
ent = std::tr1::static_pointer_cast<FLD>(cent->shared_from_this());
return;
}catch(std::tr1::bad_weak_ptr&){
// we're racing destruction.
// add a new entry.
// Field::~Field is in the process of removing this old one.
continue;
}
}
}
create->cache.insert(std::make_pair(hash, ent.get()));
// cache cleaned from Field::~Field
}
};
Field::Field(Type type)
: m_fieldType(type)
, m_hash(0)
{
REFTRACE_INCREMENT(num_instances);
}
Field::~Field() {
REFTRACE_DECREMENT(num_instances);
FieldCreatePtr create(getFieldCreate());
Lock G(create->mutex);
std::pair<FieldCreate::cache_t::iterator, FieldCreate::cache_t::iterator> itp(create->cache.equal_range(m_hash));
for(; itp.first!=itp.second; ++itp.first) {
Field* cent(itp.first->second);
if(cent==this) {
create->cache.erase(itp.first);
return;
}
}
}
std::tr1::shared_ptr<PVField> Field::build() const
{
FieldConstPtr self(shared_from_this());
return getPVDataCreate()->createPVField(self);
}
std::ostream& operator<<(std::ostream& o, const Field& f)
{
@@ -119,6 +178,11 @@ void Scalar::deserialize(ByteBuffer* /*buffer*/, DeserializableControl* /*contro
}
std::tr1::shared_ptr<PVScalar> Scalar::build() const
{
return getPVDataCreate()->createPVScalar(std::tr1::static_pointer_cast<const Scalar>(shared_from_this()));
}
std::string BoundedString::getID() const
@@ -285,6 +349,11 @@ void ScalarArray::deserialize(ByteBuffer* /*buffer*/, DeserializableControl* /*c
throw std::runtime_error("not valid operation, use FieldCreate::deserialize instead");
}
std::tr1::shared_ptr<PVScalarArray> ScalarArray::build() const
{
return getPVDataCreate()->createPVScalarArray(std::tr1::static_pointer_cast<const ScalarArray>(shared_from_this()));
}
BoundedScalarArray::~BoundedScalarArray() {}
@@ -365,6 +434,11 @@ 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)
{
@@ -407,6 +481,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()
@@ -460,11 +539,10 @@ string Structure::getID() const
}
FieldConstPtr Structure::getField(string const & fieldName) const {
size_t numberFields = fields.size();
for(size_t i=0; i<numberFields; i++) {
FieldConstPtr pfield = fields[i];
int result = fieldName.compare(fieldNames[i]);
if(result==0) return pfield;
for(size_t i=0, N=fields.size(); i<N; i++) {
if(fieldName==fieldNames[i]) {
return fields[i];
}
}
return FieldConstPtr();
}
@@ -545,6 +623,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()
@@ -743,6 +826,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)
@@ -844,6 +932,19 @@ void FieldBuilder::reset()
fields.clear();
}
FieldBuilderPtr FieldBuilder::begin()
{
FieldBuilderPtr ret(new FieldBuilder);
return ret;
}
FieldBuilderPtr FieldBuilder::begin(StructureConstPtr S)
{
FieldBuilderPtr ret(new FieldBuilder(S.get()));
return ret;
}
FieldBuilderPtr FieldBuilder::setId(string const & id)
{
this->id = id;
@@ -1080,10 +1181,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
@@ -1105,10 +1205,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
@@ -1119,10 +1218,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
@@ -1179,10 +1277,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 (
@@ -1191,29 +1288,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 (
@@ -1222,10 +1316,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
@@ -1236,10 +1329,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
@@ -1368,12 +1460,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");
@@ -1398,19 +1488,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)
@@ -1422,9 +1510,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)
{
@@ -1435,9 +1523,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)
{
@@ -1453,42 +1541,55 @@ FieldConstPtr FieldCreate::deserialize(ByteBuffer* buffer, DeserializableControl
}
}
// TODO replace with non-locking singleton pattern
namespace detail {
struct field_factory {
FieldCreatePtr fieldCreate;
field_factory() :fieldCreate(new FieldCreate()) {
registerRefCounter("Field", &Field::num_instances);
registerRefCounter("Thread", &Thread::num_instances);
}
};
}
static detail::field_factory* field_factory_s;
static epicsThreadOnceId field_factory_once = EPICS_THREAD_ONCE_INIT;
static void field_factory_init(void*)
{
try {
field_factory_s = new detail::field_factory;
}catch(std::exception& e){
std::cerr<<"Error initializing getFieldCreate() : "<<e.what()<<"\n";
}
}
const FieldCreatePtr& FieldCreate::getFieldCreate()
{
LOCAL_STATIC_LOCK;
static FieldCreatePtr fieldCreate;
static Mutex mutex;
Lock xx(mutex);
if(fieldCreate.get()==0) {
fieldCreate = FieldCreatePtr(new FieldCreate());
registerRefCounter("Field", &Field::num_instances);
}
return fieldCreate;
epicsThreadOnce(&field_factory_once, &field_factory_init, 0);
if(!field_factory_s->fieldCreate)
throw std::logic_error("getFieldCreate() not initialized");
return field_factory_s->fieldCreate;
}
FieldCreate::FieldCreate()
{
for (int i = 0; i <= MAX_SCALAR_TYPE; i++)
{
// TODO use std::make_shared
std::tr1::shared_ptr<Scalar> sp(new Scalar(static_cast<ScalarType>(i)), Field::Deleter());
ScalarConstPtr p = sp;
scalars.push_back(p);
std::tr1::shared_ptr<Scalar> sp(new Scalar(static_cast<ScalarType>(i)));
Helper::cache(this, sp);
scalars.push_back(sp);
// TODO use std::make_shared
std::tr1::shared_ptr<ScalarArray> spa(new ScalarArray(static_cast<ScalarType>(i)), Field::Deleter());
ScalarArrayConstPtr pa = spa;
std::tr1::shared_ptr<ScalarArray> spa(new ScalarArray(static_cast<ScalarType>(i)));
Helper::cache(this, spa);
scalarArrays.push_back(spa);
}
// TODO use std::make_shared
std::tr1::shared_ptr<Union> su(new Union(), Field::Deleter());
std::tr1::shared_ptr<Union> su(new Union());
Helper::cache(this, su);
variantUnion = su;
// TODO use std::make_shared
std::tr1::shared_ptr<UnionArray> sua(new UnionArray(variantUnion), Field::Deleter());
std::tr1::shared_ptr<UnionArray> sua(new UnionArray(variantUnion));
Helper::cache(this, sua);
variantUnionArray = sua;
}

View File

@@ -7,16 +7,13 @@
* @author mrk
*/
#if defined(_WIN32) && !defined(NOMINMAX)
#define NOMINMAX
#endif
#include <cstddef>
#include <cstdlib>
#include <string>
#include <cstdio>
#include <epicsMutex.h>
#include <epicsThread.h>
#define epicsExportSharedSymbols
#include <pv/lock.h>
@@ -64,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 {
@@ -103,6 +142,13 @@ PVString::PVString(ScalarConstPtr const & scalar)
storage.maxLength = 0;
}
std::ostream& PVString::dumpValue(std::ostream& o) const
{
// we escape, but do not quote, for scalar string
o<<escape(get());
return o;
}
/* mixing overrides (virtual functions) and overloads (different argument lists) is fun...
* we override all overloads to avoid the "hides overloaded virtual function" warning from clang.
* In this case we don't need/want to, so just delegate to the base class.
@@ -159,6 +205,59 @@ PVValueArray<PVUnionPtr>::PVValueArray(UnionArrayConstPtr const & unionArray)
,unionArray(unionArray)
{}
template<typename T>
PVValueArray<T>::~PVValueArray() {}
template<typename T>
ArrayConstPtr PVValueArray<T>::getArray() const
{
return std::tr1::static_pointer_cast<const Array>(this->getField());
}
template<typename T>
std::ostream& PVValueArray<T>::dumpValue(std::ostream& o) const
{
const_svector v(this->view());
typename const_svector::const_iterator it(v.begin()),
end(v.end());
o << '[';
if(it!=end) {
o << print_cast(*it++);
for(; it!=end; ++it)
o << ',' << print_cast(*it);
}
return o << ']';
}
template<>
std::ostream& PVValueArray<std::string>::dumpValue(std::ostream& o, size_t index) const
{
return o << '"' << escape(this->view().at(index)) << '"';
}
template<>
std::ostream& PVValueArray<std::string>::dumpValue(std::ostream& o) const
{
const_svector v(this->view());
const_svector::const_iterator it(v.begin()),
end(v.end());
o << '[';
if(it!=end) {
o << '"' << escape(*it++) << '"';
for(; it!=end; ++it)
o << ", \"" << escape(*it) << '"';
}
return o << ']';
}
template<typename T>
std::ostream& PVValueArray<T>::dumpValue(std::ostream& o, size_t index) const
{
return o << print_cast(this->view().at(index));
}
template<typename T>
void PVValueArray<T>::setCapacity(size_t capacity)
{
@@ -351,6 +450,18 @@ void PVValueArray<string>::serialize(ByteBuffer *pbuffer,
}
}
template<typename T>
void PVValueArray<T>::_getAsVoid(epics::pvData::shared_vector<const void>& out) const
{
out = static_shared_vector_cast<const void>(this->view());
}
template<typename T>
void PVValueArray<T>::_putFromVoid(const epics::pvData::shared_vector<const void>& in)
{
this->replace(shared_vector_convert<const T>(in));
}
// Factory
PVDataCreate::PVDataCreate()
@@ -604,19 +715,33 @@ PVUnionPtr PVDataCreate::createPVUnion(PVUnionPtr const & unionToClone)
return punion;
}
// TODO not thread-safe (local static initializers)
// TODO replace with non-locking singleton pattern
namespace detail {
struct pvfield_factory {
PVDataCreatePtr pvDataCreate;
pvfield_factory() :pvDataCreate(new PVDataCreate()) {
registerRefCounter("PVField", &PVField::num_instances);
}
};
}
static detail::pvfield_factory* pvfield_factory_s;
static epicsThreadOnceId pvfield_factory_once = EPICS_THREAD_ONCE_INIT;
static void pvfield_factory_init(void*)
{
try {
pvfield_factory_s = new detail::pvfield_factory;
}catch(std::exception& e){
std::cerr<<"Error initializing getFieldCreate() : "<<e.what()<<"\n";
}
}
const PVDataCreatePtr& PVDataCreate::getPVDataCreate()
{
static PVDataCreatePtr pvDataCreate;
static Mutex mutex;
Lock xx(mutex);
if(pvDataCreate.get()==0) {
registerRefCounter("PVField", &PVField::num_instances);
pvDataCreate = PVDataCreatePtr(new PVDataCreate());
}
return pvDataCreate;
epicsThreadOnce(&pvfield_factory_once, &pvfield_factory_init, 0);
if(!pvfield_factory_s->pvDataCreate)
throw std::logic_error("getPVDataCreate() not initialized");
return pvfield_factory_s->pvDataCreate;
}
// explicitly instanciate to ensure that windows

View File

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

View File

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

View File

@@ -76,14 +76,14 @@ void PVStructureArray::compress() {
size_t newLength = 0;
for(size_t i=0; i<length; i++) {
if(vec[i].get()!=NULL) {
if(vec[i]) {
newLength++;
continue;
}
// find first non 0
size_t notNull = 0;
for(size_t j=i+1;j<length;j++) {
if(vec[j].get()!=NULL) {
if(vec[j]) {
notNull = j;
break;
}

View File

@@ -76,14 +76,14 @@ void PVUnionArray::compress() {
size_t newLength = 0;
for(size_t i=0; i<length; i++) {
if(vec[i].get()!=NULL) {
if(vec[i]) {
newLength++;
continue;
}
// find first non 0
size_t notNull = 0;
for(size_t j=i+1;j<length;j++) {
if(vec[j].get()!=NULL) {
if(vec[j]) {
notNull = j;
break;
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -11,10 +11,7 @@ INC += pv/epicsException.h
INC += pv/serializeHelper.h
INC += pv/event.h
INC += pv/thread.h
INC += pv/executor.h
INC += pv/timeFunction.h
INC += pv/timer.h
INC += pv/queue.h
INC += pv/status.h
INC += pv/sharedPtr.h
INC += pv/debugPtr.h
@@ -32,13 +29,13 @@ LIBSRCS += bitSet.cpp
LIBSRCS += epicsException.cpp
LIBSRCS += serializeHelper.cpp
LIBSRCS += event.cpp
LIBSRCS += executor.cpp
LIBSRCS += timeFunction.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
LIBSRCS += reftrack.cpp
LIBSRCS += anyscalar.cpp

161
src/misc/anyscalar.cpp Normal file
View File

@@ -0,0 +1,161 @@
#include <epicsAssert.h>
#define epicsExportSharedSymbols
#include <shareLib.h>
#include "pv/anyscalar.h"
namespace epics {namespace pvData {
AnyScalar::AnyScalar(ScalarType type, const void *buf)
{
if(type==pvString) {
new (_wrap.blob) std::string(*static_cast<const std::string*>(buf));
} else {
memcpy(_wrap.blob, buf, ScalarTypeFunc::elementSize(type));
}
_stype = type;
}
AnyScalar::AnyScalar(const AnyScalar& o)
:_stype(o._stype)
{
if(o._stype==pvString) {
new (_wrap.blob) std::string(o._as<std::string>());
} else if(o._stype!=(ScalarType)-1) {
memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob));
}
}
#if __cplusplus>=201103L
AnyScalar::AnyScalar(AnyScalar&& o)
:_stype(o._stype)
{
typedef std::string string;
if(o._stype==pvString) {
new (_wrap.blob) std::string();
_as<std::string>() = std::move(o._as<std::string>());
o._as<string>().~string();
} else if(o._stype!=(ScalarType)-1) {
memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob));
}
o._stype = (ScalarType)-1;
}
#endif
void AnyScalar::clear() {
if(_stype==pvString) {
typedef std::string string;
_as<string>().~string();
}
// other types need no cleanup
_stype = (ScalarType)-1;
}
void AnyScalar::swap(AnyScalar& o) {
typedef std::string string;
switch((int)_stype) {
case -1:
switch((int)o._stype) {
case -1:
// nil <-> nil
break;
case pvString:
// nil <-> string
new (_wrap.blob) std::string();
_as<std::string>().swap(o._as<std::string>());
o._as<std::string>().~string();
break;
default:
// nil <-> non-string
memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob));
break;
}
break;
case pvString:
switch((int)o._stype) {
case -1:
// string <-> nil
new (o._wrap.blob) std::string();
_as<std::string>().swap(o._as<std::string>());
_as<std::string>().~string();
break;
case pvString:
// string <-> string
_as<std::string>().swap(o._as<std::string>());
break;
default: {
// string <-> non-string
std::string temp;
temp.swap(_as<std::string>());
_as<std::string>().~string();
memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob));
new (o._wrap.blob) std::string();
temp.swap(o._as<std::string>());
}
break;
}
break;
default:
switch((int)o._stype) {
case -1:
// non-string <-> nil
memcpy(o._wrap.blob, _wrap.blob, sizeof(_largest_blob));
break;
case pvString: {
// non-string <-> string
std::string temp;
temp.swap(o._as<std::string>());
o._as<std::string>().~string();
memcpy(o._wrap.blob, _wrap.blob, sizeof(_largest_blob));
new (_wrap.blob) std::string();
temp.swap(_as<std::string>());
}
break;
default:
// non-string <-> non-string
_largest_blob temp;
memcpy(&temp, _wrap.blob, sizeof(_largest_blob));
memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob));
memcpy(o._wrap.blob, &temp, sizeof(_largest_blob));
// std::swap(o._wrap.blob, _wrap.blob); // gcc <=4.3 doesn't like this
break;
}
break;
}
std::swap(_stype, o._stype);
}
const void* AnyScalar::bufferUnsafe() const {
if(_stype==pvString) {
return as<std::string>().c_str();
} else {
return _wrap.blob;
}
}
std::ostream& operator<<(std::ostream& strm, const AnyScalar& v)
{
switch(v.type()) {
#define CASE(BASETYPE, PVATYPE, DBFTYPE, PVACODE) case pv ## PVACODE: strm<<v._as<PVATYPE>(); break;
#define CASE_REAL_INT64
#define CASE_STRING
#include "pv/typemap.h"
#undef CASE
#undef CASE_REAL_INT64
#undef CASE_STRING
default:
strm<<"(nil)"; break;
}
return strm;
}
}} // namespace epics::pvData

View File

@@ -1,103 +0,0 @@
/* executor.cpp */
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
/**
* @author mrk
*/
#include <cstddef>
#include <cstdlib>
#include <cstddef>
#include <string>
#include <cstdio>
#include <epicsEvent.h>
#include <epicsMutex.h>
#include <epicsThread.h>
// Suppress deprecation warnings for the implementation
#include <compilerDependencies.h>
#undef EPICS_DEPRECATED
#define EPICS_DEPRECATED
#define epicsExportSharedSymbols
#include <pv/executor.h>
using std::string;
namespace epics { namespace pvData {
// special instance to stop the executor thread
class ExecutorShutdown : public Command {
virtual void command();
};
void ExecutorShutdown::command()
{
}
static
std::tr1::shared_ptr<Command> shutdown(new ExecutorShutdown());
Executor::Executor(string const & threadName,ThreadPriority priority)
: thread(threadName,priority,this)
{
}
Executor::~Executor()
{
execute(shutdown);
stopped.wait();
// The thread signals 'stopped' while still holding
// the lock. By taking it we wait for the run() function
// to actually return
Lock xx(mutex);
head.reset();
tail.reset();
}
void Executor::run()
{
Lock xx(mutex);
while(true) {
while(!head.get()) {
xx.unlock();
moreWork.wait();
xx.lock();
}
CommandPtr command = head;
head = command->next;
if(!command.get()) continue;
if(command.get()==shutdown.get()) break;
xx.unlock();
try {
command->command();
}catch(std::exception& e){
//TODO: feed into logging mechanism
fprintf(stderr, "Executor: Unhandled exception: %s",e.what());
}catch(...){
fprintf(stderr, "Executor: Unhandled exception");
}
xx.lock();
}
stopped.signal();
}
void Executor::execute(CommandPtr const & command)
{
Lock xx(mutex);
command->next.reset();
if(!head.get()) {
head = command;
moreWork.signal();
return;
}
CommandPtr tail = head;
while(tail->next) tail = tail->next;
tail->next = command;
}
}}

View File

@@ -507,20 +507,20 @@ void handleParseError(int err)
namespace epics { namespace pvData { namespace detail {
void parseToPOD(const string & in, boolean *out)
void parseToPOD(const char* in, boolean *out)
{
if(epicsStrCaseCmp(in.c_str(),"true")==0)
if(epicsStrCaseCmp(in,"true")==0)
*out = 1;
else if(epicsStrCaseCmp(in.c_str(),"false")==0)
else if(epicsStrCaseCmp(in,"false")==0)
*out = 0;
else
throw std::runtime_error("parseToPOD: string no match true/false");
}
#define INTFN(T, S) \
void parseToPOD(const string& in, T *out) { \
void parseToPOD(const char* in, T *out) { \
epics ## S temp; \
int err = epicsParse ## S (in.c_str(), &temp, 0, NULL); \
int err = epicsParse ## S (in, &temp, 0, NULL); \
if(err) handleParseError(err); \
else *out = temp; \
}
@@ -532,31 +532,31 @@ INTFN(uint16_t, UInt16);
INTFN(int32_t, Int32);
INTFN(uint32_t, UInt32);
void parseToPOD(const string& in, int64_t *out) {
void parseToPOD(const char* in, int64_t *out) {
#ifdef NEED_LONGLONG
int err = epicsParseLongLong(in.c_str(), out, 0, NULL);
int err = epicsParseLongLong(in, out, 0, NULL);
#else
int err = epicsParseLong(in.c_str(), out, 0, NULL);
int err = epicsParseLong(in, out, 0, NULL);
#endif
if(err) handleParseError(err);
}
void parseToPOD(const string& in, uint64_t *out) {
void parseToPOD(const char* in, uint64_t *out) {
#ifdef NEED_LONGLONG
int err = epicsParseULongLong(in.c_str(), out, 0, NULL);
int err = epicsParseULongLong(in, out, 0, NULL);
#else
int err = epicsParseULong(in.c_str(), out, 0, NULL);
int err = epicsParseULong(in, out, 0, NULL);
#endif
if(err) handleParseError(err);
}
void parseToPOD(const string& in, float *out) {
int err = epicsParseFloat(in.c_str(), out, NULL);
void parseToPOD(const char* in, float *out) {
int err = epicsParseFloat(in, out, NULL);
if(err) handleParseError(err);
}
void parseToPOD(const string& in, double *out) {
int err = epicsParseDouble(in.c_str(), out, NULL);
void parseToPOD(const char* in, double *out) {
int err = epicsParseDouble(in, out, NULL);
if(err) handleParseError(err);
#if defined(vxWorks)
/* vxWorks strtod returns [-]epicsINF when it should return ERANGE error.
@@ -564,7 +564,7 @@ void parseToPOD(const string& in, double *out) {
* this into an ERANGE error
*/
else if (*out == epicsINF || *out == -epicsINF) {
const char* s = in.c_str();
const char* s = in;
int c;
/* skip spaces and the sign */

View File

@@ -48,7 +48,7 @@ constexpr size_t cmax(size_t A, size_t B) {
assert(A.ref<double>()==5.0); // throws AnyScalar::bad_cast
@endcode
*/
class AnyScalar {
class epicsShareClass AnyScalar {
public:
struct bad_cast : public std::exception {
#if __cplusplus>=201103L
@@ -95,8 +95,11 @@ private:
return *reinterpret_cast<const T*>(_wrap.blob);
}
public:
//! Construct empty
//! @post empty()==true
AnyScalar() : _stype((ScalarType)-1) {}
//! Construct from provided value.
template<typename T>
explicit AnyScalar(T v)
{
@@ -112,149 +115,52 @@ public:
_stype = (ScalarType)ScalarTypeID<TT>::value;
}
AnyScalar(const AnyScalar& o)
:_stype(o._stype)
{
if(o._stype==pvString) {
new (_wrap.blob) std::string(o._as<std::string>());
} else if(o._stype!=(ScalarType)-1) {
memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob));
}
}
//! 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)
:_stype(o._stype)
{
typedef std::string string;
if(o._stype==pvString) {
new (_wrap.blob) std::string();
_as<std::string>() = std::move(o._as<std::string>());
o._as<string>().~string();
} else if(o._stype!=(ScalarType)-1) {
memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob));
}
o._stype = (ScalarType)-1;
}
AnyScalar(AnyScalar&& o);
#endif
~AnyScalar() {
if(_stype==pvString) {
typedef std::string string;
_as<string>().~string();
}
// other types need no cleanup
}
inline ~AnyScalar() {clear();}
AnyScalar& operator=(const AnyScalar& o) {
inline AnyScalar& operator=(const AnyScalar& o) {
AnyScalar(o).swap(*this);
return *this;
}
template<typename T>
AnyScalar& operator=(T v) {
inline AnyScalar& operator=(T v) {
AnyScalar(v).swap(*this);
return *this;
}
#if __cplusplus>=201103L
AnyScalar& operator=(AnyScalar&& o) {
if(_stype==pvString) {
typedef std::string string;
_as<string>().~string();
}
_stype = (ScalarType)-1;
inline AnyScalar& operator=(AnyScalar&& o) {
clear();
swap(o);
return *this;
}
#endif
void swap(AnyScalar& o) {
typedef std::string string;
switch((int)_stype) {
case -1:
switch((int)o._stype) {
case -1:
// nil <-> nil
break;
case pvString:
// nil <-> string
new (_wrap.blob) std::string();
_as<std::string>().swap(o._as<std::string>());
o._as<std::string>().~string();
break;
default:
// nil <-> non-string
memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob));
break;
}
break;
case pvString:
switch((int)o._stype) {
case -1:
// string <-> nil
new (o._wrap.blob) std::string();
_as<std::string>().swap(o._as<std::string>());
_as<std::string>().~string();
break;
case pvString:
// string <-> string
_as<std::string>().swap(o._as<std::string>());
break;
default: {
// string <-> non-string
std::string temp;
temp.swap(_as<std::string>());
//! Reset internal state.
//! @version Added after 7.0.0
//! @post empty()==true
void clear();
_as<std::string>().~string();
memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob));
new (o._wrap.blob) std::string();
temp.swap(o._as<std::string>());
}
break;
}
break;
default:
switch((int)o._stype) {
case -1:
// non-string <-> nil
memcpy(o._wrap.blob, _wrap.blob, sizeof(_largest_blob));
break;
case pvString: {
// non-string <-> string
std::string temp;
temp.swap(o._as<std::string>());
o._as<std::string>().~string();
memcpy(o._wrap.blob, _wrap.blob, sizeof(_largest_blob));
new (_wrap.blob) std::string();
temp.swap(_as<std::string>());
}
break;
default:
// non-string <-> non-string
_largest_blob temp;
memcpy(&temp, _wrap.blob, sizeof(_largest_blob));
memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob));
memcpy(o._wrap.blob, &temp, sizeof(_largest_blob));
// std::swap(o._wrap.blob, _wrap.blob); // gcc <=4.3 doesn't like this
break;
}
break;
}
std::swap(_stype, o._stype);
}
void swap(AnyScalar& o);
//! Type code of contained value. Or (ScalarType)-1 is empty.
inline ScalarType type() const {
return _stype;
}
void* unsafe() { return _wrap.blob; }
const void* unsafe() const { return _wrap.blob; }
inline void* unsafe() { return _wrap.blob; }
inline const void* unsafe() const { return _wrap.blob; }
inline bool empty() const { return _stype==(ScalarType)-1; }
@@ -267,8 +173,21 @@ public:
operator bool_type() const { return !empty() ? &AnyScalar::swap : 0; }
#endif
/** Return reference to wrapped value */
//! 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
*
@code
AnyScalar v(42);
v.ref<uint32>() = 42;
assert(v.ref<uint32>() = 43);
@endcode
*/
template<typename T>
inline
// T -> strip_const -> map to storage type -> add reference
typename detail::any_storage_type<typename meta::strip_const<T>::type>::type&
ref() {
@@ -277,10 +196,18 @@ public:
if(_stype!=(ScalarType)ScalarTypeID<TT>::value)
throw bad_cast();
return _as<TT>();
return reinterpret_cast<TT&>(_wrap.blob);
}
/** Return typed reference to wrapped value. Const reference does not allow modification.
*
@code
AnyScalar v(42);
assert(v.ref<uint32>() = 42);
@endcode
*/
template<typename T>
inline
// T -> strip_const -> map to storage type -> add const reference
typename meta::decorate_const<typename detail::any_storage_type<typename meta::strip_const<T>::type>::type>::type&
ref() const {
@@ -289,11 +216,12 @@ public:
if(_stype!=(ScalarType)ScalarTypeID<TT>::value)
throw bad_cast();
return _as<TT>();
return reinterpret_cast<typename meta::decorate_const<TT>::type&>(_wrap.blob);
}
/** copy out wrapped value, with a value conversion. */
template<typename T>
inline
T as() const {
typedef typename meta::strip_const<T>::type T2;
typedef typename detail::any_storage_type<T2>::type TT;
@@ -307,25 +235,11 @@ public:
}
private:
friend std::ostream& operator<<(std::ostream& strm, const AnyScalar& v);
friend epicsShareFunc std::ostream& operator<<(std::ostream& strm, const AnyScalar& v);
};
inline
std::ostream& operator<<(std::ostream& strm, const AnyScalar& v)
{
switch(v.type()) {
#define CASE(BASETYPE, PVATYPE, DBFTYPE, PVACODE) case pv ## PVACODE: strm<<v._as<PVATYPE>(); break;
#define CASE_REAL_INT64
#define CASE_STRING
#include "pv/typemap.h"
#undef CASE
#undef CASE_REAL_INT64
#undef CASE_STRING
default:
strm<<"(nil)"; break;
}
return strm;
}
epicsShareExtern
std::ostream& operator<<(std::ostream& strm, const AnyScalar& v);
}} // namespace epics::pvData

View File

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

View File

@@ -29,16 +29,7 @@
/* various compilers provide builtins for byte order swaps.
* conditions based on boost endian library
*/
#if defined(__GNUC__) && ((__GNUC__>4) || (__GNUC__==4 && __GNUC_MINOR__>=3))
#if (__GNUC__>4) || (__GNUC__==4 && __GNUC_MINOR__>=8)
#define _PVA_swap16(X) __builtin_bswap16(X)
#endif
#define _PVA_swap32(X) __builtin_bswap32(X)
#define _PVA_swap64(X) __builtin_bswap64(X)
#elif defined(__clang__)
#if defined(__clang__)
#if __has_builtin(__builtin_bswap16)
#define _PVA_swap16(X) __builtin_bswap16(X)
@@ -50,6 +41,15 @@
#define _PVA_swap64(X) __builtin_bswap64(X)
#endif
#elif defined(__GNUC__) && ((__GNUC__>4) || (__GNUC__==4 && __GNUC_MINOR__>=3))
#if (__GNUC__>4) || (__GNUC__==4 && __GNUC_MINOR__>=8)
#define _PVA_swap16(X) __builtin_bswap16(X)
#endif
#define _PVA_swap32(X) __builtin_bswap32(X)
#define _PVA_swap64(X) __builtin_bswap64(X)
#elif defined(_MSC_VER)
#define _PVA_swap16(X) _byteswap_ushort(X)
@@ -148,6 +148,58 @@ struct swap<8> {
#undef _PVA_swap32
#undef _PVA_swap64
/* PVD serialization doesn't pay attention to alignement,
* which some targets 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.
*/
#ifdef _ARCH_PPC
template<typename T>
union alignu {
T val;
char bytes[sizeof(T)];
};
template<typename T>
EPICS_ALWAYS_INLINE void store_unaligned(char *buf, T val)
{
alignu<T> A;
A.val = val;
for(unsigned i=0, N=sizeof(T); i<N; i++) {
buf[i] = A.bytes[i];
}
}
template<typename T>
EPICS_ALWAYS_INLINE T load_unaligned(const char *buf)
{
alignu<T> A;
for(unsigned i=0, N=sizeof(T); i<N; i++) {
A.bytes[i] = buf[i];
}
return A.val;
}
#else /* alignement */
template<typename T>
EPICS_ALWAYS_INLINE void store_unaligned(char *buf, T val)
{
*reinterpret_cast<T*>(buf) = val;
}
template<typename T>
EPICS_ALWAYS_INLINE T load_unaligned(const char *buf)
{
return *reinterpret_cast<const T*>(buf);
}
#endif /* alignement */
} // namespace detail
//! Unconditional byte order swap.
@@ -344,7 +396,7 @@ public:
*
* @return The size of the raw data buffer.
*/
inline std::size_t getSize() const
EPICS_ALWAYS_INLINE std::size_t getSize() const
{
return _size;
}
@@ -463,43 +515,43 @@ public:
*
* @param value The value.
*/
inline void putBoolean( bool value) { put< int8>(value ? 1 : 0); }
EPICS_ALWAYS_INLINE void putBoolean( bool value) { put< int8>(value ? 1 : 0); }
/**
* Put a byte value into the byte buffer.
*
* @param value The value.
*/
inline void putByte ( int8 value) { put< int8>(value); }
EPICS_ALWAYS_INLINE void putByte ( int8 value) { put< int8>(value); }
/**
* Put a short value into the byte buffer.
*
* @param value The value.
*/
inline void putShort ( int16 value) { put< int16>(value); }
EPICS_ALWAYS_INLINE void putShort ( int16 value) { put< int16>(value); }
/**
* Put an int value into the byte buffer.
*
* @param value The value.
*/
inline void putInt ( int32 value) { put< int32>(value); }
EPICS_ALWAYS_INLINE void putInt ( int32 value) { put< int32>(value); }
/**
* Put a long value into the byte buffer.
*
* @param value The value.
*/
inline void putLong ( int64 value) { put< int64>(value); }
EPICS_ALWAYS_INLINE void putLong ( int64 value) { put< int64>(value); }
/**
* Put a float value into the byte buffer.
*
* @param value The value.
*/
inline void putFloat ( float value) { put< float>(value); }
EPICS_ALWAYS_INLINE void putFloat ( float value) { put< float>(value); }
/**
* Put a double value into the byte buffer.
*
* @param value The value.
*/
inline void putDouble (double value) { put<double>(value); }
EPICS_ALWAYS_INLINE void putDouble (double value) { put<double>(value); }
/**
* Put a boolean value into the byte buffer at the specified index.
@@ -507,143 +559,143 @@ public:
* @param index The offset in the byte buffer,
* @param value The value.
*/
inline void putBoolean(std::size_t index, bool value) { put< int8>(index, value); }
EPICS_ALWAYS_INLINE void putBoolean(std::size_t index, bool value) { put< int8>(index, value); }
/**
* Put a byte value into the byte buffer at the specified index.
*
* @param index The offset in the byte buffer,
* @param value The value.
*/
inline void putByte (std::size_t index, int8 value) { put< int8>(index, value); }
EPICS_ALWAYS_INLINE void putByte (std::size_t index, int8 value) { put< int8>(index, value); }
/**
* Put a short value into the byte buffer at the specified index.
*
* @param index The offset in the byte buffer,
* @param value The value.
*/
inline void putShort (std::size_t index, int16 value) { put< int16>(index, value); }
EPICS_ALWAYS_INLINE void putShort (std::size_t index, int16 value) { put< int16>(index, value); }
/**
* Put an int value into the byte buffer at the specified index.
*
* @param index The offset in the byte buffer,
* @param value The value.
*/
inline void putInt (std::size_t index, int32 value) { put< int32>(index, value); }
EPICS_ALWAYS_INLINE void putInt (std::size_t index, int32 value) { put< int32>(index, value); }
/**
* Put a long value into the byte buffer at the specified index.
*
* @param index The offset in the byte buffer,
* @param value The value.
*/
inline void putLong (std::size_t index, int64 value) { put< int64>(index, value); }
EPICS_ALWAYS_INLINE void putLong (std::size_t index, int64 value) { put< int64>(index, value); }
/**
* Put a float value into the byte buffer at the specified index.
*
* @param index The offset in the byte buffer,
* @param value The value.
*/
inline void putFloat (std::size_t index, float value) { put< float>(index, value); }
EPICS_ALWAYS_INLINE void putFloat (std::size_t index, float value) { put< float>(index, value); }
/**
* Put a double value into the byte buffer at the specified index.
*
* @param index The offset in the byte buffer,
* @param value The value.
*/
inline void putDouble (std::size_t index, double value) { put<double>(index, value); }
EPICS_ALWAYS_INLINE void putDouble (std::size_t index, double value) { put<double>(index, value); }
/**
* Get a boolean value from the byte buffer.
*
* @return The value.
*/
inline bool getBoolean() { return GET( int8) != 0; }
EPICS_ALWAYS_INLINE bool getBoolean() { return GET( int8) != 0; }
/**
* Get a byte value from the byte buffer.
*
* @return The value.
*/
inline int8 getByte () { return GET( int8); }
EPICS_ALWAYS_INLINE int8 getByte () { return GET( int8); }
/**
* Get a short value from the byte buffer.
*
* @return The value.
*/
inline int16 getShort () { return GET( int16); }
EPICS_ALWAYS_INLINE int16 getShort () { return GET( int16); }
/**
* Get a int value from the byte buffer.
*
* @return The value.
*/
inline int32 getInt () { return GET( int32); }
EPICS_ALWAYS_INLINE int32 getInt () { return GET( int32); }
/**
* Get a long value from the byte buffer.
*
* @return The value.
*/
inline int64 getLong () { return GET( int64); }
EPICS_ALWAYS_INLINE int64 getLong () { return GET( int64); }
/**
* Get a float value from the byte buffer.
*
* @return The value.
*/
inline float getFloat () { return GET( float); }
EPICS_ALWAYS_INLINE float getFloat () { return GET( float); }
/**
* Get a double value from the byte buffer.
*
* @return The value.
*/
inline double getDouble () { return GET(double); }
EPICS_ALWAYS_INLINE double getDouble () { return GET(double); }
/**
* Get a boolean value from the byte buffer at the specified index.
*
* @param index The offset in the byte buffer.
* @return The value.
*/
inline bool getBoolean(std::size_t index) { return get< int8>(index) != 0; }
EPICS_ALWAYS_INLINE bool getBoolean(std::size_t index) { return get< int8>(index) != 0; }
/**
* Get a byte value from the byte buffer at the specified index.
*
* @param index The offset in the byte buffer.
* @return The value.
*/
inline int8 getByte (std::size_t index) { return get< int8>(index); }
EPICS_ALWAYS_INLINE int8 getByte (std::size_t index) { return get< int8>(index); }
/**
* Get a short value from the byte buffer at the specified index.
*
* @param index The offset in the byte buffer.
* @return The value.
*/
inline int16 getShort (std::size_t index) { return get< int16>(index); }
EPICS_ALWAYS_INLINE int16 getShort (std::size_t index) { return get< int16>(index); }
/**
* Get an int value from the byte buffer at the specified index.
*
* @param index The offset in the byte buffer.
* @return The value.
*/
inline int32 getInt (std::size_t index) { return get< int32>(index); }
EPICS_ALWAYS_INLINE int32 getInt (std::size_t index) { return get< int32>(index); }
/**
* Get a long value from the byte buffer at the specified index.
*
* @param index The offset in the byte buffer.
* @return The value.
*/
inline int64 getLong (std::size_t index) { return get< int64>(index); }
EPICS_ALWAYS_INLINE int64 getLong (std::size_t index) { return get< int64>(index); }
/**
* Get a float value from the byte buffer at the specified index.
*
* @param index The offset in the byte buffer.
* @return The value.
*/
inline float getFloat (std::size_t index) { return get< float>(index); }
EPICS_ALWAYS_INLINE float getFloat (std::size_t index) { return get< float>(index); }
/**
* Get a boolean value from the byte buffer at the specified index.
*
* @param index The offset in the byte buffer.
* @return The value.
*/
inline double getDouble (std::size_t index) { return get<double>(index); }
EPICS_ALWAYS_INLINE double getDouble (std::size_t index) { return get<double>(index); }
// TODO remove
inline const char* getArray() const
EPICS_ALWAYS_INLINE const char* getArray() const
{
return _buffer;
}
@@ -660,31 +712,31 @@ private:
};
template<>
inline bool ByteBuffer::reverse<bool>() const
EPICS_ALWAYS_INLINE bool ByteBuffer::reverse<bool>() const
{
return false;
}
template<>
inline bool ByteBuffer::reverse<int8>() const
EPICS_ALWAYS_INLINE bool ByteBuffer::reverse<int8>() const
{
return false;
}
template<>
inline bool ByteBuffer::reverse<uint8>() const
EPICS_ALWAYS_INLINE bool ByteBuffer::reverse<uint8>() const
{
return false;
}
template<>
inline bool ByteBuffer::reverse<float>() const
EPICS_ALWAYS_INLINE bool ByteBuffer::reverse<float>() const
{
return _reverseFloatEndianess;
}
template<>
inline bool ByteBuffer::reverse<double>() const
EPICS_ALWAYS_INLINE bool ByteBuffer::reverse<double>() const
{
return _reverseFloatEndianess;
}
@@ -699,8 +751,7 @@ private:
if(reverse<T>())
value = swap<T>(value);
//assert(is_aligned(_position, sizeof(T)));
*((T*)_position) = value;
detail::store_unaligned(_position, value);
_position += sizeof(T);
}
@@ -712,8 +763,7 @@ private:
if(reverse<T>())
value = swap<T>(value);
//assert(is_aligned(_buffer+index, sizeof(T))); //TODO: special case for targets which support unaligned access
*((T*)(_buffer+index)) = value;
detail::store_unaligned(_buffer+index, value);
}
#if defined (__GNUC__) && (__GNUC__ < 3)
@@ -726,8 +776,7 @@ private:
{
assert(sizeof(T)<=getRemaining());
//assert(is_aligned(_position, sizeof(T)));
T value = *((T*)_position);
T value = detail::load_unaligned<T>(_position);
_position += sizeof(T);
if(reverse<T>())
@@ -740,8 +789,7 @@ private:
{
assert(_buffer+index<=_limit);
//assert(is_aligned(_position, sizeof(T)));
T value = *((T*)(_buffer + index));
T value = detail::load_unaligned<T>(_buffer + index);
if(reverse<T>())
value = swap<T>(value);
@@ -751,17 +799,15 @@ private:
template<typename T>
inline void ByteBuffer::putArray(const T* values, std::size_t count)
{
// we require aligned arrays...
//assert(is_aligned(_position, sizeof(T)));
T* start = (T*)_position;
size_t n = sizeof(T)*count; // bytes
assert(n<=getRemaining());
if (reverse<T>()) {
for(std::size_t i=0; i<count; i++)
start[i] = swap<T>(values[i]);
for(std::size_t i=0; i<n; i+=sizeof(T)) {
detail::store_unaligned(_position+i, swap<T>(values[i]));
}
} else {
memcpy(start, values, n);
memcpy(_position, values, n);
}
_position += n;
}
@@ -769,17 +815,15 @@ private:
template<typename T>
inline void ByteBuffer::getArray(T* values, std::size_t count)
{
// we require aligned arrays...
//assert(is_aligned(_position, sizeof(T)));
const T* start = (T*)_position;
size_t n = sizeof(T)*count; // bytes
assert(n<=getRemaining());
if (reverse<T>()) {
for(std::size_t i=0; i<count; i++)
values[i] = swap<T>(start[i]);
for(std::size_t i=0; i<n; i+=sizeof(T)) {
values[i] = swap<T>(detail::load_unaligned<T>(_position+i));
}
} else {
memcpy(values, start, n);
memcpy(values, _position, n);
}
_position += n;
}

View File

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

View File

@@ -1,92 +0,0 @@
/* executor.h */
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
/**
* @author mrk
*/
#ifndef EXECUTOR_H
#define EXECUTOR_H
#include <memory>
#include <compilerDependencies.h>
#include <pv/pvType.h>
#include <pv/lock.h>
#include <pv/event.h>
#include <pv/thread.h>
#include <pv/sharedPtr.h>
#include <shareLib.h>
namespace epics { namespace pvData {
class Command;
class Executor;
typedef std::tr1::shared_ptr<Command> CommandPtr;
typedef std::tr1::shared_ptr<Executor> ExecutorPtr;
/**
* @brief A command to be called by Executor
*
*/
class epicsShareClass EPICS_DEPRECATED Command {
public:
POINTER_DEFINITIONS(Command);
/**
*
* Destructor
*/
virtual ~Command(){}
/**
*
* The command that is executed.
*/
virtual void command() = 0;
private:
CommandPtr next;
friend class Executor;
};
/**
* @brief A class that executes commands.
*
*/
class epicsShareClass EPICS_DEPRECATED Executor : public Runnable{
public:
POINTER_DEFINITIONS(Executor);
/**
* Constructor
*
* @param threadName name for the executor thread.
* @param priority The thread priority.
*/
Executor(std::string const & threadName,ThreadPriority priority);
/**
* Destructor
*/
~Executor();
/**
*
* Request to execute a command.
* @param command A shared pointer to the command instance.
*/
void execute(CommandPtr const &command);
/**
*
* The thread run method.
*/
virtual void run();
private:
CommandPtr head;
CommandPtr tail;
epics::pvData::Mutex mutex;
epics::pvData::Event moreWork;
epics::pvData::Event stopped;
epics::pvData::Thread thread;
};
}}
#endif /* EXECUTOR_H */

View File

@@ -9,11 +9,13 @@
#ifndef LOCALSTATICLOCK_H
#define LOCALSTATICLOCK_H
#include <compilerDependencies.h>
#include <pv/lock.h>
#include <shareLib.h>
epicsShareExtern epics::pvData::Mutex& getLocalStaticInitMutex();
epicsShareExtern epics::pvData::Mutex& getLocalStaticInitMutex() EPICS_DEPRECATED;
#if defined(__GNUC__) && __GNUC__ >= 4
// noop

View File

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

View File

@@ -1,183 +0,0 @@
/* queue.h */
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
/**
* @author mrk
*/
#ifndef QUEUE_H
#define QUEUE_H
#include <vector>
#include <cstddef>
#include <stdexcept>
#include <compilerDependencies.h>
#include <pv/sharedPtr.h>
namespace epics { namespace pvData {
/**
* @brief Template class for a bounded queue.
*
* An instance can make a queueElement be any class desired
* but must create a std::vector of shared_ptr to queueElements.
*/
template <typename T>
class EPICS_DEPRECATED Queue
{
public:
POINTER_DEFINITIONS(Queue);
typedef std::tr1::shared_ptr<T> queueElementPtr;
typedef std::vector<queueElementPtr> queueElementPtrArray;
/**
* Constructor
* @param elementArray The vector of shared_ptr to queue elements.
*/
Queue(queueElementPtrArray & elementArray);
/**
* Destructor
*/
virtual ~Queue();
/**
* Clear the queue.
*/
void clear();
/**
* get the capacity of the queue, i. e. number of queue elements,
* @return The capacity.
*/
int capacity();
/**
* Get the number of free elements in the queue.
* @return The number.
*/
int getNumberFree();
/**
* Get the number of used elements in the queue.
* This is the number that have been setUsed but not released.
* @return The number.
*/
int getNumberUsed();
/**
* Get the next free element.
* @return a shared_ptr to the queue element.
* This is null if queue was full.
*/
queueElementPtr getFree();
/**
* Set the element returned by getFree as used.
* Until this is called getUsed will not return it.
* @param element The element. It must be the element returned
* by the most recent call to getUsed.
*/
void setUsed(queueElementPtr const &element);
/**
* Get the oldest used element;
* @return a shared_ptr to the queue element.
* This is null if no used element is available.`
*/
queueElementPtr getUsed();
/**
* Release the element obtained by the most recent call to getUsed.
* @param element The element.
*/
void releaseUsed(queueElementPtr const &element);
private:
queueElementPtrArray elements;
// TODO use size_t instead
int size;
int numberFree;
int numberUsed;
int nextGetFree;
int nextSetUsed;
int nextGetUsed;
int nextReleaseUsed;
};
template <typename T>
Queue<T>::Queue(std::vector<queueElementPtr> &xxx)
: size(static_cast<int>(xxx.size())),
numberFree(size),
numberUsed(0),
nextGetFree(0),
nextSetUsed(0),
nextGetUsed(0),
nextReleaseUsed(0)
{
elements.swap(xxx);
}
template <typename T>
Queue<T>::~Queue(){}
template <typename T>
int Queue<T>::capacity(){return size;}
template <typename T>
int Queue<T>::getNumberFree(){return numberFree;}
template <typename T>
int Queue<T>::getNumberUsed(){return numberUsed;}
template <typename T>
void Queue<T>::clear()
{
numberFree = size;
numberUsed = 0;
nextGetFree = 0;
nextSetUsed = 0;
nextGetUsed = 0;
nextReleaseUsed = 0;
}
template <typename T>
std::tr1::shared_ptr<T> Queue<T>::getFree()
{
if(numberFree==0) return queueElementPtr();
numberFree--;
int ind = nextGetFree;
std::tr1::shared_ptr<T> queueElement = elements[nextGetFree++];
if(nextGetFree>=size) nextGetFree = 0;
return elements[ind];
}
template <typename T>
void Queue<T>::setUsed(std::tr1::shared_ptr<T> const &element)
{
if(element!=elements[nextSetUsed++]) {
throw std::logic_error("not correct queueElement");
}
numberUsed++;
if(nextSetUsed>=size) nextSetUsed = 0;
}
template <typename T>
std::tr1::shared_ptr<T> Queue<T>::getUsed()
{
if(numberUsed==0) return queueElementPtr();
int ind = nextGetUsed;
std::tr1::shared_ptr<T> queueElement = elements[nextGetUsed++];
if(nextGetUsed>=size) nextGetUsed = 0;
return elements[ind];
}
template <typename T>
void Queue<T>::releaseUsed(std::tr1::shared_ptr<T> const &element)
{
if(element!=elements[nextReleaseUsed++]) {
throw std::logic_error(
"not queueElement returned by last call to getUsed");
}
if(nextReleaseUsed>=size) nextReleaseUsed = 0;
numberUsed--;
numberFree++;
}
}}
#endif /* QUEUE_H */

View File

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

View File

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

View File

@@ -6,15 +6,15 @@
#ifndef SHAREDVECTOR_H
#define SHAREDVECTOR_H
#if defined(_WIN32) && !defined(NOMINMAX)
#define NOMINMAX
#endif
#include <ostream>
#include <algorithm>
#include <stdexcept>
#include <iterator>
#if __cplusplus>=201103L
# include <initializer_list>
#endif
#include <cassert>
#include "pv/sharedPtr.h"
@@ -70,18 +70,24 @@ namespace detail {
*/
public:
#if __cplusplus>=201103L
//! @brief Empty vector (not very interesting)
constexpr shared_vector_base() noexcept
:m_sdata(), m_offset(0), m_count(0), m_total(0)
{}
#else
//! @brief Empty vector (not very interesting)
shared_vector_base()
:m_sdata(), m_offset(0), m_count(0), m_total(0)
{}
#endif
protected:
// helper for constructors
// Ensure that offset and size are zero when we are constructed with NULL
void _null_input()
{
if(!m_sdata.get()) {
if(!m_sdata) {
m_offset = m_total = m_count = 0;
} else {
// ensure we won't have integer overflows later
@@ -90,19 +96,12 @@ namespace detail {
}
public:
#ifdef _WIN32
template<typename A>
shared_vector_base(A* v, size_t o, size_t c)
:m_sdata(v, detail::default_array_deleter<A*>())
,m_offset(o), m_count(c), m_total(c)
{_null_input();}
#else
template<typename A>
shared_vector_base(A v, size_t o, size_t c)
:m_sdata(v, detail::default_array_deleter<A>())
,m_offset(o), m_count(c), m_total(c)
{_null_input();}
#endif
shared_vector_base(const std::tr1::shared_ptr<E>& d, size_t o, size_t c)
:m_sdata(d), m_offset(o), m_count(c), m_total(c)
{_null_input();}
@@ -118,6 +117,17 @@ namespace detail {
,m_count(O.m_count), m_total(O.m_total)
{}
#if __cplusplus >= 201103L
shared_vector_base(shared_vector_base &&O)
:m_sdata(std::move(O.m_sdata))
,m_offset(O.m_offset)
,m_count(O.m_count)
,m_total(O.m_total)
{
O.clear();
}
#endif
protected:
typedef typename meta::strip_const<E>::type _E_non_const;
public:
@@ -132,7 +142,11 @@ namespace detail {
{
if(!O.unique())
throw std::runtime_error("Can't freeze non-unique vector");
#if __cplusplus >= 201103L
m_sdata = std::move(O.m_sdata);
#else
m_sdata = O.m_sdata;
#endif
O.clear();
}
@@ -146,7 +160,11 @@ namespace detail {
,m_total(O.m_total)
{
O.make_unique();
#if __cplusplus >= 201103L
m_sdata = std::move(std::tr1::const_pointer_cast<E>(O.m_sdata));
#else
m_sdata = std::tr1::const_pointer_cast<E>(O.m_sdata);
#endif
O.clear();
}
@@ -162,6 +180,21 @@ namespace detail {
return *this;
}
#if __cplusplus >= 201103L
//! @brief Move an existing vector
shared_vector_base& operator=(shared_vector_base&& o)
{
if(&o!=this) {
m_sdata=std::move(o.m_sdata);
m_offset=o.m_offset;
m_count=o.m_count;
m_total=o.m_total;
o.clear();
}
return *this;
}
#endif
//! @brief Swap the contents of this vector with another
void swap(shared_vector_base& o) {
if(&o!=this) {
@@ -180,11 +213,12 @@ namespace detail {
}
//! @brief Data is not shared?
bool unique() const {return !m_sdata || m_sdata.unique();}
bool unique() const {return !m_sdata || m_sdata.use_count()<=1;}
//! @brief Number of elements visible through this vector
size_t size() const{return m_count;}
//! @brief shorthand for size()==0
bool empty() const{return !m_count;}
@@ -218,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.
@@ -272,9 +308,22 @@ public:
// allow specialization for all E to be friends
template<typename E1, class Enable1> friend class shared_vector;
//! @brief Empty vector (not very interesting)
#if __cplusplus>=201103L
constexpr shared_vector() noexcept :base_t() {}
#else
shared_vector() :base_t() {}
#endif
#if __cplusplus>=201103L
template<typename A>
shared_vector(std::initializer_list<A> L)
:base_t(new _E_non_const[L.size()], 0, L.size())
{
_E_non_const *raw = const_cast<_E_non_const*>(data());
std::copy(L.begin(), L.end(), raw);
}
#endif
//! @brief Allocate (with new[]) a new vector of size c
explicit shared_vector(size_t c)
@@ -321,6 +370,11 @@ public:
//! @brief Copy an existing vector of same type
shared_vector(const shared_vector& o) :base_t(o) {}
#if __cplusplus>=201103L
//! @brief Move an existing vector of same type
shared_vector(shared_vector&& o) :base_t(std::move(o)) {}
#endif
//! @internal
//! Internal for static_shared_vector_cast
template<typename FROM>
@@ -342,6 +396,20 @@ public:
:base_t(O,t)
{}
inline shared_vector& operator=(const shared_vector& o)
{
this->base_t::operator=(o);
return *this;
}
#if __cplusplus>=201103L
inline shared_vector& operator=(shared_vector&& o)
{
this->base_t::operator=(std::move(o));
return *this;
}
#endif
size_t max_size() const{return ((size_t)-1)/sizeof(E);}
size_t capacity() const { return this->m_total; }
@@ -353,11 +421,16 @@ public:
* does not increase.
*
* For notes on copying see docs for make_unique().
*
* @throws std::bad_alloc if requested allocation can not be made
* @throws other exceptions from element copy ctor
*/
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);
@@ -377,13 +450,16 @@ public:
* as if make_unique() were called. This holds even if the size does not change.
*
* For notes on copying see docs for make_unique().
*
* @throws std::bad_alloc if requested allocation can not be made
* @throws other exceptions from element copy ctor
*/
void resize(size_t i) {
if(i==this->m_count) {
make_unique();
return;
}
if(this->m_sdata && this->m_sdata.unique()) {
if(this->m_sdata && this->m_sdata.use_count()==1) {
// we have data and exclusive ownership of it
if(i<=this->m_total) {
// We have room to grow (or shrink)!
@@ -392,13 +468,18 @@ public:
}
}
// must re-allocate :(
size_t new_total = std::max(this->m_total, i);
size_t new_total = this->m_total;
if(new_total < i)
new_total = i;
_E_non_const* temp=new _E_non_const[new_total];
try{
size_t n = this->size();
if(n > i)
n = i;
// Copy as much as possible from old,
// remaining elements are uninitialized.
std::copy(begin(),
begin()+std::min(i,this->size()),
begin()+n,
temp);
this->m_sdata.reset(temp, detail::default_array_deleter<pointer>());
}catch(...){
@@ -438,10 +519,14 @@ public:
}
assert(original.unique());
@endcode
*
* @throws std::bad_alloc if requested allocation can not be made
* @throws other exceptions from element copy ctor
*/
void make_unique() {
if(this->unique())
return;
// at this point we know that !!m_sdata, so get()!=NULL
_E_non_const *d = new _E_non_const[this->m_total];
try {
std::copy(this->m_sdata.get()+this->m_offset,
@@ -455,13 +540,27 @@ public:
this->m_offset=0;
}
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.
* 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
return this->m_count ? this->m_sdata.get() : (E*)(this-1);
#else
return this->m_sdata.get();
#endif
}
public:
// STL iterators
iterator begin() const{return this->m_sdata.get()+this->m_offset;}
iterator begin() const{return this->base_ptr()+this->m_offset;}
const_iterator cbegin() const{return begin();}
iterator end() const{return this->m_sdata.get()+this->m_offset+this->m_count;}
iterator end() const{return this->base_ptr()+this->m_offset+this->m_count;}
const_iterator cend() const{return end();}
reverse_iterator rbegin() const{return reverse_iterator(end());}
@@ -513,10 +612,15 @@ public:
// data access
//! @brief Return Base pointer
pointer data() const{return this->m_sdata.get()+this->m_offset;}
//! @brief Member access
//! Undefined if empty()==true.
reference operator[](size_t i) const {return this->m_sdata.get()[this->m_offset+i];}
//! @brief Member access
//! @throws std::out_of_range if i>=size().
reference at(size_t i) const
{
if(i>this->m_count)
@@ -563,7 +667,11 @@ public:
typedef std::tr1::shared_ptr<E> shared_pointer_type;
#if __cplusplus>=201103L
constexpr shared_vector() noexcept :base_t(), m_vtype((ScalarType)-1) {}
#else
shared_vector() :base_t(), m_vtype((ScalarType)-1) {}
#endif
shared_vector(pointer v, size_t o, size_t c)
:base_t(v,o,c), m_vtype((ScalarType)-1) {}
@@ -579,6 +687,11 @@ public:
shared_vector(const shared_vector& o)
:base_t(o), m_vtype(o.m_vtype) {}
#if __cplusplus>=201103L
shared_vector(shared_vector&& o)
:base_t(std::move(o)), m_vtype(o.m_vtype) {}
#endif
//! @internal
//! Internal for static_shared_vector_cast
template<typename FROM>
@@ -609,6 +722,22 @@ public:
return *this;
}
#if __cplusplus>=201103L
shared_vector& operator=(shared_vector&& o)
{
if(&o!=this) {
this->base_t::operator=(std::move(o));
m_vtype = o.m_vtype;
}
return *this;
}
#endif
void swap(shared_vector& o) {
base_t::swap(o);
std::swap(m_vtype, o.m_vtype);
}
size_t max_size() const{return (size_t)-1;}
pointer data() const{

View File

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

View File

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

View File

@@ -1,72 +0,0 @@
/* timeFunction.h */
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
/**
* @author mrk
*/
#ifndef TIMEFUNCTION_H
#define TIMEFUNCTION_H
#include <compilerDependencies.h>
#include <pv/sharedPtr.h>
#include <shareLib.h>
namespace epics { namespace pvData {
class TimeFunctionRequester;
class TimeFunction;
typedef std::tr1::shared_ptr<TimeFunctionRequester> TimeFunctionRequesterPtr;
typedef std::tr1::shared_ptr<TimeFunction> TimeFunctionPtr;
/**
* @brief Class that must be implemented by timeFunction requester.
*
*/
class epicsShareClass EPICS_DEPRECATED TimeFunctionRequester {
public:
POINTER_DEFINITIONS(TimeFunctionRequester);
/**
* Destructor
*/
virtual ~TimeFunctionRequester(){}
/**
* function to be timed.
* It will get called multiple times.
*/
virtual void function() = 0;
};
/**
* @brief Class for measuring time it takes to execute a function.
*
*/
class epicsShareClass EPICS_DEPRECATED TimeFunction {
public:
POINTER_DEFINITIONS(TimeFunction);
/**
* Constructor
* @param requester The class that has a function method.
*/
TimeFunction(TimeFunctionRequesterPtr const & requester);
/**
* Destructor
*/
~TimeFunction();
/**
* Time the function.
* @return the time in seconds to execute the function.
* Note that the function may be called many times.
*/
double timeCall();
private:
TimeFunctionRequesterPtr requester;
};
}}
#endif /* TIMEFUNCTION_H */

View File

@@ -9,6 +9,8 @@
#ifndef TIMER_H
#define TIMER_H
#include <memory>
#include <list>
#include <stddef.h>
#include <stdlib.h>
#include <stddef.h>
@@ -55,34 +57,28 @@ public:
*/
virtual void timerStopped() = 0;
private:
TimerCallbackPtr next;
TimeStamp timeToRun;
epicsTime timeToRun;
double period;
bool onList;
friend class Timer;
struct IncreasingTime;
};
/**
* @brief Support for delayed or periodic callback execution.
*
*/
class epicsShareClass Timer : public Runnable {
class epicsShareClass Timer : private Runnable {
public:
POINTER_DEFINITIONS(Timer);
/**
* Constructor
/** Create a new timer queue
* @param threadName name for the timer thread.
* @param priority thread priority
*/
Timer(std::string threadName, ThreadPriority priority);
/**
* Destructor
*/
virtual ~Timer();
/**
* The thread run method. This is called automatically.
*/
virtual void run();
//! Prevent new callbacks from being scheduled, and cancel pending callbacks
void close();
/**
* schedule a callback after a delay.
* @param timerCallback the timerCallback instance.
@@ -104,30 +100,38 @@ public:
/**
* cancel a callback.
* @param timerCallback the timerCallback to cancel.
* @returns true if the timer was queued, and now is cancelled
*/
void cancel(TimerCallbackPtr const &timerCallback);
bool cancel(TimerCallbackPtr const &timerCallback);
/**
* Is the callback scheduled to be called?
* @param timerCallback the timerCallback.
* @return (false,true) if (not, is) scheduled.
*/
bool isScheduled(TimerCallbackPtr const &timerCallback);
bool isScheduled(TimerCallbackPtr const &timerCallback) const;
/**
* show the elements in the timer queue.
* @param o The output stream for the output
*/
void dump(std::ostream& o);
void dump(std::ostream& o) const;
private:
virtual void run();
// call with mutex held
void addElement(TimerCallbackPtr const &timerCallback);
TimerCallbackPtr head;
Mutex mutex;
typedef std::list<TimerCallbackPtr> queue_t;
mutable Mutex mutex;
queue_t queue;
Event waitForWork;
bool waiting;
bool alive;
Thread thread;
};
epicsShareExtern std::ostream& operator<<(std::ostream& o, Timer& timer);
epicsShareExtern std::ostream& operator<<(std::ostream& o, const Timer& timer);
}}
#endif /* TIMER_H */

View File

@@ -22,17 +22,29 @@ namespace epics { namespace pvData {
namespace detail {
// parseToPOD wraps the epicsParse*() functions in one name
// and throws exceptions
epicsShareExtern void parseToPOD(const std::string&, boolean *out);
epicsShareExtern void parseToPOD(const std::string&, int8 *out);
epicsShareExtern void parseToPOD(const std::string&, uint8 *out);
epicsShareExtern void parseToPOD(const std::string&, int16_t *out);
epicsShareExtern void parseToPOD(const std::string&, uint16_t *out);
epicsShareExtern void parseToPOD(const std::string&, int32_t *out);
epicsShareExtern void parseToPOD(const std::string&, uint32_t *out);
epicsShareExtern void parseToPOD(const std::string&, int64_t *out);
epicsShareExtern void parseToPOD(const std::string&, uint64_t *out);
epicsShareExtern void parseToPOD(const std::string&, float *out);
epicsShareExtern void parseToPOD(const std::string&, double *out);
epicsShareExtern void parseToPOD(const char*, boolean *out);
epicsShareExtern void parseToPOD(const char*, int8 *out);
epicsShareExtern void parseToPOD(const char*, uint8 *out);
epicsShareExtern void parseToPOD(const char*, int16_t *out);
epicsShareExtern void parseToPOD(const char*, uint16_t *out);
epicsShareExtern void parseToPOD(const char*, int32_t *out);
epicsShareExtern void parseToPOD(const char*, uint32_t *out);
epicsShareExtern void parseToPOD(const char*, int64_t *out);
epicsShareExtern void parseToPOD(const char*, uint64_t *out);
epicsShareExtern void parseToPOD(const char*, float *out);
epicsShareExtern void parseToPOD(const char*, double *out);
static inline void parseToPOD(const std::string& str, boolean *out) { return parseToPOD(str.c_str(), out); }
static inline void parseToPOD(const std::string& str, int8 *out) { return parseToPOD(str.c_str(), out); }
static inline void parseToPOD(const std::string& str, uint8 *out) { return parseToPOD(str.c_str(), out); }
static inline void parseToPOD(const std::string& str, int16_t *out) { return parseToPOD(str.c_str(), out); }
static inline void parseToPOD(const std::string& str, uint16_t *out) { return parseToPOD(str.c_str(), out); }
static inline void parseToPOD(const std::string& str, int32_t *out) { return parseToPOD(str.c_str(), out); }
static inline void parseToPOD(const std::string& str, uint32_t *out) { return parseToPOD(str.c_str(), out); }
static inline void parseToPOD(const std::string& str, int64_t *out) { return parseToPOD(str.c_str(), out); }
static inline void parseToPOD(const std::string& str, uint64_t *out) { return parseToPOD(str.c_str(), out); }
static inline void parseToPOD(const std::string& str, float *out) { return parseToPOD(str.c_str(), out); }
static inline void parseToPOD(const std::string& str, double *out) { return parseToPOD(str.c_str(), out); }
/* want to pass POD types by value,
* and std::string by const reference
@@ -108,13 +120,28 @@ namespace detail {
}
};
// parse POD from C string
// TO!=const char*
template<typename TO>
struct cast_helper<TO, const char*,
typename meta::_and<
typename meta::not_same_type<TO,const char*>,
typename meta::not_same_type<TO,std::string>
>::type> {
static FORCE_INLINE TO op(const char* from) {
TO ret;
parseToPOD(from, &ret);
return ret;
}
};
} // end detail
/** @brief Casting/converting between supported scalar types.
*
* Supported types: uint8_t, int8_t, uint16_t, int16_t,
* uint32_t, int32_t, uint64_t, int64_t,
* float, double, std::string
* float, double, std::string, const char* (only FROM)
*
* As defined in pvType.h
*
@@ -181,6 +208,35 @@ static FORCE_INLINE
typename detail::print_convolute<T>::return_t
print_cast(const T& v) { return detail::print_convolute<T>::op(v); }
struct escape;
epicsShareFunc
std::ostream& operator<<(std::ostream& strm, const escape& Q);
//! Helper to print a string with escaping
//! @code strm<<'"'<<escape(astring)<<'"' @endcode
struct epicsShareClass escape
{
enum style_t {
C, // default
CSV, // a la RFC4180
};
private:
const std::string& orig;
style_t S;
public:
escape(const std::string& orig) :orig(orig), S(C) {}
~escape();
//! Change escaping style
inline escape& style(style_t s) { S = s; return *this; }
//! print to string and return. (alloc and copy)
std::string str() const;
friend
epicsShareFunc
std::ostream& operator<<(std::ostream& strm, const escape& Q);
};
}} // end namespace
#endif // PVTYPECAST_H

View File

@@ -265,7 +265,6 @@ void RefMonitor::current()
void RefMonitor::show(const RefSnapshot &snap, bool complete)
{
epicsTime now(epicsTime::getCurrent());
char buf[80];
epicsTime::getCurrent().strftime(buf, sizeof(buf), "%a %b %d %Y %H:%M:%S.%f");
buf[sizeof(buf)-1] = '\0';

View File

@@ -98,8 +98,6 @@ namespace epics {
break;
}
}
static string emptyStringtring;
string SerializeHelper::deserializeString(ByteBuffer* buffer,
DeserializableControl* control) {
@@ -139,7 +137,7 @@ namespace epics {
}
}
else
return emptyStringtring;
return std::string();
}
}
}

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

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

View File

@@ -1,56 +0,0 @@
/* timeFunction.cpp */
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
/**
* @author mrk
*/
#include <cstddef>
#include <cstdlib>
#include <cstddef>
#include <string>
#include <cstdio>
#include <epicsTime.h>
// Suppress deprecation warnings for the implementation
#include <compilerDependencies.h>
#undef EPICS_DEPRECATED
#define EPICS_DEPRECATED
#define epicsExportSharedSymbols
#include <pv/pvType.h>
#include <pv/timeStamp.h>
#include <pv/timeFunction.h>
namespace epics { namespace pvData {
TimeFunction::TimeFunction(TimeFunctionRequesterPtr const &requester)
: requester(requester) {}
TimeFunction::~TimeFunction() {}
double TimeFunction::timeCall()
{
TimeStamp startTime;
TimeStamp endTime;
double perCall = 0.0;
long ntimes = 1;
while(true) {
startTime.getCurrent();
for(long i=0; i<ntimes; i++) requester->function();
endTime.getCurrent();
double diff = TimeStamp::diff(endTime,startTime);
if(diff>=1.0) {
perCall = diff/(double)ntimes;
break;
}
ntimes *= 2;
}
return perCall;
}
}}

View File

@@ -6,16 +6,13 @@
/**
* @author mrk
*/
#if defined(_WIN32) && !defined(NOMINMAX)
#define NOMINMAX
#endif
#include <stdexcept>
#include <string>
#include <iostream>
#include <epicsThread.h>
#include <epicsGuard.h>
#define epicsExportSharedSymbols
#include <pv/timer.h>
@@ -31,66 +28,52 @@ TimerCallback::TimerCallback()
}
Timer::Timer(string threadName,ThreadPriority priority)
: waitForWork(false),
alive(true),
thread(threadName,priority,this)
:waitForWork(false)
,waiting(false)
,alive(true)
,thread(threadName,priority,this)
{}
struct TimerCallback::IncreasingTime {
bool operator()(const TimerCallbackPtr& lhs, const TimerCallbackPtr& rhs) {
assert(lhs && rhs);
return lhs->timeToRun < rhs->timeToRun;
}
};
// call with mutex held
void Timer::addElement(TimerCallbackPtr const & timerCallback)
{
assert(!timerCallback->onList);
queue_t temp;
temp.push_back(timerCallback);
timerCallback->onList = true;
if(head.get()==NULL) {
head = timerCallback;
timerCallback->next.reset();
return;
}
TimerCallbackPtr nextNode(head);
TimerCallbackPtr prevNode;
while(true) {
if(timerCallback->timeToRun < nextNode->timeToRun) {
if(prevNode.get()!=NULL) {
prevNode->next = timerCallback;
} else {
head = timerCallback;
}
timerCallback->next = nextNode;
return;
}
if(nextNode->next.get()==NULL) {
nextNode->next = timerCallback;
timerCallback->next.reset();
return;
}
prevNode = nextNode;
nextNode = nextNode->next;
}
// merge sorted lists.
// for us effectively insertion sort.
queue.merge(temp, TimerCallback::IncreasingTime());
}
void Timer::cancel(TimerCallbackPtr const &timerCallback)
bool Timer::cancel(TimerCallbackPtr const &timerCallback)
{
Lock xx(mutex);
if(!timerCallback->onList) return;
TimerCallbackPtr nextNode(head);
TimerCallbackPtr prevNode;
while(true) {
if(nextNode.get()==timerCallback.get()) {
if(prevNode.get()!=NULL) {
prevNode->next = timerCallback->next;
} else {
head = timerCallback->next;
}
timerCallback->next.reset();
timerCallback->onList = false;
return;
if(!timerCallback->onList) return false;
for(queue_t::iterator it(queue.begin()), end(queue.end()); it != end; ++it)
{
TimerCallbackPtr& cur = *it;
if(cur.get() == timerCallback.get()) {
cur->onList = false;
queue.erase(it); // invalidates cur and it
return true;
}
prevNode = nextNode;
nextNode = nextNode->next;
}
throw std::logic_error(string(""));
throw std::logic_error("Timer::cancel() onList==true, but not found");
}
bool Timer::isScheduled(TimerCallbackPtr const &timerCallback)
bool Timer::isScheduled(TimerCallbackPtr const &timerCallback) const
{
Lock xx(mutex);
return timerCallback->onList;
@@ -99,62 +82,75 @@ bool Timer::isScheduled(TimerCallbackPtr const &timerCallback)
void Timer::run()
{
TimeStamp currentTime;
while(true) {
double period = 0.0;
TimerCallbackPtr nodeToCall;
{
Lock xx(mutex);
currentTime.getCurrent();
if (!alive) break;
TimerCallbackPtr timerCallback = head;
if(timerCallback.get()!=NULL) {
double diff = TimeStamp::diff(
timerCallback->timeToRun,currentTime);
if(diff<=0.0) {
nodeToCall = timerCallback;
nodeToCall->onList = false;
head = head->next;
period = timerCallback->period;
if(period>0.0) {
timerCallback->timeToRun += period;
addElement(timerCallback);
}
timerCallback = head;
}
}
}
if(nodeToCall.get()!=NULL) {
nodeToCall->callback();
}
{
Lock xx(mutex);
if(!alive) break;
}
if(head.get()==NULL) {
epicsGuard<epicsMutex> G(mutex);
epicsTime now(epicsTime::getCurrent());
while(alive) {
double waitfor;
if(queue.empty()) {
// no jobs, just go to sleep
waiting = true;
epicsGuardRelease<epicsMutex> U(G);
waitForWork.wait();
} else {
double delay = TimeStamp::diff(head->timeToRun,currentTime);
waitForWork.wait(delay);
}
}
now = epicsTime::getCurrent();
} else if((waitfor = queue.front()->timeToRun - now) <= 0) {
// execute first expired job
TimerCallbackPtr work;
work.swap(queue.front());
work->onList = false;
queue.pop_front();
{
epicsGuardRelease<epicsMutex> U(G);
work->callback();
}
if(work->period > 0.0) {
work->timeToRun += work->period;
addElement(work);
}
// 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;
}
}
Timer::~Timer() {
close();
}
void Timer::close() {
{
Lock xx(mutex);
if(!alive)
return; // already closed
alive = false;
}
waitForWork.signal();
thread.exitWait();
TimerCallbackPtr timerCallback;
while(true) {
timerCallback = head;
if(head.get()==NULL) break;
queue_t temp;
temp.swap(queue);
for(;!temp.empty(); temp.pop_front()) {
TimerCallbackPtr& head = temp.front();
head->onList = false;
head->timerStopped();
head = timerCallback->next;
timerCallback->next.reset();
timerCallback->onList = false;
}
}
@@ -170,49 +166,44 @@ void Timer::schedulePeriodic(
double delay,
double period)
{
if(isScheduled(timerCallback)) {
throw std::logic_error(string("already queued"));
}
epicsTime now(epicsTime::getCurrent());
bool wakeup;
{
Lock xx(mutex);
if(timerCallback->onList) {
throw std::logic_error(string("already queued"));
}
if(!alive) {
xx.unlock();
timerCallback->timerStopped();
return;
}
}
TimeStamp timeStamp;
timeStamp.getCurrent();
timeStamp += delay;
timerCallback->timeToRun.getCurrent();
timerCallback->timeToRun += delay;
timerCallback->period = period;
bool isFirst = false;
{
Lock xx(mutex);
timerCallback->timeToRun = now + delay;
timerCallback->period = period;
addElement(timerCallback);
if(timerCallback.get()==head.get()) isFirst = true;
wakeup = waiting && queue.front()==timerCallback;
}
if(isFirst) waitForWork.signal();
if(wakeup) waitForWork.signal();
}
void Timer::dump(std::ostream& o)
void Timer::dump(std::ostream& o) const
{
Lock xx(mutex);
if(!alive) return;
TimeStamp currentTime;
TimerCallbackPtr nodeToCall(head);
currentTime.getCurrent();
while(true) {
if(nodeToCall.get()==NULL) return;
TimeStamp timeToRun = nodeToCall->timeToRun;
double period = nodeToCall->period;
double diff = TimeStamp::diff(timeToRun,currentTime);
o << "timeToRun " << diff << " period " << period << std::endl;
nodeToCall = nodeToCall->next;
}
epicsTime now(epicsTime::getCurrent());
for(queue_t::const_iterator it(queue.begin()), end(queue.end()); it!=end; ++it) {
const TimerCallbackPtr& nodeToCall = *it;
o << "timeToRun " << (nodeToCall->timeToRun - now)
<< " period " << nodeToCall->period << "\n";
}
}
std::ostream& operator<<(std::ostream& o, Timer& timer)
std::ostream& operator<<(std::ostream& o, const Timer& timer)
{
timer.dump(o);
return o;

View File

@@ -6,6 +6,8 @@
#include <algorithm>
#include <sstream>
#include <string.h>
#include <epicsConvert.h>
#define epicsExportSharedSymbols
@@ -18,7 +20,7 @@ using std::string;
namespace {
static void noconvert(size_t, void*, const void*)
static void noconvert()
{
throw std::runtime_error("castUnsafeV: Conversion not supported");
}
@@ -31,12 +33,10 @@ static void castVTyped(size_t count, void *draw, const void *sraw)
//std::transform(src, src+count, dest, castUnsafe<TO,FROM>);
const FROM *last = src+count;
try {
while (src != last) {
*dest = castUnsafe<TO,FROM>(*src);
++dest; ++src;
}
for(size_t i=0; i<count; i++) {
dest[i] = castUnsafe<TO,FROM>(src[i]);
}
} catch (std::exception& ex) {
// do not report index for scalars (or arrays with one element)
if (count > 1)
@@ -59,182 +59,11 @@ static void copyV(size_t count, void *draw, const void *sraw)
std::copy(src, src+count, dest);
}
typedef void (*convertfn)(size_t, void*, const void*);
/* lookup table of converter functions.
* first dimension is TO, second is FROM
*/
static convertfn converters[pvString+1][pvString+1] =
template<int N>
static void copyMem(size_t count, void *draw, const void *sraw)
{
// to pvBoolean
{ &copyV<epics::pvData::boolean>,
&noconvert,
&noconvert,
&noconvert,
&noconvert,
&noconvert,
&noconvert,
&noconvert,
&noconvert,
&noconvert,
&noconvert,
&castVTyped<epics::pvData::boolean, string>,
},
// to pvByte
{&noconvert,
&copyV<int8_t>,
&castVTyped<int8_t, int16_t>,
&castVTyped<int8_t, int32_t>,
&castVTyped<int8_t, int64_t>,
&castVTyped<int8_t, uint8_t>,
&castVTyped<int8_t, uint16_t>,
&castVTyped<int8_t, uint32_t>,
&castVTyped<int8_t, uint64_t>,
&castVTyped<int8_t, float>,
&castVTyped<int8_t, double>,
&castVTyped<int8_t, string>,
},
// to pvShort
{&noconvert,
&castVTyped<int16_t, int8_t>,
&copyV<int16_t>,
&castVTyped<int16_t, int32_t>,
&castVTyped<int16_t, int64_t>,
&castVTyped<int16_t, uint8_t>,
&castVTyped<int16_t, uint16_t>,
&castVTyped<int16_t, uint32_t>,
&castVTyped<int16_t, uint64_t>,
&castVTyped<int16_t, float>,
&castVTyped<int16_t, double>,
&castVTyped<int16_t, string>,
},
// to pvInt
{&noconvert,
&castVTyped<int32_t, int8_t>,
&castVTyped<int32_t, int16_t>,
&copyV<int32_t>,
&castVTyped<int32_t, int64_t>,
&castVTyped<int32_t, uint8_t>,
&castVTyped<int32_t, uint16_t>,
&castVTyped<int32_t, uint32_t>,
&castVTyped<int32_t, uint64_t>,
&castVTyped<int32_t, float>,
&castVTyped<int32_t, double>,
&castVTyped<int32_t, string>,
},
// to pvLong
{&noconvert,
&castVTyped<int64_t, int8_t>,
&castVTyped<int64_t, int16_t>,
&castVTyped<int64_t, int32_t>,
&copyV<int64_t>,
&castVTyped<int64_t, uint8_t>,
&castVTyped<int64_t, uint16_t>,
&castVTyped<int64_t, uint32_t>,
&castVTyped<int64_t, uint64_t>,
&castVTyped<int64_t, float>,
&castVTyped<int64_t, double>,
&castVTyped<int64_t, string>,
},
// to pvUByte
{&noconvert,
&castVTyped<uint8_t, int8_t>,
&castVTyped<uint8_t, int16_t>,
&castVTyped<uint8_t, int32_t>,
&castVTyped<uint8_t, uint64_t>,
&copyV<uint8_t>,
&castVTyped<uint8_t, uint16_t>,
&castVTyped<uint8_t, uint32_t>,
&castVTyped<uint8_t, uint64_t>,
&castVTyped<uint8_t, float>,
&castVTyped<uint8_t, double>,
&castVTyped<uint8_t, string>,
},
// to pvUShort
{&noconvert,
&castVTyped<uint16_t, int8_t>,
&castVTyped<uint16_t, int16_t>,
&castVTyped<uint16_t, int32_t>,
&castVTyped<uint16_t, uint64_t>,
&castVTyped<uint16_t, uint8_t>,
&copyV<uint16_t>,
&castVTyped<uint16_t, uint32_t>,
&castVTyped<uint16_t, uint64_t>,
&castVTyped<uint16_t, float>,
&castVTyped<uint16_t, double>,
&castVTyped<uint16_t, string>,
},
// to pvUInt
{&noconvert,
&castVTyped<uint32_t, int8_t>,
&castVTyped<uint32_t, int16_t>,
&castVTyped<uint32_t, int32_t>,
&castVTyped<uint32_t, uint64_t>,
&castVTyped<uint32_t, uint8_t>,
&castVTyped<uint32_t, uint16_t>,
&copyV<uint32_t>,
&castVTyped<uint32_t, uint64_t>,
&castVTyped<uint32_t, float>,
&castVTyped<uint32_t, double>,
&castVTyped<uint32_t, string>,
},
// to pvULong
{&noconvert,
&castVTyped<uint64_t, int8_t>,
&castVTyped<uint64_t, int16_t>,
&castVTyped<uint64_t, int32_t>,
&castVTyped<uint64_t, uint64_t>,
&castVTyped<uint64_t, uint8_t>,
&castVTyped<uint64_t, uint16_t>,
&castVTyped<uint64_t, uint32_t>,
&copyV<uint64_t>,
&castVTyped<uint64_t, float>,
&castVTyped<uint64_t, double>,
&castVTyped<uint64_t, string>,
},
// to pvFloat
{&noconvert,
&castVTyped<float, int8_t>,
&castVTyped<float, int16_t>,
&castVTyped<float, int32_t>,
&castVTyped<float, uint64_t>,
&castVTyped<float, uint8_t>,
&castVTyped<float, uint16_t>,
&castVTyped<float, uint32_t>,
&castVTyped<float, uint64_t>,
&copyV<float>,
&castVTyped<float, double>,
&castVTyped<float, string>,
},
// to pvDouble
{&noconvert,
&castVTyped<double, int8_t>,
&castVTyped<double, int16_t>,
&castVTyped<double, int32_t>,
&castVTyped<double, uint64_t>,
&castVTyped<double, uint8_t>,
&castVTyped<double, uint16_t>,
&castVTyped<double, uint32_t>,
&castVTyped<double, uint64_t>,
&castVTyped<double, float>,
&copyV<double>,
&castVTyped<double, string>,
},
// to pvString
{&castVTyped<string, epics::pvData::boolean>,
&castVTyped<string, int8_t>,
&castVTyped<string, int16_t>,
&castVTyped<string, int32_t>,
&castVTyped<string, uint64_t>,
&castVTyped<string, uint8_t>,
&castVTyped<string, uint16_t>,
&castVTyped<string, uint32_t>,
&castVTyped<string, uint64_t>,
&castVTyped<string, float>,
&castVTyped<string, double>,
&copyV<string>,
},
};
memcpy(draw, sraw, count*N);
}
} // end namespace
@@ -242,12 +71,207 @@ namespace epics { namespace pvData {
void castUnsafeV(size_t count, ScalarType to, void *dest, ScalarType from, const void *src)
{
unsigned int ito=to, ifrom=from;
#define COPYMEM(N) copyMem<N>(count, dest, src)
#define CAST(TO, FROM) castVTyped<TO, FROM>(count, dest, src)
if(ito>pvString || ifrom>pvString)
throw std::runtime_error("castUnsafeV: Invalid types");
switch(to) {
case pvBoolean:
switch(from) {
case pvBoolean: COPYMEM(1); return;
case pvString: CAST(boolean, std::string); return;
default: noconvert(); return;
}
break;
converters[ito][ifrom](count, dest, src);
case pvByte:
switch(from) {
case pvBoolean: noconvert(); return;
case pvByte:
case pvUByte: COPYMEM(1); return;
case pvShort: CAST(int8, int16); return;
case pvUShort: CAST(int8, uint16); return;
case pvInt: CAST(int8, int32); return;
case pvUInt: CAST(int8, uint32); return;
case pvLong: CAST(int8, int64); return;
case pvULong: CAST(int8, uint64); return;
case pvFloat: CAST(int8, float); return;
case pvDouble: CAST(int8, double); return;
case pvString: CAST(int8, std::string); return;
}
break;
case pvUByte:
switch(from) {
case pvBoolean: noconvert(); return;
case pvByte:
case pvUByte: COPYMEM(1); return;
case pvShort: CAST(uint8, int16); return;
case pvUShort: CAST(uint8, uint16); return;
case pvInt: CAST(uint8, int32); return;
case pvUInt: CAST(uint8, uint32); return;
case pvLong: CAST(uint8, int64); return;
case pvULong: CAST(uint8, uint64); return;
case pvFloat: CAST(uint8, float); return;
case pvDouble: CAST(uint8, double); return;
case pvString: CAST(uint8, std::string); return;
}
break;
case pvShort:
switch(from) {
case pvBoolean: noconvert(); return;
case pvByte: CAST(int16, int8); return;
case pvUByte: CAST(int16, uint8); return;
case pvShort:
case pvUShort: COPYMEM(2); return;
case pvInt: CAST(int16, int32); return;
case pvUInt: CAST(int16, uint32); return;
case pvLong: CAST(int16, int64); return;
case pvULong: CAST(int16, uint64); return;
case pvFloat: CAST(int16, float); return;
case pvDouble: CAST(int16, double); return;
case pvString: CAST(int16, std::string); return;
}
break;
case pvUShort:
switch(from) {
case pvBoolean: noconvert(); return;
case pvByte: CAST(uint16, int8); return;
case pvUByte: CAST(uint16, uint8); return;
case pvShort:
case pvUShort: COPYMEM(2); return;
case pvInt: CAST(uint16, int32); return;
case pvUInt: CAST(uint16, uint32); return;
case pvLong: CAST(uint16, int64); return;
case pvULong: CAST(uint16, uint64); return;
case pvFloat: CAST(uint16, float); return;
case pvDouble: CAST(uint16, double); return;
case pvString: CAST(uint16, std::string); return;
}
break;
case pvInt:
switch(from) {
case pvBoolean: noconvert(); return;
case pvByte: CAST(int32, int8); return;
case pvUByte: CAST(int32, uint8); return;
case pvShort: CAST(int32, int16); return;
case pvUShort: CAST(int32, uint16); return;
case pvInt:
case pvUInt: COPYMEM(4); return;
case pvLong: CAST(int32, int64); return;
case pvULong: CAST(int32, uint64); return;
case pvFloat: CAST(int32, float); return;
case pvDouble: CAST(int32, double); return;
case pvString: CAST(int32, std::string); return;
}
break;
case pvUInt:
switch(from) {
case pvBoolean: noconvert(); return;
case pvByte: CAST(uint32, int8); return;
case pvUByte: CAST(uint32, uint8); return;
case pvShort: CAST(uint32, int16); return;
case pvUShort: CAST(uint32, uint16); return;
case pvInt:
case pvUInt: COPYMEM(4); return;
case pvLong: CAST(uint32, int64); return;
case pvULong: CAST(uint32, uint64); return;
case pvFloat: CAST(uint32, float); return;
case pvDouble: CAST(uint32, double); return;
case pvString: CAST(uint32, std::string); return;
}
break;
case pvLong:
switch(from) {
case pvBoolean: noconvert(); return;
case pvByte: CAST(int64, int8); return;
case pvUByte: CAST(int64, uint8); return;
case pvShort: CAST(int64, int16); return;
case pvUShort: CAST(int64, uint16); return;
case pvInt: CAST(int64, int32); return;
case pvUInt: CAST(int64, uint32); return;
case pvLong:
case pvULong: COPYMEM(8); return;
case pvFloat: CAST(int64, float); return;
case pvDouble: CAST(int64, double); return;
case pvString: CAST(int64, std::string); return;
}
break;
case pvULong:
switch(from) {
case pvBoolean: noconvert(); return;
case pvByte: CAST(uint64, int8); return;
case pvUByte: CAST(uint64, uint8); return;
case pvShort: CAST(uint64, int16); return;
case pvUShort: CAST(uint64, uint16); return;
case pvInt: CAST(uint64, int32); return;
case pvUInt: CAST(uint64, uint32); return;
case pvLong:
case pvULong: COPYMEM(8); return;
case pvFloat: CAST(uint64, float); return;
case pvDouble: CAST(uint64, double); return;
case pvString: CAST(uint64, std::string); return;
}
break;
case pvFloat:
switch(from) {
case pvBoolean: noconvert(); return;
case pvByte: CAST(float, int8); return;
case pvUByte: CAST(float, uint8); return;
case pvShort: CAST(float, int16); return;
case pvUShort: CAST(float, uint16); return;
case pvInt: CAST(float, int32); return;
case pvUInt: CAST(float, uint32); return;
case pvLong: CAST(float, int64); return;
case pvULong: CAST(float, uint64); return;
case pvFloat: COPYMEM(4); return;
case pvDouble: CAST(float, double); return;
case pvString: CAST(float, std::string); return;
}
break;
case pvDouble:
switch(from) {
case pvBoolean: noconvert(); return;
case pvByte: CAST(double, int8); return;
case pvUByte: CAST(double, uint8); return;
case pvShort: CAST(double, int16); return;
case pvUShort: CAST(double, uint16); return;
case pvInt: CAST(double, int32); return;
case pvUInt: CAST(double, uint32); return;
case pvLong: CAST(double, int64); return;
case pvULong: CAST(double, uint64); return;
case pvFloat: CAST(double, float); return;
case pvDouble: COPYMEM(8); return;
case pvString: CAST(double, std::string); return;
}
break;
case pvString:
switch(from) {
case pvBoolean: CAST(std::string, boolean); return;
case pvByte: CAST(std::string, int8); return;
case pvUByte: CAST(std::string, uint8); return;
case pvShort: CAST(std::string, int16); return;
case pvUShort: CAST(std::string, uint16); return;
case pvInt: CAST(std::string, int32); return;
case pvUInt: CAST(std::string, uint32); return;
case pvLong: CAST(std::string, int64); return;
case pvULong: CAST(std::string, uint64); return;
case pvFloat: CAST(std::string, float); return;
case pvDouble: CAST(std::string, double); return;
case pvString: copyV<std::string>(count, dest, src); return;
}
break;
}
THROW_EXCEPTION2(std::logic_error, "Undefined cast");
}
}}

View File

@@ -30,14 +30,14 @@ bool PVTimeStamp::attach(PVFieldPtr const & pvField)
PVStructure* pvStructure = xxx.get();
while(true) {
PVLongPtr pvLong = pvStructure->getSubField<PVLong>("secondsPastEpoch");
if(pvLong.get()!=NULL) {
if(pvLong) {
pvSecs = pvLong;
pvNano = pvStructure->getSubField<PVInt>("nanoseconds");
pvUserTag = pvStructure->getSubField<PVInt>("userTag");
}
if(pvSecs.get()!=NULL
&& pvNano.get()!=NULL
&& pvUserTag.get()!=NULL) return true;
if(pvSecs
&& pvNano
&& pvUserTag) return true;
detach();
// look up the tree for a timeSyamp
pvStructure = pvStructure->getParent();

View File

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

View File

@@ -12,7 +12,9 @@
#include <string>
#include <stdexcept>
#include <iostream>
#include <map>
#include <pv/lock.h>
#include <pv/noDefaultMethods.h>
#include <pv/pvType.h>
#include <pv/byteBuffer.h>
@@ -21,6 +23,40 @@
#include <shareLib.h>
#if defined(PVD_INTERNAL)
# define PVD_DEPRECATED(msg)
#elif __GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 5
# define PVD_DEPRECATED(msg) __attribute__((deprecated(msg)))
#else
# define PVD_DEPRECATED(msg) EPICS_DEPRECATED
#endif
#define PVD_DEPRECATED_52 PVD_DEPRECATED("See https://github.com/epics-base/pvDataCPP/issues/52")
/* C++11 keywords
@code
struct Base {
virtual void foo();
};
struct Class : public Base {
virtual void foo() OVERRIDE FINAL;
};
@endcode
*/
#ifndef FINAL
# if __cplusplus>=201103L
# define FINAL final
# else
# define FINAL
# endif
#endif
#ifndef OVERRIDE
# if __cplusplus>=201103L
# define OVERRIDE override
# else
# define OVERRIDE
# endif
#endif
namespace epics { namespace pvData {
namespace format {
@@ -91,6 +127,13 @@ class UnionArray;
class BoundedString;
class PVField;
class PVScalar;
class PVScalarArray;
class PVStructure;
class PVUnion;
template<typename T> class PVValueArray;
/**
* typedef for a shared pointer to an immutable Field.
*/
@@ -295,9 +338,6 @@ public:
static size_t num_instances;
POINTER_DEFINITIONS(Field);
/**
* Destructor.
*/
virtual ~Field();
/**
* Get the field type.
@@ -317,6 +357,10 @@ public:
*/
virtual std::ostream& dump(std::ostream& o) const = 0;
//! Allocate a new instance
//! @version Added after 7.0.0
std::tr1::shared_ptr<PVField> build() const;
protected:
/**
* Constructor
@@ -325,6 +369,9 @@ protected:
Field(Type type);
private:
const Type m_fieldType;
unsigned int m_hash;
struct Helper;
friend struct Helper;
friend class StructureArray;
friend class Structure;
@@ -332,8 +379,7 @@ private:
friend class StandardField;
friend class BasePVStructureArray;
friend class FieldCreate;
struct Deleter{void operator()(Field *p){delete p;}};
EPICS_NOT_COPYABLE(Field)
};
epicsShareExtern std::ostream& operator<<(std::ostream& o, const Field& field);
@@ -346,9 +392,6 @@ epicsShareExtern std::ostream& operator<<(std::ostream& o, const Field& field);
class epicsShareClass Scalar : public Field{
public:
POINTER_DEFINITIONS(Scalar);
/**
* Destructor.
*/
virtual ~Scalar();
typedef Scalar& reference;
typedef const Scalar& const_reference;
@@ -358,12 +401,16 @@ public:
*/
ScalarType getScalarType() const {return scalarType;}
virtual std::string getID() const;
virtual std::string getID() const OVERRIDE;
virtual std::ostream& dump(std::ostream& o) const;
virtual std::ostream& dump(std::ostream& o) const OVERRIDE FINAL;
virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const;
virtual void deserialize(ByteBuffer *buffer, DeserializableControl *control);
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);
@@ -375,6 +422,7 @@ private:
friend class BoundedScalarArray;
friend class FixedScalarArray;
friend class BoundedString;
EPICS_NOT_COPYABLE(Scalar)
};
/**
@@ -384,16 +432,13 @@ private:
class epicsShareClass BoundedString : public Scalar{
public:
POINTER_DEFINITIONS(BoundedString);
/**
* Destructor.
*/
virtual ~BoundedString();
typedef BoundedString& reference;
typedef const BoundedString& const_reference;
virtual std::string getID() const;
virtual std::string getID() const OVERRIDE FINAL;
virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const;
virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const OVERRIDE FINAL;
std::size_t getMaximumLength() const;
@@ -402,6 +447,7 @@ protected:
private:
std::size_t maxLength;
friend class FieldCreate;
EPICS_NOT_COPYABLE(BoundedString)
};
/**
@@ -411,9 +457,6 @@ private:
class epicsShareClass Array : public Field{
public:
POINTER_DEFINITIONS(Array);
/**
* Destructor.
*/
virtual ~Array();
typedef Array& reference;
typedef const Array& const_reference;
@@ -439,6 +482,7 @@ protected:
*/
Array(Type type);
EPICS_NOT_COPYABLE(Array)
};
/**
@@ -462,26 +506,27 @@ public:
*/
ScalarType getElementType() const {return elementType;}
virtual ArraySizeType getArraySizeType() const {return Array::variable;}
virtual ArraySizeType getArraySizeType() const OVERRIDE {return Array::variable;}
virtual std::size_t getMaximumCapacity() const {return 0;}
virtual std::size_t getMaximumCapacity() const OVERRIDE {return 0;}
virtual std::string getID() const;
virtual std::string getID() const OVERRIDE;
virtual std::ostream& dump(std::ostream& o) const;
virtual std::ostream& dump(std::ostream& o) const OVERRIDE FINAL;
virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const;
virtual void deserialize(ByteBuffer *buffer, DeserializableControl *control);
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:
/**
* Destructor.
*/
virtual ~ScalarArray();
private:
const std::string getIDScalarArrayLUT() const;
ScalarType elementType;
friend class FieldCreate;
EPICS_NOT_COPYABLE(ScalarArray)
};
@@ -503,22 +548,19 @@ public:
*/
BoundedScalarArray(ScalarType scalarType, std::size_t size);
virtual ArraySizeType getArraySizeType() const {return Array::bounded;}
virtual ArraySizeType getArraySizeType() const OVERRIDE FINAL {return Array::bounded;}
virtual std::size_t getMaximumCapacity() const {return size;}
virtual std::size_t getMaximumCapacity() const OVERRIDE FINAL {return size;}
virtual std::string getID() const;
virtual std::string getID() const OVERRIDE FINAL;
virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const;
virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const OVERRIDE FINAL;
protected:
/**
* Destructor.
*/
virtual ~BoundedScalarArray();
private:
std::size_t size;
friend class FieldCreate;
EPICS_NOT_COPYABLE(BoundedScalarArray)
};
/**
@@ -538,22 +580,19 @@ public:
*/
FixedScalarArray(ScalarType scalarType, std::size_t size);
virtual ArraySizeType getArraySizeType() const {return Array::fixed;}
virtual ArraySizeType getArraySizeType() const OVERRIDE FINAL {return Array::fixed;}
virtual std::size_t getMaximumCapacity() const {return size;}
virtual std::size_t getMaximumCapacity() const OVERRIDE FINAL {return size;}
virtual std::string getID() const;
virtual std::string getID() const OVERRIDE FINAL;
virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const;
virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const OVERRIDE FINAL;
protected:
/**
* Destructor.
*/
virtual ~FixedScalarArray();
private:
std::size_t size;
friend class FieldCreate;
EPICS_NOT_COPYABLE(FixedScalarArray)
};
/**
@@ -570,18 +609,22 @@ public:
* Get the introspection interface for the array elements.
* @return The introspection interface.
*/
StructureConstPtr getStructure() const {return pstructure;}
const StructureConstPtr& getStructure() const {return pstructure;}
virtual ArraySizeType getArraySizeType() const {return Array::variable;}
virtual ArraySizeType getArraySizeType() const OVERRIDE FINAL {return Array::variable;}
virtual std::size_t getMaximumCapacity() const {return 0;}
virtual std::size_t getMaximumCapacity() const OVERRIDE FINAL {return 0;}
virtual std::string getID() const;
virtual std::string getID() const OVERRIDE FINAL;
virtual std::ostream& dump(std::ostream& o) const;
virtual std::ostream& dump(std::ostream& o) const OVERRIDE FINAL;
virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const;
virtual void deserialize(ByteBuffer *buffer, DeserializableControl *control);
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:
/**
@@ -589,13 +632,12 @@ protected:
* @param structure The introspection interface for the elements.
*/
StructureArray(StructureConstPtr const & structure);
/**
* Destructor.
*/
public:
virtual ~StructureArray();
private:
StructureConstPtr pstructure;
friend class FieldCreate;
EPICS_NOT_COPYABLE(StructureArray)
};
/**
@@ -614,16 +656,20 @@ public:
*/
UnionConstPtr getUnion() const {return punion;}
virtual ArraySizeType getArraySizeType() const {return Array::variable;}
virtual ArraySizeType getArraySizeType() const OVERRIDE FINAL {return Array::variable;}
virtual std::size_t getMaximumCapacity() const {return 0;}
virtual std::size_t getMaximumCapacity() const OVERRIDE FINAL {return 0;}
virtual std::string getID() const;
virtual std::string getID() const OVERRIDE FINAL;
virtual std::ostream& dump(std::ostream& o) const;
virtual std::ostream& dump(std::ostream& o) const OVERRIDE FINAL;
virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const;
virtual void deserialize(ByteBuffer *buffer, DeserializableControl *control);
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:
/**
@@ -631,13 +677,12 @@ protected:
* @param _punion The introspection interface for the elements.
*/
UnionArray(UnionConstPtr const & _punion);
/**
* Destructor.
*/
public:
virtual ~UnionArray();
private:
UnionConstPtr punion;
friend class FieldCreate;
EPICS_NOT_COPYABLE(UnionArray)
};
/**
@@ -659,9 +704,6 @@ public:
*/
static const std::string & defaultId();
/**
* Destructor.
*/
virtual ~Structure();
typedef Structure& reference;
typedef const Structure& const_reference;
@@ -682,8 +724,8 @@ public:
template<typename FT>
std::tr1::shared_ptr<const FT> getField(std::string const &fieldName) const
{
FieldConstPtr field = getField(fieldName);
if (field.get())
FieldConstPtr field(getField(fieldName));
if (field)
return std::tr1::dynamic_pointer_cast<const FT>(field);
else
return std::tr1::shared_ptr<const FT>();
@@ -695,13 +737,13 @@ public:
* @return The introspection interface.
* This will hold a null pointer if the field is not in the structure.
*/
FieldConstPtr getField(std::size_t index) const {return fields.at(index);}
const FieldConstPtr& getField(std::size_t index) const {return fields.at(index);}
template<typename FT>
std::tr1::shared_ptr<const FT> getField(std::size_t index) const
{
FieldConstPtr field = getField(index);
if (field.get())
const FieldConstPtr& field(getField(index));
if (field)
return std::tr1::dynamic_pointer_cast<const FT>(field);
else
return std::tr1::shared_ptr<const FT>();
@@ -728,15 +770,19 @@ public:
* @param fieldIndex The index of the desired field.
* @return The fieldName.
*/
std::string getFieldName(std::size_t fieldIndex) const {return fieldNames.at(fieldIndex);}
const std::string& getFieldName(std::size_t fieldIndex) const {return fieldNames.at(fieldIndex);}
virtual std::string getID() const;
virtual std::string getID() const OVERRIDE FINAL;
virtual std::ostream& dump(std::ostream& o) const;
virtual std::ostream& dump(std::ostream& o) const OVERRIDE FINAL;
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;
virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const;
virtual void deserialize(ByteBuffer *buffer, DeserializableControl *control);
protected:
Structure(StringArray const & fieldNames, FieldConstPtrArray const & fields, std::string const & id = defaultId());
private:
@@ -744,10 +790,11 @@ private:
FieldConstPtrArray fields;
std::string id;
virtual void dumpFields(std::ostream& o) const;
void dumpFields(std::ostream& o) const;
friend class FieldCreate;
friend class Union;
EPICS_NOT_COPYABLE(Structure)
};
/**
@@ -780,9 +827,6 @@ public:
*/
static const std::string & anyId();
/**
* Destructor.
*/
virtual ~Union();
typedef Union& reference;
typedef const Union& const_reference;
@@ -804,7 +848,7 @@ public:
std::tr1::shared_ptr<const FT> getField(std::string const &fieldName) const
{
FieldConstPtr field = getField(fieldName);
if (field.get())
if (field)
return std::tr1::dynamic_pointer_cast<const FT>(field);
else
return std::tr1::shared_ptr<const FT>();
@@ -822,7 +866,7 @@ public:
std::tr1::shared_ptr<const FT> getField(std::size_t index) const
{
FieldConstPtr field = getField(index);
if (field.get())
if (field)
return std::tr1::dynamic_pointer_cast<const FT>(field);
else
return std::tr1::shared_ptr<const FT>();
@@ -867,12 +911,16 @@ public:
*/
int32 guess(Type t, ScalarType s) const;
virtual std::string getID() const;
virtual std::string getID() const OVERRIDE FINAL;
virtual std::ostream& dump(std::ostream& o) const;
virtual std::ostream& dump(std::ostream& o) const OVERRIDE FINAL;
virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const;
virtual void deserialize(ByteBuffer *buffer, DeserializableControl *control);
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();
@@ -882,10 +930,11 @@ private:
FieldConstPtrArray fields;
std::string id;
virtual void dumpFields(std::ostream& o) const;
void dumpFields(std::ostream& o) const;
friend class FieldCreate;
friend class Structure;
EPICS_NOT_COPYABLE(Union)
};
class FieldCreate;
@@ -905,6 +954,12 @@ class epicsShareClass FieldBuilder :
public std::tr1::enable_shared_from_this<FieldBuilder>
{
public:
//! Create a new instance of in-line @c Field builder.
//! @version Added after 7.0.0
static FieldBuilderPtr begin();
//! Create a new instance of in-line @c Field builder pre-initialized with and existing Structure
static FieldBuilderPtr begin(StructureConstPtr S);
/**
* Set ID of an object to be created.
* @param id id to be set.
@@ -926,7 +981,7 @@ public:
* @param maxLength a string maximum length.
* @return this instance of a @c FieldBuilder.
*/
FieldBuilderPtr addBoundedString(std::string const & name, std::size_t maxLength);
FieldBuilderPtr addBoundedString(std::string const & name, std::size_t maxLength) PVD_DEPRECATED_52;
/**
* Add a @c Field (e.g. @c Structure, @c Union).
@@ -951,7 +1006,7 @@ public:
* @param size Array fixed size.
* @return this instance of a @c FieldBuilder.
*/
FieldBuilderPtr addFixedArray(std::string const & name, ScalarType scalarType, std::size_t size);
FieldBuilderPtr addFixedArray(std::string const & name, ScalarType scalarType, std::size_t size) PVD_DEPRECATED_52;
/**
* Add bounded-size array of @c Scalar elements.
@@ -960,7 +1015,7 @@ public:
* @param bound Array maximum capacity (size).
* @return this instance of a @c FieldBuilder.
*/
FieldBuilderPtr addBoundedArray(std::string const & name, ScalarType scalarType, std::size_t bound);
FieldBuilderPtr addBoundedArray(std::string const & name, ScalarType scalarType, std::size_t bound) PVD_DEPRECATED_52;
/**
* Add array of @c Field elements.
@@ -1065,11 +1120,16 @@ private:
const bool createNested; // true - endNested() creates in parent, false - endNested() appends to parent
};
namespace detail {
struct field_factory;
}
/**
* @brief This is a singleton class for creating introspection interfaces.
*
*/
class epicsShareClass FieldCreate {
friend struct detail::field_factory;
public:
static const FieldCreatePtr &getFieldCreate();
/**
@@ -1095,7 +1155,7 @@ public:
* @return a @c BoundedString interface for the newly created object.
* @throws IllegalArgumentException if maxLength == 0.
*/
BoundedStringConstPtr createBoundedString(std::size_t maxLength) const;
BoundedStringConstPtr createBoundedString(std::size_t maxLength) const PVD_DEPRECATED_52;
/**
* Create an @c Array field, variable size array.
* @param elementType The @c ScalarType for array elements
@@ -1108,14 +1168,14 @@ public:
* @param size Fixed array size.
* @return An @c Array Interface for the newly created object.
*/
ScalarArrayConstPtr createFixedScalarArray(ScalarType elementType, std::size_t size) const;
ScalarArrayConstPtr createFixedScalarArray(ScalarType elementType, std::size_t size) const PVD_DEPRECATED_52;
/**
* Create an @c Array field, bounded size array.
* @param elementType The @c ScalarType for array elements
* @param bound Array maximum capacity.
* @return An @c Array Interface for the newly created object.
*/
ScalarArrayConstPtr createBoundedScalarArray(ScalarType elementType, std::size_t bound) const;
ScalarArrayConstPtr createBoundedScalarArray(ScalarType elementType, std::size_t bound) const PVD_DEPRECATED_52;
/**
* Create an @c Array field that is has element type @c Structure
* @param structure The @c Structure for each array element.
@@ -1214,11 +1274,20 @@ public:
private:
FieldCreate();
// const after ctor
std::vector<ScalarConstPtr> scalars;
std::vector<ScalarArrayConstPtr> scalarArrays;
UnionConstPtr variantUnion;
UnionArrayConstPtr variantUnionArray;
mutable Mutex mutex;
typedef std::multimap<unsigned int, Field*> cache_t;
mutable cache_t cache;
struct Helper;
friend class Field;
EPICS_NOT_COPYABLE(FieldCreate)
};
/**
@@ -1270,67 +1339,35 @@ OP(pvDouble, double)
OP(pvString, std::string)
#undef OP
/**
* @brief Hash a Scalar
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&);
/** 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.
*/
struct ScalarHashFunction {
size_t operator() (const Scalar& scalar) const { return scalar.getScalarType(); }
};
#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);}
/**
* @brief Hash a ScalarArray
*
*/
struct ScalarArrayHashFunction {
size_t operator() (const ScalarArray& scalarArray) const { return 0x10 | scalarArray.getElementType(); }
};
/**
* @brief Hash a Structure
*
*/
struct StructureHashFunction {
size_t operator() (const Structure& /*structure*/) const { return 0; }
// TODO hash
// final int PRIME = 31;
// return PRIME * Arrays.hashCode(fieldNames) + Arrays.hashCode(fields);
};
/**
* @brief Hash a StructureArray
*
*/
struct StructureArrayHashFunction {
size_t operator() (const StructureArray& structureArray) const { StructureHashFunction shf; return (0x10 | shf(*(structureArray.getStructure()))); }
};
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&);
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);}
MAKE_COMPARE(Field)
MAKE_COMPARE(Scalar)
MAKE_COMPARE(ScalarArray)
MAKE_COMPARE(Structure)
MAKE_COMPARE(StructureArray)
MAKE_COMPARE(Union)
MAKE_COMPARE(UnionArray)
MAKE_COMPARE(BoundedString)
#undef MAKE_COMPARE
}}
/**

View File

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

View File

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

View File

@@ -12,18 +12,22 @@ include $(PVDATA_TEST)/pv/Makefile
include $(PVDATA_TEST)/property/Makefile
include $(PVDATA_TEST)/copy/Makefile
# The testHarness runs all the test programs in a known working order.
# pvDataAllTests runs all the test programs in a known working order.
testHarness_SRCS += pvDataAllTests.c
PROD_vxWorks = vxTestHarness
vxTestHarness_SRCS += $(testHarness_SRCS)
TESTSPEC_vxWorks = vxTestHarness.$(MUNCH_SUFFIX); pvDataAllTests
# Name the application pvdTestHarness
pvdTestHarness_SRCS = $(testHarness_SRCS)
PROD_RTEMS += rtemsTestHarness
rtemsTestHarness_SRCS += rtemsTestHarness.c rtemsConfig.c
rtemsTestHarness_SRCS += $(testHarness_SRCS)
TESTSPEC_RTEMS = rtemsTestHarness.$(MUNCH_SUFFIX); pvDataAllTests
# Build for vxWorks
PROD_vxWorks += pvdTestHarness
TESTSPEC_vxWorks = pvdTestHarness.$(MUNCH_SUFFIX); pvDataAllTests
# Build for RTEMS, with harness code & OS configuration
PROD_RTEMS += pvdTestHarness
pvdTestHarness_SRCS_RTEMS = rtemsTestHarness.c
TESTSPEC_RTEMS = pvdTestHarness.$(MUNCH_SUFFIX); pvDataAllTests
# Build test scripts for hosts
TESTSCRIPTS_HOST += $(TESTS:%=%.t)
include $(TOP)/configure/RULES

View File

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

View File

@@ -20,6 +20,10 @@
#include <epicsUnitTest.h>
#include <testMain.h>
#include <compilerDependencies.h>
#undef EPICS_DEPRECATED
#define EPICS_DEPRECATED
#include <pv/standardField.h>
#include <pv/standardPVField.h>
#include <pv/convert.h>
@@ -41,7 +45,6 @@ static void testPVScalar(
PVScalarPtr pvValueMaster;
PVScalarPtr pvValueCopy;
BitSetPtr bitSet;
size_t offset;
ConvertPtr convert = getConvert();
pvValueMaster = pvMaster->getSubField<PVScalar>(valueNameMaster);
@@ -58,7 +61,7 @@ static void testPVScalar(
testOk1(convert->toDouble(pvValueCopy)==.06);
testOk1(bitSet->get(pvValueCopy->getFieldOffset()));
offset = pvCopy->getCopyOffset(pvValueMaster);
pvCopy->getCopyOffset(pvValueMaster);
bitSet->clear();
convert->fromDouble(pvValueMaster,1.0);
@@ -87,7 +90,6 @@ static void testPVScalarArray(
PVScalarArrayPtr pvValueMaster;
PVScalarArrayPtr pvValueCopy;
BitSetPtr bitSet;
size_t offset;
size_t n = 5;
shared_vector<double> values(n);
shared_vector<const double> cvalues;
@@ -113,7 +115,7 @@ static void testPVScalarArray(
pvValueCopy->getAs(cvalues);
testOk1(cvalues[0]==0.06);
offset = pvCopy->getCopyOffset(pvValueMaster);
pvCopy->getCopyOffset(pvValueMaster);
bitSet->clear();
values.resize(n);

View File

@@ -59,11 +59,6 @@ testTimeStamp_SRCS += testTimeStamp.cpp
testHarness_SRCS += testTimeStamp.cpp
TESTS += testTimeStamp
TESTPROD_HOST += testQueue
testQueue_SRCS += testQueue.cpp
testHarness_SRCS += testQueue.cpp
TESTS += testQueue
TESTPROD_HOST += testTypeCast
testTypeCast_SRCS += testTypeCast.cpp
testHarness_SRCS += testTypeCast.cpp
@@ -84,3 +79,7 @@ TESTS += test_reftrack
TESTPROD_HOST += testanyscalar
testanyscalar_SRCS += testanyscalar.cpp
TESTS += testanyscalar
TESTPROD_HOST += testprinter
testprinter_SRCS += testprinter.cpp
TESTS += testprinter

View File

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

View File

@@ -1,164 +0,0 @@
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
/*
* testQueue.cpp
*
* Created on: 2010.12
* Author: Marty Kraimer
*/
#include <cstddef>
#include <cstdlib>
#include <cstddef>
#include <string>
#include <cstdio>
#include <epicsUnitTest.h>
#include <testMain.h>
#include <epicsThread.h>
#include <pv/lock.h>
#include <pv/timeStamp.h>
#include <pv/queue.h>
#include <pv/event.h>
using namespace epics::pvData;
struct Data {
int a;
int b;
};
typedef std::tr1::shared_ptr<Data> DataPtr;
typedef std::vector<DataPtr> DataPtrArray;
static const int numElements = 5;
typedef Queue<Data> DataQueue;
class Sink;
typedef std::tr1::shared_ptr<Sink> SinkPtr;
class Sink : public epicsThreadRunable {
public:
static SinkPtr create(DataQueue &queue);
Sink(DataQueue &queue);
~Sink();
void stop();
void look();
virtual void run();
private:
DataQueue &queue;
bool isStopped;
Event *wait;
Event *stopped;
Event *waitReturn;
Event *waitEmpty;
epicsThread *thread;
};
SinkPtr Sink::create(DataQueue &queue)
{
return SinkPtr(new Sink(queue));
}
Sink::Sink(DataQueue &queue)
: queue(queue),
isStopped(false),
wait(new Event()),
stopped(new Event()),
waitReturn(new Event()),
waitEmpty(new Event()),
thread(new epicsThread(*this,"sink",epicsThreadGetStackSize(epicsThreadStackSmall)))
{
thread->start();
}
Sink::~Sink() {
delete thread;
delete waitEmpty;
delete waitReturn;
delete stopped;
delete wait;
}
void Sink::stop()
{
isStopped = true;
wait->signal();
stopped->wait();
}
void Sink::look()
{
wait->signal();
waitEmpty->wait();
}
void Sink::run()
{
while(!isStopped) {
wait->wait();
if(isStopped) break;
while(true) {
DataPtr data = queue.getUsed();
if(data.get()==NULL) {
waitEmpty->signal();
break;
}
printf(" sink a %d b %d\n",data->a,data->b);
queue.releaseUsed(data);
}
}
stopped->signal();
}
static void testBasic() {
DataPtrArray dataArray;
dataArray.reserve(numElements);
for(int i=0; i<numElements; i++) {
dataArray.push_back(DataPtr(new Data()));
}
DataQueue queue(dataArray);
DataPtr data = queue.getFree();
int value = 0;
while(data.get()!=NULL) {
data->a = value;
data->b = value*10;
value++;
queue.setUsed(data);
data = queue.getFree();
}
SinkPtr sink = SinkPtr(new Sink(queue));
queue.clear();
while(true) {
data = queue.getFree();
if(data.get()==NULL) break;
printf("source a %d b %d\n",data->a,data->b);
queue.setUsed(data);
}
sink->look();
// now alternate
for(int i=0; i<numElements; i++) {
data = queue.getFree();
testOk1(data.get()!=NULL);
printf("source a %d b %d\n",data->a,data->b);
queue.setUsed(data);
sink->look();
}
sink->stop();
printf("PASSED\n");
}
MAIN(testQueue)
{
testPlan(5);
testDiag("Tests queue");
testBasic();
return testDone();
}

View File

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

View File

@@ -12,7 +12,7 @@
#include <vector>
#include <epicsUnitTest.h>
#include <pv/pvUnitTest.h>
#include <testMain.h>
#include "pv/sharedVector.h"
@@ -20,16 +20,21 @@
using std::string;
using namespace epics::pvData;
static void testEmpty()
namespace {
void testEmpty()
{
testDiag("Test empty vector");
epics::pvData::shared_vector<int32> empty, empty2;
epics::pvData::shared_vector<int32> empty, empty2, empty3(0u);
testOk1(empty.size()==0);
testOk1(empty.empty());
testOk1(empty.begin()==empty.end());
testOk1(empty.unique());
testOk1(empty3.empty());
testOk1(empty3.unique());
testOk1(empty.data()==NULL);
testOk1(empty==empty);
@@ -38,7 +43,7 @@ static void testEmpty()
testOk1(!(empty!=empty2));
}
static void testInternalAlloc()
void testInternalAlloc()
{
testDiag("Test vector alloc w/ new[]");
@@ -75,19 +80,17 @@ static void testInternalAlloc()
testOk1(internal.data()==NULL);
}
namespace {
//Note: STL shared_ptr requires that deletors be copy constructable
template<typename E>
struct callCounter {
std::tr1::shared_ptr<int32> count;
callCounter():count(new int32){*count=0;}
callCounter(const callCounter& o):count(o.count) {};
callCounter& operator=(const callCounter& o){count=o.count;}
void operator()(E){*count=1;}
};
}
//Note: STL shared_ptr requires that deletors be copy constructable
template<typename E>
struct callCounter {
std::tr1::shared_ptr<int32> count;
callCounter():count(new int32){*count=0;}
callCounter(const callCounter& o):count(o.count) {}
callCounter& operator=(const callCounter& o){count=o.count;}
void operator()(E){(*count)++;}
};
static void testExternalAlloc()
void testExternalAlloc()
{
testDiag("Test vector external alloc");
@@ -133,7 +136,7 @@ static void testExternalAlloc()
testOk1(*tracker.count==1);
}
static void testShare()
void testShare()
{
testDiag("Test vector Sharing");
@@ -195,7 +198,7 @@ static void testShare()
testOk1(two[19]==5000);
}
static void testConst()
void testConst()
{
testDiag("Test constant vector");
@@ -236,7 +239,7 @@ static void testConst()
testOk1(rodata.data()!=rodata2.data());
}
static void testSlice()
void testSlice()
{
testDiag("Test vector slicing");
@@ -286,7 +289,7 @@ static void testSlice()
testOk1(half2.data()==NULL);
}
static void testCapacity()
void testCapacity()
{
testDiag("Test vector capacity");
@@ -328,7 +331,7 @@ static void testCapacity()
testOk1(vect[1]==124);
}
static void testPush()
void testPush()
{
epics::pvData::shared_vector<int32> vect;
@@ -353,7 +356,7 @@ static void testPush()
testOk1(nallocs==26);
}
static void testVoid()
void testVoid()
{
testDiag("Test vector cast to/from void");
@@ -373,7 +376,7 @@ static void testVoid()
VV.clear();
}
static void testConstVoid()
void testConstVoid()
{
testDiag("Test vector cast to/from const void");
@@ -401,7 +404,7 @@ static void testConstVoid()
struct dummyStruct {};
static void testNonPOD()
void testNonPOD()
{
testDiag("Test vector of non-POD types");
@@ -429,7 +432,7 @@ static void testNonPOD()
testOk1(structs2[1].get()==temp);
}
static void testVectorConvert()
void testVectorConvert()
{
testDiag("Test shared_vector_convert");
@@ -474,7 +477,7 @@ static void testVectorConvert()
testOk1(strings.at(0)=="42");
}
static void testWeak()
void testWeak()
{
testDiag("Test weak_ptr counting");
@@ -499,7 +502,7 @@ static void testWeak()
testOk1(!data.unique());
}
static void testICE()
void testICE()
{
testDiag("Test freeze and thaw");
@@ -563,7 +566,6 @@ static void testICE()
}
}
static
void testBad()
{
epics::pvData::shared_vector<int> I;
@@ -620,7 +622,6 @@ void testBad()
//I = epics::pvData::that(CF);
}
static
void testAutoSwap()
{
epics::auto_ptr<int> A(new int(42)), B(new int(43));
@@ -631,9 +632,71 @@ void testAutoSwap()
testOk1(42==*B);
}
void testCXX11Move()
{
#if __cplusplus>=201103L
testDiag("Check std::move()");
shared_vector<int32> A(4, 42),
B(std::move(A));
testOk1(A.unique());
testOk1(B.unique());
testOk1(A.empty());
testOk1(B.size()==4);
testOk1(!B.empty() && B[0]==42);
A = std::move(B);
testOk1(A.unique());
testOk1(B.unique());
testOk1(B.empty());
testOk1(A.size()==4);
testOk1(!A.empty() && A[0]==42);
shared_vector<void> C(shared_vector_convert<void>(A)),
D(std::move(C));
A.clear();
testOk1(C.unique());
testOk1(D.unique());
testOk1(C.empty());
testOk1(D.size()==4*4);
C = std::move(D);
testOk1(C.unique());
testOk1(D.unique());
testOk1(C.size()==4*4);
testOk1(D.empty());
#else
testSkip(18, "Not -std=c++11");
#endif
}
void testCXX11Init()
{
#if __cplusplus>=201103L
testDiag("Check c++11 style array initialization");
shared_vector<const int32> A = {1.0, 2.0, 3.0};
testOk1(A.size()==3);
int32 sum = 0;
for (auto V: A) {
sum += V;
}
testOk1(sum==6);
#else
testSkip(2, "Not -std=c++11");
#endif
}
} // namespace
MAIN(testSharedVector)
{
testPlan(169);
testPlan(191);
testDiag("Tests for shared_vector");
testDiag("sizeof(shared_vector<int32>)=%lu",
@@ -655,5 +718,7 @@ MAIN(testSharedVector)
testICE();
testBad();
testAutoSwap();
testCXX11Move();
testCXX11Init();
return testDone();
}

View File

@@ -23,8 +23,6 @@
#include <pv/event.h>
#include <pv/thread.h>
#include <pv/executor.h>
#include <pv/timeFunction.h>
using namespace epics::pvData;
using std::string;
@@ -65,49 +63,6 @@ static void testThreadRun() {
testDiag("testThreadRun PASSED");
}
class Basic;
typedef std::tr1::shared_ptr<Basic> BasicPtr;
class Basic :
public Command,
public std::tr1::enable_shared_from_this<Basic>
{
public:
POINTER_DEFINITIONS(Basic);
Basic(ExecutorPtr const &executor)
: executor(executor) {}
~Basic()
{
}
void run()
{
executor->execute(getPtrSelf());
bool result = wait.wait();
testOk1(result==true);
if(result==false) testDiag("basic::run wait returned false");
}
virtual void command()
{
wait.signal();
}
private:
Basic::shared_pointer getPtrSelf()
{
return shared_from_this();
}
ExecutorPtr executor;
Event wait;
};
typedef std::tr1::shared_ptr<Basic> BasicPtr;
static void testBasic() {
ExecutorPtr executor(new Executor(string("basic"),middlePriority));
BasicPtr basic( new Basic(executor));
basic->run();
testDiag("testBasic PASSED");
}
namespace {
struct fninfo {
int cnt;
@@ -197,49 +152,11 @@ static void testBinders()
#endif
}
class MyFunc : public TimeFunctionRequester {
public:
POINTER_DEFINITIONS(MyFunc);
MyFunc(BasicPtr const &basic);
virtual void function();
private:
BasicPtr basic;
};
MyFunc::MyFunc(BasicPtr const &basic)
: basic(basic)
{}
void MyFunc::function()
{
basic->run();
}
typedef std::tr1::shared_ptr<MyFunc> MyFuncPtr;
#ifdef TESTTHREADCONTEXT
static void testThreadContext() {
ExecutorPtr executor(new Executor(string("basic"),middlePriority));
BasicPtr basic(new Basic(executor));
MyFuncPtr myFunc(new MyFunc(basic));
TimeFunctionPtr timeFunction(new TimeFunction(myFunc));
double perCall = timeFunction->timeCall();
perCall *= 1e6;
testDiag("time per call %f microseconds",perCall);
testDiag("testThreadContext PASSED");
}
#endif
MAIN(testThread)
{
testPlan(7);
testPlan(6);
testDiag("Tests thread");
testThreadRun();
testBasic();
testBinders();
#ifdef TESTTHREADCONTEXT
testThreadContext();
#endif
return testDone();
}

View File

@@ -30,7 +30,7 @@ void testTimeStampInternal()
testOk1(nanoSecPerSec==1000000000);
TimeStamp current;
current.getCurrent();
testDiag("current %lli %i milliSec %lli\n",
testDiag("current %lld %d milliSec %lld\n",
(long long)current.getSecondsPastEpoch(),
(int)current.getNanoseconds(),
(long long)current.getMilliseconds());
@@ -46,7 +46,7 @@ void testTimeStampInternal()
(ctm.tm_isdst==0) ? "false" : "true");
tt = time(&tt);
current.fromTime_t(tt);
testDiag("fromTime_t\ncurrent %lli %i milliSec %lli\n",
testDiag("fromTime_t\ncurrent %lld %d milliSec %lld\n",
(long long)current.getSecondsPastEpoch(),
(int)current.getNanoseconds(),
(long long)current.getMilliseconds());

View File

@@ -15,9 +15,11 @@
#include <string>
#include <cstdio>
#include <iostream>
#include <exception>
#include <epicsUnitTest.h>
#include <testMain.h>
#include <epicsGuard.h>
#include <pv/timeStamp.h>
#include <pv/event.h>
@@ -27,175 +29,214 @@
using namespace epics::pvData;
using std::string;
static TimeStamp currentTimeStamp;
static double oneDelay = 4.0;
static double twoDelay = 2.0;
static double threeDelay = 1.0;
static int ntimes = 3;
static const double delays[3] = {1.0, 2.0, 4.0};
static const unsigned ntimes = 3;
class MyCallback;
typedef std::tr1::shared_ptr<MyCallback> MyCallbackPtr;
namespace {
class MyCallback : public TimerCallback {
public:
POINTER_DEFINITIONS(MyCallback);
MyCallback(string name,EventPtr const & wait)
: name(name),
wait(wait)
{
}
~MyCallback()
{
}
struct Marker : public TimerCallback
{
POINTER_DEFINITIONS(Marker);
Event wait, hold;
virtual ~Marker() {}
virtual void callback()
{
timeStamp.getCurrent();
wait->signal();
wait.signal();
hold.wait();
}
virtual void timerStopped() {}
};
struct MyCallback : public TimerCallback {
POINTER_DEFINITIONS(MyCallback);
MyCallback(const string& name)
:name(name)
,counter(0)
,first_gbl(0)
{}
virtual ~MyCallback() {}
virtual void callback()
{
testDiag("expire %s",name.c_str());
{
epicsGuard<Mutex> G(gbl_mutex);
counter++;
if(first_gbl==0) {
first_gbl = ++gbl_counter;
}
}
wait.signal();
}
virtual void timerStopped()
{
printf("timerStopped %s\n",name.c_str());
testDiag("timerStopped %s",name.c_str());
}
TimeStamp &getTimeStamp() { return timeStamp;}
private:
string name;
EventPtr wait;
TimeStamp timeStamp;
void clear() {
epicsGuard<Mutex> G(gbl_mutex);
counter = gbl_counter = 0;
}
const string name;
unsigned counter, first_gbl;
Event wait;
static unsigned gbl_counter;
static Mutex gbl_mutex;
};
static void testBasic()
unsigned MyCallback::gbl_counter;
Mutex MyCallback::gbl_mutex;
typedef std::tr1::shared_ptr<MyCallback> MyCallbackPtr;
}// namespace
static void testBasic(unsigned oneOrd, unsigned twoOrd, unsigned threeOrd)
{
testDiag("\n\ntestBasic oneDelay %lf twoDelay %lf threeDaley %lf\n",
oneDelay,twoDelay,threeDelay);
testDiag("testBasic oneOrd %u twoOrd %u threeOrd %u",
oneOrd,twoOrd,threeOrd);
string one("one");
string two("two");
string three("three");
EventPtr eventOne(new Event());
EventPtr eventTwo(new Event());
EventPtr eventThree(new Event());
TimerPtr timer(new Timer(string("timer"),middlePriority));
MyCallbackPtr callbackOne(new MyCallback(one,eventOne));
MyCallbackPtr callbackTwo(new MyCallback(two,eventTwo));
MyCallbackPtr callbackThree(new MyCallback(three,eventThree));
for(int n=0; n<ntimes; n++) {
currentTimeStamp.getCurrent();
testOk1(!timer->isScheduled(callbackOne));
testOk1(!timer->isScheduled(callbackTwo));
testOk1(!timer->isScheduled(callbackThree));
timer->scheduleAfterDelay(callbackOne,oneDelay);
timer->scheduleAfterDelay(callbackTwo,twoDelay);
timer->scheduleAfterDelay(callbackThree,threeDelay);
if(oneDelay>.1) testOk1(timer->isScheduled(callbackOne));
if(twoDelay>.1) testOk1(timer->isScheduled(callbackTwo));
if(threeDelay>.1) testOk1(timer->isScheduled(callbackThree));
Timer timer("timer" ,middlePriority);
eventOne->wait();
eventTwo->wait();
eventThree->wait();
double diff;
double delta;
diff = TimeStamp::diff(
callbackOne->getTimeStamp(),currentTimeStamp);
delta = diff - oneDelay;
Marker::shared_pointer marker(new Marker);
MyCallbackPtr callbackOne(new MyCallback("one"));
MyCallbackPtr callbackTwo(new MyCallback("two"));
MyCallbackPtr callbackThree(new MyCallback("three"));
if(delta<0.0) delta = -delta;
testOk1(delta<.1);
diff = TimeStamp::diff(
callbackTwo->getTimeStamp(),currentTimeStamp);
delta = diff - twoDelay;
for(unsigned n=0; n<ntimes; n++) {
testDiag("iteration %u", n);
if(delta<0.0) delta = -delta;
testOk1(delta<.1);
diff = TimeStamp::diff(
callbackThree->getTimeStamp(),currentTimeStamp);
delta = diff - threeDelay;
testOk1(!timer.isScheduled(marker));
timer.scheduleAfterDelay(marker, 0.01);
marker->wait.wait();
// timer worker is blocked
testOk1(!timer.isScheduled(callbackOne));
testOk1(!timer.isScheduled(callbackTwo));
testOk1(!timer.isScheduled(callbackThree));
callbackOne->clear();
callbackTwo->clear();
callbackThree->clear();
timer.scheduleAfterDelay(callbackOne,delays[oneOrd]);
timer.scheduleAfterDelay(callbackTwo,delays[twoOrd]);
timer.scheduleAfterDelay(callbackThree,delays[threeOrd]);
testOk1(timer.isScheduled(callbackOne));
testOk1(timer.isScheduled(callbackTwo));
testOk1(timer.isScheduled(callbackThree));
marker->hold.signal(); // let the worker loose
callbackOne->wait.wait();
callbackTwo->wait.wait();
callbackThree->wait.wait();
testOk1(!timer.isScheduled(callbackOne));
testOk1(!timer.isScheduled(callbackTwo));
testOk1(!timer.isScheduled(callbackThree));
{
epicsGuard<Mutex> G(MyCallback::gbl_mutex);
testOk(callbackOne->counter==1, "%s counter %u = 1", callbackOne->name.c_str(), callbackOne->counter);
testOk(callbackTwo->counter==1, "%s counter %u = 1", callbackTwo->name.c_str(), callbackTwo->counter);
testOk(callbackThree->counter==1, "%s counter %u = 1", callbackThree->name.c_str(), callbackThree->counter);
testOk(callbackOne->first_gbl==oneOrd+1, "%s first_gbl %u = %u", callbackOne->name.c_str(), callbackOne->first_gbl, oneOrd+1);
testOk(callbackTwo->first_gbl==twoOrd+1, "%s first_gbl %u = %u", callbackTwo->name.c_str(), callbackTwo->first_gbl, twoOrd+1);
testOk(callbackThree->first_gbl==threeOrd+1, "%s first_gbl %u = %u", callbackThree->name.c_str(), callbackThree->first_gbl, threeOrd+1);
}
if(delta<0.0) delta = -delta;
testOk1(delta<.1);
}
printf("testBasic PASSED\n");
}
static void testCancel()
static void testCancel(unsigned oneOrd, unsigned twoOrd, unsigned threeOrd,
unsigned oneReal, unsigned threeReal)
{
testDiag("\n\ntestCancel oneDelay %lf twoDelay %lf threeDaley %lf\n",
oneDelay,twoDelay,threeDelay);
testDiag("testCancel oneOrd %u twoOrd %u threeOrd %u",
oneOrd,twoOrd,threeOrd);
string one("one");
string two("two");
string three("three");
EventPtr eventOne(new Event());
EventPtr eventTwo(new Event());
EventPtr eventThree(new Event());
TimerPtr timer(new Timer(string("timer"),middlePriority));
MyCallbackPtr callbackOne(new MyCallback(one,eventOne));
MyCallbackPtr callbackTwo(new MyCallback(two,eventTwo));
MyCallbackPtr callbackThree(new MyCallback(three,eventThree));
for(int n=0; n<ntimes; n++) {
currentTimeStamp.getCurrent();
testOk1(!timer->isScheduled(callbackOne));
testOk1(!timer->isScheduled(callbackTwo));
testOk1(!timer->isScheduled(callbackThree));
timer->scheduleAfterDelay(callbackOne,oneDelay);
timer->scheduleAfterDelay(callbackTwo,twoDelay);
timer->scheduleAfterDelay(callbackThree,threeDelay);
timer->cancel(callbackTwo);
if(oneDelay>.1) testOk1(timer->isScheduled(callbackOne));
testOk1(!timer->isScheduled(callbackTwo));
if(threeDelay>.1) testOk1(timer->isScheduled(callbackThree));
Timer timer("timer" ,middlePriority);
eventOne->wait();
eventThree->wait();
double diff;
double delta;
diff = TimeStamp::diff(
callbackOne->getTimeStamp(),currentTimeStamp);
delta = diff - oneDelay;
Marker::shared_pointer marker(new Marker);
MyCallbackPtr callbackOne(new MyCallback("one"));
MyCallbackPtr callbackTwo(new MyCallback("two"));
MyCallbackPtr callbackThree(new MyCallback("three"));
if(delta<0.0) delta = -delta;
testOk1(delta<.1);
diff = TimeStamp::diff(
callbackThree->getTimeStamp(),currentTimeStamp);
delta = diff - threeDelay;
for(unsigned n=0; n<ntimes; n++) {
testDiag("iteration %u", n);
testOk1(!timer.isScheduled(marker));
timer.scheduleAfterDelay(marker, 0.01);
marker->wait.wait();
// timer worker is blocked
testOk1(!timer.isScheduled(callbackOne));
testOk1(!timer.isScheduled(callbackTwo));
testOk1(!timer.isScheduled(callbackThree));
callbackOne->clear();
callbackTwo->clear();
callbackThree->clear();
timer.scheduleAfterDelay(callbackOne,delays[oneOrd]);
timer.scheduleAfterDelay(callbackTwo,delays[twoOrd]);
timer.scheduleAfterDelay(callbackThree,delays[threeOrd]);
testOk1(timer.isScheduled(callbackOne));
testOk1(timer.isScheduled(callbackTwo));
testOk1(timer.isScheduled(callbackThree));
timer.cancel(callbackTwo);
testOk1(timer.isScheduled(callbackOne));
testOk1(!timer.isScheduled(callbackTwo));
testOk1(timer.isScheduled(callbackThree));
marker->hold.signal(); // let the worker loose
callbackOne->wait.wait();
callbackThree->wait.wait();
testOk1(!timer.isScheduled(callbackOne));
testOk1(!timer.isScheduled(callbackTwo));
testOk1(!timer.isScheduled(callbackThree));
{
epicsGuard<Mutex> G(MyCallback::gbl_mutex);
testOk(callbackOne->counter==1, "%s counter %u = 1", callbackOne->name.c_str(), callbackOne->counter);
testOk(callbackTwo->counter==0, "%s counter %u = 0", callbackTwo->name.c_str(), callbackTwo->counter);
testOk(callbackThree->counter==1, "%s counter %u = 1", callbackThree->name.c_str(), callbackThree->counter);
testOk(callbackOne->first_gbl==oneReal+1, "%s first_gbl %u = %u", callbackOne->name.c_str(), callbackOne->first_gbl, oneOrd+1);
testOk(callbackTwo->first_gbl==0, "%s first_gbl %u = %u", callbackTwo->name.c_str(), callbackTwo->first_gbl, 0);
testOk(callbackThree->first_gbl==threeReal+1, "%s first_gbl %u = %u", callbackThree->name.c_str(), callbackThree->first_gbl, threeOrd+1);
}
if(delta<0.0) delta = -delta;
testOk1(delta<.1);
}
printf("testCancel PASSED\n");
}
MAIN(testTimer)
{
testPlan(171);
testDiag("Tests timer");
oneDelay = .4;
twoDelay = .2;
threeDelay = .1;
printf("oneDelay %f twoDelay %f threeDelay %f\n",
oneDelay,twoDelay,threeDelay);
testBasic();
testCancel();
oneDelay = .1;
twoDelay = .2;
threeDelay = .4;
printf("oneDelay %f twoDelay %f threeDelay %f\n",
oneDelay,twoDelay,threeDelay);
testBasic();
testCancel();
oneDelay = .1;
twoDelay = .4;
threeDelay = .2;
printf("oneDelay %f twoDelay %f threeDelay %f\n",
oneDelay,twoDelay,threeDelay);
testBasic();
testCancel();
oneDelay = .0;
twoDelay = .0;
threeDelay = .0;
printf("oneDelay %f twoDelay %f threeDelay %f\n",
oneDelay,twoDelay,threeDelay);
testBasic();
testCancel();
testPlan(315);
try {
testDiag("Tests timer");
testBasic(2, 1, 0);
testCancel(2, 1, 0, 1, 0);
testBasic(0, 1, 2);
testCancel(0, 1, 2, 0, 1);
testBasic(0, 2, 1);
testCancel(0, 2, 1, 0, 1);
}catch(std::exception& e) {
testFail("Unhandled exception: %s", e.what());
}
return testDone();
}

View File

@@ -4,10 +4,6 @@
*/
/* Author: Michael Davidsaver */
#if defined(_WIN32) && !defined(NOMINMAX)
#define NOMINMAX
#endif
#include <fstream>
#include <iostream>
#include <algorithm>
@@ -123,7 +119,7 @@ namespace {
MAIN(testTypeCast)
{
testPlan(123);
testPlan(124);
try {
@@ -138,10 +134,12 @@ try {
float xfloat=0.0;
double xdouble=0.0;
string xstring("0");
const char* xcstring = "0";
typedef float float_t;
typedef double double_t;
typedef string string_t;
typedef const char* cstring_t;
// force all possibilities to be compiled
#define CHECK(M, N) x## M = ::epics::pvData::castUnsafe<M ##_t>(x## N); \
@@ -158,6 +156,7 @@ try {
CHECK(int8, float);
CHECK(int8, double);
CHECK(int8, string);
CHECK(int8, cstring);
CHECK(uint8, int8);
CHECK(uint8, uint8);
@@ -170,6 +169,7 @@ try {
CHECK(uint8, float);
CHECK(uint8, double);
CHECK(uint8, string);
CHECK(uint8, cstring);
CHECK(int16, int8);
CHECK(int16, uint8);
@@ -182,6 +182,7 @@ try {
CHECK(int16, float);
CHECK(int16, double);
CHECK(int16, string);
CHECK(int16, cstring);
CHECK(uint16, int8);
CHECK(uint16, uint8);
@@ -194,6 +195,7 @@ try {
CHECK(uint16, float);
CHECK(uint16, double);
CHECK(uint16, string);
CHECK(uint16, cstring);
CHECK(int32, int8);
CHECK(int32, uint8);
@@ -206,6 +208,7 @@ try {
CHECK(int32, float);
CHECK(int32, double);
CHECK(int32, string);
CHECK(int32, cstring);
CHECK(uint32, int8);
CHECK(uint32, uint8);
@@ -218,6 +221,7 @@ try {
CHECK(uint32, float);
CHECK(uint32, double);
CHECK(uint32, string);
CHECK(uint32, cstring);
CHECK(int64, int8);
CHECK(int64, uint8);
@@ -229,7 +233,8 @@ try {
CHECK(int64, uint64);
CHECK(int64, float);
CHECK(int64, double);
//CHECK(int64, string);
CHECK(int64, string);
CHECK(int64, cstring);
CHECK(uint64, int8);
CHECK(uint64, uint8);
@@ -241,7 +246,8 @@ try {
CHECK(uint64, uint64);
CHECK(uint64, float);
CHECK(uint64, double);
//CHECK(uint64, string);
CHECK(uint64, string);
CHECK(uint64, cstring);
CHECK(float, int8);
CHECK(float, uint8);
@@ -254,6 +260,7 @@ try {
CHECK(float, float);
CHECK(float, double);
CHECK(float, string);
CHECK(float, cstring);
CHECK(double, int8);
CHECK(double, uint8);
@@ -266,6 +273,7 @@ try {
CHECK(double, float);
CHECK(double, double);
CHECK(double, string);
CHECK(double, cstring);
CHECK(string, int8);
CHECK(string, uint8);
@@ -278,6 +286,9 @@ try {
CHECK(string, float);
CHECK(string, double);
CHECK(string, string);
CHECK(string, cstring);
// cast to const char* not supported
#undef CHECK
testDiag("Integer signed <=> unsigned");
@@ -344,6 +355,7 @@ try {
TEST2(string, "1.1e-100", double, 1.1e-100);
TEST(double, 1.1e100, string, "1.1E+100");
TEST(double, 1.1e100, const char*, "1.1E+100");
// any non-zero value is true
TEST(string, "true", epics::pvData::boolean, 100);

View File

@@ -23,7 +23,7 @@ struct MyTest {
MAIN(testUnitTest)
{
testPlan(0);
testPlan(10);
try {
TEST_METHOD(MyTest, test1);
TEST_METHOD(MyTest, test2);

View File

@@ -42,6 +42,23 @@ void test_ctor()
testEqual(D.ref<std::string>(), "bar");
}
void test_ctor_void()
{
testDiag("test_ctor_void()");
pvd::int32 i = 42;
pvd::AnyScalar A(pvd::pvInt, (void*)&i);
testEqual(A.type(), pvd::pvInt);
testEqual(A.ref<pvd::int32>(), 42);
std::string s("hello");
pvd::AnyScalar B(pvd::pvString, (void*)&s);
testEqual(B.type(), pvd::pvString);
testEqual(B.ref<std::string>(), "hello");
}
void test_basic()
{
testDiag("test_basic()");
@@ -56,6 +73,14 @@ void test_basic()
testEqual(I.as<double>(), 42.0);
testEqual(I.as<std::string>(), "42");
const pvd::AnyScalar I2(I);
testEqual(I2.type(), pvd::pvInt);
testEqual(I2.ref<pvd::int32>(), 42);
testEqual(I2.as<pvd::int32>(), 42);
testEqual(I2.as<double>(), 42.0);
testEqual(I2.as<std::string>(), "42");
testThrows(pvd::AnyScalar::bad_cast, I.ref<double>());
{
@@ -213,10 +238,11 @@ void test_move()
MAIN(testanyscalar)
{
testPlan(66);
testPlan(75);
try {
test_empty();
test_ctor();
test_ctor_void();
test_basic();
test_swap();
test_move();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -56,10 +56,10 @@ MAIN(testPVType)
int64 longInt = 0x7fffffff;
longInt <<= 32;
longInt |= 0xffffffff;
printf("int8 max %lli",(long long)longInt);
printf("int8 max %lld",(long long)longInt);
longInt = intValue = 0x80000000;;
longInt <<= 32;
printf(" min %lli\n",(long long)longInt);
printf(" min %lld\n",(long long)longInt);
printf("PASSED\n");
return testDone();

View File

@@ -12,6 +12,7 @@
#include <stdio.h>
#include <epicsThread.h>
#include <epicsUnitTest.h>
#include <epicsExit.h>
/* copy */
int testCreateRequest(void);
@@ -22,7 +23,6 @@ int testBaseException(void);
int testBitSet(void);
int testByteBuffer(void);
int testOverrunBitSet(void);
int testQueue(void);
int testSerialization(void);
int testSharedVector(void);
int testThread(void);
@@ -71,7 +71,6 @@ void pvDataAllTests(void)
runTest(testBitSet);
runTest(testByteBuffer);
runTest(testOverrunBitSet);
runTest(testQueue);
runTest(testSerialization);
runTest(testSharedVector);
runTest(testThread);
@@ -86,5 +85,6 @@ void pvDataAllTests(void)
/* property */
runTest(testCreateRequest);
}
epicsExit(0); /* Trigger test harness */
}

View File

@@ -1,72 +0,0 @@
/*************************************************************************\
* Copyright (c) 2002 The University of Saskatchewan
* EPICS BASE Versions 3.13.7
* and higher are distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/*
* RTEMS configuration for EPICS
* Revision-Id: anj@aps.anl.gov-20101005192737-disfz3vs0f3fiixd
* Author: W. Eric Norum
* norume@aps.anl.gov
* (630) 252-4793
*/
#include <rtems.h>
/*
***********************************************************************
* RTEMS CONFIGURATION *
***********************************************************************
*/
#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
#if __RTEMS_MAJOR__>4 || (__RTEMS_MAJOR__==4 && __RTEMS_MINOR__>9) || (__RTEMS_MAJOR__==4 && __RTEMS_MINOR__==9 && __RTEMS_REVISION__==99)
# define CONFIGURE_UNIFIED_WORK_AREAS
#else
# define CONFIGURE_EXECUTIVE_RAM_SIZE (2000*1024)
#endif
#define CONFIGURE_MAXIMUM_TASKS rtems_resource_unlimited(30)
#define CONFIGURE_MAXIMUM_SEMAPHORES rtems_resource_unlimited(500)
#define CONFIGURE_MAXIMUM_TIMERS rtems_resource_unlimited(20)
#define CONFIGURE_MAXIMUM_MESSAGE_QUEUES rtems_resource_unlimited(5)
#define CONFIGURE_MAXIMUM_USER_EXTENSIONS 1
#define CONFIGURE_LIBIO_MAXIMUM_FILE_DESCRIPTORS 150
#define CONFIGURE_USE_IMFS_AS_BASE_FILESYSTEM
#define CONFIGURE_MAXIMUM_DRIVERS 8
#define CONFIGURE_MICROSECONDS_PER_TICK 20000
#define CONFIGURE_INIT_TASK_PRIORITY 80
#define CONFIGURE_MALLOC_STATISTICS 1
#define CONFIGURE_INIT
#define CONFIGURE_INIT_TASK_INITIAL_MODES (RTEMS_PREEMPT | \
RTEMS_NO_TIMESLICE | \
RTEMS_NO_ASR | \
RTEMS_INTERRUPT_LEVEL(0))
#define CONFIGURE_INIT_TASK_ATTRIBUTES (RTEMS_FLOATING_POINT | RTEMS_LOCAL)
#define CONFIGURE_INIT_TASK_STACK_SIZE (16*1024)
rtems_task Init (rtems_task_argument argument);
#define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER
#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
#define CONFIGURE_FILESYSTEM_NFS
#define CONFIGURE_FILESYSTEM_IMFS
/*
* This should be made BSP dependent, not CPU dependent but I know of no
* appropriate conditionals to use.
* The new general time support makes including the RTC driver less important.
*/
#if !defined(mpc604) && !defined(__mc68040__) && !defined(__mcf5200__) && !defined(mpc7455) && !defined(__arm__) /* don't have RTC code */
#define CONFIGURE_APPLICATION_NEEDS_RTC_DRIVER
#endif
#include <bsp.h>
#include <rtems/confdefs.h>

View File

@@ -2,40 +2,11 @@
* 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 <stdlib.h>
#include <rtems/rtems_bsdnet.h>
#include <rtems/error.h>
#include "rtemsNetworking.h"
extern void pvDataAllTests(void);
#include <epicsExit.h>
#include <osdTime.h>
rtems_task
Init (rtems_task_argument ignored)
int main(int argc, char **argv)
{
rtems_bsdnet_initialize_network ();
//rtems_bsdnet_show_if_stats ();
rtems_time_of_day timeOfDay;
if (rtems_clock_get(RTEMS_CLOCK_GET_TOD,&timeOfDay) != RTEMS_SUCCESSFUL) {
timeOfDay.year = 2014;
timeOfDay.month = 1;
timeOfDay.day = 1;
timeOfDay.hour = 0;
timeOfDay.minute = 0;
timeOfDay.second = 0;
timeOfDay.ticks = 0;
rtems_status_code ret = rtems_clock_set(&timeOfDay);
if (ret != RTEMS_SUCCESSFUL) {
printf("**** Can't set time %s\n", rtems_status_text(ret));
}
}
osdTimeRegister();
extern void pvDataAllTests(void);
pvDataAllTests();
epicsExit(0);
pvDataAllTests(); /* calls epicsExit(0) */
return 0;
}