176 Commits

Author SHA1 Message Date
Andrew Johnson
e069c699e9 Final commit for 8.0.7 2025-12-15 11:44:53 -06:00
Andrew Johnson
8963b2dba1 Add release notes for changes since 8.0.6
- Jeremy Lorelli
- Andrew Johnson
- Dirk Zimoch
- Chris Johns
- Michael Davidsaver
2025-12-15 11:37:47 -06:00
Michael Davidsaver
9ec9a526f5 revise Timer phase reset 2025-12-05 17:01:00 -08:00
Chris Johns
b4114589ff Skip any missed timer events updating the timer to now
This stops a timer calling the callback for every missed event if the
time jumps forward when corrected or moved.
2025-12-05 17:01:00 -08:00
6689c9ce1f Allow testing deprecated functions without causing compiler warnings 2025-12-03 16:47:55 -08:00
Andrew Johnson
0c1773f25d Clang warnings: sprintf() => epicsSnprintf() 2025-12-01 18:20:27 -08:00
Andrew Johnson
d0ff9a9592 CI: Final GHA config adjustments
Don't change the MSC C++ standard when building submodules!
2025-12-01 17:39:24 -06:00
JJL772
bf36070860 Allow Timer to be cancelled during callback execution
Useful for when e.g. a periodic timer needs to cancel itself
2025-11-19 08:49:41 -08:00
Andrew Johnson
5552773e5e CI: Rename and adjust jobs (#102)
* Update Appveyor and GHA CI build configurations to more closely match Base CI.

* Other CI script clean-up
2025-11-10 12:29:19 -06:00
Andrew Johnson
b3a1077270 CI: Update Ubuntu 20.04 to 22.04 2025-05-05 12:26:24 -05:00
Ralph Lange
1c5f75bcd6 Merge pull request #97 from ralphlange/fix-ci
ci: consistent AppVeyor job config for pvData/pvAccess
2024-12-25 09:09:33 +01:00
Ralph Lange
2455039594 ci: consistent AppVeyor job config for pvData/pvAccess 2024-12-24 17:19:59 +01:00
Ralph Lange
f47676c225 Merge pull request #96 from ralphlange/fix-ci
Fix CI issues
2024-12-19 14:42:15 +01:00
Ralph Lange
0881e0ed86 ci: update appveyor.yml from base 2024-12-19 08:48:41 +01:00
Ralph Lange
e457b60b30 ci: Update GHA workflow from base
- Run Fedora in containers
- Add Linux cross builds
2024-12-18 21:32:11 +01:00
Ralph Lange
a6746f8161 GHA: Update actions/upload-artifact to v4 2024-12-16 12:08:55 +01:00
Ralph Lange
144f0228cc ci: update ci-scripts to v3.4.1 2023-12-15 09:40:45 +01:00
Andrew Johnson
7300e6b0bd Set next development version 2023-12-13 14:52:07 -06:00
Andrew Johnson
0ef8a36172 Set version numbers for release 2023-12-13 14:46:12 -06:00
Andrew Johnson
c0be043aaf Replease UNRELEASED 2023-12-13 14:43:07 -06:00
Ralph Lange
dd74289eaf Silence warning about uninitialized local variable
found by static code analysis (cppcheck @ sonarqube)
(that doesn't realize ">>=" calls an overloaded operator)
2023-11-01 09:41:07 -05:00
thomasms
3da69257a0 Remove duplicate doxygen comment - see issue #75 2023-11-01 09:39:33 -05:00
Michael Davidsaver
13e4e577bb gha update 2023-10-22 16:24:15 -07:00
Michael Davidsaver
5387face45 rename print.cpp -> jprint.cpp
In GHA builder somehow print.cpp becomes PRINT.obj
instead of print.obj for mkmf.pl, which later fails
when print.obj is missing.  (apparently windows
filesystems are now case sensitive...)
2023-10-22 16:24:15 -07:00
JJL772
eac2a8e70f Fix use-after-destroy in epicsRefSnapshopCurrent 2023-08-06 21:30:32 -07:00
JJL772
04fcb7e38f Don't return local copy of std::string in AnyScalar::bufferUnsafe 2023-08-06 21:30:32 -07:00
Andrew Johnson
45018a2163 Generate JSON5 when available
Fixes lp: #2029482 / GitHub #92
2023-08-06 21:28:01 -07:00
Simon Rose
b7ad4478a4 Update debugPtr to work with EPICS base 7.0.7
There are at least two changes to EPICS base since the addition of
debugPtr:
* In pvAccess/**/dbdToPv.cpp, a write to an ostream was added (also in
  pvData/**/testSerialization.cpp) which does not resolve correctly when
  the operator<< overload is in the global namespace.
* In pvAccess/**/caChannel.cpp, weak_ptr->expired() was added

The interface to deal with each of these has been added.
2023-05-29 17:09:29 -07:00
Michael Davidsaver
c16f19c80e Flip #if logic for unaligned access
Assume only x86 can correctly/efficiently handle unaligned access.
2023-05-24 21:49:38 -07:00
Michael Davidsaver
87018882d1 ARM/Linux can fault on unaligned access
Sometimes SIGBUS results from unaligned access.
2023-05-24 20:44:32 -07:00
2547514abb fix for use-after-free warning 2023-02-28 09:04:58 -08:00
Michael Davidsaver
9447eacbd2 gha update 2022-11-26 11:15:37 -08:00
Michael Davidsaver
0b424a71ec isprint() wants value in range [-1, 255]
The MSVC impl. assert()s this
2022-11-26 10:52:03 -08:00
Andrew Johnson
45671faaea Set next development version 2022-09-07 10:51:29 -05:00
Andrew Johnson
016d1154fc Set module version number for release 2022-09-07 10:17:07 -05:00
Michael Davidsaver
f3911d5831 add 3.16 build 2022-01-27 09:44:54 -08:00
Michael Davidsaver
93e90c5a04 fix 3.16 2022-01-27 09:27:19 -08:00
Michael Davidsaver
8039c75b3e .ci: update 2022-01-27 07:51:17 -08:00
Michael Davidsaver
a647c8b174 printJSON() use yajl_gen 2022-01-19 10:27:09 -08:00
Michael Davidsaver
d3b4976ea2 Set next development version 2021-02-25 09:36:05 -08:00
Michael Davidsaver
b1c8303870 Update version numbers for release 2021-02-25 09:35:12 -08:00
Michael Davidsaver
a3ef984f4f ci: switch travis -> github 2020-12-20 11:08:29 -08:00
Michael Davidsaver
ca86a63180 update release notes 2020-11-14 13:43:38 -08:00
Michael Davidsaver
07b79693af Deprecate/remove unused buffer alignment tools
ByteBuffer::align() does not work as expected
on some RTEMS targets where malloc() returns
buffers aligned only to 4 bytes.

SerializableControl::alignBuffer() and
DeserializableControl::alignData() are implemented,
but never called, in this module and pvAccessCPP.
ByteBuffer::align() is only needed to implement
these two methods.

Leave non-pure virtual stubs to assist in migration.
2020-11-14 11:07:15 -08:00
Michael Davidsaver
4ef9e18ac6 enable RTEMS CI testing 2020-11-13 20:51:23 -08:00
Michael Davidsaver
320cc3c60b shared_vector_convert<>() fix convert of empty array
Empty arrays can be untyped, but this doesn't matter.
2020-09-29 15:52:49 -07:00
Michael Davidsaver
45d0d76a7f Set next development version 2020-07-26 13:30:34 -07:00
Michael Davidsaver
b2b42d5f8c Update version numbers for release 2020-07-26 13:29:52 -07:00
Michael Davidsaver
90b1f2f6da doc 2020-06-17 14:08:14 -07:00
Michael Davidsaver
2e775ef2fb drop THROW_BASE_EXCEPTION_CAUSE() 2020-06-17 14:08:14 -07:00
Michael Davidsaver
f17d2bbca1 add maybeQuote()
Something for the *NIX gurus
to light their pipes with.
2020-06-17 14:08:14 -07:00
Michael Davidsaver
79b02254c4 update ci-scripts to 3.0.1 2020-06-17 14:06:50 -07:00
Michael Davidsaver
19245ce805 sharedVector extend base_ptr() to VS2013 2020-06-03 10:14:38 -07:00
Andrew Johnson
0fa927afa7 Set next development version 2020-05-28 16:26:00 -05:00
Andrew Johnson
d9b3f98e35 Update version numbers for release 2020-05-28 13:37:15 -05:00
Michael Davidsaver
81e7968230 doc 2020-05-20 14:54:39 -07:00
Michael Davidsaver
06f3b96992 ci-scripts 2.3.2 (with appveyor) 2020-04-27 12:18:16 -07:00
1038182a16 removed empty lines at end of file 2020-04-15 07:53:22 -07:00
60091bfe56 removed spaces at end of line 2020-04-15 07:53:22 -07:00
f4de6dd9b1 replaced tabs with spaces 2020-04-15 07:53:22 -07:00
Michael Davidsaver
3d93a80cce travis switch to ci-scripts 2019-12-20 10:33:03 -08:00
Michael Davidsaver
94eff54fa9 Revert "Properly declare isnan() in C++ testcode"
This reverts commit 07b75e4543.

std:: qualified names not portable until c++11
epicsMath.h changed to ensure that unqualified
names continue to work.
2019-12-19 18:50:33 -08:00
Michael Davidsaver
e9dde4d2f8 update doc 2019-12-13 09:32:42 -08:00
Michael Davidsaver
0a5e22e5b0 testPVData print additional sizes 2019-12-13 09:32:42 -08:00
Michael Davidsaver
3a5cfba4e1 catch exception by ref 2019-12-13 09:32:42 -08:00
Ralph Lange
07b75e4543 Properly declare isnan() in C++ testcode 2019-12-11 13:29:27 +01:00
Michael Davidsaver
e4f150b34f finalize doxygen 2019-11-03 20:03:24 -08:00
Andrew Johnson
ca2ae0d0e7 Incr version and set development flag after release 2019-10-31 17:04:21 -05:00
Andrew Johnson
828506720c Clear development flag for 8.0.1 release 2019-10-31 16:57:41 -05:00
Andrew Johnson
ad712b63f9 Sub-bullets must be indented by at least 2 spaces 2019-10-31 16:52:53 -05:00
Michael Davidsaver
4c3d5a788d release notes for 8.0.1 2019-10-31 10:34:52 -07:00
Michael Davidsaver
df89135455 cleanup 2019-10-11 16:17:49 -07:00
Michael Davidsaver
95d452870c move dedup to most derived
Making virtual calls from a base class dtor is not safe.
2019-10-11 16:17:25 -07:00
Michael Davidsaver
8a82ff9fe4 Revert "dynamic_cast may not be defined during dtor"
This reverts commit dd24b2ad75.
2019-10-11 08:41:30 -07:00
Michael Davidsaver
dd24b2ad75 dynamic_cast may not be defined during dtor 2019-09-30 18:24:13 -07:00
Michael Davidsaver
17fa7a7724 travis matrix build
switch to matrix build to test multiple ubuntu versions
2019-09-23 13:41:23 -07:00
Michael Davidsaver
d01ba94ed7 avoid extraneous copy 2019-09-23 10:14:55 -07:00
Michael Davidsaver
c7c7585950 release notes 2019-09-23 10:14:55 -07:00
Michael Davidsaver
f869936cfd travis drop 3.14 build 2019-09-23 10:14:55 -07:00
Michael Davidsaver
0ad253c6f0 Remove Base 3.14 compatibility 2019-09-23 10:14:55 -07:00
Michael Davidsaver
52d04d5044 note class sizes 2019-09-23 10:14:55 -07:00
Ralph Lange
bff2dc9cd2 rtd-ci: add read-the-docs integration 2019-09-06 11:27:03 +02:00
Andrew Johnson
b903df5d0d Update version number to 8.0.1 DEVELOPMENT 2019-08-13 11:11:57 -05:00
Michael Davidsaver
416d910577 Timer: further attempt to avoid state corruption 2019-07-31 20:13:33 -07:00
Michael Davidsaver
37b7a0708f Timer more alive checks 2019-07-31 19:01:20 -07:00
Michael Davidsaver
6ceaa6adb0 update release notes for 8.0.0 2019-07-24 11:19:30 -07:00
Michael Davidsaver
0447826e7c minor 2019-07-14 19:09:54 -07:00
Michael Davidsaver
3ef60a61a2 always epicsThreadStackBig
On RTEMS at least, c++ code needs the largest
standard stack frame size.
2019-07-08 09:07:09 -07:00
Daniel Damiani
31802a8bde Added additional explicit template instantiations to fix build problems on macos with c+11 2019-07-04 07:09:58 -07:00
Bruno Martins
c2bc77a649 Improve and add new tests for testIntrospect 2019-05-20 16:35:59 -07:00
Bruno Martins
4dd7a18301 Improve docs. Make getFieldT(offset) use vector::at. 2019-05-20 16:35:59 -07:00
Bruno Martins
a29894ee2b getField<T>() fail to compile when T isn't Field or a sub-class 2019-05-20 16:35:59 -07:00
Bruno Martins
2f8ac7f673 Fix tests with expected exceptions for getField(size_t) 2019-05-20 16:35:59 -07:00
Bruno Martins
b050fbbcbe Revert {Structure,Union}::getField to previous behavior 2019-05-20 16:35:59 -07:00
Bruno Martins
caa11605fc Add {Structure,Union}::getFieldT, fix {Structure,Union}::getField 2019-05-20 16:35:59 -07:00
Michael Davidsaver
cfcdd1a3f9 deprecate ByteBuffer::getArray()
in favor of identical ByteBuffer::getBuffer()
2019-05-18 18:21:06 -07:00
Michael Davidsaver
95ff606ba1 quiet (un)signed comparison warning 2019-05-16 10:17:56 -07:00
Michael Davidsaver
4cc9b650c5 getSubField<T>() fail to compile when T isn't PVField or a sub-class 2019-05-16 10:17:56 -07:00
Michael Davidsaver
cd2436342d Revert "Replace display.format with .form and .precision"
This reverts commit 4ffddfa2f6.
2019-05-06 10:40:54 -07:00
Michael Davidsaver
3ae2d09fe3 bump module version after removals 2019-05-01 14:43:33 -07:00
Michael Davidsaver
35b3403de6 remove deprecated pvCopy 2019-05-01 14:18:48 -07:00
Michael Davidsaver
f780ebdf76 remove deprecated localStaticLock 2019-05-01 14:06:33 -07:00
Michael Davidsaver
93f0518b4b Merge remote-tracking branch 'md/display-format'
* md/display-format:
  Replace display.format with .form and .precision
2019-05-01 14:03:57 -07:00
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
4ffddfa2f6 Replace display.format with .form and .precision
Remove instance accessors w/o replacement
as a prelude to deprecation.
2019-01-16 09:06:15 -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
126 changed files with 5145 additions and 4380 deletions

113
.appveyor.yml Normal file
View File

@@ -0,0 +1,113 @@
# .appveyor.yml for use with EPICS Base ci-scripts
# (see: https://github.com/epics-base/ci-scripts)
# This is YAML - indentation levels are crucial
#---------------------------------#
# build cache #
#---------------------------------#
# The AppVeyor cache allowance is way too small (1GB per account across all
# projects, branches and jobs) to be used for the dependency builds.
cache:
- C:\Users\appveyor\.tools
#---------------------------------#
# repository cloning #
#---------------------------------#
init:
# Set autocrlf to make batch files work
- cmd: git config --global core.autocrlf true
clone_depth: 5
#---------------------------------#
# build matrix configuration #
#---------------------------------#
# Build Configurations: dll/static, regular/debug
configuration:
- dynamic
- static
- dynamic-debug
- static-debug
# Environment variables: compiler toolchain, base version, setup file, ...
environment:
# common / default variables for all jobs
SETUP_PATH: .ci-local
EPICS_TEST_IMPRECISE_TIMING: YES
BASE: 7.0
matrix:
- CMP: vs2019
BASE: 3.15
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
- CMP: vs2019
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
- CMP: vs2017
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
- CMP: vs2015
- CMP: gcc
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
# TODO: static linking w/ readline isn't working. Bypass auto-detect
COMMANDLINE_LIBRARY: EPICS
# Platform: processor architecture
platform: x64
# Matrix configuration: exclude sets of jobs
matrix:
exclude:
# MinGW debug builds use the same libraries, unlike VS
- configuration: dynamic-debug
CMP: gcc
- configuration: static-debug
CMP: gcc
#---------------------------------#
# building & testing #
#---------------------------------#
install:
- cmd: git submodule update --init --recursive
- cmd: pip install git+https://github.com/mdavidsaver/ci-core-dumper#egg=ci-core-dumper
- cmd: python .ci/cue.py prepare
build_script:
- cmd: python .ci/cue.py build
test_script:
- cmd: python -m ci_core_dumper install
- cmd: python .ci/cue.py -T 20M test
on_finish:
- ps: Get-ChildItem *.tap -Recurse -Force | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name }
- cmd: python .ci/cue.py -T 5M test-results
on_failure:
- cmd: python -m ci_core_dumper report
#---------------------------------#
# debugging #
#---------------------------------#
## To connect by remote desktop to a failed build, uncomment the lines below.
## You must connect within the usual build timeout limit (60 minutes),
## so adjust the build matrix above to just build the config of interest.
#on_failure:
# - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
# - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
#---------------------------------#
# notifications #
#---------------------------------#
notifications:
- provider: Email
to:
- core-talk@aps.anl.gov
on_build_success: false
- provider: GitHubPullRequest

1
.ci Submodule

Submodule .ci added at 20f8e05393

6
.ci-local/defaults.set Normal file
View File

@@ -0,0 +1,6 @@
# EPICS Base
BASE_DIRNAME=base
BASE_REPONAME=epics-base
BASE_REPOOWNER=epics-base
BASE_VARNAME=EPICS_BASE
BASE_RECURSIVE=NO

View File

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

View File

@@ -1,138 +0,0 @@
#!/bin/sh
set -e -x
CURDIR="$PWD"
QDIR="$HOME/.cache/qemu"
if [ -n "$RTEMS" -a "$TEST" = "YES" ]
then
git clone --quiet --branch vme --depth 10 https://github.com/mdavidsaver/qemu.git "$HOME/.build/qemu"
cd "$HOME/.build/qemu"
HEAD=`git log -n1 --pretty=format:%H`
echo "HEAD revision $HEAD"
[ -e "$HOME/.cache/qemu/built" ] && BUILT=`cat "$HOME/.cache/qemu/built"`
echo "Cached revision $BUILT"
if [ "$HEAD" != "$BUILT" ]
then
echo "Building QEMU"
git submodule --quiet update --init
install -d "$HOME/.build/qemu/build"
cd "$HOME/.build/qemu/build"
"$HOME/.build/qemu/configure" --prefix="$HOME/.cache/qemu/usr" --target-list=i386-softmmu --disable-werror
make -j2
make install
echo "$HEAD" > "$HOME/.cache/qemu/built"
fi
fi
cd "$CURDIR"
cat << EOF > configure/RELEASE.local
EPICS_BASE=$HOME/.source/epics-base
EOF
install -d "$HOME/.source"
cd "$HOME/.source"
add_base_module() {
MODULE=$1
BRANCH=$2
( cd epics-base/modules && \
git clone --quiet --depth 5 --branch "$MODULE"/"$BRANCH" https://github.com/${REPOBASE:-epics-base}/epics-base.git "$MODULE" && \
cd "$MODULE" && git log -n1 )
}
if [ "$BRBASE" ]
then
git clone --quiet --depth 5 --branch "$BRBASE" https://github.com/${REPOBASE:-epics-base}/epics-base.git epics-base
(cd epics-base && git log -n1 )
else
git clone --quiet --depth 5 --branch core/"${BRCORE:-master}" https://github.com/${REPOBASE:-epics-base}/epics-base.git epics-base
( cd epics-base && git log -n1 )
add_base_module libcom "${BRLIBCOM:-master}"
fi
EPICS_HOST_ARCH=`sh epics-base/startup/EpicsHostArch`
# requires wine and g++-mingw-w64-i686
if [ "$WINE" = "32" ]
then
echo "Cross mingw32"
sed -i -e '/CMPLR_PREFIX/d' epics-base/configure/os/CONFIG_SITE.linux-x86.win32-x86-mingw
cat << EOF >> epics-base/configure/os/CONFIG_SITE.linux-x86.win32-x86-mingw
CMPLR_PREFIX=i686-w64-mingw32-
EOF
cat << EOF >> epics-base/configure/CONFIG_SITE
CROSS_COMPILER_TARGET_ARCHS+=win32-x86-mingw
EOF
fi
if [ "$STATIC" = "YES" ]
then
echo "Build static libraries/executables"
cat << EOF >> epics-base/configure/CONFIG_SITE
SHARED_LIBRARIES=NO
STATIC_BUILD=YES
EOF
fi
case "$CMPLR" in
clang)
echo "Host compiler is clang"
cat << EOF >> epics-base/configure/os/CONFIG_SITE.Common.$EPICS_HOST_ARCH
GNU = NO
CMPLR_CLASS = clang
CC = clang
CCC = clang++
EOF
# hack
sed -i -e 's/CMPLR_CLASS = gcc/CMPLR_CLASS = clang/' epics-base/configure/CONFIG.gnuCommon
clang --version
;;
*)
echo "Host compiler is default"
gcc --version
;;
esac
cat <<EOF >> epics-base/configure/CONFIG_SITE
USR_CPPFLAGS += $USR_CPPFLAGS
USR_CFLAGS += $USR_CFLAGS
USR_CXXFLAGS += $USR_CXXFLAGS
EOF
# set RTEMS to eg. "4.9" or "4.10"
# requires qemu, bison, flex, texinfo, install-info
if [ -n "$RTEMS" ]
then
echo "Cross RTEMS${RTEMS} for pc386"
install -d /home/travis/.cache
curl -L "https://github.com/mdavidsaver/rsb/releases/download/travis-20160306-2/rtems${RTEMS}-i386-trusty-20190306-2.tar.gz" \
| tar -C /home/travis/.cache -xj
sed -i -e '/^RTEMS_VERSION/d' -e '/^RTEMS_BASE/d' epics-base/configure/os/CONFIG_SITE.Common.RTEMS
cat << EOF >> epics-base/configure/os/CONFIG_SITE.Common.RTEMS
RTEMS_VERSION=$RTEMS
RTEMS_BASE=/home/travis/.cache/rtems${RTEMS}-i386
EOF
cat << EOF >> epics-base/configure/CONFIG_SITE
CROSS_COMPILER_TARGET_ARCHS+=RTEMS-pc386
EOF
# find local qemu-system-i386
export PATH="$HOME/.cache/qemu/usr/bin:$PATH"
echo -n "Using QEMU: "
type qemu-system-i386 || echo "Missing qemu"
EXTRA=RTEMS_QEMU_FIXUPS=YES
fi
make -j2 -C epics-base $EXTRA

301
.github/workflows/ci-scripts-build.yml vendored Normal file
View File

@@ -0,0 +1,301 @@
# .github/workflows/ci-scripts-build.yml for use with EPICS Base ci-scripts
# (see: https://github.com/epics-base/ci-scripts)
# This is YAML - indentation levels are crucial
# Workflow name, shared by all branches
name: pvData
# Trigger on pushes and PRs to any branch
on:
push:
paths-ignore:
- .appveyor.yml
pull_request:
env:
SETUP_PATH: .ci-local:.ci
EPICS_TEST_IMPRECISE_TIMING: YES
EPICS_TEST_TIMEOUT: 300 # 5 min
jobs:
native:
name: ${{ matrix.name }}
runs-on: ${{ matrix.os }}
# Set environment variables from matrix parameters
env:
# NB: PVA modules build against both BASE 7.0 and 3.15
BASE: ${{ matrix.base }}
CMP: ${{ matrix.cmp }}
BCFG: ${{ matrix.configuration }}
CI_CROSS_TARGETS: ${{ matrix.cross }}
EXTRA: ${{ matrix.extra }}
TEST: ${{ matrix.test }}
strategy:
fail-fast: false
matrix:
# Job names also name artifacts, character limitations apply
include:
- name: "7.0 Ub gcc c++20 Werror"
base: "7.0"
os: ubuntu-latest
cmp: gcc
configuration: default
# Turn all warnings into errors,
# except for those we could not fix (yet).
# Remove respective -Wno-error=... flag once it is fixed.
extra: "CMD_CXXFLAGS=-std=c++20
CMD_CPPFLAGS='-fdiagnostics-color
-fstack-protector-strong
-Wformat
-Werror
-Werror=format-security
-Wno-error=deprecated-declarations
-Wno-error=stringop-truncation
-Wno-error=restrict
-Wno-error=sizeof-pointer-memaccess
-Wno-error=nonnull
-Wno-error=dangling-pointer
-Wno-error=format-overflow
-Wno-error=stringop-overread
-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3'
CMD_LDFLAGS=-Wl,-z,relro"
- name: "7.0 Ub gcc C++11, static"
base: "7.0"
os: ubuntu-latest
cmp: gcc
configuration: static
extra: "CMD_CXXFLAGS=-std=c++11"
- name: "7.0 Ub gcc u-char"
base: "7.0"
os: ubuntu-latest
cmp: gcc
configuration: static
extra: "CMD_CFLAGS=-funsigned-char CMD_CXXFLAGS=-funsigned-char"
- name: "7.0 Ub clang"
base: "7.0"
os: ubuntu-latest
cmp: clang
configuration: default
- name: "7.0 Ub clang C++11"
base: "7.0"
os: ubuntu-latest
cmp: clang
configuration: default
extra: "CMD_CXXFLAGS=-std=c++11"
- name: "7.0 MacOS clang"
base: "7.0"
os: macos-latest
cmp: clang
configuration: default
# Cross builds
- name: "3.15 Ub-22 gcc + MinGW"
base: "3.15"
os: ubuntu-22.04
cmp: gcc
configuration: default
cross: "windows-x64-mingw"
- name: "7.0 Ub gcc + linux-aarch64"
base: "7.0"
os: ubuntu-latest
cmp: gcc
configuration: default
cross: "linux-aarch64"
- name: "7.0 Ub gcc + linux-arm gnueabi"
base: "7.0"
os: ubuntu-latest
cmp: gcc
configuration: default
cross: "linux-arm@arm-linux-gnueabi"
- name: "7.0 Ub gcc + linux-arm gnueabihf"
base: "7.0"
os: ubuntu-latest
cmp: gcc
configuration: default
cross: "linux-arm@arm-linux-gnueabihf"
- name: "7.0 Ub gcc + MinGW"
base: "7.0"
os: ubuntu-latest
cmp: gcc
configuration: default
cross: "windows-x64-mingw"
- name: "7.0 Ub gcc + MinGW, static"
base: "7.0"
os: ubuntu-latest
cmp: gcc
configuration: static
cross: "windows-x64-mingw"
- name: "7.0 Ub-22 gcc + RT-4.9 pc386"
base: "7.0"
os: ubuntu-22.04
cmp: gcc
configuration: default
cross: "RTEMS-pc386-qemu@4.9"
- name: "7.0 Ub-22 gcc + RT-4.10 pc386"
base: "7.0"
os: ubuntu-22.04
cmp: gcc
configuration: default
cross: "RTEMS-pc386-qemu@4.10"
test: NO
- name: "7.0 Ub-22 gcc + RT-5.1 pc686"
base: "7.0"
os: ubuntu-22.04
cmp: gcc
configuration: default
cross: "RTEMS-pc686-qemu@5"
- name: "7.0 Ub-22 gcc + RT-5.1 beatnik,zynq_a9,uC5282"
base: "7.0"
os: ubuntu-22.04
cmp: gcc
configuration: default
cross: "RTEMS-beatnik@5:RTEMS-xilinx_zynq_a9_qemu@5:RTEMS-uC5282@5"
test: NO
# Windows builds
- name: "7.0 Win-22 MSC-22"
base: "7.0"
os: windows-2022
cmp: vs2022
configuration: default
- name: "7.0 Win-22 MSC-22 static"
base: "7.0"
os: windows-2022
cmp: vs2022
configuration: static
- name: "7.0 Win-22 MSC-22 debug"
base: "7.0"
os: windows-2022
cmp: vs2022
configuration: debug
extra: "CMD_CXXFLAGS=-analyze"
- name: "7.0 Win-22 MinGW"
base: "7.0"
os: windows-2022
cmp: gcc
configuration: default
steps:
- uses: actions/checkout@v4
with:
submodules: true
- name: Automatic core dumper analysis
uses: mdavidsaver/ci-core-dumper@master
- name: "apt-get install"
run: |
sudo apt-get update
sudo apt-get -y install qemu-system-x86 g++-mingw-w64-x86-64 gdb
if: runner.os == 'Linux'
- name: Prepare and compile dependencies
run: python .ci/cue.py prepare
- name: Build main module
run: python .ci/cue.py build
- name: Run main module tests
run: python .ci/cue.py -T 60M test
- name: Upload tapfiles Artifact
if: ${{ always() }}
uses: actions/upload-artifact@v4
with:
name: tapfiles ${{ matrix.name }}
path: '**/O.*/*.tap'
if-no-files-found: ignore
- name: Collect and show test results
if: ${{ always() }}
run: python .ci/cue.py -T 5M test-results
docker:
name: ${{ matrix.name }}
runs-on: ubuntu-latest
container:
image: ${{ matrix.image }}
# Set environment variables from matrix parameters
env:
BASE: ${{ matrix.base }}
CMP: ${{ matrix.cmp }}
BCFG: ${{ matrix.configuration }}
EXTRA: ${{ matrix.extra }}
TEST: ${{ matrix.test }}
strategy:
fail-fast: false
matrix:
# Job names also name artifacts, character limitations apply
include:
- name: "7.0 CentOS-8 gcc"
base: "7.0"
image: centos:8
cmp: gcc
configuration: default
- name: "7.0 Rocky-9 gcc"
base: "7.0"
image: rockylinux:9
cmp: gcc
configuration: default
- name: "7.0 Fedora-33 gcc"
base: "7.0"
image: fedora:33
cmp: gcc
configuration: default
- name: "7.0 Fedora gcc"
base: "7.0"
image: fedora:latest
cmp: gcc
configuration: default
steps:
- name: "Fix repo URLs on CentOS-8"
# centos:8 is frozen, repos are in the vault
if: matrix.image=='centos:8'
run: |
sed -i -e "s|mirrorlist=|#mirrorlist=|" \
-e "s|#baseurl=http://mirror|baseurl=http://vault|" \
/etc/yum.repos.d/CentOS-Linux-{BaseOS,AppStream,Extras,Plus}.repo
- name: "Redhat setup"
run: |
dnf -y install python3 gdb make perl gcc-c++ glibc-devel readline-devel ncurses-devel perl-devel perl-Test-Simple
git --version || dnf -y install git
python3 --version
- uses: actions/checkout@v4
with:
submodules: true
- name: Automatic core dumper analysis
uses: mdavidsaver/ci-core-dumper@master
- name: Prepare and compile dependencies
run: python3 .ci/cue.py prepare
- name: Build main module
run: python3 .ci/cue.py build
- name: Run main module tests
run: python3 .ci/cue.py -T 20M test
- name: Upload tapfiles Artifact
if: ${{ always() }}
uses: actions/upload-artifact@v4
with:
name: tapfiles ${{ matrix.name }}
path: '**/O.*/*.tap'
if-no-files-found: ignore
- name: Collect and show test results
if: ${{ always() }}
run: python3 .ci/cue.py -T 5M test-results

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule ".ci"]
path = .ci
url = https://github.com/epics-base/ci-scripts

17
.readthedocs.yml Normal file
View File

@@ -0,0 +1,17 @@
# .readthedocs.yml
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
# Required
version: 2
# Build documentation in the documentation/ directory with Sphinx
sphinx:
configuration: documentation/conf.py
# Build documentation with MkDocs
#mkdocs:
# configuration: mkdocs.yml
# Optionally build your docs in additional formats such as PDF and ePub
formats: all

View File

@@ -1,29 +0,0 @@
sudo: false
dist: trusty
language: c++
compiler:
- gcc
addons:
apt:
packages:
- libreadline6-dev
- libncurses5-dev
- perl
- clang
- g++-mingw-w64-i686
install:
- ./.ci/travis-prepare.sh
script:
- ./.ci/travis-build.sh
env:
- BRCORE=master BRLIBCOM=master
- CMPLR=clang
- USR_CXXFLAGS=-std=c++11
- CMPLR=clang USR_CXXFLAGS=-std=c++11
- WINE=32 TEST=NO STATIC=YES
- WINE=32 TEST=NO STATIC=NO
- RTEMS=4.10 TEST=NO
- RTEMS=4.9 TEST=NO
- BRBASE=3.16
- BRBASE=3.15
- BRBASE=3.14

316
Doxyfile
View File

@@ -1,4 +1,4 @@
# Doxyfile 1.8.6
# Doxyfile 1.8.8
# This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project.
@@ -32,33 +32,33 @@ DOXYFILE_ENCODING = UTF-8
# title of most generated pages and in a few other places.
# The default value is: My Project.
PROJECT_NAME = pvDataCPP
PROJECT_NAME = "PVData C++"
# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER =
PROJECT_NUMBER = 8.0.7
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
# quick idea about the purpose of the project. Keep the description short.
PROJECT_BRIEF =
PROJECT_BRIEF =
# With the PROJECT_LOGO tag one can specify an logo or icon that is included in
# the documentation. The maximum height of the logo should not exceed 55 pixels
# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo
# to the output directory.
PROJECT_LOGO =
PROJECT_LOGO =
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
# into which the generated documentation will be written. If a relative path is
# entered, it will be relative to the location where doxygen was started. If
# left blank the current directory will be used.
OUTPUT_DIRECTORY =
OUTPUT_DIRECTORY = .
# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub-
# directories (in 2 levels) under the output directory of each output format and
@@ -70,6 +70,14 @@ OUTPUT_DIRECTORY =
CREATE_SUBDIRS = NO
# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
# characters to appear in the names of generated files. If set to NO, non-ASCII
# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
# U+3044.
# The default value is: NO.
ALLOW_UNICODE_NAMES = NO
# The OUTPUT_LANGUAGE tag is used to specify the language in which all
# documentation generated by doxygen is written. Doxygen will use this
# information to generate all constant output in the proper language.
@@ -110,7 +118,17 @@ REPEAT_BRIEF = YES
# the entity):The $name class, The $name widget, The $name file, is, provides,
# specifies, contains, represents, a, an and the.
ABBREVIATE_BRIEF =
ABBREVIATE_BRIEF = "The $name class" \
"The $name widget" \
"The $name file" \
is \
provides \
specifies \
contains \
represents \
a \
an \
the
# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
# doxygen will generate a detailed section even if there is only a brief
@@ -125,7 +143,7 @@ ALWAYS_DETAILED_SEC = NO
# operators of the base classes will not be shown.
# The default value is: NO.
INLINE_INHERITED_MEMB = YES
INLINE_INHERITED_MEMB = NO
# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path
# before files name in the file list and in the header files. If set to NO the
@@ -144,7 +162,7 @@ FULL_PATH_NAMES = YES
# will be relative from the directory where doxygen is started.
# This tag requires that the tag FULL_PATH_NAMES is set to YES.
STRIP_FROM_PATH =
STRIP_FROM_PATH = src
# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
# path mentioned in the documentation of a class, which tells the reader which
@@ -153,7 +171,7 @@ STRIP_FROM_PATH =
# specify the list of include paths that are normally passed to the compiler
# using the -I flag.
STRIP_FROM_INC_PATH =
STRIP_FROM_INC_PATH = src
# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
# less readable) file names. This can be useful is your file systems doesn't
@@ -220,13 +238,13 @@ TAB_SIZE = 4
# "Side Effects:". You can put \n's in the value part of an alias to insert
# newlines.
ALIASES =
ALIASES =
# This tag can be used to specify a number of word-keyword mappings (TCL only).
# A mapping has the form "name=value". For example adding "class=itcl::class"
# will allow you to use the command class in the itcl::class meaning.
TCL_SUBST =
TCL_SUBST =
# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
# only. Doxygen will then generate output that is more tailored for C. For
@@ -261,16 +279,19 @@ OPTIMIZE_OUTPUT_VHDL = NO
# extension. Doxygen has a built-in mapping, but you can override or extend it
# using this tag. The format is ext=language, where ext is a file extension, and
# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make
# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
# (default is Fortran), use: inc=Fortran f=C.
# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:
# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
# Fortran. In the later case the parser tries to guess whether the code is fixed
# or free formatted code, this is the default for Fortran type files), VHDL. For
# instance to make doxygen treat .inc files as Fortran files (default is PHP),
# and .f files as C (default is Fortran), use: inc=Fortran f=C.
#
# Note For files without extension you can use no_extension as a placeholder.
#
# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
# the files are not read by doxygen.
EXTENSION_MAPPING =
EXTENSION_MAPPING =
# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
# according to the Markdown format, which allows for more readable
@@ -298,7 +319,7 @@ AUTOLINK_SUPPORT = YES
# diagrams that involve STL classes more complete and accurate.
# The default value is: NO.
BUILTIN_STL_SUPPORT = YES
BUILTIN_STL_SUPPORT = NO
# If you use Microsoft's C++/CLI language, you should set this option to YES to
# enable parsing support.
@@ -398,7 +419,7 @@ LOOKUP_CACHE_SIZE = 0
# normally produced when WARNINGS is set to YES.
# The default value is: NO.
EXTRACT_ALL = YES
EXTRACT_ALL = NO
# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will
# be included in the documentation.
@@ -487,7 +508,7 @@ INTERNAL_DOCS = NO
# and Mac users are advised to set this option to NO.
# The default value is: system dependent.
CASE_SENSE_NAMES = YES
CASE_SENSE_NAMES = NO
# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
# their full class and namespace scopes in the documentation. If set to YES the
@@ -606,7 +627,7 @@ GENERATE_DEPRECATEDLIST= YES
# sections, marked by \if <section_label> ... \endif and \cond <section_label>
# ... \endcond blocks.
ENABLED_SECTIONS =
ENABLED_SECTIONS =
# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
# initial value of a variable or macro / define can have for it to appear in the
@@ -648,7 +669,7 @@ SHOW_NAMESPACES = YES
# by doxygen. Whatever the program writes to standard output is used as the file
# version. For an example see the documentation.
FILE_VERSION_FILTER =
FILE_VERSION_FILTER =
# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
# by doxygen. The layout file controls the global structure of the generated
@@ -661,7 +682,7 @@ FILE_VERSION_FILTER =
# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
# tag is left empty.
LAYOUT_FILE =
LAYOUT_FILE =
# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
# the reference definitions. This must be a list of .bib files. The .bib
@@ -669,10 +690,9 @@ LAYOUT_FILE =
# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
# For LaTeX the style of the bibliography can be controlled using
# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
# search path. Do not use file names with spaces, bibtex cannot handle them. See
# also \cite for info how to create references.
# search path. See also \cite for info how to create references.
CITE_BIB_FILES =
CITE_BIB_FILES =
#---------------------------------------------------------------------------
# Configuration options related to warning and progress messages
@@ -699,7 +719,7 @@ WARNINGS = YES
# will automatically be disabled.
# The default value is: YES.
WARN_IF_UNDOCUMENTED = YES
WARN_IF_UNDOCUMENTED = NO
# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
# potential errors in the documentation, such as not documenting some parameters
@@ -731,7 +751,7 @@ WARN_FORMAT = "$file:$line: $text"
# messages should be written. If left blank the output is written to standard
# error (stderr).
WARN_LOGFILE =
WARN_LOGFILE =
#---------------------------------------------------------------------------
# Configuration options related to the input files
@@ -743,7 +763,12 @@ WARN_LOGFILE =
# spaces.
# Note: If this tag is empty the current directory is searched.
INPUT = include
INPUT = src/pv \
documentation/mainpage.dox \
src/copy/pv \
src/misc/pv \
src/json/pv \
documentation/release_notes.dox
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
@@ -763,13 +788,13 @@ INPUT_ENCODING = UTF-8
# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf,
# *.qsf, *.as and *.js.
FILE_PATTERNS =
FILE_PATTERNS =
# The RECURSIVE tag can be used to specify whether or not subdirectories should
# be searched for input files as well.
# The default value is: NO.
RECURSIVE = YES
RECURSIVE = NO
# The EXCLUDE tag can be used to specify files and/or directories that should be
# excluded from the INPUT source files. This way you can easily exclude a
@@ -778,7 +803,7 @@ RECURSIVE = YES
# Note that relative paths are relative to the directory from which doxygen is
# run.
EXCLUDE =
EXCLUDE =
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
# directories that are symbolic links (a Unix file system feature) are excluded
@@ -794,7 +819,7 @@ EXCLUDE_SYMLINKS = NO
# Note that the wildcards are matched against the file with absolute path, so to
# exclude all test directories for example use the pattern */test/*
EXCLUDE_PATTERNS =
EXCLUDE_PATTERNS =
# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
# (namespaces, classes, functions, etc.) that should be excluded from the
@@ -805,20 +830,20 @@ EXCLUDE_PATTERNS =
# Note that the wildcards are matched against the file with absolute path, so to
# exclude all test directories use the pattern */test/*
EXCLUDE_SYMBOLS =
EXCLUDE_SYMBOLS =
# The EXAMPLE_PATH tag can be used to specify one or more files or directories
# that contain example code fragments that are included (see the \include
# command).
EXAMPLE_PATH =
EXAMPLE_PATH = examples
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
# *.h) to filter out the source-files in the directories. If left blank all
# files are included.
EXAMPLE_PATTERNS =
EXAMPLE_PATTERNS = *.cpp
# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
# searched for input files to be used with the \include or \dontinclude commands
@@ -831,7 +856,7 @@ EXAMPLE_RECURSIVE = NO
# that contain images that are to be included in the documentation (see the
# \image command).
IMAGE_PATH =
IMAGE_PATH =
# The INPUT_FILTER tag can be used to specify a program that doxygen should
# invoke to filter for each input file. Doxygen will invoke the filter program
@@ -848,7 +873,7 @@ IMAGE_PATH =
# code is scanned, but not when the output code is generated. If lines are added
# or removed, the anchors will not be placed correctly.
INPUT_FILTER =
INPUT_FILTER =
# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
# basis. Doxygen will compare the file name with each pattern and apply the
@@ -857,7 +882,7 @@ INPUT_FILTER =
# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
# patterns match the file name, INPUT_FILTER is applied.
FILTER_PATTERNS =
FILTER_PATTERNS =
# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
# INPUT_FILTER ) will also be used to filter the input files that are used for
@@ -872,14 +897,14 @@ FILTER_SOURCE_FILES = NO
# *.ext= (so without naming a filter).
# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
FILTER_SOURCE_PATTERNS =
FILTER_SOURCE_PATTERNS =
# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
# is part of the input, its contents will be placed on the main page
# (index.html). This can be useful if you have a project on for instance GitHub
# and want to reuse the introduction page also for the doxygen output.
USE_MDFILE_AS_MAINPAGE =
USE_MDFILE_AS_MAINPAGE =
#---------------------------------------------------------------------------
# Configuration options related to source browsing
@@ -911,13 +936,13 @@ STRIP_CODE_COMMENTS = YES
# function all documented functions referencing it will be listed.
# The default value is: NO.
REFERENCED_BY_RELATION = YES
REFERENCED_BY_RELATION = NO
# If the REFERENCES_RELATION tag is set to YES then for each documented function
# all documented entities called/used by that function will be listed.
# The default value is: NO.
REFERENCES_RELATION = YES
REFERENCES_RELATION = NO
# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
# to YES, then the hyperlinks from functions in REFERENCES_RELATION and
@@ -967,6 +992,25 @@ USE_HTAGS = NO
VERBATIM_HEADERS = YES
# If the CLANG_ASSISTED_PARSING tag is set to YES, then doxygen will use the
# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the
# cost of reduced performance. This can be particularly helpful with template
# rich C++ code for which doxygen's built-in parser lacks the necessary type
# information.
# Note: The availability of this option depends on whether or not doxygen was
# compiled with the --with-libclang option.
# The default value is: NO.
CLANG_ASSISTED_PARSING = YES
# If clang assisted parsing is enabled you can provide the compiler with command
# line options that you would normally use when invoking the compiler. Note that
# the include paths will already be set by doxygen for the files and directories
# specified with INPUT and INCLUDE_PATH.
# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
CLANG_OPTIONS =
#---------------------------------------------------------------------------
# Configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
@@ -976,7 +1020,7 @@ VERBATIM_HEADERS = YES
# classes, structs, unions or interfaces.
# The default value is: YES.
ALPHABETICAL_INDEX = NO
ALPHABETICAL_INDEX = YES
# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
# which the alphabetical index list will be split.
@@ -991,7 +1035,7 @@ COLS_IN_ALPHA_INDEX = 5
# while generating the index headers.
# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
IGNORE_PREFIX =
IGNORE_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the HTML output
@@ -1008,7 +1052,7 @@ GENERATE_HTML = YES
# The default directory is: html.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_OUTPUT = documentation/html
HTML_OUTPUT = html/doxygen
# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
# generated HTML page (for example: .htm, .php, .asp).
@@ -1035,7 +1079,7 @@ HTML_FILE_EXTENSION = .html
# of the possible markers and block names see the documentation.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_HEADER =
HTML_HEADER =
# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
# generated HTML page. If the tag is left blank doxygen will generate a standard
@@ -1045,7 +1089,7 @@ HTML_HEADER =
# that doxygen normally uses.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_FOOTER =
HTML_FOOTER =
# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
# sheet that is used by each HTML page. It can be used to fine-tune the look of
@@ -1057,18 +1101,20 @@ HTML_FOOTER =
# obsolete.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_STYLESHEET =
HTML_STYLESHEET =
# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user-
# defined cascading style sheet that is included after the standard style sheets
# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
# cascading style sheets that are included after the standard style sheets
# created by doxygen. Using this option one can overrule certain style aspects.
# This is preferred over using HTML_STYLESHEET since it does not replace the
# standard style sheet and is therefor more robust against future updates.
# Doxygen will copy the style sheet file to the output directory. For an example
# see the documentation.
# Doxygen will copy the style sheet files to the output directory.
# Note: The order of the extra stylesheet files is of importance (e.g. the last
# stylesheet in the list overrules the setting of the previous ones in the
# list). For an example see the documentation.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_EXTRA_STYLESHEET =
HTML_EXTRA_STYLESHEET =
# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
# other source files which should be copied to the HTML output directory. Note
@@ -1078,7 +1124,7 @@ HTML_EXTRA_STYLESHEET =
# files will be copied as-is; there are no commands or markers available.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_EXTRA_FILES = documentation/pvDataCPP.html
HTML_EXTRA_FILES =
# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
# will adjust the colors in the stylesheet and background images according to
@@ -1206,7 +1252,7 @@ GENERATE_HTMLHELP = NO
# written to the html output directory.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
CHM_FILE =
CHM_FILE =
# The HHC_LOCATION tag can be used to specify the location (absolute path
# including file name) of the HTML help compiler ( hhc.exe). If non-empty
@@ -1214,7 +1260,7 @@ CHM_FILE =
# The file has to be specified with full path.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
HHC_LOCATION =
HHC_LOCATION =
# The GENERATE_CHI flag controls if a separate .chi index file is generated (
# YES) or that it should be included in the master .chm file ( NO).
@@ -1227,10 +1273,11 @@ GENERATE_CHI = NO
# and project file content.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
CHM_INDEX_ENCODING =
CHM_INDEX_ENCODING =
# The BINARY_TOC flag controls whether a binary table of contents is generated (
# YES) or a normal table of contents ( NO) in the .chm file.
# YES) or a normal table of contents ( NO) in the .chm file. Furthermore it
# enables the Previous and Next buttons.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
@@ -1257,7 +1304,7 @@ GENERATE_QHP = NO
# the HTML output folder.
# This tag requires that the tag GENERATE_QHP is set to YES.
QCH_FILE =
QCH_FILE =
# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
# Project output. For more information please see Qt Help Project / Namespace
@@ -1282,7 +1329,7 @@ QHP_VIRTUAL_FOLDER = doc
# filters).
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_CUST_FILTER_NAME =
QHP_CUST_FILTER_NAME =
# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
# custom filter to add. For more information please see Qt Help Project / Custom
@@ -1290,21 +1337,21 @@ QHP_CUST_FILTER_NAME =
# filters).
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_CUST_FILTER_ATTRS =
QHP_CUST_FILTER_ATTRS =
# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
# project's filter section matches. Qt Help Project / Filter Attributes (see:
# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_SECT_FILTER_ATTRS =
QHP_SECT_FILTER_ATTRS =
# The QHG_LOCATION tag can be used to specify the location of Qt's
# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
# generated .qhp file.
# This tag requires that the tag GENERATE_QHP is set to YES.
QHG_LOCATION =
QHG_LOCATION =
# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
# generated, together with the HTML files, they form an Eclipse help plugin. To
@@ -1437,7 +1484,7 @@ MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_EXTENSIONS =
MATHJAX_EXTENSIONS =
# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
# of code that will be used on startup of the MathJax code. See the MathJax site
@@ -1445,7 +1492,7 @@ MATHJAX_EXTENSIONS =
# example see the documentation.
# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_CODEFILE =
MATHJAX_CODEFILE =
# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
# the HTML output. The underlying search engine uses javascript and DHTML and
@@ -1466,15 +1513,15 @@ MATHJAX_CODEFILE =
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
SEARCHENGINE = NO
SEARCHENGINE = YES
# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
# implemented using a web server instead of a web client using Javascript. There
# are two flavours of web server based searching depending on the
# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for
# searching and an index file used by the script. When EXTERNAL_SEARCH is
# enabled the indexing and searching needs to be provided by external tools. See
# the section "External Indexing and Searching" for details.
# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
# setting. When disabled, doxygen will generate a PHP script for searching and
# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
# and searching needs to be provided by external tools. See the section
# "External Indexing and Searching" for details.
# The default value is: NO.
# This tag requires that the tag SEARCHENGINE is set to YES.
@@ -1505,7 +1552,7 @@ EXTERNAL_SEARCH = NO
# Searching" for details.
# This tag requires that the tag SEARCHENGINE is set to YES.
SEARCHENGINE_URL =
SEARCHENGINE_URL =
# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
# search data is written to a file for indexing by an external tool. With the
@@ -1521,7 +1568,7 @@ SEARCHDATA_FILE = searchdata.xml
# projects and redirect the results back to the right project.
# This tag requires that the tag SEARCHENGINE is set to YES.
EXTERNAL_SEARCH_ID =
EXTERNAL_SEARCH_ID =
# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
# projects other than the one defined by this configuration file, but that are
@@ -1531,7 +1578,7 @@ EXTERNAL_SEARCH_ID =
# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
# This tag requires that the tag SEARCHENGINE is set to YES.
EXTRA_SEARCH_MAPPINGS =
EXTRA_SEARCH_MAPPINGS =
#---------------------------------------------------------------------------
# Configuration options related to the LaTeX output
@@ -1583,7 +1630,7 @@ COMPACT_LATEX = NO
# The default value is: a4.
# This tag requires that the tag GENERATE_LATEX is set to YES.
PAPER_TYPE = a4wide
PAPER_TYPE = a4
# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
# that should be included in the LaTeX output. To get the times font for
@@ -1592,7 +1639,7 @@ PAPER_TYPE = a4wide
# If left blank no extra packages will be included.
# This tag requires that the tag GENERATE_LATEX is set to YES.
EXTRA_PACKAGES =
EXTRA_PACKAGES =
# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
# generated LaTeX document. The header should contain everything until the first
@@ -1602,22 +1649,24 @@ EXTRA_PACKAGES =
#
# Note: Only use a user-defined header if you know what you are doing! The
# following commands have a special meaning inside the header: $title,
# $datetime, $date, $doxygenversion, $projectname, $projectnumber. Doxygen will
# replace them by respectively the title of the page, the current date and time,
# only the current date, the version number of doxygen, the project name (see
# PROJECT_NAME), or the project number (see PROJECT_NUMBER).
# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
# $projectbrief, $projectlogo. Doxygen will replace $title with the empy string,
# for the replacement values of the other commands the user is refered to
# HTML_HEADER.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_HEADER =
LATEX_HEADER =
# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
# generated LaTeX document. The footer should contain everything after the last
# chapter. If it is left blank doxygen will generate a standard footer.
# chapter. If it is left blank doxygen will generate a standard footer. See
# LATEX_HEADER for more information on how to generate a default footer and what
# special commands can be used inside the footer.
#
# Note: Only use a user-defined footer if you know what you are doing!
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_FOOTER =
LATEX_FOOTER =
# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
# other source files which should be copied to the LATEX_OUTPUT output
@@ -1625,7 +1674,7 @@ LATEX_FOOTER =
# markers available.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_EXTRA_FILES =
LATEX_EXTRA_FILES =
# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
@@ -1634,15 +1683,15 @@ LATEX_EXTRA_FILES =
# The default value is: YES.
# This tag requires that the tag GENERATE_LATEX is set to YES.
PDF_HYPERLINKS = NO
PDF_HYPERLINKS = YES
# If the LATEX_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
# the PDF file directly from the LaTeX files. Set this option to YES to get a
# higher quality PDF documentation.
# The default value is: YES.
# This tag requires that the tag GENERATE_LATEX is set to YES.
USE_PDFLATEX = NO
USE_PDFLATEX = YES
# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
# command to the generated LaTeX files. This will instruct LaTeX to keep running
@@ -1725,14 +1774,14 @@ RTF_HYPERLINKS = NO
# default style sheet that doxygen normally uses.
# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_STYLESHEET_FILE =
RTF_STYLESHEET_FILE =
# Set optional variables used in the generation of an RTF document. Syntax is
# similar to doxygen's config file. A template extensions file can be generated
# using doxygen -e rtf extensionFile.
# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_EXTENSIONS_FILE =
RTF_EXTENSIONS_FILE =
#---------------------------------------------------------------------------
# Configuration options related to the man page output
@@ -1762,6 +1811,13 @@ MAN_OUTPUT = man
MAN_EXTENSION = .3
# The MAN_SUBDIR tag determines the name of the directory created within
# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
# MAN_EXTENSION with the initial . removed.
# This tag requires that the tag GENERATE_MAN is set to YES.
MAN_SUBDIR =
# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
# will generate one additional man file for each entity documented in the real
# man page(s). These additional files only source the real man page, but without
@@ -1789,18 +1845,6 @@ GENERATE_XML = NO
XML_OUTPUT = xml
# The XML_SCHEMA tag can be used to specify a XML schema, which can be used by a
# validating XML parser to check the syntax of the XML files.
# This tag requires that the tag GENERATE_XML is set to YES.
XML_SCHEMA =
# The XML_DTD tag can be used to specify a XML DTD, which can be used by a
# validating XML parser to check the syntax of the XML files.
# This tag requires that the tag GENERATE_XML is set to YES.
XML_DTD =
# If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program
# listings (including syntax highlighting and cross-referencing information) to
# the XML output. Note that enabling this will significantly increase the size
@@ -1828,6 +1872,15 @@ GENERATE_DOCBOOK = NO
DOCBOOK_OUTPUT = docbook
# If the DOCBOOK_PROGRAMLISTING tag is set to YES doxygen will include the
# program listings (including syntax highlighting and cross-referencing
# information) to the DOCBOOK output. Note that enabling this will significantly
# increase the size of the DOCBOOK output.
# The default value is: NO.
# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
DOCBOOK_PROGRAMLISTING = NO
#---------------------------------------------------------------------------
# Configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
@@ -1876,7 +1929,7 @@ PERLMOD_PRETTY = YES
# overwrite each other's variables.
# This tag requires that the tag GENERATE_PERLMOD is set to YES.
PERLMOD_MAKEVAR_PREFIX =
PERLMOD_MAKEVAR_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the preprocessor
@@ -1895,7 +1948,7 @@ ENABLE_PREPROCESSING = YES
# The default value is: NO.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
MACRO_EXPANSION = NO
MACRO_EXPANSION = YES
# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
# the macro expansion is limited to the macros specified with the PREDEFINED and
@@ -1917,7 +1970,7 @@ SEARCH_INCLUDES = YES
# preprocessor.
# This tag requires that the tag SEARCH_INCLUDES is set to YES.
INCLUDE_PATH =
INCLUDE_PATH = src/misc src ../../include/
# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
# patterns (like *.h and *.hpp) to filter out the header-files in the
@@ -1925,7 +1978,7 @@ INCLUDE_PATH =
# used.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
INCLUDE_FILE_PATTERNS =
INCLUDE_FILE_PATTERNS =
# The PREDEFINED tag can be used to specify one or more macro names that are
# defined before the preprocessor is started (similar to the -D option of e.g.
@@ -1935,7 +1988,7 @@ INCLUDE_FILE_PATTERNS =
# recursively expanded use the := operator instead of the = operator.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
PREDEFINED =
PREDEFINED =
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
# tag can be used to specify a list of macro names that should be expanded. The
@@ -1947,14 +2000,14 @@ PREDEFINED =
EXPAND_AS_DEFINED =
# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
# remove all refrences to function-like macros that are alone on a line, have an
# all uppercase name, and do not end with a semicolon. Such function macros are
# typically used for boiler-plate code, and will confuse the parser if not
# remove all references to function-like macros that are alone on a line, have
# an all uppercase name, and do not end with a semicolon. Such function macros
# are typically used for boiler-plate code, and will confuse the parser if not
# removed.
# The default value is: YES.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
SKIP_FUNCTION_MACROS = YES
SKIP_FUNCTION_MACROS = NO
#---------------------------------------------------------------------------
# Configuration options related to external references
@@ -1969,17 +2022,17 @@ SKIP_FUNCTION_MACROS = YES
# where loc1 and loc2 can be relative or absolute paths or URLs. See the
# section "Linking to external documentation" for more information about the use
# of tag files.
# Note: Each tag file must have an unique name (where the name does NOT include
# Note: Each tag file must have a unique name (where the name does NOT include
# the path). If a tag file is not located in the directory in which doxygen is
# run, you must also specify the path to the tagfile here.
TAGFILES =
TAGFILES = "libstdc++.tag = http://gcc.gnu.org/onlinedocs/libstdc++/latest-doxygen"
# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
# tag file that is based on the input files it reads. See section "Linking to
# external documentation" for more information about the usage of tag files.
GENERATE_TAGFILE =
GENERATE_TAGFILE = pvdata.tag
# If the ALLEXTERNALS tag is set to YES all external class will be listed in the
# class index. If set to NO only the inherited external classes will be listed.
@@ -1992,14 +2045,14 @@ ALLEXTERNALS = NO
# listed.
# The default value is: YES.
EXTERNAL_GROUPS = YES
EXTERNAL_GROUPS = NO
# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed in
# the related pages index. If set to NO, only the current project's pages will
# be listed.
# The default value is: YES.
EXTERNAL_PAGES = YES
EXTERNAL_PAGES = NO
# The PERL_PATH should be the absolute path and name of the perl script
# interpreter (i.e. the result of 'which perl').
@@ -2027,14 +2080,14 @@ CLASS_DIAGRAMS = YES
# the mscgen tool resides. If left empty the tool is assumed to be found in the
# default search path.
MSCGEN_PATH =
MSCGEN_PATH =
# You can include diagrams made with dia in doxygen documentation. Doxygen will
# then run dia to produce the diagram and insert it in the documentation. The
# DIA_PATH tag allows you to specify the directory where the dia binary resides.
# If left empty dia is assumed to be found in the default search path.
DIA_PATH =
DIA_PATH =
# If set to YES, the inheritance and collaboration graphs will hide inheritance
# and usage relations if the target is undocumented or is not a class.
@@ -2047,9 +2100,9 @@ HIDE_UNDOC_RELATIONS = YES
# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
# Bell Labs. The other options in this section have no effect if this option is
# set to NO
# The default value is: NO.
# The default value is: YES.
HAVE_DOT = NO
HAVE_DOT = YES
# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
# to run in parallel. When set to 0 doxygen will base this on the number of
@@ -2061,7 +2114,7 @@ HAVE_DOT = NO
DOT_NUM_THREADS = 0
# When you want a differently looking font n the dot files that doxygen
# When you want a differently looking font in the dot files that doxygen
# generates you can specify the font name using DOT_FONTNAME. You need to make
# sure dot is able to find the font, which can be done by putting it in a
# standard location or by setting the DOTFONTPATH environment variable or by
@@ -2083,7 +2136,7 @@ DOT_FONTSIZE = 10
# the path where dot can find it using this tag.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_FONTPATH =
DOT_FONTPATH =
# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
# each documented class showing the direct and indirect inheritance relations.
@@ -2199,7 +2252,9 @@ DIRECTORY_GRAPH = YES
# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
# to make the SVG files visible in IE 9+ (other browsers do not have this
# requirement).
# Possible values are: png, jpg, gif and svg.
# Possible values are: png, png:cairo, png:cairo:cairo, png:cairo:gd, png:gd,
# png:gd:gd, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, gif, gif:cairo,
# gif:cairo:gd, gif:gd, gif:gd:gd and svg.
# The default value is: png.
# This tag requires that the tag HAVE_DOT is set to YES.
@@ -2221,26 +2276,35 @@ INTERACTIVE_SVG = NO
# found. If left blank, it is assumed the dot tool can be found in the path.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_PATH =
DOT_PATH =
# The DOTFILE_DIRS tag can be used to specify one or more directories that
# contain dot files that are included in the documentation (see the \dotfile
# command).
# This tag requires that the tag HAVE_DOT is set to YES.
DOTFILE_DIRS =
DOTFILE_DIRS =
# The MSCFILE_DIRS tag can be used to specify one or more directories that
# contain msc files that are included in the documentation (see the \mscfile
# command).
MSCFILE_DIRS =
MSCFILE_DIRS =
# The DIAFILE_DIRS tag can be used to specify one or more directories that
# contain dia files that are included in the documentation (see the \diafile
# command).
DIAFILE_DIRS =
DIAFILE_DIRS =
# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
# path where java can find the plantuml.jar file. If left blank, it is assumed
# PlantUML is not used or called during a preprocessing step. Doxygen will
# generate a warning when it encounters a \startuml command in this case and
# will not generate output for the diagram.
# This tag requires that the tag HAVE_DOT is set to YES.
PLANTUML_JAR_PATH =
# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
# that will be shown in the graph. If the number of nodes in a graph becomes

View File

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

View File

@@ -1,4 +1,12 @@
EPICS_PVD_MAJOR_VERSION = 7
# Version number for the PV Data API and shared library
EPICS_PVD_MAJOR_VERSION = 8
EPICS_PVD_MINOR_VERSION = 0
EPICS_PVD_MAINTENANCE_VERSION = 1
EPICS_PVD_DEVELOPMENT_FLAG = 1
EPICS_PVD_MAINTENANCE_VERSION = 7
# Development flag, set to zero for release versions
EPICS_PVD_DEVELOPMENT_FLAG = 0
# Immediately after a release the MAINTENANCE_VERSION
# will be incremented and the DEVELOPMENT_FLAG set to 1

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,7 @@
.wy-side-nav-search {
background-color: #18334B;
}
.wy-side-nav-search input[type="text"] {
border-color: #18334b;
}

78
documentation/conf.py Normal file
View File

@@ -0,0 +1,78 @@
# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# http://www.sphinx-doc.org/en/master/config
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
# -- Project information -----------------------------------------------------
project = 'EPICS Documentation'
copyright = '2019, EPICS Controls.'
author = 'EPICS'
# -- General configuration ---------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.intersphinx',
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
# Intersphinx links to subprojects
intersphinx_mapping = {
'how-tos': ('https://docs.epics-controls.org/projects/how-tos/en/latest', None),
}
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'sphinx_rtd_theme'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
html_css_files = [
'css/custom.css',
]
master_doc = 'index'
html_theme_options = {
'logo_only': True,
}
html_logo = "images/EPICS_white_logo_v02.png"
html_extra_path = ['../html']
# -- Run Doxygen ------------------------------------------------------------
import subprocess
subprocess.call('cd ..; mkdir -p html/doxygen; doxygen', shell=True)

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

16
documentation/index.rst Normal file
View File

@@ -0,0 +1,16 @@
pvData (C++) Library
====================
.. toctree::
:hidden:
EPICS Website <https://epics-controls.org>
EPICS Documentation Home <https://docs.epics-controls.org>
.. toctree::
:maxdepth: 1
:caption: pvDataCPP
Reference Manual and API Documentation <https://docs.epics-controls.org/projects/pvdata-cpp/en/latest/doxygen>
Source Code Repository on GitHub <https://github.com/epics-base/pvDataCPP>

View File

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

View File

@@ -2,46 +2,138 @@
@page release_notes Release Notes
Release 7.1.0 (UNRELEASED)
Release 8.0.7 (Dec 2025)
========================
- Compatible changes
- Allow epics::pvData::Timer to be cancelled during callback execution.
- Clang compiler warnings cleaned up.
- Limit periodic timers to one catch-up after missing many events.
Release 8.0.6 (Dec 2023)
========================
- Compatible changes
- Actually enable JSON-5 output in PVStructure::Formatter::JSON when available.
- Fix unaligned access issues for some ARM/Linux targets.
Release 8.0.5 (Sep 2022)
========================
- Compatible changes
- Internal changes to use the YAJL API for generating JSON and JSON-5 output.
Release 8.0.4 (Feb 2021)
========================
- Incompatible changes
- Remove ByteBuffer::align()
- Compatible changes
- Deprecate SerializableControl::alignBuffer() and DeserializableControl::alignData()
- shared_vector_convert<>() fix convert of empty, untyped, array
Release 8.0.3 (July 2020)
=========================
- Incompatible changes
- Removed THROW_BASE_EXCEPTION_CAUSE() macro which has long ignored its cause.
Any external users should switch to the functionally identical THROW_BASE_EXCEPTION_CAUSE()
- Various printing of functions now conditionally escape strings
including quote '\"' and similar charactors.
Release 8.0.2 (May 2020)
========================
- Changes to documentation and unittests.
No functional changes to library.
Release 8.0.1 (Nov 2019)
==========================
- Incompatible changes
- Requires Base >= 3.15
- Bug fixes
Release 8.0.0 (July 2019)
=========================
- Deprecations
- pv/localStaticLock.h
- ByteBuffer::getArray()
- Removals
- Remove previously deprecated executor.h, queue.h and timerFunction.h
- Remove *HashFunction functors to "hash" Field sub-classes which were never fully implemented.
- Fixes
- Make thread safe getFieldCreate() and getPVDataCreate()
- Workaround for MSVC pickyness that iterators be non-NULL, even when not de-referenced.
- Fix alignment fault during (de)serialization on RTEMS/vxWorks.
- Fix shared_vector::swap() for void specialization.
- Changes in several Field sub-classes to return const ref. instead of a copy.
- pv/localStaticLock.h
- pv/pvCopy.h (see epics::pvData::PVRequestMapper)
- Additions
- shared_vector add c++11 move and construct for initializer list.
- Add AnyScalar::clear()
- Add ctor AnyScalar(ScalarType, const void*) to allow construction from an untyped buffer.
- Add Timer::close()
- Allow castUnsafe() from const char* without first allocating a std::string.
- Add {Structure,Union}::getFieldT
Release 7.1.3 (Apr 2019)
========================
- Fix for array serialization error to/from big endian.
https://github.com/epics-base/pvDataCPP/issues/65
Release 7.1.2 (Mar 2019)
========================
- 7.1.1 tag pushed prematurely.
Release 7.1.1 (Mar 2019)
========================
- Fixes
- Init order issue with StandardField::getStandardField()
- Build fix for Visual Studio 2013+
Release 7.1.0 (Nov 2018)
========================
- Deprecations
- BoundedString, BoundedScalarArray, and FixedScalarArray will be removed unless they are fixed.
See https://github.com/epics-base/pvDataCPP/issues/52 for discussion.
- pv/localStaticLock.h
- pv/pvCopy.h (see epics::pvData::PVRequestMapper)
- Removals
- Remove previously deprecated executor.h, queue.h and timerFunction.h
- Remove *HashFunction functors to "hash" Field sub-classes which were never fully implemented.
- Fixes
- Make thread safe getFieldCreate() and getPVDataCreate()
- Workaround for MSVC pickyness that iterators be non-NULL, even when not de-referenced.
- Fix alignment fault during (de)serialization on RTEMS/vxWorks.
- Fix epics::pvData::shared_vector::swap() for void specialization.
- Changes in several epics::pvData::Field sub-classes to return const ref. instead of a copy.
- Additions
- epics::pvData::shared_vector add c++11 move and construct for initializer list.
- Add epics::pvData::AnyScalar::clear()
- Add ctor epics::pvData::AnyScalar(ScalarType, const void*) to allow construction from an untyped buffer.
- Add epics::pvData::Timer::close()
- Allow epics::pvData::castUnsafe() from const char* without first allocating a std::string.
- De-duplication of epics::pvData::Field instances is performed using a global hash table.
Identical definitions will share a single instance. Allows O(0) comparision.
- Add epics::pvData::PVRequestMapper to facilitate (partial) copying between PVStructure instances
modified by a pvRequest.
- Add shorthand notations epics::pvData::FieldBuilder::begin() and epics::pvData::Field::build()
Release 7.0.0 (Dec 2017)
========================
- Removals
- Remove requester.h, monitor.h, and destroyable.h.. Migrated to the pvAccessCPP module.
- Previously deprecated monitorPlugin.h is removed.
- Remove pv/messageQueue.h and epics::pvData::MessageQueue
- Remove requester.h, monitor.h, and destroyable.h.. Migrated to the pvAccessCPP module.
- Previously deprecated monitorPlugin.h is removed.
- Remove pv/messageQueue.h and epics::pvData::MessageQueue
- Deprecate the following utility classes, to be removed in 8.0.
- epics::pvData::Queue
- epics::pvData::Executor
- epics::pvData::TimeFunction
- epics::pvData::Queue
- epics::pvData::Executor
- epics::pvData::TimeFunction
- Additions
- Add pv/pvdVersion.h which is included by pv/pvIntrospect.h
- Add epics::pvData::createRequest() function. Alternative to epics::pvData::CreateRequest class which throws on error.
- epics::pvData::FieldBuilder allow Structure defintion to be changed/appended
- Add epics::pvData::ValueBuilder like FieldBuilder also sets initial values.
- Can also be constructed using an existing PVStructure to allow "editing".
- Add debugPtr.h wrapper with reference tracking to assist in troubleshooting shared_ptr related ref. loops.
- Add @ref pvjson utilities
- Add pv/pvdVersion.h which is included by pv/pvIntrospect.h
- Add epics::pvData::createRequest() function. Alternative to epics::pvData::CreateRequest class which throws on error.
- epics::pvData::FieldBuilder allow Structure defintion to be changed/appended
- Add epics::pvData::ValueBuilder like FieldBuilder also sets initial values.
- Can also be constructed using an existing PVStructure to allow "editing".
- Add debugPtr.h wrapper with reference tracking to assist in troubleshooting shared_ptr related ref. loops.
- Add @ref pvjson utilities
- Add reftrack @ref pvd_reftrack
- Add header typemap.h to facilitate boilerplate switch() over ScalarType
- Add epics::auto_ptr typedef in help writing code supporting both c++98 and c++11 w/o copious deprecation warnings.
Release 6.0.1

View File

@@ -3,7 +3,6 @@
SRC_DIRS += $(PVDATA_SRC)/copy
INC += pv/createRequest.h
INC += pv/pvCopy.h
LIBSRCS += createRequest.cpp
LIBSRCS += pvCopy.cpp
LIBSRCS += requestmapper.cpp

View File

@@ -30,7 +30,7 @@ static FieldCreatePtr fieldCreate = getFieldCreate();
struct CreateRequestImpl {
struct Node
struct Node
{
string name;
vector<Node> nodes;
@@ -55,7 +55,7 @@ struct CreateRequestImpl {
CreateRequestImpl() {}
void removeBlanks(string& str)
{
@@ -129,7 +129,7 @@ struct CreateRequestImpl {
if(pos==string::npos) break;
numValues++;
index = pos +1;
}
}
vector<string> valueList(numValues,"");
index=0;
for(size_t i=0; i<numValues; i++) {
@@ -318,7 +318,7 @@ struct CreateRequestImpl {
if (!request.empty()) removeBlanks(request);
if (request.empty())
{
return pvDataCreate->createPVStructure(fieldCreate->createStructure());
return fieldCreate->createStructure()->build();
}
size_t offsetRecord = request.find("record[");
size_t offsetField = request.find("field(");
@@ -437,7 +437,7 @@ struct CreateRequestImpl {
}
StructureConstPtr structure = fieldCreate->createStructure(names, fields);
if(!structure) throw std::invalid_argument("bad request " + crequest);
PVStructurePtr pvStructure = pvDataCreate->createPVStructure(structure);
PVStructurePtr pvStructure = structure->build();
for(size_t i=0; i<optionList.size(); ++i) {
OptionPair pair = optionList[i];
string name = pair.name;
@@ -483,4 +483,3 @@ PVStructure::shared_pointer createRequest(std::string const & request)
}} // namespace

View File

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

View File

@@ -1,232 +0,0 @@
/* pvCopy.h */
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
/**
* @author Marty Kraimer
* @date 2013.04
*/
#ifndef PVCOPY_H
#define PVCOPY_H
#include <string>
#include <stdexcept>
#include <memory>
#include <shareLib.h>
#include <pv/pvData.h>
#include <pv/bitSet.h>
namespace epics { namespace pvData{
class PVCopyTraverseMasterCallback;
typedef std::tr1::shared_ptr<PVCopyTraverseMasterCallback> PVCopyTraverseMasterCallbackPtr;
/**
* @brief Callback for traversing master structure
*
* Must be implemented by code that creates pvCopy.
*/
class epicsShareClass PVCopyTraverseMasterCallback
{
public:
POINTER_DEFINITIONS(PVCopyTraverseMasterCallback);
virtual ~PVCopyTraverseMasterCallback() {}
/**
* Called once for each field in master.
* @param pvField The field in master.
*/
virtual void nextMasterPVField(epics::pvData::PVFieldPtr const &pvField) = 0;
};
class PVCopy;
typedef std::tr1::shared_ptr<PVCopy> PVCopyPtr;
struct CopyNode;
typedef std::tr1::shared_ptr<CopyNode> CopyNodePtr;
struct CopyMasterNode;
typedef std::tr1::shared_ptr<CopyMasterNode> CopyMasterNodePtr;
struct CopyStructureNode;
typedef std::tr1::shared_ptr<CopyStructureNode> CopyStructureNodePtr;
/**
* @brief Support for subset of fields in a pvStructure.
*
* Class that manages one or more PVStructures that holds an arbitrary subset of the fields
* in another PVStructure called master.
*/
class epicsShareClass PVCopy :
public std::tr1::enable_shared_from_this<PVCopy>
{
public:
POINTER_DEFINITIONS(PVCopy);
/**
* Create a new pvCopy
* @param pvMaster The top-level structure for which a copy of
* an arbitrary subset of the fields in master will be created and managed.
* @param pvRequest Selects the set of subfields desired and options for each field.
* @param structureName The name for the top level of any PVStructure created.
*/
static PVCopyPtr create(
PVStructurePtr const &pvMaster,
PVStructurePtr const &pvRequest,
std::string const & structureName);
virtual ~PVCopy(){}
void destroy();
/**
* Get the top-level structure of master
* @returns The master top-level structure.
* This should not be modified.
*/
PVStructurePtr getPVMaster();
/**
* Traverse all the fields in master.
* @param callback This is called for each field on master.
*/
void traverseMaster(PVCopyTraverseMasterCallbackPtr const & callback)
{
traverseMaster(headNode,callback);
}
/**
* Get the introspection interface for a PVStructure for e copy.
*/
StructureConstPtr getStructure();
/**
* Create a copy instance. Monitors keep a queue of monitor elements.
* Since each element needs a PVStructure, multiple top-level structures will be created.
*/
PVStructurePtr createPVStructure();
/**
* Given a field in pvMaster. return the offset in copy for the same field.
* A value of std::string::npos means that the copy does not have this field.
* @param masterPVField The field in master.
*/
std::size_t getCopyOffset(PVFieldPtr const &masterPVField);
/**
* Given a field in pvMaster. return the offset in copy for the same field.
* A value of std::string::npos means that the copy does not have this field.
* @param masterPVStructure A structure in master that has masterPVField.
* @param masterPVField The field in master.
*/
std::size_t getCopyOffset(
PVStructurePtr const &masterPVStructure,
PVFieldPtr const &masterPVField);
/**
* Given an offset in the copy get the corresponding field in pvMaster.
* @param structureOffset The offset in the copy.
*/
PVFieldPtr getMasterPVField(std::size_t structureOffset);
/**
* Initialize the fields in copyPVStructure by giving each field
* the value from the corresponding field in pvMaster.
* bitSet will be set to show that all fields are changed.
* @param copyPVStructure A copy top-level structure.
* @param bitSet A bitSet for copyPVStructure.
*/
void initCopy(
PVStructurePtr const &copyPVStructure,
BitSetPtr const &bitSet);
/**
* Set all fields in copyPVStructure to the value of the corresponding field in pvMaster.
* Each field that is changed has it's corresponding bit set in bitSet.
* @param copyPVStructure A copy top-level structure.
* @param bitSet A bitSet for copyPVStructure.
*/
void updateCopySetBitSet(
PVStructurePtr const &copyPVStructure,
BitSetPtr const &bitSet);
/**
* For each set bit in bitSet
* set the field in copyPVStructure to the value of the corresponding field in pvMaster.
* @param copyPVStructure A copy top-level structure.
* @param bitSet A bitSet for copyPVStructure.
*/
void updateCopyFromBitSet(
PVStructurePtr const &copyPVStructure,
BitSetPtr const &bitSet);
/**
* For each set bit in bitSet
* set the field in pvMaster to the value of the corresponding field in copyPVStructure
* @param copyPVStructure A copy top-level structure.
* @param bitSet A bitSet for copyPVStructure.
*/
void updateMaster(
PVStructurePtr const &copyPVStructure,
BitSetPtr const &bitSet);
/**
* Get the options for the field at the specified offset.
* @param fieldOffset the offset in copy.
* @returns A NULL is returned if no options were specified for the field.
* If options were specified,PVStructurePtr is a structures
* with a set of PVString subfields that specify name,value pairs.s
* name is the subField name and value is the subField value.
*/
PVStructurePtr getOptions(std::size_t fieldOffset);
/**
* For debugging.
*/
std::string dump();
private:
void dump(
std::string *builder,
CopyNodePtr const &node,
int indentLevel);
PVCopyPtr getPtrSelf()
{
return shared_from_this();
}
void traverseMaster(CopyNodePtr const &node, PVCopyTraverseMasterCallbackPtr const & callback);
PVStructurePtr pvMaster;
StructureConstPtr structure;
CopyNodePtr headNode;
PVStructurePtr cacheInitStructure;
PVCopy(PVStructurePtr const &pvMaster);
friend class PVCopyMonitor;
bool init(PVStructurePtr const &pvRequest);
std::string dump(
std::string const &value,
CopyNodePtr const &node,
int indentLevel);
StructureConstPtr createStructure(
PVStructurePtr const &pvMaster,
PVStructurePtr const &pvFromRequest);
CopyNodePtr createStructureNodes(
PVStructurePtr const &pvMasterStructure,
PVStructurePtr const &pvFromRequest,
PVStructurePtr const &pvFromField);
void updateStructureNodeSetBitSet(
PVStructurePtr const &pvCopy,
CopyStructureNodePtr const &structureNode,
BitSetPtr const &bitSet);
void updateSubFieldSetBitSet(
PVFieldPtr const &pvCopy,
PVFieldPtr const &pvMaster,
BitSetPtr const &bitSet);
void updateStructureNodeFromBitSet(
PVStructurePtr const &pvCopy,
CopyStructureNodePtr const &structureNode,
BitSetPtr const &bitSet,
bool toCopy,
bool doAll);
void updateSubFieldFromBitSet(
PVFieldPtr const &pvCopy,
PVFieldPtr const &pvMasterField,
BitSetPtr const &bitSet,
bool toCopy,
bool doAll);
CopyMasterNodePtr getCopyOffset(
CopyStructureNodePtr const &structureNode,
PVFieldPtr const &masterPVField);
CopyMasterNodePtr getMasterNode(
CopyStructureNodePtr const &structureNode,
std::size_t structureOffset);
};
}}
#endif /* PVCOPY_H */

View File

@@ -1,649 +0,0 @@
/* pvCopy.cpp */
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
/**
* @author Marty Kraimer
* @date 2013.04
*/
#include <string>
#include <stdexcept>
#include <memory>
#include <sstream>
#include <epicsThread.h>
#define epicsExportSharedSymbols
#include <pv/thread.h>
#include <pv/pvCopy.h>
using std::tr1::static_pointer_cast;
using std::tr1::dynamic_pointer_cast;
using std::string;
using std::size_t;
using std::cout;
using std::endl;
namespace epics { namespace pvData {
/**
* Convenience method for implementing dump.
* It generates a newline and inserts blanks at the beginning of the newline.
* @param builder The std::string * being constructed.
* @param indentLevel Indent level, Each level is four spaces.
*/
static void newLine(string *buffer, int indentLevel)
{
*buffer += "\n";
*buffer += string(indentLevel*4, ' ');
}
struct CopyNode {
CopyNode()
: isStructure(false),
structureOffset(0),
nfields(0)
{}
bool isStructure;
size_t structureOffset; // In the copy
size_t nfields;
PVStructurePtr options;
};
struct CopyMasterNode : public CopyNode{
PVFieldPtr masterPVField;
};
typedef std::vector<CopyNodePtr> CopyNodePtrArray;
typedef std::tr1::shared_ptr<CopyNodePtrArray> CopyNodePtrArrayPtr;
struct CopyStructureNode : public CopyNode {
CopyNodePtrArrayPtr nodes;
};
PVCopyPtr PVCopy::create(
PVStructurePtr const &pvMaster,
PVStructurePtr const &pvRequest,
string const & structureName)
{
PVStructurePtr pvStructure(pvRequest);
if(structureName.size()>0) {
if(pvRequest->getStructure()->getNumberFields()>0) {
pvStructure = pvRequest->getSubField<PVStructure>(structureName);
if(!pvStructure) return PVCopyPtr();
}
} else if(pvStructure->getSubField<PVStructure>("field")) {
pvStructure = pvRequest->getSubField<PVStructure>("field");
}
PVCopyPtr pvCopy(new PVCopy(pvMaster));
bool result = pvCopy->init(pvStructure);
if(!result) pvCopy.reset();
return pvCopy;
}
PVCopy::PVCopy(
PVStructurePtr const &pvMaster)
: pvMaster(pvMaster)
{
}
void PVCopy::destroy()
{
headNode.reset();
}
PVStructurePtr PVCopy::getPVMaster()
{
return pvMaster;
}
void PVCopy::traverseMaster(CopyNodePtr const &innode, PVCopyTraverseMasterCallbackPtr const & callback)
{
CopyNodePtr node = innode;
if(!node->isStructure) {
CopyMasterNodePtr masterNode = static_pointer_cast<CopyMasterNode>(node);
callback->nextMasterPVField(masterNode->masterPVField);
return;
}
CopyStructureNodePtr structNode = static_pointer_cast<CopyStructureNode>(node);
CopyNodePtrArrayPtr nodes = structNode->nodes;
for(size_t i=0; i< nodes->size(); i++) {
node = (*nodes)[i];
traverseMaster(node,callback);
}
}
StructureConstPtr PVCopy::getStructure()
{
return structure;
}
PVStructurePtr PVCopy::createPVStructure()
{
if(cacheInitStructure) {
PVStructurePtr save = cacheInitStructure;
cacheInitStructure.reset();
return save;
}
PVStructurePtr pvStructure =
getPVDataCreate()->createPVStructure(structure);
return pvStructure;
}
PVStructurePtr PVCopy::getOptions(std::size_t fieldOffset)
{
if(fieldOffset==0) return headNode->options;
CopyNodePtr node = headNode;
while(true) {
if(!node->isStructure) {
if(node->structureOffset==fieldOffset) return node->options;
return PVStructurePtr();
}
CopyStructureNodePtr structNode = static_pointer_cast<CopyStructureNode>(node);
CopyNodePtrArrayPtr nodes = structNode->nodes;
boolean okToContinue = false;
for(size_t i=0; i< nodes->size(); i++) {
node = (*nodes)[i];
size_t soff = node->structureOffset;
if(fieldOffset>=soff && fieldOffset<soff+node->nfields) {
if(fieldOffset==soff) return node->options;
if(!node->isStructure) {
return PVStructurePtr();
}
okToContinue = true;
break;
}
}
if(okToContinue) continue;
throw std::invalid_argument("fieldOffset not valid");
}
}
size_t PVCopy::getCopyOffset(PVFieldPtr const &masterPVField)
{
if(masterPVField->getFieldOffset()==0) return 0;
if(!headNode->isStructure) {
CopyMasterNodePtr masterNode = static_pointer_cast<CopyMasterNode>(headNode);
if((masterNode->masterPVField.get())==masterPVField.get()) {
return headNode->structureOffset;
}
PVStructure * parent = masterPVField->getParent();
size_t offsetParent = parent->getFieldOffset();
size_t off = masterPVField->getFieldOffset();
size_t offdiff = off -offsetParent;
if(offdiff<masterNode->nfields) return headNode->structureOffset + offdiff;
return string::npos;
}
CopyStructureNodePtr node = static_pointer_cast<CopyStructureNode>(headNode);
CopyMasterNodePtr masterNode = getCopyOffset(node,masterPVField);
if(masterNode) return masterNode->structureOffset;
return string::npos;
}
size_t PVCopy::getCopyOffset(
PVStructurePtr const &masterPVStructure,
PVFieldPtr const &masterPVField)
{
CopyMasterNodePtr masterNode;
if(!headNode->isStructure) {
masterNode = static_pointer_cast<CopyMasterNode>(headNode);
if(masterNode->masterPVField.get()!=masterPVStructure.get()) return string::npos;
} else {
CopyStructureNodePtr node = static_pointer_cast<CopyStructureNode>(headNode);
masterNode = getCopyOffset(node,masterPVField);
}
if(!masterNode) return string::npos;
size_t diff = masterPVField->getFieldOffset()
- masterPVStructure->getFieldOffset();
return masterNode->structureOffset + diff;
}
PVFieldPtr PVCopy::getMasterPVField(size_t structureOffset)
{
CopyMasterNodePtr masterNode;
if(!headNode->isStructure) {
masterNode = static_pointer_cast<CopyMasterNode>(headNode);
} else {
CopyStructureNodePtr node = static_pointer_cast<CopyStructureNode>(headNode);
masterNode = getMasterNode(node,structureOffset);
}
if(!masterNode) {
throw std::invalid_argument(
"PVCopy::getMasterPVField: setstructureOffset not valid");
}
size_t diff = structureOffset - masterNode->structureOffset;
PVFieldPtr pvMasterField = masterNode->masterPVField;
if(diff==0) return pvMasterField;
PVStructurePtr pvStructure
= static_pointer_cast<PVStructure>(pvMasterField);
return pvStructure->getSubField(
pvMasterField->getFieldOffset() + diff);
}
void PVCopy::initCopy(
PVStructurePtr const &copyPVStructure,
BitSetPtr const &bitSet)
{
bitSet->clear();
bitSet->set(0);
updateCopyFromBitSet(copyPVStructure,bitSet);
}
void PVCopy::updateCopySetBitSet(
PVStructurePtr const &copyPVStructure,
BitSetPtr const &bitSet)
{
if(headNode->isStructure) {
CopyStructureNodePtr node = static_pointer_cast<CopyStructureNode>(headNode);
updateStructureNodeSetBitSet(copyPVStructure,node,bitSet);
} else {
CopyMasterNodePtr masterNode = static_pointer_cast<CopyMasterNode>(headNode);
PVFieldPtr pvMasterField= masterNode->masterPVField;
PVFieldPtr copyPVField = copyPVStructure;
PVFieldPtr pvField = pvMasterField;
if(pvField->getField()->getType()==epics::pvData::structure) {
updateSubFieldSetBitSet(copyPVField,pvMasterField,bitSet);
return;
}
bool isEqual = (*copyPVField == *pvField);
if(!isEqual) {
copyPVField->copyUnchecked(*pvField);
bitSet->set(copyPVField->getFieldOffset());
}
}
}
void PVCopy::updateCopyFromBitSet(
PVStructurePtr const &copyPVStructure,
BitSetPtr const &bitSet)
{
bool doAll = bitSet->get(0);
if(headNode->isStructure) {
CopyStructureNodePtr node = static_pointer_cast<CopyStructureNode>(headNode);
updateStructureNodeFromBitSet(copyPVStructure,node,bitSet,true,doAll);
} else {
CopyMasterNodePtr masterNode = static_pointer_cast<CopyMasterNode>(headNode);
updateSubFieldFromBitSet(copyPVStructure, masterNode->masterPVField,bitSet, true,doAll);
}
}
void PVCopy::updateMaster(
PVStructurePtr const &copyPVStructure,
BitSetPtr const &bitSet)
{
bool doAll = bitSet->get(0);
if(headNode->isStructure) {
CopyStructureNodePtr node =
static_pointer_cast<CopyStructureNode>(headNode);
updateStructureNodeFromBitSet(
copyPVStructure,node,bitSet,false,doAll);
} else {
CopyMasterNodePtr masterNode =
static_pointer_cast<CopyMasterNode>(headNode);
updateSubFieldFromBitSet( copyPVStructure,masterNode->masterPVField,bitSet,false,doAll);
}
}
string PVCopy::dump()
{
string builder;
dump(&builder,headNode,0);
return builder;
}
void PVCopy::dump(string *builder,CopyNodePtr const &node,int indentLevel)
{
newLine(builder,indentLevel);
std::stringstream ss;
ss << (node->isStructure ? "structureNode" : "masterNode");
ss << " structureOffset " << node->structureOffset;
ss << " nfields " << node->nfields;
*builder += ss.str();
PVStructurePtr options = node->options;
if(options) {
newLine(builder,indentLevel +1);
// TODO !!! ugly
std::ostringstream oss;
oss << *options;
*builder += oss.str();
newLine(builder,indentLevel);
}
if(!node->isStructure) {
CopyMasterNodePtr masterNode = static_pointer_cast<CopyMasterNode>(node);
string name = masterNode->masterPVField->getFullName();
*builder += " masterField " + name;
return;
}
CopyStructureNodePtr structureNode =
static_pointer_cast<CopyStructureNode>(node);
CopyNodePtrArrayPtr nodes = structureNode->nodes;
for(size_t i=0; i<nodes->size(); ++i) {
if((*nodes)[i].get()==NULL) {
newLine(builder,indentLevel +1);
ss.str("");
ss << "node[" << i << "] is null";
*builder += ss.str();
continue;
}
dump(builder,(*nodes)[i],indentLevel+1);
}
}
bool PVCopy::init(epics::pvData::PVStructurePtr const &pvRequest)
{
PVStructurePtr pvMasterStructure = pvMaster;
size_t len = pvRequest->getPVFields().size();
bool entireMaster = false;
if(len==string::npos) entireMaster = true;
if(len==0) entireMaster = true;
PVStructurePtr pvOptions;
if(len==1) {
pvOptions = pvRequest->getSubField<PVStructure>("_options");
}
if(entireMaster) {
structure = pvMasterStructure->getStructure();
CopyMasterNodePtr masterNode(new CopyMasterNode());
headNode = masterNode;
masterNode->options = pvOptions;
masterNode->isStructure = false;
masterNode->structureOffset = 0;
masterNode->masterPVField = pvMasterStructure;
masterNode->nfields = pvMasterStructure->getNumberFields();
return true;
}
structure = createStructure(pvMasterStructure,pvRequest);
if(!structure) return false;
cacheInitStructure = createPVStructure();
headNode = createStructureNodes(
pvMaster,
pvRequest,
cacheInitStructure);
return true;
}
string PVCopy::dump(
string const &value,
CopyNodePtr const &node,
int indentLevel)
{
throw std::logic_error(string("Not Implemented"));
}
StructureConstPtr PVCopy::createStructure(
PVStructurePtr const &pvMaster,
PVStructurePtr const &pvFromRequest)
{
if(pvFromRequest->getStructure()->getNumberFields()==0) {
return pvMaster->getStructure();
}
PVFieldPtrArray const &pvFromRequestFields = pvFromRequest->getPVFields();
StringArray const &fromRequestFieldNames = pvFromRequest->getStructure()->getFieldNames();
size_t length = pvFromRequestFields.size();
if(length==0) return StructureConstPtr();
FieldConstPtrArray fields; fields.reserve(length);
StringArray fieldNames; fields.reserve(length);
for(size_t i=0; i<length; ++i) {
string const &fieldName = fromRequestFieldNames[i];
PVFieldPtr pvMasterField = pvMaster->getSubField(fieldName);
if(!pvMasterField) continue;
FieldConstPtr field = pvMasterField->getField();
if(field->getType()==epics::pvData::structure) {
PVStructurePtr pvRequestStructure = static_pointer_cast<PVStructure>(
pvFromRequestFields[i]);
if(pvRequestStructure->getNumberFields()>0) {
StringArray const &names = pvRequestStructure->getStructure()->
getFieldNames();
size_t num = names.size();
if(num>0 && names[0].compare("_options")==0) --num;
if(num>0) {
if(pvMasterField->getField()->getType()!=epics::pvData::structure) continue;
fieldNames.push_back(fieldName);
fields.push_back(createStructure(
static_pointer_cast<PVStructure>(pvMasterField),
pvRequestStructure));
continue;
}
}
}
fieldNames.push_back(fieldName);
fields.push_back(field);
}
size_t numsubfields = fields.size();
if(numsubfields==0) return StructureConstPtr();
return getFieldCreate()->createStructure(fieldNames, fields);
}
CopyNodePtr PVCopy::createStructureNodes(
PVStructurePtr const &pvMasterStructure,
PVStructurePtr const &pvFromRequest,
PVStructurePtr const &pvFromCopy)
{
PVFieldPtrArray const & copyPVFields = pvFromCopy->getPVFields();
PVStructurePtr pvOptions;
PVFieldPtr pvField = pvFromRequest->getSubField("_options");
if(pvField) pvOptions = static_pointer_cast<PVStructure>(pvField);
size_t number = copyPVFields.size();
CopyNodePtrArrayPtr nodes(new CopyNodePtrArray());
nodes->reserve(number);
for(size_t i=0; i<number; i++) {
PVFieldPtr copyPVField = copyPVFields[i];
string fieldName = copyPVField->getFieldName();
PVStructurePtr requestPVStructure = pvFromRequest->getSubField<PVStructure>(fieldName);
PVStructurePtr pvSubFieldOptions = requestPVStructure->getSubField<PVStructure>("_options");
PVFieldPtr pvMasterField;
PVFieldPtrArray const & pvMasterFields = pvMasterStructure->getPVFields();
for(size_t j=0; i<pvMasterFields.size(); j++ ) {
if(pvMasterFields[j]->getFieldName().compare(fieldName)==0) {
pvMasterField = pvMasterFields[j];
break;
}
}
size_t numberRequest = requestPVStructure->getPVFields().size();
if(pvSubFieldOptions) numberRequest--;
if(numberRequest>0) {
nodes->push_back(createStructureNodes(
static_pointer_cast<PVStructure>(pvMasterField),
requestPVStructure,
static_pointer_cast<PVStructure>(copyPVField)));
continue;
}
CopyMasterNodePtr masterNode(new CopyMasterNode());
masterNode->options = pvSubFieldOptions;
masterNode->isStructure = false;
masterNode->masterPVField = pvMasterField;
masterNode->nfields = copyPVField->getNumberFields();
masterNode->structureOffset = copyPVField->getFieldOffset();
nodes->push_back(masterNode);
}
CopyStructureNodePtr structureNode(new CopyStructureNode());
structureNode->isStructure = true;
structureNode->nodes = nodes;
structureNode->structureOffset = pvFromCopy->getFieldOffset();
structureNode->nfields = pvFromCopy->getNumberFields();
structureNode->options = pvOptions;
return structureNode;
}
void PVCopy::updateStructureNodeSetBitSet(
PVStructurePtr const &pvCopy,
CopyStructureNodePtr const &structureNode,
epics::pvData::BitSetPtr const &bitSet)
{
for(size_t i=0; i<structureNode->nodes->size(); i++) {
CopyNodePtr node = (*structureNode->nodes)[i];
PVFieldPtr pvField = pvCopy->getSubField(node->structureOffset);
if(node->isStructure) {
PVStructurePtr xxx = static_pointer_cast<PVStructure>(pvField);
CopyStructureNodePtr yyy =
static_pointer_cast<CopyStructureNode>(node);
updateStructureNodeSetBitSet(xxx,yyy,bitSet);
} else {
CopyMasterNodePtr masterNode =
static_pointer_cast<CopyMasterNode>(node);
updateSubFieldSetBitSet(pvField,masterNode->masterPVField,bitSet);
}
}
}
void PVCopy::updateSubFieldSetBitSet(
PVFieldPtr const &pvCopy,
PVFieldPtr const &pvMaster,
BitSetPtr const &bitSet)
{
FieldConstPtr field = pvCopy->getField();
Type type = field->getType();
if(type!=epics::pvData::structure) {
bool isEqual = (*pvCopy == *pvMaster);
if(isEqual) {
if(type==structureArray) {
// always act as though a change occurred.
// Note that array elements are shared.
bitSet->set(pvCopy->getFieldOffset());
}
}
if(isEqual) return;
pvCopy->copyUnchecked(*pvMaster);
bitSet->set(pvCopy->getFieldOffset());
return;
}
PVStructurePtr pvCopyStructure = static_pointer_cast<PVStructure>(pvCopy);
PVFieldPtrArray const & pvCopyFields = pvCopyStructure->getPVFields();
PVStructurePtr pvMasterStructure =
static_pointer_cast<PVStructure>(pvMaster);
PVFieldPtrArray const & pvMasterFields =
pvMasterStructure->getPVFields();
size_t length = pvCopyFields.size();
for(size_t i=0; i<length; i++) {
updateSubFieldSetBitSet(pvCopyFields[i],pvMasterFields[i],bitSet);
}
}
void PVCopy::updateStructureNodeFromBitSet(
PVStructurePtr const &pvCopy,
CopyStructureNodePtr const &structureNode,
BitSetPtr const &bitSet,
bool toCopy,
bool doAll)
{
size_t offset = structureNode->structureOffset;
if(!doAll) {
size_t nextSet = bitSet->nextSetBit(offset);
if(nextSet==string::npos) return;
}
if(offset>=pvCopy->getNextFieldOffset()) return;
if(!doAll) doAll = bitSet->get(offset);
CopyNodePtrArrayPtr nodes = structureNode->nodes;
for(size_t i=0; i<nodes->size(); i++) {
CopyNodePtr node = (*nodes)[i];
PVFieldPtr pvField = pvCopy->getSubFieldT(node->structureOffset);
if(node->isStructure) {
PVStructurePtr xxx = static_pointer_cast<PVStructure>(pvField);
CopyStructureNodePtr subStructureNode =
static_pointer_cast<CopyStructureNode>(node);
updateStructureNodeFromBitSet(
xxx,subStructureNode,bitSet,toCopy,doAll);
} else {
CopyMasterNodePtr masterNode =
static_pointer_cast<CopyMasterNode>(node);
updateSubFieldFromBitSet(
pvField,masterNode->masterPVField,bitSet,toCopy,doAll);
}
}
}
void PVCopy::updateSubFieldFromBitSet(
PVFieldPtr const &pvCopy,
PVFieldPtr const &pvMasterField,
BitSetPtr const &bitSet,
bool toCopy,
bool doAll)
{
if(!doAll) {
doAll = bitSet->get(pvCopy->getFieldOffset());
}
if(!doAll) {
size_t offset = pvCopy->getFieldOffset();
size_t nextSet = bitSet->nextSetBit(offset);
if(nextSet==string::npos) return;
if(nextSet>=pvCopy->getNextFieldOffset()) return;
}
if(pvCopy->getField()->getType()==epics::pvData::structure) {
PVStructurePtr pvCopyStructure =
static_pointer_cast<PVStructure>(pvCopy);
PVFieldPtrArray const & pvCopyFields = pvCopyStructure->getPVFields();
if(pvMasterField->getField()->getType() !=epics::pvData::structure)
{
throw std::logic_error(string("Logic error"));
}
PVStructurePtr pvMasterStructure =
static_pointer_cast<PVStructure>(pvMasterField);
PVFieldPtrArray const & pvMasterFields =
pvMasterStructure->getPVFields();
for(size_t i=0; i<pvCopyFields.size(); i++) {
updateSubFieldFromBitSet(
pvCopyFields[i],
pvMasterFields[i],
bitSet,toCopy,doAll);
}
} else {
if(toCopy) {
pvCopy->copyUnchecked(*pvMasterField);
} else {
pvMasterField->copyUnchecked(*pvCopy);
}
}
}
CopyMasterNodePtr PVCopy::getCopyOffset(
CopyStructureNodePtr const &structureNode,
PVFieldPtr const &masterPVField)
{
size_t offset = masterPVField->getFieldOffset();
CopyNodePtrArrayPtr nodes = structureNode->nodes;
for(size_t i=0; i< nodes->size(); i++) {
CopyNodePtr node = (*nodes)[i];
if(!node->isStructure) {
CopyMasterNodePtr masterNode =
static_pointer_cast<CopyMasterNode>(node);
size_t off = masterNode->masterPVField->getFieldOffset();
size_t nextOffset = masterNode->masterPVField->getNextFieldOffset();
if(offset>= off && offset<nextOffset) return masterNode;
} else {
CopyStructureNodePtr subNode =
static_pointer_cast<CopyStructureNode>(node);
CopyMasterNodePtr masterNode =
getCopyOffset(subNode,masterPVField);
if(masterNode) return masterNode;
}
}
return CopyMasterNodePtr();
}
CopyMasterNodePtr PVCopy::getMasterNode(
CopyStructureNodePtr const &structureNode,
std::size_t structureOffset)
{
CopyNodePtrArrayPtr nodes = structureNode->nodes;
for(size_t i=0; i< nodes->size(); i++) {
CopyNodePtr node = (*nodes)[i];
if(structureOffset>=(node->structureOffset + node->nfields)) continue;
if(!node->isStructure) {
CopyMasterNodePtr masterNode =
static_pointer_cast<CopyMasterNode>(node);
return masterNode;
}
CopyStructureNodePtr subNode =
static_pointer_cast<CopyStructureNode>(node);
return getMasterNode(subNode,structureOffset);
}
return CopyMasterNodePtr();
}
}}

328
src/copy/requestmapper.cpp Normal file
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,58 +33,58 @@ bool operator==(const Field& a, const Field& b)
case scalar: {
const Scalar &A=static_cast<const Scalar&>(a);
const Scalar &B=static_cast<const Scalar&>(b);
return A==B;
return compare(A, B);
}
case scalarArray: {
const ScalarArray &A=static_cast<const ScalarArray&>(a);
const ScalarArray &B=static_cast<const ScalarArray&>(b);
return A==B;
return compare(A, B);
}
case structure: {
const Structure &A=static_cast<const Structure&>(a);
const Structure &B=static_cast<const Structure&>(b);
return A==B;
return compare(A, B);
}
case structureArray: {
const StructureArray &A=static_cast<const StructureArray&>(a);
const StructureArray &B=static_cast<const StructureArray&>(b);
return A==B;
return compare(A, B);
}
case union_: {
const Union &A=static_cast<const Union&>(a);
const Union &B=static_cast<const Union&>(b);
return A==B;
return compare(A, B);
}
case unionArray: {
const UnionArray &A=static_cast<const UnionArray&>(a);
const UnionArray &B=static_cast<const UnionArray&>(b);
return A==B;
return compare(A, B);
}
default:
throw std::logic_error("Invalid Field type in comparison");
}
}
bool operator==(const Scalar& a, const Scalar& b)
bool compare(const Scalar& a, const Scalar& b)
{
if(&a==&b)
return true;
return a.getScalarType()==b.getScalarType();
}
bool operator==(const ScalarArray& a, const ScalarArray& b)
bool compare(const ScalarArray& a, const ScalarArray& b)
{
if(&a==&b)
return true;
return a.getElementType()==b.getElementType();
}
bool operator==(const Structure& a, const Structure& b)
bool compare(const Structure& a, const Structure& b)
{
if(&a==&b)
return true;
if (a.getID()!=b.getID())
return false;
return false;
size_t nflds=a.getNumberFields();
if (b.getNumberFields()!=nflds)
return false;
@@ -101,17 +101,17 @@ bool operator==(const Structure& a, const Structure& b)
return std::equal( an.begin(), an.end(), bn.begin() );
}
bool operator==(const StructureArray& a, const StructureArray& b)
bool compare(const StructureArray& a, const StructureArray& b)
{
return *(a.getStructure().get())==*(b.getStructure().get());
}
bool operator==(const Union& a, const Union& b)
bool compare(const Union& a, const Union& b)
{
if(&a==&b)
return true;
if (a.getID()!=b.getID())
return false;
return false;
size_t nflds=a.getNumberFields();
if (b.getNumberFields()!=nflds)
return false;
@@ -128,12 +128,12 @@ bool operator==(const Union& a, const Union& b)
return std::equal( an.begin(), an.end(), bn.begin() );
}
bool operator==(const UnionArray& a, const UnionArray& b)
bool compare(const UnionArray& a, const UnionArray& b)
{
return *(a.getUnion().get())==*(b.getUnion().get());
}
bool operator==(const BoundedString& a, const BoundedString& b)
bool compare(const BoundedString& a, const BoundedString& b)
{
if(&a==&b)
return true;
@@ -268,7 +268,7 @@ bool compareField(const PVUnion* left, const PVUnion* right)
if(*ls!=*right->getUnion())
return false;
if (ls->isVariant())
{
const PVField::const_shared_pointer& lval = left->get();

View File

@@ -24,10 +24,10 @@ using std::tr1::static_pointer_cast;
using std::size_t;
using std::string;
namespace epics { namespace pvData {
namespace epics { namespace pvData {
static std::vector<string> split(string commaSeparatedList) {
static std::vector<string> split(const string& commaSeparatedList) {
string::size_type numValues = 1;
string::size_type index=0;
while(true) {
@@ -60,7 +60,7 @@ void Convert::getString(string *buf,PVField const *pvField,int /*indentLevel*/)
size_t Convert::fromString(PVStructurePtr const &pvStructure, StringArray const & from, size_t fromStartIndex)
{
size_t processed = 0;
PVFieldPtrArray const & fieldsData = pvStructure->getPVFields();
if (fieldsData.size() != 0) {
size_t length = pvStructure->getStructure()->getNumberFields();
@@ -93,7 +93,7 @@ size_t Convert::fromString(PVStructurePtr const &pvStructure, StringArray const
}
}
}
return processed;
}

View File

@@ -7,10 +7,6 @@
* @author mrk
*/
#if defined(_WIN32) && !defined(NOMINMAX)
#define NOMINMAX
#endif
#include <cstddef>
#include <cstdlib>
#include <string>
@@ -18,6 +14,8 @@
#include <stdexcept>
#include <sstream>
#include <epicsString.h>
#include <epicsStdio.h>
#include <epicsMutex.h>
#include <epicsThread.h>
@@ -27,6 +25,8 @@
#include <pv/pvIntrospect.h>
#include <pv/factory.h>
#include <pv/serializeHelper.h>
#include <pv/thread.h>
#include <pv/pvData.h>
using std::tr1::static_pointer_cast;
using std::size_t;
@@ -34,13 +34,54 @@ using std::string;
namespace epics { namespace pvData {
static DebugLevel debugLevel = lowDebug;
size_t Field::num_instances;
struct Field::Helper {
static unsigned hash(Field *fld) {
std::ostringstream key;
// hash the output of operator<<()
// not efficient, but stable within this process.
key<<(*fld);
unsigned H = epicsStrHash(key.str().c_str(), 0xbadc0de1);
fld->m_hash = H;
return H;
}
};
struct FieldCreate::Helper {
template<typename FLD>
static void cache(const FieldCreate *create, std::tr1::shared_ptr<FLD>& ent) {
unsigned hash = Field::Helper::hash(ent.get());
Lock G(create->mutex);
// we examine raw pointers stored in create->cache, which is safe under create->mutex
std::pair<cache_t::iterator, cache_t::iterator> itp(create->cache.equal_range(hash));
for(; itp.first!=itp.second; ++itp.first) {
Field* cent(itp.first->second);
FLD* centx(dynamic_cast<FLD*>(cent));
if(centx && compare(*centx, *ent)) {
try{
ent = std::tr1::static_pointer_cast<FLD>(cent->shared_from_this());
return;
}catch(std::tr1::bad_weak_ptr&){
// we're racing destruction.
// add a new entry.
// Field::~Field is in the process of removing this old one.
continue;
}
}
}
create->cache.insert(std::make_pair(hash, ent.get()));
// cache cleaned from Field::~Field
}
};
Field::Field(Type type)
: m_fieldType(type)
, m_hash(0)
{
REFTRACE_INCREMENT(num_instances);
}
@@ -49,10 +90,31 @@ Field::~Field() {
REFTRACE_DECREMENT(num_instances);
}
void Field::cacheCleanup()
{
const FieldCreatePtr& create(getFieldCreate());
Lock G(create->mutex);
std::pair<FieldCreate::cache_t::iterator, FieldCreate::cache_t::iterator> itp(create->cache.equal_range(m_hash));
for(; itp.first!=itp.second; ++itp.first) {
Field* cent(itp.first->second);
if(cent==this) {
create->cache.erase(itp.first);
return;
}
}
}
std::tr1::shared_ptr<PVField> Field::build() const
{
FieldConstPtr self(shared_from_this());
return getPVDataCreate()->createPVField(self);
}
std::ostream& operator<<(std::ostream& o, const Field& f)
{
return f.dump(o);
return f.dump(o);
};
Scalar::Scalar(ScalarType scalarType)
@@ -62,7 +124,10 @@ Scalar::Scalar(ScalarType scalarType)
THROW_EXCEPTION2(std::invalid_argument, "Can't construct Scalar from invalid ScalarType");
}
Scalar::~Scalar(){}
Scalar::~Scalar()
{
cacheCleanup();
}
std::ostream& Scalar::dump(std::ostream& o) const
{
@@ -72,18 +137,18 @@ std::ostream& Scalar::dump(std::ostream& o) const
string Scalar::getID() const
{
static const string idScalarLUT[] = {
"boolean", // pvBoolean
"byte", // pvByte
"short", // pvShort
"int", // pvInt
"long", // pvLong
"ubyte", // pvUByte
"ushort", // pvUShort
"uint", // pvUInt
"ulong", // pvULong
"float", // pvFloat
"double", // pvDouble
"string" // pvString
"boolean", // pvBoolean
"byte", // pvByte
"short", // pvShort
"int", // pvInt
"long", // pvLong
"ubyte", // pvUByte
"ushort", // pvUShort
"uint", // pvUInt
"ulong", // pvULong
"float", // pvFloat
"double", // pvDouble
"string" // pvString
};
return idScalarLUT[scalarType];
}
@@ -119,6 +184,11 @@ void Scalar::deserialize(ByteBuffer* /*buffer*/, DeserializableControl* /*contro
}
std::tr1::shared_ptr<PVScalar> Scalar::build() const
{
return getPVDataCreate()->createPVScalar(std::tr1::static_pointer_cast<const Scalar>(shared_from_this()));
}
std::string BoundedString::getID() const
@@ -147,20 +217,21 @@ BoundedString::BoundedString(std::size_t maxStringLength) :
THROW_EXCEPTION2(std::invalid_argument, "maxLength == 0");
}
BoundedString::~BoundedString() {}
BoundedString::~BoundedString()
{
cacheCleanup();
}
static string emptyStringtring;
static void serializeStructureField(const Structure* structure, ByteBuffer* buffer, SerializableControl* control)
{
// to optimize default (non-empty) IDs optimization
// empty IDs are not allowed
string id = structure->getID();
if (id == Structure::DEFAULT_ID) // TODO slow comparison
SerializeHelper::serializeString(emptyStringtring, buffer, control);
else
SerializeHelper::serializeString(id, buffer, control);
// to optimize default (non-empty) IDs optimization
// empty IDs are not allowed
string id = structure->getID();
if (id == Structure::DEFAULT_ID) // TODO slow comparison
SerializeHelper::serializeString(string(), buffer, control);
else
SerializeHelper::serializeString(id, buffer, control);
FieldConstPtrArray const & fields = structure->getFields();
StringArray const & fieldNames = structure->getFieldNames();
@@ -188,18 +259,18 @@ static StructureConstPtr deserializeStructureField(const FieldCreate* fieldCreat
if (id.empty())
return fieldCreate->createStructure(fieldNames, fields);
else
return fieldCreate->createStructure(id, fieldNames, fields);
return fieldCreate->createStructure(id, fieldNames, fields);
}
static void serializeUnionField(const Union* punion, ByteBuffer* buffer, SerializableControl* control)
{
// to optimize default (non-empty) IDs optimization
// empty IDs are not allowed
string id = punion->getID();
if (id == Union::DEFAULT_ID) // TODO slow comparison
SerializeHelper::serializeString(emptyStringtring, buffer, control);
else
SerializeHelper::serializeString(id, buffer, control);
// to optimize default (non-empty) IDs optimization
// empty IDs are not allowed
string id = punion->getID();
if (id == Union::DEFAULT_ID) // TODO slow comparison
SerializeHelper::serializeString(string(), buffer, control);
else
SerializeHelper::serializeString(id, buffer, control);
FieldConstPtrArray const & fields = punion->getFields();
StringArray const & fieldNames = punion->getFieldNames();
@@ -227,7 +298,7 @@ static UnionConstPtr deserializeUnionField(const FieldCreate* fieldCreate, ByteB
if (id.empty())
return fieldCreate->createUnion(fieldNames, fields);
else
return fieldCreate->createUnion(id, fieldNames, fields);
return fieldCreate->createUnion(id, fieldNames, fields);
}
Array::Array(Type type)
@@ -245,23 +316,26 @@ ScalarArray::ScalarArray(ScalarType elementType)
throw std::invalid_argument("Can't construct ScalarArray from invalid ScalarType");
}
ScalarArray::~ScalarArray() {}
ScalarArray::~ScalarArray()
{
cacheCleanup();
}
const string ScalarArray::getIDScalarArrayLUT() const
{
static const string idScalarArrayLUT[] = {
"boolean[]", // pvBoolean
"byte[]", // pvByte
"short[]", // pvShort
"int[]", // pvInt
"long[]", // pvLong
"ubyte[]", // pvUByte
"ushort[]", // pvUShort
"uint[]", // pvUInt
"ulong[]", // pvULong
"float[]", // pvFloat
"double[]", // pvDouble
"string[]" // pvString
"boolean[]", // pvBoolean
"byte[]", // pvByte
"short[]", // pvShort
"int[]", // pvInt
"long[]", // pvLong
"ubyte[]", // pvUByte
"ushort[]", // pvUShort
"uint[]", // pvUInt
"ulong[]", // pvULong
"float[]", // pvFloat
"double[]", // pvDouble
"string[]" // pvString
};
return idScalarArrayLUT[elementType];
}
@@ -285,8 +359,16 @@ void ScalarArray::deserialize(ByteBuffer* /*buffer*/, DeserializableControl* /*c
throw std::runtime_error("not valid operation, use FieldCreate::deserialize instead");
}
std::tr1::shared_ptr<PVScalarArray> ScalarArray::build() const
{
return getPVDataCreate()->createPVScalarArray(std::tr1::static_pointer_cast<const ScalarArray>(shared_from_this()));
}
BoundedScalarArray::~BoundedScalarArray() {}
BoundedScalarArray::~BoundedScalarArray()
{
cacheCleanup();
}
BoundedScalarArray::BoundedScalarArray(ScalarType elementType, size_t size)
: ScalarArray(elementType),
@@ -297,7 +379,8 @@ BoundedScalarArray::BoundedScalarArray(ScalarType elementType, size_t size)
string BoundedScalarArray::getID() const
{
char buffer[32];
sprintf(buffer, "%s<%zu>", ScalarTypeFunc::name(getElementType()), size);
epicsSnprintf(buffer, sizeof(buffer), "%s<%lu>",
ScalarTypeFunc::name(getElementType()), (unsigned long) size);
return string(buffer);
}
@@ -308,7 +391,10 @@ void BoundedScalarArray::serialize(ByteBuffer *buffer, SerializableControl *cont
}
FixedScalarArray::~FixedScalarArray() {}
FixedScalarArray::~FixedScalarArray()
{
cacheCleanup();
}
FixedScalarArray::FixedScalarArray(ScalarType elementType, size_t size)
: ScalarArray(elementType),
@@ -319,7 +405,8 @@ FixedScalarArray::FixedScalarArray(ScalarType elementType, size_t size)
string FixedScalarArray::getID() const
{
char buffer[32];
sprintf(buffer, "%s[%zu]", ScalarTypeFunc::name(getElementType()), size);
epicsSnprintf(buffer, sizeof(buffer), "%s[%lu]",
ScalarTypeFunc::name(getElementType()), (unsigned long) size);
return string(buffer);
}
@@ -336,13 +423,14 @@ StructureArray::StructureArray(StructureConstPtr const & structure)
{
}
StructureArray::~StructureArray() {
if(debugLevel==highDebug) printf("~StructureArray\n");
StructureArray::~StructureArray()
{
cacheCleanup();
}
string StructureArray::getID() const
{
return pstructure->getID() + "[]";
return pstructure->getID() + "[]";
}
std::ostream& StructureArray::dump(std::ostream& o) const
@@ -365,18 +453,24 @@ void StructureArray::deserialize(ByteBuffer* /*buffer*/, DeserializableControl*
throw std::runtime_error("not valid operation, use FieldCreate::deserialize instead");
}
std::tr1::shared_ptr<PVValueArray<std::tr1::shared_ptr<PVStructure> > > StructureArray::build() const
{
return getPVDataCreate()->createPVStructureArray(std::tr1::static_pointer_cast<const StructureArray>(shared_from_this()));
}
UnionArray::UnionArray(UnionConstPtr const & _punion)
: Array(unionArray),punion(_punion)
{
}
UnionArray::~UnionArray() {
if(debugLevel==highDebug) printf("~UnionArray\n");
UnionArray::~UnionArray()
{
cacheCleanup();
}
string UnionArray::getID() const
{
return punion->getID() + "[]";
return punion->getID() + "[]";
}
std::ostream& UnionArray::dump(std::ostream& o) const
@@ -407,6 +501,11 @@ void UnionArray::deserialize(ByteBuffer* /*buffer*/, DeserializableControl* /*co
throw std::runtime_error("not valid operation, use FieldCreate::deserialize instead");
}
std::tr1::shared_ptr<PVValueArray<std::tr1::shared_ptr<PVUnion> > > UnionArray::build() const
{
return getPVDataCreate()->createPVUnionArray(std::tr1::static_pointer_cast<const UnionArray>(shared_from_this()));
}
const string Structure::DEFAULT_ID = Structure::defaultId();
const string & Structure::defaultId()
@@ -451,12 +550,15 @@ Structure::Structure (
}
}
Structure::~Structure() { }
Structure::~Structure()
{
cacheCleanup();
}
string Structure::getID() const
{
return id;
return id;
}
FieldConstPtr Structure::getField(string const & fieldName) const {
@@ -478,6 +580,21 @@ size_t Structure::getFieldIndex(string const &fieldName) const {
return -1;
}
FieldConstPtr Structure::getFieldImpl(string const & fieldName, bool throws) const {
for(size_t i=0, N=fields.size(); i<N; i++)
if(fieldName==fieldNames[i])
return fields[i];
if (throws) {
std::stringstream ss;
ss << "Failed to get field: "
<< fieldName << " (not found)";
throw std::runtime_error(ss.str());
} else {
return FieldConstPtr();
}
}
std::ostream& Structure::dump(std::ostream& o) const
{
o << format::indent() << getID() << std::endl;
@@ -544,6 +661,11 @@ void Structure::deserialize(ByteBuffer* /*buffer*/, DeserializableControl* /*con
throw std::runtime_error("not valid operation, use FieldCreate::deserialize instead");
}
std::tr1::shared_ptr<PVStructure> Structure::build() const
{
return getPVDataCreate()->createPVStructure(std::tr1::static_pointer_cast<const Structure>(shared_from_this()));
}
const string Union::DEFAULT_ID = Union::defaultId();
const string & Union::defaultId()
@@ -609,7 +731,10 @@ Union::Union (
}
}
Union::~Union() { }
Union::~Union()
{
cacheCleanup();
}
int32 Union::guess(Type t, ScalarType s) const
{
@@ -649,7 +774,7 @@ int32 Union::guess(Type t, ScalarType s) const
string Union::getID() const
{
return id;
return id;
}
FieldConstPtr Union::getField(string const & fieldName) const {
@@ -672,6 +797,21 @@ size_t Union::getFieldIndex(string const &fieldName) const {
return -1;
}
FieldConstPtr Union::getFieldImpl(string const & fieldName, bool throws) const {
for(size_t i=0, N=fields.size(); i<N; i++)
if(fieldName==fieldNames[i])
return fields[i];
if (throws) {
std::stringstream ss;
ss << "Failed to get field: "
<< fieldName << " (not found)";
throw std::runtime_error(ss.str());
} else {
return FieldConstPtr();
}
}
std::ostream& Union::dump(std::ostream& o) const
{
o << format::indent() << getID() << std::endl;
@@ -742,6 +882,11 @@ void Union::deserialize(ByteBuffer* /*buffer*/, DeserializableControl* /*control
throw std::runtime_error("not valid operation, use FieldCreate::deserialize instead");
}
std::tr1::shared_ptr<PVUnion> Union::build() const
{
return getPVDataCreate()->createPVUnion(std::tr1::static_pointer_cast<const Union>(shared_from_this()));
}
FieldBuilder::FieldBuilder()
:fieldCreate(getFieldCreate())
,idSet(false)
@@ -824,7 +969,7 @@ FieldBuilder::FieldBuilder(const FieldBuilderPtr & _parentBuilder,
{}
FieldBuilder::FieldBuilder(FieldBuilderPtr const & _parentBuilder,
string const & _nestedName,
string const & _nestedName,
Type _nestedClassToBuild, bool _nestedArray)
:fieldCreate(_parentBuilder->fieldCreate)
,idSet(false)
@@ -837,16 +982,29 @@ FieldBuilder::FieldBuilder(FieldBuilderPtr const & _parentBuilder,
void FieldBuilder::reset()
{
id.erase();
id.erase();
idSet = false;
fieldNames.clear();
fields.clear();
fieldNames.clear();
fields.clear();
}
FieldBuilderPtr FieldBuilder::begin()
{
FieldBuilderPtr ret(new FieldBuilder);
return ret;
}
FieldBuilderPtr FieldBuilder::begin(StructureConstPtr S)
{
FieldBuilderPtr ret(new FieldBuilder(S.get()));
return ret;
}
FieldBuilderPtr FieldBuilder::setId(string const & id)
{
this->id = id;
idSet = true;
idSet = true;
return shared_from_this();
}
@@ -868,7 +1026,7 @@ FieldBuilderPtr FieldBuilder::add(string const & name, FieldConstPtr const & fie
} else if(*cur!=*field) {
THROW_EXCEPTION2(std::runtime_error, "duplicate field name w/ different type : "+name);
} // else exact duplicate is silently ignored
return shared_from_this();
return shared_from_this();
}
FieldBuilderPtr FieldBuilder::addArray(string const & name, ScalarType scalarType)
@@ -910,7 +1068,7 @@ FieldBuilderPtr FieldBuilder::addArray(string const & name, FieldConstPtr const
msg << element->getType();
THROW_EXCEPTION2(std::invalid_argument, msg.str());
}
return add(name, fld);
}
@@ -945,7 +1103,7 @@ StructureConstPtr FieldBuilder::createStructure()
{
if (parentBuilder.get())
THROW_EXCEPTION2(std::runtime_error, "createStructure() called in nested FieldBuilder");
StructureConstPtr field(static_pointer_cast<const Structure>(createFieldInternal(structure)));
reset();
return field;
@@ -955,7 +1113,7 @@ UnionConstPtr FieldBuilder::createUnion()
{
if (parentBuilder.get())
THROW_EXCEPTION2(std::runtime_error, "createUnion() called in nested FieldBuilder");
UnionConstPtr field(static_pointer_cast<const Union>(createFieldInternal(union_)));
reset();
return field;
@@ -1023,7 +1181,7 @@ FieldBuilderPtr FieldBuilder::endNested()
{
if (!parentBuilder)
THROW_EXCEPTION2(std::runtime_error, "FieldBuilder::endNested() can only be called to create nested fields");
FieldConstPtr nestedField = createFieldInternal(nestedClassToBuild);
if(createNested) {
@@ -1079,10 +1237,9 @@ ScalarConstPtr FieldCreate::createScalar(ScalarType scalarType) const
BoundedStringConstPtr FieldCreate::createBoundedString(std::size_t maxLength) const
{
// TODO use std::make_shared
std::tr1::shared_ptr<BoundedString> s(new BoundedString(maxLength), Field::Deleter());
BoundedStringConstPtr sa = s;
return sa;
std::tr1::shared_ptr<BoundedString> s(new BoundedString(maxLength));
Helper::cache(this, s);
return s;
}
ScalarArrayConstPtr FieldCreate::createScalarArray(ScalarType elementType) const
@@ -1092,7 +1249,7 @@ ScalarArrayConstPtr FieldCreate::createScalarArray(ScalarType elementType) const
strm << elementType;
THROW_EXCEPTION2(std::invalid_argument, strm.str());
}
return scalarArrays[elementType];
}
@@ -1104,10 +1261,9 @@ ScalarArrayConstPtr FieldCreate::createFixedScalarArray(ScalarType elementType,
THROW_EXCEPTION2(std::invalid_argument, strm.str());
}
// TODO use std::make_shared
std::tr1::shared_ptr<ScalarArray> s(new FixedScalarArray(elementType, size), Field::Deleter());
ScalarArrayConstPtr sa = s;
return sa;
std::tr1::shared_ptr<ScalarArray> s(new FixedScalarArray(elementType, size));
Helper::cache(this, s);
return s;
}
ScalarArrayConstPtr FieldCreate::createBoundedScalarArray(ScalarType elementType, size_t size) const
@@ -1118,10 +1274,9 @@ ScalarArrayConstPtr FieldCreate::createBoundedScalarArray(ScalarType elementType
THROW_EXCEPTION2(std::invalid_argument, strm.str());
}
// TODO use std::make_shared
std::tr1::shared_ptr<ScalarArray> s(new BoundedScalarArray(elementType, size), Field::Deleter());
ScalarArrayConstPtr sa = s;
return sa;
std::tr1::shared_ptr<ScalarArray> s(new BoundedScalarArray(elementType, size));
Helper::cache(this, s);
return s;
}
StructureConstPtr FieldCreate::createStructure () const
@@ -1178,10 +1333,9 @@ StructureConstPtr FieldCreate::createStructure (
StringArray const & fieldNames,FieldConstPtrArray const & fields) const
{
validateFieldNames(fieldNames);
// TODO use std::make_shared
std::tr1::shared_ptr<Structure> sp(new Structure(fieldNames,fields), Field::Deleter());
StructureConstPtr structure = sp;
return structure;
std::tr1::shared_ptr<Structure> sp(new Structure(fieldNames,fields));
Helper::cache(this, sp);
return sp;
}
StructureConstPtr FieldCreate::createStructure (
@@ -1190,29 +1344,26 @@ StructureConstPtr FieldCreate::createStructure (
FieldConstPtrArray const & fields) const
{
validateFieldNames(fieldNames);
// TODO use std::make_shared
std::tr1::shared_ptr<Structure> sp(new Structure(fieldNames,fields,id), Field::Deleter());
StructureConstPtr structure = sp;
return structure;
std::tr1::shared_ptr<Structure> sp(new Structure(fieldNames,fields,id));
Helper::cache(this, sp);
return sp;
}
StructureArrayConstPtr FieldCreate::createStructureArray(
StructureConstPtr const & structure) const
{
// TODO use std::make_shared
std::tr1::shared_ptr<StructureArray> sp(new StructureArray(structure), Field::Deleter());
StructureArrayConstPtr structureArray = sp;
return structureArray;
std::tr1::shared_ptr<StructureArray> sp(new StructureArray(structure));
Helper::cache(this, sp);
return sp;
}
UnionConstPtr FieldCreate::createUnion (
StringArray const & fieldNames,FieldConstPtrArray const & fields) const
{
validateFieldNames(fieldNames);
// TODO use std::make_shared
std::tr1::shared_ptr<Union> sp(new Union(fieldNames,fields), Field::Deleter());
UnionConstPtr punion = sp;
return punion;
std::tr1::shared_ptr<Union> sp(new Union(fieldNames,fields));
Helper::cache(this, sp);
return sp;
}
UnionConstPtr FieldCreate::createUnion (
@@ -1221,10 +1372,9 @@ UnionConstPtr FieldCreate::createUnion (
FieldConstPtrArray const & fields) const
{
validateFieldNames(fieldNames);
// TODO use std::make_shared
std::tr1::shared_ptr<Union> sp(new Union(fieldNames,fields,id), Field::Deleter());
UnionConstPtr punion = sp;
return punion;
std::tr1::shared_ptr<Union> sp(new Union(fieldNames,fields,id));
Helper::cache(this, sp);
return sp;
}
UnionConstPtr FieldCreate::createVariantUnion () const
@@ -1235,10 +1385,9 @@ UnionConstPtr FieldCreate::createVariantUnion () const
UnionArrayConstPtr FieldCreate::createUnionArray(
UnionConstPtr const & punion) const
{
// TODO use std::make_shared
std::tr1::shared_ptr<UnionArray> sp(new UnionArray(punion), Field::Deleter());
UnionArrayConstPtr unionArray = sp;
return unionArray;
std::tr1::shared_ptr<UnionArray> sp(new UnionArray(punion));
Helper::cache(this, sp);
return sp;
}
UnionArrayConstPtr FieldCreate::createVariantUnionArray () const
@@ -1302,7 +1451,7 @@ static int decodeScalar(int8 code)
pvUInt, // unsigned 32-bits
pvULong // unsigned 64-bits
};
static const int floatLUT[] =
{
-1, // reserved
@@ -1367,12 +1516,10 @@ FieldConstPtr FieldCreate::deserialize(ByteBuffer* buffer, DeserializableControl
size_t size = SerializeHelper::readSize(buffer, control);
// TODO use std::make_shared
std::tr1::shared_ptr<Field> sp(
new BoundedString(size),
Field::Deleter());
FieldConstPtr p = sp;
return p;
new BoundedString(size));
Helper::cache(this, sp);
return sp;
}
else
throw std::invalid_argument("invalid type encoding");
@@ -1397,19 +1544,17 @@ FieldConstPtr FieldCreate::deserialize(ByteBuffer* buffer, DeserializableControl
{
// TODO use std::make_shared
std::tr1::shared_ptr<Field> sp(
new FixedScalarArray(static_cast<epics::pvData::ScalarType>(scalarType), size),
Field::Deleter());
FieldConstPtr p = sp;
return p;
new FixedScalarArray(static_cast<epics::pvData::ScalarType>(scalarType), size));
Helper::cache(this, sp);
return sp;
}
else
{
// TODO use std::make_shared
std::tr1::shared_ptr<Field> sp(
new BoundedScalarArray(static_cast<epics::pvData::ScalarType>(scalarType), size),
Field::Deleter());
FieldConstPtr p = sp;
return p;
new BoundedScalarArray(static_cast<epics::pvData::ScalarType>(scalarType), size));
Helper::cache(this, sp);
return sp;
}
}
else if (typeCode == 0x80)
@@ -1421,9 +1566,9 @@ FieldConstPtr FieldCreate::deserialize(ByteBuffer* buffer, DeserializableControl
// Type type = Type.structureArray;
StructureConstPtr elementStructure = std::tr1::static_pointer_cast<const Structure>(control->cachedDeserialize(buffer));
// TODO use std::make_shared
std::tr1::shared_ptr<Field> sp(new StructureArray(elementStructure), Field::Deleter());
FieldConstPtr p = sp;
return p;
std::tr1::shared_ptr<Field> sp(new StructureArray(elementStructure));
Helper::cache(this, sp);
return sp;
}
else if (typeCode == 0x81)
{
@@ -1434,9 +1579,9 @@ FieldConstPtr FieldCreate::deserialize(ByteBuffer* buffer, DeserializableControl
// Type type = Type.unionArray;
UnionConstPtr elementUnion = std::tr1::static_pointer_cast<const Union>(control->cachedDeserialize(buffer));
// TODO use std::make_shared
std::tr1::shared_ptr<Field> sp(new UnionArray(elementUnion), Field::Deleter());
FieldConstPtr p = sp;
return p;
std::tr1::shared_ptr<Field> sp(new UnionArray(elementUnion));
Helper::cache(this, sp);
return sp;
}
else if (typeCode == 0x82)
{
@@ -1457,6 +1602,7 @@ struct field_factory {
FieldCreatePtr fieldCreate;
field_factory() :fieldCreate(new FieldCreate()) {
registerRefCounter("Field", &Field::num_instances);
registerRefCounter("Thread", &Thread::num_instances);
}
};
}
@@ -1485,23 +1631,21 @@ FieldCreate::FieldCreate()
{
for (int i = 0; i <= MAX_SCALAR_TYPE; i++)
{
// TODO use std::make_shared
std::tr1::shared_ptr<Scalar> sp(new Scalar(static_cast<ScalarType>(i)), Field::Deleter());
ScalarConstPtr p = sp;
scalars.push_back(p);
std::tr1::shared_ptr<Scalar> sp(new Scalar(static_cast<ScalarType>(i)));
Helper::cache(this, sp);
scalars.push_back(sp);
// TODO use std::make_shared
std::tr1::shared_ptr<ScalarArray> spa(new ScalarArray(static_cast<ScalarType>(i)), Field::Deleter());
ScalarArrayConstPtr pa = spa;
std::tr1::shared_ptr<ScalarArray> spa(new ScalarArray(static_cast<ScalarType>(i)));
Helper::cache(this, spa);
scalarArrays.push_back(spa);
}
// TODO use std::make_shared
std::tr1::shared_ptr<Union> su(new Union(), Field::Deleter());
std::tr1::shared_ptr<Union> su(new Union());
Helper::cache(this, su);
variantUnion = su;
// TODO use std::make_shared
std::tr1::shared_ptr<UnionArray> sua(new UnionArray(variantUnion), Field::Deleter());
std::tr1::shared_ptr<UnionArray> sua(new UnionArray(variantUnion));
Helper::cache(this, sua);
variantUnionArray = sua;
}
@@ -1514,4 +1658,3 @@ namespace std{
return o << "nullptr";
}
}

View File

@@ -28,7 +28,7 @@ PVArray::PVArray(FieldConstPtr const & field)
{
capacityMutable = false;
PVField::setImmutable();
}
}
bool PVArray::isCapacityMutable() const
{
@@ -49,7 +49,7 @@ PVArray::PVArray(FieldConstPtr const & field)
std::ostream& operator<<(format::array_at_internal const& manip, const PVArray& array)
{
return array.dumpValue(manip.stream, manip.index);
return array.dumpValue(manip.stream, manip.index);
}
}}

View File

@@ -7,10 +7,6 @@
* @author mrk
*/
#if defined(_WIN32) && !defined(NOMINMAX)
#define NOMINMAX
#endif
#include <cstddef>
#include <cstdlib>
#include <string>
@@ -65,6 +61,48 @@ template<> const ScalarType PVStringArray::typeCode = pvString;
template<typename T>
PVScalarValue<T>::~PVScalarValue() {}
template<typename T>
std::ostream& PVScalarValue<T>::dumpValue(std::ostream& o) const
{
return o << get();
}
template<typename T>
void PVScalarValue<T>::operator>>=(T& value) const
{
value = get();
}
template<typename T>
void PVScalarValue<T>::operator<<=(typename storage_t::arg_type value)
{
put(value);
}
template<typename T>
void PVScalarValue<T>::assign(const PVScalar& scalar)
{
if(isImmutable())
throw std::invalid_argument("destination is immutable");
copyUnchecked(scalar);
}
template<typename T>
void PVScalarValue<T>::copy(const PVScalar& from)
{
assign(from);
}
template<typename T>
void PVScalarValue<T>::copyUnchecked(const PVScalar& from)
{
if(this==&from)
return;
T result;
from.getAs((void*)&result, typeCode);
put(result);
}
template<typename T>
void PVScalarValue<T>::serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher) const {
@@ -104,6 +142,12 @@ PVString::PVString(ScalarConstPtr const & scalar)
storage.maxLength = 0;
}
std::ostream& PVString::dumpValue(std::ostream& o) const
{
o<<maybeQuote(get());
return o;
}
/* mixing overrides (virtual functions) and overloads (different argument lists) is fun...
* we override all overloads to avoid the "hides overloaded virtual function" warning from clang.
* In this case we don't need/want to, so just delegate to the base class.
@@ -115,17 +159,17 @@ void PVString::serialize(ByteBuffer *pbuffer,
void PVString::serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher, size_t offset, size_t count) const
{
// check bounds
// check bounds
const size_t length = storage.value.length();
/*if (offset < 0) offset = 0;
else*/ if (offset > length) offset = length;
//if (count < 0) count = length;
/*if (offset < 0) offset = 0;
else*/ if (offset > length) offset = length;
//if (count < 0) count = length;
const size_t maxCount = length - offset;
if (count > maxCount)
count = maxCount;
// write
const size_t maxCount = length - offset;
if (count > maxCount)
count = maxCount;
// write
SerializeHelper::serializeSubstring(storage.value, offset, count, pbuffer, pflusher);
}
@@ -146,7 +190,7 @@ template<typename T>
PVValueArray<T>::PVValueArray(ScalarArrayConstPtr const & scalarArray)
:base_t(scalarArray)
,value()
{}
PVValueArray<PVStructurePtr>::PVValueArray(StructureArrayConstPtr const & structureArray)
@@ -160,6 +204,59 @@ PVValueArray<PVUnionPtr>::PVValueArray(UnionArrayConstPtr const & unionArray)
,unionArray(unionArray)
{}
template<typename T>
PVValueArray<T>::~PVValueArray() {}
template<typename T>
ArrayConstPtr PVValueArray<T>::getArray() const
{
return std::tr1::static_pointer_cast<const Array>(this->getField());
}
template<typename T>
std::ostream& PVValueArray<T>::dumpValue(std::ostream& o) const
{
const_svector v(this->view());
typename const_svector::const_iterator it(v.begin()),
end(v.end());
o << '[';
if(it!=end) {
o << print_cast(*it++);
for(; it!=end; ++it)
o << ',' << print_cast(*it);
}
return o << ']';
}
template<>
std::ostream& PVValueArray<std::string>::dumpValue(std::ostream& o, size_t index) const
{
return o << maybeQuote(this->view().at(index));
}
template<>
std::ostream& PVValueArray<std::string>::dumpValue(std::ostream& o) const
{
const_svector v(this->view());
const_svector::const_iterator it(v.begin()),
end(v.end());
o << '[';
if(it!=end) {
o << maybeQuote(*it++);
for(; it!=end; ++it)
o << ", " << maybeQuote(*it);
}
return o << ']';
}
template<typename T>
std::ostream& PVValueArray<T>::dumpValue(std::ostream& o, size_t index) const
{
return o << print_cast(this->view().at(index));
}
template<typename T>
void PVValueArray<T>::setCapacity(size_t capacity)
{
@@ -352,6 +449,18 @@ void PVValueArray<string>::serialize(ByteBuffer *pbuffer,
}
}
template<typename T>
void PVValueArray<T>::_getAsVoid(epics::pvData::shared_vector<const void>& out) const
{
out = static_shared_vector_cast<const void>(this->view());
}
template<typename T>
void PVValueArray<T>::_putFromVoid(const epics::pvData::shared_vector<const void>& in)
{
this->replace(shared_vector_convert<const T>(in));
}
// Factory
PVDataCreate::PVDataCreate()
@@ -517,7 +626,7 @@ PVScalarArrayPtr PVDataCreate::createPVScalarArray(
return PVScalarArrayPtr(new PVStringArray(scalarArray));
}
throw std::logic_error("PVDataCreate::createPVScalarArray should never get here");
}
PVScalarArrayPtr PVDataCreate::createPVScalarArray(
@@ -648,6 +757,18 @@ template class PVScalarValue<uint64>;
template class PVScalarValue<float>;
template class PVScalarValue<double>;
template class PVScalarValue<std::string>;
template class PVValueArray<boolean>;
template class PVValueArray<int8>;
template class PVValueArray<uint8>;
template class PVValueArray<int16>;
template class PVValueArray<uint16>;
template class PVValueArray<int32>;
template class PVValueArray<uint32>;
template class PVValueArray<int64>;
template class PVValueArray<uint64>;
template class PVValueArray<float>;
template class PVValueArray<double>;
template class PVValueArray<std::string>;
}} // namespace epics::pvData
@@ -658,4 +779,3 @@ namespace std{
return o << "nullptr";
}
}

View File

@@ -62,7 +62,7 @@ size_t PVField::getNumberFields() const
void PVField::setImmutable() {immutable = true;}
void PVField::postPut()
void PVField::postPut()
{
if(postHandler) postHandler->postPut();
}
@@ -91,7 +91,7 @@ bool PVField::equals(PVField &pv)
std::ostream& operator<<(std::ostream& o, const PVField& f)
{
return f.dumpValue(o);
return f.dumpValue(o);
};
string PVField::getFullName() const
@@ -184,62 +184,16 @@ void PVField::copy(const PVField& from)
if(isImmutable())
throw std::invalid_argument("destination is immutable");
if (getField()->getType() != from.getField()->getType())
if (getField() != from.getField())
throw std::invalid_argument("field types do not match");
switch(getField()->getType())
{
case scalar:
{
const PVScalar* fromS = static_cast<const PVScalar*>(&from);
PVScalar* toS = static_cast<PVScalar*>(this);
toS->copy(*fromS);
break;
}
case scalarArray:
{
const PVScalarArray* fromS = static_cast<const PVScalarArray*>(&from);
PVScalarArray* toS = static_cast<PVScalarArray*>(this);
toS->copy(*fromS);
break;
}
case structure:
{
const PVStructure* fromS = static_cast<const PVStructure*>(&from);
PVStructure* toS = static_cast<PVStructure*>(this);
toS->copy(*fromS);
break;
}
case structureArray:
{
const PVStructureArray* fromS = static_cast<const PVStructureArray*>(&from);
PVStructureArray* toS = static_cast<PVStructureArray*>(this);
toS->copy(*fromS);
break;
}
case union_:
{
const PVUnion* fromS = static_cast<const PVUnion*>(&from);
PVUnion* toS = static_cast<PVUnion*>(this);
toS->copy(*fromS);
break;
}
case unionArray:
{
const PVUnionArray* fromS = static_cast<const PVUnionArray*>(&from);
PVUnionArray* toS = static_cast<PVUnionArray*>(this);
toS->copy(*fromS);
break;
}
default:
{
throw std::logic_error("PVField::copy unknown type");
}
}
copyUnchecked(from);
}
void PVField::copyUnchecked(const PVField& from)
{
assert(getField()==from.getField());
switch(getField()->getType())
{
case scalar:

View File

@@ -38,7 +38,7 @@ PVStructure::PVStructure(StructureConstPtr const & structurePtr)
pvFields.push_back(pvDataCreate->createPVField(fields[i]));
}
for(size_t i=0; i<numberFields; i++) {
pvFields[i]->setParentAndName(this,fieldNames[i]);
pvFields[i]->setParentAndName(this,fieldNames[i]);
}
}
@@ -60,9 +60,7 @@ PVStructure::PVStructure(StructureConstPtr const & structurePtr,
}
}
PVStructure::~PVStructure()
{
}
PVStructure::~PVStructure() {}
void PVStructure::setImmutable()
{
@@ -74,16 +72,6 @@ void PVStructure::setImmutable()
PVField::setImmutable();
}
const StructureConstPtr& PVStructure::getStructure() const
{
return structurePtr;
}
const PVFieldPtrArray & PVStructure::getPVFields() const
{
return pvFields;
}
PVFieldPtr PVStructure::getSubFieldImpl(size_t fieldOffset, bool throws) const
{
const PVStructure *current = this;
@@ -180,7 +168,7 @@ PVFieldPtr PVStructure::getSubFieldImpl(const char *name, bool throws) const
std::stringstream ss;
ss << "Failed to get field: " << fullName << " ("
<< std::string(fullName, sep) << " not found)";
throw std::runtime_error(ss.str());
throw std::runtime_error(ss.str());
}
else
return PVFieldPtr();
@@ -196,7 +184,7 @@ PVFieldPtr PVStructure::getSubFieldImpl(const char *name, bool throws) const
std::stringstream ss;
ss << "Failed to get field: " << fullName
<< " (" << std::string(fullName, sep)
<< " is not a structure)";
<< " is not a structure)";
throw std::runtime_error(ss.str());
}
else
@@ -319,22 +307,22 @@ std::ostream& PVStructure::dumpValue(std::ostream& o) const
o << format::indent() << getStructure()->getID() << ' ' << getFieldName();
o << std::endl;
{
format::indent_scope s(o);
format::indent_scope s(o);
PVFieldPtrArray const & fieldsData = getPVFields();
if (fieldsData.size() != 0) {
size_t length = getStructure()->getNumberFields();
for(size_t i=0; i<length; i++) {
PVFieldPtr fieldField = fieldsData[i];
Type type = fieldField->getField()->getType();
if (type == scalar || type == scalarArray)
o << format::indent() << fieldField->getField()->getID() << ' ' << fieldField->getFieldName() << ' ' << *(fieldField.get()) << std::endl;
else
o << *(fieldField.get());
}
}
PVFieldPtrArray const & fieldsData = getPVFields();
if (fieldsData.size() != 0) {
size_t length = getStructure()->getNumberFields();
for(size_t i=0; i<length; i++) {
PVFieldPtr fieldField = fieldsData[i];
Type type = fieldField->getField()->getType();
if (type == scalar || type == scalarArray)
o << format::indent() << fieldField->getField()->getID() << ' ' << fieldField->getFieldName() << ' ' << *(fieldField.get()) << std::endl;
else
o << *(fieldField.get());
}
}
}
return o;
return o;
}

View File

@@ -218,10 +218,10 @@ std::ostream& PVStructureArray::dumpValue(std::ostream& o) const
size_t length = getLength();
if (length > 0)
{
format::indent_scope s(o);
format::indent_scope s(o);
for (size_t i = 0; i < length; i++)
dumpValue(o, i);
dumpValue(o, i);
}
return o;

View File

@@ -23,7 +23,7 @@ using std::size_t;
using std::string;
namespace epics { namespace pvData {
#define PVUNION_UNDEFINED_INDEX -1
const int32 PVUnion::UNDEFINED_INDEX = PVUNION_UNDEFINED_INDEX;
@@ -75,13 +75,13 @@ PVFieldPtr PVUnion::select(int32 index)
return value;
}
PVFieldPtr PVUnion::select(string const & fieldName)
{
int32 index = variant ? -1 : static_cast<int32>(unionPtr->getFieldIndex(fieldName));
if (index == -1)
if (index == -1)
throw std::invalid_argument("no such fieldName");
return select(index);
return select(index);
}
void PVUnion::set(int32 index, PVFieldPtr const & value)
@@ -94,14 +94,14 @@ void PVUnion::set(int32 index, PVFieldPtr const & value)
{
// for undefined index we accept only null values
if (value)
throw std::invalid_argument("non-null value for index == UNDEFINED_INDEX");
throw std::invalid_argument("non-null value for index == UNDEFINED_INDEX");
}
else if (index < 0 || size_t(index) >= unionPtr->getFields().size())
throw std::invalid_argument("index out of bounds");
else if (!value)
throw std::invalid_argument("Can't set defined index w/ NULL");
else if (value->getField() != unionPtr->getField(index))
throw std::invalid_argument("selected field and its introspection data do not match");
throw std::invalid_argument("selected field and its introspection data do not match");
}
selector = index;
@@ -136,7 +136,7 @@ void PVUnion::serialize(ByteBuffer *pbuffer, SerializableControl *pflusher) cons
// write selector value
SerializeHelper::writeSize(selector, pbuffer, pflusher);
// write value, no value for UNDEFINED_INDEX
if (selector != UNDEFINED_INDEX)
if (selector != UNDEFINED_INDEX)
value->serialize(pbuffer, pflusher);
}
}
@@ -180,20 +180,20 @@ std::ostream& PVUnion::dumpValue(std::ostream& o) const
{
o << format::indent() << getUnion()->getID() << ' ' << getFieldName() << std::endl;
{
format::indent_scope s(o);
format::indent_scope s(o);
const PVField::const_shared_pointer& fieldField = get();
if (fieldField.get() == NULL)
o << format::indent() << "(none)" << std::endl;
else
{
Type type = fieldField->getField()->getType();
if (type == scalar || type == scalarArray)
o << format::indent() << fieldField->getField()->getID() << ' ' << fieldField->getFieldName() << ' ' << *(fieldField.get()) << std::endl;
else
o << *(fieldField.get());
}
if (fieldField.get() == NULL)
o << format::indent() << "(none)" << std::endl;
else
{
Type type = fieldField->getField()->getType();
if (type == scalar || type == scalarArray)
o << format::indent() << fieldField->getField()->getID() << ' ' << fieldField->getFieldName() << ' ' << *(fieldField.get()) << std::endl;
else
o << *(fieldField.get());
}
}
return o;
return o;
}
void PVUnion::copy(const PVUnion& from)

View File

@@ -217,10 +217,10 @@ std::ostream& PVUnionArray::dumpValue(std::ostream& o) const
size_t length = getLength();
if (length > 0)
{
format::indent_scope s(o);
format::indent_scope s(o);
for (size_t i = 0; i < length; i++)
dumpValue(o, i);
dumpValue(o, i);
}
return o;

View File

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

View File

@@ -20,7 +20,7 @@
using std::string;
namespace epics { namespace pvData {
namespace epics { namespace pvData {
StandardPVField::StandardPVField()
: standardField(getStandardField()),

View File

@@ -59,7 +59,7 @@ namespace ScalarTypeFunc {
if(type>=pvBoolean && type<=pvDouble) return true;
return false;
}
static const char* names[] = {
"boolean",
"byte", "short", "int", "long",

View File

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

View File

@@ -17,7 +17,7 @@ using std::cout;
using std::endl;
using std::string;
namespace epics { namespace pvData {
namespace epics { namespace pvData {
template<typename T>
void copy(
@@ -272,4 +272,3 @@ void copy(
}
}}

View File

@@ -7,4 +7,4 @@ INC += pv/json.h
LIBSRCS += parsehelper.cpp
LIBSRCS += parseany.cpp
LIBSRCS += parseinto.cpp
LIBSRCS += print.cpp
LIBSRCS += jprint.cpp

305
src/json/jprint.cpp Normal file
View File

@@ -0,0 +1,305 @@
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
#include <vector>
#include <sstream>
#include <errlog.h>
#include <yajl_gen.h>
#define epicsExportSharedSymbols
#include <pv/pvdVersion.h>
#include <pv/pvData.h>
#include <pv/valueBuilder.h>
#include <pv/bitSet.h>
#include "pv/json.h"
namespace pvd = epics::pvData;
namespace {
using namespace pvd::yajl;
void yg(yajl_gen_status sts) {
const char *msg = "<\?\?\?>";
switch(sts) {
case yajl_gen_status_ok:
case yajl_gen_generation_complete:
return;
#define CASE(STS) case STS: msg = #STS; break
CASE(yajl_gen_keys_must_be_strings);
CASE(yajl_gen_in_error_state);
CASE(yajl_gen_no_buf);
CASE(yajl_gen_invalid_number);
CASE(yajl_max_depth_exceeded);
#ifdef EPICS_YAJL_VERSION
CASE(yajl_gen_invalid_string);
#endif
#undef CASE
}
throw std::runtime_error(msg);
}
static
void stream_printer(void * ctx,
const char * str,
size_arg len)
{
std::ostream *strm = (std::ostream*)ctx;
strm->write(str, len);
}
struct args {
yajl_gen handle;
const pvd::JSONPrintOptions& opts;
std::string indent;
args(std::ostream& strm,
const pvd::JSONPrintOptions& opts)
:opts(opts)
,indent(opts.indent, ' ')
{
#ifndef EPICS_YAJL_VERSION
yajl_gen_config conf;
conf.beautify = opts.multiLine;
conf.indentString = indent.c_str();
if(!(handle = yajl_gen_alloc2(stream_printer, NULL, NULL, &strm)))
throw std::bad_alloc();
if(opts.json5) {
static bool warned;
if(!warned) {
warned = true;
errlogPrintf("Warning: Ignoring request to print JSON5. Update Base >= 7.0.6.1");
}
}
#else
if(!(handle = yajl_gen_alloc(NULL)))
throw std::bad_alloc();
if(opts.multiLine) {
yajl_gen_config(handle, yajl_gen_beautify, 1);
yajl_gen_config(handle, yajl_gen_indent_string, indent.c_str());
} else {
yajl_gen_config(handle, yajl_gen_beautify, 0);
}
# if EPICS_VERSION_INT>=VERSION_INT(7,0,6,1)
yajl_gen_config(handle, yajl_gen_json5, (int)opts.json5);
# else
if(opts.json5) {
static bool warned;
if(!warned) {
warned = true;
errlogPrintf("Warning: Ignoring request to print JSON5. Update Base >= 7.0.6.1");
}
}
# endif
yajl_gen_config(handle, yajl_gen_print_callback, stream_printer, &strm);
#endif
}
~args() {
yajl_gen_free(handle);
}
};
void yg_string(yajl_gen handle, const std::string& s) {
yg(yajl_gen_string(handle, (const unsigned char*)s.c_str(), s.size()));
}
void show_field(args& A, const pvd::PVField* fld, const pvd::BitSet *mask);
void show_struct(args& A, const pvd::PVStructure* fld, const pvd::BitSet *mask)
{
const pvd::StructureConstPtr& type = fld->getStructure();
const pvd::PVFieldPtrArray& children = fld->getPVFields();
const pvd::StringArray& names = type->getFieldNames();
yg(yajl_gen_map_open(A.handle));
for(size_t i=0, N=names.size(); i<N; i++)
{
if(mask && !mask->get(children[i]->getFieldOffset())) continue;
yg_string(A.handle, names[i]);
show_field(A, children[i].get(), mask);
}
yg(yajl_gen_map_close(A.handle));
}
void show_field(args& A, const pvd::PVField* fld, const pvd::BitSet *mask)
{
switch(fld->getField()->getType())
{
case pvd::scalar:
{
const pvd::PVScalar *scalar=static_cast<const pvd::PVScalar*>(fld);
switch(scalar->getScalar()->getScalarType()) {
case pvd::pvString: yg_string(A.handle, scalar->getAs<std::string>()); break;
case pvd::pvBoolean: yg(yajl_gen_bool(A.handle, scalar->getAs<pvd::boolean>())); break;
case pvd::pvDouble:
case pvd::pvFloat: yg(yajl_gen_double(A.handle, scalar->getAs<double>())); break;
// case pvd::pvULong: // can't always be exactly represented...
default:
yg(yajl_gen_integer(A.handle, scalar->getAs<pvd::int64>())); break;
}
}
return;
case pvd::scalarArray:
{
const pvd::PVScalarArray *scalar=static_cast<const pvd::PVScalarArray*>(fld);
pvd::shared_vector<const void> arr;
scalar->getAs<void>(arr);
yg(yajl_gen_array_open(A.handle));
switch(arr.original_type()) {
case pvd::pvString: {
pvd::shared_vector<const std::string> sarr(pvd::shared_vector_convert<const std::string>(arr));
for(size_t i=0, N=sarr.size(); i<N; i++) {
yg_string(A.handle, sarr[i]);
}
break;
}
case pvd::pvBoolean: {
pvd::shared_vector<const pvd::boolean> sarr(pvd::shared_vector_convert<const pvd::boolean>(arr));
for(size_t i=0, N=sarr.size(); i<N; i++) {
yg(yajl_gen_bool(A.handle, sarr[i]));
}
break;
}
case pvd::pvDouble:
case pvd::pvFloat: {
pvd::shared_vector<const double> sarr(pvd::shared_vector_convert<const double>(arr));
for(size_t i=0, N=sarr.size(); i<N; i++) {
yg(yajl_gen_double(A.handle, sarr[i]));
}
break;
}
default: {
pvd::shared_vector<const pvd::int64> sarr(pvd::shared_vector_convert<const pvd::int64>(arr));
for(size_t i=0, N=sarr.size(); i<N; i++) {
yg(yajl_gen_integer(A.handle, sarr[i]));
}
break;
}
}
yg(yajl_gen_array_close(A.handle));
}
return;
case pvd::structure:
show_struct(A, static_cast<const pvd::PVStructure*>(fld), mask);
return;
case pvd::structureArray:
{
pvd::PVStructureArray::const_svector arr(static_cast<const pvd::PVStructureArray*>(fld)->view());
yg(yajl_gen_array_open(A.handle));
for(size_t i=0, N=arr.size(); i<N; i++) {
if(arr[i])
show_struct(A, arr[i].get(), 0);
else
yg(yajl_gen_null(A.handle));
}
yg(yajl_gen_array_close(A.handle));
}
return;
case pvd::union_:
{
const pvd::PVUnion *U=static_cast<const pvd::PVUnion*>(fld);
const pvd::PVField::const_shared_pointer& C(U->get());
if(!C) {
yg(yajl_gen_null(A.handle));
} else {
show_field(A, C.get(), 0);
}
}
return;
case pvd::unionArray: {
const pvd::PVUnionArray *U=static_cast<const pvd::PVUnionArray*>(fld);
pvd::PVUnionArray::const_svector arr(U->view());
yg(yajl_gen_array_open(A.handle));
for(size_t i=0, N=arr.size(); i<N; i++) {
if(arr[i])
show_field(A, arr[i].get(), 0);
else
yg(yajl_gen_null(A.handle));
}
yg(yajl_gen_array_close(A.handle));
}
return;
}
// should not be reached
if(A.opts.ignoreUnprintable)
yg(yajl_gen_null(A.handle));
else
throw std::runtime_error("Encountered unprintable field type");
}
void expandBS(const pvd::PVStructure& top, pvd::BitSet& mask, bool parents) {
if(mask.get(0)) { // special handling because getSubField(0) not allowed
// wildcard
for(size_t idx=1, N=top.getNumberFields(); idx<N; idx++) {
mask.set(idx);
}
} else {
for(pvd::int32 idx = mask.nextSetBit(0), N=top.getNumberFields(); idx>=0 && idx<N; idx=mask.nextSetBit(idx+1)) {
pvd::PVField::const_shared_pointer fld = top.getSubFieldT(idx);
// look forward and mark all children
for(size_t i=idx+1, N=fld->getNextFieldOffset(); i<N; i++)
mask.set(i);
if(parents) {
// look back and mark all parents
// we've already stepped past all parents so siblings will not be automatically marked
for(const pvd::PVStructure *parent = fld->getParent(); parent; parent = parent->getParent()) {
mask.set(parent->getFieldOffset());
}
}
}
}
}
} // namespace
namespace epics{namespace pvData{
JSONPrintOptions::JSONPrintOptions()
:multiLine(true)
,ignoreUnprintable(true)
,indent(0)
,json5(false)
{}
void printJSON(std::ostream& strm,
const PVStructure& val,
const BitSet& mask,
const JSONPrintOptions& opts)
{
args A(strm, opts);
pvd::BitSet emask(mask);
expandBS(val, emask, true);
if(!emask.get(0)) return;
show_struct(A, &val, &emask);
}
void printJSON(std::ostream& strm,
const PVField& val,
const JSONPrintOptions& opts)
{
args A(strm, opts);
show_field(A, &val, 0);
}
}} // namespace epics::pvData

View File

@@ -10,8 +10,6 @@
#include <pv/pvData.h>
#include <pv/valueBuilder.h>
#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1)
#include "pv/json.h"
namespace pvd = epics::pvData;
@@ -280,5 +278,3 @@ parseJSON(std::istream& strm)
}
}} // namespace epics::pvData
#endif // EPICS_VERSION_INT

View File

@@ -9,8 +9,6 @@
#define epicsExportSharedSymbols
#include <pv/pvdVersion.h>
#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1)
#include "pv/json.h"
namespace {
@@ -118,5 +116,3 @@ bool yajl_parse_helper(std::istream& src,
}
}} // namespace epics::pvData
#endif // EPICS_VERSION_INT

View File

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

View File

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

View File

@@ -13,8 +13,6 @@
#include <pv/pvdVersion.h>
#include <pv/pvData.h>
#if EPICS_VERSION_INT>=VERSION_INT(3,15,0,1)
#ifdef epicsExportSharedSymbols
# define pvjson_epicsExportSharedSymbols
# undef epicsExportSharedSymbols
@@ -46,20 +44,38 @@ struct epicsShareClass JSONPrintOptions
bool multiLine; //!< include new lines
bool ignoreUnprintable;//!< ignore union/union array when encountered
unsigned indent; //!< Initial indentation (# of spaces)
bool json5; //!< Output extended JSON (eg. NaN). @since 8.1.0
JSONPrintOptions();
};
/** Print PVStructure as JSON
*
* Restrictions:
*
* - No support for union or array of union
* 'mask' selects those fields which will be printed.
* @version Overload added after 7.0.0
*/
epicsShareFunc
void printJSON(std::ostream& strm,
const PVField::const_shared_pointer& val,
const PVStructure& val,
const BitSet& mask,
const JSONPrintOptions& opts = JSONPrintOptions());
/** Print PVField as JSON
* @version Overload added after 7.0.0
*/
epicsShareFunc
void printJSON(std::ostream& strm,
const PVField& val,
const JSONPrintOptions& opts = JSONPrintOptions());
// To be deprecated in favor of previous form
FORCE_INLINE
void printJSON(std::ostream& strm,
const PVField::const_shared_pointer& val,
const JSONPrintOptions& opts = JSONPrintOptions())
{
printJSON(strm, *val, opts);
}
/** Parse JSON text into a PVStructure
*
* Restrictions:
@@ -82,12 +98,22 @@ PVStructure::shared_pointer parseJSON(std::istream& strm);
* @param dest Store in fields of this structure
* @param assigned Which fields of _dest_ were assigned. (Optional)
* @throws std::runtime_error on failure. dest and assigned may be modified.
* @version Overload added after 7.0.0
*/
epicsShareFunc
void parseJSON(std::istream& strm,
const PVField::shared_pointer& dest,
PVField& dest,
BitSet *assigned=0);
// To be deprecated in favor of previous form
FORCE_INLINE
void parseJSON(std::istream& strm,
const PVField::shared_pointer& dest,
BitSet *assigned=0)
{
parseJSON(strm, *dest, assigned);
}
/** Wrapper around yajl_parse()
*
@@ -120,8 +146,4 @@ typedef size_t size_arg;
}} // namespace epics::pvData
#else
# error JSON parser requires EPICS Base >= 3.15.0.1
#endif // EPICS_VERSION_INT
#endif // PV_JSON_H

View File

@@ -15,7 +15,6 @@ INC += pv/timer.h
INC += pv/status.h
INC += pv/sharedPtr.h
INC += pv/debugPtr.h
INC += pv/localStaticLock.h
INC += pv/typeCast.h
INC += pv/sharedVector.h
INC += pv/templateMeta.h
@@ -31,8 +30,8 @@ LIBSRCS += serializeHelper.cpp
LIBSRCS += event.cpp
LIBSRCS += timer.cpp
LIBSRCS += status.cpp
LIBSRCS += localStaticLock.cpp
LIBSRCS += typeCast.cpp
LIBSRCS += thread.cpp
LIBSRCS += parseToPOD.cpp
LIBSRCS += pvUnitTest.cpp
LIBSRCS += debugPtr.cpp

View File

@@ -31,7 +31,7 @@ AnyScalar::AnyScalar(const AnyScalar& o)
}
#if __cplusplus>=201103L
AnyScalar::AnyScalar(AnyScalar&& o)
AnyScalar::AnyScalar(AnyScalar&& o) noexcept
:_stype(o._stype)
{
typedef std::string string;
@@ -136,7 +136,7 @@ void AnyScalar::swap(AnyScalar& o) {
}
const void* AnyScalar::bufferUnsafe() const {
if(_stype==pvString) {
return as<std::string>().c_str();
return ref<std::string>().c_str();
} else {
return _wrap.blob;
}

View File

@@ -42,12 +42,12 @@
#define CHECK_POST() assert(words.empty() || words.back()!=0)
namespace epics { namespace pvData {
BitSet::shared_pointer BitSet::create(uint32 nbits)
{
return BitSet::shared_pointer(new BitSet(nbits));
}
BitSet::BitSet() {}
BitSet::BitSet(uint32 nbits)
@@ -327,7 +327,7 @@ namespace epics { namespace pvData {
SerializeHelper::writeSize(len, buffer, flusher);
flusher->ensureBuffer(len);
n = len / 8;
n = len / 8;
for (uint32 i = 0; i < n; i++)
buffer->putLong(words[i]);
@@ -338,7 +338,7 @@ namespace epics { namespace pvData {
void BitSet::deserialize(ByteBuffer* buffer, DeserializableControl* control) {
uint32 bytes = static_cast<uint32>(SerializeHelper::readSize(buffer, control)); // in bytes
uint32 bytes = static_cast<uint32>(SerializeHelper::readSize(buffer, control)); // in bytes
size_t wordsInUse = (bytes + 7) / BYTES_PER_WORD;
words.resize(wordsInUse);

View File

@@ -35,7 +35,7 @@ void shared_ptr_base::track_new()
}
// create new tracker if ptr!=nullptr, otherwise clear
void shared_ptr_base::track_new(void* ptr)
void shared_ptr_base::track_new(const void* ptr)
{
track_clear();
if(ptr){

View File

@@ -27,7 +27,7 @@
using std::string;
namespace epics { namespace pvData {
namespace epics { namespace pvData {
Event::~Event() {

View File

@@ -1,39 +0,0 @@
/* localStaticLock.cpp */
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
/**
* @author mse
*/
#define epicsExportSharedSymbols
#include <pv/localStaticLock.h>
static int nifty_counter;
static epics::pvData::Mutex* g_localStaticInitMutex;
epics::pvData::Mutex& getLocalStaticInitMutex()
{
return *g_localStaticInitMutex;
}
// The counter is initialized at load-time, i.e., before any of the static objects are initialized.
MutexInitializer::MutexInitializer ()
{
if (0 == nifty_counter++)
{
// Initialize static members.
g_localStaticInitMutex = new epics::pvData::Mutex();
}
}
MutexInitializer::~MutexInitializer ()
{
if (0 == --nifty_counter)
{
// Clean-up.
delete g_localStaticInitMutex;
}
}

View File

@@ -25,406 +25,6 @@ using std::string;
#define NEED_LONGLONG
#endif
#ifndef EPICS_VERSION_INT
#define VERSION_INT(V,R,M,P) ( ((V)<<24) | ((R)<<16) | ((M)<<8) | (P))
#define EPICS_VERSION_INT VERSION_INT(EPICS_VERSION, EPICS_REVISION, EPICS_MODIFICATION, EPICS_PATCH_LEVEL)
#endif
#if EPICS_VERSION_INT < VERSION_INT(3,15,0,1)
/* These integer conversion primitives added to epicsStdlib.c in 3.15.0.1 */
#define S_stdlib_noConversion 1 /* No digits to convert */
#define S_stdlib_extraneous 2 /* Extraneous characters */
#define S_stdlib_underflow 3 /* Too small to represent */
#define S_stdlib_overflow 4 /* Too large to represent */
#define S_stdlib_badBase 5 /* Number base not supported */
static int
epicsParseLong(const char *str, long *to, int base, char **units)
{
int c;
char *endp;
long value;
while ((c = *str) && isspace(c))
++str;
errno = 0;
value = strtol(str, &endp, base);
if (endp == str)
return S_stdlib_noConversion;
if (errno == EINVAL) /* Not universally supported */
return S_stdlib_badBase;
if (errno == ERANGE)
return S_stdlib_overflow;
while ((c = *endp) && isspace(c))
++endp;
if (c && !units)
return S_stdlib_extraneous;
*to = value;
if (units)
*units = endp;
return 0;
}
static int
epicsParseULong(const char *str, unsigned long *to, int base, char **units)
{
int c;
char *endp;
unsigned long value;
while ((c = *str) && isspace(c))
++str;
errno = 0;
value = strtoul(str, &endp, base);
if (endp == str)
return S_stdlib_noConversion;
if (errno == EINVAL) /* Not universally supported */
return S_stdlib_badBase;
if (errno == ERANGE)
return S_stdlib_overflow;
while ((c = *endp) && isspace(c))
++endp;
if (c && !units)
return S_stdlib_extraneous;
*to = value;
if (units)
*units = endp;
return 0;
}
static int
epicsParseDouble(const char *str, double *to, char **units)
{
int c;
char *endp;
double value;
while ((c = *str) && isspace(c))
++str;
errno = 0;
value = epicsStrtod(str, &endp);
if (endp == str)
return S_stdlib_noConversion;
if (errno == ERANGE)
return (value == 0) ? S_stdlib_underflow : S_stdlib_overflow;
while ((c = *endp) && isspace(c))
++endp;
if (c && !units)
return S_stdlib_extraneous;
*to = value;
if (units)
*units = endp;
return 0;
}
/* These call the primitives */
static int
epicsParseInt8(const char *str, epicsInt8 *to, int base, char **units)
{
long value;
int status = epicsParseLong(str, &value, base, units);
if (status)
return status;
if (value < -0x80 || value > 0x7f)
return S_stdlib_overflow;
*to = (epicsInt8)value;
return 0;
}
static int
epicsParseUInt8(const char *str, epicsUInt8 *to, int base, char **units)
{
unsigned long value;
int status = epicsParseULong(str, &value, base, units);
if (status)
return status;
if (value > 0xff && value <= ~0xffUL)
return S_stdlib_overflow;
*to = (epicsUInt8)value;
return 0;
}
static int
epicsParseInt16(const char *str, epicsInt16 *to, int base, char **units)
{
long value;
int status = epicsParseLong(str, &value, base, units);
if (status)
return status;
if (value < -0x8000 || value > 0x7fff)
return S_stdlib_overflow;
*to = (epicsInt16)value;
return 0;
}
static int
epicsParseUInt16(const char *str, epicsUInt16 *to, int base, char **units)
{
unsigned long value;
int status = epicsParseULong(str, &value, base, units);
if (status)
return status;
if (value > 0xffff && value <= ~0xffffUL)
return S_stdlib_overflow;
*to = (epicsUInt16)value;
return 0;
}
static int
epicsParseInt32(const char *str, epicsInt32 *to, int base, char **units)
{
long value;
int status = epicsParseLong(str, &value, base, units);
if (status)
return status;
#if (LONG_MAX > 0x7fffffff)
if (value < -0x80000000L || value > 0x7fffffffL)
return S_stdlib_overflow;
#endif
*to = (epicsInt32)value;
return 0;
}
static int
epicsParseUInt32(const char *str, epicsUInt32 *to, int base, char **units)
{
unsigned long value;
int status = epicsParseULong(str, &value, base, units);
if (status)
return status;
#if (ULONG_MAX > 0xffffffff)
if (value > 0xffffffffUL && value <= ~0xffffffffUL)
return S_stdlib_overflow;
#endif
*to = (epicsUInt32)value;
return 0;
}
static int
epicsParseFloat(const char *str, float *to, char **units)
{
double value, abs;
int status = epicsParseDouble(str, &value, units);
if (status)
return status;
abs = fabs(value);
if (value > 0 && abs <= FLT_MIN)
return S_stdlib_underflow;
if (finite(value) && abs >= FLT_MAX)
return S_stdlib_overflow;
*to = (float)value;
return 0;
}
#endif
// Sometimes we have to provide our own copy of strtoll()
#if defined(_WIN32) && !defined(_MINGW)
// On Windows with MSVC, Base-3.15 provides strtoll()
# define NEED_OLL_FUNCS (EPICS_VERSION_INT < VERSION_INT(3,15,0,1))
#elif defined(vxWorks)
// On VxWorks, Base-3.15 provides strtoll()
# define NEED_OLL_FUNCS (EPICS_VERSION_INT < VERSION_INT(3,15,0,1))
#else
// Other architectures all provide strtoll()
# define NEED_OLL_FUNCS 0
#endif
#if defined(NEED_LONGLONG) && NEED_OLL_FUNCS
static
long long strtoll(const char *ptr, char ** endp, int base)
{
size_t inlen = strlen(ptr);
long long result;
unsigned char offset=0;
assert(base==0);
if(ptr[0]=='-')
offset=1;
try {
std::istringstream strm(ptr);
assert(strm.rdbuf()->in_avail()>=0
&& inlen==(size_t)strm.rdbuf()->in_avail());
if(ptr[offset]=='0') {
if(ptr[offset+1]=='x')
strm >> std::hex;
else
strm >> std::oct;
}
strm >> result;
if(strm.fail())
goto noconvert;
assert(strm.rdbuf()->in_avail()>=0
&& inlen>=(size_t)strm.rdbuf()->in_avail());
size_t consumed = inlen - strm.rdbuf()->in_avail();
*endp = (char*)ptr + consumed;
return result;
} catch(...) {
goto noconvert;
}
return result;
noconvert:
*endp = (char*)ptr;
return 0;
}
#if defined(vxWorks)
/* The VxWorks version of std::istringstream >> uint64_t is buggy,
* provide our own implementation
*/
static
unsigned long long strtoull(const char *nptr, char **endptr, int base)
{
const char *s = nptr;
unsigned long long acc;
int c;
unsigned long long cutoff;
int neg = 0, any, cutlim;
do
c = *s++;
while (isspace(c));
if (c == '-')
{
neg = 1;
c = *s++;
}
else if (c == '+')
c = *s++;
if ((base == 0 || base == 16) &&
c == '0' && (*s == 'x' || *s == 'X'))
{
c = s[1];
s += 2;
base = 16;
}
if (base == 0)
base = c == '0' ? 8 : 10;
cutoff = (unsigned long long) UINT64_MAX / (unsigned long long) base;
cutlim = (unsigned long long) UINT64_MAX % (unsigned long long) base;
for (acc = 0, any = 0;; c = *s++)
{
if (isdigit(c))
c -= '0';
else if (isalpha(c))
c -= isupper(c) ? 'A' - 10 : 'a' - 10;
else
break;
if (c >= base)
break;
if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
any = -1;
else
{
any = 1;
acc *= base;
acc += c;
}
}
if (any < 0)
{
acc = UINT64_MAX;
errno = ERANGE;
}
else if (neg)
acc = -acc;
if (endptr != 0)
*endptr = any ? (char *) s - 1 : (char *) nptr;
return (acc);
}
#else
static
unsigned long long strtoull(const char *ptr, char ** endp, int base)
{
size_t inlen = strlen(ptr);
unsigned long long result;
assert(base==0);
try {
std::istringstream strm(ptr);
assert(strm.rdbuf()->in_avail()>=0
&& inlen==(size_t)strm.rdbuf()->in_avail());
if(ptr[0]=='0') {
if(ptr[1]=='x')
strm >> std::hex;
else
strm >> std::oct;
}
strm >> result;
if(strm.fail())
goto noconvert;
assert(strm.rdbuf()->in_avail()>=0
&& inlen>=(size_t)strm.rdbuf()->in_avail());
size_t consumed = inlen - strm.rdbuf()->in_avail();
*endp = (char*)ptr + consumed;
return result;
} catch(...) {
goto noconvert;
}
return result;
noconvert:
*endp = (char*)ptr;
return 0;
}
#endif
#endif
/* do we need long long? */
#ifdef NEED_LONGLONG
static int

View File

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

View File

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

View File

@@ -16,6 +16,7 @@
#include <epicsEndian.h>
#include <shareLib.h>
#include <epicsAssert.h>
#include <compilerDependencies.h>
#include <pv/templateMeta.h>
#include <pv/pvType.h>
@@ -149,14 +150,17 @@ struct swap<8> {
#undef _PVA_swap64
/* PVD serialization doesn't pay attention to alignement,
* which some targets really care about and treat unaligned
* which some targets (ARM and powerpc) really care about and treat unaligned
* access as a fault, or with a heavy penalty (~= to a syscall).
*
* For those targets,, we will have to live with the increase
* in execution time and/or object code size of byte-wise copy.
*
* Treat x86 32/64 as an outlier, and assume all other targets
* need, or greatly benefit, from aligned access.
*/
#ifdef _ARCH_PPC
#if !(defined(__x86_64__) || defined(_M_AMD64) || defined(__i386__) || defined(_M_IX86))
template<typename T>
union alignu {
@@ -493,23 +497,6 @@ public:
{
return sizeof(T)>1 && _reverseEndianess;
}
/**
* Adjust position to the next multiple of 'size.
* @param size The alignment requirement, must be a power of 2. (unchecked)
* @param fill value to use for padding bytes (default '\0').
*
* @note This alignment is absolute, not necessarily with respect to _buffer.
*/
inline void align(std::size_t size, char fill='\0')
{
const std::size_t k = size - 1, bufidx = (std::size_t)_position;
if(bufidx&k) {
std::size_t npad = size-(bufidx&k);
assert(npad<=getRemaining());
std::fill(_position, _position+npad, fill);
_position += npad;
}
}
/**
* Put a boolean value into the byte buffer.
*
@@ -695,7 +682,7 @@ public:
EPICS_ALWAYS_INLINE double getDouble (std::size_t index) { return get<double>(index); }
// TODO remove
EPICS_ALWAYS_INLINE const char* getArray() const
EPICS_ALWAYS_INLINE const char* getArray() const EPICS_DEPRECATED
{
return _buffer;
}
@@ -803,8 +790,8 @@ private:
assert(n<=getRemaining());
if (reverse<T>()) {
for(std::size_t i=0; i<n; i+=sizeof(T)) {
detail::store_unaligned(_position+i, swap<T>(values[i]));
for(std::size_t i=0; i<count; i++) {
detail::store_unaligned(_position+i*sizeof(T), swap<T>(values[i]));
}
} else {
memcpy(_position, values, n);
@@ -819,8 +806,8 @@ private:
assert(n<=getRemaining());
if (reverse<T>()) {
for(std::size_t i=0; i<n; i+=sizeof(T)) {
values[i] = swap<T>(detail::load_unaligned<T>(_position+i));
for(std::size_t i=0; i<count; i++) {
values[i] = swap<T>(detail::load_unaligned<T>(_position+i*sizeof(T)));
}
} else {
memcpy(values, _position, n);

View File

@@ -78,7 +78,7 @@ protected:
// add ourselves to tracker
void track_new();
// create new tracker if ptr!=nullptr, otherwise clear
void track_new(void* ptr);
void track_new(const void* ptr);
// copy tracker and add ourself
void track_assign(const shared_ptr_base& o);
void track_clear();
@@ -286,6 +286,7 @@ public:
long use_count() const noexcept { return real.use_count(); }
bool unique() const noexcept { return real.unique(); }
bool expired() const noexcept { return real.expired(); }
};
template<class Base>
@@ -316,13 +317,12 @@ do_enable_shared_from_this(const shared_ptr<Store>& dest,
self->xxInternalSelf = actual;
}
}} // namespace epics::debug
template<typename T>
inline std::ostream& operator<<(std::ostream& strm, const epics::debug::shared_ptr<T>& ptr)
inline std::ostream& operator<<(std::ostream& strm, const shared_ptr<T>& ptr)
{
strm<<ptr.get();
return strm;
}
}} // namespace epics::debug
#endif // DEBUGPTR_H

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>
@@ -211,7 +207,7 @@ public:
/**
* Constructor.
*/
explicit BaseException(const std::string msg) : std::logic_error(msg) {}
explicit BaseException(const std::string& msg) : std::logic_error(msg) {}
/**
* Destructor.
@@ -233,7 +229,6 @@ private:
#endif
#define THROW_BASE_EXCEPTION(msg) THROW_EXCEPTION2(::epics::pvData::BaseException, msg)
#define THROW_BASE_EXCEPTION_CAUSE(msg, cause) THROW_EXCEPTION2(::epics::pvData::BaseException, msg)
}
}

View File

@@ -1,34 +0,0 @@
/* localStaticLock.h */
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
/**
* @author mse
*/
#ifndef LOCALSTATICLOCK_H
#define LOCALSTATICLOCK_H
#include <compilerDependencies.h>
#include <pv/lock.h>
#include <shareLib.h>
epicsShareExtern epics::pvData::Mutex& getLocalStaticInitMutex() EPICS_DEPRECATED;
#if defined(__GNUC__) && __GNUC__ >= 4
// noop
#define LOCAL_STATIC_LOCK
#else
#define LOCAL_STATIC_LOCK epics::pvData::Lock localStaticInitMutexLock(getLocalStaticInitMutex());
#endif
static class epicsShareClass MutexInitializer {
public:
MutexInitializer ();
~MutexInitializer ();
} localStaticMutexInitializer; // Note object here in the header.
#endif /* LOCALSTATICLOCK_H */

View File

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

View File

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

View File

@@ -5,6 +5,38 @@
#ifndef REFTRACK_H
#define REFTRACK_H
/** @page pvd_reftrack RefTrack
*
* reftrack.h is a utility for listing, finding, and reading global atomic counters.
* By convention used to expose object instance counters as a way of detecting (slow)
* reference/resource leaks before they cause problems.
*
* cf. the IOC shell commands "refshow", "refsave", and "refdiff".
*
* Example usage:
*
* @code
* // my header.h
* struct MyClass {
* MyClass();
* ~MyClass();
* static size_t num_instances;
* ...
* };
* ...
* // my src.cpp
* size_t MyClass::num_instances;
* MyClass::MyClass() {
* REFTRACE_INCREMENT(num_instances);
* }
* MyClass::~MyClass() {
* REFTRACE_DECREMENT(num_instances);
* }
* // in some IOC registrar or global ctor
* registerRefCounter("MyClass", &MyClass::num_instances);
* @endcode
*/
#ifdef __cplusplus
#include <map>
@@ -14,27 +46,10 @@
#include <stdlib.h>
#include <epicsVersion.h>
#include <epicsAtomic.h>
#ifndef VERSION_INT
# define VERSION_INT(V,R,M,P) ( ((V)<<24) | ((R)<<16) | ((M)<<8) | (P))
#endif
#ifndef EPICS_VERSION_INT
# define EPICS_VERSION_INT VERSION_INT(EPICS_VERSION, EPICS_REVISION, EPICS_MODIFICATION, EPICS_PATCH_LEVEL)
#endif
#if EPICS_VERSION_INT>=VERSION_INT(3,15,1,0)
# include <epicsAtomic.h>
# define REFTRACK_USE_ATOMIC
#endif
#ifdef REFTRACK_USE_ATOMIC
# define REFTRACE_INCREMENT(counter) ::epics::atomic::increment(counter)
# define REFTRACE_DECREMENT(counter) ::epics::atomic::decrement(counter)
#else
# define REFTRACE_INCREMENT(counter) do{}while(0)
# define REFTRACE_DECREMENT(counter) do{}while(0)
#endif
#define REFTRACE_INCREMENT(counter) ::epics::atomic::increment(counter)
#define REFTRACE_DECREMENT(counter) ::epics::atomic::decrement(counter)
#include <shareLib.h>

View File

@@ -16,6 +16,14 @@
#include <shareLib.h>
#if defined(PVD_INTERNAL)
# define PVD_DEPRECATED(msg)
#elif __GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 5
# define PVD_DEPRECATED(msg) __attribute__((deprecated(msg)))
#else
# define PVD_DEPRECATED(msg) EPICS_DEPRECATED
#endif
namespace epics { namespace pvData {
class SerializableControl;
@@ -47,11 +55,7 @@ namespace epics { namespace pvData {
* @param size The number of bytes.
*/
virtual void ensureBuffer(std::size_t size) =0;
/**
* Add pad bytes to buffer.
* @param alignment alignment required.
*/
virtual void alignBuffer(std::size_t alignment) =0;
virtual void alignBuffer(std::size_t alignment) PVD_DEPRECATED("Deprecated for lack of use") {}
/**
* Method for serializing primitive array data.
* Hook for supplying custom serialization implementation.
@@ -98,14 +102,8 @@ namespace epics { namespace pvData {
* @param size The number of bytes.
*/
virtual void ensureData(std::size_t size) =0;
/**
* Align buffer.
* Note that this takes care only current buffer alignment.
* If streaming protocol is used,
* care must be taken that entire stream is aligned.
* @param alignment size in bytes, must be power of two.
*/
virtual void alignData(std::size_t alignment) =0;
// Deprecated for lack of use
virtual void alignData(std::size_t alignment) PVD_DEPRECATED("Deprecated for lack of use") {};
/**
* Method for deserializing array data.
* Hook for supplying custom deserialization implementation.
@@ -150,7 +148,7 @@ namespace epics { namespace pvData {
*/
virtual void serialize(ByteBuffer *buffer,
SerializableControl *flusher) const = 0;
/**
/**
* Deserialize buffer.
* @param buffer serialization buffer.
* @param flusher deserialization control.
@@ -213,7 +211,7 @@ namespace epics { namespace pvData {
*/
virtual void serialize(ByteBuffer *buffer,
SerializableControl *flusher,BitSet *bitSet) const = 0;
/**
/**
* Deserialize buffer.
* @param buffer serialization buffer.
* @param flusher deserialization control.

View File

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

View File

@@ -6,10 +6,6 @@
#ifndef SHAREDVECTOR_H
#define SHAREDVECTOR_H
#if defined(_WIN32) && !defined(NOMINMAX)
#define NOMINMAX
#endif
#include <ostream>
#include <algorithm>
#include <stdexcept>
@@ -256,7 +252,9 @@ namespace detail {
m_total -= offset;
m_count = std::min(length, max_count);
if(length > max_count)
length = max_count;
m_count = length;
}
// Access to members.
@@ -430,15 +428,17 @@ public:
void reserve(size_t i) {
if(this->unique() && i<=this->m_total)
return;
size_t new_count = std::min(this->m_count, i);
size_t new_count = this->m_count;
if(new_count > i)
new_count = i;
_E_non_const* temp=new _E_non_const[i];
try{
std::copy(begin(), begin()+new_count, temp);
this->m_sdata.reset(temp, detail::default_array_deleter<E*>());
}catch(...){
delete[] temp;
throw;
}
this->m_sdata.reset(temp, detail::default_array_deleter<E*>());
this->m_offset = 0;
this->m_count = new_count;
this->m_total = i;
@@ -468,19 +468,24 @@ public:
}
}
// must re-allocate :(
size_t new_total = std::max(this->m_total, i);
size_t new_total = this->m_total;
if(new_total < i)
new_total = i;
_E_non_const* temp=new _E_non_const[new_total];
try{
size_t n = this->size();
if(n > i)
n = i;
// Copy as much as possible from old,
// remaining elements are uninitialized.
std::copy(begin(),
begin()+std::min(i,this->size()),
begin()+n,
temp);
this->m_sdata.reset(temp, detail::default_array_deleter<pointer>());
}catch(...){
delete[] temp;
throw;
}
this->m_sdata.reset(temp, detail::default_array_deleter<pointer>());
this->m_offset= 0;
this->m_count = i;
this->m_total = new_total;
@@ -539,11 +544,11 @@ private:
/* Hack alert.
* For reasons of simplicity and efficiency, we want to use raw pointers for iteration.
* However, shared_ptr::get() isn't defined when !m_sdata, although practically it gives NULL.
* Unfortunately, many of the MSVC (<= VS 2010) STL methods assert() that iterators are never NULL.
* Unfortunately, many of the MSVC (<= VS 2013) STL methods assert() that iterators are never NULL.
* So we fudge here by abusing 'this' so that our iterators are always !NULL.
*/
inline E* base_ptr() const {
#if defined(_MSC_VER) && _MSC_VER<=1600
#if defined(_MSC_VER) && _MSC_VER<=1800
return this->m_count ? this->m_sdata.get() : (E*)(this-1);
#else
return this->m_sdata.get();
@@ -832,9 +837,13 @@ namespace detail {
typedef typename meta::strip_const<TO>::type to_t;
ScalarType stype = src.original_type(),
dtype = (ScalarType)ScalarTypeID<TO>::value;
if(stype==dtype) {
if(src.empty()) {
return shared_vector<TO>();
} else if(stype==dtype) {
// no convert needed
return shared_vector<TO>(src, detail::_shared_vector_cast_tag());
} else {
// alloc and convert
shared_vector<to_t> ret(src.size()/ScalarTypeFunc::elementSize(stype));

View File

@@ -28,10 +28,10 @@ namespace epics { namespace pvData {
class epicsShareClass Status : public epics::pvData::Serializable {
public:
POINTER_DEFINITIONS(Status);
/**
* Status type enum.
*/
enum StatusType {
/**
* Status type enum.
*/
enum StatusType {
/** Operation completed successfully. */
STATUSTYPE_OK,
/** Operation completed successfully, but there is a warning message. */
@@ -40,11 +40,11 @@ namespace epics { namespace pvData {
STATUSTYPE_ERROR,
/** Operation failed due to an unexpected error. */
STATUSTYPE_FATAL
};
static const char* StatusTypeName[];
static Status Ok;
};
static const char* StatusTypeName[];
static Status Ok;
static inline Status warn(const std::string& m) { return Status(STATUSTYPE_WARNING, m); }
static inline Status error(const std::string& m) { return Status(STATUSTYPE_ERROR, m); }
@@ -54,10 +54,10 @@ namespace epics { namespace pvData {
* Creates OK status; STATUSTYPE_OK, empty message and stackDump.
*/
Status() :m_statusType(STATUSTYPE_OK) {}
/**
* Create non-OK status.
*/
/**
* Create non-OK status.
*/
Status(StatusType type, std::string const & message);
/**
@@ -77,13 +77,13 @@ namespace epics { namespace pvData {
* Get error message describing an error. Required if error status.
* @return error message.
*/
inline std::string getMessage() const { return m_message; }
inline const std::string& getMessage() const { return m_message; }
/**
* Get stack dump where error (exception) happened. Optional.
* @return stack dump.
*/
inline std::string getStackDump() const { return m_stackDump; }
inline const std::string& getStackDump() const { return m_stackDump; }
/**
* Convenient OK test. Same as <code>(getType() == StatusType.OK)</code>.

View File

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

View File

@@ -60,6 +60,8 @@ private:
epicsTime timeToRun;
double period;
bool onList;
bool cancelled;
bool processing;
friend class Timer;
struct IncreasingTime;
};
@@ -126,6 +128,7 @@ private:
mutable Mutex mutex;
queue_t queue;
Event waitForWork;
bool waiting;
bool alive;
Thread thread;
};

View File

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

View File

@@ -25,15 +25,6 @@
namespace {
#ifndef REFTRACK_USE_ATOMIC
static inline
size_t readref(const size_t *ref)
{
volatile const size_t *vref = ref;
return *vref;
}
#endif
typedef epicsGuard<epicsMutex> Guard;
typedef epicsGuardRelease<epicsMutex> UnGuard;
@@ -88,11 +79,7 @@ size_t readRefCounter(const char *name)
refgbl_t::counters_t::iterator it(refgbl->counters.find(name));
if(it==refgbl->counters.end())
return 0;
#ifdef REFTRACK_USE_ATOMIC
return atomic::get(*it->second);
#else
return readref(it->second);
#endif
}
const RefSnapshot::Count&
@@ -119,11 +106,7 @@ void RefSnapshot::update()
end=counters.end();
it!=end; ++it)
{
#ifdef REFTRACK_USE_ATOMIC
size_t cnt = atomic::get(*it->second);
#else
size_t cnt = readref(it->second);
#endif
counts[it->first] = Count(cnt, 0);
}
@@ -223,7 +206,7 @@ void RefMonitor::start(double period)
impl->period = period;
impl->worker.reset(new epicsThread(*impl,
"RefMonitor",
epicsThreadGetStackSize(epicsThreadStackSmall),
epicsThreadGetStackSize(epicsThreadStackBig),
epicsThreadPriorityMin));
impl->worker->start();
}
@@ -288,10 +271,10 @@ char* epicsRefSnapshotCurrent()
snap.update();
std::ostringstream strm;
strm<<snap;
const char *str = strm.str().c_str();
char *ret = (char*)malloc(strlen(str)+1);
std::string str = strm.str();
char *ret = (char*)malloc(str.length()+1);
if(ret)
strcpy(ret, str);
strcpy(ret, str.c_str());
return ret;
}catch(std::exception& e){
return epicsStrDup(e.what());

View File

@@ -32,7 +32,7 @@ namespace epics {
}
void SerializeHelper::writeSize(std::size_t s, ByteBuffer* buffer) {
if(s==(std::size_t)-1) // null // TODO remove
if(s==(std::size_t)-1) // null not a size, but used in PVUnion::serialize()
buffer->putByte(-1);
else if(s<254)
buffer->putByte(static_cast<int8>(s));
@@ -103,13 +103,13 @@ namespace epics {
DeserializableControl* control) {
std::size_t size = SerializeHelper::readSize(buffer, control);
if(size!=(size_t)-1) // TODO null strings check, to be removed in the future
if(size!=(size_t)-1) // TODO null strings check, to be removed in the future
{
if (buffer->getRemaining()>=size)
{
// entire string is in buffer, simply create a string out of it (copy)
std::size_t pos = buffer->getPosition();
string str(buffer->getArray()+pos, size);
string str(buffer->getBuffer()+pos, size);
buffer->setPosition(pos+size);
return str;
}
@@ -122,7 +122,7 @@ namespace epics {
while(true) {
std::size_t toRead = min(size-i, buffer->getRemaining());
std::size_t pos = buffer->getPosition();
str.append(buffer->getArray()+pos, toRead);
str.append(buffer->getBuffer()+pos, toRead);
buffer->setPosition(pos+toRead);
i += toRead;
if(i<size)
@@ -174,14 +174,6 @@ struct ToString : public epics::pvData::SerializableControl
assert(bufwrap.getRemaining()>0);
}
virtual void alignBuffer(std::size_t alignment)
{
if(bufwrap.getRemaining()<alignment)
flushSerializeBuffer();
assert(bufwrap.getRemaining()>=alignment);
bufwrap.align(alignment);
}
virtual bool directSerialize(
ByteBuffer *existingBuffer,
const char* toSerialize,
@@ -232,16 +224,6 @@ struct FromString : public epics::pvData::DeserializableControl
throw std::logic_error("Incomplete buffer");
}
virtual void alignData(std::size_t alignment)
{
size_t pos = buf.getPosition(), k = alignment-1;
if(pos&k) {
std::size_t npad = alignment-(pos&k);
ensureData(npad);
buf.align(alignment);
}
}
virtual bool directDeserialize(
ByteBuffer *existingBuffer,
char* deserializeTo,

View File

@@ -32,7 +32,7 @@ Status::Status(StatusType type, string const & message, string const & stackDump
if (type == STATUSTYPE_OK)
throw std::invalid_argument("type == STATUSTYPE_OK");
}
void Status::maximize(const Status& o)
{
if(m_statusType < o.m_statusType) {
@@ -44,40 +44,40 @@ void Status::maximize(const Status& o)
void Status::serialize(ByteBuffer *buffer, SerializableControl *flusher) const
{
flusher->ensureBuffer(1);
if (m_statusType == STATUSTYPE_OK)
{
// special code for okStatus (optimization)
buffer->putByte((int8)-1);
}
else
{
buffer->putByte((int8)m_statusType);
SerializeHelper::serializeString(m_message, buffer, flusher);
SerializeHelper::serializeString(m_stackDump, buffer, flusher);
}
flusher->ensureBuffer(1);
if (m_statusType == STATUSTYPE_OK)
{
// special code for okStatus (optimization)
buffer->putByte((int8)-1);
}
else
{
buffer->putByte((int8)m_statusType);
SerializeHelper::serializeString(m_message, buffer, flusher);
SerializeHelper::serializeString(m_stackDump, buffer, flusher);
}
}
void Status::deserialize(ByteBuffer *buffer, DeserializableControl *flusher)
{
flusher->ensureData(1);
int8 typeCode = buffer->getByte();
if (typeCode == (int8)-1)
{
// in most of the cases status will be OK, we statistically optimize
if (m_statusType != STATUSTYPE_OK)
{
m_statusType = STATUSTYPE_OK;
flusher->ensureData(1);
int8 typeCode = buffer->getByte();
if (typeCode == (int8)-1)
{
// in most of the cases status will be OK, we statistically optimize
if (m_statusType != STATUSTYPE_OK)
{
m_statusType = STATUSTYPE_OK;
m_message.clear();
m_stackDump.clear();
}
}
else
{
m_statusType = (StatusType)typeCode;
m_message = SerializeHelper::deserializeString(buffer, flusher);
m_stackDump = SerializeHelper::deserializeString(buffer, flusher);
}
}
}
else
{
m_statusType = (StatusType)typeCode;
m_message = SerializeHelper::deserializeString(buffer, flusher);
m_stackDump = SerializeHelper::deserializeString(buffer, flusher);
}
}
void Status::dump(std::ostream& o) const

147
src/misc/thread.cpp Normal file
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(epicsThreadStackBig);
}
size_t Thread::num_instances;
Thread::Config::Config() {this->x_setdefault();}
Thread::Config::Config(Runnable *r) {this->x_setdefault();this->run(r);}
Thread::Config::Config(void(*fn)(void*), void *ptr) {this->x_setdefault();this->run(fn, ptr);}
#if __cplusplus>=201103L
Thread::Config::Config(std::function<void()>&& fn) {this->x_setdefault();this->run(std::move(fn));}
#endif
Thread::Config& Thread::Config::name(const std::string& n)
{ this->p_strm.str(n); return *this; }
Thread::Config& Thread::Config::prio(unsigned int p)
{ this->p_prio = p; return *this; }
Thread::Config& Thread::Config::stack(epicsThreadStackSizeClass s)
{ this->p_stack = epicsThreadGetStackSize(s); return *this; }
Thread::Config& Thread::Config::autostart(bool a)
{ this->p_autostart = a; return *this; }
Thread::Config& Thread::Config::run(Runnable* r)
{ this->p_runner = r; return *this; }
Thread::Config& Thread::Config::run(void(*fn)(void*), void *ptr)
{
this->p_owned_runner.reset(new detail::FuncRunner(fn, ptr));
this->p_runner = this->p_owned_runner.get();
return *this;
}
#if __cplusplus>=201103L
Thread::Config& Thread::Config::run(std::function<void()> &&fn)
{
this->p_owned_runner.reset(new detail::BindRunner(std::move(fn)));
this->p_runner = this->p_owned_runner.get();
return *this;
}
#endif
Thread::Thread(std::string name,
ThreadPriority priority,
Runnable *runnable,
epicsThreadStackSizeClass stkcls)
:epicsThread(*runnable,
name.c_str(),
epicsThreadGetStackSize(stkcls),
priority)
{
REFTRACE_INCREMENT(num_instances);
this->start();
}
Thread::Thread(Runnable &runnable,
std::string name,
unsigned int stksize,
unsigned int priority)
:epicsThread(runnable,
name.c_str(),
stksize,
priority)
{
REFTRACE_INCREMENT(num_instances);
this->start();
}
Thread::Thread(Config& c)
:epicsThread(c.x_getrunner(), c.p_strm.str().c_str(),
c.p_stack, c.p_prio)
{
REFTRACE_INCREMENT(num_instances);
#if __cplusplus>=201103L
p_owned = std::move(c.p_owned_runner);
#else
p_owned = c.p_owned_runner;
#endif
if(c.p_autostart)
this->start();
}
Thread::~Thread()
{
this->exitWait();
REFTRACE_DECREMENT(num_instances);
}
}} // epics::pvData

View File

@@ -6,10 +6,6 @@
/**
* @author mrk
*/
#if defined(_WIN32) && !defined(NOMINMAX)
#define NOMINMAX
#endif
#include <stdexcept>
#include <string>
@@ -23,18 +19,21 @@
using std::string;
namespace epics { namespace pvData {
namespace epics { namespace pvData {
TimerCallback::TimerCallback()
: period(0.0),
onList(false)
onList(false),
cancelled(false),
processing(false)
{
}
Timer::Timer(string threadName,ThreadPriority priority)
: waitForWork(false),
alive(true),
thread(threadName,priority,this)
:waitForWork(false)
,waiting(false)
,alive(true)
,thread(threadName,priority,this)
{}
struct TimerCallback::IncreasingTime {
@@ -53,6 +52,7 @@ void Timer::addElement(TimerCallbackPtr const & timerCallback)
temp.push_back(timerCallback);
timerCallback->onList = true;
timerCallback->cancelled = false;
// merge sorted lists.
// for us effectively insertion sort.
@@ -63,14 +63,22 @@ void Timer::addElement(TimerCallbackPtr const & timerCallback)
bool Timer::cancel(TimerCallbackPtr const &timerCallback)
{
Lock xx(mutex);
/* If we're processing, just set the cancel flag */
if (timerCallback->processing) {
timerCallback->cancelled = true;
return true;
}
if(!timerCallback->onList) return false;
if(!alive) {
timerCallback->onList = false;
return true;
}
for(queue_t::iterator it(queue.begin()), end(queue.end()); it != end; ++it)
{
TimerCallbackPtr& cur = *it;
if(cur.get() == timerCallback.get()) {
queue.erase(it);
cur->onList = false;
// iteration now invalid
queue.erase(it); // invalidates cur and it
return true;
}
}
@@ -95,6 +103,7 @@ void Timer::run()
if(queue.empty()) {
// no jobs, just go to sleep
waiting = true;
epicsGuardRelease<epicsMutex> U(G);
waitForWork.wait();
@@ -106,6 +115,7 @@ void Timer::run()
TimerCallbackPtr work;
work.swap(queue.front());
work->onList = false;
work->processing = true;
queue.pop_front();
{
@@ -114,7 +124,15 @@ void Timer::run()
work->callback();
}
if(work->period > 0.0) {
work->processing = false;
if(work->period > 0.0 && alive && !work->cancelled) {
if(waitfor <= -work->period) {
// Periodic timer phase has fallen behind by at least one full period.
// Could be due to previous slow jobs, or a system time jump forward.
// Reset the phase.
work->timeToRun = now = epicsTime::getCurrent();
}
work->timeToRun += work->period;
addElement(work);
}
@@ -122,12 +140,14 @@ void Timer::run()
// don't update 'now' until all expired jobs run
} else {
waiting = true;
// wait for first un-expired
epicsGuardRelease<epicsMutex> U(G);
waitForWork.wait(waitfor);
now = epicsTime::getCurrent();
}
waiting = false;
}
}
@@ -169,7 +189,7 @@ void Timer::schedulePeriodic(
{
epicsTime now(epicsTime::getCurrent());
bool wasempty;
bool wakeup;
{
Lock xx(mutex);
if(timerCallback->onList) {
@@ -185,10 +205,10 @@ void Timer::schedulePeriodic(
timerCallback->timeToRun = now + delay;
timerCallback->period = period;
wasempty = queue.empty();
addElement(timerCallback);
wakeup = waiting && queue.front()==timerCallback;
}
if(wasempty) waitForWork.signal();
if(wakeup) waitForWork.signal();
}
void Timer::dump(std::ostream& o) const

View File

@@ -30,9 +30,9 @@ static void castVTyped(size_t count, void *draw, const void *sraw)
{
TO *dest=(TO*)draw;
const FROM *src=(FROM*)sraw;
//std::transform(src, src+count, dest, castUnsafe<TO,FROM>);
try {
for(size_t i=0; i<count; i++) {
dest[i] = castUnsafe<TO,FROM>(src[i]);

View File

@@ -20,7 +20,7 @@
using std::string;
namespace epics { namespace pvData {
namespace epics { namespace pvData {
AlarmSeverity AlarmSeverityFunc::getSeverity(int value)
{

View File

@@ -44,11 +44,6 @@ public:
//default constructors and destructor are OK
//returns (false,true) if pvField(is not, is) a valid alarm structure
//An automatic detach is issued if already attached.
/*
* Attach to a field of a PVData object.
* @param pvField The pvField.
* @return (false,true) if the pvField (is not, is) an alarm structure.
*/
/*
* Attach to a field of a PVData object.
* @param pvField The pvField.

View File

@@ -18,7 +18,7 @@
using std::tr1::static_pointer_cast;
using std::string;
namespace epics { namespace pvData {
namespace epics { namespace pvData {
string PVAlarm::noAlarmFound("No alarm structure found");
string PVAlarm::notAttached("Not attached to an alarm structure");

View File

@@ -15,7 +15,7 @@
#include <pv/pvData.h>
#include <pv/pvControl.h>
namespace epics { namespace pvData {
namespace epics { namespace pvData {
using std::tr1::static_pointer_cast;
using std::string;

View File

@@ -18,7 +18,7 @@
using std::tr1::static_pointer_cast;
using std::string;
namespace epics { namespace pvData {
namespace epics { namespace pvData {
string PVDisplay::noDisplayFound("No display structure found");
string PVDisplay::notAttached("Not attached to an display structure");

View File

@@ -18,7 +18,7 @@
using std::tr1::static_pointer_cast;
using std::string;
namespace epics { namespace pvData {
namespace epics { namespace pvData {
string PVEnumerated::notFound("No enumerated structure found");
string PVEnumerated::notAttached("Not attached to an enumerated structure");

View File

@@ -18,7 +18,7 @@
using std::tr1::static_pointer_cast;
using std::string;
namespace epics { namespace pvData {
namespace epics { namespace pvData {
string PVTimeStamp::noTimeStamp("No timeStamp structure found");
string PVTimeStamp::notAttached("Not attached to a timeStamp structure");
@@ -93,5 +93,5 @@ bool PVTimeStamp::set(TimeStamp const & timeStamp)
}
return returnValue;
}
}}

View File

@@ -19,7 +19,7 @@
#include <pv/pvType.h>
#include <pv/timeStamp.h>
namespace epics { namespace pvData {
namespace epics { namespace pvData {
const int32 milliSecPerSec = 1000;
const int32 microSecPerSec = 1000000;

View File

@@ -9,10 +9,6 @@
#ifndef PVDATA_H
#define PVDATA_H
#if defined(_WIN32) && !defined(NOMINMAX)
#define NOMINMAX
#endif
#include <string>
#include <map>
#include <stdexcept>
@@ -21,6 +17,8 @@
#include <iostream>
#include <iomanip>
#include <epicsAssert.h>
#include <pv/pvIntrospect.h>
#include <pv/typeCast.h>
#include <pv/anyscalar.h>
@@ -241,6 +239,7 @@ public:
void copyUnchecked(const PVField& from);
static size_t num_instances; // use atomic::get() or volatile* access
enum {isPVField=1};
protected:
PVField::shared_pointer getPtrSelf()
{
@@ -260,6 +259,7 @@ private:
PostHandlerPtr postHandler;
friend class PVDataCreate;
friend class PVStructure;
EPICS_NOT_COPYABLE(PVField)
};
epicsShareExtern std::ostream& operator<<(std::ostream& o, const PVField& f);
@@ -340,6 +340,7 @@ public:
protected:
explicit PVScalar(ScalarConstPtr const & scalar);
EPICS_NOT_COPYABLE(PVScalar)
};
namespace detail {
@@ -397,31 +398,22 @@ public:
* Put a new value into the PVScalar.
* @param value The value.
*/
void put(typename storage_t::arg_type v) {
inline void put(typename storage_t::arg_type v) {
storage.store(v);
PVField::postPut();
}
std::ostream& dumpValue(std::ostream& o) const OVERRIDE
{
return o << get();
}
virtual std::ostream& dumpValue(std::ostream& o) const OVERRIDE;
// get operator
// double value; doubleField >>= value;
// NOTE: virtual is needed for MS C++ compiler to get this operator exported
virtual void operator>>=(T& value) const
{
value = get();
}
virtual void operator>>=(T& value) const;
// put operator
// double value = 12.8; doubleField <<= value;
// NOTE: virtual is needed for MS C++ compiler to get this operator exported
virtual void operator<<=(typename storage_t::arg_type value)
{
put(value);
}
virtual void operator<<=(typename storage_t::arg_type value);
template<typename T1>
inline T1 getAs() const {
@@ -439,44 +431,29 @@ public:
PVScalar::putFrom(v);
}
virtual void assign(const PVScalar& scalar) OVERRIDE
{
if(isImmutable())
throw std::invalid_argument("destination is immutable");
copyUnchecked(scalar);
}
virtual void copy(const PVScalar& from) OVERRIDE
{
assign(from);
}
virtual void copyUnchecked(const PVScalar& from) OVERRIDE
{
if(this==&from)
return;
T result;
from.getAs((void*)&result, typeCode);
put(result);
}
virtual void assign(const PVScalar& scalar) OVERRIDE FINAL;
virtual void copy(const PVScalar& from) OVERRIDE FINAL;
virtual void copyUnchecked(const PVScalar& from) OVERRIDE FINAL;
virtual void serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher) const OVERRIDE;
virtual void deserialize(ByteBuffer *pbuffer,
DeserializableControl *pflusher) OVERRIDE;
DeserializableControl *pflusher) OVERRIDE FINAL;
protected:
explicit PVScalarValue(ScalarConstPtr const & scalar)
: PVScalar(scalar), storage() {}
virtual void getAs(void * result, ScalarType rtype) const OVERRIDE
virtual void getAs(void * result, ScalarType rtype) const OVERRIDE FINAL
{
const T src = get();
castUnsafeV(1, rtype, result, typeCode, (const void*)&src);
}
public:
virtual void getAs(AnyScalar& v) const
virtual void getAs(AnyScalar& v) const OVERRIDE FINAL
{
v = get();
}
virtual void putFrom(const void *src, ScalarType stype) OVERRIDE
virtual void putFrom(const void *src, ScalarType stype) OVERRIDE FINAL
{
T result;
castUnsafeV(1, typeCode, (void*)&result, stype, src);
@@ -486,6 +463,7 @@ protected:
friend class PVDataCreate;
storage_t storage;
EPICS_NOT_COPYABLE(PVScalarValue)
};
/**
@@ -547,14 +525,17 @@ public:
*/
virtual ~PVString() {}
virtual std::ostream& dumpValue(std::ostream& o) const OVERRIDE FINAL;
virtual void serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher) const OVERRIDE;
SerializableControl *pflusher) const OVERRIDE FINAL;
virtual void serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher, size_t offset, size_t count) const OVERRIDE;
SerializableControl *pflusher, size_t offset, size_t count) const OVERRIDE FINAL;
protected:
explicit PVString(ScalarConstPtr const & scalar);
friend class PVDataCreate;
EPICS_NOT_COPYABLE(PVString)
};
typedef std::tr1::shared_ptr<PVString> PVStringPtr;
@@ -624,6 +605,7 @@ protected:
private:
bool capacityMutable;
friend class PVDataCreate;
EPICS_NOT_COPYABLE(PVArray)
};
epicsShareExtern std::ostream& operator<<(format::array_at_internal const& manip, const PVArray& array);
@@ -718,6 +700,7 @@ protected:
explicit PVScalarArray(ScalarArrayConstPtr const & scalarArray);
private:
friend class PVDataCreate;
EPICS_NOT_COPYABLE(PVScalarArray)
};
@@ -740,17 +723,17 @@ public:
* Set the field to be immutable, i.e. it can no longer be modified.
* This is permanent, i.e. once done the field cannot be made mutable.
*/
virtual void setImmutable() OVERRIDE;
virtual void setImmutable() OVERRIDE FINAL;
/**
* Get the introspection interface
* @return The interface.
*/
const StructureConstPtr &getStructure() const;
inline const StructureConstPtr &getStructure() const { return structurePtr; }
/**
* Get the array of pointers to the subfields in the structure.
* @return The array.
*/
const PVFieldPtrArray & getPVFields() const;
inline const PVFieldPtrArray & getPVFields() const { return pvFields; }
/**
* Get the subfield with the specified offset.
@@ -782,12 +765,14 @@ public:
template<typename PVD, typename A>
inline std::tr1::shared_ptr<PVD> getSubField(A a)
{
STATIC_ASSERT(PVD::isPVField); // only allow cast from PVField sub-class
return std::tr1::dynamic_pointer_cast<PVD>(getSubFieldImpl(a, false));
}
template<typename PVD, typename A>
inline std::tr1::shared_ptr<const PVD> getSubField(A a) const
{
STATIC_ASSERT(PVD::isPVField); // only allow cast from PVField sub-class
return std::tr1::dynamic_pointer_cast<const PVD>(getSubFieldImpl(a, false));
}
@@ -820,6 +805,7 @@ public:
template<typename PVD, typename A>
inline std::tr1::shared_ptr<PVD> getSubFieldT(A a)
{
STATIC_ASSERT(PVD::isPVField); // only allow cast from PVField sub-class
std::tr1::shared_ptr<PVD> ret(std::tr1::dynamic_pointer_cast<PVD>(getSubFieldImpl(a, true)));
if(!ret)
throwBadFieldType(a);
@@ -829,6 +815,7 @@ public:
template<typename PVD, typename A>
inline std::tr1::shared_ptr<const PVD> getSubFieldT(A a) const
{
STATIC_ASSERT(PVD::isPVField); // only allow cast from PVField sub-class
std::tr1::shared_ptr<const PVD> ret(std::tr1::dynamic_pointer_cast<const PVD>(getSubFieldImpl(a, true)));
if(!ret)
throwBadFieldType(a);
@@ -841,14 +828,14 @@ public:
* @param pflusher Interface to call when buffer is full.
*/
virtual void serialize(
ByteBuffer *pbuffer,SerializableControl *pflusher) const OVERRIDE;
ByteBuffer *pbuffer,SerializableControl *pflusher) const OVERRIDE FINAL;
/**
* Deserialize
* @param pbuffer The byte buffer.
* @param pflusher Interface to call when buffer is empty.
*/
virtual void deserialize(
ByteBuffer *pbuffer,DeserializableControl *pflusher) OVERRIDE;
ByteBuffer *pbuffer,DeserializableControl *pflusher) OVERRIDE FINAL;
/**
* Serialize.
* @param pbuffer The byte buffer.
@@ -856,7 +843,7 @@ public:
* @param pbitSet A bitset the specifies which fields to serialize.
*/
virtual void serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher,BitSet *pbitSet) const OVERRIDE;
SerializableControl *pflusher,BitSet *pbitSet) const OVERRIDE FINAL;
/**
* Deserialize
* @param pbuffer The byte buffer.
@@ -864,7 +851,7 @@ public:
* @param pbitSet A bitset the specifies which fields to deserialize.
*/
virtual void deserialize(ByteBuffer *pbuffer,
DeserializableControl*pflusher,BitSet *pbitSet) OVERRIDE;
DeserializableControl*pflusher,BitSet *pbitSet) OVERRIDE FINAL;
/**
* Constructor
* @param structure The introspection interface.
@@ -877,14 +864,56 @@ public:
*/
PVStructure(StructureConstPtr const & structure,PVFieldPtrArray const & pvFields);
virtual std::ostream& dumpValue(std::ostream& o) const OVERRIDE;
virtual std::ostream& dumpValue(std::ostream& o) const OVERRIDE FINAL;
void copy(const PVStructure& from);
void copyUnchecked(const PVStructure& from);
void copyUnchecked(const PVStructure& from, const BitSet& maskBitSet, bool inverse = false);
struct Formatter {
enum mode_t {
Auto,
Plain,
ANSI,
};
enum format_t {
Raw,
NT,
JSON,
};
private:
const PVStructure& xtop;
const BitSet* xshow;
const BitSet* xhighlight;
mode_t xmode;
format_t xfmt;
public:
explicit Formatter(const PVStructure& top)
:xtop(top)
,xshow(0)
,xhighlight(0)
,xmode(Auto)
,xfmt(NT)
{}
// those fields (and their parents) to be printed. non-NT mode.
FORCE_INLINE Formatter& show(const BitSet& set) { xshow = &set; return *this; }
// those fields (and not their parents) to be specially highlighted. non-NT mode.
FORCE_INLINE Formatter& highlight(const BitSet& set) { xhighlight = &set; return *this; }
FORCE_INLINE Formatter& mode(mode_t m) { xmode = m; return *this; }
FORCE_INLINE Formatter& format(format_t f) { xfmt = f; return *this; }
friend epicsShareFunc std::ostream& operator<<(std::ostream& strm, const Formatter& format);
friend void printRaw(std::ostream& strm, const PVStructure::Formatter& format, const PVStructure& cur);
};
FORCE_INLINE Formatter stream() const { return Formatter(*this); }
private:
inline PVFieldPtr getSubFieldImpl(const std::string& name, bool throws) const {
return getSubFieldImpl(name.c_str(), throws);
}
@@ -895,8 +924,12 @@ private:
StructureConstPtr structurePtr;
std::string extendsStructureName;
friend class PVDataCreate;
EPICS_NOT_COPYABLE(PVStructure)
};
epicsShareFunc
std::ostream& operator<<(std::ostream& strm, const PVStructure::Formatter& format);
/**
* @brief PVUnion has a single subfield.
*
@@ -915,11 +948,11 @@ public:
typedef PVUnion & reference;
typedef const PVUnion & const_reference;
/**
* Undefined index.
* Default value upon PVUnion construction. Can be set by the user.
* Corresponds to @c null value.
*/
/**
* Undefined index.
* Default value upon PVUnion construction. Can be set by the user.
* Corresponds to @c null value.
*/
static const int32 UNDEFINED_INDEX;
/**
@@ -937,11 +970,13 @@ public:
template<typename PVT>
inline std::tr1::shared_ptr<PVT> get() {
STATIC_ASSERT(PVT::isPVField); // only allow cast from PVField sub-class
return std::tr1::dynamic_pointer_cast<PVT>(get());
}
template<typename PVT>
inline std::tr1::shared_ptr<const PVT> get() const {
STATIC_ASSERT(PVT::isPVField); // only allow cast from PVField sub-class
return std::tr1::dynamic_pointer_cast<const PVT>(get());
}
@@ -955,6 +990,7 @@ public:
template<typename PVT>
inline std::tr1::shared_ptr<PVT> select(int32 index) {
STATIC_ASSERT(PVT::isPVField); // only allow cast from PVField sub-class
return std::tr1::dynamic_pointer_cast<PVT>(select(index));
}
@@ -1020,21 +1056,21 @@ public:
* @param pflusher Interface to call when buffer is full.
*/
virtual void serialize(
ByteBuffer *pbuffer,SerializableControl *pflusher) const OVERRIDE;
ByteBuffer *pbuffer,SerializableControl *pflusher) const OVERRIDE FINAL;
/**
* Deserialize
* @param pbuffer The byte buffer.
* @param pflusher Interface to call when buffer is empty.
*/
virtual void deserialize(
ByteBuffer *pbuffer,DeserializableControl *pflusher) OVERRIDE;
ByteBuffer *pbuffer,DeserializableControl *pflusher) OVERRIDE FINAL;
/**
* Constructor
* @param punion The introspection interface.
*/
explicit PVUnion(UnionConstPtr const & punion);
virtual std::ostream& dumpValue(std::ostream& o) const OVERRIDE;
virtual std::ostream& dumpValue(std::ostream& o) const OVERRIDE FINAL;
void copy(const PVUnion& from);
void copyUnchecked(const PVUnion& from);
@@ -1045,9 +1081,10 @@ private:
friend class PVDataCreate;
UnionConstPtr unionPtr; // same as PVField::getField()
int32 selector;
PVFieldPtr value;
bool variant;
int32 selector;
PVFieldPtr value;
bool variant;
EPICS_NOT_COPYABLE(PVUnion)
};
@@ -1122,6 +1159,7 @@ namespace detail {
return thaw(result);
}
EPICS_NOT_COPYABLE(PVVectorStorage)
};
} // namespace detail
@@ -1152,67 +1190,40 @@ public:
/**
* Destructor
*/
virtual ~PVValueArray() {}
virtual ~PVValueArray();
/**
* Get introspection interface.
*/
virtual ArrayConstPtr getArray() const OVERRIDE
{
return std::tr1::static_pointer_cast<const Array>(this->getField());
}
virtual ArrayConstPtr getArray() const OVERRIDE FINAL;
std::ostream& dumpValue(std::ostream& o) const OVERRIDE
{
const_svector v(this->view());
typename const_svector::const_iterator it(v.begin()),
end(v.end());
o << '[';
if(it!=end) {
o << print_cast(*it++);
for(; it!=end; ++it)
o << ',' << print_cast(*it);
virtual std::ostream& dumpValue(std::ostream& o) const OVERRIDE FINAL;
virtual std::ostream& dumpValue(std::ostream& o, size_t index) const OVERRIDE FINAL;
}
return o << ']';
}
virtual size_t getLength() const OVERRIDE FINAL {return value.size();}
virtual size_t getCapacity() const OVERRIDE FINAL {return value.capacity();}
virtual size_t getLength() const OVERRIDE {return value.size();}
virtual size_t getCapacity() const OVERRIDE {return value.capacity();}
virtual void setCapacity(size_t capacity) OVERRIDE FINAL;
virtual void setLength(size_t length) OVERRIDE FINAL;
virtual void setCapacity(size_t capacity) OVERRIDE;
virtual void setLength(size_t length) OVERRIDE;
virtual const_svector view() const OVERRIDE {return value;}
virtual void swap(const_svector &other) OVERRIDE;
virtual void replace(const const_svector& next) OVERRIDE;
virtual const_svector view() const OVERRIDE FINAL {return value;}
virtual void swap(const_svector &other) OVERRIDE FINAL;
virtual void replace(const const_svector& next) OVERRIDE FINAL;
// from Serializable
virtual void serialize(ByteBuffer *pbuffer,SerializableControl *pflusher) const OVERRIDE;
virtual void deserialize(ByteBuffer *pbuffer,DeserializableControl *pflusher) OVERRIDE;
virtual void serialize(ByteBuffer *pbuffer,SerializableControl *pflusher) const OVERRIDE FINAL;
virtual void deserialize(ByteBuffer *pbuffer,DeserializableControl *pflusher) OVERRIDE FINAL;
virtual void serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher, size_t offset, size_t count) const OVERRIDE;
std::ostream& dumpValue(std::ostream& o, size_t index) const OVERRIDE
{
return o << print_cast(this->view().at(index));
}
SerializableControl *pflusher, size_t offset, size_t count) const OVERRIDE FINAL;
protected:
virtual void _getAsVoid(epics::pvData::shared_vector<const void>& out) const OVERRIDE
{
out = static_shared_vector_cast<const void>(this->view());
}
virtual void _putFromVoid(const epics::pvData::shared_vector<const void>& in) OVERRIDE
{
// TODO: try to re-use storage
this->replace(shared_vector_convert<const T>(in));
}
virtual void _getAsVoid(epics::pvData::shared_vector<const void>& out) const OVERRIDE FINAL;
virtual void _putFromVoid(const epics::pvData::shared_vector<const void>& in) OVERRIDE FINAL;
explicit PVValueArray(ScalarArrayConstPtr const & scalar);
const_svector value;
friend class PVDataCreate;
EPICS_NOT_COPYABLE(PVValueArray)
};
@@ -1241,24 +1252,24 @@ public:
*/
virtual ~PVValueArray() {}
virtual ArrayConstPtr getArray() const OVERRIDE
virtual ArrayConstPtr getArray() const OVERRIDE FINAL
{
return std::tr1::static_pointer_cast<const Array>(structureArray);
}
virtual size_t getLength() const OVERRIDE {return value.size();}
virtual size_t getCapacity() const OVERRIDE {return value.capacity();}
virtual size_t getLength() const OVERRIDE FINAL {return value.size();}
virtual size_t getCapacity() const OVERRIDE FINAL {return value.capacity();}
/**
* Set the array capacity.
* @param capacity The length.
*/
virtual void setCapacity(size_t capacity) OVERRIDE;
virtual void setCapacity(size_t capacity) OVERRIDE FINAL;
/**
* Set the array length.
* @param length The length.
*/
virtual void setLength(std::size_t length) OVERRIDE;
virtual void setLength(std::size_t length) OVERRIDE FINAL;
/**
* Get the introspection interface
@@ -1270,36 +1281,36 @@ public:
* @param number The number of elements to add.
* @return the new length of the array.
*/
virtual std::size_t append(std::size_t number) FINAL;
std::size_t append(std::size_t number);
/**
* Remove elements from the array.
* @param offset The offset of the first element to remove.
* @param number The number of elements to remove.
* @return (false,true) if the elements were removed.
*/
virtual bool remove(std::size_t offset,std::size_t number) FINAL;
bool remove(std::size_t offset,std::size_t number);
/**
* Compress. This removes all null elements from the array.
*/
virtual void compress() FINAL;
void compress();
virtual const_svector view() const OVERRIDE { return value; }
virtual void swap(const_svector &other) OVERRIDE;
virtual void replace(const const_svector &other) OVERRIDE {
virtual const_svector view() const OVERRIDE FINAL { return value; }
virtual void swap(const_svector &other) OVERRIDE FINAL;
virtual void replace(const const_svector &other) OVERRIDE FINAL {
checkLength(other.size());
value = other;
PVField::postPut();
}
virtual void serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher) const OVERRIDE;
SerializableControl *pflusher) const OVERRIDE FINAL;
virtual void deserialize(ByteBuffer *buffer,
DeserializableControl *pflusher) OVERRIDE;
DeserializableControl *pflusher) OVERRIDE FINAL;
virtual void serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher, std::size_t offset, std::size_t count) const OVERRIDE;
SerializableControl *pflusher, std::size_t offset, std::size_t count) const OVERRIDE FINAL;
virtual std::ostream& dumpValue(std::ostream& o) const OVERRIDE;
virtual std::ostream& dumpValue(std::ostream& o, std::size_t index) const OVERRIDE;
virtual std::ostream& dumpValue(std::ostream& o) const OVERRIDE FINAL;
virtual std::ostream& dumpValue(std::ostream& o, std::size_t index) const OVERRIDE FINAL;
void copy(const PVStructureArray& from);
void copyUnchecked(const PVStructureArray& from);
@@ -1310,6 +1321,7 @@ private:
StructureArrayConstPtr structureArray;
const_svector value;
friend class PVDataCreate;
EPICS_NOT_COPYABLE(PVValueArray)
};
@@ -1339,24 +1351,24 @@ public:
*/
virtual ~PVValueArray() {}
virtual ArrayConstPtr getArray() const OVERRIDE
virtual ArrayConstPtr getArray() const OVERRIDE FINAL
{
return std::tr1::static_pointer_cast<const Array>(unionArray);
}
virtual size_t getLength() const OVERRIDE {return value.size();}
virtual size_t getCapacity() const OVERRIDE {return value.capacity();}
virtual size_t getLength() const OVERRIDE FINAL {return value.size();}
virtual size_t getCapacity() const OVERRIDE FINAL {return value.capacity();}
/**
* Set the array capacity.
* @param capacity The length.
*/
virtual void setCapacity(size_t capacity) OVERRIDE;
virtual void setCapacity(size_t capacity) OVERRIDE FINAL;
/**
* Set the array length.
* @param length The length.
*/
virtual void setLength(std::size_t length) OVERRIDE;
virtual void setLength(std::size_t length) OVERRIDE FINAL;
/**
* Get the introspection interface
@@ -1368,36 +1380,36 @@ public:
* @param number The number of elements to add.
* @return the new length of the array.
*/
virtual std::size_t append(std::size_t number) FINAL;
std::size_t append(std::size_t number);
/**
* Remove elements from the array.
* @param offset The offset of the first element to remove.
* @param number The number of elements to remove.
* @return (false,true) if the elements were removed.
*/
virtual bool remove(std::size_t offset,std::size_t number) FINAL;
bool remove(std::size_t offset,std::size_t number);
/**
* Compress. This removes all null elements from the array.
*/
virtual void compress() FINAL;
void compress();
virtual const_svector view() const OVERRIDE { return value; }
virtual void swap(const_svector &other) OVERRIDE;
virtual void replace(const const_svector &other) OVERRIDE {
virtual void replace(const const_svector &other) OVERRIDE FINAL {
checkLength(other.size());
value = other;
PVField::postPut();
}
virtual void serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher) const OVERRIDE;
SerializableControl *pflusher) const OVERRIDE FINAL;
virtual void deserialize(ByteBuffer *buffer,
DeserializableControl *pflusher) OVERRIDE;
DeserializableControl *pflusher) OVERRIDE FINAL;
virtual void serialize(ByteBuffer *pbuffer,
SerializableControl *pflusher, std::size_t offset, std::size_t count) const OVERRIDE;
SerializableControl *pflusher, std::size_t offset, std::size_t count) const OVERRIDE FINAL;
virtual std::ostream& dumpValue(std::ostream& o) const OVERRIDE;
virtual std::ostream& dumpValue(std::ostream& o, std::size_t index) const OVERRIDE;
virtual std::ostream& dumpValue(std::ostream& o) const OVERRIDE FINAL;
virtual std::ostream& dumpValue(std::ostream& o, std::size_t index) const OVERRIDE FINAL;
void copy(const PVUnionArray& from);
void copyUnchecked(const PVUnionArray& from);
@@ -1408,6 +1420,7 @@ private:
UnionArrayConstPtr unionArray;
const_svector value;
friend class PVDataCreate;
EPICS_NOT_COPYABLE(PVValueArray)
};
@@ -1623,6 +1636,7 @@ public:
private:
PVDataCreate();
FieldCreatePtr fieldCreate;
EPICS_NOT_COPYABLE(PVDataCreate)
};
/**
@@ -1650,9 +1664,3 @@ namespace std{
}
#endif /* PVDATA_H */
/** @page Overview Documentation
*
* <a href = "pvDataCPP.html">pvData.html</a>
*
*/

View File

@@ -12,7 +12,11 @@
#include <string>
#include <stdexcept>
#include <iostream>
#include <map>
#include <epicsAssert.h>
#include <pv/lock.h>
#include <pv/noDefaultMethods.h>
#include <pv/pvType.h>
#include <pv/byteBuffer.h>
@@ -20,6 +24,7 @@
#include <pv/pvdVersion.h>
#include <shareLib.h>
#define PVD_DEPRECATED_52 PVD_DEPRECATED("See https://github.com/epics-base/pvDataCPP/issues/52")
/* C++11 keywords
@code
@@ -116,6 +121,13 @@ class UnionArray;
class BoundedString;
class PVField;
class PVScalar;
class PVScalarArray;
class PVStructure;
class PVUnion;
template<typename T> class PVValueArray;
/**
* typedef for a shared pointer to an immutable Field.
*/
@@ -339,14 +351,24 @@ public:
*/
virtual std::ostream& dump(std::ostream& o) const = 0;
//! Allocate a new instance
//! @version Added after 7.0.0
std::tr1::shared_ptr<PVField> build() const;
enum {isField=1};
protected:
/**
* Constructor
* @param type The field type.
*/
Field(Type type);
void cacheCleanup();
private:
const Type m_fieldType;
unsigned int m_hash;
struct Helper;
friend struct Helper;
friend class StructureArray;
friend class Structure;
@@ -354,8 +376,7 @@ private:
friend class StandardField;
friend class BasePVStructureArray;
friend class FieldCreate;
struct Deleter{void operator()(Field *p){delete p;}};
EPICS_NOT_COPYABLE(Field)
};
epicsShareExtern std::ostream& operator<<(std::ostream& o, const Field& field);
@@ -383,6 +404,10 @@ public:
virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const OVERRIDE;
virtual void deserialize(ByteBuffer *buffer, DeserializableControl *control) OVERRIDE FINAL;
//! Allocate a new instance
//! @version Added after 7.0.0
std::tr1::shared_ptr<PVScalar> build() const;
protected:
Scalar(ScalarType scalarType);
@@ -394,6 +419,7 @@ private:
friend class BoundedScalarArray;
friend class FixedScalarArray;
friend class BoundedString;
EPICS_NOT_COPYABLE(Scalar)
};
/**
@@ -418,6 +444,7 @@ protected:
private:
std::size_t maxLength;
friend class FieldCreate;
EPICS_NOT_COPYABLE(BoundedString)
};
/**
@@ -452,6 +479,7 @@ protected:
*/
Array(Type type);
EPICS_NOT_COPYABLE(Array)
};
/**
@@ -485,13 +513,17 @@ public:
virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const OVERRIDE;
virtual void deserialize(ByteBuffer *buffer, DeserializableControl *control) OVERRIDE FINAL;
//! Allocate a new instance
//! @version Added after 7.0.0
std::tr1::shared_ptr<PVScalarArray> build() const;
protected:
virtual ~ScalarArray();
private:
const std::string getIDScalarArrayLUT() const;
ScalarType elementType;
friend class FieldCreate;
EPICS_NOT_COPYABLE(ScalarArray)
};
@@ -521,11 +553,11 @@ public:
virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const OVERRIDE FINAL;
protected:
virtual ~BoundedScalarArray();
private:
std::size_t size;
friend class FieldCreate;
EPICS_NOT_COPYABLE(BoundedScalarArray)
};
/**
@@ -553,11 +585,11 @@ public:
virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const OVERRIDE FINAL;
protected:
virtual ~FixedScalarArray();
private:
std::size_t size;
friend class FieldCreate;
EPICS_NOT_COPYABLE(FixedScalarArray)
};
/**
@@ -587,16 +619,22 @@ public:
virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const OVERRIDE FINAL;
virtual void deserialize(ByteBuffer *buffer, DeserializableControl *control) OVERRIDE FINAL;
//! Allocate a new instance
//! @version Added after 7.0.0
std::tr1::shared_ptr<PVValueArray<std::tr1::shared_ptr<PVStructure> > > build() const;
protected:
/**
* Constructor.
* @param structure The introspection interface for the elements.
*/
StructureArray(StructureConstPtr const & structure);
public:
virtual ~StructureArray();
private:
StructureConstPtr pstructure;
friend class FieldCreate;
EPICS_NOT_COPYABLE(StructureArray)
};
/**
@@ -626,16 +664,22 @@ public:
virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const OVERRIDE FINAL;
virtual void deserialize(ByteBuffer *buffer, DeserializableControl *control) OVERRIDE FINAL;
//! Allocate a new instance
//! @version Added after 7.0.0
std::tr1::shared_ptr<PVValueArray<std::tr1::shared_ptr<PVUnion> > > build() const;
protected:
/**
* Constructor.
* @param _punion The introspection interface for the elements.
*/
UnionArray(UnionConstPtr const & _punion);
public:
virtual ~UnionArray();
private:
UnionConstPtr punion;
friend class FieldCreate;
EPICS_NOT_COPYABLE(UnionArray)
};
/**
@@ -666,40 +710,96 @@ public:
* @return The number of fields.
*/
std::size_t getNumberFields() const {return fieldNames.size();}
/**
* Get the field for the specified fieldName.
* @param fieldName The name of the field to get;
* @return The introspection interface.
* This will hold a null pointer if the field is not in the structure.
* Lookup Field by name
* @param fieldName Member field name. May not contain '.'
* @return NULL if no member by this name.
*/
FieldConstPtr getField(std::string const &fieldName) const;
/** Lookup Field by name and cast to Field sub-class.
* @param fieldName Member field name. May not contain '.'
* @return NULL If no member by this name, or member exists, but has type other than FT.
*/
template<typename FT>
std::tr1::shared_ptr<const FT> getField(std::string const &fieldName) const
{
FieldConstPtr field(getField(fieldName));
if (field)
return std::tr1::dynamic_pointer_cast<const FT>(field);
else
return std::tr1::shared_ptr<const FT>();
STATIC_ASSERT(FT::isField); // only allow cast from Field sub-class
return std::tr1::dynamic_pointer_cast<const FT>(getField(fieldName));
}
/**
* Get the field for the specified fieldName.
* @param index The index of the field to get;
* @return The introspection interface.
* This will hold a null pointer if the field is not in the structure.
* Lookup Field by name
* @param fieldName Member field name. May not contain '.'
* @return Field pointer (never NULL)
* @throws std::runtime_error If no member by this name
*/
FieldConstPtr getFieldT(std::string const &fieldName) const {return getFieldImpl(fieldName, true);};
/** Lookup Field by name and cast to Field sub-class.
* @param fieldName Member field name. May not contain '.'
* @return Field pointer (never NULL)
* @throws std::runtime_error If no member by this name, or member exists, but has type other than FT.
*/
template<typename FT>
std::tr1::shared_ptr<const FT> getFieldT(std::string const &fieldName) const
{
STATIC_ASSERT(FT::isField); // only allow cast from Field sub-class
std::tr1::shared_ptr<const FT> result(
std::tr1::dynamic_pointer_cast<const FT>(getFieldT(fieldName))
);
if (!result)
throw std::runtime_error("Wrong Field type");
return result;
}
/** Lookup Field by index, within this Structure.
* @param index Index of member in this structure. @code index>=0 && index<getNumberFields() @endcode
* @return Field pointer (never NULL)
* @throws std::out_of_range If index >= getNumberFields()
*/
const FieldConstPtr& getField(std::size_t index) const {return fields.at(index);}
/** Lookup Field by index, within this Structure.
* @param index Index of member in this structure. @code index>=0 && index<getNumberFields() @endcode
* @return NULL if member is not a sub-class of FT
* @throws std::out_of_range If index >= getNumberFields()
*/
template<typename FT>
std::tr1::shared_ptr<const FT> getField(std::size_t index) const
{
const FieldConstPtr& field(getField(index));
if (field)
return std::tr1::dynamic_pointer_cast<const FT>(field);
else
return std::tr1::shared_ptr<const FT>();
STATIC_ASSERT(FT::isField); // only allow cast from Field sub-class
return std::tr1::dynamic_pointer_cast<const FT>(getField(index));
}
/** Lookup Field by index, within this Structure.
* @param index Index of member in this structure. @code index>=0 && index<getNumberFields() @endcode
* @return Field pointer (never NULL)
* @throws std::out_of_range If index >= getNumberFields()
*/
FieldConstPtr getFieldT(std::size_t index) const {return fields.at(index);}
/** Lookup Field by index, within this Structure.
* @param index Index of member in this structure. @code index>=0 && index<getNumberFields() @endcode
* @return Field pointer (never NULL)
* @throws std::out_of_range If index >= getNumberFields()
* @throws std::runtime_error If member is not a sub-class of FT
*/
template<typename FT>
std::tr1::shared_ptr<const FT> getFieldT(std::size_t index) const
{
STATIC_ASSERT(FT::isField); // only allow cast from Field sub-class
std::tr1::shared_ptr<const FT> result(
std::tr1::dynamic_pointer_cast<const FT>(getFieldT(index))
);
if (!result)
throw std::runtime_error("Wrong Field type");
return result;
}
/**
@@ -731,7 +831,11 @@ public:
virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const OVERRIDE FINAL;
virtual void deserialize(ByteBuffer *buffer, DeserializableControl *control) OVERRIDE FINAL;
//! Allocate a new instance
//! @version Added after 7.0.0
std::tr1::shared_ptr<PVStructure> build() const;
protected:
Structure(StringArray const & fieldNames, FieldConstPtrArray const & fields, std::string const & id = defaultId());
private:
@@ -739,10 +843,12 @@ private:
FieldConstPtrArray fields;
std::string id;
FieldConstPtr getFieldImpl(const std::string& fieldName, bool throws) const;
void dumpFields(std::ostream& o) const;
friend class FieldCreate;
friend class Union;
EPICS_NOT_COPYABLE(Structure)
};
/**
@@ -784,40 +890,96 @@ public:
* @return The number of fields.
*/
std::size_t getNumberFields() const {return fieldNames.size();}
/**
* Get the field for the specified fieldName.
* @param fieldName The name of the field to get;
* @return The introspection interface.
* This will hold a null pointer if the field is not in the union.
* Lookup Field by name
* @param fieldName Member field name. May not contain '.'
* @return NULL if no member by this name.
*/
FieldConstPtr getField(std::string const &fieldName) const;
/** Lookup Field by name and cast to Field sub-class.
* @param fieldName Member field name. May not contain '.'
* @return NULL If no member by this name, or member exists, but has type other than FT.
*/
template<typename FT>
std::tr1::shared_ptr<const FT> getField(std::string const &fieldName) const
{
FieldConstPtr field = getField(fieldName);
if (field)
return std::tr1::dynamic_pointer_cast<const FT>(field);
else
return std::tr1::shared_ptr<const FT>();
STATIC_ASSERT(FT::isField); // only allow cast from Field sub-class
return std::tr1::dynamic_pointer_cast<const FT>(getField(fieldName));
}
/**
* Get the field for the specified fieldName.
* @param index The index of the field to get;
* @return The introspection interface.
* This will hold a null pointer if the field is not in the union.
* Lookup Field by name
* @param fieldName Member field name. May not contain '.'
* @return Field pointer (never NULL)
* @throws std::runtime_error If no member by this name
*/
FieldConstPtr getFieldT(std::string const &fieldName) const {return getFieldImpl(fieldName, true);};
/** Lookup Field by name and cast to Field sub-class.
* @param fieldName Member field name. May not contain '.'
* @return Field pointer (never NULL)
* @throws std::runtime_error If no member by this name, or member exists, but has type other than FT.
*/
template<typename FT>
std::tr1::shared_ptr<const FT> getFieldT(std::string const &fieldName) const
{
STATIC_ASSERT(FT::isField); // only allow cast from Field sub-class
std::tr1::shared_ptr<const FT> result(
std::tr1::dynamic_pointer_cast<const FT>(getFieldT(fieldName))
);
if (!result)
throw std::runtime_error("Wrong Field type");
return result;
}
/** Lookup Field by index, within this Union.
* @param index Index of member in this union. @code index>=0 && index<getNumberFields() @endcode
* @return Field pointer (never NULL)
* @throws std::out_of_range If index >= getNumberFields()
*/
FieldConstPtr getField(std::size_t index) const {return fields.at(index);}
/** Lookup Field by index, within this Union.
* @param index Index of member in this union. @code index>=0 && index<getNumberFields() @endcode
* @return NULL if member is not a sub-class of FT
* @throws std::out_of_range If index >= getNumberFields()
*/
template<typename FT>
std::tr1::shared_ptr<const FT> getField(std::size_t index) const
{
FieldConstPtr field = getField(index);
if (field)
return std::tr1::dynamic_pointer_cast<const FT>(field);
else
return std::tr1::shared_ptr<const FT>();
STATIC_ASSERT(FT::isField); // only allow cast from Field sub-class
return std::tr1::dynamic_pointer_cast<const FT>(getField(index));
}
/** Lookup Field by index, within this Union.
* @param index Index of member in this union. @code index>=0 && index<getNumberFields() @endcode
* @return Field pointer (never NULL)
* @throws std::out_of_range If index >= getNumberFields()
*/
FieldConstPtr getFieldT(std::size_t index) const {return fields.at(index);}
/** Lookup Field by index, within this Structure.
* @param index Index of member in this structure. @code index>=0 && index<getNumberFields() @endcode
* @return Field pointer (never NULL)
* @throws std::out_of_range If index >= getNumberFields()
* @throws std::runtime_error If member is not a sub-class of FT
*/
template<typename FT>
std::tr1::shared_ptr<const FT> getFieldT(std::size_t index) const
{
STATIC_ASSERT(FT::isField); // only allow cast from Field sub-class
std::tr1::shared_ptr<const FT> result(
std::tr1::dynamic_pointer_cast<const FT>(getFieldT(index))
);
if (!result)
throw std::runtime_error("Wrong Field type");
return result;
}
/**
@@ -865,6 +1027,10 @@ public:
virtual void serialize(ByteBuffer *buffer, SerializableControl *control) const OVERRIDE FINAL;
virtual void deserialize(ByteBuffer *buffer, DeserializableControl *control) OVERRIDE FINAL;
//! Allocate a new instance
//! @version Added after 7.0.0
std::tr1::shared_ptr<PVUnion> build() const;
protected:
Union();
@@ -873,11 +1039,13 @@ private:
StringArray fieldNames;
FieldConstPtrArray fields;
std::string id;
FieldConstPtr getFieldImpl(const std::string& fieldName, bool throws) const;
void dumpFields(std::ostream& o) const;
friend class FieldCreate;
friend class Structure;
EPICS_NOT_COPYABLE(Union)
};
class FieldCreate;
@@ -897,12 +1065,18 @@ class epicsShareClass FieldBuilder :
public std::tr1::enable_shared_from_this<FieldBuilder>
{
public:
/**
* Set ID of an object to be created.
* @param id id to be set.
//! Create a new instance of in-line @c Field builder.
//! @version Added after 7.0.0
static FieldBuilderPtr begin();
//! Create a new instance of in-line @c Field builder pre-initialized with and existing Structure
static FieldBuilderPtr begin(StructureConstPtr S);
/**
* Set ID of an object to be created.
* @param id id to be set.
* @return this instance of a @c FieldBuilder.
*/
FieldBuilderPtr setId(std::string const & id);
*/
FieldBuilderPtr setId(std::string const & id);
/**
* Add a @c Scalar.
@@ -918,7 +1092,7 @@ public:
* @param maxLength a string maximum length.
* @return this instance of a @c FieldBuilder.
*/
FieldBuilderPtr addBoundedString(std::string const & name, std::size_t maxLength);
FieldBuilderPtr addBoundedString(std::string const & name, std::size_t maxLength) PVD_DEPRECATED_52;
/**
* Add a @c Field (e.g. @c Structure, @c Union).
@@ -943,7 +1117,7 @@ public:
* @param size Array fixed size.
* @return this instance of a @c FieldBuilder.
*/
FieldBuilderPtr addFixedArray(std::string const & name, ScalarType scalarType, std::size_t size);
FieldBuilderPtr addFixedArray(std::string const & name, ScalarType scalarType, std::size_t size) PVD_DEPRECATED_52;
/**
* Add bounded-size array of @c Scalar elements.
@@ -952,7 +1126,7 @@ public:
* @param bound Array maximum capacity (size).
* @return this instance of a @c FieldBuilder.
*/
FieldBuilderPtr addBoundedArray(std::string const & name, ScalarType scalarType, std::size_t bound);
FieldBuilderPtr addBoundedArray(std::string const & name, ScalarType scalarType, std::size_t bound) PVD_DEPRECATED_52;
/**
* Add array of @c Field elements.
@@ -1032,20 +1206,20 @@ private:
FieldBuilder(const FieldBuilderPtr & _parentBuilder, const std::string& name, const Union*);
FieldBuilder(const FieldBuilderPtr & _parentBuilder, const std::string& name, const UnionArray*);
FieldBuilder(FieldBuilderPtr const & parentBuilder,
std::string const & nestedName,
Type nestedClassToBuild, bool nestedArray);
std::string const & nestedName,
Type nestedClassToBuild, bool nestedArray);
const Field *findField(const std::string& name, Type ftype);
void reset();
FieldConstPtr createFieldInternal(Type type);
void reset();
FieldConstPtr createFieldInternal(Type type);
friend class FieldCreate;
const FieldCreatePtr fieldCreate;
std::string id;
bool idSet;
std::string id;
bool idSet;
StringArray fieldNames;
FieldConstPtrArray fields;
@@ -1069,11 +1243,11 @@ class epicsShareClass FieldCreate {
friend struct detail::field_factory;
public:
static const FieldCreatePtr &getFieldCreate();
/**
* Create a new instance of in-line @c Field builder.
* @return a new instance of a @c FieldBuilder.
*/
FieldBuilderPtr createFieldBuilder() const;
/**
* Create a new instance of in-line @c Field builder.
* @return a new instance of a @c FieldBuilder.
*/
FieldBuilderPtr createFieldBuilder() const;
/**
* Create a new instance of in-line @c Field builder pre-initialized with and existing Structure
* @return a new instance of a @c FieldBuilder.
@@ -1092,7 +1266,7 @@ public:
* @return a @c BoundedString interface for the newly created object.
* @throws IllegalArgumentException if maxLength == 0.
*/
BoundedStringConstPtr createBoundedString(std::size_t maxLength) const;
BoundedStringConstPtr createBoundedString(std::size_t maxLength) const PVD_DEPRECATED_52;
/**
* Create an @c Array field, variable size array.
* @param elementType The @c ScalarType for array elements
@@ -1105,14 +1279,14 @@ public:
* @param size Fixed array size.
* @return An @c Array Interface for the newly created object.
*/
ScalarArrayConstPtr createFixedScalarArray(ScalarType elementType, std::size_t size) const;
ScalarArrayConstPtr createFixedScalarArray(ScalarType elementType, std::size_t size) const PVD_DEPRECATED_52;
/**
* Create an @c Array field, bounded size array.
* @param elementType The @c ScalarType for array elements
* @param bound Array maximum capacity.
* @return An @c Array Interface for the newly created object.
*/
ScalarArrayConstPtr createBoundedScalarArray(ScalarType elementType, std::size_t bound) const;
ScalarArrayConstPtr createBoundedScalarArray(ScalarType elementType, std::size_t bound) const PVD_DEPRECATED_52;
/**
* Create an @c Array field that is has element type @c Structure
* @param structure The @c Structure for each array element.
@@ -1141,7 +1315,7 @@ public:
* @return a @c Structure interface for the newly created object.
*/
StructureConstPtr createStructure (
std::string const & id,
std::string const & id,
StringArray const & fieldNames,
FieldConstPtrArray const & fields) const;
/**
@@ -1177,7 +1351,7 @@ public:
* @return a @c Union interface for the newly created object.
*/
UnionConstPtr createUnion (
std::string const & id,
std::string const & id,
StringArray const & fieldNames,
FieldConstPtrArray const & fields) const;
/**
@@ -1211,11 +1385,20 @@ public:
private:
FieldCreate();
// const after ctor
std::vector<ScalarConstPtr> scalars;
std::vector<ScalarArrayConstPtr> scalarArrays;
UnionConstPtr variantUnion;
UnionArrayConstPtr variantUnionArray;
mutable Mutex mutex;
typedef std::multimap<unsigned int, Field*> cache_t;
mutable cache_t cache;
struct Helper;
friend class Field;
EPICS_NOT_COPYABLE(FieldCreate)
};
/**
@@ -1267,32 +1450,35 @@ OP(pvDouble, double)
OP(pvString, std::string)
#undef OP
bool epicsShareExtern operator==(const Field&, const Field&);
bool epicsShareExtern operator==(const Scalar&, const Scalar&);
bool epicsShareExtern operator==(const ScalarArray&, const ScalarArray&);
bool epicsShareExtern operator==(const Structure&, const Structure&);
bool epicsShareExtern operator==(const StructureArray&, const StructureArray&);
bool epicsShareExtern operator==(const Union&, const Union&);
bool epicsShareExtern operator==(const UnionArray&, const UnionArray&);
bool epicsShareExtern operator==(const BoundedString&, const BoundedString&);
bool epicsShareExtern compare(const Field&, const Field&);
bool epicsShareExtern compare(const Scalar&, const Scalar&);
bool epicsShareExtern compare(const ScalarArray&, const ScalarArray&);
bool epicsShareExtern compare(const Structure&, const Structure&);
bool epicsShareExtern compare(const StructureArray&, const StructureArray&);
bool epicsShareExtern compare(const Union&, const Union&);
bool epicsShareExtern compare(const UnionArray&, const UnionArray&);
bool epicsShareExtern compare(const BoundedString&, const BoundedString&);
static inline bool operator!=(const Field& a, const Field& b)
{return !(a==b);}
static inline bool operator!=(const Scalar& a, const Scalar& b)
{return !(a==b);}
static inline bool operator!=(const ScalarArray& a, const ScalarArray& b)
{return !(a==b);}
static inline bool operator!=(const Structure& a, const Structure& b)
{return !(a==b);}
static inline bool operator!=(const StructureArray& a, const StructureArray& b)
{return !(a==b);}
static inline bool operator!=(const Union& a, const Union& b)
{return !(a==b);}
static inline bool operator!=(const UnionArray& a, const UnionArray& b)
{return !(a==b);}
static inline bool operator!=(const BoundedString& a, const BoundedString& b)
{return !(a==b);}
/** Equality with other Field
*
* The creation process of class FieldCreate ensures that identical field definitions
* will share the same instance. So pointer equality is sufficient to show defintion
* equality. If in doubt, compare() will do an full test.
*/
#define MAKE_COMPARE(CLASS) \
static FORCE_INLINE bool operator==(const CLASS& a, const CLASS& b) {return (void*)&a==(void*)&b;} \
static FORCE_INLINE bool operator!=(const CLASS& a, const CLASS& b) {return !(a==b);}
MAKE_COMPARE(Field)
MAKE_COMPARE(Scalar)
MAKE_COMPARE(ScalarArray)
MAKE_COMPARE(Structure)
MAKE_COMPARE(StructureArray)
MAKE_COMPARE(Union)
MAKE_COMPARE(UnionArray)
MAKE_COMPARE(BoundedString)
#undef MAKE_COMPARE
}}
/**

View File

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

View File

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

View File

@@ -74,7 +74,7 @@ struct ValueBuilder::child_scalar_array : public ValueBuilder::child
{
builder->addArray(name, array.original_type());
}
virtual void store(const PVFieldPtr& val)
virtual void store(const PVFieldPtr& val) OVERRIDE FINAL
{
if(val->getField()->getType()!=scalarArray)
THROW_EXCEPTION2(std::logic_error, "Scalar Array type mis-match");
@@ -285,7 +285,7 @@ PVStructure::shared_pointer ValueBuilder::buildPVStructure() const
type = tbuild->createStructure();
}
PVStructure::shared_pointer root(getPVDataCreate()->createPVStructure(type));
PVStructure::shared_pointer root(type->build());
child_struct::storeStruct(*this, root);

View File

@@ -72,7 +72,7 @@ static bool checkBitSetPVField(
bool BitSetUtil::compress(BitSetPtr const &bitSet,PVStructurePtr const &pvStructure)
{
return checkBitSetPVField(pvStructure,bitSet,0);
return checkBitSetPVField(pvStructure,bitSet,0);
}
}}

View File

@@ -12,6 +12,8 @@ include $(PVDATA_TEST)/pv/Makefile
include $(PVDATA_TEST)/property/Makefile
include $(PVDATA_TEST)/copy/Makefile
PROD_SRCS_RTEMS += rtemsTestData.c
# pvDataAllTests runs all the test programs in a known working order.
testHarness_SRCS += pvDataAllTests.c
@@ -30,6 +32,11 @@ TESTSPEC_RTEMS = pvdTestHarness.$(MUNCH_SUFFIX); pvDataAllTests
# Build test scripts for hosts
TESTSCRIPTS_HOST += $(TESTS:%=%.t)
ifneq ($(filter $(T_A),$(CROSS_COMPILER_RUNTEST_ARCHS)),)
TESTPROD = $(TESTPROD_HOST)
TESTSCRIPTS += $(TESTS:%=%.t)
endif
include $(TOP)/configure/RULES

View File

@@ -6,8 +6,3 @@ TESTPROD_HOST += testCreateRequest
testCreateRequest_SRCS = testCreateRequest.cpp
testHarness_SRCS += testCreateRequest.cpp
TESTS += testCreateRequest
TESTPROD_HOST += testPVCopy
testPVCopy_SRCS += testPVCopy.cpp
testHarness_SRCS += testPVCopy.cpp
TESTS += testPVCopy

View File

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

View File

@@ -1,295 +0,0 @@
/*testPVCopyMain.cpp */
/*
* Copyright information and license terms for this software can be
* found in the file LICENSE that is included with the distribution
*/
/**
* @author mrk
*/
/* Author: Marty Kraimer */
#include <cstddef>
#include <cstdlib>
#include <cstddef>
#include <string>
#include <cstdio>
#include <memory>
#include <iostream>
#include <epicsUnitTest.h>
#include <testMain.h>
#include <pv/standardField.h>
#include <pv/standardPVField.h>
#include <pv/convert.h>
#include <pv/pvCopy.h>
#include <pv/createRequest.h>
using namespace std;
using std::tr1::static_pointer_cast;
using namespace epics::pvData;
static void testPVScalar(
string const & valueNameMaster,
string const & valueNameCopy,
PVStructurePtr const & pvMaster,
PVCopyPtr const & pvCopy)
{
PVStructurePtr pvStructureCopy;
PVScalarPtr pvValueMaster;
PVScalarPtr pvValueCopy;
BitSetPtr bitSet;
ConvertPtr convert = getConvert();
pvValueMaster = pvMaster->getSubField<PVScalar>(valueNameMaster);
convert->fromDouble(pvValueMaster,.04);
StructureConstPtr structure = pvCopy->getStructure();
pvStructureCopy = pvCopy->createPVStructure();
pvValueCopy = pvStructureCopy->getSubField<PVScalar>(valueNameCopy);
bitSet = BitSetPtr(new BitSet(pvStructureCopy->getNumberFields()));
pvCopy->initCopy(pvStructureCopy, bitSet);
convert->fromDouble(pvValueMaster,.06);
testOk1(convert->toDouble(pvValueCopy)==.04);
pvCopy->updateCopySetBitSet(pvStructureCopy,bitSet);
testOk1(convert->toDouble(pvValueCopy)==.06);
testOk1(bitSet->get(pvValueCopy->getFieldOffset()));
pvCopy->getCopyOffset(pvValueMaster);
bitSet->clear();
convert->fromDouble(pvValueMaster,1.0);
bitSet->set(0);
testOk1(convert->toDouble(pvValueCopy)==0.06);
pvCopy->updateCopyFromBitSet(pvStructureCopy,bitSet);
testOk1(convert->toDouble(pvValueCopy)==1.0);
convert->fromDouble(pvValueCopy,2.0);
bitSet->set(0);
testOk1(convert->toDouble(pvValueMaster)==1.0);
pvCopy->updateMaster(pvStructureCopy,bitSet);
testOk1(convert->toDouble(pvValueMaster)==2.0);
}
static void testPVScalarArray(
string const & valueNameMaster,
string const & valueNameCopy,
PVStructurePtr const & pvMaster,
PVCopyPtr const & pvCopy)
{
PVStructurePtr pvStructureCopy;
PVScalarArrayPtr pvValueMaster;
PVScalarArrayPtr pvValueCopy;
BitSetPtr bitSet;
size_t n = 5;
shared_vector<double> values(n);
shared_vector<const double> cvalues;
pvValueMaster = pvMaster->getSubField<PVScalarArray>(valueNameMaster);
for(size_t i=0; i<n; i++) values[i] = i;
const shared_vector<const double> xxx(freeze(values));
pvValueMaster->putFrom(xxx);
StructureConstPtr structure = pvCopy->getStructure();
pvStructureCopy = pvCopy->createPVStructure();
pvValueCopy = pvStructureCopy->getSubField<PVScalarArray>(valueNameCopy);
bitSet = BitSetPtr(new BitSet(pvStructureCopy->getNumberFields()));
pvCopy->initCopy(pvStructureCopy, bitSet);
values.resize(n);
for(size_t i=0; i<n; i++) values[i] = i + .06;
const shared_vector<const double> yyy(freeze(values));
pvValueMaster->putFrom(yyy);
pvValueCopy->getAs(cvalues);
testOk1(cvalues[0]==0.0);
pvCopy->updateCopySetBitSet(pvStructureCopy,bitSet);
pvValueCopy->getAs(cvalues);
testOk1(cvalues[0]==0.06);
pvCopy->getCopyOffset(pvValueMaster);
bitSet->clear();
values.resize(n);
for(size_t i=0; i<n; i++) values[i] = i + 1.0;
const shared_vector<const double> zzz(freeze(values));
pvValueMaster->putFrom(zzz);
bitSet->set(0);
pvValueCopy->getAs(cvalues);
testOk1(cvalues[0]==0.06);
pvCopy->updateCopyFromBitSet(pvStructureCopy,bitSet);
pvValueCopy->getAs(cvalues);
testOk1(cvalues[0]==1.0);
values.resize(n);
for(size_t i=0; i<n; i++) values[i] = i + 2.0;
const shared_vector<const double> ttt(freeze(values));
pvValueMaster->putFrom(ttt);
bitSet->set(0);
pvValueMaster->getAs(cvalues);
testOk1(cvalues[0]==2.0);
pvCopy->updateMaster(pvStructureCopy,bitSet);
pvValueMaster->getAs(cvalues);
testOk1(cvalues[0]==1.0);
}
static void scalarTest()
{
testDiag("scalarTest()");
PVStructurePtr pvMaster;
string request;
PVStructurePtr pvRequest;
PVFieldPtr pvMasterField;
PVCopyPtr pvCopy;
string valueNameMaster;
string valueNameCopy;
StandardPVFieldPtr standardPVField = getStandardPVField();
pvMaster = standardPVField->scalar(pvDouble,"alarm,timeStamp,display");
valueNameMaster = request = "value";
CreateRequest::shared_pointer createRequest = CreateRequest::create();
pvRequest = createRequest->createRequest(request);
pvCopy = PVCopy::create(pvMaster,pvRequest,"");
valueNameCopy = "value";
testPVScalar(valueNameMaster,valueNameCopy,pvMaster,pvCopy);
request = "";
valueNameMaster = "value";
pvRequest = createRequest->createRequest(request);
pvCopy = PVCopy::create(pvMaster,pvRequest,"");
valueNameCopy = "value";
testPVScalar(valueNameMaster,valueNameCopy,pvMaster,pvCopy);
request = "alarm,timeStamp,value";
valueNameMaster = "value";
pvRequest = createRequest->createRequest(request);
pvCopy = PVCopy::create(pvMaster,pvRequest,"");
valueNameCopy = "value";
testPVScalar(valueNameMaster,valueNameCopy,pvMaster,pvCopy);
}
static void arrayTest()
{
testDiag("arrayTest");
PVStructurePtr pvMaster;
string request;
PVStructurePtr pvRequest;
PVFieldPtr pvMasterField;
PVCopyPtr pvCopy;
string valueNameMaster;
string valueNameCopy;
CreateRequest::shared_pointer createRequest = CreateRequest::create();
StandardPVFieldPtr standardPVField = getStandardPVField();
pvMaster = standardPVField->scalarArray(pvDouble,"alarm,timeStamp");
valueNameMaster = request = "value";
pvRequest = createRequest->createRequest(request);
pvCopy = PVCopy::create(pvMaster,pvRequest,"");
valueNameCopy = "value";
testPVScalarArray(valueNameMaster,valueNameCopy,pvMaster,pvCopy);
request = "";
valueNameMaster = "value";
pvRequest = createRequest->createRequest(request);
pvCopy = PVCopy::create(pvMaster,pvRequest,"");
valueNameCopy = "value";
testPVScalarArray(valueNameMaster,valueNameCopy,pvMaster,pvCopy);
request = "alarm,timeStamp,value";
valueNameMaster = "value";
pvRequest = createRequest->createRequest(request);
pvCopy = PVCopy::create(pvMaster,pvRequest,"");
valueNameCopy = "value";
testPVScalarArray(valueNameMaster,valueNameCopy,pvMaster,pvCopy);
}
static PVStructurePtr createPowerSupply()
{
FieldCreatePtr fieldCreate = getFieldCreate();
StandardFieldPtr standardField = getStandardField();
PVDataCreatePtr pvDataCreate = getPVDataCreate();
size_t nfields = 5;
StringArray names;
names.reserve(nfields);
FieldConstPtrArray powerSupply;
powerSupply.reserve(nfields);
names.push_back("alarm");
powerSupply.push_back(standardField->alarm());
names.push_back("timeStamp");
powerSupply.push_back(standardField->timeStamp());
string properties("alarm,display");
names.push_back("voltage");
powerSupply.push_back(standardField->scalar(pvDouble,properties));
names.push_back("power");
powerSupply.push_back(standardField->scalar(pvDouble,properties));
names.push_back("current");
powerSupply.push_back(standardField->scalar(pvDouble,properties));
return pvDataCreate->createPVStructure(
fieldCreate->createStructure(names,powerSupply));
}
static void powerSupplyTest()
{
testDiag("powerSupplyTest");
PVStructurePtr pvMaster;
string request;
PVStructurePtr pvRequest;
PVFieldPtr pvMasterField;
PVCopyPtr pvCopy;
string builder;
string valueNameMaster;
string valueNameCopy;
CreateRequest::shared_pointer createRequest = CreateRequest::create();
pvMaster = createPowerSupply();
valueNameMaster = request = "power.value";
pvRequest = createRequest->createRequest(request);
pvCopy = PVCopy::create(pvMaster,pvRequest,"");
valueNameCopy = "power.value";
testPVScalar(valueNameMaster,valueNameCopy,pvMaster,pvCopy);
request = "";
valueNameMaster = "power.value";
pvRequest = createRequest->createRequest(request);
pvCopy = PVCopy::create(pvMaster,pvRequest,"");
valueNameCopy = "power.value";
testPVScalar(valueNameMaster,valueNameCopy,pvMaster,pvCopy);
request = "alarm,timeStamp,voltage.value,power.value,current.value";
valueNameMaster = "power.value";
pvRequest = createRequest->createRequest(request);
pvCopy = PVCopy::create(pvMaster,pvRequest,"");
valueNameCopy = "power.value";
testPVScalar(valueNameMaster,valueNameCopy,pvMaster,pvCopy);
request = "alarm,timeStamp,voltage{value,alarm},power{value,alarm,display},current.value";
valueNameMaster = "power.value";
pvRequest = createRequest->createRequest(request);
pvCopy = PVCopy::create(pvMaster,pvRequest,"");
valueNameCopy = "power.value";
testPVScalar(valueNameMaster,valueNameCopy,pvMaster,pvCopy);
}
MAIN(testPVCopy)
{
testPlan(67);
scalarTest();
arrayTest();
powerSupplyTest();
return testDone();
}

View File

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

View File

@@ -31,16 +31,6 @@ void Unroller::unroll<0>(double /*d*/) {
THROW_BASE_EXCEPTION("the root cause");
}
void internalTestBaseException(int /*unused*/ = 0)
{
try {
// NOTE: 5, 4, 3, 2, 1 calls will be optimized and not shown
Unroller().unroll<5>(42.0);
} catch (BaseException& be3) {
THROW_BASE_EXCEPTION_CAUSE("exception 1", be3);
}
}
void testBaseExceptionTest() {
printf("testBaseException... ");
@@ -50,16 +40,6 @@ void testBaseExceptionTest() {
printf("\n\n%s\n\n", be.what());
}
try {
try {
internalTestBaseException();
} catch (BaseException& be2) {
THROW_BASE_EXCEPTION_CAUSE("exception 2", be2);
}
} catch (BaseException& be) {
printf("\n\n%s\n\n", be.what());
}
testPass("testBaseException");
}
@@ -88,4 +68,3 @@ MAIN(testBaseException)
testBaseExceptionTest();
return testDone();
}

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