ATLAS Offline Software
Loading...
Searching...
No Matches
MixingEventSelector.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4
6ATLAS_NO_CHECK_FILE_THREAD_SAFETY; // non-MT EventSelector
7
11#include "EventInfo/EventInfo.h"
12#include "EventInfo/EventID.h"
13#include "EventInfo/EventType.h"
18#include "SGTools/DataProxy.h"
20
21#include "GaudiKernel/GaudiException.h"
22#include "GaudiKernel/TypeNameString.h"
23#include "GaudiKernel/MsgStream.h"
24#include "GaudiKernel/GenericAddress.h"
25#include "CLHEP/Random/RandFlat.h"
27#include <charconv>
28#include <algorithm>
29#include <cassert>
30#include <functional>
31#include <iostream>
32#include <fstream>
33#include <stdexcept>
34#include <sstream>
35#include <string>
36
37using namespace std;
38using SG::DataProxy;
39
40#ifdef DEBUG_OUTPUT_STATUS
41ofstream outfile2("status.txt");
42#endif
43
44MixingEventSelector::MixingEventSelector(const string& name, ISvcLocator* svc) :
45 base_class(name,svc),
47 m_pEventStore( "StoreGateSvc", name )
48{
49}
50
53
54StatusCode
56 ATH_MSG_INFO ("Initializing " << name());
57
58// defer this (it triggers a init loop via PPS
59// if (!m_pEventStore.retrieve().isSuccess())
60// return StatusCode::FAILURE;
61
62 //setup random stream
63 CLHEP::HepRandomEngine* collEng(m_atRndmSvc->GetEngine(m_randomStreamName.value()));
64 if(nullptr == collEng ) {
65 ATH_MSG_ERROR ("can not get random stream " << m_randomStreamName.value());
66 return StatusCode::FAILURE;
67 }
68 //flat distribution in [0,1] range
69 m_chooseRangeRand = std::make_unique<CLHEP::RandFlat>(*(collEng), 0.0, 1.0);
70
71 ATH_CHECK( m_helperTools.retrieve() );
72 for (ToolHandle<IAthenaSelectorTool>& tool : m_helperTools) {
73 ATH_CHECK( tool->postInitialize() );
74 }
75
76 return StatusCode::SUCCESS;
77}
78
79StatusCode
81 ATH_MSG_DEBUG ("Finalizing " << name());
82
83 for (ToolHandle<IAthenaSelectorTool>& tool : m_helperTools) {
84 tool->preFinalize().ignore();
85 }
86
87 const std::string& fname(m_statusFileName.value());
88 ofstream outfile(fname.c_str());
89 if ( outfile ) {
92 while (i != iEnd) outfile << (*i++).toString();
93 } else if (!fname.empty()) {
94 ATH_MSG_WARNING("unable to open trigger list status file " << fname);
95 }
96
97 return StatusCode::SUCCESS;
98}
99
100
101void
102MixingEventSelector::setUpTriggerList(Gaudi::Details::PropertyBase&) {
103 using std::placeholders::_1;
105 for_each(m_triggerListProp.value().begin(), m_triggerListProp.value().end(),
106 bind(&MixingEventSelector::decodeTrigger, this, _1));
108}
109
110void
111MixingEventSelector::decodeTrigger(const std::string & triggDescr) {
112 //all this would be much more readable without error reporting...
113 const auto tokens = CxxUtils::tokenize(triggDescr, " :");
114 //we need exactly three tokens: selector name, first and last event 2 B read
115 if (tokens.size() == 3) {
116 auto iToken = tokens.begin();
117 Gaudi::Utils::TypeNameString selTN(*iToken++);
118 //get selector
119 SmartIF<IEvtSelector> pSelector(serviceLocator()->service(selTN));
120 if (pSelector) {
121 //FIXME if (!pSelector.done()) {
122 //try to add to trig list
123 unsigned int firstEvt{};
124 unsigned int lastEvt{};
125 auto string1 = *iToken++;
126 auto string2 = *iToken;
127 auto [ptr1, ec1] = std::from_chars(string1.data(), string1.data() + string1.size(), firstEvt);
128 auto [ptr2, ec2] = std::from_chars(string2.data(), string2.data() + string2.size(), lastEvt);
129 if ( ec1 != std::errc() || ec2 != std::errc() ) {
130 ATH_MSG_ERROR("decodeTrigger: Can't cast ["<< string1 << " " << string2 << "] to double(frequency). SKIPPING");
131 } else {
132 if (m_trigList.add(Trigger(pSelector, firstEvt, lastEvt))) {
133 if (msgLvl(MSG::DEBUG)) {
134 SmartIF<INamedInterface> pNamed(pSelector);
135 if (pNamed) {
136 msg() << "decodeTrigger: added selector " << pNamed->name()
137 << " first event to be read " << firstEvt
138 << " last event to be read " << lastEvt << endmsg;
139 }
140 }
141 } else {
143 ("decodeTrigger: Selector ["
144 << selTN.type() << '/' << selTN.name()
145 << "] not added");
146 } //can add to range
147 }
148 } else {
150 ("decodeTrigger: Selector ["
151 << selTN.type() << '/' << selTN.name()
152 << "] can not be found or created");
153 } //selector available
154 } else {
156 ("decodeTrigger: Badly formatted descriptor ["
157 << triggDescr << "]. SKIPPING");
158 } //can parse property string
159}
160
161#ifndef NDEBUG
162#define FORWARD___DEBUG( METHOD )\
163 { ATH_MSG_VERBOSE ("forwarding " << #METHOD << " to service "\
164 << (validTrigger() ? currentTrigger()->name() : "NONE")); }
165#else
166#define FORWARD___DEBUG( METHOD )
167#endif
168
169StatusCode MixingEventSelector::createContext(IEvtSelector::Context*& pctxt) const {
170 pctxt=nullptr;
171 if (!validTrigger()) return StatusCode::FAILURE;
173 StatusCode sc(StatusCode::FAILURE);
174 while (iTr != m_trigList.end() && (sc=(iTr++)->createContext(pctxt)).isSuccess()) ;
175 return sc;
176}
177
178StatusCode
179MixingEventSelector::next(IEvtSelector::Context& /*c*/) const {
180 StatusCode sc(StatusCode::FAILURE);
181 do {
182#ifdef DEBUG_OUTPUT_STATUS
185 while (i != iEnd) outfile2 << (*i++).toString(); //output status
186#endif
188 sc = currentTrigger()->next();
189 if (sc.isFailure()){
191 continue; // we have to add "continue;" after "m_trigList.remove(m_pCurrentTrigger);
192 }
193 } while ((sc.isFailure() || currentTrigger()->done()) && !m_trigList.empty() );
194 // use !m_trigList.empty() to replace validTrigger(), which is not working well
195
196 std::vector<ToolHandle<IAthenaSelectorTool> >::const_iterator
197 i(m_helperTools.begin()), iE(m_helperTools.end());
198 while (i != iE) ((*i++)->preNext()).ignore();
199
200 ++m_eventPos;
202
203 i=m_helperTools.begin(); iE=m_helperTools.end();
204 while (sc.isSuccess() && (i != iE)) {
205 sc =(*i)->postNext();
206 if (sc.isRecoverable())
207 ATH_MSG_INFO("Request skipping event from: " << (*i)->name());
208 else if (sc.isFailure())
209 ATH_MSG_WARNING((*i)->name() << ":postNext failed");
210 ++i;
211 }
212 return sc;
213}
214
215
216StatusCode
217MixingEventSelector::createAddress(const IEvtSelector::Context& /*c*/,
218 IOpaqueAddress*& pAddr) const {
219 pAddr=nullptr;
220 if (!validTrigger()) return StatusCode::FAILURE;
221
223 return (currentTrigger()->createAddress(pAddr).isSuccess() &&
224 pAddr != nullptr) ? StatusCode::SUCCESS : StatusCode::FAILURE;
225}
226
227StatusCode
229 IAddressProvider::tadList& /*tads*/ ) {
230 return StatusCode::SUCCESS;
231}
232
233StatusCode
235 IAddressProvider::tadList& /*tads*/ )
236{
237 if (storeID != StoreID::EVENT_STORE && storeID != StoreID::PILEUP_STORE)
238 return StatusCode::SUCCESS;
239
241 if (!m_pEventStore->contains (mclid, m_mergedEventInfoKey)) {
242 // We create the DataProxy here rather than relying on ProxyProvideSvc
243 // to do it because we want to set a non-default dataloader on the proxy.
244 SG::TransientAddress tad (mclid,
246 new GenericAddress (0, mclid),
247 false);
248 auto dp = std::make_unique<SG::DataProxy> (std::move(tad),
249 this,
250 true);
251 ATH_CHECK( m_pEventStore->addToStore (mclid, dp.release()) );
252 }
253 return StatusCode::SUCCESS;
254}
255
256StatusCode
258 const EventContext&) {
259 return StatusCode::FAILURE;
260}
261
262#undef FORWARD___DEBUG
263
264unsigned long MixingEventSelector::getEventNo() const {
265 return ( (m_eventNumbers.value().size() > m_eventPos) ?
266 m_eventNumbers.value()[m_eventPos] :
267 m_eventPos );
268}
269
270//remember that m_pCurrentTrigger and m_trigList are mutable
273 //we look for the first trigger which is not done
274 do {
275 //if called before initialize, choose the last trigger in list
276 unsigned int iTrig = (nullptr != m_chooseRangeRand) ?
277 (unsigned int)(m_chooseRangeRand->fire() * double(m_trigList.todo())) :
278 m_trigList.todo();
279 m_pCurrentTrigger = m_trigList.elementInRange(iTrig);
280 } while (validTrigger() && m_pCurrentTrigger->done() &&
281 m_trigList.remove(m_pCurrentTrigger)); //remove a done trigger
282
283 //printouts
284 if (!validTrigger()) {
285 ATH_MSG_INFO ("setCurrentTrigger: end of input");
286 } else {
287 ATH_MSG_DEBUG ("setCurrentTrigger: now using selector "
288 << currentTrigger()->name());
289 }
290
291 return m_pCurrentTrigger;
292}
293
298
299bool
301 bool newElem(end() == find(begin(), end() , trig));
302 if (newElem) {
303 m_todo += trig.todo();
304 m_trigs.push_back(trig);
305 m_rangeEnd.push_back(m_todo);
306#ifndef NDEBUG
307 MsgStream log(Athena::getMessageSvc(), "MixingEventSelector::TriggerList");
308 if (log.level() <= MSG::DEBUG) {
309 log << MSG::DEBUG << "add: trigger "
310 << trig.name() << ". New trigger table: \n" << toString() <<endmsg;
311 }
312#endif
313 }
314 return newElem;
315}
316
317bool
319 bool removed(false);
320 unsigned int nElem(m_trigs.size());
321 if (nElem) {
322 unsigned int trigTodo(iTrig->todo());
323 unsigned int trigIndex(iTrig - begin());
324#ifndef NDEBUG
325 string trigName(iTrig->name());
326#endif
327 m_trigs.erase(iTrig);
328 removed = nElem > m_trigs.size();
329 if (removed) {
330 //this means we erased iTrig. Remove its todo count from total
331 m_todo -= trigTodo;
332 //and from each of the subsequent ranges
333 for (unsigned int i=trigIndex+1; i<m_rangeEnd.size(); ++i)
334 m_rangeEnd[i] -= trigTodo;
335 //finally erase the corresponding rangeEnd element
336 m_rangeEnd.erase(m_rangeEnd.begin()+trigIndex);
337#ifndef NDEBUG
338 MsgStream log(Athena::getMessageSvc(), "MixingEventSelector::TriggerList"); //FIXME
339 if (log.level() <= MSG::DEBUG) {
340 log << MSG::DEBUG << "remove: trigger "
341 << trigName
342 << ". New trigger table: \n" << toString() <<endmsg;
343 }
344#endif
345 }
346 }
347 return removed;
348}
349
350string
352 ostringstream os;
353 for (unsigned int i=0; i<m_trigs.size(); ++i) {
354 //cant do os << m_trigs[i].toString();
355 os << m_trigs[i].name() << ", already read=" << m_trigs[i].read()
356 << ", to do=" << m_trigs[i].todo()
357 << (m_trigs[i].done() ? " done " : " ")
358 << " - endRange: " << m_rangeEnd[i] << '\n';
359 }
360 os << endl;
361 return string(os.str());
362}
363
366 iterator iTrig(m_trigs.begin()), eTrig(m_trigs.end());
367 std::vector<unsigned int>::const_iterator iRange(m_rangeEnd.begin());
368 std::vector<unsigned int>::const_iterator eRange(m_rangeEnd.end());
369 while (eRange != iRange && eTrig != iTrig && i > *iRange){
370 ++iRange; ++iTrig;
371 }
372#ifndef NDEBUG
373 MsgStream log(Athena::getMessageSvc(), "MixingEventSelector::TriggerList");
374 if (log.level() <= MSG::VERBOSE) {
375 if (eRange != iRange) log << MSG::VERBOSE
376 << "elementInRange: rangeEnd=" << *iRange
377 << " matches i=" << i << endmsg;
378 }
379#endif
380 return iTrig;
381}
382
384 unsigned int firstEvt,
385 unsigned int lastEvt):
386 m_pSelector(pSel), m_firstEvent(firstEvt),
387 m_toRead(lastEvt+1), m_todo(m_toRead-firstEvt), m_reads(0),
388 m_current(nullptr)
389 { }
390
391IEvtSelector::Context& MixingEventSelector::Trigger::currentContext() const {
392 //make sure we have a context!
393 if (nullptr == m_current) {
394 IEvtSelector::Context* pTmp(nullptr);
395 if ((createContext(pTmp)).isFailure())
396 throw GaudiException("MixingEventSelector::Trigger::currentContext(): can't create context",
397 name(),StatusCode::FAILURE);
398 }
399 // cppcheck-suppress nullPointerRedundantCheck; createContext() sets m_current
400 return *m_current;
401}
402
403std::string
405 ostringstream os;
406 os << name() << ", already read=" << m_reads
407 << ", to do=" << todo() << endl;
408 return string(os.str());
409}
410
411StatusCode
413 StatusCode sc(StatusCode::FAILURE);
414 if (!done()) {
415 sc= selector().next(currentContext());
416 while (sc.isSuccess() && (++m_reads < m_firstEvent))
417 sc = selector().next(currentContext());// m_reads, m_current are mutable
418 }
419 // cout << "calling next on Trigger " << this << " " << dynamic_cast<IService&>(selector()).name() << endl;
420 return sc;
421}
422
423StatusCode
425 return selector().previous(currentContext());
426}
427
428StatusCode
430 return (done() ?
431 StatusCode::FAILURE :
433}
434
435StatusCode
436MixingEventSelector::Trigger::createContext(IEvtSelector::Context*& pCtxt) const{
437 StatusCode sc(StatusCode::FAILURE);
438 if (!done()) {
439 sc = selector().createContext(pCtxt);
440 m_current = pCtxt;
441 }
442 return sc;
443}
444
445bool
447 // cout << "calling done on Trigger " << this << " " << dynamic_cast<IService&>(selector()).name() << endl;
448 // cout << refCtxt << " " << selector().end() << endl;
449 // cout << toRead() << " " << m_reads << endl;
450 // cout << "done returns " << ((m_current && (currentContext() == *selector().end())) || (toRead() <= m_reads)) << endl;
451 return (toRead() <= m_reads);
452}
453
454
455StatusCode MixingEventSelector::last(IEvtSelector::Context&) const {
456 return StatusCode::FAILURE;
457}
458StatusCode MixingEventSelector::next(IEvtSelector::Context&, int) const {
459 return StatusCode::FAILURE;
460}
461StatusCode MixingEventSelector::previous(IEvtSelector::Context&) const {
462 return StatusCode::FAILURE;
463}
464StatusCode MixingEventSelector::previous(IEvtSelector::Context&,int) const {
465 return StatusCode::FAILURE;
466}
467StatusCode MixingEventSelector::rewind(IEvtSelector::Context&) const {
468 return StatusCode::FAILURE;
469}
470StatusCode MixingEventSelector::releaseContext(IEvtSelector::Context*&) const {
471 return StatusCode::FAILURE;
472}
473StatusCode MixingEventSelector::resetCriteria(const std::string&,
474 IEvtSelector::Context&)const {
475 return StatusCode::FAILURE;
476}
477
478
479// IConverter implementation.
481{
482 static const CLID cid = ClassID_traits<EventInfo>::ID(); return cid;
483}
485{
486 return 0;
487}
488
489
501StatusCode MixingEventSelector::createObj(IOpaqueAddress* /*pAddress*/,
502 DataObject*& refpObject)
503{
504 const EventInfo* pEInfo(nullptr);
505 if (!(m_pEventStore->retrieve(pEInfo)).isSuccess()) {
507 ("addMergedEventInfo: event store does not contain "\
508 "an EventInfo object!");
509 return StatusCode::RECOVERABLE;
510 }
511
512 // copy the original event to a new MergedEventInfo
513 auto mei = std::make_unique<MergedEventInfo> (*pEInfo,
515 getEventNo());
516 refpObject = SG::asStorable (std::move (mei));
517
518
519 //finally slam a new EventID in place of the old! HACK!!! FIXME!!
520 (const_cast<EventInfo*>(pEInfo))->setEventID(
522
523 return StatusCode::SUCCESS;
524}
525
526
527// IConverter dummies.
528StatusCode MixingEventSelector::setDataProvider(IDataProviderSvc* /*pService*/)
529{
530 std::abort();
531}
532SmartIF<IDataProviderSvc>& MixingEventSelector::dataProvider() const
533{
534 std::abort();
535}
536StatusCode MixingEventSelector::setConversionSvc(IConversionSvc* /*pService*/)
537{
538 std::abort();
539}
540SmartIF<IConversionSvc>& MixingEventSelector::conversionSvc() const
541{
542 std::abort();
543}
544StatusCode MixingEventSelector::setAddressCreator(IAddressCreator* /*creator*/)
545{
546 std::abort();
547}
548SmartIF<IAddressCreator>& MixingEventSelector::addressCreator() const
549{
550 std::abort();
551}
552StatusCode MixingEventSelector::fillObjRefs(IOpaqueAddress* /*pAddress*/, DataObject* /*pObject*/)
553{
554 std::abort();
555}
556StatusCode MixingEventSelector::updateObj(IOpaqueAddress* /*pAddress*/, DataObject* /*refpObject*/)
557{
558 std::abort();
559}
560StatusCode MixingEventSelector::updateObjRefs(IOpaqueAddress* /*pAddress*/, DataObject* /*pObject*/)
561{
562 std::abort();
563}
564StatusCode MixingEventSelector::createRep(DataObject* /*pObject*/, IOpaqueAddress*& /*refpAddress*/)
565{
566 std::abort();
567}
568StatusCode MixingEventSelector::fillRepRefs(IOpaqueAddress* /*pAddress*/, DataObject* /*pObject*/)
569{
570 std::abort();
571}
572StatusCode MixingEventSelector::updateRep(IOpaqueAddress* /*pAddress*/, DataObject* /*pObject*/)
573{
574 std::abort();
575}
576StatusCode MixingEventSelector::updateRepRefs(IOpaqueAddress* /*pAddress*/, DataObject* /*pObject*/)
577{
578 std::abort();
579}
#define endmsg
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_ERROR(x)
#define ATH_MSG_INFO(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
a traits class that associates a CLID to a type T It also detects whether T inherits from Gaudi DataO...
This class provides a unique identification for each event, in terms of run/event number and/or a tim...
This class provides general information about an event.
uint32_t CLID
The Class ID type.
static Double_t sc
This class provides general information about an event.
#define FORWARD___DEBUG(METHOD)
a stream of events read from different selectors.
convert to and from a SG storable
This class contains trigger related information.
Define macros for attributes used to control the static checker.
#define ATLAS_NO_CHECK_FILE_THREAD_SAFETY
This class provides a unique identification for each event, in terms of run/event number and/or a tim...
Definition EventID.h:35
std::list< SG::TransientAddress * > tadList
the list of available selectors with their frequency range
std::vector< Trigger >::iterator iterator
std::vector< unsigned int > m_rangeEnd
std::vector< Trigger >::const_iterator const_iterator
a selector with an associated frequency
StatusCode createContext(IEvtSelector::Context *&pCtxt) const
Trigger(IEvtSelector *pSel, unsigned int firstEvt, unsigned int lastEvt)
const std::string & name() const
StatusCode createAddress(IOpaqueAddress *&) const
IEvtSelector & selector() const
IEvtSelector::Context * m_current
IEvtSelector::Context & currentContext() const
Gaudi::Property< std::string > m_mergedEventInfoKey
virtual StatusCode setConversionSvc(IConversionSvc *pService) override
TriggerList::iterator currentTrigger() const
"intelligent" accessor to the above
Gaudi::Property< std::string > m_statusFileName
virtual StatusCode createContext(IEvtSelector::Context *&refpCtxt) const override
virtual StatusCode initialize() override
TriggerList::iterator setCurrentTrigger() const
choose current trigger at random. Actually modifies state (use mutables)
virtual StatusCode next(IEvtSelector::Context &refCtxt) const override
virtual SmartIF< IDataProviderSvc > & dataProvider() const override
virtual StatusCode createAddress(const IEvtSelector::Context &refCtxt, IOpaqueAddress *&) const override
virtual StatusCode rewind(IEvtSelector::Context &refCtxt) const override
Gaudi::Property< int > m_outputRunNumber
virtual StatusCode previous(IEvtSelector::Context &) const override
Gaudi::Property< std::vector< unsigned long > > m_eventNumbers
unsigned long getEventNo() const
setup and lookup m_evtsNotUsedSoFar. Returns next event no
virtual StatusCode updateObj(IOpaqueAddress *pAddress, DataObject *refpObject) override
virtual StatusCode last(IEvtSelector::Context &) const override
MixingEventSelector(const std::string &name, ISvcLocator *svc)
void decodeTrigger(const std::string &triggDescr)
functor that creates a Trigger object and adds it to m_trigList
Gaudi::Property< std::string > m_randomStreamName
virtual StatusCode createRep(DataObject *pObject, IOpaqueAddress *&refpAddress) override
Gaudi::Property< std::vector< std::string > > m_triggerListProp
virtual StatusCode setAddressCreator(IAddressCreator *creator) override
virtual StatusCode updateObjRefs(IOpaqueAddress *pAddress, DataObject *pObject) override
virtual StatusCode updateAddress(StoreID::type storeID, SG::TransientAddress *tad, const EventContext &ctx) override
virtual StatusCode releaseContext(IEvtSelector::Context *&refCtxt) const override
virtual const CLID & objType() const override
TriggerList m_trigList
the ingredients
virtual StatusCode updateRepRefs(IOpaqueAddress *pAddress, DataObject *pObject) override
virtual StatusCode fillRepRefs(IOpaqueAddress *pAddress, DataObject *pObject) override
virtual StatusCode fillObjRefs(IOpaqueAddress *pAddress, DataObject *pObject) override
virtual StatusCode finalize() override
virtual StatusCode loadAddresses(StoreID::type storeID, IAddressProvider::tadList &tads) override
virtual StatusCode updateRep(IOpaqueAddress *pAddress, DataObject *pObject) override
virtual SmartIF< IAddressCreator > & addressCreator() const override
virtual StatusCode preLoadAddresses(StoreID::type storeID, IAddressProvider::tadList &tads) override
TriggerList::iterator m_pCurrentTrigger
the current trigger
ServiceHandle< StoreGateSvc > m_pEventStore
virtual StatusCode setDataProvider(IDataProviderSvc *pService) override
ServiceHandle< IAtRndmGenSvc > m_atRndmSvc
virtual long repSvcType() const override
unsigned long m_eventPos
the internal event number
void setUpTriggerList(Gaudi::Details::PropertyBase &trigList)
TriggerList property call-back.
std::unique_ptr< CLHEP::RandFlat > m_chooseRangeRand
ToolHandleArray< IAthenaSelectorTool > m_helperTools
virtual StatusCode createObj(IOpaqueAddress *pAddress, DataObject *&refpObject) override
Create the transient representation of an object.
virtual SmartIF< IConversionSvc > & conversionSvc() const override
virtual StatusCode resetCriteria(const std::string &cr, IEvtSelector::Context &c) const override
@ EVENT_STORE
Definition StoreID.h:26
@ PILEUP_STORE
Definition StoreID.h:31
singleton-like access to IMessageSvc via open function and helper
std::string find(const std::string &s)
return a remapped string
Definition hcg.cxx:140
IMessageSvc * getMessageSvc(bool quiet=false)
std::vector< std::string > tokenize(std::string_view the_str, std::string_view delimiters)
Splits the string into smaller substrings.
DataObject * asStorable(SG::DataObjectSharedPtr< T > pObject)
STL namespace.
MsgStream & msg
Definition testRead.cxx:32