...
This commit is contained in:
@@ -18,6 +18,9 @@
|
|||||||
namespace mcc
|
namespace mcc
|
||||||
{
|
{
|
||||||
|
|
||||||
|
constexpr double MCC_DEGRESS_TO_RADS = std::numbers::pi / 180.0;
|
||||||
|
constexpr double MCC_RADS_TO_DEGRESS = 1.0 / MCC_DEGRESS_TO_RADS;
|
||||||
|
|
||||||
constexpr double MCC_HALF_PI = std::numbers::pi / 2.0;
|
constexpr double MCC_HALF_PI = std::numbers::pi / 2.0;
|
||||||
constexpr double MCC_TWO_PI = std::numbers::pi * 2.0;
|
constexpr double MCC_TWO_PI = std::numbers::pi * 2.0;
|
||||||
|
|
||||||
|
|||||||
@@ -198,7 +198,7 @@ public:
|
|||||||
error_t operator()(traits::mcc_input_char_range auto const& input, VT& value)
|
error_t operator()(traits::mcc_input_char_range auto const& input, VT& value)
|
||||||
{
|
{
|
||||||
if constexpr (std::is_arithmetic_v<VT>) {
|
if constexpr (std::is_arithmetic_v<VT>) {
|
||||||
auto v = mcc::utils::numFromStr<VT>(trimSpaces(input));
|
auto v = mcc::utils::numFromStr<VT>(utils::trimSpaces(input));
|
||||||
if (!v.has_value()) {
|
if (!v.has_value()) {
|
||||||
return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE;
|
return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE;
|
||||||
}
|
}
|
||||||
@@ -302,8 +302,8 @@ public:
|
|||||||
{
|
{
|
||||||
if constexpr (mcc_angle_c<VT>) { // scalar
|
if constexpr (mcc_angle_c<VT>) { // scalar
|
||||||
auto res = utils::parsAngleString(input, hms);
|
auto res = utils::parsAngleString(input, hms);
|
||||||
if (res) {
|
if (res) { // returned angle is in degrees!!!
|
||||||
value = res.value();
|
value = res.value() * MCC_DEGRESS_TO_RADS;
|
||||||
} else {
|
} else {
|
||||||
return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE;
|
return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE;
|
||||||
}
|
}
|
||||||
|
|||||||
312
mcc_serialization_common.h
Normal file
312
mcc_serialization_common.h
Normal file
@@ -0,0 +1,312 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
* *
|
||||||
|
* MOUNT CONTROL COMPONENTS LIBRARY *
|
||||||
|
* *
|
||||||
|
* *
|
||||||
|
* COMMON DEFINITIONS FOR DATA SERIALIZATION/DESERIALIZATION *
|
||||||
|
* *
|
||||||
|
****************************************************************************************/
|
||||||
|
|
||||||
|
#include "mcc_concepts.h"
|
||||||
|
#include "mcc_error.h"
|
||||||
|
#include "mcc_traits.h"
|
||||||
|
|
||||||
|
namespace mcc
|
||||||
|
{
|
||||||
|
|
||||||
|
enum class MccSerializedCoordPairFormat {
|
||||||
|
MCC_SERIALIZED_FORMAT_DEGREES, // both angles are in degrees as floating-point number
|
||||||
|
MCC_SERIALIZED_FORMAT_SXGM_HOURDEG, // X is in hour and Y is in degree sexagesimal representation
|
||||||
|
MCC_SERIALIZED_FORMAT_SXGM_DEGDEG, // both angles are in sexagesimal degrees
|
||||||
|
MCC_SERIALIZED_FORMAT_UNKNOWN
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr std::string_view MCC_SERIALIZED_FORMAT_DEGREES_STR = "SRCP-FORMAT-DEGREES";
|
||||||
|
static constexpr std::string_view MCC_SERIALIZED_FORMAT_SXGM_HOURDEG_STR = "SRCP-FORMAT-SXGM_HOURDEG";
|
||||||
|
static constexpr std::string_view MCC_SERIALIZED_FORMAT_SXGM_DEGDEG_STR = "SRCP-FORMAT-SXGM_DEGDEG";
|
||||||
|
|
||||||
|
|
||||||
|
static constexpr std::string_view MccSerializedCoordPairFormatToStr(MccSerializedCoordPairFormat fmt)
|
||||||
|
{
|
||||||
|
return fmt == MccSerializedCoordPairFormat::MCC_SERIALIZED_FORMAT_DEGREES ? MCC_SERIALIZED_FORMAT_DEGREES_STR
|
||||||
|
: fmt == MccSerializedCoordPairFormat::MCC_SERIALIZED_FORMAT_SXGM_HOURDEG
|
||||||
|
? MCC_SERIALIZED_FORMAT_SXGM_HOURDEG_STR
|
||||||
|
: fmt == MccSerializedCoordPairFormat::MCC_SERIALIZED_FORMAT_SXGM_DEGDEG
|
||||||
|
? MCC_SERIALIZED_FORMAT_SXGM_DEGDEG_STR
|
||||||
|
: MCC_SERIALIZED_FORMAT_DEGREES_STR;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <traits::mcc_input_char_range R>
|
||||||
|
static constexpr MccSerializedCoordPairFormat MccSerializedCoordPairFormatStrToValue(R&& str)
|
||||||
|
{
|
||||||
|
if constexpr (std::is_array_v<std::decay_t<R>>) {
|
||||||
|
return MccSerializedCoordPairFormatStrToValue(std::string_view{str});
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto hash = mcc::utils::FNV1aHash(std::forward<R>(str));
|
||||||
|
|
||||||
|
return hash == mcc::utils::FNV1aHash(MCC_SERIALIZED_FORMAT_DEGREES_STR)
|
||||||
|
? MccSerializedCoordPairFormat::MCC_SERIALIZED_FORMAT_DEGREES
|
||||||
|
: hash == mcc::utils::FNV1aHash(MCC_SERIALIZED_FORMAT_SXGM_HOURDEG_STR)
|
||||||
|
? MccSerializedCoordPairFormat::MCC_SERIALIZED_FORMAT_SXGM_HOURDEG
|
||||||
|
: hash == mcc::utils::FNV1aHash(MCC_SERIALIZED_FORMAT_SXGM_DEGDEG_STR)
|
||||||
|
? MccSerializedCoordPairFormat::MCC_SERIALIZED_FORMAT_SXGM_DEGDEG
|
||||||
|
: MccSerializedCoordPairFormat::MCC_SERIALIZED_FORMAT_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct MccSerializedCoordPairFormatPrec {
|
||||||
|
uint8_t hour_prec = 2; // number of decimal places in hour seconds (sexagesimal format)
|
||||||
|
uint8_t deg_prec = 1; // number of decimal places in arcseconds (sexagesimal format)
|
||||||
|
// slightly better than 0.1 arcsecond precision
|
||||||
|
uint8_t decimals = 6; // number of decimal places in degrees (floating-point number format)
|
||||||
|
// if 0, then number of decimal places is according to formating rules of 'double' type
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class MccTimePointFormat {
|
||||||
|
MCC_TIMEPOINT_FORMAT_DATE, // UTC date
|
||||||
|
MCC_TIMEPOINT_FORMAT_MJD, // MJD
|
||||||
|
MCC_TIMEPOINT_FORMAT_JD, // JD
|
||||||
|
MCC_TIMEPOINT_FORMAT_JEPOCH, // Julian epoch
|
||||||
|
MCC_TIMEPOINT_FORMAT_UNKNOWN
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr std::string_view MCC_TIMEPOINT_FORMAT_DATE_STR = "TP-FORMAT-DATE";
|
||||||
|
static constexpr std::string_view MCC_TIMEPOINT_FORMAT_MJD_STR = "TP-FORMAT-MJD";
|
||||||
|
static constexpr std::string_view MCC_TIMEPOINT_FORMAT_JD_STR = "TP-FORMAT-JD";
|
||||||
|
static constexpr std::string_view MCC_TIMEPOINT_FORMAT_JEPOCH_STR = "TP-FORMAT-JEPOCH";
|
||||||
|
|
||||||
|
static constexpr std::string_view MccTimePointFormatToStr(MccTimePointFormat fmt)
|
||||||
|
{
|
||||||
|
return fmt == MccTimePointFormat::MCC_TIMEPOINT_FORMAT_DATE ? MCC_TIMEPOINT_FORMAT_DATE_STR
|
||||||
|
: fmt == MccTimePointFormat::MCC_TIMEPOINT_FORMAT_MJD ? MCC_TIMEPOINT_FORMAT_MJD_STR
|
||||||
|
: fmt == MccTimePointFormat::MCC_TIMEPOINT_FORMAT_JD ? MCC_TIMEPOINT_FORMAT_JD_STR
|
||||||
|
: fmt == MccTimePointFormat::MCC_TIMEPOINT_FORMAT_JEPOCH ? MCC_TIMEPOINT_FORMAT_JEPOCH_STR
|
||||||
|
: MCC_TIMEPOINT_FORMAT_MJD_STR;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <traits::mcc_input_char_range R>
|
||||||
|
static constexpr MccTimePointFormat MccTimePointFormatStrToValue(R&& str)
|
||||||
|
{
|
||||||
|
if constexpr (std::is_array_v<std::decay_t<R>>) {
|
||||||
|
return MccTimePointFormatStrToValue(std::string_view{str});
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto hash = mcc::utils::FNV1aHash(std::forward<R>(str));
|
||||||
|
|
||||||
|
return hash == mcc::utils::FNV1aHash(MCC_TIMEPOINT_FORMAT_DATE_STR) ? MccTimePointFormat::MCC_TIMEPOINT_FORMAT_DATE
|
||||||
|
: hash == mcc::utils::FNV1aHash(MCC_TIMEPOINT_FORMAT_MJD_STR) ? MccTimePointFormat::MCC_TIMEPOINT_FORMAT_MJD
|
||||||
|
: hash == mcc::utils::FNV1aHash(MCC_TIMEPOINT_FORMAT_JD_STR) ? MccTimePointFormat::MCC_TIMEPOINT_FORMAT_JD
|
||||||
|
: hash == mcc::utils::FNV1aHash(MCC_TIMEPOINT_FORMAT_JEPOCH_STR)
|
||||||
|
? MccTimePointFormat::MCC_TIMEPOINT_FORMAT_JEPOCH
|
||||||
|
: MccTimePointFormat::MCC_TIMEPOINT_FORMAT_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* SERIALIZATION/DESERIALIZATION PROCESS TUNING PARAMETERS */
|
||||||
|
|
||||||
|
// delimiter between items of serializing values sequence
|
||||||
|
static constexpr std::string_view MCC_SERIALIZING_DEFAULT_SEQ_DELIMITER{";"};
|
||||||
|
|
||||||
|
// delimiter between items of aggregative (multi-element) serializing value
|
||||||
|
static constexpr std::string_view MCC_SERIALIZING_DEFAULT_ELEM_DELIMITER{","};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
concept mcc_serialization_params_c = requires(T t) {
|
||||||
|
requires traits::mcc_output_char_range<decltype(t.seq_delim)>;
|
||||||
|
requires traits::mcc_output_char_range<decltype(t.elem_delim)>;
|
||||||
|
|
||||||
|
requires std::same_as<decltype(t.coordpair_format), MccSerializedCoordPairFormat>;
|
||||||
|
requires std::same_as<decltype(t.coordpair_prec), MccSerializedCoordPairFormatPrec>;
|
||||||
|
requires std::same_as<decltype(t.timepoint_format), MccTimePointFormat>;
|
||||||
|
|
||||||
|
// if true - normalize angle in sexagesimal format (to control rounding)
|
||||||
|
// (to avoid something like "24:00:00.0" for sexagesimal 'hours:minutes:seconds' format)
|
||||||
|
requires std::convertible_to<decltype(t.norm_sxgm), bool>;
|
||||||
|
|
||||||
|
// if true - interpretate serialized angle in sexagesimal format as 'hours:minutes:seconds'
|
||||||
|
// otherwise as 'degrees:arcmins:arcsecs'
|
||||||
|
requires std::convertible_to<decltype(t.sxgm_hms), bool>;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* SERIALIZER/DESERIALIZER CONCEPTS */
|
||||||
|
|
||||||
|
template <mcc_error_c RetT>
|
||||||
|
struct mcc_serializer_interface_t {
|
||||||
|
virtual ~mcc_serializer_interface_t() = default;
|
||||||
|
|
||||||
|
typedef RetT error_t;
|
||||||
|
|
||||||
|
template <std::derived_from<mcc_serializer_interface_t> SelfT, traits::mcc_output_char_range R, typename ValueT>
|
||||||
|
RetT operator()(this SelfT&& self, R& output, ValueT const& value)
|
||||||
|
{
|
||||||
|
return std::forward<SelfT>(self)(output, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
mcc_serializer_interface_t() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
concept mcc_serializer_c =
|
||||||
|
std::derived_from<T, mcc_serializer_interface_t<typename T::error_t>> && requires(T t, const T t_const) {
|
||||||
|
// static const variable with name of the serializer
|
||||||
|
requires std::formattable<decltype(T::serializerName), char> && std::is_const_v<decltype(T::serializerName)>;
|
||||||
|
|
||||||
|
// must define a type "params_t"
|
||||||
|
requires mcc_serialization_params_c<typename T::params_t>;
|
||||||
|
|
||||||
|
{ t.setParams(std::declval<typename T::params_t const&>()) };
|
||||||
|
|
||||||
|
{ t_const.getParams() } -> std::same_as<typename T::params_t>;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
template <mcc_error_c RetT>
|
||||||
|
struct mcc_deserializer_interface_t {
|
||||||
|
virtual ~mcc_deserializer_interface_t() = default;
|
||||||
|
|
||||||
|
typedef RetT error_t;
|
||||||
|
|
||||||
|
template <std::derived_from<mcc_deserializer_interface_t> SelfT, traits::mcc_input_char_range R, typename ValueT>
|
||||||
|
RetT operator()(this SelfT&& self, R const& input, ValueT& value)
|
||||||
|
{
|
||||||
|
return std::forward<SelfT>(self)(input, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
mcc_deserializer_interface_t() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
concept mcc_deserializer_c =
|
||||||
|
std::derived_from<T, mcc_deserializer_interface_t<typename T::error_t>> && requires(T t, const T t_const) {
|
||||||
|
// static const variable with name of the deserializer
|
||||||
|
requires std::formattable<decltype(T::deserializerName), char> &&
|
||||||
|
std::is_const_v<decltype(T::deserializerName)>;
|
||||||
|
|
||||||
|
// must define a type "params_t"
|
||||||
|
requires mcc_serialization_params_c<typename T::params_t>;
|
||||||
|
|
||||||
|
{ t.setParams(std::declval<typename T::params_t const&>()) };
|
||||||
|
|
||||||
|
{ t_const.getParams() } -> std::same_as<typename T::params_t>;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* BASE CLASS IMPLEMENTATION FOR SERIALIZER/DESERIALIZER */
|
||||||
|
|
||||||
|
namespace impl
|
||||||
|
{
|
||||||
|
|
||||||
|
// default definition of serialization/deserialization process parameters structure
|
||||||
|
struct mcc_serialization_params_t {
|
||||||
|
std::string seq_delim{MCC_SERIALIZING_DEFAULT_SEQ_DELIMITER};
|
||||||
|
std::string elem_delim{MCC_SERIALIZING_DEFAULT_ELEM_DELIMITER};
|
||||||
|
|
||||||
|
MccSerializedCoordPairFormat coordpair_format{MccSerializedCoordPairFormat::MCC_SERIALIZED_FORMAT_SXGM_HOURDEG};
|
||||||
|
|
||||||
|
MccSerializedCoordPairFormatPrec coordpair_prec{
|
||||||
|
MccSerializedCoordPairFormatPrec{.hour_prec = 2, .deg_prec = 1, .decimals = 6}};
|
||||||
|
|
||||||
|
MccTimePointFormat timepoint_format{MccTimePointFormat::MCC_TIMEPOINT_FORMAT_DATE};
|
||||||
|
|
||||||
|
bool norm_sxgm{false};
|
||||||
|
|
||||||
|
bool sxgm_hms{false};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
|
||||||
|
template <mcc_serialization_params_c ParamsT>
|
||||||
|
struct _params_manipulator_t {
|
||||||
|
virtual ~_params_manipulator_t() = default;
|
||||||
|
|
||||||
|
typedef ParamsT params_t;
|
||||||
|
|
||||||
|
template <mcc_serialization_params_c p_t>
|
||||||
|
void setParams(p_t const& pars)
|
||||||
|
{
|
||||||
|
if constexpr (std::same_as<p_t, params_t>) {
|
||||||
|
_params = pars;
|
||||||
|
} else {
|
||||||
|
_params.seq_delim = pars.seq_delim;
|
||||||
|
_params.elem_delim = pars.elem_delim;
|
||||||
|
_params.coordpair_format = pars.coordpair_format;
|
||||||
|
_params.coordpair_prec = pars.coordpair_prec;
|
||||||
|
_params.timepoint_format = pars.timepoint_format;
|
||||||
|
_params.norm_sxgm = pars.norm_sxgm;
|
||||||
|
_params.sxgm_hms = pars.sxgm_hms;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <mcc_serialization_params_c p_t>
|
||||||
|
requires(!std::same_as<p_t, params_t>)
|
||||||
|
p_t getParams() const
|
||||||
|
{
|
||||||
|
p_t pars;
|
||||||
|
|
||||||
|
pars.seq_delim = _params.seq_delim;
|
||||||
|
pars.elem_delim = _params.elem_delim;
|
||||||
|
pars.coordpair_format = _params.coordpair_format;
|
||||||
|
pars.coordpair_prec = _params.coordpair_prec;
|
||||||
|
pars.timepoint_format = _params.timepoint_format;
|
||||||
|
pars.norm_sxgm = _params.norm_sxgm;
|
||||||
|
pars.sxgm_hms = _params.sxgm_hms;
|
||||||
|
|
||||||
|
return pars;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
params_t getParams() const
|
||||||
|
{
|
||||||
|
return _params;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
_params_manipulator_t() = default;
|
||||||
|
|
||||||
|
params_t _params;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
|
||||||
|
|
||||||
|
template <mcc_serialization_params_c ParamsT>
|
||||||
|
struct MccSerializerBase : mcc_serializer_interface_t<MccError>, details::_params_manipulator_t<ParamsT> {
|
||||||
|
virtual ~MccSerializerBase() = default;
|
||||||
|
|
||||||
|
using typename mcc_serializer_interface_t<MccError>::error_t;
|
||||||
|
|
||||||
|
using typename details::_params_manipulator_t<ParamsT>::params_t;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
MccSerializerBase() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <mcc_serialization_params_c ParamsT>
|
||||||
|
struct MccDeserializerBase : mcc_deserializer_interface_t<MccError>, details::_params_manipulator_t<ParamsT> {
|
||||||
|
virtual ~MccDeserializerBase() = default;
|
||||||
|
|
||||||
|
using typename mcc_deserializer_interface_t<MccError>::error_t;
|
||||||
|
|
||||||
|
using typename details::_params_manipulator_t<ParamsT>::params_t;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
MccDeserializerBase() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace impl
|
||||||
|
|
||||||
|
} // namespace mcc
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
// #include <mcc_ccte_erfa.h>
|
// #include <mcc_ccte_erfa.h>
|
||||||
#include <mcc_coordinate.h>
|
#include <mcc_coordinate.h>
|
||||||
|
#include <mcc_deserializer.h>
|
||||||
#include <mcc_serializer.h>
|
#include <mcc_serializer.h>
|
||||||
|
|
||||||
using namespace mcc::impl;
|
using namespace mcc::impl;
|
||||||
@@ -28,12 +29,25 @@ void serialize(VT const& value)
|
|||||||
|
|
||||||
auto err = ser(s, value);
|
auto err = ser(s, value);
|
||||||
if (err) {
|
if (err) {
|
||||||
std::cout << "SERIALIZing ERR: " << err << "\n";
|
std::cout << "SERIALIZING ERR: " << err << "\n";
|
||||||
} else {
|
} else {
|
||||||
std::cout << "SERIALIZED: " << s << "\n";
|
std::cout << "SERIALIZED: " << s << "\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename VT>
|
||||||
|
void deserialize(mcc::traits::mcc_input_char_range auto const& s, VT& value)
|
||||||
|
{
|
||||||
|
MccDeserializer<VT> deser;
|
||||||
|
|
||||||
|
auto err = deser(s, value);
|
||||||
|
if (err) {
|
||||||
|
std::cout << "DESERIALIZING ERR: " << err << "\n";
|
||||||
|
} else {
|
||||||
|
std::cout << "DESERIALIZION IS OK\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
skypt_t::cctEngine.setStateERFA(saoras);
|
skypt_t::cctEngine.setStateERFA(saoras);
|
||||||
@@ -141,5 +155,22 @@ int main()
|
|||||||
std::cout << "\tfor 'sky point' type: ";
|
std::cout << "\tfor 'sky point' type: ";
|
||||||
serialize(pt);
|
serialize(pt);
|
||||||
|
|
||||||
|
|
||||||
|
std::cout << "\n\nDESERIALIZATION TEST:\n";
|
||||||
|
v = 0.0;
|
||||||
|
|
||||||
|
std::string s = "123.7687";
|
||||||
|
|
||||||
|
deserialize(s, v);
|
||||||
|
std::cout << "\tfor 'double' type: v = " << v << "\n";
|
||||||
|
|
||||||
|
s = " 11:22:33.453, -32.19820931,65632.87987987,RADEC-OBS ";
|
||||||
|
deserialize(s, pt);
|
||||||
|
pt.toAtSameEpoch(radec_obs);
|
||||||
|
std::cout << "\tfor 'sky point' type: x = " << radec_obs.x().sexagesimal(true)
|
||||||
|
<< ", y = " << radec_obs.y().degrees() << ", epoch = " << pt.epoch().MJD()
|
||||||
|
<< ", kind = " << MccCoordPairKindToStr(pt.pairKind()) << "\n";
|
||||||
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user