257 Commits
6.0.1 ... 7.1.3

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

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

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

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

Fully support printing.

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

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

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

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

This rev. remove #define NOMINMAX from public headers,
but no public headers use min()/max() and this was
never the correct way to use this macro as by convention
library headers are included after system headers,
which is too late to have an effect.
2018-05-28 11:24:49 -07:00
Michael Davidsaver
997e68c99a remove usage of min()/max() from public headers
Avoids need for #define NOMINMAX w/ MSVC
2018-05-28 10:14:03 -07:00
Michael Davidsaver
25663d9a7b additional EPICS_NOT_COPYABLE
attempt to pacify, or at least clarify, msvc
link error.
2018-05-21 21:02:32 -07:00
Michael Davidsaver
1e55266396 pvUnitTest.h avoid unintended copies
template argument matching doesn't always preserve
rvalue references, so force use of const ref.
2018-05-21 11:21:01 -07:00
Michael Davidsaver
810ae15991 printer.cpp avoid alloc of spaces
No need to allocate a std::string
just to repeat spaces.
2018-05-21 11:21:01 -07:00
Michael Davidsaver
271fec7f5e printer.cpp: clean whitespace 2018-05-21 11:21:01 -07:00
Michael Davidsaver
c43486791e prevent Field/PVField from being copied
I don't think this was ever intended to be possible,
but it was...
2018-05-21 11:19:42 -07:00
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
Andrew Johnson
07afe3887b Reset DEVELOPMENT_FLAG for 7.0.0 release 2017-12-14 18:27:26 -06:00
Andrew Johnson
25434ba84f Insert missing release note entries
Most of these changes were only committed on the release branch and
never pulled onto the master branch. Don't want to lose history.
2017-12-14 18:23:51 -06:00
Michael Davidsaver
9787dbd14f anyscalar.h ensure that storage really is large enough
correct failure on cygwin x86 where apparently
sizeof(double) > sizeof(std::string)
2017-12-11 21:08:59 -06:00
Michael Davidsaver
fd9081c80e minor 2017-12-11 21:08:59 -06:00
Michael Davidsaver
e79c49019d quiet warning 2017-12-11 21:08:59 -06:00
Michael Davidsaver
5bc081a3af skip unnecessary inclusion of localStaticLock.h 2017-12-11 21:08:59 -06:00
Andrew Johnson
5976eb5186 Include <top>/../RELEASE.<host>.local 2017-12-06 20:37:27 -06:00
Andrew Johnson
b194bc05b1 Unify .gitignore files 2017-11-30 12:03:33 -06:00
Andrew Johnson
18207fd79e Use 'make test-results' in travis-build script 2017-11-29 16:36:39 -06:00
Andrew Johnson
7c1e0a51eb Fixes needed for older VxWorks GCC 2017-11-29 16:35:22 -06:00
Ralph Lange
7196658166 jenkins: remove microbench option from CB build 2017-11-15 17:28:05 +01:00
Michael Davidsaver
187fe67ffa fixup debugPtr 2017-11-14 17:13:43 -06:00
Andrew Johnson
7136098c3c Suppress unnecessary deprecation warnings
Disable warnings when compiling the implementations of
deprecated classes and functions.

Removes the unused USAGE_DEPRECATED and USAGE_ERROR macros
from pvData.h which aren't visible outside of it anyway.
2017-11-07 22:19:56 -06:00
Andrew Johnson
7979238029 Let's make timeStamp constants actually const 2017-11-07 11:46:36 -06:00
Michael Davidsaver
fc38dff3b0 testPVData: clarify getSubField() by index on sub-struct 2017-11-07 08:18:12 -06:00
Michael Davidsaver
c590204cf9 add epics::auto_ptr<T> and epics::swap()
Avoid the flood of auto_ptr deprecation warnings
in the common cases of using auto_ptr
to automatically delete.
2017-11-06 12:30:40 -06:00
Michael Davidsaver
284e49c807 add EPICS_NOT_COPYABLE()
More localize (my preference), and avoids
warning spam with windows builds.
2017-11-06 11:29:59 -06:00
Michael Davidsaver
43fcd3d1e2 debugPtr use libCom instead of std::
cross-builds of mingw claim c++11 but don't have std::mutex
2017-11-05 14:49:45 -06:00
Michael Davidsaver
a9f2d7df40 reftrack: remove inline operator[]
Use of class static member 'zero'
in an inline'd method is causing DLL confusion
in dependent modules.
2017-11-02 10:08:07 -05:00
Michael Davidsaver
21a03d2b85 rename configure/CONFIG_PVDATA_VERSION 2017-11-01 11:20:14 -05:00
Michael Davidsaver
f123b8654a update ignore 2017-11-01 09:40:42 -05:00
Michael Davidsaver
51cbe538e8 handle yajl 2.1.0 API changes 2017-10-31 19:34:30 -05:00
Marty Kraimer
e247a2c4eb Merge pull request #48 from mrkraimer/master
pvAlarm, pvTimeStamp, pvControl, pvDisplay: only put to fields that h…
2017-10-17 14:52:54 -04:00
Michael Davidsaver
490b6684ac more createRequest tests 2017-10-17 10:15:00 -05:00
mrkraimer
6fdeadf171 pvAlarm, pvTimeStamp, pvControl, pvDisplay: only put to fields that have changed 2017-10-16 10:00:22 -04:00
Michael Davidsaver
a88d491012 win64 one more time, move explicit instantiations after all definitions. 2017-10-06 11:06:23 +02:00
Michael Davidsaver
fd34d68933 another win64 attempt, explicitly instanciate 2017-10-05 16:24:45 +02:00
Michael Davidsaver
215e3aab7b one more attempt to appease win64 2017-10-05 15:32:31 +02:00
Michael Davidsaver
cd4feb3bab install CONFIG_PVD 2017-10-05 14:02:05 +02:00
Michael Davidsaver
6b0af421dd another attempt to appease msvc win64 2017-10-05 11:24:23 +02:00
Michael Davidsaver
9aeb4f2a96 attempt to fix win64 linking error 2017-10-05 10:39:47 +02:00
Michael Davidsaver
9b1e789e62 PVUnion: get() const propagation, add guess(), and inline trival 2017-09-30 13:43:50 -05:00
Michael Davidsaver
406b163bcc factory methods avoid creating unnecessary temporaries
Avoid some ref. counter activity (still have global mutex...)
2017-09-30 11:09:52 -05:00
Michael Davidsaver
6f2cae95e1 PVField: getParent() const propagation and inline trival 2017-09-30 11:00:37 -05:00
Michael Davidsaver
594a29b2db add PVScalar::putFrom/getAs variants for AnyScalar 2017-09-30 10:04:31 -05:00
Michael Davidsaver
0d12464e30 add AnyScalar 2017-09-29 17:22:43 -05:00
Michael Davidsaver
ccd9ab70ee pvUnitTest.h const-ness 2017-09-29 16:12:01 -05:00
Michael Davidsaver
27f2f87e29 reftrack class Field 2017-09-29 15:30:50 -05:00
Michael Davidsaver
635eb9d36d Status: inline trival and add maximize() w/ shorthand operator |= 2017-09-29 13:37:20 -05:00
Ralph Lange
f3e7f9bb8f travis-ci: consolidate travis configuration 2017-09-28 15:10:49 +02:00
Ralph Lange
2f69665056 travis-ci: fix RTEMS/qemu builds 2017-09-22 16:10:21 +02:00
Michael Davidsaver
559f7bc1b7 travis-ci: remove non-functional RTEMS builds 2017-09-21 16:13:18 -05:00
Michael Davidsaver
693f00caf5 Merge remote-tracking branch 'origin/master'
* origin/master:
  ci: changes for EPICS 7 Base structure

# Conflicts:
#	.travis.yml
2017-09-21 15:07:21 -05:00
Michael Davidsaver
822173979c FieldBuilder::add() ignore exact duplicates 2017-09-21 13:41:11 -05:00
Michael Davidsaver
9bce66f307 FieldBuilder edit union/structureArray/unionArray
Allow appending fields to existing types
to allow structure-like types
2017-09-21 13:41:11 -05:00
Michael Davidsaver
7a71e758b1 pvUnitTest.h multi-line prints 2017-09-21 13:41:11 -05:00
Michael Davidsaver
72fe0ca3e7 minor 2017-09-21 13:41:11 -05:00
Michael Davidsaver
fd0570f0c9 travis-ci fix c++11 builds 2017-09-21 13:41:11 -05:00
Ralph Lange
e0037a0c8b ci: changes for EPICS 7 Base structure 2017-09-21 16:14:02 +02:00
Michael Davidsaver
c7c83282ee parseJSON() more forgiving scalar array handling
Allow eg. to initialize array of integers with [1.0, 2.0]
2017-09-07 15:55:21 -05:00
Michael Davidsaver
8f98d9792b add typemap.h
helper for switch() over DBF or PVD scalar type codes
2017-09-07 15:53:40 -05:00
Michael Davidsaver
111f7bd15e parseJSON() track modified fields 2017-09-07 11:53:54 -05:00
Michael Davidsaver
787af8de18 BitSet building convenience
Allow set()/clear()/flip() to be chained.
Support c++11 initializer lists.
2017-09-07 11:49:07 -05:00
Michael Davidsaver
db6ebfe71b parseJSON() assign union with scalar value 2017-09-06 18:34:00 -05:00
Michael Davidsaver
1cb490039f remove another broken no-arg ctors 2017-08-30 17:55:04 -05:00
Michael Davidsaver
a152a64f1c RefSnapshot operator
move into class definition to hopefully appease MSVC
2017-08-30 17:04:02 -05:00
Michael Davidsaver
08f50e56ac win32 doesn't have ssize_t 2017-08-30 14:20:10 -05:00
Michael Davidsaver
9ae221ca0c apply reftrack to PVField 2017-08-30 11:13:10 -05:00
Michael Davidsaver
aca8da5891 remove broken no-arg ctors
Non-useful bypass of factory which leaves
private pointers undefined (eg. parent field).
2017-08-30 11:11:47 -05:00
Michael Davidsaver
34896560ea add reftrack.h
Add global Reference Counter tracker
2017-08-30 11:11:47 -05:00
Michael Davidsaver
3597fbe382 avoid unnecessary temp shared_ptr
avoid some extra ref. counter operations.
2017-08-16 17:49:17 +02:00
Michael Davidsaver
0b262baf97 fix getSubField() by index 2017-08-16 16:29:06 +02:00
Michael Davidsaver
78b51ebe59 c++98 compatible, but still reduced, number of getSubField() specializations 2017-08-16 11:22:42 +02:00
Michael Davidsaver
08a92468fe PVStructure::getSubField() const propagation 2017-08-15 18:35:54 +02:00
Michael Davidsaver
b84ed964f9 reduce # of PVStructure::getSubField overloads 2017-08-15 18:35:54 +02:00
Michael Davidsaver
8bfe7b6b9d debugPtr compat
Fails for newer RTEMS w/ c++11 but no backtrace()
2017-08-15 18:35:54 +02:00
Michael Davidsaver
dbae173399 don't import/export inline classes 2017-07-18 14:32:42 +02:00
Michael Davidsaver
4d4dbcda4d expose const void* form of PVScalar::putFrom() 2017-07-18 14:28:11 +02:00
Michael Davidsaver
fd4584a49d resolve ambiguity 2017-07-18 11:12:44 +02:00
Michael Davidsaver
ceb9f795cb whitespace: quiet gcc6 indentation warning 2017-07-17 16:25:00 +02:00
Michael Davidsaver
443c254d46 no C99 initializers
because MSVC doesn't support this...
2017-07-17 15:58:04 +02:00
Michael Davidsaver
87fa150ced 3.14 compat 2017-07-13 19:14:52 +02:00
Michael Davidsaver
4cfdb8233f more import/export 2017-07-13 18:49:32 +02:00
Michael Davidsaver
5e42cf76eb import/export fix 2017-07-13 18:35:38 +02:00
Michael Davidsaver
dccf6193da more doc 2017-07-13 18:25:25 +02:00
Michael Davidsaver
ee4fdf3f39 json print/parse from/to PVStructure 2017-07-13 18:02:10 +02:00
Michael Davidsaver
918b7f96db valueBuilder: support scalar array fields 2017-07-12 18:33:20 +02:00
Michael Davidsaver
ef55345665 pvUnitTest: compare array fields 2017-07-12 18:26:43 +02:00
Michael Davidsaver
0a41dbb443 ScalarTypeFunc::allocArray missing export 2017-07-12 15:46:01 +02:00
Michael Davidsaver
934ad32e52 debugPtr import/export 2017-07-12 15:29:34 +02:00
Michael Davidsaver
b582f0f880 createRequest clean message before each run 2017-07-12 15:28:09 +02:00
Michael Davidsaver
face3de44a more doc 2017-07-12 14:29:31 +02:00
Michael Davidsaver
8a7b9d776f requester.h moves to pvAccessCPP 2017-07-12 13:13:44 +02:00
Michael Davidsaver
a8c5d1095d remove MessageQueue 2017-07-12 13:07:36 +02:00
Michael Davidsaver
9fa5028f6c PVStructure::getStructure() avoid creation of temporary
avoid some ref-counter activity
2017-07-07 14:39:25 +02:00
Michael Davidsaver
0de3c089d2 testByteBuffer always test both byte orders 2017-07-07 11:21:00 +02:00
Michael Davidsaver
919bc0138a add pvUnitTest.h 2017-07-06 17:05:30 +02:00
Michael Davidsaver
a0210af5c6 remove unnecessary 'typename' 2017-07-06 13:23:24 +02:00
Michael Davidsaver
888291db9a move destroyable.h to pvAccessCPP 2017-07-05 11:59:22 +02:00
Michael Davidsaver
4fce663795 travis-ci 2017-07-04 15:54:29 +02:00
Michael Davidsaver
fc3384fb95 remove raw html in favor of generated
reformat release notes.
2017-07-04 15:33:43 +02:00
Michael Davidsaver
f9a30ca08e update doc 2017-07-04 15:08:01 +02:00
Michael Davidsaver
cbbe691f70 ValueBuilder dllexport 2017-07-04 12:22:04 +02:00
Michael Davidsaver
afbf0809c3 more travis-ci 2017-07-03 18:36:59 +02:00
Michael Davidsaver
db8d4b4347 fix # of tests 2017-07-03 18:35:57 +02:00
Michael Davidsaver
904ee7dfec travis-ci test mingw/win32 dll/static builds 2017-07-03 18:21:10 +02:00
Michael Davidsaver
bef616632c add ValueBuilder 2017-07-03 17:34:38 +02:00
Michael Davidsaver
1d2e5d182e oops 2017-06-26 19:26:59 +02:00
Michael Davidsaver
568ee1fa85 add debugPtr.h to troubleshoot shared_ptr problems
A wrapper around shared_ptr which tracks backwards references
to help untangle complicated ownership situations (aka. ref loop
waiting to happen).
2017-06-26 15:59:45 +02:00
Michael Davidsaver
66633a7728 test cleanup
remove compile time "debug" flag from from tests.
use testDiag() instead
2017-06-26 15:16:43 +02:00
Michael Davidsaver
18283b44b2 more sharedPtr 2017-06-19 14:15:06 +02:00
Michael Davidsaver
278696b28e more sharedPtr compat 2017-06-15 18:23:20 +02:00
Michael Davidsaver
26efd09d45 doxygen 2017-06-14 17:06:38 +02:00
Michael Davidsaver
ec9aba79ae fix warning about missing override w/ c++11 2017-06-14 16:21:51 +02:00
Michael Davidsaver
07e42d81f4 travis-ci 2017-06-14 14:20:44 +02:00
Michael Davidsaver
d272afc128 fixup sharedPtr 2017-06-14 13:59:02 +02:00
Michael Davidsaver
6cf9fa2208 fix "hides overloaded virtual function" warning 2017-06-14 13:22:06 +02:00
Michael Davidsaver
a72451cdbe Add Destroyable::cleaner to help w/ shared_ptr<> wrapping 2017-06-06 18:38:55 +02:00
Michael Davidsaver
e82489b158 Timer: actually join thread 2017-06-02 11:03:46 +02:00
Michael Davidsaver
e4c7fa6a1c move monitor.h to pvAccessCPP 2017-05-24 17:45:41 -04:00
Michael Davidsaver
48ed24dabf remove no-op monitor.cpp 2017-05-24 16:33:16 -04:00
Michael Davidsaver
9ef060a2f2 remove deprecated monitorPlugin.h 2017-05-24 16:31:26 -04:00
Michael Davidsaver
255c41607f remove deprecated monitorPlugin.h 2017-05-24 16:06:00 -04:00
Michael Davidsaver
22da026888 deprecate unused Queue, MessageQueue, Executor, and TimeFunction 2017-05-15 15:12:28 -04:00
Michael Davidsaver
def0a63008 Queue remove nullElement
avoid potential 0=1 situation.
2017-05-15 14:53:54 -04:00
Michael Davidsaver
6e62e123e8 FieldBuilder allow append to Structure
Helpful to allow a new definition to be created
as an extension of an existing definition.
2017-05-15 12:46:32 -04:00
Michael Davidsaver
262df56024 testFieldBuilder: cleanup 2017-05-12 14:26:18 -04:00
Michael Davidsaver
81cdf071a8 FieldBuilder detect duplicate field names 2017-05-12 13:24:37 -04:00
Michael Davidsaver
c5ce75888e Merge remote-tracking branch 'md/overhaulbytebuf'
* md/overhaulbytebuf:
  overhaul byteBuffer implementation
2017-05-10 15:36:49 -04:00
Michael Davidsaver
fb232896a8 avoid unnecessary globals 2017-05-10 15:00:55 -04:00
Michael Davidsaver
5efa462f19 add c++11 override/final 2017-05-10 15:00:55 -04:00
Michael Davidsaver
225f3ab125 Remove hidden sub-class
collapse Base* and Default* classes
into parents.
2017-05-10 15:00:55 -04:00
Michael Davidsaver
664fbfeb6e pvCopy: remove unnecessary globals 2017-05-07 20:17:26 -04:00
Michael Davidsaver
66f8ca0501 simpler createRequest()
A single function which throws exceptions on error
2017-05-07 20:07:01 -04:00
Michael Davidsaver
e977d63f08 add pvdVersion.h 2017-04-15 17:26:18 -04:00
Michael Davidsaver
08fc3cab38 update testPVUnion 2017-04-15 16:19:23 -04:00
Michael Davidsaver
a01885536c PVUnion fixups
select(int32)

throw for variant w/ other than UNDEFINED_INDEX.
Now same behavior for variant and discriminating,
which is to clear contents.
Fix selector range check.

set(int32)

Fix set(UNDEFINED_INDEX, NULL) for discriminating.
Fix selector range check.
2017-04-15 16:19:23 -04:00
Michael Davidsaver
f7343674ee make PVUnion::UNDEFINED_INDEX const 2017-04-15 16:19:23 -04:00
Michael Davidsaver
fc2adf98ba Status: add helpers
so that

epics::pvData::Status(epics::pvData::Status::STATUSTYPE_WARNING, msg)

can become

epics::pvData::Status::warn(msg)
2017-04-15 16:19:23 -04:00
Michael Davidsaver
a3c57a5077 bitSet: add logical_and()/_or()
Basic logical operations for tests
where a temporary can be avoided.
2017-04-15 16:19:15 -04:00
Michael Davidsaver
28b5dd0163 shared_ptr: further simplify static_shared_vector<>() 2017-04-15 16:06:01 -04:00
Michael Davidsaver
c0b69e4e6f add monitor buffer statistics
to help answer the question of why no monitorEvent()s
are happening.
2017-04-15 16:05:26 -04:00
Michael Davidsaver
a9111d78d3 overhaul byteBuffer implementation
align(n) now fills skipped bytes with '\0'.

align(n,f) to choose a different fill value.

No other changes intended

Use intrinsic byte order swapping builtins for gcc, clang, and msvc.

use assert() to check pre/post conditions.

Remove unused condition macros and unreachable code.

Add tests of byte swapping primitives and
test the correctness of unaligned operations.

add illustrations of flip() and rewind()
2017-04-07 15:09:47 -04:00
Dave Hickin
cc91e22038 testThread: Namespace qualify Thread
Fixes build error with latest RTEMS (Thread ambiguous).
2016-09-16 21:22:33 +01:00
Andrew Johnson
4555f69733 #include <algorithm> required for MSVS 2015 2016-09-14 16:23:32 -05:00
Michael Davidsaver
da8ba56dd1 testTypeCast: mangle values to be printable
print  (u)int8 as integer instead of character
2016-09-07 15:29:58 -04:00
Michael Davidsaver
4d6d5620b0 Revert "testTypeCast: Escape non-printing characters"
This reverts commit f7cad98f3e.
2016-09-07 15:28:48 -04:00
Dave Hickin
f7cad98f3e testTypeCast: Escape non-printing characters 2016-09-07 14:40:16 +01:00
Dave Hickin
d978c4c3bf Fix for MinGW
Fixes #42
2016-09-07 10:45:39 +01:00
Dave Hickin
1c4b7810b1 jenkins: Remove COPYRIGHT from tar 2016-07-22 16:07:35 +01:00
Dave Hickin
a6ec43f81c SHRLIB: Set version to 6.1 2016-07-22 16:05:32 +01:00
151 changed files with 12689 additions and 12941 deletions

10
.ci/travis-build.sh Executable file
View File

@@ -0,0 +1,10 @@
#!/bin/sh
set -e -x
make -j2
if [ "$TEST" != "NO" ]
then
make -j2 tapfiles
make -j2 -s test-results
fi

83
.ci/travis-prepare.sh Executable file
View File

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

30
.gitignore vendored
View File

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

30
.travis.yml Normal file
View File

@@ -0,0 +1,30 @@
sudo: false
dist: trusty
language: c++
compiler:
- gcc
addons:
apt:
packages:
- libreadline6-dev
- libncurses5-dev
- perl
- clang
- g++-mingw-w64-i686
- qemu-system-x86
install:
- ./.ci/travis-prepare.sh
script:
- ./.ci/travis-build.sh
env:
- 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

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

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

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

View File

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

View File

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

View File

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

3
documentation/.gitignore vendored Normal file
View File

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

File diff suppressed because it is too large Load Diff

16
documentation/Makefile Normal file
View File

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

View File

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

View File

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

45
documentation/commit-gh.sh Executable file
View File

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

View File

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

Binary file not shown.

View File

@@ -0,0 +1,58 @@
#ifndef MAINPAGE_H
#define MAINPAGE_H
/**
@mainpage pvDataCPP documentation
- This module is included in [EPICS Base releases](https://epics-controls.org/resources-and-support/base/) beginning with 7.0.1
- It may also be [Downloaded](https://github.com/epics-base/pvDataCPP/releases) and built separately.
- @htmlonly <a href="modules.html">API components</a> @endhtmlonly
- @ref release_notes
The epics::pvData namespace.
See pv/pvData.h header.
@code
#include <pv/pvData.h>
#include <pv/createRequest.h>
@endcode
- Type description epics::pvData::Field and sub-classes
- Value container epics::pvData::PVField and sub-classes
- POD array handling epics::pvData::shared_vector
- pvRequest parsing epics::pvData::createRequest()
Define a structure type and create a container with default values.
@code
namespace pvd = epics::pvData;
pvd::StructureConstPtr stype(pvd::FieldBuilder::begin()
->add("fld1", pvd::pvInt)
->addNestedStructure("sub")
->add("fld2", pvd::pvString)
->endNested()
->createStructure());
pvd::PVStructuretPtr value(stype->build());
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 {
pvd::int32 fld1;
struct {
std::string fld2;
} sub;
};
stype value;
value.fld1 = 4;
value.fld2 = pvd::castUnsafe<std::string>(4.2);
@endcode
*/
#endif /* MAINPAGE_H */

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -1,15 +1,91 @@
/**
@page release_notes Release Notes
Release 7.1.3 (Apr 2019)
========================
- Fix for array serialization error to/from big endian.
https://github.com/epics-base/pvDataCPP/issues/65
Release 7.1.2 (Mar 2019)
========================
- 7.1.1 tag pushed prematurely.
Release 7.1.1 (Mar 2019)
========================
- Fixes
- Init order issue with StandardField::getStandardField()
- Build fix for Visual Studio 2013+
Release 7.1.0 (Nov 2018)
========================
- Deprecations
- BoundedString, BoundedScalarArray, and FixedScalarArray will be removed unless they are fixed.
See https://github.com/epics-base/pvDataCPP/issues/52 for discussion.
- pv/localStaticLock.h
- pv/pvCopy.h (see epics::pvData::PVRequestMapper)
- Removals
- Remove previously deprecated executor.h, queue.h and timerFunction.h
- Remove *HashFunction functors to "hash" Field sub-classes which were never fully implemented.
- Fixes
- Make thread safe getFieldCreate() and getPVDataCreate()
- Workaround for MSVC pickyness that iterators be non-NULL, even when not de-referenced.
- Fix alignment fault during (de)serialization on RTEMS/vxWorks.
- Fix epics::pvData::shared_vector::swap() for void specialization.
- Changes in several epics::pvData::Field sub-classes to return const ref. instead of a copy.
- Additions
- epics::pvData::shared_vector add c++11 move and construct for initializer list.
- Add epics::pvData::AnyScalar::clear()
- Add ctor epics::pvData::AnyScalar(ScalarType, const void*) to allow construction from an untyped buffer.
- Add epics::pvData::Timer::close()
- Allow epics::pvData::castUnsafe() from const char* without first allocating a std::string.
- De-duplication of epics::pvData::Field instances is performed using a global hash table.
Identical definitions will share a single instance. Allows O(0) comparision.
- Add epics::pvData::PVRequestMapper to facilitate (partial) copying between PVStructure instances
modified by a pvRequest.
- Add shorthand notations epics::pvData::FieldBuilder::begin() and epics::pvData::Field::build()
Release 7.0.0 (Dec 2017)
========================
- Removals
- Remove requester.h, monitor.h, and destroyable.h.. Migrated to the pvAccessCPP module.
- Previously deprecated monitorPlugin.h is removed.
- Remove pv/messageQueue.h and epics::pvData::MessageQueue
- Deprecate the following utility classes, to be removed in 8.0.
- epics::pvData::Queue
- epics::pvData::Executor
- epics::pvData::TimeFunction
- Additions
- Add pv/pvdVersion.h which is included by pv/pvIntrospect.h
- Add epics::pvData::createRequest() function. Alternative to epics::pvData::CreateRequest class which throws on error.
- epics::pvData::FieldBuilder allow Structure defintion to be changed/appended
- Add epics::pvData::ValueBuilder like FieldBuilder also sets initial values.
- Can also be constructed using an existing PVStructure to allow "editing".
- Add debugPtr.h wrapper with reference tracking to assist in troubleshooting shared_ptr related ref. loops.
- Add @ref pvjson utilities
- 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
=============
The changes since release 6.0.0 are:
* Fix "Problem building pvDataCPP for win32-x86-mingw" (issue #42)
* In src/misc/bitSet.cpp #include <algorithm> required for MSVS 2015
* In src/misc/bitSet.cpp #include "algorithm" required for MSVS 2015
* In testApp/misc/testTypeCast.cpp print (u)int8 values as integers
* Minor documentation updates
Release 6.0.0
=============
Release 6.0.0 (Aug. 2016)
=======================
The main changes since release 5.0.4 are:
@@ -49,13 +125,13 @@ string to std::cerr.
Serialization/deserialization helpers added
-------------------------------------------
A helper function, serializeToVector, has been added which serializes a
A helper function, serializeToVector, has been added which serializes a
Serializable object into a standard vector of UInt8s.
Similarly a function deserializeFromVector deserializes a standard vector into
a Deserializable object.
A function deserializeFromBuffer deserializes a ByteBuffer into a
A function deserializeFromBuffer deserializes a ByteBuffer into a
Deserializable object.
Field name validation performed
@@ -90,7 +166,7 @@ deserializer) when the number of bits was 56-63 modulo 64. C++ serialization
has been fixed.
Fix exposed issue in deserialization on 32-bit platforms which
has also been corrected.
has also been corrected.
Fixed truncation in BitSet::or_and (issue #27)
----------------------------------------------
@@ -111,38 +187,38 @@ The only change since release 5.0.2 is:
Fixed buffer overflow in PVUnion::serialize() (issue #20)
---------------------------------------------------------
A PVUnion whose stored value was null was serialized without checking
A PVUnion whose stored value was null was serialized without checking
whether the buffer had sufficient capacity. This has been fixed by calling
ensureBuffer().
Release 5.0.2
=============
Release 5.0.2 (Sep. 2015)
=========================
The main changes since release 4.0.3 are:
The main changes since release 4.0 are:
* Deprecated getXXXField() methods have been removed from PVStructure
* Convert copy methods and equals operators (re)moved
* Convert::copyUnion now always copies between subfields.
* New method getSubFieldT, like getSubField except it throws an exception
* findSubField method removed from PVStructure
* New stream operators for Field and PVField are provided
* New template versions of Structure::getField
* Fixes for static initialisation order issues
* CreateRequest prevents a possible SEGFAULT
- Deprecated getXXXField() methods have been removed from PVStructure
- Convert copy methods and equals operators (re)moved
- Convert::copyUnion now always copies between subfields.
- New method getSubFieldT, like getSubField except it throws an exception
- findSubField method removed from PVStructure
- New stream operators for Field and PVField are provided
- New template versions of Structure::getField
- Fixes for static initialisation order issues
- CreateRequest prevents a possible SEGFAULT
Deprecated getXXXField methods have been removed from PVStructure
-----------------------------------------------------------------
-------------------------------------------------------------------
The following methods have been removed from PVStructure
* getBooleanField
* getByteField, getShortField, getIntField, getLongField
* getUByteField, getUShortField, getUIntField, getULongField
* getStringField
* getStructureField, getUnionField
* getScalarArrayField, getStructureArrayField, getUnionArrayField
- getBooleanField
- getByteField, getShortField, getIntField, getLongField
- getUByteField, getUShortField, getUIntField, getULongField
- getStringField
- getStructureField, getUnionField
- getScalarArrayField, getStructureArrayField, getUnionArrayField
Use template getSubField instead, e.g. use
@@ -190,7 +266,7 @@ PVStructure has a new template member
getSubFieldT(std::string const &fieldName)
that is like <b>getSubField</b> except that it throws a runtime_error
that is like `getSubField()` except that it throws a runtime_error
instead of returning null.
@@ -236,8 +312,8 @@ New template version of Structure::getField
A new template getField method has been added to Structure
template<typename FT >
std::tr1::shared_ptr< const FT > getField(std::string const &fieldName) const
template<typename FT >
std::tr1::shared_ptr< const FT > getField(std::string const &fieldName) const
Can be used, for example, as follows:
@@ -264,13 +340,13 @@ Release 4.0.3
The main changes since release 3.0.2 are:
* array semantics now enforce Copy On Write.
* String no longer defined.
* timeStamp and valueAlarm name changes
* toString replaced by stream I/O
* union is new type.
* copy is new.
* monitorPlugin is new.
- array semantics now enforce Copy On Write.
- String no longer defined.
- timeStamp and valueAlarm name changes
- toString replaced by stream I/O
- union is new type.
- copy is new.
- monitorPlugin is new.
New Semantics for Arrays
--------
@@ -326,8 +402,8 @@ There are two new basic types: union_t and unionArray.
A union is like a structure that has a single subfield.
There are two flavors:
* <b>variant union</b> The field can have any type.
* <b>union</b> The field can any of specified set of types.
- *variant union* The field can have any type.
- *union* The field can any of specified set of types.
The field type can be dynamically changed.
@@ -348,3 +424,5 @@ This is prototype and is subject to debate.
Release 3.0.2
==========
This was the starting point for RELEASE_NOTES
*/

14
examples/Makefile Normal file
View File

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

30
examples/unittest.cpp Normal file
View File

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

View File

@@ -21,23 +21,15 @@ 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
}
###########################################
# Defaults for EPICS Base and MB
# Defaults for EPICS Base
DEFAULT_BASE=3.15.4
BASE=${BASE:-${DEFAULT_BASE}}
MB=${MB:-"NO_MICROBENCH"}
###########################################
# Fetch and unpack dependencies
@@ -73,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

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

View File

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

View File

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

View File

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

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

View File

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

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

View File

@@ -7,10 +7,6 @@
* @author mrk
*/
#if defined(_WIN32) && !defined(NOMINMAX)
#define NOMINMAX
#endif
#include <cstddef>
#include <cstdlib>
#include <string>
@@ -18,13 +14,18 @@
#include <stdexcept>
#include <sstream>
#include <epicsString.h>
#include <epicsMutex.h>
#include <epicsThread.h>
#define epicsExportSharedSymbols
#include <pv/reftrack.h>
#include <pv/lock.h>
#include <pv/pvIntrospect.h>
#include <pv/factory.h>
#include <pv/serializeHelper.h>
#include <pv/thread.h>
#include <pv/pvData.h>
using std::tr1::static_pointer_cast;
using std::size_t;
@@ -32,16 +33,79 @@ using std::string;
namespace epics { namespace pvData {
static DebugLevel debugLevel = lowDebug;
size_t Field::num_instances;
struct Field::Helper {
static unsigned hash(Field *fld) {
std::ostringstream key;
// hash the output of operator<<()
// not efficient, but stable within this process.
key<<(*fld);
unsigned H = epicsStrHash(key.str().c_str(), 0xbadc0de1);
fld->m_hash = H;
return H;
}
};
struct FieldCreate::Helper {
template<typename FLD>
static void cache(const FieldCreate *create, std::tr1::shared_ptr<FLD>& ent) {
unsigned hash = Field::Helper::hash(ent.get());
Lock G(create->mutex);
// we examine raw pointers stored in create->cache, which is safe under create->mutex
std::pair<cache_t::iterator, cache_t::iterator> itp(create->cache.equal_range(hash));
for(; itp.first!=itp.second; ++itp.first) {
Field* cent(itp.first->second);
FLD* centx(dynamic_cast<FLD*>(cent));
if(centx && compare(*centx, *ent)) {
try{
ent = std::tr1::static_pointer_cast<FLD>(cent->shared_from_this());
return;
}catch(std::tr1::bad_weak_ptr&){
// we're racing destruction.
// add a new entry.
// Field::~Field is in the process of removing this old one.
continue;
}
}
}
create->cache.insert(std::make_pair(hash, ent.get()));
// cache cleaned from Field::~Field
}
};
Field::Field(Type type)
: m_fieldType(type)
, m_hash(0)
{
REFTRACE_INCREMENT(num_instances);
}
Field::~Field() {
REFTRACE_DECREMENT(num_instances);
FieldCreatePtr create(getFieldCreate());
Lock G(create->mutex);
std::pair<FieldCreate::cache_t::iterator, FieldCreate::cache_t::iterator> itp(create->cache.equal_range(m_hash));
for(; itp.first!=itp.second; ++itp.first) {
Field* cent(itp.first->second);
if(cent==this) {
create->cache.erase(itp.first);
return;
}
}
}
std::tr1::shared_ptr<PVField> Field::build() const
{
FieldConstPtr self(shared_from_this());
return getPVDataCreate()->createPVField(self);
}
std::ostream& operator<<(std::ostream& o, const Field& f)
{
@@ -112,6 +176,11 @@ void Scalar::deserialize(ByteBuffer* /*buffer*/, DeserializableControl* /*contro
}
std::tr1::shared_ptr<PVScalar> Scalar::build() const
{
return getPVDataCreate()->createPVScalar(std::tr1::static_pointer_cast<const Scalar>(shared_from_this()));
}
std::string BoundedString::getID() const
@@ -278,6 +347,11 @@ void ScalarArray::deserialize(ByteBuffer* /*buffer*/, DeserializableControl* /*c
throw std::runtime_error("not valid operation, use FieldCreate::deserialize instead");
}
std::tr1::shared_ptr<PVScalarArray> ScalarArray::build() const
{
return getPVDataCreate()->createPVScalarArray(std::tr1::static_pointer_cast<const ScalarArray>(shared_from_this()));
}
BoundedScalarArray::~BoundedScalarArray() {}
@@ -329,9 +403,7 @@ StructureArray::StructureArray(StructureConstPtr const & structure)
{
}
StructureArray::~StructureArray() {
if(debugLevel==highDebug) printf("~StructureArray\n");
}
StructureArray::~StructureArray() {}
string StructureArray::getID() const
{
@@ -358,14 +430,17 @@ void StructureArray::deserialize(ByteBuffer* /*buffer*/, DeserializableControl*
throw std::runtime_error("not valid operation, use FieldCreate::deserialize instead");
}
std::tr1::shared_ptr<PVValueArray<std::tr1::shared_ptr<PVStructure> > > StructureArray::build() const
{
return getPVDataCreate()->createPVStructureArray(std::tr1::static_pointer_cast<const StructureArray>(shared_from_this()));
}
UnionArray::UnionArray(UnionConstPtr const & _punion)
: Array(unionArray),punion(_punion)
{
}
UnionArray::~UnionArray() {
if(debugLevel==highDebug) printf("~UnionArray\n");
}
UnionArray::~UnionArray() {}
string UnionArray::getID() const
{
@@ -400,6 +475,11 @@ void UnionArray::deserialize(ByteBuffer* /*buffer*/, DeserializableControl* /*co
throw std::runtime_error("not valid operation, use FieldCreate::deserialize instead");
}
std::tr1::shared_ptr<PVValueArray<std::tr1::shared_ptr<PVUnion> > > UnionArray::build() const
{
return getPVDataCreate()->createPVUnionArray(std::tr1::static_pointer_cast<const UnionArray>(shared_from_this()));
}
const string Structure::DEFAULT_ID = Structure::defaultId();
const string & Structure::defaultId()
@@ -453,11 +533,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();
}
@@ -538,6 +617,11 @@ void Structure::deserialize(ByteBuffer* /*buffer*/, DeserializableControl* /*con
throw std::runtime_error("not valid operation, use FieldCreate::deserialize instead");
}
std::tr1::shared_ptr<PVStructure> Structure::build() const
{
return getPVDataCreate()->createPVStructure(std::tr1::static_pointer_cast<const Structure>(shared_from_this()));
}
const string Union::DEFAULT_ID = Union::defaultId();
const string & Union::defaultId()
@@ -605,6 +689,41 @@ Union::Union (
Union::~Union() { }
int32 Union::guess(Type t, ScalarType s) const
{
if(t!=scalar && t!=scalarArray)
THROW_EXCEPTION2(std::logic_error, "PVUnion::guess() only support scalar and scalarArray");
int32 ret = -1;
for(size_t i=0, N=fields.size(); i<N; i++)
{
if(fields[i]->getType()!=t)
continue;
ScalarType type;
switch(fields[i]->getType()) {
case scalar:
type = static_cast<const Scalar*>(fields[i].get())->getScalarType();
break;
case scalarArray:
type = static_cast<const ScalarArray*>(fields[i].get())->getElementType();
break;
default:
continue;
}
if(type==s) {
// exact match
ret = i;
break; // we're done
} else if(ret==-1) {
// first partial match
ret = i;
}
}
return ret;
}
string Union::getID() const
{
@@ -701,18 +820,102 @@ 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) {}
FieldBuilder::FieldBuilder()
:fieldCreate(getFieldCreate())
,idSet(false)
,nestedClassToBuild(structure)
,nestedArray(false)
,createNested(true)
{}
FieldBuilder::FieldBuilder(const Structure* S)
:fieldCreate(getFieldCreate())
,id(S->getID())
,idSet(!id.empty())
,fieldNames(S->getFieldNames())
,fields(S->getFields())
,parentBuilder()
,nestedClassToBuild(structure)
,nestedName()
,nestedArray(false)
,createNested(false)
{}
FieldBuilder::FieldBuilder(const FieldBuilderPtr & _parentBuilder,
const std::string& name,
const Structure* S)
:fieldCreate(_parentBuilder->fieldCreate)
,id(S->getID())
,idSet(!id.empty())
,fieldNames(S->getFieldNames())
,fields(S->getFields())
,parentBuilder(_parentBuilder)
,nestedClassToBuild(structure)
,nestedName(name)
,nestedArray(false)
,createNested(false)
{}
FieldBuilder::FieldBuilder(const FieldBuilderPtr & _parentBuilder,
const std::string& name,
const StructureArray* S)
:fieldCreate(getFieldCreate())
,id(S->getStructure()->getID())
,idSet(!id.empty())
,fieldNames(S->getStructure()->getFieldNames())
,fields(S->getStructure()->getFields())
,parentBuilder(_parentBuilder)
,nestedClassToBuild(structure)
,nestedName(name)
,nestedArray(true)
,createNested(false)
{}
FieldBuilder::FieldBuilder(const FieldBuilderPtr & _parentBuilder,
const std::string& name,
const Union* S)
:fieldCreate(getFieldCreate())
,id(S->getID())
,idSet(!id.empty())
,fieldNames(S->getFieldNames())
,fields(S->getFields())
,parentBuilder(_parentBuilder)
,nestedClassToBuild(union_)
,nestedName(name)
,nestedArray(false)
,createNested(false)
{}
FieldBuilder::FieldBuilder(const FieldBuilderPtr & _parentBuilder,
const std::string& name,
const UnionArray* S)
:fieldCreate(getFieldCreate())
,id(S->getUnion()->getID())
,idSet(!id.empty())
,fieldNames(S->getUnion()->getFieldNames())
,fields(S->getUnion()->getFields())
,parentBuilder(_parentBuilder)
,nestedClassToBuild(union_)
,nestedName(name)
,nestedArray(true)
,createNested(false)
{}
FieldBuilder::FieldBuilder(FieldBuilderPtr const & _parentBuilder,
string const & _nestedName,
Type _nestedClassToBuild, bool _nestedArray) :
fieldCreate(getFieldCreate()),
idSet(false),
parentBuilder(_parentBuilder),
nestedClassToBuild(_nestedClassToBuild),
nestedName(_nestedName),
nestedArray(_nestedArray)
Type _nestedClassToBuild, bool _nestedArray)
:fieldCreate(_parentBuilder->fieldCreate)
,idSet(false)
,parentBuilder(_parentBuilder)
,nestedClassToBuild(_nestedClassToBuild)
,nestedName(_nestedName)
,nestedArray(_nestedArray)
,createNested(true)
{}
void FieldBuilder::reset()
@@ -723,6 +926,19 @@ void FieldBuilder::reset()
fields.clear();
}
FieldBuilderPtr FieldBuilder::begin()
{
FieldBuilderPtr ret(new FieldBuilder);
return ret;
}
FieldBuilderPtr FieldBuilder::begin(StructureConstPtr S)
{
FieldBuilderPtr ret(new FieldBuilder(S.get()));
return ret;
}
FieldBuilderPtr FieldBuilder::setId(string const & id)
{
this->id = id;
@@ -732,56 +948,57 @@ FieldBuilderPtr FieldBuilder::setId(string const & id)
FieldBuilderPtr FieldBuilder::add(string const & name, ScalarType scalarType)
{
fields.push_back(fieldCreate->createScalar(scalarType)); fieldNames.push_back(name);
return shared_from_this();
return add(name, fieldCreate->createScalar(scalarType));
}
FieldBuilderPtr FieldBuilder::addBoundedString(std::string const & name, std::size_t maxLength)
{
fields.push_back(fieldCreate->createBoundedString(maxLength)); fieldNames.push_back(name);
return shared_from_this();
return add(name, fieldCreate->createBoundedString(maxLength));
}
FieldBuilderPtr FieldBuilder::add(string const & name, FieldConstPtr const & field)
{
fields.push_back(field); fieldNames.push_back(name);
const Field* cur = findField(name, field->getType());
if(!cur) {
fields.push_back(field); fieldNames.push_back(name);
} else if(*cur!=*field) {
THROW_EXCEPTION2(std::runtime_error, "duplicate field name w/ different type : "+name);
} // else exact duplicate is silently ignored
return shared_from_this();
}
FieldBuilderPtr FieldBuilder::addArray(string const & name, ScalarType scalarType)
{
fields.push_back(fieldCreate->createScalarArray(scalarType)); fieldNames.push_back(name);
return shared_from_this();
return add(name, fieldCreate->createScalarArray(scalarType));
}
FieldBuilderPtr FieldBuilder::addFixedArray(string const & name, ScalarType scalarType, size_t size)
{
fields.push_back(fieldCreate->createFixedScalarArray(scalarType, size)); fieldNames.push_back(name);
return shared_from_this();
return add(name, fieldCreate->createFixedScalarArray(scalarType, size));
}
FieldBuilderPtr FieldBuilder::addBoundedArray(string const & name, ScalarType scalarType, size_t size)
{
fields.push_back(fieldCreate->createBoundedScalarArray(scalarType, size)); fieldNames.push_back(name);
return shared_from_this();
return add(name, fieldCreate->createBoundedScalarArray(scalarType, size));
}
FieldBuilderPtr FieldBuilder::addArray(string const & name, FieldConstPtr const & element)
{
FieldConstPtr fld;
switch (element->getType())
{
case structure:
fields.push_back(fieldCreate->createStructureArray(static_pointer_cast<const Structure>(element)));
fld = fieldCreate->createStructureArray(static_pointer_cast<const Structure>(element));
break;
case union_:
fields.push_back(fieldCreate->createUnionArray(static_pointer_cast<const Union>(element)));
fld = fieldCreate->createUnionArray(static_pointer_cast<const Union>(element));
break;
case scalar:
if (std::tr1::dynamic_pointer_cast<const BoundedString>(element).get())
THROW_EXCEPTION2(std::invalid_argument, "bounded string arrays are not supported");
fields.push_back(fieldCreate->createScalarArray(static_pointer_cast<const Scalar>(element)->getScalarType()));
fld = fieldCreate->createScalarArray(static_pointer_cast<const Scalar>(element)->getScalarType());
break;
// scalarArray?
default:
@@ -790,8 +1007,7 @@ FieldBuilderPtr FieldBuilder::addArray(string const & name, FieldConstPtr const
THROW_EXCEPTION2(std::invalid_argument, msg.str());
}
fieldNames.push_back(name);
return shared_from_this();
return add(name, fld);
}
FieldConstPtr FieldBuilder::createFieldInternal(Type type)
@@ -841,48 +1057,111 @@ UnionConstPtr FieldBuilder::createUnion()
return field;
}
const Field* FieldBuilder::findField(const std::string& name, Type ftype)
{
// linear search on the theory that the number of fields is small
for(size_t i=0; i<fieldNames.size(); i++)
{
if(name!=fieldNames[i])
continue;
if(fields[i]->getType()!=ftype)
THROW_EXCEPTION2(std::invalid_argument, "nested field not required type: "+name);
return fields[i].get();
}
return 0;
}
FieldBuilderPtr FieldBuilder::addNestedStructure(string const & name)
{
const Field *cur = findField(name, structure);
if(cur) {
return FieldBuilderPtr(new FieldBuilder(shared_from_this(), name,
static_cast<const Structure*>(cur)));
}
return FieldBuilderPtr(new FieldBuilder(shared_from_this(), name, structure, false));
}
FieldBuilderPtr FieldBuilder::addNestedUnion(string const & name)
{
const Field *cur = findField(name, union_);
if(cur) {
return FieldBuilderPtr(new FieldBuilder(shared_from_this(), name,
static_cast<const Union*>(cur)));
}
return FieldBuilderPtr(new FieldBuilder(shared_from_this(), name, union_, false));
}
FieldBuilderPtr FieldBuilder::addNestedStructureArray(string const & name)
{
const Field *cur = findField(name, structureArray);
if(cur) {
return FieldBuilderPtr(new FieldBuilder(shared_from_this(), name,
static_cast<const StructureArray*>(cur)));
}
return FieldBuilderPtr(new FieldBuilder(shared_from_this(), name, structure, true));
}
FieldBuilderPtr FieldBuilder::addNestedUnionArray(string const & name)
{
const Field *cur = findField(name, unionArray);
if(cur) {
return FieldBuilderPtr(new FieldBuilder(shared_from_this(), name,
static_cast<const UnionArray*>(cur)));
}
return FieldBuilderPtr(new FieldBuilder(shared_from_this(), name, union_, true));
}
FieldBuilderPtr FieldBuilder::endNested()
{
if (!parentBuilder.get())
THROW_EXCEPTION2(std::runtime_error, "this method can only be called to create nested fields");
if (!parentBuilder)
THROW_EXCEPTION2(std::runtime_error, "FieldBuilder::endNested() can only be called to create nested fields");
FieldConstPtr nestedField = createFieldInternal(nestedClassToBuild);
if (nestedArray)
parentBuilder->addArray(nestedName, nestedField);
else
parentBuilder->add(nestedName, nestedField);
return parentBuilder;
}
if(createNested) {
if (nestedArray)
parentBuilder->addArray(nestedName, nestedField);
else
parentBuilder->add(nestedName, nestedField);
return parentBuilder;
} else {
for(size_t i=0, N = parentBuilder->fieldNames.size(); i<N; i++)
{
if(nestedName!=parentBuilder->fieldNames[i])
continue;
if(nestedArray) {
if(nestedClassToBuild==structure)
parentBuilder->fields[i] = fieldCreate->createStructureArray(std::tr1::static_pointer_cast<const Structure>(nestedField));
else if(nestedClassToBuild==union_)
parentBuilder->fields[i] = fieldCreate->createUnionArray(std::tr1::static_pointer_cast<const Union>(nestedField));
else
throw std::logic_error("bad nested class");
} else {
parentBuilder->fields[i] = nestedField;
}
return parentBuilder;
}
// this only reached if bug in ctor
THROW_EXCEPTION2(std::logic_error, "no nested field field?");
}
}
FieldBuilderPtr FieldCreate::createFieldBuilder() const
{
return FieldBuilderPtr(new FieldBuilder());
}
FieldBuilderPtr FieldCreate::createFieldBuilder(StructureConstPtr S) const
{
FieldBuilderPtr ret(new FieldBuilder(S.get()));
return ret;
}
ScalarConstPtr FieldCreate::createScalar(ScalarType scalarType) const
{
if(scalarType<0 || scalarType>MAX_SCALAR_TYPE) {
@@ -896,10 +1175,9 @@ ScalarConstPtr FieldCreate::createScalar(ScalarType scalarType) const
BoundedStringConstPtr FieldCreate::createBoundedString(std::size_t maxLength) const
{
// TODO use std::make_shared
std::tr1::shared_ptr<BoundedString> s(new BoundedString(maxLength), Field::Deleter());
BoundedStringConstPtr sa = s;
return sa;
std::tr1::shared_ptr<BoundedString> s(new BoundedString(maxLength));
Helper::cache(this, s);
return s;
}
ScalarArrayConstPtr FieldCreate::createScalarArray(ScalarType elementType) const
@@ -921,10 +1199,9 @@ ScalarArrayConstPtr FieldCreate::createFixedScalarArray(ScalarType elementType,
THROW_EXCEPTION2(std::invalid_argument, strm.str());
}
// TODO use std::make_shared
std::tr1::shared_ptr<ScalarArray> s(new FixedScalarArray(elementType, size), Field::Deleter());
ScalarArrayConstPtr sa = s;
return sa;
std::tr1::shared_ptr<ScalarArray> s(new FixedScalarArray(elementType, size));
Helper::cache(this, s);
return s;
}
ScalarArrayConstPtr FieldCreate::createBoundedScalarArray(ScalarType elementType, size_t size) const
@@ -935,10 +1212,9 @@ ScalarArrayConstPtr FieldCreate::createBoundedScalarArray(ScalarType elementType
THROW_EXCEPTION2(std::invalid_argument, strm.str());
}
// TODO use std::make_shared
std::tr1::shared_ptr<ScalarArray> s(new BoundedScalarArray(elementType, size), Field::Deleter());
ScalarArrayConstPtr sa = s;
return sa;
std::tr1::shared_ptr<ScalarArray> s(new BoundedScalarArray(elementType, size));
Helper::cache(this, s);
return s;
}
StructureConstPtr FieldCreate::createStructure () const
@@ -995,10 +1271,9 @@ StructureConstPtr FieldCreate::createStructure (
StringArray const & fieldNames,FieldConstPtrArray const & fields) const
{
validateFieldNames(fieldNames);
// TODO use std::make_shared
std::tr1::shared_ptr<Structure> sp(new Structure(fieldNames,fields), Field::Deleter());
StructureConstPtr structure = sp;
return structure;
std::tr1::shared_ptr<Structure> sp(new Structure(fieldNames,fields));
Helper::cache(this, sp);
return sp;
}
StructureConstPtr FieldCreate::createStructure (
@@ -1007,29 +1282,26 @@ StructureConstPtr FieldCreate::createStructure (
FieldConstPtrArray const & fields) const
{
validateFieldNames(fieldNames);
// TODO use std::make_shared
std::tr1::shared_ptr<Structure> sp(new Structure(fieldNames,fields,id), Field::Deleter());
StructureConstPtr structure = sp;
return structure;
std::tr1::shared_ptr<Structure> sp(new Structure(fieldNames,fields,id));
Helper::cache(this, sp);
return sp;
}
StructureArrayConstPtr FieldCreate::createStructureArray(
StructureConstPtr const & structure) const
{
// TODO use std::make_shared
std::tr1::shared_ptr<StructureArray> sp(new StructureArray(structure), Field::Deleter());
StructureArrayConstPtr structureArray = sp;
return structureArray;
std::tr1::shared_ptr<StructureArray> sp(new StructureArray(structure));
Helper::cache(this, sp);
return sp;
}
UnionConstPtr FieldCreate::createUnion (
StringArray const & fieldNames,FieldConstPtrArray const & fields) const
{
validateFieldNames(fieldNames);
// TODO use std::make_shared
std::tr1::shared_ptr<Union> sp(new Union(fieldNames,fields), Field::Deleter());
UnionConstPtr punion = sp;
return punion;
std::tr1::shared_ptr<Union> sp(new Union(fieldNames,fields));
Helper::cache(this, sp);
return sp;
}
UnionConstPtr FieldCreate::createUnion (
@@ -1038,10 +1310,9 @@ UnionConstPtr FieldCreate::createUnion (
FieldConstPtrArray const & fields) const
{
validateFieldNames(fieldNames);
// TODO use std::make_shared
std::tr1::shared_ptr<Union> sp(new Union(fieldNames,fields,id), Field::Deleter());
UnionConstPtr punion = sp;
return punion;
std::tr1::shared_ptr<Union> sp(new Union(fieldNames,fields,id));
Helper::cache(this, sp);
return sp;
}
UnionConstPtr FieldCreate::createVariantUnion () const
@@ -1052,10 +1323,9 @@ UnionConstPtr FieldCreate::createVariantUnion () const
UnionArrayConstPtr FieldCreate::createUnionArray(
UnionConstPtr const & punion) const
{
// TODO use std::make_shared
std::tr1::shared_ptr<UnionArray> sp(new UnionArray(punion), Field::Deleter());
UnionArrayConstPtr unionArray = sp;
return unionArray;
std::tr1::shared_ptr<UnionArray> sp(new UnionArray(punion));
Helper::cache(this, sp);
return sp;
}
UnionArrayConstPtr FieldCreate::createVariantUnionArray () const
@@ -1184,12 +1454,10 @@ FieldConstPtr FieldCreate::deserialize(ByteBuffer* buffer, DeserializableControl
size_t size = SerializeHelper::readSize(buffer, control);
// TODO use std::make_shared
std::tr1::shared_ptr<Field> sp(
new BoundedString(size),
Field::Deleter());
FieldConstPtr p = sp;
return p;
new BoundedString(size));
Helper::cache(this, sp);
return sp;
}
else
throw std::invalid_argument("invalid type encoding");
@@ -1214,19 +1482,17 @@ FieldConstPtr FieldCreate::deserialize(ByteBuffer* buffer, DeserializableControl
{
// TODO use std::make_shared
std::tr1::shared_ptr<Field> sp(
new FixedScalarArray(static_cast<epics::pvData::ScalarType>(scalarType), size),
Field::Deleter());
FieldConstPtr p = sp;
return p;
new FixedScalarArray(static_cast<epics::pvData::ScalarType>(scalarType), size));
Helper::cache(this, sp);
return sp;
}
else
{
// TODO use std::make_shared
std::tr1::shared_ptr<Field> sp(
new BoundedScalarArray(static_cast<epics::pvData::ScalarType>(scalarType), size),
Field::Deleter());
FieldConstPtr p = sp;
return p;
new BoundedScalarArray(static_cast<epics::pvData::ScalarType>(scalarType), size));
Helper::cache(this, sp);
return sp;
}
}
else if (typeCode == 0x80)
@@ -1238,9 +1504,9 @@ FieldConstPtr FieldCreate::deserialize(ByteBuffer* buffer, DeserializableControl
// Type type = Type.structureArray;
StructureConstPtr elementStructure = std::tr1::static_pointer_cast<const Structure>(control->cachedDeserialize(buffer));
// TODO use std::make_shared
std::tr1::shared_ptr<Field> sp(new StructureArray(elementStructure), Field::Deleter());
FieldConstPtr p = sp;
return p;
std::tr1::shared_ptr<Field> sp(new StructureArray(elementStructure));
Helper::cache(this, sp);
return sp;
}
else if (typeCode == 0x81)
{
@@ -1251,9 +1517,9 @@ FieldConstPtr FieldCreate::deserialize(ByteBuffer* buffer, DeserializableControl
// Type type = Type.unionArray;
UnionConstPtr elementUnion = std::tr1::static_pointer_cast<const Union>(control->cachedDeserialize(buffer));
// TODO use std::make_shared
std::tr1::shared_ptr<Field> sp(new UnionArray(elementUnion), Field::Deleter());
FieldConstPtr p = sp;
return p;
std::tr1::shared_ptr<Field> sp(new UnionArray(elementUnion));
Helper::cache(this, sp);
return sp;
}
else if (typeCode == 0x82)
{
@@ -1269,46 +1535,58 @@ FieldConstPtr FieldCreate::deserialize(ByteBuffer* buffer, DeserializableControl
}
}
// TODO replace with non-locking singleton pattern
FieldCreatePtr FieldCreate::getFieldCreate()
{
LOCAL_STATIC_LOCK;
static FieldCreatePtr fieldCreate;
static Mutex mutex;
namespace detail {
struct field_factory {
FieldCreatePtr fieldCreate;
field_factory() :fieldCreate(new FieldCreate()) {
registerRefCounter("Field", &Field::num_instances);
registerRefCounter("Thread", &Thread::num_instances);
}
};
}
Lock xx(mutex);
if(fieldCreate.get()==0) fieldCreate = FieldCreatePtr(new FieldCreate());
return fieldCreate;
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()
{
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;
}
FieldCreatePtr getFieldCreate() {
return FieldCreate::getFieldCreate();
}
}}
namespace std{

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>
@@ -24,6 +21,7 @@
#include <pv/pvData.h>
#include <pv/factory.h>
#include <pv/serializeHelper.h>
#include <pv/reftrack.h>
using std::tr1::static_pointer_cast;
using std::size_t;
@@ -59,135 +57,111 @@ template<> const ScalarType PVFloatArray::typeCode = pvFloat;
template<> const ScalarType PVDoubleArray::typeCode = pvDouble;
template<> const ScalarType PVStringArray::typeCode = pvString;
/** Default storage for scalar values
*/
template<typename T>
class BasePVScalar : public PVScalarValue<T> {
public:
typedef T value_type;
typedef T* pointer;
typedef const T* const_pointer;
BasePVScalar(ScalarConstPtr const & scalar);
virtual ~BasePVScalar();
virtual T get() const ;
virtual void put(T val);
virtual void serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher) const;
virtual void deserialize(ByteBuffer *pbuffer,
DeserializableControl *pflusher);
private:
T value;
};
template<typename T>
BasePVScalar<T>::BasePVScalar(ScalarConstPtr const & scalar)
: PVScalarValue<T>(scalar),value(0)
{}
//Note: '0' is a suitable default for all POD types (not string)
PVScalarValue<T>::~PVScalarValue() {}
template<typename T>
BasePVScalar<T>::~BasePVScalar() {}
template<typename T>
T BasePVScalar<T>::get() const { return value;}
template<typename T>
void BasePVScalar<T>::put(T val)
std::ostream& PVScalarValue<T>::dumpValue(std::ostream& o) const
{
value = val;
PVField::postPut();
return o << get();
}
template<typename T>
void BasePVScalar<T>::serialize(ByteBuffer *pbuffer,
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 {
pflusher->ensureBuffer(sizeof(T));
pbuffer->put(value);
pbuffer->put(storage.value);
}
template<>
void PVScalarValue<std::string>::serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher) const {
SerializeHelper::serializeString(storage.value, pbuffer, pflusher);
}
template<typename T>
void BasePVScalar<T>::deserialize(ByteBuffer *pbuffer,
void PVScalarValue<T>::deserialize(ByteBuffer *pbuffer,
DeserializableControl *pflusher)
{
pflusher->ensureData(sizeof(T));
value = pbuffer->GET(T);
storage.value = pbuffer->GET(T);
}
typedef BasePVScalar<boolean> BasePVBoolean;
typedef BasePVScalar<int8> BasePVByte;
typedef BasePVScalar<int16> BasePVShort;
typedef BasePVScalar<int32> BasePVInt;
typedef BasePVScalar<int64> BasePVLong;
typedef BasePVScalar<uint8> BasePVUByte;
typedef BasePVScalar<uint16> BasePVUShort;
typedef BasePVScalar<uint32> BasePVUInt;
typedef BasePVScalar<uint64> BasePVULong;
typedef BasePVScalar<float> BasePVFloat;
typedef BasePVScalar<double> BasePVDouble;
template<>
void PVScalarValue<std::string>::deserialize(ByteBuffer *pbuffer,
DeserializableControl *pflusher)
{
storage.value = SerializeHelper::deserializeString(pbuffer, pflusher);
// TODO: check for violations of maxLength?
}
// BasePVString is special case, since it implements SerializableArray
class BasePVString : public PVString {
public:
typedef string value_type;
typedef string* pointer;
typedef const string* const_pointer;
BasePVString(ScalarConstPtr const & scalar);
virtual ~BasePVString();
virtual string get() const ;
virtual void put(string val);
virtual void serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher) const;
virtual void deserialize(ByteBuffer *pbuffer,
DeserializableControl *pflusher);
virtual void serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher, size_t offset, size_t count) const;
private:
string value;
std::size_t maxLength;
};
BasePVString::BasePVString(ScalarConstPtr const & scalar)
: PVString(scalar),value()
PVString::PVString(ScalarConstPtr const & scalar)
: PVScalarValue<std::string>(scalar)
{
BoundedStringConstPtr boundedString = std::tr1::dynamic_pointer_cast<const BoundedString>(scalar);
if (boundedString.get())
maxLength = boundedString->getMaximumLength();
storage.maxLength = boundedString->getMaximumLength();
else
maxLength = 0;
storage.maxLength = 0;
}
BasePVString::~BasePVString() {}
string BasePVString::get() const { return value;}
void BasePVString::put(string val)
std::ostream& PVString::dumpValue(std::ostream& o) const
{
if (maxLength > 0 && val.length() > maxLength)
throw std::overflow_error("string too long");
value = val;
postPut();
// we escape, but do not quote, for scalar string
o<<escape(get());
return o;
}
void BasePVString::serialize(ByteBuffer *pbuffer,
/* mixing overrides (virtual functions) and overloads (different argument lists) is fun...
* we override all overloads to avoid the "hides overloaded virtual function" warning from clang.
* In this case we don't need/want to, so just delegate to the base class.
*/
void PVString::serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher) const
{
SerializeHelper::serializeString(value, pbuffer, pflusher);
}
{PVScalarValue<std::string>::serialize(pbuffer, pflusher);}
void BasePVString::deserialize(ByteBuffer *pbuffer,
DeserializableControl *pflusher)
{
value = SerializeHelper::deserializeString(pbuffer, pflusher);
}
void BasePVString::serialize(ByteBuffer *pbuffer,
void PVString::serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher, size_t offset, size_t count) const
{
// check bounds
const size_t length = /*(value == null) ? 0 :*/ value.length();
const size_t length = storage.value.length();
/*if (offset < 0) offset = 0;
else*/ if (offset > length) offset = length;
//if (count < 0) count = length;
@@ -197,10 +171,10 @@ void BasePVString::serialize(ByteBuffer *pbuffer,
count = maxCount;
// write
SerializeHelper::serializeSubstring(value, offset, count, pbuffer, pflusher);
SerializeHelper::serializeSubstring(storage.value, offset, count, pbuffer, pflusher);
}
void PVArray::checkLength(size_t len)
void PVArray::checkLength(size_t len) const
{
Array::ArraySizeType type = getArray()->getArraySizeType();
if (type != Array::variable)
@@ -213,61 +187,79 @@ void PVArray::checkLength(size_t len)
}
}
/** Default storage for arrays
*/
template<typename T>
class DefaultPVArray : public PVValueArray<T> {
public:
typedef T* pointer;
typedef const T* const_pointer;
typedef std::vector<T> vector;
typedef const std::vector<T> const_vector;
typedef std::tr1::shared_ptr<vector> shared_vector;
typedef ::epics::pvData::shared_vector<T> svector;
typedef ::epics::pvData::shared_vector<const T> const_svector;
DefaultPVArray(ScalarArrayConstPtr const & scalarArray);
virtual ~DefaultPVArray();
virtual size_t getLength() const {return value.size();}
virtual size_t getCapacity() const {return value.capacity();}
virtual void setCapacity(size_t capacity);
virtual void setLength(size_t length);
virtual const_svector view() const {return value;}
virtual void swap(const_svector &other);
virtual void replace(const const_svector& next);
// from Serializable
virtual void serialize(ByteBuffer *pbuffer,SerializableControl *pflusher) const;
virtual void deserialize(ByteBuffer *pbuffer,DeserializableControl *pflusher);
virtual void serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher, size_t offset, size_t count) const;
private:
const_svector value;
};
template<typename T>
DefaultPVArray<T>::DefaultPVArray(ScalarArrayConstPtr const & scalarArray)
: PVValueArray<T>(scalarArray),
value()
PVValueArray<T>::PVValueArray(ScalarArrayConstPtr const & scalarArray)
:base_t(scalarArray)
,value()
{}
PVValueArray<PVStructurePtr>::PVValueArray(StructureArrayConstPtr const & structureArray)
:base_t(structureArray)
,structureArray(structureArray)
{}
PVValueArray<PVUnionPtr>::PVValueArray(UnionArrayConstPtr const & unionArray)
:base_t(unionArray)
,unionArray(unionArray)
{}
template<typename T>
PVValueArray<T>::~PVValueArray() {}
template<typename T>
ArrayConstPtr PVValueArray<T>::getArray() const
{
ArrayConstPtr array = this->getArray();
if (array->getArraySizeType() == Array::fixed)
{
// this->setLength(array->getMaximumCapacity());
this->setCapacityMutable(false);
}
return std::tr1::static_pointer_cast<const Array>(this->getField());
}
template<typename T>
DefaultPVArray<T>::~DefaultPVArray()
{ }
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>
void DefaultPVArray<T>::setCapacity(size_t capacity)
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)
{
if(this->isCapacityMutable()) {
this->checkLength(capacity);
@@ -278,7 +270,7 @@ void DefaultPVArray<T>::setCapacity(size_t capacity)
}
template<typename T>
void DefaultPVArray<T>::setLength(size_t length)
void PVValueArray<T>::setLength(size_t length)
{
if(this->isImmutable())
THROW_EXCEPTION2(std::logic_error, "immutable");
@@ -295,7 +287,7 @@ void DefaultPVArray<T>::setLength(size_t length)
}
template<typename T>
void DefaultPVArray<T>::replace(const const_svector& next)
void PVValueArray<T>::replace(const const_svector& next)
{
this->checkLength(next.size());
@@ -304,7 +296,7 @@ void DefaultPVArray<T>::replace(const const_svector& next)
}
template<typename T>
void DefaultPVArray<T>::swap(const_svector &other)
void PVValueArray<T>::swap(const_svector &other)
{
if (this->isImmutable())
THROW_EXCEPTION2(std::logic_error, "immutable");
@@ -316,13 +308,13 @@ void DefaultPVArray<T>::swap(const_svector &other)
template<typename T>
void DefaultPVArray<T>::serialize(ByteBuffer *pbuffer,
void PVValueArray<T>::serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher) const {
serialize(pbuffer, pflusher, 0, this->getLength());
}
template<typename T>
void DefaultPVArray<T>::deserialize(ByteBuffer *pbuffer,
void PVValueArray<T>::deserialize(ByteBuffer *pbuffer,
DeserializableControl *pcontrol) {
size_t size = this->getArray()->getArraySizeType() == Array::fixed ?
@@ -371,7 +363,7 @@ void DefaultPVArray<T>::deserialize(ByteBuffer *pbuffer,
}
template<typename T>
void DefaultPVArray<T>::serialize(ByteBuffer *pbuffer,
void PVValueArray<T>::serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher, size_t offset, size_t count) const
{
//TODO: avoid incrementing the ref counter...
@@ -415,7 +407,7 @@ void DefaultPVArray<T>::serialize(ByteBuffer *pbuffer,
// specializations for string
template<>
void DefaultPVArray<string>::deserialize(ByteBuffer *pbuffer,
void PVValueArray<string>::deserialize(ByteBuffer *pbuffer,
DeserializableControl *pcontrol) {
size_t size = this->getArray()->getArraySizeType() == Array::fixed ?
@@ -442,7 +434,7 @@ void DefaultPVArray<string>::deserialize(ByteBuffer *pbuffer,
}
template<>
void DefaultPVArray<string>::serialize(ByteBuffer *pbuffer,
void PVValueArray<string>::serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher, size_t offset, size_t count) const {
const_svector temp(value);
@@ -458,18 +450,17 @@ void DefaultPVArray<string>::serialize(ByteBuffer *pbuffer,
}
}
typedef DefaultPVArray<boolean> DefaultPVBooleanArray;
typedef DefaultPVArray<int8> BasePVByteArray;
typedef DefaultPVArray<int16> BasePVShortArray;
typedef DefaultPVArray<int32> BasePVIntArray;
typedef DefaultPVArray<int64> BasePVLongArray;
typedef DefaultPVArray<uint8> BasePVUByteArray;
typedef DefaultPVArray<uint16> BasePVUShortArray;
typedef DefaultPVArray<uint32> BasePVUIntArray;
typedef DefaultPVArray<uint64> BasePVULongArray;
typedef DefaultPVArray<float> BasePVFloatArray;
typedef DefaultPVArray<double> BasePVDoubleArray;
typedef DefaultPVArray<string> BasePVStringArray;
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
@@ -564,29 +555,29 @@ PVScalarPtr PVDataCreate::createPVScalar(ScalarConstPtr const & scalar)
ScalarType scalarType = scalar->getScalarType();
switch(scalarType) {
case pvBoolean:
return PVScalarPtr(new BasePVBoolean(scalar));
return PVScalarPtr(new PVBoolean(scalar));
case pvByte:
return PVScalarPtr(new BasePVByte(scalar));
return PVScalarPtr(new PVByte(scalar));
case pvShort:
return PVScalarPtr(new BasePVShort(scalar));
return PVScalarPtr(new PVShort(scalar));
case pvInt:
return PVScalarPtr(new BasePVInt(scalar));
return PVScalarPtr(new PVInt(scalar));
case pvLong:
return PVScalarPtr(new BasePVLong(scalar));
return PVScalarPtr(new PVLong(scalar));
case pvUByte:
return PVScalarPtr(new BasePVUByte(scalar));
return PVScalarPtr(new PVUByte(scalar));
case pvUShort:
return PVScalarPtr(new BasePVUShort(scalar));
return PVScalarPtr(new PVUShort(scalar));
case pvUInt:
return PVScalarPtr(new BasePVUInt(scalar));
return PVScalarPtr(new PVUInt(scalar));
case pvULong:
return PVScalarPtr(new BasePVULong(scalar));
return PVScalarPtr(new PVULong(scalar));
case pvFloat:
return PVScalarPtr(new BasePVFloat(scalar));
return PVScalarPtr(new PVFloat(scalar));
case pvDouble:
return PVScalarPtr(new BasePVDouble(scalar));
return PVScalarPtr(new PVDouble(scalar));
case pvString:
return PVScalarPtr(new BasePVString(scalar));
return PVScalarPtr(new PVString(scalar));
}
throw std::logic_error("PVDataCreate::createPVScalar should never get here");
}
@@ -611,29 +602,29 @@ PVScalarArrayPtr PVDataCreate::createPVScalarArray(
{
switch(scalarArray->getElementType()) {
case pvBoolean:
return PVScalarArrayPtr(new DefaultPVBooleanArray(scalarArray));
return PVScalarArrayPtr(new PVBooleanArray(scalarArray));
case pvByte:
return PVScalarArrayPtr(new BasePVByteArray(scalarArray));
return PVScalarArrayPtr(new PVByteArray(scalarArray));
case pvShort:
return PVScalarArrayPtr(new BasePVShortArray(scalarArray));
return PVScalarArrayPtr(new PVShortArray(scalarArray));
case pvInt:
return PVScalarArrayPtr(new BasePVIntArray(scalarArray));
return PVScalarArrayPtr(new PVIntArray(scalarArray));
case pvLong:
return PVScalarArrayPtr(new BasePVLongArray(scalarArray));
return PVScalarArrayPtr(new PVLongArray(scalarArray));
case pvUByte:
return PVScalarArrayPtr(new BasePVUByteArray(scalarArray));
return PVScalarArrayPtr(new PVUByteArray(scalarArray));
case pvUShort:
return PVScalarArrayPtr(new BasePVUShortArray(scalarArray));
return PVScalarArrayPtr(new PVUShortArray(scalarArray));
case pvUInt:
return PVScalarArrayPtr(new BasePVUIntArray(scalarArray));
return PVScalarArrayPtr(new PVUIntArray(scalarArray));
case pvULong:
return PVScalarArrayPtr(new BasePVULongArray(scalarArray));
return PVScalarArrayPtr(new PVULongArray(scalarArray));
case pvFloat:
return PVScalarArrayPtr(new BasePVFloatArray(scalarArray));
return PVScalarArrayPtr(new PVFloatArray(scalarArray));
case pvDouble:
return PVScalarArrayPtr(new BasePVDoubleArray(scalarArray));
return PVScalarArrayPtr(new PVDoubleArray(scalarArray));
case pvString:
return PVScalarArrayPtr(new BasePVStringArray(scalarArray));
return PVScalarArrayPtr(new PVStringArray(scalarArray));
}
throw std::logic_error("PVDataCreate::createPVScalarArray should never get here");
@@ -724,23 +715,51 @@ PVUnionPtr PVDataCreate::createPVUnion(PVUnionPtr const & unionToClone)
return punion;
}
// TODO not thread-safe (local static initializers)
// TODO replace with non-locking singleton pattern
PVDataCreatePtr PVDataCreate::getPVDataCreate()
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*)
{
static PVDataCreatePtr pvDataCreate;
static Mutex mutex;
Lock xx(mutex);
if(pvDataCreate.get()==0) pvDataCreate = PVDataCreatePtr(new PVDataCreate());
return pvDataCreate;
try {
pvfield_factory_s = new detail::pvfield_factory;
}catch(std::exception& e){
std::cerr<<"Error initializing getFieldCreate() : "<<e.what()<<"\n";
}
}
PVDataCreatePtr getPVDataCreate() {
return PVDataCreate::getPVDataCreate();
const PVDataCreatePtr& PVDataCreate::getPVDataCreate()
{
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
// builds emit exported symbols for inline'd methods
template class PVScalarValue<boolean>;
template class PVScalarValue<int8>;
template class PVScalarValue<uint8>;
template class PVScalarValue<int16>;
template class PVScalarValue<uint16>;
template class PVScalarValue<int32>;
template class PVScalarValue<uint32>;
template class PVScalarValue<int64>;
template class PVScalarValue<uint64>;
template class PVScalarValue<float>;
template class PVScalarValue<double>;
template class PVScalarValue<std::string>;
}} // namespace epics::pvData
namespace std{
std::ostream& operator<<(std::ostream& o, const epics::pvData::PVField *ptr)

View File

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

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

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

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

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

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

View File

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

10
src/json/Makefile Normal file
View File

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

284
src/json/parseany.cpp Normal file
View File

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

122
src/json/parsehelper.cpp Normal file
View File

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

352
src/json/parseinto.cpp Normal file
View File

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

236
src/json/print.cpp Normal file
View File

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

154
src/json/pv/json.h Normal file
View File

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

View File

@@ -4,7 +4,6 @@ SRC_DIRS += $(PVDATA_SRC)/misc
INC += pv/noDefaultMethods.h
INC += pv/lock.h
INC += pv/requester.h
INC += pv/serialize.h
INC += pv/bitSet.h
INC += pv/byteBuffer.h
@@ -12,32 +11,31 @@ 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/messageQueue.h
INC += pv/destroyable.h
INC += pv/status.h
INC += pv/sharedPtr.h
INC += pv/debugPtr.h
INC += pv/localStaticLock.h
INC += pv/typeCast.h
INC += pv/sharedVector.h
INC += pv/templateMeta.h
INC += pv/current_function.h
INC += pv/pvUnitTest.h
INC += pv/reftrack.h
INC += pv/anyscalar.h
LIBSRCS += byteBuffer.cpp
LIBSRCS += bitSet.cpp
LIBSRCS += epicsException.cpp
LIBSRCS += requester.cpp
LIBSRCS += serializeHelper.cpp
LIBSRCS += event.cpp
LIBSRCS += executor.cpp
LIBSRCS += timeFunction.cpp
LIBSRCS += timer.cpp
LIBSRCS += status.cpp
LIBSRCS += messageQueue.cpp
LIBSRCS += localStaticLock.cpp
LIBSRCS += typeCast.cpp
LIBSRCS += 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

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

165
src/misc/debugPtr.cpp Normal file
View File

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

View File

@@ -1,98 +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>
#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

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

View File

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

251
src/misc/pv/anyscalar.h Normal file
View File

@@ -0,0 +1,251 @@
#ifndef PV_ANYSCALAR_H
#define PV_ANYSCALAR_H
#if __cplusplus>=201103L
# include <type_traits>
#endif
#include <ostream>
#include <exception>
#include <map>
#include <epicsAssert.h>
#include <pv/templateMeta.h>
#include <pv/typeCast.h>
#include <pv/pvIntrospect.h> /* for ScalarType enum */
namespace epics{namespace pvData{
namespace detail {
// special mangling for AnyScalar ctor to map from argument type to storage type.
// allow construction from constants.
template <typename T>
struct any_storage_type { typedef T type; };
template<> struct any_storage_type<int> { typedef int32 type; };
template<> struct any_storage_type<unsigned> { typedef uint32 type; };
template<> struct any_storage_type<char*> { typedef std::string type; };
template<> struct any_storage_type<const char*> { typedef std::string type; };
#if __cplusplus>=201103L
// std::max() isn't constexpr until c++14 :(
constexpr size_t cmax(size_t A, size_t B) {
return A>B ? A : B;
}
#endif
}// namespace detail
/** A type-safe variant union capable of holding
* any of the PVD scalar types (POD or string)
*
@code
AnyScalar A(5);
assert(A.type()==pvInt);
assert(A.ref<int32>()==5);
assert(A.as<int32>()==5);
assert(A.as<double>()==5.0);
assert(A.ref<double>()==5.0); // throws AnyScalar::bad_cast
@endcode
*/
class epicsShareClass AnyScalar {
public:
struct bad_cast : public std::exception {
#if __cplusplus>=201103L
bad_cast() noexcept {}
virtual ~bad_cast() noexcept {}
virtual const char* what() const noexcept
#else
bad_cast() throw() {}
virtual ~bad_cast() throw() {}
virtual const char* what() const throw()
#endif
{ return "bad_cast() type mis-match"; }
};
private:
ScalarType _stype;
// always reserve enough storage for std::string or double (assumed worst case)
#if __cplusplus>=201103L
struct wrap_t {
typename std::aligned_storage<detail::cmax(sizeof(std::string), sizeof(double)),
detail::cmax(alignof(std::string), alignof(double))
>::type blob[1];
} _wrap;
#else
struct wrap_t {
union blob_t {
char data[sizeof(std::string)];
double align_f; // assume std::string alignment <= 8
} blob[1];
} _wrap;
#endif
// assumed largest non-string type
typedef double _largest_blob;
template<typename T>
inline T& _as() {
return *reinterpret_cast<T*>(_wrap.blob);
}
template<typename T>
inline const T& _as() const {
return *reinterpret_cast<const T*>(_wrap.blob);
}
public:
//! Construct empty
//! @post empty()==true
AnyScalar() : _stype((ScalarType)-1) {}
//! Construct from provided value.
template<typename T>
explicit AnyScalar(T v)
{
typedef typename meta::strip_const<T>::type T2;
typedef typename detail::any_storage_type<T2>::type TT;
STATIC_ASSERT(sizeof(TT)<=sizeof(_wrap.blob));
new (_wrap.blob) TT(v);
// this line fails to compile when type T can't be mapped to one of
// the PVD scalar types.
_stype = (ScalarType)ScalarTypeID<TT>::value;
}
//! 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);
#endif
inline ~AnyScalar() {clear();}
inline AnyScalar& operator=(const AnyScalar& o) {
AnyScalar(o).swap(*this);
return *this;
}
template<typename T>
inline AnyScalar& operator=(T v) {
AnyScalar(v).swap(*this);
return *this;
}
#if __cplusplus>=201103L
inline AnyScalar& operator=(AnyScalar&& o) {
clear();
swap(o);
return *this;
}
#endif
//! Reset internal state.
//! @version Added after 7.0.0
//! @post empty()==true
void clear();
void swap(AnyScalar& o);
//! Type code of contained value. Or (ScalarType)-1 is empty.
inline ScalarType type() const {
return _stype;
}
inline void* unsafe() { return _wrap.blob; }
inline const void* unsafe() const { return _wrap.blob; }
inline bool empty() const { return _stype==(ScalarType)-1; }
#if __cplusplus>=201103L
explicit operator bool() const { return !empty(); }
#else
private:
typedef void (AnyScalar::*bool_type)(AnyScalar&);
public:
operator bool_type() const { return !empty() ? &AnyScalar::swap : 0; }
#endif
//! Provide read-only access to underlying buffer.
//! For a string this is std::string::c_str().
//! @version Added after 7.0.0
const void* bufferUnsafe() const;
/** Return typed reference to wrapped value. Non-const reference allows value modification
*
* @throws bad_cast when the requested type does not match the stored type
@code
AnyScalar v(42);
v.ref<uint32>() = 43;
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() {
typedef typename meta::strip_const<T>::type T2;
typedef typename detail::any_storage_type<T2>::type TT;
if(_stype!=(ScalarType)ScalarTypeID<TT>::value)
throw bad_cast();
return reinterpret_cast<TT&>(_wrap.blob);
}
/** Return typed reference to wrapped value. Const reference does not allow modification.
*
* @throws bad_cast when the requested type does not match the stored type
@code
const 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 {
typedef typename meta::strip_const<T>::type T2;
typedef typename detail::any_storage_type<T2>::type TT;
if(_stype!=(ScalarType)ScalarTypeID<TT>::value)
throw bad_cast();
return reinterpret_cast<typename meta::decorate_const<TT>::type&>(_wrap.blob);
}
/** copy out wrapped value, with a value conversion.
*
* @throws bad_cast when empty()==true
*/
template<typename T>
inline
T as() const {
typedef typename meta::strip_const<T>::type T2;
typedef typename detail::any_storage_type<T2>::type TT;
if(_stype==(ScalarType)-1)
throw bad_cast();
TT ret;
castUnsafeV(1, (ScalarType)ScalarTypeID<T2>::value, &ret,
_stype, _wrap.blob);
return ret;
}
private:
friend epicsShareFunc std::ostream& operator<<(std::ostream& strm, const AnyScalar& v);
};
epicsShareExtern
std::ostream& operator<<(std::ostream& strm, const AnyScalar& v);
}} // namespace epics::pvData
#endif // PV_ANYSCALAR_H

View File

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

File diff suppressed because it is too large Load Diff

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

View File

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

View File

@@ -33,10 +33,6 @@
#ifndef EPICSEXCEPTION_H_
#define EPICSEXCEPTION_H_
#if defined(_WIN32) && !defined(NOMINMAX)
#define NOMINMAX
#endif
#include <stdexcept>
#include <string>
#include <cstdio>
@@ -181,7 +177,7 @@ do { \
#define PRINT_EXCEPTION(EI) PRINT_EXCEPTION2(EI,stderr)
#ifndef __GNUC__
#if !defined(__GNUC__) || __GNUC__ < 4
# define SHOW_EXCEPTION(EI) ::epics::pvData::detail::showException(EI)
#else
# define SHOW_EXCEPTION(EI) \

View File

@@ -1,90 +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 <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 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 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

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

View File

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

View File

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

188
src/misc/pv/pvUnitTest.h Normal file
View File

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

View File

@@ -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 <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 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:
queueElementPtr nullElement;
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 nullElement;
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 nullElement;
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 */

173
src/misc/pv/reftrack.h Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -11,6 +11,7 @@
#include <memory>
#include <sstream>
#include <stdexcept>
#if __cplusplus>=201103L
#include <functional>
@@ -41,37 +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 epicsShareClass RunnableMethod : public Runnable, private NoDefaultMethods
{
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
{
@@ -85,26 +56,14 @@ 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 epicsShareClass Thread : public epicsThread, private NoDefaultMethods {
class epicsShareClass Thread : public epicsThread {
EPICS_NOT_COPYABLE(Thread)
public:
/** @brief Holds all the configuration necessary to launch a @class Thread
*
@@ -139,56 +98,31 @@ public:
std::ostringstream p_strm;
bool p_autostart;
Runnable *p_runner;
#if __cplusplus>=201103L
typedef std::unique_ptr<Runnable> p_owned_runner_t;
#else
typedef std::auto_ptr<Runnable> p_owned_runner_t;
#endif
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)())
@@ -198,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<<
@@ -229,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);
/**
*
@@ -256,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,70 +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 <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 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 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,31 +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;
Event waitForDone;
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

53
src/misc/pvUnitTest.cpp Normal file
View File

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

299
src/misc/reftrack.cpp Normal file
View File

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

View File

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

View File

@@ -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();
}
}
}

View File

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

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,51 +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>
#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,67 +28,52 @@ TimerCallback::TimerCallback()
}
Timer::Timer(string threadName,ThreadPriority priority)
: waitForWork(false),
waitForDone(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;
@@ -100,63 +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);
}
}
waitForDone.signal();
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();
waitForDone.wait();
TimerCallbackPtr timerCallback;
while(true) {
timerCallback = head;
if(head.get()==NULL) break;
thread.exitWait();
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;
}
}
@@ -172,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)
@@ -55,186 +55,15 @@ template<typename T>
static void copyV(size_t count, void *draw, const void *sraw)
{
T *dest=(T*)draw;
const T *src=(T*)sraw;
const T *src=(const T*)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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

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