mxlib
c++ tools for analyzing astronomical data and other tasks by Jared R. Males. [git repo]
Loading...
Searching...
No Matches
milkImage.hpp
Go to the documentation of this file.
1/** \file milkImage.hpp
2 * \brief Interface to MILK::ImageStreamIO shared memory streams
3 * \ingroup image_processing_files
4 * \author Jared R. Males (jaredmales@gmail.com)
5 *
6 */
7
8//***********************************************************************//
9// Copyright 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 improc_milkImage_hpp
28#define improc_milkImage_hpp
29
30#ifdef MXLIB_MILK
31
32#ifdef MXLIB_CUDA
33#define HAVE_CUDA
34#endif
35
36#include <ImageStreamIO/ImageStreamIO.h>
37
38#include "../mxlib.hpp"
39#include "eigenImage.hpp"
40
41namespace mx
42{
43namespace improc
44{
45
46template <typename typeT>
47struct ImageStructTypeCode;
48
49template <>
50struct ImageStructTypeCode<uint8_t>
51{
52 constexpr static int TypeCode = _DATATYPE_UINT8;
53};
54
55template <>
56struct ImageStructTypeCode<int8_t>
57{
58 constexpr static int TypeCode = _DATATYPE_INT8;
59};
60
61template <>
62struct ImageStructTypeCode<char>
63{
64 constexpr static int TypeCode = _DATATYPE_INT8;
65};
66
67template <>
68struct ImageStructTypeCode<uint16_t>
69{
70 constexpr static int TypeCode = _DATATYPE_UINT16;
71};
72
73template <>
74struct ImageStructTypeCode<int16_t>
75{
76 constexpr static int TypeCode = _DATATYPE_INT16;
77};
78
79template <>
80struct ImageStructTypeCode<uint32_t>
81{
82 constexpr static int TypeCode = _DATATYPE_UINT32;
83};
84
85template <>
86struct ImageStructTypeCode<int32_t>
87{
88 constexpr static int TypeCode = _DATATYPE_INT32;
89};
90
91template <>
92struct ImageStructTypeCode<uint64_t>
93{
94 constexpr static int TypeCode = _DATATYPE_UINT64;
95};
96template <>
97struct ImageStructTypeCode<int64_t>
98{
99 constexpr static int TypeCode = _DATATYPE_INT64;
100};
101template <>
102struct ImageStructTypeCode<float>
103{
104 constexpr static int TypeCode = _DATATYPE_FLOAT;
105};
106
107template <>
108struct ImageStructTypeCode<double>
109{
110 constexpr static int TypeCode = _DATATYPE_DOUBLE;
111};
112
113template <>
114struct ImageStructTypeCode<std::complex<float>>
115{
116 constexpr static int TypeCode = _DATATYPE_COMPLEX_FLOAT;
117};
118
119template <>
120struct ImageStructTypeCode<std::complex<double>>
121{
122 constexpr static int TypeCode = _DATATYPE_COMPLEX_DOUBLE;
123};
124
125/// Class to interface with an ImageStreamIO image in shared memory
126/**
127 *
128 * Use with Eigen::Map (aliased as mx::improc::eigenMap)
129 * \code
130 * using namespace mx::improc;
131 * milkImage<float> mim("image"); //connects to image.im.shm
132 * eigenMap<float> im(mim); //the conversion operator passes a reference to the internal map
133 * im.setRandom(); // im is now a map pointed at the image data. Eigen functions are now available.
134 * im(64,64) *= 2000;
135 * im /= 0.2;
136 * \endcode
137 * Once you have changed something via the Eigen::Map you want to notify others connected to the stream
138 * via
139 * \code
140 * mim.post();
141 * \endcode
142 *
143 * \ingroup eigen_image_processing
144 */
145template <typename _dataT>
146class milkImage
147{
148 public:
149 typedef _dataT dataT; ///< The data type
150
151 protected:
152 std::string m_name; ///< The image name, from name.im.shm (the .im.shm should not be given).
153
154 IMAGE *m_image{ nullptr }; ///< Pointer to the ImageStreamIO IMAGE structure.
155
156 dataT *m_raw{ nullptr }; // The raw pointer address is stored here for checks
157
158 eigenMap<dataT> *m_map{ nullptr }; // An Eigen::Map of the array
159
160 uint64_t m_size_0{ 0 }; ///< The size[0] of the image when last opened.
161
162 uint64_t m_size_1{ 0 }; ///< The size[1] of the image when last opened.
163
164 bool m_passive{ false }; /**< If true then the cnt0 counter is not incremented on post. Usage: this will not
165 trigger the dmcomb process in cacao, so the DM will not pick up the shape until
166 something does trigger it. */
167
168 public:
169 /// Default c'tor
170 milkImage();
171
172 /// Constructor which opens the specified image, which must already exist in shared memory.
173 milkImage( const std::string &imname /**< [in] The image name, from name.im.shm
174 (the .im.shm should not be given).*/
175 );
176
177 /// Constructor which (re-)creates the specified image with the given size
178 milkImage( const std::string &imname, ///< [in] The image name, from name.im.shm (the .im.shm should not be given).
179 uint32_t sz0, ///< [in] the x size of the image
180 uint32_t sz1 ///< [in] the y size of the image
181 );
182
183 /// Constructor which (re-)creates the specified image and copies the provided image to it
184 milkImage( const std::string &imname, ///< [in] The image name, from name.im.shm (the .im.shm should not be given).
185 const eigenImage<dataT> &im ///< [in] An existing eigenImage
186 );
187
188 /// D'tor
189 ~milkImage();
190
191 /// Open and connect to an image, allocating the eigenMap.
192 /**
193 * \throws std::invalid_argument if the image type_code does not match dataT.
194 */
195 void open( const std::string &imname /**< [in] The image name, from name.im.shm
196 (the .im.shm should not be given).*/
197 );
198
199 /// Create (or re-create) and connect to an image, allocating the eigenMap.
200 /**
201 * \throws std::invalid_argument if the image type_code does not match dataT.
202 */
203 void create( const std::string &imname, ///< [in] The image name, for name.im.shm (the .im.shm should not be given).
204 uint32_t sz0, ///< [in] the x size of the image
205 uint32_t sz1 ///< [in] the y size of the image
206 );
207
208 /// Create and connect to an image using an existing eigenImage, allocating the eigenMap and copying the image to
209 /// the shmim.
210 /**
211 * \throws std::invalid_argument if the image type_code does not match dataT.
212 */
213 void create( const std::string &imname, ///< [in] The image name, for name.im.shm (the .im.shm should not be given).
214 const eigenImage<dataT> &im ///< [in] An existing eigenImage
215 );
216
217 /// Get the raw pointer
218 dataT *data();
219
220 /// Get the raw pointer
221 const dataT *data() const;
222
223 /// Get the width of the image
224 /**
225 * \returns the current value of m_size_0
226 */
227 uint32_t rows() const;
228
229 /// Get the height of the image
230 /**
231 * \returns the current value of m_size_1
232 */
233 uint32_t cols() const;
234
235 /// Get the total size of the image
236 /**
237 * \returns the current value of m_size_0 x m_size_1
238 */
239 uint64_t size() const;
240
241 /// Get the size of a dimension of the image
242 /**
243 * \returns the current value of m_size_0 or m_size_1 depending on n
244 */
245 uint32_t size( unsigned n /**< [in] the dimension to get the size of*/ ) const;
246
247 /// Set the passive flag
248 /** Sets \ref m_passive
249 *
250 */
251 void passive( bool pass /**< [in] new value of the \ref m_passive flag */ );
252
253 /// Get the passive flag
254 /** Gets \ref m_passive
255 *
256 */
257 bool passive() const;
258
259 /// Checks if the image is connected and is still the same format as when connected.
260 /** Checks on pointer value, size[], and data_type.
261 *
262 * \todo check inode!
263 *
264 * \returns true if connected and no changes
265 * \returns false if not connected or something changed. All maps are now invalid.
266 */
267 bool valid() const;
268
269 /// Reopens the image.
270 /** Same as
271 * \code
272 * close();
273 * open(m_name);
274 * \endcode
275 */
276 void reopen();
277
278 // Close the image
279 void close();
280
281 /// Get an eigenMap
282 /** Use with caution:
283 * - there is no way to know if the image has changed
284 * - you should check \ref valid() before using
285 *
286 *
287 * \returns an Eigen::Map<Array,-1,-1> reference
288 *
289 * \throws if the image is not opened
290 *
291 */
292 eigenMap<dataT> &operator()();
293
294 /// Get an eigenMap (const version)
295 /** Use with caution:
296 * - there is no way to know if the image has changed
297 * - you should check \ref valid() before using
298 *
299 *
300 * \returns an Eigen::Map<Array,-1,-1> reference
301 *
302 * \throws if the image is not opened
303 *
304 */
305 const eigenMap<dataT> &operator()() const;
306
307 /// Conversion operator returns an eigenMap
308 /** Use this like
309 * \code
310 * milkImage<float> mim("imname");
311 * eigenMap<float> im(mim); //you now have an Eigen interface to the image
312 * \endcode
313 * but with caution:
314 * - there is no way to know if the image has changed
315 * - you should check \ref valid() before using
316 *
317 *
318 * \returns an Eigen::Map<Array,-1,-1> object
319 *
320 * \throws if the image is not opened
321 *
322 */
323 operator eigenMap<dataT>();
324
325 /// Copy data from another milkImage to this shared memory stream
326 /** Sets the write flag, copies using the Eigen assignment to map, unsets the write flag, then posts.
327 *
328 * \throws on an error
329 *
330 */
331 milkImage &operator=( const milkImage &im /**< [in] the milkImage to copy to this stream*/ );
332
333 /// Copy data from an Eigen Array type to the shared memory stream
334 /** Sets the write flag, copies using the Eigen assigment to map, unsets the write flag, then posts.
335 *
336 * \throws on an error
337 *
338 */
339 template <typename eigenT>
340 milkImage &operator=( const eigenT &im /**< [in] the eigen array to copy to the stream*/ );
341
342 /// Access a single value in the array by its linear index
343 /** This does not set the write flag or post after finishing. You must manage these steps
344 * if using this to modify the array.
345 *
346 * This is intended for use inside loops and so does no validity or range checks. You must ensure that the image is
347 * connected and valid and that \p idx is in range.
348 *
349 * \returns a reference to the value at the given position
350 */
351 dataT &operator[]( size_t idx /**< [in] the linear index of the pixel in the array */ );
352
353 /// Access a single value in the array by its linear index (const version)
354 /** This does not set the write flag or post after finishing. You must manage these steps
355 * if using this to modify the array.
356 *
357 * This is intended for use inside loops and so does no validity or range checks. You must ensure that the image is
358 * connected and valid and that \p idx is in range.
359 *
360 * \returns a reference to the value at the given position
361 */
362 const dataT &operator[]( size_t idx /**< [in] the linear index of the pixel in the array */ ) const;
363
364 /// Access a single value in the array by its row and column coordinates
365 /** This does not set the write flag or post after finishing. You must manage these steps
366 * if using this to modify the array.
367 *
368 * This is intended for use inside loops and so does no validity or range checks. You must ensure that the image is
369 * connected and valid and that \p row and \p col are in range.
370 *
371 * \returns a reference to the value at the given position
372 */
373 dataT &operator()( int row, /**< [in] the row of the element*/
374 int col /**< [in] the columnof the element */
375 );
376
377 /// Access a single value in the array by its row and column coordinates (const version)
378 /** This does not set the write flag or post after finishing. You must manage these steps
379 * if using this to modify the array.
380 *
381 * This is intended for use inside loops and so does no validity or range checks. You must ensure that the image is
382 * connected and valid and that \p row and \p col are in range.
383 *
384 * \returns a reference to the value at the given position
385 */
386 const dataT &operator()( int row, /**< [in] the row of the element*/
387 int col /**< [in] the columnof the element */
388 ) const;
389
390 /// Set the write flag
391 /** The write flag is set to indicate whether or not the the data is being changed.
392 * The write flag will be set to false by \ref post().
393 *
394 * \throws if the image is not opened
395 */
396 void setWrite( bool wrflag = true /**< [in] [optional] the desired value of the write flag. Default is true.*/ );
397
398 /// Update the metadata and post all semaphores
399 /**
400 * \todo need to set wtime, have a version with atime
401 *
402 * \throws if the image is not opened
403 */
404 void post();
405
406 /// Get the creation time
407 /**
408 * \returns the ImageStream creationtime
409 */
410 const timespec &creationtime() const;
411
412 /// Get the last access time
413 /**
414 * \returns ImageStream lastaccesstime
415 */
416 const timespec &lastaccesstime() const;
417
418 /// Get the acquisition time
419 /**
420 * \returns ImageStream acquisition time
421 */
422 const timespec &atime() const;
423
424 /// Get the write time
425 /**
426 * \returns ImageStream writetime
427 */
428 const timespec &writetime() const;
429
430 /// Get the value of cnt0 (the frame counter)
431 /**
432 * \returns the current value of cnt0
433 */
434 const uint64_t &cnt0() const;
435};
436
437template <typename dataT>
438milkImage<dataT>::milkImage()
439{
440}
441
442template <typename dataT>
443milkImage<dataT>::milkImage( const std::string &imname )
444{
445 open( imname );
446}
447
448template <typename dataT>
449milkImage<dataT>::milkImage( const std::string &imname, const eigenImage<dataT> &im )
450{
451 create( imname, im );
452}
453
454template <typename dataT>
455milkImage<dataT>::milkImage( const std::string &imname, uint32_t sz0, uint32_t sz1 )
456{
457 create( imname, sz0, sz1 );
458}
459
460template <typename dataT>
461milkImage<dataT>::~milkImage()
462{
463 close();
464}
465
466template <typename dataT>
467void milkImage<dataT>::open( const std::string &imname )
468{
469 if( m_image )
470 {
471 ImageStreamIO_closeIm( m_image );
472 delete m_image;
473 m_image = nullptr;
474 }
475
476 if( m_map )
477 {
478 delete m_map;
479 m_map = nullptr;
480 }
481
482 m_image = new IMAGE;
483
484 errno_t rv = ImageStreamIO_openIm( m_image, imname.c_str() );
485
486 if( rv != IMAGESTREAMIO_SUCCESS )
487 {
488 delete m_image;
489 m_image = nullptr;
490 throw( mx::exception( error_t::liberr, "ImageStreamIO_openIm returned an error" ) );
491 }
492
493 if( ImageStructTypeCode<dataT>::TypeCode != m_image->md->datatype )
494 {
495 delete m_image;
496 m_image = nullptr;
497 throw( mx::exception( error_t::invalidarg, "shmim datatype does not match template type" ) );
498 }
499
500 m_name = imname;
501 m_raw = (dataT *)m_image->array.raw;
502 m_size_0 = m_image->md->size[0];
503 m_size_1 = m_image->md->size[1];
504
505 m_map = new eigenMap<dataT>( m_raw, m_size_0, m_size_1 );
506}
507
508template <typename dataT>
509void milkImage<dataT>::create( const std::string &imname, uint32_t sz0, uint32_t sz1 )
510{
511 if( m_image )
512 {
513 ImageStreamIO_closeIm( m_image );
514 delete m_image;
515 m_image = nullptr;
516 }
517
518 if( m_map )
519 {
520 delete m_map;
521 m_map = nullptr;
522 }
523
524 m_image = new IMAGE;
525
526 uint32_t imsize[3];
527 imsize[0] = sz0;
528 imsize[1] = sz1;
529 imsize[2] = 1;
530
531 errno_t rv = ImageStreamIO_createIm_gpu( m_image,
532 imname.c_str(),
533 3,
534 imsize,
535 ImageStructTypeCode<dataT>::TypeCode,
536 -1,
537 1,
538 IMAGE_NB_SEMAPHORE,
539 0,
540 CIRCULAR_BUFFER | ZAXIS_TEMPORAL,
541 0 );
542
543 if( rv != IMAGESTREAMIO_SUCCESS )
544 {
545 delete m_image;
546 m_image = nullptr;
547 throw( mx::exception( error_t::liberr, "ImageStreamIO_createIm_gpu returned an error" ) );
548 }
549
550 m_image->md->cnt1 = 0;
551
552 if( ImageStructTypeCode<dataT>::TypeCode != m_image->md->datatype )
553 {
554 delete m_image;
555 m_image = nullptr;
556 throw( mx::exception( error_t::invalidarg, "shmim datatype does not match template type" ) );
557 }
558
559 m_name = imname;
560 m_raw = (dataT *)m_image->array.raw;
561 m_size_0 = m_image->md->size[0];
562 m_size_1 = m_image->md->size[1];
563
564 m_map = new eigenMap<dataT>( m_raw, m_size_0, m_size_1 );
565}
566
567template <typename dataT>
568void milkImage<dataT>::create( const std::string &imname, const eigenImage<dataT> &im )
569{
570 create( imname, im.rows(), im.cols() );
571 operator=( im );
572}
573
574template <typename dataT>
575eigenMap<dataT> &milkImage<dataT>::operator()()
576{
577 if( m_map == nullptr )
578 {
579 throw( mx::exception( error_t::invalidconfig, "Image is not open (map is null)" ) );
580 }
581
582 return *m_map;
583}
584
585template <typename dataT>
586const eigenMap<dataT> &milkImage<dataT>::operator()() const
587{
588 if( m_map == nullptr )
589 {
590 throw( mx::exception( error_t::invalidconfig, "Image is not open (map is null)" ) );
591 }
592
593 return *m_map;
594}
595
596template <typename dataT>
597milkImage<dataT>::operator eigenMap<dataT>()
598{
599 return operator()();
600}
601
602template <typename dataT>
603void milkImage<dataT>::reopen()
604{
605 close();
606 open( m_name );
607}
608
609template <typename dataT>
610void milkImage<dataT>::close()
611{
612 if( m_map != nullptr )
613 {
614 delete m_map;
615 m_map = nullptr;
616 }
617
618 if( m_image == nullptr )
619 {
620 return;
621 }
622
623 errno_t rv = ImageStreamIO_closeIm( m_image );
624 delete m_image;
625 m_image = nullptr;
626
627 if( rv != IMAGESTREAMIO_SUCCESS )
628 {
629 throw( mx::exception( error_t::liberr, "ImageStreamIO_closeIm returned an error" ) );
630 }
631}
632
633template <typename dataT>
634dataT *milkImage<dataT>::data()
635{
636 return m_raw;
637}
638
639template <typename dataT>
640const dataT *milkImage<dataT>::data() const
641{
642 return m_raw;
643}
644
645template <typename dataT>
646uint32_t milkImage<dataT>::rows() const
647{
648 return m_size_0;
649}
650
651template <typename dataT>
652uint32_t milkImage<dataT>::cols() const
653{
654 return m_size_1;
655}
656
657template <typename dataT>
658uint32_t milkImage<dataT>::size( unsigned n ) const
659{
660 if( n == 0 )
661 {
662 return m_size_0;
663 }
664 else if( n == 1 )
665 {
666 return m_size_1;
667 }
668
669 return 0;
670}
671
672template <typename dataT>
673uint64_t milkImage<dataT>::size() const
674{
675 return m_size_0 * m_size_1;
676}
677
678template <typename dataT>
679void milkImage<dataT>::passive( bool pass )
680{
681 m_passive = pass;
682}
683
684template <typename dataT>
685bool milkImage<dataT>::passive() const
686{
687 return m_passive;
688}
689
690template <typename dataT>
691bool milkImage<dataT>::valid() const
692{
693 if( m_image == nullptr )
694 {
695 return false;
696 }
697
698 if( m_raw != (dataT *)m_image->array.raw || m_size_0 != m_image->md->size[0] || m_size_1 != m_image->md->size[1] ||
699 ImageStructTypeCode<dataT>::TypeCode != m_image->md->datatype )
700 {
701 return false;
702 }
703
704 return true;
705}
706
707template <typename dataT>
708milkImage<dataT> &milkImage<dataT>::operator=( const milkImage<dataT> &im )
709{
710 if( m_image == nullptr )
711 {
712 throw( mx::exception( error_t::invalidconfig, "Image is not open (image is null)" ) );
713 }
714
715 if( m_map == nullptr )
716 {
717 throw( mx::exception( error_t::invalidconfig, "Image is not open (map is null)" ) );
718 }
719
720 setWrite( true );
721
722 *m_map = im(); // gets the map from im, will throw on invalid
723
724 setWrite( false );
725
726 post();
727
728 return *this;
729}
730
731template <typename dataT>
732template <typename eigenT>
733milkImage<dataT> &milkImage<dataT>::operator=( const eigenT &im )
734{
735 if( m_image == nullptr )
736 {
737 throw( mx::exception( error_t::invalidconfig, "Image is not open (image is null)" ) );
738 }
739
740 if( m_map == nullptr )
741 {
742 throw( mx::exception( error_t::invalidconfig, "Image is not open (map is null)" ) );
743 }
744
745 setWrite( true );
746
747 *m_map = im;
748
749 post();
750
751 return *this;
752}
753
754template <typename dataT>
755dataT &milkImage<dataT>::operator[]( size_t idx )
756{
757 return m_raw[idx];
758}
759
760template <typename dataT>
761const dataT &milkImage<dataT>::operator[]( size_t idx ) const
762{
763 return m_raw[idx];
764}
765
766template <typename dataT>
767dataT &milkImage<dataT>::operator()( int row, int col )
768{
769 return ( *m_map )( row, col );
770}
771
772template <typename dataT>
773const dataT &milkImage<dataT>::operator()( int row, int col ) const
774{
775 return ( *m_map )( row, col );
776}
777
778template <typename dataT>
779void milkImage<dataT>::setWrite( bool wrflag )
780{
781 if( m_image == nullptr )
782 {
783 throw( mx::exception( error_t::invalidconfig, "Image is not open" ) );
784 }
785
786 m_image->md->write = wrflag;
787}
788
789template <typename dataT>
790void milkImage<dataT>::post()
791{
792 if( m_image == nullptr )
793 {
794 throw( mx::exception( error_t::invalidconfig, "Image is not open" ) );
795 }
796
797 if( !m_passive )
798 {
799 errno_t rv = ImageStreamIO_UpdateIm( m_image );
800 m_image->md->atime = m_image->md->writetime;
801
802 if( rv != IMAGESTREAMIO_SUCCESS )
803 {
804 throw( mx::exception( error_t::liberr, "ImageStreamIO_UpdateIm returned an error" ) );
805 }
806 }
807 else
808 {
809 if( clock_gettime( CLOCK_ISIO, &m_image->md->writetime ) == -1 )
810 {
811 throw( mx::exception( errno2error_t( errno ), "clock_gettime returned an error" ) );
812 }
813 m_image->md->atime = m_image->md->writetime;
814
815 m_image->md->write = 0;
816 ImageStreamIO_sempost( m_image, -1 );
817 }
818}
819
820template <typename dataT>
821const timespec &milkImage<dataT>::creationtime() const
822{
823 return m_image->md->creationtime;
824}
825
826template <typename dataT>
827const timespec &milkImage<dataT>::lastaccesstime() const
828{
829 return m_image->md->lastaccesstime;
830}
831
832template <typename dataT>
833const timespec &milkImage<dataT>::atime() const
834{
835 return m_image->md->atime;
836}
837
838template <typename dataT>
839const timespec &milkImage<dataT>::writetime() const
840{
841 return m_image->md->writetime;
842}
843
844template <typename dataT>
845const uint64_t &milkImage<dataT>::cnt0() const
846{
847 return m_image->md->cnt0;
848}
849
850
851} // namespace improc
852} // namespace mx
853
854#endif // MXLIB_MILK
855#endif // improc_milkImage_hpp
Tools for using the eigen library for image processing.
Eigen::Array< scalarT, -1, -1 > eigenImage
Definition of the eigenImage type, which is an alias for Eigen::Array.
Eigen::Map< Eigen::Array< scalarT, -1, -1 > > eigenMap
Definition of the eigenMap type, which is an alias for Eigen::Map<Array>.
static constexpr error_t errno2error_t(const int &err)
Convert an errno code to error_t.
Definition error_t.hpp:2056
@ exception
An exception was thrown.
Definition error_t.hpp:51
@ invalidconfig
A config setting was invalid.
Definition error_t.hpp:30
@ invalidarg
An argument was invalid.
Definition error_t.hpp:29
@ liberr
An error was returned by a library.
Definition error_t.hpp:50
Declarations of some libarary wide utilities.
The mxlib c++ namespace.
Definition mxlib.hpp:37