mxlib
c++ tools for analyzing astronomical data and other tasks by Jared R. Males. [git repo]
Loading...
Searching...
No Matches
fitsHeaderCard.hpp
Go to the documentation of this file.
1/** \file fitsHeaderCard.hpp
2 * \brief A class to work with a FITS header card
3 * \ingroup fits_processing_files
4 * \author Jared R. Males (jaredmales@gmail.com)
5 *
6 */
7
8//***********************************************************************//
9// Copyright 2015-2025 Jared R. Males (jaredmales@gmail.com)
10//
11// This file is part of mxlib.
12//
13// mxlib is free software: you can redistribute it and/or modify
14// it under the terms of the GNU General Public License as published by
15// the Free Software Foundation, either version 3 of the License, or
16// (at your option) any later version.
17//
18// mxlib is distributed in the hope that it will be useful,
19// but WITHOUT ANY WARRANTY; without even the implied warranty of
20// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21// GNU General Public License for more details.
22//
23// You should have received a copy of the GNU General Public License
24// along with mxlib. If not, see <http://www.gnu.org/licenses/>.
25//***********************************************************************//
26
27#ifndef ioutils_fits_fitsHeaderCard_hpp
28#define ioutils_fits_fitsHeaderCard_hpp
29
30#include <format>
31
32#include "../../mxlib.hpp"
33
34#include "../../meta/tagT.hpp"
35#include "fitsUtils.hpp"
36
37namespace mx
38{
39namespace fits
40{
41
42/// \cond
43// strip leading and trailing whitespace and then opening and closing ''. leaves spaces between ''.
44inline void stripApostWS( std::string &str )
45{
46 if( str.size() == 0 )
47 {
48 return;
49 }
50
51 if( str[0] != '\'' && str[0] != ' ' && str.back() != ' ' ) // get out fast if we can
52 {
53 return;
54 }
55
56 // strip white space at front
57 size_t ns = str.find_first_not_of( " \t\r\n" );
58 if( ns != std::string::npos && ns != 0 )
59 {
60 str.erase( 0, ns );
61
62 if( str.size() == 0 )
63 {
64 return;
65 }
66 }
67 else if( ns == std::string::npos ) // the rare all spaces
68 {
69 str = "";
70 return;
71 }
72
73 // strip white space at back
74 ns = str.find_last_not_of( " \t\r\n" );
75 if( ns != std::string::npos && ns != str.size() - 1 )
76 {
77 str.erase( ns + 1 );
78
79 if( str.size() == 0 )
80 {
81 return;
82 }
83 }
84
85 if( str[0] == '\'' && str.back() == '\'' )
86 {
87 if( str.size() == 1 || str.size() == 2 )
88 {
89 str = "";
90 return;
91 }
92 str.erase( str.size() - 1, 1 );
93 str.erase( 0, 1 );
94 }
95}
96/// \endcond
97
98/// Class to manage the three components of a FITS header card
99/** Since FITS does not provide the type in keyword=value pairs in a FITS header, it is up to the user
100 * to determine the type. Furthermore, since we want to read values from files, type conversions must
101 * be done at runtime. The result is that we must be able to accept a string, which is converted
102 * to a given type on demand as determined at runtime.
103 *
104 * Conversion from string to native type, or vice versa, only occurs when needed. So if you set the value to,
105 * say, a double, the value is not converted to string format unless specifically requested. If the write function is
106 * called when in this state, the cfitsio routine is called directly. This conversion only on demand is most important
107 * for values read from a file, then written to another file. In this case, no conversion to its double (etc)
108 * representation occurs.
109 *
110 * Note that because of the checks to determine the type and appropriate return values, accessing the value in a card
111 * is possibly slower than accessing a variable due to various if statements and error checking.
112 * This means that you should typically do
113 * so once and use a local variable for repeated use.
114 *
115 * \ingroup fits_processing
116 */
117template <class verboseT = verbose::d>
119{
120
121 protected:
122 /// The keyword
123 std::string m_keyword;
124
125 /// The FITS type of the value, and indicates which member of m_values to access.
126 int m_type{ fitsType<fitsUnknownType>() };
127
128 /// The native type is held in a union.
129 union values
130 {
131 bool Bool; ///< the bool value
132 char Char; ///< the char value
133 unsigned char UChar; ///< the unsigned char value
134 short Short; ///< the short value
135 unsigned short UShort; ///< the unsigned short value
136 int Int; ///< the int value
137 unsigned int UInt; ///< the unsigned int value
138 long Long; ///< the long value
139 unsigned long ULong; ///< the unsigned long value
140 long long LongLong; ///< the long long value
141 unsigned long long ULongLong; ///< the unsigned long long value
142 float Float; ///< the float value
143 std::complex<float> complexFloat; ///< the std::complex<float> value
144 double Double; ///< the double value
145 std::complex<double> complexDouble; ///< the std::complex<double> value
146 long double LongDouble; ///< the long double value
147 std::complex<long double> complexLongDouble; ///< the std::complex<long double> value
148
149 // clang-format off
150 #ifdef HAS_QUAD
151 __float128 Quad; ///< the float128 value
152 std::complex<__float128> complexQuad; ///< the std::complex<__float128> value
153 #endif
154 // clang-format on
155
156 /// c'tor. have to specify due to inclusion of std::complex types.
158 {
159 return;
160 }
161
162 bool &member( meta::tagT<bool> )
163 {
164 return Bool;
165 }
166
167 char &member( meta::tagT<char> )
168 {
169 return Char;
170 }
171
172 unsigned char &member( meta::tagT<unsigned char> )
173 {
174 return UChar;
175 }
176
177 short &member( meta::tagT<short> )
178 {
179 return Short;
180 }
181
182 unsigned short &member( meta::tagT<unsigned short> )
183 {
184 return UShort;
185 }
186
187 int &member( meta::tagT<int> )
188 {
189 return Int;
190 }
191
192 unsigned int &member( meta::tagT<unsigned int> )
193 {
194 return UInt;
195 }
196
197 long &member( meta::tagT<long> )
198 {
199 return Long;
200 }
201
202 unsigned long &member( meta::tagT<unsigned long> )
203 {
204 return ULong;
205 }
206
207 long long &member( meta::tagT<long long> )
208 {
209 return LongLong;
210 }
211
212 unsigned long long &member( meta::tagT<unsigned long long> )
213 {
214 return ULongLong;
215 }
216
217 float &member( meta::tagT<float> )
218 {
219 return Float;
220 }
221
222 std::complex<float> &member( meta::tagT<std::complex<float>> )
223 {
224 return complexFloat;
225 }
226
227 double &member( meta::tagT<double> )
228 {
229 return Double;
230 }
231
232 std::complex<double> &member( meta::tagT<std::complex<double>> )
233 {
234 return complexDouble;
235 }
236
237 long double &member( meta::tagT<long double> )
238 {
239 return LongDouble;
240 }
241
242 std::complex<long double> &member( meta::tagT<std::complex<long double>> )
243 {
244 return complexLongDouble;
245 }
246
247 // clang-format off
248 #ifdef HAS_QUAD
249 __float128 &member( meta::tagT<__float128> )
250 {
251 return Quad;
252 }
253
254 std::complex<__float128> &member( meta::tagT<std::complex<__float128>> )
255 {
256 return complexQuad;
257 }
258 #endif
259 // clang-format on
260
261 template <typename typeT>
262 typeT &member()
263 {
264 return member( meta::tagT<typeT>() );
265 }
266
267 } m_value;
268
269 /// The value in string form
270 std::stringstream m_valueStr;
271
272 bool m_valueGood{ false }; ///< Flag indicating if the value is valid
273 bool m_valueStrGood{ false }; ///< Flag indicating if the value string is valid
274
275 /// The comment
276 std::string m_comment;
277
278 public:
279 /// \name Constructors
280 /**
281 */
282 //@{
283
284 /// Basic c'tor
286
287 /// Construct from the three components for a value of string type
288 /**
289 */
290 fitsHeaderCard( const std::string &k, ///< [in] the keyword
291 const std::string &v, ///< [in] the value string
292 const std::string &c = "" ///< [in] the comment
293 );
294
295 /// Construct from the three components for a value of string type
296 /** Have to provide overload for char * to avoid template version
297 */
298 fitsHeaderCard( const std::string &k, ///< [in] the keyword
299 char *v, ///< [in] the value string
300 const std::string &c = "" ///< [in] the comment
301 );
302
303 /// Construct from the three components for a value of string type
304 /** Have to provide overload for const char * to avoid template version
305 */
306 fitsHeaderCard( const std::string &k, ///< [in] the keyword
307 const char *v, ///< [in] the value string
308 const std::string &c = "" ///< [in] the comment
309 );
310
311 /// Construct from the three components, when already in a string format
312 /** Use this when the value is not a string
313 */
314 fitsHeaderCard( const std::string &k, ///< [in] the keyword
315 const std::string &v, ///< [in] the value string
316 const int &type, ///< [in] the type of the value
317 const std::string &c = "" ///< [in] the comment
318 );
319
320 /// Construct from the three components, when it's really a comment card
321 /** This overload is provided to facilitate handling of comments when re-writing the file.
322 *
323 */
324 fitsHeaderCard( const std::string &k, ///< [in] the keyword
325 fitsCommentType v, ///< [in] an object of type fitsCommentType
326 const std::string &c ///< [in] the comment
327 );
328
329 /// Construct from the three components, when it's really a history card
330 /** This overload is provided to facilitate handling of history when re-writing the file.
331 *
332 */
333 fitsHeaderCard( const std::string &k, ///< [in] the keyword
334 fitsHistoryType v, ///< [in] an object of type fitsHistoryType
335 const std::string &c ///< [in] the comment
336 );
337
338 /// Construct from just keyword, when value's type is unknown
339 /**
340 */
341 explicit fitsHeaderCard( const std::string &k /**< [in] the keyword*/ );
342
343 /// Construct from just keyword, when value's type known
344 /**
345 */
346 fitsHeaderCard( const std::string &k, ///< [in] the keyword
347 const int type ///< [in] the type
348 );
349
350 /// Construct from the three components for a char.
351 /**
352 */
353 template <typename typeT>
354 fitsHeaderCard( const std::string &k, ///< [in] they keyword
355 const typeT v, ///< [in] the value
356 const std::string &c = "" ///< [in] the comment
357 );
358
359 /// Copy constructor
361
362 //@}
363
364 /// Assignment
366
367 protected:
368 ///\name Converters
369 /** @{
370 */
371
372 /// Convert from the type to a string.
373 /** This populates m_valueStr and sets m_valueStrGood so that this conversion
374 * only occurs once.
375 */
377
378 /// Convert from string to the type
379 /** This populates the appropriate union field and sets m_valueGood so that
380 * this conversion only occurs once.
381 */
382 template <typename typeT>
384
385 /// Get the value from its type converted to a different type.
386 template <typename typeT>
387 error_t convertedValue( typeT &cval /**< [out] the converted value */ );
388
389 /// Convert the value from its type to a different type.
390 error_t convertValue( int newtype /**< [in] the new type */ );
391
392 ///@}
393
394 public:
395 ///\name Accessors
396 /** @{
397 */
398
399 /// Get the keyword
400 /**
401 * \returns a const reference to m_keyword
402 */
403 const std::string &keyword() const;
404
405 /// Set the keyword
406 /**
407 * \returns error_t::noerror on success
408 */
409 error_t keyword( const std::string &kw /**< [in] the new keyword */ );
410
411 /// Get the type
412 /**
413 * \returns the value of m_type
414 */
415 int type() const;
416
417 /// Set the type
418 /** If this is a change in type and the native type is set in m_value (indicated by m_valueGood == true)
419 * then it is converted to the new type. Otherwise, no conversion occurs.
420 *
421 * \returns error_t::noerror on success
422 */
423 error_t type( const int &t /**< [in] the new type */ );
424
425 protected:
426 /** \name tag dispatching for getting value
427 * @{
428 */
429
430 /// Get value for anything not a string
431 template <typename typeT>
433
434 /// Get value tag dispatcher for std::string
435 /**
436 */
438
439 /// Get value tag dispatcher for char
440 /** Calls valueNonString
441 */
443
444 /// Get value tag dispatcher for unsigned char
445 /** Calls valueNonString
446 */
448
449 /// Get value tag dispatcher for short
450 /** Calls valueNonString
451 */
453
454 /// Get value tag dispatcher for unsigned short
455 /** Calls valueNonString
456 */
458
459 /// Get value tag dispatcher for int
460 /** Calls valueNonString
461 */
463
464 /// Get value tag dispatcher for unsigned int
465 /** Calls valueNonString
466 */
468
469 /// Get value tag dispatcher for long
470 /** Calls valueNonString
471 */
473
474 /// Get value tag dispatcher for unsigned long
475 /** Calls valueNonString
476 */
478
479 /// Get value tag dispatcher for long long
480 /** Calls valueNonString
481 */
483
484 /// Get value tag dispatcher for unsigned long long
485 /** Calls valueNonString
486 */
488
489 /// Get value tag dispatcher for float
490 /** Calls valueNonString
491 */
493
494 /// Get value tag dispatcher for double
495 /** Calls valueNonString
496 */
498
499 ///@}
500
501 public:
502 /// Get the value
503 /** Returns the value as typeT. Conversions occur
504 * automatically if necessary.
505 *
506 * \returns the value converted to typeT as necessary
507 *
508 * \b Errors
509 * - mx::error_t::noerror on success
510 * - other errors possible due to conversions
511 *
512 */
513 template <typename typeT>
514 typeT value( mx::error_t *errc = nullptr /**< [in] [optional] error code */ );
515
516 /// Get the value as a string
517 /** This calls value<string>().
518 *
519 * \returns the value converted to string as necessary
520 *
521 */
522 std::string String( error_t *errc = nullptr /**< [in] [optional] error code */ );
523
524 /// Get the value as a char
525 /** This calls value<char>().
526 *
527 * \returns the value converted to char as necessary
528 *
529 */
530 char Char( error_t *errc = nullptr /**< [in] [optional] error code */ );
531
532 /// Get the value as an unsigned char
533 /** This calls value<unsigned char>().
534 *
535 * \returns the value converted to unsigned char as necessary
536 *
537 */
538 unsigned char UChar( error_t *errc = nullptr /**< [in] [optional] error code */ );
539
540 /// Get the value as a short
541 /** This calls value<short>().
542 *
543 * \returns the value converted to short as necessary
544 *
545 */
546 short Short( error_t *errc = nullptr /**< [in] [optional] error code */ );
547
548 /// Get the value as an unsigned short
549 /** This calls value<unsigned short>().
550 *
551 * \returns the value converted to unsigned short as necessary
552 *
553 */
554 unsigned short UShort( error_t *errc = nullptr /**< [in] [optional] error code */ );
555
556 /// Get the value as a int
557 /** This calls value<int>().
558 *
559 * \returns the value converted to int as necessary
560 *
561 */
562 int Int( error_t *errc = nullptr /**< [in] [optional] error code */ );
563
564 /// Get the value as an unsigned int
565 /** This calls value<unsigned int>().
566 *
567 * \returns the value converted to unsigned int as necessary
568 *
569 */
570 unsigned int UInt( error_t *errc = nullptr /**< [in] [optional] error code */ );
571
572 /// Get the value as a long
573 /** This calls value<long>().
574 *
575 * \returns the value converted to long as necessary
576 *
577 */
578 long Long( error_t *errc = nullptr /**< [in] [optional] error code */ );
579
580 /// Get the value as an unsigned long
581 /** This calls value<unsigned long>().
582 *
583 * \returns the value converted to unsigned long as necessary
584 *
585 */
586 unsigned long ULong( error_t *errc = nullptr /**< [in] [optional] error code */ );
587
588 /// Get the value as a long long
589 /** This calls value<long long>().
590 *
591 * \returns the value converted to long long as necessary
592 *
593 */
594 long long LongLong( error_t *errc = nullptr /**< [in] [optional] error code */ );
595
596 /// Get the value as an unsigned long long
597 /** This calls value<unsigned long long>().
598 *
599 * \returns the value converted to unsigned long long as necessaryvalue(
600 *
601 */
602 unsigned long long ULongLong( error_t *errc = nullptr /**< [in] [optional] error code */ );
603
604 /// Get the value as a float
605 /** This calls value<float>().
606 *
607 * \returns the value converted to float as necessary
608 *
609 */
610 float Float( error_t *errc = nullptr /**< [in] [optional] error code */ );
611
612 /// Get the value as a std::complex<float>
613 /** This calls value<std::complex<float>>().
614 *
615 * \returns the value converted to std::complex<float> as necessary
616 *
617 */
618 std::complex<float> complexFloat( error_t *errc = nullptr /**< [in] [optional] error code */ );
619
620 /// Get the value as a double
621 /** This calls value<double>().
622 *
623 * \returns the value converted to double as necessary
624 *
625 */
626 double Double( error_t *errc = nullptr /**< [in] [optional] error code */ );
627
628 /// Get the value as a std::complex<double>
629 /** This calls value<std::complex<double>>().
630 *
631 * \returns the value converted to std::complex<double> as necessary
632 *
633 */
634 std::complex<double> complexDouble( error_t *errc = nullptr /**< [in] [optional] error code */ );
635
636 /// Set the value to a char * string
637 error_t value( const char *v /**< [in] a character string*/ );
638
639 /// Set the value to a std::string
640 /**
641 */
642 error_t value( const std::string &v /**< [in] a std::string*/ );
643
644 /// Set the value for a non-string type
645 template <typename typeT>
646 mx::error_t value( const typeT &v /**< [in] the value to set */ );
647
648 /// Get the current value string
649 /**
650 * \returns m_valueStr;
651 */
652 std::string valueStr();
653
654 /// Get the current value good flag
655 /**
656 * \returns m_valueGood;
657 */
658 bool valueGood();
659
660 /// Get the current value string good flag
661 /**
662 * \returns m_valueStrGood;
663 */
665
666 /// Get the comment
667 /** \returns the value of m_comment
668 */
669 const std::string &comment();
670
671 /// Set the comment
672 /**
673 * \returns error_t::noerror on success
674 */
675 error_t comment( const std::string &c /**< [in] the new comment */ );
676
677 //@}
678
679 error_t appendContinue( const fitsHeaderCard & card);
680
681 ///\name Output
682 /**
683 */
684 //@{
685
686 /// Writes this card to a FITS file, using \ref mx::improc::fits_write_key<typename typeT>(fitsfile * fptr, char *
687 /// keyword, void * value, char * comment).
688 /**
689 */
690 mx::error_t write( fitsfile *fptr );
691
692 //@}
693
694}; // fitsHeaderCard
695
696template <class verboseT>
700
701template <class verboseT>
702fitsHeaderCard<verboseT>::fitsHeaderCard( const std::string &k, const std::string &v, const std::string &c )
703{
704 m_keyword = k;
705
706 std::string str = v;
707 stripApostWS( str );
708 m_valueStr.str( str );
709 m_valueGood = false;
710 m_valueStrGood = true;
711 m_type = fitsType<std::string>();
712 m_comment = c;
713}
714
715template <class verboseT>
716fitsHeaderCard<verboseT>::fitsHeaderCard( const std::string &k, char *v, const std::string &c )
717{
718 m_keyword = k;
719 std::string str = v;
720 stripApostWS( str );
721 m_valueStr.str( str );
722 m_valueGood = false;
723 m_valueStrGood = true;
724 m_type = fitsType<std::string>();
725 m_comment = c;
726}
727
728template <class verboseT>
729fitsHeaderCard<verboseT>::fitsHeaderCard( const std::string &k, const char *v, const std::string &c )
730{
731 m_keyword = k;
732 std::string str = v;
733 stripApostWS( str );
734 m_valueStr.str( str );
735 m_valueGood = false;
736 m_valueStrGood = true;
737 m_type = fitsType<std::string>();
738 m_comment = c;
739}
740
741template <class verboseT>
743 const std::string &v,
744 const int &type,
745 const std::string &c )
746{
747 m_keyword = k;
748 std::string str = v;
749 stripApostWS( str );
750 m_valueStr.str( str );
751 m_valueGood = false;
752 m_valueStrGood = true;
753 m_type = type;
754 m_comment = c;
755}
756
757template <class verboseT>
758fitsHeaderCard<verboseT>::fitsHeaderCard( const std::string &k, fitsCommentType v, const std::string &c )
759{
760 m_keyword = k;
761 m_valueGood = false;
762 m_valueStrGood = false;
763 m_type = fitsType<fitsCommentType>();
764 m_comment = c;
765}
766
767template <class verboseT>
768fitsHeaderCard<verboseT>::fitsHeaderCard( const std::string &k, fitsHistoryType v, const std::string &c )
769{
770 m_keyword = k;
771 m_valueGood = false;
772 m_valueStrGood = false;
773 m_type = fitsType<fitsHistoryType>();
774 m_comment = c;
775}
776
777template <class verboseT>
779{
780 m_keyword = k;
781}
782
783template <class verboseT>
784fitsHeaderCard<verboseT>::fitsHeaderCard( const std::string &k, const int type )
785{
786 m_keyword = k;
787 m_type = type;
788}
789
790template <class verboseT>
791template <typename typeT>
792fitsHeaderCard<verboseT>::fitsHeaderCard( const std::string &k, const typeT v, const std::string &c )
793{
794 m_keyword = k;
795 value( v );
796 m_comment = c;
797}
798
799template <class verboseT>
801{
802 m_keyword = card.m_keyword;
803 m_type = card.m_type;
804 memcpy( &m_value, &card.m_value, sizeof( values ) );
805 m_valueStr.str( card.m_valueStr.str() );
806 m_valueGood = card.m_valueGood;
807 m_valueStrGood = card.m_valueStrGood;
808 m_comment = card.m_comment;
809}
810
811template <class verboseT>
813{
814 m_keyword = card.m_keyword;
815 m_type = card.m_type;
816 memcpy( &m_value, &card.m_value, sizeof( values ) );
817 m_valueStr.str( card.m_valueStr.str() );
818 m_valueGood = card.m_valueGood;
819 m_valueStrGood = card.m_valueStrGood;
820 m_comment = card.m_comment;
821
822 return *this;
823}
824
825template <class verboseT>
827{
828 if( !m_valueGood )
829 {
831 }
832
833 if( m_type == fitsType<char *>() || m_type == fitsType<std::string>() )
834 {
835 m_valueStrGood = true; // It should be hard to get here, but just in case.
837 }
838
839 m_valueStr.str( "" );
840 m_valueStr.precision( 10 );
841
842 switch( m_type )
843 {
844 case fitsType<char>():
845 m_valueStr << static_cast<int>( m_value.Char );
846 break;
847 case fitsType<unsigned char>():
848 m_valueStr << static_cast<int>( m_value.UChar );
849 break;
850 case fitsType<short>():
851 m_valueStr << m_value.Short;
852 break;
853 case fitsType<unsigned short>():
854 m_valueStr << m_value.UShort;
855 break;
856 case fitsType<int>():
857 m_valueStr << m_value.Int;
858 break;
859 case fitsType<unsigned int>():
860 m_valueStr << m_value.UInt;
861 break;
862 case fitsType<long>():
863 m_valueStr << m_value.Long;
864 break;
865 case fitsType<unsigned long>():
866 m_valueStr << m_value.ULong;
867 break;
868 case fitsType<long long>():
869 m_valueStr << m_value.LongLong;
870 break;
871 case fitsType<unsigned long long>():
872 m_valueStr << m_value.ULongLong;
873 break;
874 case fitsType<float>():
875 m_valueStr << m_value.Float;
876 break;
877 case fitsType<std::complex<float>>():
878 m_valueStr << m_value.complexFloat;
879 break;
880 case fitsType<double>():
881 m_valueStr << m_value.Double;
882 break;
883 case fitsType<std::complex<double>>():
884 m_valueStr << m_value.complexDouble;
885 break;
886 case fitsType<fitsCommentType>():
888 case fitsType<fitsHistoryType>():
890 case fitsType<fitsContinueType>():
892 default:
893 return internal::mxlib_error_report<verboseT>( mx::error_t::invalidarg,
894 "Unknown FITS type for " + m_keyword );
895 }
896
897 m_valueStrGood = true;
899}
900
901template <class verboseT>
902template <typename typeT>
904{
905 m_type = fitsType<typeT>();
906
907 error_t errc;
908
909 m_value.template member<typeT>() = ioutils::stoT<typeT>( m_valueStr.str(), &errc );
910
911 if( errc != mx::error_t::noerror )
912 {
913 m_valueGood = false;
914 return errc;
915 }
916
917 m_valueGood = true;
918
919 return error_t::noerror;
920}
921
922template <class verboseT>
923template <typename typeT>
925{
926 switch( m_type )
927 {
928 case fitsType<unsigned char>():
929 {
930 cval = m_value.UChar;
931 return error_t::noerror;
932 }
933 case fitsType<char>():
934 {
935 cval = m_value.Char;
936 return error_t::noerror;
937 }
938 case fitsType<short>():
939 {
940 cval = m_value.Short;
941 return error_t::noerror;
942 }
943 case fitsType<unsigned short>():
944 {
945 cval = m_value.UShort;
946 return error_t::noerror;
947 }
948 case fitsType<int>():
949 {
950 cval = m_value.Int;
951 return error_t::noerror;
952 }
953 case fitsType<unsigned int>():
954 {
955 cval = m_value.UInt;
956 return error_t::noerror;
957 }
958 case fitsType<long>():
959 {
960 cval = m_value.Long;
961 return error_t::noerror;
962 }
963 case fitsType<unsigned long>():
964 {
965 cval = m_value.ULong;
966 return error_t::noerror;
967 }
968 case fitsType<long long>():
969 {
970 cval = m_value.LongLong;
971 return error_t::noerror;
972 }
973 case fitsType<unsigned long long>():
974 {
975 cval = m_value.ULongLong;
976 return error_t::noerror;
977 }
978 case fitsType<float>():
979 {
980 cval = m_value.Float;
981 return error_t::noerror;
982 }
983 case fitsType<std::complex<float>>():
984 {
985 return internal::mxlib_error_report<verboseT>( error_t::notimpl,
986 "can't convert complex type for " + m_keyword );
987 }
988 case fitsType<double>():
989 {
990 cval = m_value.Double;
991 return error_t::noerror;
992 }
993 case fitsType<std::complex<double>>():
994 {
995 return internal::mxlib_error_report<verboseT>( error_t::notimpl,
996 "can't convert complex type for " + m_keyword );
997 }
998 case fitsType<fitsCommentType>():
999 {
1000 return internal::mxlib_error_report<verboseT>( error_t::invalidarg,
1001 "cannot convert comment to numeric type for " + m_keyword );
1002 }
1003 case fitsType<fitsHistoryType>():
1004 {
1005 return internal::mxlib_error_report<verboseT>( error_t::invalidarg,
1006 "cannot convert history to numeric type for " + m_keyword );
1007 }
1008 case fitsType<fitsContinueType>():
1009 {
1010 return internal::mxlib_error_report<verboseT>( error_t::invalidarg,
1011 "cannot convert continue to numeric type for " + m_keyword );
1012 }
1013 case TSTRING:
1014 {
1015 return internal::mxlib_error_report<verboseT>( error_t::invalidarg,
1016 "cannot convert string to numeric type for " + m_keyword );
1017 }
1018 default:
1019 {
1020 return internal::mxlib_error_report<verboseT>( error_t::invalidarg,
1021 "invalid FITS type conversion for " + m_keyword );
1022 }
1023 }
1024}
1025
1026template <class verboseT>
1028{
1029 if( !m_valueGood )
1030 {
1031 m_type = newtype;
1032 return error_t::noerror;
1033 }
1034
1035 mx::error_t errc;
1036 switch( newtype )
1037 {
1038 case fitsType<unsigned char>():
1039 {
1040 errc = convertedValue<unsigned char>( m_value.UChar );
1041 break;
1042 }
1043 case fitsType<char>():
1044 {
1045 errc = convertedValue<char>( m_value.Char );
1046 break;
1047 }
1048 case fitsType<short>():
1049 {
1050 errc = convertedValue<short>( m_value.Short );
1051 break;
1052 }
1053 case fitsType<unsigned short>():
1054 {
1055 errc = convertedValue<unsigned short>( m_value.UShort );
1056 break;
1057 }
1058 case fitsType<int>():
1059 {
1060 errc = convertedValue<int>( m_value.Int );
1061 break;
1062 }
1063 case fitsType<unsigned int>():
1064 {
1065 errc = convertedValue<unsigned int>( m_value.UInt );
1066 break;
1067 }
1068 case fitsType<long>():
1069 {
1070 errc = convertedValue<long>( m_value.Long );
1071 break;
1072 }
1073 case fitsType<unsigned long>():
1074 {
1075 errc = convertedValue<unsigned long>( m_value.ULong );
1076 break;
1077 }
1078 case fitsType<long long>():
1079 {
1080 errc = convertedValue<long long>( m_value.LongLong );
1081 break;
1082 }
1083 case fitsType<unsigned long long>():
1084 {
1085 errc = convertedValue<unsigned long long>( m_value.ULongLong );
1086 break;
1087 }
1088 case fitsType<float>():
1089 {
1090 errc = convertedValue<float>( m_value.Float );
1091 break;
1092 }
1093 case fitsType<std::complex<float>>():
1094 {
1095 return internal::mxlib_error_report<verboseT>( error_t::notimpl,
1096 "can't convert complex type for " + m_keyword );
1097 }
1098 case fitsType<double>():
1099 {
1100 errc = convertedValue<double>( m_value.Double );
1101 break;
1102 }
1103 case fitsType<std::complex<double>>():
1104 {
1105 return internal::mxlib_error_report<verboseT>( error_t::notimpl,
1106 "can't convert complex type for " + m_keyword );
1107 }
1108 case fitsType<fitsCommentType>():
1109 {
1110 return internal::mxlib_error_report<verboseT>( error_t::invalidarg,
1111 "cannot convert comment to numeric type for " + m_keyword );
1112 }
1113 case fitsType<fitsHistoryType>():
1114 {
1115 return internal::mxlib_error_report<verboseT>( error_t::invalidarg,
1116 "cannot convert history to numeric type for " + m_keyword );
1117 }
1118 case fitsType<fitsContinueType>():
1119 {
1120 return internal::mxlib_error_report<verboseT>( error_t::invalidarg,
1121 "cannot convert continue to numeric type for " + m_keyword );
1122 }
1123 case TSTRING:
1124 {
1125 convertToString();
1126 m_type = newtype;
1127 m_valueGood = false;
1128 return error_t::noerror;
1129 }
1130 default:
1131 {
1132 return internal::mxlib_error_report<verboseT>( error_t::invalidarg,
1133 "invalid FITS type conversion for " + m_keyword );
1134 }
1135 }
1136
1137 if( !!errc )
1138 {
1139 return errc;
1140 }
1141
1142 m_type = newtype;
1143 m_valueGood = true;
1144
1145 return error_t::noerror;
1146}
1147
1148template <class verboseT>
1149const std::string &fitsHeaderCard<verboseT>::keyword() const
1150{
1151 return m_keyword;
1152}
1153
1154template <class verboseT>
1156{
1157 m_keyword = kw;
1158 return error_t::noerror;
1159}
1160
1161template <class verboseT>
1163{
1164 return m_type;
1165}
1166
1167template <class verboseT>
1169{
1170 if( t == m_type )
1171 {
1172 return error_t::noerror;
1173 }
1174
1175 if( m_valueGood )
1176 {
1177 error_t errc = convertValue( t );
1178 if( errc != error_t::noerror )
1179 {
1180 return errc;
1181 }
1182 }
1183 else
1184 {
1185 m_type = t;
1186 }
1187
1188 // Need to reconvert, always favor the actual value.
1189 if( m_valueGood && m_valueStrGood )
1190 {
1191 m_valueStrGood = false;
1192 }
1193
1194 return error_t::noerror;
1195}
1196
1197template <class verboseT>
1198template <typename typeT>
1200{
1201 errc = mx::error_t::noerror; // to be changed if needed
1202
1203 if( m_valueGood == false )
1204 {
1205 errc = convertFromString<typeT>();
1206 }
1207
1208 if( m_type != fitsType<typeT>() )
1209 {
1210 typeT val;
1211 errc = convertedValue<typeT>( val );
1212 return val;
1213 }
1214
1215 return m_value.template member<typeT>();
1216}
1217
1218template <class verboseT>
1220{
1221 errc = mx::error_t::noerror;
1222
1223 if( m_valueStrGood == false )
1224 {
1225 errc = convertToString();
1226 if( !!errc )
1227 {
1228 return "";
1229 }
1230 }
1231
1232 // Strip ' from beginning and end if present
1233 std::string str = m_valueStr.str();
1234
1235 // Reload it so it's there for next time:
1236 m_valueStr.str( str );
1237
1238 return str;
1239}
1240
1241template <class verboseT>
1243{
1244 return valueNonString<char>( errc );
1245}
1246
1247template <class verboseT>
1249{
1250 return valueNonString<unsigned char>( errc );
1251}
1252
1253template <class verboseT>
1255{
1256 return valueNonString<short>( errc );
1257}
1258
1259template <class verboseT>
1261{
1262 return valueNonString<unsigned short>( errc );
1263}
1264
1265template <class verboseT>
1267{
1268 return valueNonString<int>( errc );
1269}
1270
1271template <class verboseT>
1273{
1274 return valueNonString<unsigned int>( errc );
1275}
1276
1277template <class verboseT>
1279{
1280 return valueNonString<long>( errc );
1281}
1282
1283template <class verboseT>
1285{
1286 return valueNonString<unsigned long>( errc );
1287}
1288
1289template <class verboseT>
1291{
1292 return valueNonString<long long>( errc );
1293}
1294
1295template <class verboseT>
1297{
1298 return valueNonString<unsigned long long>( errc );
1299}
1300
1301template <class verboseT>
1303{
1304 return valueNonString<float>( errc );
1305}
1306
1307template <class verboseT>
1309{
1310 return valueNonString<double>( errc );
1311}
1312
1313template <class verboseT>
1314template <typename typeT>
1316{
1317 mx::error_t _errc;
1318
1319 typeT val = value( meta::tagT<typeT>(), _errc );
1320
1321 if( errc )
1322 {
1323 *errc = _errc;
1324 }
1325
1326 if( _errc != mx::error_t::noerror )
1327 {
1328 internal::mxlib_error_report<verboseT>( _errc, "getting value for " + m_keyword );
1329 }
1330
1331 return val;
1332}
1333
1334template <class verboseT>
1336{
1337 return value<std::string>( errc );
1338}
1339
1340template <class verboseT>
1342{
1343 return value<char>( errc );
1344}
1345
1346template <class verboseT>
1348{
1349 return value<unsigned char>( errc );
1350}
1351
1352template <class verboseT>
1354{
1355 return value<short>( errc );
1356}
1357
1358template <class verboseT>
1360{
1361 return value<unsigned short>( errc );
1362}
1363
1364template <class verboseT>
1366{
1367 return value<int>( errc );
1368}
1369
1370template <class verboseT>
1372{
1373 return value<unsigned int>( errc );
1374}
1375
1376template <class verboseT>
1378{
1379 return value<long>( errc );
1380}
1381
1382template <class verboseT>
1384{
1385 return value<unsigned long>( errc );
1386}
1387
1388template <class verboseT>
1390{
1391 return value<long long>( errc );
1392}
1393
1394template <class verboseT>
1396{
1397 return value<unsigned long long>( errc );
1398}
1399
1400template <class verboseT>
1402{
1403 return value<float>( errc );
1404}
1405
1406/*template <class verboseT>
1407std::complex<float> fitsHeaderCard<verboseT>::complexFloat()
1408{
1409 return value<std::complex<float>>();
1410}*/
1411
1412template <class verboseT>
1414{
1415 return value<double>( errc );
1416}
1417
1418/*
1419template <class verboseT>
1420std::complex<double> fitsHeaderCard<verboseT>::complexDouble()
1421{
1422 return value<std::complex<double>>();
1423}*/
1424
1425template <class verboseT>
1427{
1428 std::string str = v;
1429
1430 return value( str );
1431}
1432
1433template <class verboseT>
1435{
1436 // Strip ' from beginning and end if present
1437 std::string str = v;
1438
1439 stripApostWS( str );
1440
1441 // Reload it so it's there for next time:
1442 m_valueStr.str( str );
1443 m_valueGood = false;
1444 m_valueStrGood = true;
1445 m_type = fitsType<std::string>();
1446
1447 return mx::error_t::noerror;
1448}
1449
1450template <class verboseT>
1451template <typename typeT>
1453{
1454 m_type = fitsType<typeT>();
1455 m_value.template member<typeT>() = v;
1456 m_valueGood = true;
1457 m_valueStrGood = false;
1458
1459 return mx::error_t::noerror;
1460}
1461
1462template <class verboseT>
1464{
1465 if( !m_valueStrGood )
1466 {
1467 convertToString();
1468 }
1469
1470 std::string s = m_valueStr.str();
1471 return s;
1472}
1473
1474template <class verboseT>
1476{
1477 return m_valueGood;
1478}
1479
1480template <class verboseT>
1482{
1483 return m_valueStrGood;
1484}
1485
1486template <class verboseT>
1488{
1489 return m_comment;
1490}
1491
1492template <class verboseT>
1494{
1495 m_comment = c;
1496 return error_t::noerror;
1497}
1498
1499template <class verboseT>
1501{
1502 //Check if m_type is string
1503 if(m_type != fitsType<char*>() && m_type != fitsType<std::string>())
1504 {
1505 return internal::mxlib_error_report<verboseT>(error_t::invalidarg, "attempt to continue a non-string card");
1506 }
1507
1508 //Check if m_valueStrGood is true
1509 if(!m_valueStrGood)
1510 {
1511 return internal::mxlib_error_report<verboseT>(error_t::invalidconfig, "attempt to continue a card with no value");
1512 }
1513
1514 std::string newstr = card.m_comment;
1515
1516 //Check if this is the last one
1517 size_t slash = newstr.find_last_of('\'');
1518 if(slash != std::string::npos)
1519 {
1520 slash = newstr.find_first_of('/', slash);
1521
1522 if(slash != std::string::npos)
1523 {
1524 //extract comment
1525 size_t comm = newstr.find_first_not_of(' ', slash+1);
1526 if(comm != std::string::npos)
1527 {
1528 m_comment = newstr.substr(comm);
1529 }
1530
1531 //and then erase it
1532 newstr.erase(slash);
1533 }
1534 }
1535 stripApostWS(newstr);
1536
1537 // have to remove & if needed
1538 std::string vstr = m_valueStr.str();
1539 if(vstr.back() == '&')
1540 {
1541 vstr.erase(vstr.size()-1);
1542 }
1543
1544 m_valueStr.str(vstr + newstr);
1545
1546 return error_t::noerror;
1547}
1548
1549template <class verboseT>
1551{
1552 if( m_type == fitsType<char *>() || m_type == fitsType<std::string>() )
1553 {
1554 return fits_write_key<char *>( fptr,
1555 (char *)m_keyword.c_str(),
1556 (void *)m_valueStr.str().c_str(),
1557 (char *)m_comment.c_str() );
1558 }
1559
1560 // If the string is good, meaning already converted.
1561 if( m_valueStrGood == true )
1562 {
1563 // This populates the card directly.
1564 return fits_write_key<fitsUnknownType>( fptr,
1565 (char *)m_keyword.c_str(),
1566 (void *)m_valueStr.str().c_str(),
1567 (char *)m_comment.c_str() );
1568 }
1569
1570 // Ok, now we write the type directly using fitsio routines because it hasn't been converted.
1571 switch( m_type )
1572 {
1573 case fitsType<bool>():
1574 {
1575 return fits_write_key<bool>( fptr, (char *)m_keyword.c_str(), &m_value.Bool, (char *)m_comment.c_str() );
1576 }
1577 case fitsType<char>():
1578 {
1579 return fits_write_key<char>( fptr, (char *)m_keyword.c_str(), &m_value.Char, (char *)m_comment.c_str() );
1580 }
1581 case fitsType<unsigned char>():
1582 {
1583 return fits_write_key<unsigned char>( fptr,
1584 (char *)m_keyword.c_str(),
1585 &m_value.UChar,
1586 (char *)m_comment.c_str() );
1587 }
1588 case fitsType<short>():
1589 {
1590 return fits_write_key<short>( fptr, (char *)m_keyword.c_str(), &m_value.Short, (char *)m_comment.c_str() );
1591 }
1592 case fitsType<unsigned short>():
1593 {
1594 return fits_write_key<unsigned short>( fptr,
1595 (char *)m_keyword.c_str(),
1596 &m_value.UShort,
1597 (char *)m_comment.c_str() );
1598 }
1599 case fitsType<int>():
1600 {
1601 return fits_write_key<int>( fptr, (char *)m_keyword.c_str(), &m_value.Int, (char *)m_comment.c_str() );
1602 }
1603 case fitsType<unsigned int>():
1604 {
1605 return fits_write_key<unsigned int>( fptr,
1606 (char *)m_keyword.c_str(),
1607 &m_value.UInt,
1608 (char *)m_comment.c_str() );
1609 }
1610 case fitsType<long>():
1611 {
1612 return fits_write_key<long>( fptr, (char *)m_keyword.c_str(), &m_value.Long, (char *)m_comment.c_str() );
1613 }
1614 case fitsType<unsigned long>():
1615 {
1616 return fits_write_key<unsigned long>( fptr,
1617 (char *)m_keyword.c_str(),
1618 &m_value.ULong,
1619 (char *)m_comment.c_str() );
1620 }
1621 case fitsType<long long>():
1622 {
1623 return fits_write_key<long long>( fptr,
1624 (char *)m_keyword.c_str(),
1625 &m_value.LongLong,
1626 (char *)m_comment.c_str() );
1627 }
1628 case fitsType<unsigned long long>():
1629 {
1630 return fits_write_key<unsigned long long>( fptr,
1631 (char *)m_keyword.c_str(),
1632 &m_value.ULongLong,
1633 (char *)m_comment.c_str() );
1634 }
1635 case fitsType<float>():
1636 {
1637 return fits_write_key<float>( fptr, (char *)m_keyword.c_str(), &m_value.Float, (char *)m_comment.c_str() );
1638 }
1639 case fitsType<double>():
1640 {
1641 return fits_write_key<double>( fptr,
1642 (char *)m_keyword.c_str(),
1643 &m_value.Double,
1644 (char *)m_comment.c_str() );
1645 }
1646 case fitsType<fitsCommentType>():
1647 {
1648 return fits_write_comment( fptr, (char *)m_comment.c_str() );
1649 }
1650 case fitsType<fitsHistoryType>():
1651 {
1652 return fits_write_history( fptr, (char *)m_comment.c_str() );
1653 }
1654 default:
1655 {
1656 return internal::mxlib_error_report<verboseT>( error_t::invalidarg, "invalid FITS type for " + m_keyword );
1657 }
1658 }
1659}
1660
1661extern template class fitsHeaderCard<verbose::d>;
1662
1663} // namespace fits
1664} // namespace mx
1665
1666#endif // ioutils_fits_fitsHeaderCard_hpp
Class to manage the three components of a FITS header card.
char value(meta::tagT< char >, mx::error_t &errc)
Get value tag dispatcher for char.
long long value(meta::tagT< long long >, mx::error_t &errc)
Get value tag dispatcher for long long.
fitsHeaderCard(const std::string &k, char *v, const std::string &c="")
Construct from the three components for a value of string type.
fitsHeaderCard(const std::string &k)
Construct from just keyword, when value's type is unknown.
std::complex< float > complexFloat(error_t *errc=nullptr)
Get the value as a std::complex<float>
error_t convertFromString()
Convert from string to the type.
error_t comment(const std::string &c)
Set the comment.
mx::error_t write(fitsfile *fptr)
int type() const
Get the type.
fitsHeaderCard(const std::string &k, const char *v, const std::string &c="")
Construct from the three components for a value of string type.
fitsHeaderCard(const fitsHeaderCard &card)
Copy constructor.
std::string value(meta::tagT< std::string >, mx::error_t &errc)
Get value tag dispatcher for std::string.
long value(meta::tagT< long >, mx::error_t &errc)
Get value tag dispatcher for long.
fitsHeaderCard(const std::string &k, const typeT v, const std::string &c="")
Construct from the three components for a char.
unsigned short UShort(error_t *errc=nullptr)
Get the value as an unsigned short.
unsigned char UChar(error_t *errc=nullptr)
Get the value as an unsigned char.
bool m_valueGood
Flag indicating if the value is valid.
float Float(error_t *errc=nullptr)
Get the value as a float.
double Double(error_t *errc=nullptr)
Get the value as a double.
unsigned int value(meta::tagT< unsigned int >, mx::error_t &errc)
Get value tag dispatcher for unsigned int.
unsigned long value(meta::tagT< unsigned long >, mx::error_t &errc)
Get value tag dispatcher for unsigned long.
long long LongLong(error_t *errc=nullptr)
Get the value as a long long.
char Char(error_t *errc=nullptr)
Get the value as a char.
fitsHeaderCard(const std::string &k, const int type)
Construct from just keyword, when value's type known.
long Long(error_t *errc=nullptr)
Get the value as a long.
bool valueStrGood()
Get the current value string good flag.
std::string m_comment
The comment.
fitsHeaderCard(const std::string &k, fitsCommentType v, const std::string &c)
Construct from the three components, when it's really a comment card.
short value(meta::tagT< short >, mx::error_t &errc)
Get value tag dispatcher for short.
bool m_valueStrGood
Flag indicating if the value string is valid.
error_t type(const int &t)
Set the type.
unsigned int UInt(error_t *errc=nullptr)
Get the value as an unsigned int.
typeT value(mx::error_t *errc=nullptr)
Get the value.
const std::string & keyword() const
Get the keyword.
mx::error_t convertToString()
Convert from the type to a string.
std::string m_keyword
The keyword.
fitsHeaderCard(const std::string &k, const std::string &v, const std::string &c="")
Construct from the three components for a value of string type.
error_t convertValue(int newtype)
Convert the value from its type to a different type.
int value(meta::tagT< int >, mx::error_t &errc)
Get value tag dispatcher for int.
double value(meta::tagT< double >, mx::error_t &errc)
Get value tag dispatcher for double.
error_t value(const std::string &v)
Set the value to a std::string.
error_t convertedValue(typeT &cval)
Get the value from its type converted to a different type.
unsigned long long value(meta::tagT< unsigned long long >, mx::error_t &errc)
Get value tag dispatcher for unsigned long long.
short Short(error_t *errc=nullptr)
Get the value as a short.
bool valueGood()
Get the current value good flag.
unsigned char value(meta::tagT< unsigned char >, mx::error_t &errc)
Get value tag dispatcher for unsigned char.
fitsHeaderCard(const std::string &k, const std::string &v, const int &type, const std::string &c="")
Construct from the three components, when already in a string format.
unsigned long long ULongLong(error_t *errc=nullptr)
Get the value as an unsigned long long.
std::string valueStr()
Get the current value string.
error_t keyword(const std::string &kw)
Set the keyword.
fitsHeaderCard(const std::string &k, fitsHistoryType v, const std::string &c)
Construct from the three components, when it's really a history card.
unsigned long ULong(error_t *errc=nullptr)
Get the value as an unsigned long.
const std::string & comment()
Get the comment.
unsigned short value(meta::tagT< unsigned short >, mx::error_t &errc)
Get value tag dispatcher for unsigned short.
std::string String(error_t *errc=nullptr)
Get the value as a string.
typeT valueNonString(mx::error_t &errc)
Get value for anything not a string.
int m_type
The FITS type of the value, and indicates which member of m_values to access.
int Int(error_t *errc=nullptr)
Get the value as a int.
std::complex< double > complexDouble(error_t *errc=nullptr)
Get the value as a std::complex<double>
std::stringstream m_valueStr
The value in string form.
float value(meta::tagT< float >, mx::error_t &errc)
Get value tag dispatcher for float.
error_t value(const char *v)
Set the value to a char * string.
fitsHeaderCard & operator=(const fitsHeaderCard &card)
Assignment.
mx::error_t value(const typeT &v)
Set the value for a non-string type.
Declares and defines utilities to work with FITS files.
error_t
The mxlib error codes.
Definition error_t.hpp:26
@ notimpl
A component or technique is not implemented.
@ noerror
No error has occurred.
@ paramnotset
A parameter was not set.
@ invalidconfig
A config setting was invalid.
@ invalidarg
An argument was invalid.
mx::error_t fits_write_key< bool >(fitsfile *fptr, char *keyword, void *value, char *comment)
Specialization to handle the case bool.
The mxlib c++ namespace.
Definition mxlib.hpp:37
Empty type for tag dispatching.
Definition tagT.hpp:44
The native type is held in a union.
std::complex< float > complexFloat
the std::complex<float> value
unsigned char UChar
the unsigned char value
values()
c'tor. have to specify due to inclusion of std::complex types.
unsigned long long ULongLong
the unsigned long long value
unsigned long ULong
the unsigned long value
unsigned int UInt
the unsigned int value
std::complex< long double > complexLongDouble
the std::complex<long double> value
long double LongDouble
the long double value
unsigned short UShort
the unsigned short value
long long LongLong
the long long value
std::complex< double > complexDouble
the std::complex<double> value