libzypp 17.31.32
LookupAttr.cc
Go to the documentation of this file.
1/*---------------------------------------------------------------------\
2| ____ _ __ __ ___ |
3| |__ / \ / / . \ . \ |
4| / / \ V /| _/ _/ |
5| / /__ | | | | | | |
6| /_____||_| |_| |_| |
7| |
8\---------------------------------------------------------------------*/
12#include <iostream>
13#include <sstream>
14
15#include <zypp/base/LogTools.h>
16#include <zypp/base/String.h>
17
18#include <zypp/sat/detail/PoolImpl.h>
19
20#include <zypp/sat/Pool.h>
21#include <zypp/sat/LookupAttr.h>
23
24#include <zypp/CheckSum.h>
25
26using std::endl;
27
29namespace zypp
30{
32 namespace sat
33 {
34
36
38 //
39 // CLASS NAME : LookupAttr::Impl
40 //
42
56 {
57 public:
59 : _parent( SolvAttr::noAttr )
60 {}
62 : _attr( attr_r ), _parent( attr_r.parent() ), _solv( loc_r == REPO_ATTR ? SOLVID_META : noSolvableId )
63 {}
70
71 public:
72 SolvAttr attr() const
73 { return _attr; }
74
76 {
77 _attr = attr_r;
79 if ( p != SolvAttr::noAttr )
80 _parent = p;
81 }
82
83 const StrMatcher & strMatcher() const
84 { return _strMatcher; }
85
87 {
88 matcher_r.compile();
90 }
91
92 public:
93 bool pool() const
94 { return ! (_repo || _solv); }
95
97 {
99 _solv = Solvable( loc_r == REPO_ATTR ? SOLVID_META : noSolvableId );
100 }
101
103 { return _repo; }
104
106 {
107 _repo = repo_r;
108 _solv = Solvable( loc_r == REPO_ATTR ? SOLVID_META : noSolvableId );
109 }
110
112 { return _solv; }
113
119
121 { return _parent; }
122
125
126 public:
128 {
129 if ( _attr == SolvAttr::noAttr || sat::Pool::instance().reposEmpty() )
130 return end();
131
133 if ( _solv )
135 else if ( _repo )
136 whichRepo = _repo.id();
137
139 if ( _parent != SolvAttr::noAttr )
140 ::dataiterator_prepend_keyname( dip.get(), _parent.id() );
141
142 return iterator( dip ); // iterator takes over ownership!
143 }
144
146 { return iterator(); }
147
148 private:
154
155 private:
156 friend Impl * rwcowClone<Impl>( const Impl * rhs );
158 Impl * clone() const
159 { return new Impl( *this ); }
160 };
161
163 //
164 // CLASS NAME : LookupAttr
165 //
167
169 : _pimpl( new Impl )
170 {}
171
178
185
192
193
195
197 { return _pimpl->attr(); }
198
200 { _pimpl->setAttr( attr_r ); }
201
203 { return _pimpl->strMatcher(); }
204
206 { _pimpl->setStrMatcher( matcher_r ); }
207
209
210 bool LookupAttr::pool() const
211 { return _pimpl->pool(); }
212
214 { _pimpl->setPool( loc_r ); }
215
217 { return _pimpl->repo(); }
218
221
223 { return _pimpl->solvable(); }
224
226 { _pimpl->setSolvable( solv_r ); }
227
229 { return _pimpl->parent(); }
230
232 { _pimpl->setParent( attr_r ); }
233
235
237 { return _pimpl->begin(); }
238
240 { return _pimpl->end(); }
241
242 bool LookupAttr::empty() const
243 { return begin() == end(); }
244
246 {
247 size_type c = 0;
248 for_( it, begin(), end() )
249 ++c;
250 return c;
251 }
252
254
255 std::ostream & operator<<( std::ostream & str, const LookupAttr & obj )
256 {
257 if ( obj.attr() == SolvAttr::noAttr )
258 return str << "search nothing";
259
260 if ( obj.attr() )
261 str << "search " << obj.attr() << " in ";
262 else
263 str << "search ALL in ";
264
265 if ( obj.solvable() )
266 return str << obj.solvable();
267 if ( obj.repo() )
268 return str << obj.repo();
269 return str << "pool";
270 }
271
272 std::ostream & dumpOn( std::ostream & str, const LookupAttr & obj )
273 {
274 return dumpRange( str << obj, obj.begin(), obj.end() );
275 }
276
278 //
279 // CLASS NAME : LookupRepoAttr
280 //
282
286
289
291 //
292 // CLASS NAME : detail::DIWrap
293 //
295
296 namespace detail
297 {
299 const std::string & mstring_r, int flags_r )
300 : _dip( new ::Dataiterator )
301 , _mstring( mstring_r )
302 {
303 ::dataiterator_init( _dip, sat::Pool::instance().get(), repoId_r, solvId_r, attrId_r,
304 _mstring.empty() ? 0 : _mstring.c_str(), flags_r );
305 }
306
308 const char * mstring_r, int flags_r )
309 : _dip( new ::Dataiterator )
310 , _mstring( mstring_r ? mstring_r : "" )
311 {
312 ::dataiterator_init( _dip, sat::Pool::instance().get(), repoId_r, solvId_r, attrId_r,
313 _mstring.empty() ? 0 : _mstring.c_str(), flags_r );
314 }
315
316 DIWrap::DIWrap( const DIWrap & rhs )
317 : _dip( 0 )
318 , _mstring( rhs._mstring )
319 {
320 if ( rhs._dip )
321 {
322 _dip = new ::Dataiterator;
323 ::dataiterator_init_clone( _dip, rhs._dip );
324 ::dataiterator_strdup( _dip );
325 }
326 }
327
329 {
330 if ( _dip )
331 {
332 ::dataiterator_free( _dip );
333 delete _dip;
334 }
335 }
336
337 std::ostream & operator<<( std::ostream & str, const DIWrap & obj )
338 { return str << obj.get(); }
339 }
340
342 //
343 // CLASS NAME : LookupAttr::iterator
344 //
346
348 // position and moving
350
353
355 { return _dip ? Solvable( _dip->solvid ) : Solvable::noSolvable; }
356
358 { return _dip ? SolvAttr( _dip->key->name ) : SolvAttr::noAttr; }
359
361 { if ( _dip ) ::dataiterator_skip_attribute( _dip.get() ); }
362
364 { if ( _dip ) ::dataiterator_skip_solvable( _dip.get() ); }
365
367 { if ( _dip ) ::dataiterator_skip_repo( _dip.get() ); }
368
370 { if ( _dip ) { _dip.get()->repoid = -1; _dip.get()->flags |= SEARCH_THISSOLVID; } }
371
373 { if ( _dip ) { _dip.get()->repoid = -1; } }
374
376 // attr value type test
378
380 { return _dip ? _dip->key->type : detail::noId; }
381
383 {
384 switch ( solvAttrType() )
385 {
386 case REPOKEY_TYPE_NUM:
388 return true;
389 break;
390 }
391 return false;
392 }
393
395 {
396 switch ( solvAttrType() )
397 {
398 case REPOKEY_TYPE_ID:
401 case REPOKEY_TYPE_STR:
403 return true;
404 break;
405 }
406 return false;
407 }
408
410 {
411 switch ( solvAttrType() )
412 {
413 case REPOKEY_TYPE_ID:
416 return true;
417 break;
418 }
419 return false;
420 }
421
423 {
424 switch ( solvAttrType() )
425 {
426 case REPOKEY_TYPE_MD5:
429 return true;
430 break;
431 }
432 return false;
433 }
434
436 namespace
437 {
438 enum SubType { ST_NONE, // no sub-structure
439 ST_FLEX, // flexarray
440 ST_SUB }; // inside sub-structure
441 SubType subType( const detail::DIWrap & dip )
442 {
443 if ( ! dip )
444 return ST_NONE;
445 if ( dip.get()->key->type == REPOKEY_TYPE_FLEXARRAY )
446 return ST_FLEX;
447 return dip.get()->kv.parent ? ST_SUB : ST_NONE;
448 }
449 }
451
453 { return subType( _dip ) != ST_NONE; }
454
456 // Iterate sub-structures.
458
460 { return( subBegin() == subEnd() ); }
461
463 {
464 size_type c = 0;
465 for_( it, subBegin(), subEnd() )
466 ++c;
467 return c;
468 }
469
471 {
472 SubType subtype( subType( _dip ) );
473 if ( subtype == ST_NONE )
474 return subEnd();
475 // setup the new sub iterator with the remembered position
476 detail::DIWrap dip( 0, 0, 0 );
477 ::dataiterator_clonepos( dip.get(), _dip.get() );
478 switch ( subtype )
479 {
480 case ST_NONE: // not reached
481 break;
482 case ST_FLEX:
483 ::dataiterator_seek( dip.get(), DI_SEEK_CHILD|DI_SEEK_STAY );
484 break;
485 case ST_SUB:
486 ::dataiterator_seek( dip.get(), DI_SEEK_REWIND|DI_SEEK_STAY );
487 break;
488 }
489 return iterator( dip ); // iterator takes over ownership!
490 }
491
496
498 {
499 iterator it = subBegin();
501 {
502 while ( it != subEnd() && it.inSolvAttr() != attr_r )
503 ++it;
504 }
505 return it;
506 }
507
509 {
510 if ( attrname_r.empty() )
511 return subBegin();
512
513 SubType subtype( subType( _dip ) );
514 if ( subtype == ST_NONE )
515 return subBegin();
516
517 std::string subattr( inSolvAttr().asString() );
518 if ( subtype == ST_FLEX )
519 {
520 // append ":attrname"
521 subattr += ":";
523 }
524 else
525 {
526 // replace "oldname" after ':' with "attrname"
527 std::string::size_type pos( subattr.rfind( ':' ) );
528 if ( pos != std::string::npos )
529 {
530 subattr.erase( pos+1 );
532 }
533 else
534 subattr = attrname_r; // no ':' so replace all.
535 }
536 return subFind( SolvAttr( subattr ) );
537 }
538
540 // attr value retrieval
542
544 {
545 if ( _dip )
546 {
547 switch ( solvAttrType() )
548 {
549 case REPOKEY_TYPE_NUM:
551 return _dip->kv.num;
552 break;
553 }
554 }
555 return 0;
556 }
557
559 { return asInt(); }
560
561 unsigned long long LookupAttr::iterator::asUnsignedLL() const
562 {
563 if ( _dip )
564 {
565 switch ( solvAttrType() )
566 {
567 case REPOKEY_TYPE_NUM:
569 return SOLV_KV_NUM64(&_dip->kv);
570 break;
571 }
572 }
573 return 0;
574 }
575
577 { return asInt(); }
578
579
580 const char * LookupAttr::iterator::c_str() const
581 {
582 if ( _dip )
583 {
584 switch ( solvAttrType() )
585 {
586 case REPOKEY_TYPE_ID:
589 if ( _dip->data && _dip->data->localpool )
590 return ::stringpool_id2str( &_dip->data->spool, _dip->kv.id ); // in local pool
591 else
592 return IdString( _dip->kv.id ).c_str(); // in global pool
593 break;
594
595 case REPOKEY_TYPE_STR:
596 return _dip->kv.str;
597 break;
598
600 // may or may not be stringified depending on SEARCH_FILES flag
601 return( _dip->flags & SEARCH_FILES
602 ? _dip->kv.str
603 : ::repodata_dir2str( _dip->data, _dip->kv.id, _dip->kv.str ) );
604 break;
605 }
606 }
607 return 0;
608 }
609
611 {
612 if ( _dip )
613 {
614 switch ( solvAttrType() )
615 {
616 case REPOKEY_TYPE_ID:
619 {
620 detail::IdType id = ::repodata_globalize_id( _dip->data, _dip->kv.id, 1 );
621 return ISRELDEP(id) ? Capability( id ).asString()
622 : IdString( id ).asString();
623 }
624 break;
625
626 case REPOKEY_TYPE_STR:
628 {
629 const char * ret( c_str() );
630 return ret ? ret : "";
631 }
632 break;
633
634 case REPOKEY_TYPE_NUM:
636 return str::numstring( asInt() );
637 break;
638
639 case REPOKEY_TYPE_MD5:
642 {
643 return asCheckSum().asString();
644 }
645 break;
646
648 {
649 std::ostringstream str;
650 str << "{" << endl;
651 for_( it, subBegin(), subEnd() )
652 {
653 str << " " << it.inSolvAttr() << " = " << it.asString() << endl;
654 }
655 str << "}";
656 return str.str();
657 }
658 break;
659 }
660 }
661 return std::string();
662 }
663
665 {
666 if ( _dip )
667 {
668 switch ( solvAttrType() )
669 {
670 case REPOKEY_TYPE_ID:
673 return IdString( ::repodata_globalize_id( _dip->data, _dip->kv.id, 1 ) );
674 break;
675 }
676 }
677 return IdString();
678 }
679
681 {
682 if ( _dip )
683 {
684 switch ( solvAttrType() )
685 {
686 case REPOKEY_TYPE_MD5:
687 return CheckSum::md5( ::repodata_chk2str( _dip->data, solvAttrType(), (unsigned char *)_dip->kv.str ) );
688 break;
689
691 return CheckSum::sha1( ::repodata_chk2str( _dip->data, solvAttrType(), (unsigned char *)_dip->kv.str ) );
692 break;
693
695 return CheckSum::sha224( ::repodata_chk2str( _dip->data, solvAttrType(), (unsigned char *)_dip->kv.str ) );
696 break;
697
699 return CheckSum::sha256( ::repodata_chk2str( _dip->data, solvAttrType(), (unsigned char *)_dip->kv.str ) );
700 break;
701
703 return CheckSum::sha384( ::repodata_chk2str( _dip->data, solvAttrType(), (unsigned char *)_dip->kv.str ) );
704 break;
705
707 return CheckSum::sha512( ::repodata_chk2str( _dip->data, solvAttrType(), (unsigned char *)_dip->kv.str ) );
708 break;
709 }
710 }
711 return CheckSum();
712 }
713
715 // internal stuff below
717
721
723 : iterator_adaptor_( 0 )
724 , _dip( rhs._dip )
725 {
726 base_reference() = _dip.get();
727 }
728
730 : iterator_adaptor_( 0 )
731 {
732 _dip.swap( dip_r ); // take ownership!
733 base_reference() = _dip.get();
734 increment();
735 }
736
739
741 {
742 if ( &rhs != this )
743 {
744 _dip = rhs._dip;
745 base_reference() = _dip.get();
746 }
747 return *this;
748 }
749
751
753 {
754 // Iterator equal is same position in same container.
755 // Here: same attribute in same solvable.
756 return( lhs.solvid == rhs.solvid && lhs.key->name == rhs.key->name );
757 }
758
760 {
761 return _dip ? ::repodata_globalize_id( _dip->data, _dip->kv.id, 1 )
762 : detail::noId;
763 }
764
766 {
767 if ( _dip )
768 {
769 if ( ! ::dataiterator_step( _dip.get() ) )
770 {
771 _dip.reset();
772 base_reference() = 0;
773 }
774 else
775 {
776 ::dataiterator_strdup( _dip.get() );
777 }
778 }
779 }
780
781 std::ostream & operator<<( std::ostream & str, const LookupAttr::iterator & obj )
782 {
783 const detail::CDataiterator * dip = obj.get();
784 if ( ! dip )
785 return str << "EndOfQuery";
786
787 if ( obj.inSolvable() )
788 str << obj.inSolvable();
789 else if ( obj.inRepo() )
790 str << obj.inRepo();
791
792 str << '<' << obj.inSolvAttr() << (obj.solvAttrSubEntry() ? ">(*" : ">(")
793 << IdString(obj.solvAttrType()) << ") = " << obj.asString();
794 return str;
795 }
796
797 template<> CheckSum LookupAttr::iterator::asType<CheckSum>() const
798 { return asCheckSum(); }
799
801 } // namespace sat
804} // namespace zypp
806
807std::ostream & operator<<( std::ostream & str, const zypp::sat::detail::CDataiterator * obj )
808{
809 str << "detail::CDataiterator(";
810 if ( ! obj )
811 {
812 str << "NULL";
813 }
814 else
815 {
816 str << "|" << zypp::Repository(obj->repo);
817 str << "|" << zypp::sat::Solvable(obj->solvid);
818 str << "|" << zypp::IdString(obj->key->name);
819 str << "|" << zypp::IdString(obj->key->type);
820 str << "|" << obj->repodataid;
821 str << "|" << obj->repoid;
822 }
823 return str << ")";
824}
825
Reference counted access to a Tp object calling a custom Dispose function when the last AutoDispose h...
Definition AutoDispose.h:94
void reset()
Reset to default Ctor values.
Convenience char* constructible from std::string and char*, it maps (char*)0 to an empty string.
Definition String.h:91
A sat capability.
Definition Capability.h:63
std::string asString() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition Capability.h:168
static CheckSum md5(const std::string &checksum)
Definition CheckSum.h:73
static CheckSum sha384(const std::string &checksum)
Definition CheckSum.h:78
static CheckSum sha1(const std::string &checksum)
Definition CheckSum.h:75
static CheckSum sha512(const std::string &checksum)
Definition CheckSum.h:79
static CheckSum sha256(const std::string &checksum)
Definition CheckSum.h:77
static CheckSum sha224(const std::string &checksum)
Definition CheckSum.h:76
IdType id() const
Access to the sat-pools string space.
Definition IdString.h:43
const char * c_str() const
Conversion to const char *
Definition IdString.cc:50
std::string asString() const
Conversion to std::string
Definition IdString.h:98
int get() const
Return the integer representation.
Definition StrMatcher.h:150
static const Repository noRepository
Represents no Repository.
Definition Repository.h:62
IdType id() const
Expert backdoor.
Definition Repository.h:316
String matching (STRING|SUBSTRING|GLOB|REGEX).
Definition StrMatcher.h:298
const std::string & searchstring() const
The current searchstring.
const Match & flags() const
The current search flags.
LookupAttr implememtation.
Definition LookupAttr.cc:56
Impl(SolvAttr attr_r, Solvable solv_r)
Definition LookupAttr.cc:67
void setSolvable(Solvable solv_r)
void setStrMatcher(const StrMatcher &matcher_r)
Definition LookupAttr.cc:86
void setParent(SolvAttr attr_r)
Solvable solvable() const
LookupAttr::iterator end() const
const StrMatcher & strMatcher() const
Definition LookupAttr.cc:83
void setPool(Location loc_r)
Definition LookupAttr.cc:96
Impl * clone() const
clone for RWCOW_pointer
LookupAttr::iterator begin() const
Impl(SolvAttr attr_r, Location loc_r)
Definition LookupAttr.cc:61
Repository repo() const
void setRepo(Repository repo_r, Location loc_r)
void setAttr(SolvAttr attr_r)
Definition LookupAttr.cc:75
Impl(SolvAttr attr_r, Repository repo_r, Location loc_r)
Definition LookupAttr.cc:64
iterator subBegin() const
Iterator to the begin of a sub-structure.
unsigned long long asUnsignedLL() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
unsigned asUnsigned() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
void stayInThisSolvable()
Stop after all matches in the current Solvable are processed.
IdString idStr() const
As IdStr.
detail::IdType dereference() const
void nextSkipSolvable()
On the next call to operator++ advance to the next Solvable.
SolvAttr inSolvAttr() const
The current SolvAttr.
bool subEmpty() const
Whether the sub-structure is empty.
iterator subFind(SolvAttr attr_r) const
Iterator pointing to the first occurance of SolvAttr attr_r in sub-structure.
CheckSum asCheckSum() const
As CheckSum.
bool solvAttrCheckSum() const
Whether this is a CheckSum attribute.
std::string asString() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
void nextSkipSolvAttr()
On the next call to operator++ advance to the next SolvAttr.
bool asBool() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
iterator subEnd() const
Iterator behind the end of a sub-structure.
void stayInThisRepo()
Stop after all matches in the current Repository are processed.
bool solvAttrSubEntry() const
Whether this is the entry to a sub-structure (flexarray).
Solvable inSolvable() const
The current Solvable.
void nextSkipRepo()
On the next call to operator++ advance to the next Repository.
const char * c_str() const
Conversion to string types.
size_type subSize() const
Ammount of attributes in the sub-structure.
iterator & operator=(const iterator &rhs)
bool solvAttrNumeric() const
Whether this is a numeric attribute (incl.
bool solvAttrIdString() const
Whether this string attribute is available as IdString.
bool dip_equal(const detail::CDataiterator &lhs, const detail::CDataiterator &rhs) const
bool solvAttrString() const
Whether this is a string attribute.
detail::IdType solvAttrType() const
The current SolvAttr type.
int asInt() const
Conversion to numeric types.
Repository inRepo() const
The current Repository.
Lightweight attribute value lookup.
Definition LookupAttr.h:108
iterator end() const
Iterator behind the end of query results.
RWCOW_pointer< Impl > _pimpl
Definition LookupAttr.h:227
const StrMatcher & strMatcher() const
The pattern to match.
bool empty() const
Whether the query is empty.
Location
Specify the where to look for the attribule.
Definition LookupAttr.h:116
@ REPO_ATTR
Search for repository attributes.
Definition LookupAttr.h:118
void setStrMatcher(const StrMatcher &matcher_r)
Set the pattern to match.
Repository repo() const
Whether to search in one Repository.
SolvAttr attr() const
The SolvAttr to search.
SolvAttr parent() const
Whether to search within a sub-structure (SolvAttr::noAttr if not)
void setAttr(SolvAttr attr_r)
Set the SolvAttr to search.
void setRepo(Repository repo_r, Location=SOLV_ATTR)
Set search in one Repository.
bool pool() const
Whether to search in Pool.
size_type size() const
Ammount of results.
iterator begin() const
Iterator to the begin of query results.
void setSolvable(Solvable solv_r)
Set search in one Solvable.
Solvable solvable() const
Whether to search in one Solvable.
std::ostream & operator<<(std::ostream &str, const LookupAttr &obj)
Stream output.
LookupAttr()
Default ctor finds nothing.
void setPool(Location=SOLV_ATTR)
Set search in Pool (all repositories).
void setParent(SolvAttr attr_r)
Set search within a sub-structure (SolvAttr::noAttr for none)
void setRepo(Repository repo_r)
Set search in one Repository.
LookupRepoAttr()
Default ctor finds nothing.
Definition LookupAttr.h:261
static Pool instance()
Singleton ctor.
Definition Pool.h:55
Solvable attribute keys.
Definition SolvAttr.h:41
static const SolvAttr noAttr
Value representing noAttr ("")
Definition SolvAttr.h:48
SolvAttr parent() const
Return the parent of well know sub-structure attributes (SolvAttr::noAttr if none).
Definition SolvAttr.cc:152
static const SolvAttr allAttr
Value to request searching all Attributes (0).
Definition SolvAttr.h:46
A Solvable object within the sat Pool.
Definition Solvable.h:54
IdType id() const
Expert backdoor.
Definition Solvable.h:428
static const Solvable noSolvable
Represents no Solvable.
Definition Solvable.h:75
Repository repository() const
The Repository this Solvable belongs to.
Definition Solvable.cc:363
Wrapper around sat detail::CDataiterator.
Definition LookupAttr.h:293
detail::CDataiterator * get() const
Definition LookupAttr.h:331
void swap(DIWrap &rhs)
Definition LookupAttr.h:308
DIWrap()
NULL detail::CDataiterator
Definition LookupAttr.h:296
detail::CDataiterator * _dip
Definition LookupAttr.h:335
String related utilities and Regular expression matching.
static const IdType noId(0)
static const SolvableIdType noSolvableId(0)
Id to denote Solvable::noSolvable.
static const RepoIdType noRepoId(0)
Id to denote Repo::noRepository.
::s_Dataiterator CDataiterator
Wrapped libsolv C data type exposed as backdoor.
Definition PoolMember.h:58
int IdType
Generic Id type.
Definition PoolMember.h:104
std::ostream & operator<<(std::ostream &str, const DIWrap &obj)
unsigned SolvableIdType
Id type to connect Solvable and sat-solvable.
Definition PoolMember.h:125
CRepo * RepoIdType
Id type to connect Repo and sat-repo.
Definition PoolMember.h:133
std::ostream & operator<<(std::ostream &str, const FileConflicts &obj)
std::ostream & dumpOn(std::ostream &str, const LocaleSupport &obj)
std::string numstring(char n, int w=0)
Definition String.h:289
Easy-to use interface to the ZYPP dependency resolver.
std::ostream & dumpRange(std::ostream &str, TIterator begin, TIterator end, const std::string &intro="{", const std::string &pfx="\n ", const std::string &sep="\n ", const std::string &sfx="\n", const std::string &extro="}")
Print range defined by iterators (multiline style).
Definition LogTools.h:107
std::ostream & operator<<(std::ostream &str, const SerialNumber &obj)
std::string asString(const Patch::Category &obj)
Definition Patch.cc:122
#define for_(IT, BEG, END)
Convenient for-loops using iterator.
Definition Easy.h:28