GDCM 2.2.1

- Main Page
- Related Pages
- Namespaces
- Classes
- Files
- Examples

- File List
- File Members

gdcmElement.h
Go to the documentation of this file.
1 /*=========================================================================
2
3 Program: GDCM (Grassroots DICOM). A DICOM library
4
5 Copyright (c) 2006-2011 Mathieu Malaterre
6 All rights reserved.
7 See Copyright.txt or http://gdcm.sourceforge.net/Copyright.html for details.
8
9 This software is distributed WITHOUT ANY WARRANTY; without even
10 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
11 PURPOSE. See the above copyright notice for more information.
12
13 =========================================================================*/
14 #ifndef GDCMELEMENT_H
15 #define GDCMELEMENT_H
16
17 #include "gdcmTypes.h"
18 #include "gdcmVR.h"
19 #include "gdcmTag.h"
20 #include "gdcmVM.h"
21 #include "gdcmByteValue.h"
22 #include "gdcmDataElement.h"
23 #include "gdcmSwapper.h"
24
25 #include <string>
26 #include <vector>
27 #include <sstream>
28 #include <limits>
29 #include <cmath>
30 #include <cstring>
31
32 namespace gdcm
33 {
34
35 // Forward declaration
41 template<int T> class EncodingImplementation;
42
48 template<int TVR, int TVM>
49 class Element
50 {
51 public:
52 typename VRToType<TVR>::Type Internal[VMToLength<TVM>::Length];
53 typedef typename VRToType<TVR>::Type Type;
54
55 static VR GetVR() { return (VR::VRType)TVR; }
56 static VM GetVM() { return (VM::VMType)TVM; }
57
58 unsigned long GetLength() const {
59 return VMToLength<TVM>::Length;
60 }
61 // Implementation of Print is common to all Mode (ASCII/Binary)
62 // TODO: Can we print a \ when in ASCII...well I don't think so
63 // it would mean we used a bad VM then, right ?
64 void Print(std::ostream &_os) const {
65 _os << Internal[0]; // VM is at least garantee to be one
66 for(int i=1; i<VMToLength<TVM>::Length; ++i)
67 _os << "," << Internal[i];
68 }
69
70 const typename VRToType<TVR>::Type *GetValues() const {
71 return Internal;
72 }
73 const typename VRToType<TVR>::Type &GetValue(unsigned int idx = 0) const {
74 assert( idx < VMToLength<TVM>::Length );
75 return Internal[idx];
76 }
77 typename VRToType<TVR>::Type &GetValue(unsigned int idx = 0) {
78 assert( idx < VMToLength<TVM>::Length );
79 return Internal[idx];
80 }
81 typename VRToType<TVR>::Type operator[] (unsigned int idx) const {
82 return GetValue(idx);
83 }
84 void SetValue(typename VRToType<TVR>::Type v, unsigned int idx = 0) {
85 assert( idx < VMToLength<TVM>::Length );
86 Internal[idx] = v;
87 }
88
89 void SetFromDataElement(DataElement const &de) {
90 const ByteValue *bv = de.GetByteValue();
91 if( !bv ) return;
92 #ifdef GDCM_WORDS_BIGENDIAN
93 if( de.GetVR() == VR::UN /*|| de.GetVR() == VR::INVALID*/ )
94 #else
95 if( de.GetVR() == VR::UN || de.GetVR() == VR::INVALID )
96 #endif
97 {
98 Set(de.GetValue());
99 }
100 else
101 {
102 SetNoSwap(de.GetValue());
103 }
104 }
105
106 DataElement GetAsDataElement() const {
107 DataElement ret;
108 std::ostringstream os;
109 EncodingImplementation<VRToEncoding<TVR>::Mode>::Write(Internal,
110 GetLength(),os);
111 ret.SetVR( (VR::VRType)TVR );
112 assert( ret.GetVR() != VR::SQ );
113 if( (VR::VRType)VRToEncoding<TVR>::Mode == VR::VRASCII )
114 {
115 if( GetVR() != VR::UI )
116 {
117 if( os.str().size() % 2 )
118 {
119 os << " ";
120 }
121 }
122 }
123 VL::Type osStrSize = (VL::Type)os.str().size();
124 ret.SetByteValue( os.str().c_str(), osStrSize );
125
126 return ret;
127 }
128
129 void Read(std::istream &_is) {
130 return EncodingImplementation<VRToEncoding<TVR>::Mode>::Read(Internal,
131 GetLength(),_is);
132 }
133 void Write(std::ostream &_os) const {
134 return EncodingImplementation<VRToEncoding<TVR>::Mode>::Write(Internal,
135 GetLength(),_os);
136 }
137
138 // FIXME: remove this function
139 // this is only used in gdcm::SplitMosaicFilter / to pass value of a CSAElement
140 void Set(Value const &v) {
141 const ByteValue *bv = dynamic_cast<const ByteValue*>(&v);
142 if( bv ) {
143 //memcpy(Internal, bv->GetPointer(), bv->GetLength());
144 std::stringstream ss;
145 std::string s = std::string( bv->GetPointer(), bv->GetLength() );
146 ss.str( s );
147 EncodingImplementation<VRToEncoding<TVR>::Mode>::Read(Internal,
148 GetLength(),ss);
149 }
150 }
151 protected:
152 void SetNoSwap(Value const &v) {
153 const ByteValue *bv = dynamic_cast<const ByteValue*>(&v);
154 assert( bv ); // That would be bad...
155 //memcpy(Internal, bv->GetPointer(), bv->GetLength());
156 std::stringstream ss;
157 std::string s = std::string( bv->GetPointer(), bv->GetLength() );
158 ss.str( s );
159 EncodingImplementation<VRToEncoding<TVR>::Mode>::ReadNoSwap(Internal,
160 GetLength(),ss);
161 }
162 };
163
164 struct ignore_char {
165 ignore_char(char c): m_char(c) {}
166 char m_char;
167 };
168 ignore_char const backslash('\\');
169
170 inline std::istream& operator>> (std::istream& in, ignore_char const& ic) {
171 if (!in.eof())
172 in.clear(in.rdstate() & ~std::ios_base::failbit);
173 if (in.get() != ic.m_char)
174 in.setstate(std::ios_base::failbit);
175 return in;
176 }
177
178
179 // Implementation to perform formatted read and write
180 template<> class EncodingImplementation<VR::VRASCII> {
181 public:
182 template<typename T> // FIXME this should be VRToType<TVR>::Type
183 static inline void ReadComputeLength(T* data, unsigned int &length,
184 std::istream &_is) {
185 assert( data );
186 //assert( length ); // != 0
187 length = 0;
188 assert( _is );
189 #if 0
190 char sep;
191 while( _is >> data[length++] )
192 {
193 // Get the separator in between the values
194 assert( _is );
195 _is.get(sep);
196 assert( sep == '\\' || sep == ' ' ); // FIXME: Bad use of assert
197 if( sep == ' ' ) length--; // FIXME
198 }
199 #else
200 while( _is >> std::ws >> data[length++] >> std::ws >> backslash )
201 {
202 }
203 #endif
204 }
205
206 template<typename T> // FIXME this should be VRToType<TVR>::Type
207 static inline void Read(T* data, unsigned long length,
208 std::istream &_is) {
209 assert( data );
210 assert( length ); // != 0
211 assert( _is );
212 // FIXME BUG: what if >> operation fails ?
213 // gdcmData/MR00010001.dcm / SpacingBetweenSlices
214 _is >> std::ws >> data[0];
215 char sep;
216 //std::cout << "GetLength: " << af->GetLength() << std::endl;
217 for(unsigned long i=1; i<length;++i) {
218 assert( _is );
219 // Get the separator in between the values
220 _is >> std::ws >> sep; //_is.get(sep);
221 assert( sep == '\\' ); // FIXME: Bad use of assert
222 _is >> std::ws >> data[i];
223 }
224 }
225
226 template<typename T>
227 static inline void ReadNoSwap(T* data, unsigned long length,
228 std::istream &_is) {
229 Read(data,length,_is);
230 }
231 template<typename T>
232 static inline void Write(const T* data, unsigned long length,
233 std::ostream &_os) {
234 assert( data );
235 assert( length );
236 assert( _os );
237 _os << data[0];
238 for(unsigned long i=1; i<length; ++i) {
239 assert( _os );
240 _os << "\\" << data[i];
241 }
242 }
243 };
244
245 template < typename Float >
246 std::string to_string ( Float data ) {
247 std::stringstream in;
248 // in.imbue(std::locale::classic()); // This is not required AFAIK
249 int const digits =
250 static_cast< int >(
251 - std::log( std::numeric_limits<Float>::epsilon() )
252 / std::log( 10.0 ) );
253 if ( in << std::dec << std::setprecision(/*2+*/digits) << data ) {
254 return ( in.str() );
255 } else {
256 throw "Impossible Conversion"; // should not happen ...
257 }
258 }
259
260 /* Writing VR::DS is not that easy after all */
261 // http://groups.google.com/group/comp.lang.c++/browse_thread/thread/69ccd26f000a0802
262 template<> inline void EncodingImplementation<VR::VRASCII>::Write(const float * data, unsigned long length, std::ostream &_os) {
263 assert( data );
264 assert( length );
265 assert( _os );
266 _os << to_string(data[0]);
267 for(unsigned long i=1; i<length; ++i) {
268 assert( _os );
269 _os << "\\" << to_string(data[i]);
270 }
271 }
272
273 template<> inline void EncodingImplementation<VR::VRASCII>::Write(const double* data, unsigned long length, std::ostream &_os) {
274 assert( data );
275 assert( length );
276 assert( _os );
277 _os << to_string(data[0]);
278 for(unsigned long i=1; i<length; ++i) {
279 assert( _os );
280 _os << "\\" << to_string(data[i]);
281 }
282 }
283
284
285 // Implementation to perform binary read and write
286 // TODO rewrite operation so that either:
287 // #1. dummy implementation use a pointer to Internal and do ++p (faster)
288 // #2. Actually do some meta programming to unroll the loop
289 // (no notion of order in VM ...)
290 template<> class EncodingImplementation<VR::VRBINARY> {
291 public:
292 template<typename T> // FIXME this should be VRToType<TVR>::Type
293 static inline void ReadComputeLength(T* data, unsigned int &length,
294 std::istream &_is) {
295 const unsigned int type_size = sizeof(T);
296 assert( data ); // Can we read from pointer ?
297 //assert( length );
298 length /= type_size;
299 assert( _is ); // Is stream valid ?
300 _is.read( reinterpret_cast<char*>(data+0), type_size);
301 for(unsigned long i=1; i<length; ++i) {
302 assert( _is );
303 _is.read( reinterpret_cast<char*>(data+i), type_size );
304 }
305 }
306 template<typename T>
307 static inline void ReadNoSwap(T* data, unsigned long length,
308 std::istream &_is) {
309 const unsigned int type_size = sizeof(T);
310 assert( data ); // Can we read from pointer ?
311 assert( length );
312 assert( _is ); // Is stream valid ?
313 _is.read( reinterpret_cast<char*>(data+0), type_size);
314 for(unsigned long i=1; i<length; ++i) {
315 assert( _is );
316 _is.read( reinterpret_cast<char*>(data+i), type_size );
317 }
318 //ByteSwap<T>::SwapRangeFromSwapCodeIntoSystem(data,
319 // _is.GetSwapCode(), length);
320 //SwapperNoOp::SwapArray(data,length);
321 }
322 template<typename T>
323 static inline void Read(T* data, unsigned long length,
324 std::istream &_is) {
325 const unsigned int type_size = sizeof(T);
326 assert( data ); // Can we read from pointer ?
327 assert( length );
328 assert( _is ); // Is stream valid ?
329 _is.read( reinterpret_cast<char*>(data+0), type_size);
330 for(unsigned long i=1; i<length; ++i) {
331 assert( _is );
332 _is.read( reinterpret_cast<char*>(data+i), type_size );
333 }
334 //ByteSwap<T>::SwapRangeFromSwapCodeIntoSystem(data,
335 // _is.GetSwapCode(), length);
336 SwapperNoOp::SwapArray(data,length);
337 }
338 template<typename T>
339 static inline void Write(const T* data, unsigned long length,
340 std::ostream &_os) {
341 const unsigned int type_size = sizeof(T);
342 assert( data ); // Can we write into pointer ?
343 assert( length );
344 assert( _os ); // Is stream valid ?
345 //ByteSwap<T>::SwapRangeFromSwapCodeIntoSystem((T*)data,
346 // _os.GetSwapCode(), length);
347 T swappedData = SwapperNoOp::Swap(data[0]);
348 _os.write( reinterpret_cast<const char*>(&swappedData), type_size);
349 for(unsigned long i=1; i<length;++i) {
350 assert( _os );
351 swappedData = SwapperNoOp::Swap(data[i]);
352 _os.write( reinterpret_cast<const char*>(&swappedData), type_size );
353 }
354 //ByteSwap<T>::SwapRangeFromSwapCodeIntoSystem((T*)data,
355 // _os.GetSwapCode(), length);
356 }
357 };
358
359 // For particular case for ASCII string
360 // WARNING: This template explicitely instanciates a particular
361 // EncodingImplementation THEREFORE it is required to be declared after the
362 // EncodingImplementation is needs (doh!)
363 #if 0
364 template<int TVM>
365 class Element<TVM>
366 {
367 public:
368 Element(const char array[])
369 {
370 unsigned int i = 0;
371 const char sep = '\\';
372 std::string sarray = array;
373 std::string::size_type pos1 = 0;
374 std::string::size_type pos2 = sarray.find(sep, pos1+1);
375 while(pos2 != std::string::npos)
376 {
377 Internal[i++] = sarray.substr(pos1, pos2-pos1);
378 pos1 = pos2+1;
379 pos2 = sarray.find(sep, pos1+1);
380 }
381 Internal[i] = sarray.substr(pos1, pos2-pos1);
382 // Shouldn't we do the contrary, since we know how many separators
383 // (and default behavior is to discard anything after the VM declared
384 assert( GetLength()-1 == i );
385 }
386
387 unsigned long GetLength() const {
388 return VMToLength<TVM>::Length;
389 }
390 // Implementation of Print is common to all Mode (ASCII/Binary)
391 void Print(std::ostream &_os) const {
392 _os << Internal[0]; // VM is at least garantee to be one
393 for(int i=1; i<VMToLength<TVM>::Length; ++i)
394 _os << "," << Internal[i];
395 }
396
397 void Read(std::istream &_is) {
398 EncodingImplementation<VR::VRASCII>::Read(Internal, GetLength(),_is);
399 }
400 void Write(std::ostream &_os) const {
401 EncodingImplementation<VR::VRASCII>::Write(Internal, GetLength(),_os);
402 }
403 private:
404 typename String Internal[VMToLength<TVM>::Length];
405 };
406
407 template< int TVM>
408 class Element<VR::PN, TVM> : public StringElement<TVM>
409 {
410 };
411 #endif
412
413 // Implementation for the undefined length (dynamically allocated array)
414 template<int TVR>
415 class Element<TVR, VM::VM1_n>
416 {
417 public:
418 // This the way to prevent default initialization
419 explicit Element() { Internal=0; Length=0; Save = false; }
420 ~Element() {
421 if( Save ) {
422 delete[] Internal;
423 }
424 Internal = 0;
425 }
426
427 static VR GetVR() { return (VR::VRType)TVR; }
428 static VM GetVM() { return VM::VM1_n; }
429
430 // Length manipulation
431 // SetLength should really be protected anyway...all operation
432 // should go through SetArray
433 unsigned long GetLength() const { return Length; }
434 typedef typename VRToType<TVR>::Type Type;
435
436 void SetLength(unsigned long len) {
437 const unsigned int size = sizeof(Type);
438 if( len ) {
439 if( len > Length ) {
440 // perform realloc
441 assert( (len / size) * size == len );
442 Type *internal = new Type[len / size];
443 assert( Save == false );
444 Save = true; // ????
445 if( Internal )
446 {
447 memcpy(internal, Internal, len);
448 delete[] Internal;
449 }
450 Internal = internal;
451 }
452 }
453 Length = len / size;
454 }
455
456 // If save is set to zero user should not delete the pointer
457 //void SetArray(const typename VRToType<TVR>::Type *array, int len, bool save = false)
458 void SetArray(const Type *array, unsigned long len,
459 bool save = false) {
460 if( save ) {
461 SetLength(len); // realloc
462 memcpy(Internal, array, len/*/sizeof(Type)*/);
463 assert( Save == false );
464 }
465 else {
466 // TODO rewrite this stupid code:
467 assert( Length == 0 );
468 assert( Internal == 0 );
469 assert( Save == false );
470 Length = len / sizeof(Type);
471 //assert( (len / sizeof(Type)) * sizeof(Type) == len );
472 // MR00010001.dcm is a tough kid: 0019,105a is supposed to be VR::FL, VM::VM3 but
473 // length is 14 bytes instead of 12 bytes. Simply consider value is total garbage.
474 if( (len / sizeof(Type)) * sizeof(Type) != len ) { Internal = 0; Length = 0; }
475 else Internal = const_cast< Type*>(array);
476 }
477 Save = save;
478 }
479 void SetValue(typename VRToType<TVR>::Type v, unsigned int idx = 0) {
480 assert( idx < Length );
481 Internal[idx] = v;
482 }
483 const typename VRToType<TVR>::Type &GetValue(unsigned int idx = 0) const {
484 assert( idx < Length );
485 return Internal[idx];
486 }
487 typename VRToType<TVR>::Type &GetValue(unsigned int idx = 0) {
488 assert( idx < Length );
489 return Internal[idx];
490 }
491 typename VRToType<TVR>::Type operator[] (unsigned int idx) const {
492 return GetValue(idx);
493 }
494 void Set(Value const &v) {
495 const ByteValue *bv = dynamic_cast<const ByteValue*>(&v);
496 assert( bv ); // That would be bad...
497 if( (VR::VRType)(VRToEncoding<TVR>::Mode) == VR::VRBINARY )
498 {
499 const Type* array = (Type*)bv->GetPointer();
500 if( array ) {
501 assert( array ); // That would be bad...
502 assert( Internal == 0 );
503 SetArray(array, bv->GetLength() ); }
504 }
505 else
506 {
507 std::stringstream ss;
508 std::string s = std::string( bv->GetPointer(), bv->GetLength() );
509 ss.str( s );
510 EncodingImplementation<VRToEncoding<TVR>::Mode>::Read(Internal,
511 GetLength(),ss);
512 }
513 }
514 void SetFromDataElement(DataElement const &de) {
515 const ByteValue *bv = de.GetByteValue();
516 if( !bv ) return;
517 #ifdef GDCM_WORDS_BIGENDIAN
518 if( de.GetVR() == VR::UN /*|| de.GetVR() == VR::INVALID*/ )
519 #else
520 if( de.GetVR() == VR::UN || de.GetVR() == VR::INVALID )
521 #endif
522 {
523 Set(de.GetValue());
524 }
525 else
526 {
527 SetNoSwap(de.GetValue());
528 }
529 }
530
531
532 // Need to be placed after definition of EncodingImplementation<VR::VRASCII>
533 void WriteASCII(std::ostream &os) const {
534 return EncodingImplementation<VR::VRASCII>::Write(Internal, GetLength(), os);
535 }
536
537 // Implementation of Print is common to all Mode (ASCII/Binary)
538 void Print(std::ostream &_os) const {
539 assert( Length );
540 assert( Internal );
541 _os << Internal[0]; // VM is at least garantee to be one
542 const unsigned long length = GetLength() < 25 ? GetLength() : 25;
543 for(unsigned long i=1; i<length; ++i)
544 _os << "," << Internal[i];
545 }
546 void Read(std::istream &_is) {
547 if( !Internal ) return;
548 EncodingImplementation<VRToEncoding<TVR>::Mode>::Read(Internal,
549 GetLength(),_is);
550 }
551 //void ReadComputeLength(std::istream &_is) {
552 // if( !Internal ) return;
553 // EncodingImplementation<VRToEncoding<TVR>::Mode>::ReadComputeLength(Internal,
554 // Length,_is);
555 // }
556 void Write(std::ostream &_os) const {
557 EncodingImplementation<VRToEncoding<TVR>::Mode>::Write(Internal,
558 GetLength(),_os);
559 }
560
561 DataElement GetAsDataElement() const {
562 DataElement ret;
563 ret.SetVR( (VR::VRType)TVR );
564 assert( ret.GetVR() != VR::SQ );
565 if( Internal )
566 {
567 std::ostringstream os;
568 EncodingImplementation<VRToEncoding<TVR>::Mode>::Write(Internal,
569 GetLength(),os);
570 if( (VR::VRType)VRToEncoding<TVR>::Mode == VR::VRASCII )
571 {
572 if( GetVR() != VR::UI )
573 {
574 if( os.str().size() % 2 )
575 {
576 os << " ";
577 }
578 }
579 }
580 VL::Type osStrSize = (VL::Type)os.str().size();
581 ret.SetByteValue( os.str().c_str(), osStrSize );
582 }
583 return ret;
584 }
585
586 Element(const Element&_val) {
587 if( this != &_val) {
588 *this = _val;
589 }
590 }
591
592 Element &operator=(const Element &_val) {
593 Length = 0; // SYITF
594 Internal = 0;
595 SetArray(_val.Internal, _val.Length, true);
596 return *this;
597 }
598 protected:
599 void SetNoSwap(Value const &v) {
600 const ByteValue *bv = dynamic_cast<const ByteValue*>(&v);
601 assert( bv ); // That would be bad...
602 if( (VR::VRType)(VRToEncoding<TVR>::Mode) == VR::VRBINARY )
603 {
604 const Type* array = (Type*)bv->GetPointer();
605 if( array ) {
606 assert( array ); // That would be bad...
607 assert( Internal == 0 );
608 SetArray(array, bv->GetLength() ); }
609 }
610 else
611 {
612 std::stringstream ss;
613 std::string s = std::string( bv->GetPointer(), bv->GetLength() );
614 ss.str( s );
615 EncodingImplementation<VRToEncoding<TVR>::Mode>::ReadNoSwap(Internal,
616 GetLength(),ss);
617 }
618 }
619
620 private:
621 typename VRToType<TVR>::Type *Internal;
622 unsigned long Length; // unsigned int ??
623 bool Save;
624 };
625
626 //template <int TVM = VM::VM1_n>
627 //class Element<VR::OB, TVM > : public Element<VR::OB, VM::VM1_n> {};
628
629 // Partial specialization for derivatives of 1-n : 2-n, 3-n ...
630 template<int TVR>
631 class Element<TVR, VM::VM1_2> : public Element<TVR, VM::VM1_n>
632 {
633 public:
634 typedef Element<TVR, VM::VM1_n> Parent;
635 void SetLength(int len) {
636 if( len != 1 || len != 2 ) return;
637 Parent::SetLength(len);
638 }
639 };
640 template<int TVR>
641 class Element<TVR, VM::VM2_n> : public Element<TVR, VM::VM1_n>
642 {
643 public:
644 typedef Element<TVR, VM::VM1_n> Parent;
645 void SetLength(int len) {
646 if( len <= 1 ) return;
647 Parent::SetLength(len);
648 }
649 };
650 template<int TVR>
651 class Element<TVR, VM::VM2_2n> : public Element<TVR, VM::VM2_n>
652 {
653 public:
654 typedef Element<TVR, VM::VM2_n> Parent;
655 void SetLength(int len) {
656 if( len % 2 ) return;
657 Parent::SetLength(len);
658 }
659 };
660 template<int TVR>
661 class Element<TVR, VM::VM3_n> : public Element<TVR, VM::VM1_n>
662 {
663 public:
664 typedef Element<TVR, VM::VM1_n> Parent;
665 void SetLength(int len) {
666 if( len <= 2 ) return;
667 Parent::SetLength(len);
668 }
669 };
670 template<int TVR>
671 class Element<TVR, VM::VM3_3n> : public Element<TVR, VM::VM3_n>
672 {
673 public:
674 typedef Element<TVR, VM::VM3_n> Parent;
675 void SetLength(int len) {
676 if( len % 3 ) return;
677 Parent::SetLength(len);
678 }
679 };
680
681
682 //template<int T> struct VRToLength;
683 //template <> struct VRToLength<VR::AS>
684 //{ enum { Length = VM::VM1 }; }
685 //template<>
686 //class Element<VR::AS> : public Element<VR::AS, VRToLength<VR::AS>::Length >
687
688 // only 0010 1010 AS 1 Patient's Age
689 template<>
690 class Element<VR::AS, VM::VM5>
691 {
692 public:
693 char Internal[VMToLength<VM::VM5>::Length * sizeof( VRToType<VR::AS>::Type )];
694 void Print(std::ostream &_os) const {
695 _os << Internal;
696 }
697 unsigned long GetLength() const {
698 return VMToLength<VM::VM5>::Length;
699 }
700 };
701
702 template <>
703 class Element<VR::OB, VM::VM1> : public Element<VR::OB, VM::VM1_n> {};
704 // Make it impossible to compile any other cases:
705 template <int TVM> class Element<VR::OB, TVM>;
706
707 // Same for OW:
708 template <>
709 class Element<VR::OW, VM::VM1> : public Element<VR::OW, VM::VM1_n> {};
710 // Make it impossible to compile any other cases:
711 template <int TVM> class Element<VR::OW, TVM>;
712
713 } // namespace gdcm
714
715 #endif //GDCMELEMENT_H