Loading...
Searching...
No Matches
Caster.h
1/*
2
3MIT License
4
5Copyright (c) 2017 FMI Open Development / Markus Peura, first.last@fmi.fi
6
7Permission is hereby granted, free of charge, to any person obtaining a copy
8of this software and associated documentation files (the "Software"), to deal
9in the Software without restriction, including without limitation the rights
10to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11copies of the Software, and to permit persons to whom the Software is
12furnished to do so, subject to the following conditions:
13
14The above copyright notice and this permission notice shall be included in all
15copies or substantial portions of the Software.
16
17THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23SOFTWARE.
24
25*/
26/*
27Part of Rack development has been done in the BALTRAD projects part-financed
28by the European Union (European Regional Development Fund and European
29Neighbourhood Partnership Instrument, Baltic Sea Region Programme 2007-2013)
30*/
31#ifndef DRAIN_CASTER
32#define DRAIN_CASTER
33
34
35#include <cstddef> // nullptr
36#include <stdexcept>
37#include <iostream>
38#include <sstream>
39#include <cmath> // for NaN
40
41
42//#include "TypeName.h"
43//#include "TypeUtils.h" // for typesetter
44#include "Type.h"
45
46// Backport nullptr_t streaming for C++14 (operator<< for nullptr_t added in C++17).
47#if __cplusplus < 201703L
48#ifndef DRAIN_NULLPTR_STREAM_COMPAT
49#define DRAIN_NULLPTR_STREAM_COMPAT
50inline std::ostream & operator<<(std::ostream & os, std::nullptr_t) {
51 return os << static_cast<const void *>(nullptr);
52}
53#endif
54#endif
55
56
57// // using namespace std;
58
59namespace drain {
60
61
62
64
77class Caster {
78
79
80
81public:
82
84
87 inline
89 unsetType();
90 };
91
93
95 inline
96 Caster (const Caster &c){
97 //std::cerr << "Caster:: warning: copy const;" << std::endl;
98 setType(c.getType());
99 ptr = c.ptr;
100 };
101
102 inline virtual
103 ~Caster(){};
104
105 inline
106 void link(void *p, const std::type_info &t){
107 ptr = p;
108 setType(t);
109 }
110
111 template <class T>
112 inline
113 void link(T *p){
114 link(p, typeid(T));
115 }
116
117 template <class T>
118 inline
119 void link(T &p){
120 link(&p, typeid(T));
121 }
122
123
125
128 inline
129 void setType(const std::type_info &t){
130 Type::call<drain::Type::typeSetter>(*this, t);
131 }
132
134 //
135 // Note: updateType planned to be renamed "directly" to setType?
136 template <class F>
137 inline
138 void setType(){
139 updateType<F>();
140 }
141
143 virtual inline
144 bool requestType(const std::type_info & t){
145 return getType() == t;
146 }
147
149 void unsetType(); // inline not possible here due to template<> (?)
150
151
153 inline
154 const std::type_info & getType() const { return *type; };
155
156 inline
157 bool typeIsSet() const { return type != &typeid(void); };
158
160 inline
161 size_t getElementSize() const { return byteSize; };
162
163
165 // => See the base type implementations below.
166 template <class T>
167 void put(void *p, const T & x) const {
168 if (typeIsSet()){
169 std::stringstream sstr;
170 sstr << x;
171 //std::cerr << "Caster::put(p, T x): stringstream conversion: " << x << "=>" << sstr.str() << std::endl;
172 put(p, sstr.str());
173 }
174 else {
175 throw std::runtime_error(std::string("Caster::put(void *, const T & ), type unset"));
176 }
177 }
178
180 void put(void *p, std::nullptr_t) const;
181
183 template <class T>
184 inline
185 void put(const T & x) const { // NEW
186 put(this->ptr, x);
187 }
188
189 // Consider assigning default type (std::string?)
190 /*
191 * Notice that assigning to \c char is potentially ambiguous.
192 * char c;
193 * put(&c, "67"); // 6 or 67 or ascii('6') ?
194 * put(&c, "C"); // 0 or 67 == ascii('C') ?
195 */
196 void put(void *p, const char * x) const;
197
199 inline
200 void put(const char * x) const { // NEW
201 put(this->ptr, x);
202 }
203
204
206 template <class T>
207 T get(const void *p) const {
208 throw std::runtime_error(std::string("Caster::get() unimplemented output type: ") + typeid(T).name());
209 return T();
210 }
211
212 template <class T>
213 inline
214 T get() const { // NEW
215 return get<T>(this->ptr);
216 }
217
218
220 inline
221 void translate(const Caster &c, const void *ptrC, void *ptr) const {
222 //(this->*translatePtr)(c, ptrC, ptr);
223 (*translatePtr)(c, ptrC, ptr);
224 };
225
226
228 inline
229 void translate(const Caster &c) const { // NEW
230 //(this->*translatePtr)(c, c.ptr, this->ptr);
231 (*translatePtr)(c, c.ptr, this->ptr);
232 };
233
234
235 // New 2024/05
236 inline
237 bool compare(const void *ptr, const Caster &c, const void *cPtr) const {
238 return (*comparePtr)(ptr, c, cPtr);
239 }
240
241 // New 2024/05
242 inline
243 std::istream & fromStream(std::istream & istr, const void *p) const{
244 return (*fromStreamPtr)(istr, p);
245 }
246
248 inline
249 std::ostream & toOStream(std::ostream &ostr, const void *p) const {
250 return (*toOStreamPtr)(ostr,p);
251 }
252
254 inline
255 std::ostream & toOStream(std::ostream &ostr) const { // NEW
256 return (*toOStreamPtr)(ostr, this->ptr);
257 }
258
260 void *ptr = nullptr; // NEW, visible for CastableIterator
261
262protected:
263
264
266 template <class F>
267 void updateType();
268
269
270 //void updateCasterType();
271
272 // New / experimental
273 void (* putBool)(void *p, const bool &x);
274 void (* putInt)(void *p, const int &x);
275 void (* putChar)(void *p, const char &x);
276 void (* putUChar)(void *p, const unsigned char &x);
277 void (* putUInt)(void *p, const unsigned int &x);
278 void (* putShort)(void *p, const short &x);
279 void (* putUShort)(void *p, const unsigned short &x);
280 void (* putLong)(void *p, const long &x);
281 void (* putULong)(void *p, const unsigned long &x);
282 void (* putFloat)(void *p, const float &x);
283 void (* putDouble)(void *p, const double &x);
284
285
286 char (* getChar)(const void *p);
287 unsigned char (* getUChar)(const void *p);
288 int (* getInt)(const void *p);
289 unsigned int (* getUInt)(const void *p);
290 short (* getShort)(const void *p);
291 unsigned short (* getUShort)(const void *p);
292 long (* getLong)(const void *p);
293 unsigned long (* getULong)(const void *p);
294 float (* getFloat)(const void *p);
295 double (* getDouble)(const void *p);
296
297 bool (* getBool)(const void *p);
298
299
301 //void (Caster::* translatePtr)(const Caster &c, const void *ptrC, void *ptr) const;
302 void (* translatePtr)(const Caster &c, const void *ptrC, void *ptr);
303
304 // New
305 //bool (Caster::* comparePtr)(const Caster &c, size_t i) const = nullptr;
306 bool (* comparePtr)(const void *, const Caster &c, const void *) = nullptr;
307
309 // std::ostream & (Caster::* toOStreamPtr)(std::ostream &ostr, const void *p) const;
310 std::ostream & (* toOStreamPtr)(std::ostream &ostr, const void *p);
311 std::istream & (* fromStreamPtr)(std::istream &istr, const void *p) = nullptr;
312
314 /*
315 * \tparam T - outer type
316 * \tparam F - inner type
317 */
318 template <class T,class F>
319 static inline
320 void putT(void *p, const T &x) {
321 *(F*)p = static_cast<F>(x);
322 }
323
324
326 template <class T>
327 static inline
328 void putToVoidT(void *p, const T &x) {
329 }
330
332
337 template <class T>
338 static inline
339 void putToStringT(void *p, const T &x) {
340 std::stringstream sstr;
341 sstr << x;
342 *(std::string*)p = sstr.str();
343 }
344
346 static inline
347 void putToStringT(void *p, std::nullptr_t) {
348 putToStringT(p, 0);
349 }
350
351
352 // Future member: enables forwarding value x to a Caster.
353 template <class T>
354 static inline
355 void putToCasterT(void *p, const T &x) {
356 Caster &c = *(Caster *)p;
357 // std::cerr << __FUNCTION__ << "(p, T x): x=" << x << std::endl;
358 // std::cerr << __FUNCTION__ << "(p, T x): dst=" << c.getType().name() << std::endl;
359 c.put(c.ptr, x);
360 // std::cerr << __FUNCTION__ << "(p, T x): (string)" << c.get<std::string>(c.ptr) << std::endl;
361 // std::cerr << __FUNCTION__ << "(p, T x): (double)" << c.get<double>(c.ptr) << std::endl;
362 }
363
364
365
367
371 template <class T,class F>
372 static inline
373 T getT(const void *p) {
374 return static_cast<T>(*(F*)p);
375 }
376
378
383 template <class T>
384 static inline
385 T getFromVoidT(const void *p){
386 static const T t(0);
387 //static const T t(NAN);
388 return t;
389 }
390
392 /*
393 * \tparam T - output type
394 *
395 * The internal storage type is std::string.
396 *
397 */
398 template <class T>
399 static inline
400 T getFromStringT(const void *p) {
401 T x(0); // why (0) ?
402 std::stringstream sstr;
403 sstr << *(const std::string *)p;
404 sstr >> x;
405 return x;
406 }
407
408 // Future member: enables forwarding from a Caster.
409 /*
410 template <class T>
411 static inline
412 T getFromCasterT(const void *p) {
413 const Caster &c = *(const Caster *)p;
414 //std::cerr << c.getType().name() << " <- " << x << std::endl;
415 return c.get<T>(c.ptr); // or even c.get<T>()
416 }
417 */
418
419
421
424 template <class F>
425 static
426 void translateT(const Caster &c, const void *ptrC, void *ptr) { // const {
427 //std::cerr << "Caster::" << __FUNCTION__ << ": " << c.getType().name() << std::endl;
428 *(F *)ptr = c.get<F>(ptrC);
429 }
430
432
435 template <class F>
436 static
437 bool compareT(const void *ptr, const Caster &c, const void *cPtr){ // const
438
439 if (ptr == nullptr)
440 return false;
441
442 if (cPtr == nullptr)
443 return false;
444
445 return (*(F *)ptr == c.get<F>(cPtr));
446 // return false;
447 // return this->get<F>(i) == c.get<F>(i);
448 }
449
451 template <class F>
452 static
453 std::ostream & toOStreamT(std::ostream & ostr, const void *p) { //const {
454 if (p != nullptr)
455 ostr << *(F*)p;
456 return ostr;
457 }
458
459 template <class F>
460 static
461 std::ostream & toOStreamFloats(std::ostream & ostr, const void *p){
462
463 if (!p)
464 throw std::runtime_error("Caster::toOStreamT<double>:: null ptr as argument");
465
466 const F d = *(F *)p;
467
468 if (std::isnan(d)){
469 ostr << "null"; // JSON has no NaN (only true, false and null)
470 //ostr << "NaN"; // JSONtree std literal
471 }
472 /* TODO: add at least one decimal for floating-point numbers ".0"
473 else if ((d == rint(d)) && (abs(d) < 1.0E+5)){
474 //ostr << std::ios::fixed << std::setprecision(2) << d;
475
476 const std::streamsize prec = ostr.precision();
477 // // const std::ios_base::fmtflags flags = ostr.setf(std::ios::fixed, std::ios::floatfield );
478
479 ostr.precision(1);
480 ostr << d;
481 // // ostr.setf(flags);
482 ostr.precision(prec);
483
484 }
485 */
486 else {
487 ostr << d;
488 }
489
490 return ostr;
491
492 }
493
495 template <class F>
496 static
497 std::istream & fromStreamT(std::istream & istr, const void *p) { //const {
498 if (p != nullptr){
499 istr >> *(F*)p;
500 }
501 return istr;
502 }
503
504
506 const std::type_info *type;
507
509 size_t byteSize;
510
511};
512
513template <>
514inline
515void Caster::link(Caster &c){
516 link(c.ptr, c.getType());
517}
518
519
520template <>
521void Caster::updateType<void>();
522
523template <>
524void Caster::updateType<std::string>();
525
527template <>
528void Caster::updateType<Caster>();
529
530
531template <class F>
533
534 if (!std::is_fundamental<F>::value){
535 throw std::runtime_error(std::string(__FILE__) + __FUNCTION__ + ':' + typeid(F).name() + ": cannot convert to basic types");
536 return;
537 }
538
539
540 type = & typeid(F);
541 byteSize = sizeof(F)/sizeof(char);
542
544 putBool = & Caster::putT<bool,F>;
545 putInt = & Caster::putT<int,F>;
546 putChar = & Caster::putT<char,F>;
547 putUChar = & Caster::putT<unsigned char,F>;
548 putUInt = & Caster::putT<unsigned int,F>;
549 putShort = & Caster::putT<short,F>;
550 putUShort = & Caster::putT<unsigned short,F>;
551 putLong = & Caster::putT<long int,F>;
552 putULong = & Caster::putT<unsigned long,F>;
553 putFloat = & Caster::putT<float,F>;
554 putDouble = & Caster::putT<double,F>;
555
556 getBool = & Caster::getT<bool,F>;
557 getInt = & Caster::getT<int,F>;
558 getChar = & Caster::getT<char,F>;
559 getUChar = & Caster::getT<unsigned char,F>;
560 getUInt = & Caster::getT<unsigned int,F>;
561 getShort = & Caster::getT<short,F>;
562 getUShort = & Caster::getT<unsigned short,F>;
563 getLong = & Caster::getT<long,F>;
564 getULong = & Caster::getT<unsigned long,F>;
565 getFloat = & Caster::getT<float,F>;
566 getDouble = & Caster::getT<double,F>;
567
568 translatePtr = & Caster::translateT<F>;
569 comparePtr = & Caster::compareT<F>;
570
571 toOStreamPtr = & Caster::toOStreamT<F>;
572 fromStreamPtr = & Caster::fromStreamT<F>;
573}
574
575
576
577template <>
578inline
579void Caster::put<bool>(void *p, const bool & x) const {
580 //(this->*putBool)(p,x);
581 (this->putBool)(p,x);
582}
583
584template <>
585inline
586void Caster::put<char>(void *p, const char & x) const {
587 (this->putChar)(p,x);
588}
589
590template <>
591inline
592void Caster::put<unsigned char>(void *p, const unsigned char & x) const {
593 (this->putUChar)(p,x);
594}
595
596template <> inline
597void Caster::put<int>(void *p, const int & x) const {
598 // (this->putInt)(p,x);
599 (this->putInt)(p,x);
600}
601
602template <> inline
603void Caster::put<unsigned int>(void *p, const unsigned int & x) const {
604 (this->putUInt)(p,x);
605}
606
607template <> inline
608void Caster::put<long>(void *p, const long & x) const {
609 (this->putLong)(p,x);
610}
611
612template <> inline
613void Caster::put<unsigned long>(void *p, const unsigned long & x) const {
614 (this->putULong)(p,x);
615}
616
617template <> inline
618void Caster::put<float>(void *p, const float& x) const {
619 (this->putFloat)(p,x);
620}
621
622template <> inline
623void Caster::put<double>(void *p, const double & x) const {
624 (this->putDouble)(p,x);
625}
626
627template <> inline
628void Caster::put<std::string>(void *p, const std::string & x) const {
629 //put(p, x.c_str()); Dangerous, if F = std::string
630
631 //std::cerr << "Laita std::string x=" << x << std::endl;
632 if (getType() == typeid(std::string))
633 *(std::string *)p = x;
634 else if (getType() == typeid(char)){
635 // std::cout << "note: experimental str to char\n";
636 if (x.empty())
637 p = nullptr; //'\0'; // NULL, 0 ?
638 else {
639 if (x.size() > 1)
640 std::cerr << "Caster::put<std::string>() warning: single-char dst, assigned 1st of multi-char '"<< x << "'\n";
641 *(char *)p = x.at(0);
642 }
643 }
644 else
645 put(p, x.c_str());
646
647}
648
649/*
650template <> inline
651void Caster::toOStr(std::ostream & ostr, void *p) const {
652 (this->*toOStr)(p,x);
653}
654 */
655
656template <> inline
657bool Caster::get<bool>(const void *p) const {
658 return (this->getBool)(p);
659}
660
661
662template <> inline
663char Caster::get<char>(const void *p) const {
664 return (this->getChar)(p);
665}
666
667template <> inline
668unsigned char Caster::get<unsigned char>(const void *p) const {
669 return (this->getUChar)(p);
670}
671
672template <> inline
673short Caster::get<short>(const void *p) const {
674 return (this->getShort)(p);
675}
676
677template <> inline
678unsigned short Caster::get<unsigned short>(const void *p) const {
679 return (this->getUShort)(p);
680}
681
682template <> inline
683int Caster::get<int>(const void *p) const {
684 return (this->getInt)(p);
685}
686
687template <> inline
688unsigned int Caster::get<unsigned int>(const void *p) const {
689 return (this->getUInt)(p);
690}
691
692template <> inline
693long Caster::get<long>(const void *p) const {
694 return (this->getLong)(p);
695}
696
697template <> inline
698unsigned long Caster::get<unsigned long>(const void *p) const {
699 return (this->getULong)(p);
700}
701
702template <> inline
703float Caster::get<float>(const void *p) const {
704 return (this->getFloat)(p);
705}
706
707template <> inline
708double Caster::get<double>(const void *p) const {
709 return (this->getDouble)(p);
710}
711
713
714template <> inline
715std::string Caster::get<std::string>(const void *p) const {
716
717 // void is handled by toOStr, but this is faster.
718 if (getType() == typeid(void)){
719 static const std::string empty;
720 return empty;
721 }
722 else if (getType() == typeid(char)){
723 // std::cout << "note: experimental char to str\n";
724 return std::string(1, *(char *)p); // NOTE: this does not handle char array of more elements!
725 }
726 else if (getType() == typeid(bool)){
727 return *(const bool *)p ? "true" : "false";
728 }
729 else if (getType() == typeid(std::string)){
730 return *(const std::string *)p;
731 }
732 else {
733 std::stringstream sstr;
734 toOStream(sstr, p);
735 // sstr << (this->getDouble)(p);
736 return sstr.str();
737 }
738}
739
740
741// Does nothing, leaves ptr intact.
742template <>
743inline
744void Caster::translateT<void>(const Caster &c, const void *ptrC, void *ptr){ // const {
745}
746
747// Writes to buffer of Caster pointed by ptr.
748template <>
749inline
750void Caster::translateT<Caster>(const Caster &c, const void *ptrC, void *ptr){ // const {
751 Caster & c2 = *(Caster *)ptr;
752 c2.translate(c, ptrC, c2.ptr);
753 //*(F *)ptr = c.get<F>(ptrC);
754}
755
757template <>
758inline
759double Caster::getFromVoidT<double>(const void *p){
760 return NAN;
761}
762
763
764// Specialisation for floats returns NAN.
765template <>
766inline
767float Caster::getFromVoidT<float>(const void *p){
768 return NAN;
769}
770
772
775template <>
776inline
777bool Caster::compareT<void>(const void *ptr, const Caster &c, const void *cPtr){ // const {
778 return false;
779}
780
781
782// VOID
784template <>
785inline
786std::ostream & Caster::toOStreamT<void>(std::ostream &ostr, const void *p){ // const {
787 return ostr;
788}
789
790// BOOL
792template <>
793inline
794std::ostream & Caster::toOStreamT<bool>(std::ostream &ostr, const void *p){
795 ostr << (*(const bool *)p ? "true" : "false");
796 return ostr;
797}
798
800template <>
801inline
802std::ostream & Caster::toOStreamT<Caster>(std::ostream &ostr, const void *p){ // const {
803 const Caster &c = *(const Caster *)p;
804 c.toOStream(ostr);
805 return ostr;
806}
807
808// Yes, to get trailing period and zero: .0
809template <>
810inline
811std::ostream & Caster::toOStreamT<float>(std::ostream & ostr, const void *p){
812 return toOStreamFloats<float>(ostr, p);
813}
814
815// Yes, to get trailing period and zero: .0
816template <>
817inline
818std::ostream & Caster::toOStreamT<double>(std::ostream & ostr, const void *p){
819 return toOStreamFloats<double>(ostr, p);
820}
821
822
823
824} // namespace drain
825
826
827#endif
828
829// Drain
Definition Caster.h:77
std::ostream & toOStream(std::ostream &ostr) const
Write data to output stream.
Definition Caster.h:255
static void putToStringT(void *p, std::nullptr_t)
nullptr_t overload: treated as 0 (avoids ambiguous operator<< in C++14).
Definition Caster.h:347
void updateType()
Sets pointers to the type-dependent conversion functions.
Definition Caster.h:532
void put(const T &x) const
Write to internal pointer using put(this->ptr, x).
Definition Caster.h:185
void put(const char *x) const
Write to internal pointer, calls put(this->ptr, x).
Definition Caster.h:200
const std::type_info * type
Current type.
Definition Caster.h:506
std::ostream & toOStream(std::ostream &ostr, const void *p) const
Write data to output stream.
Definition Caster.h:249
static T getFromVoidT(const void *p)
This default implementation is for integral numeric types, returns zero. Specialisation for floats re...
Definition Caster.h:385
static T getT(const void *p)
The main handler converting input to output.
Definition Caster.h:373
T get(const void *p) const
Default implementation throws an error. See specialized implementation below.
Definition Caster.h:207
void unsetType()
calls void setType(void)().
Definition Caster.cpp:153
const std::type_info & getType() const
Returns type_info of the current type.
Definition Caster.h:154
static bool compareT(const void *ptr, const Caster &c, const void *cPtr)
Checks equality through base type casting.
Definition Caster.h:437
static void putToVoidT(void *p, const T &x)
Void - does nothing, regardless of input type.
Definition Caster.h:328
size_t getElementSize() const
Returns the size of the base type (size of an element, not of element array).
Definition Caster.h:161
void setType(const std::type_info &t)
Calls setType<T>() for which typeid(T) = t.
Definition Caster.h:129
Caster()
Default constructor. Leaves the type undefined.
Definition Caster.h:88
virtual bool requestType(const std::type_info &t)
Utility for derived classes, which may be able to adapt memory array type as well.
Definition Caster.h:144
size_t byteSize
Size of target memory (target object) in bytes.
Definition Caster.h:509
void translate(const Caster &c) const
Convert from str Caster.
Definition Caster.h:229
void(* translatePtr)(const Caster &c, const void *ptrC, void *ptr)
Convert from str pointer and Caster.
Definition Caster.h:302
std::ostream &(* toOStreamPtr)(std::ostream &ostr, const void *p)
Write to stream.
Definition Caster.h:310
void setType()
Sets pointers to the type-dependent conversion functions.
Definition Caster.h:138
void translate(const Caster &c, const void *ptrC, void *ptr) const
Convert from str pointer and Caster.
Definition Caster.h:221
static T getFromStringT(const void *p)
The main handler converting input to output.
Definition Caster.h:400
void * ptr
Future member: enables setting Caster type.
Definition Caster.h:260
static std::istream & fromStreamT(std::istream &istr, const void *p)
Output.
Definition Caster.h:497
static std::ostream & toOStreamT(std::ostream &ostr, const void *p)
Output.
Definition Caster.h:453
void put(void *p, const T &x) const
Default conversion (for unconventional types). Uses std::stringstream for conversion.
Definition Caster.h:167
static void putToStringT(void *p, const T &x)
A handler for converting input to a std::string.
Definition Caster.h:339
Caster(const Caster &c)
Copy constructor. Copies the type.
Definition Caster.h:96
static void translateT(const Caster &c, const void *ptrC, void *ptr)
Casts from ptrC to ptr.
Definition Caster.h:426
static void putT(void *p, const T &x)
Convert input of base type to internally applied base type.
Definition Caster.h:320
Definition Data.h:1473
std::ostream & operator<<(std::ostream &ostr, const PlainData< DT > &d)
Definition Data.h:631