diff --git a/externals/coda-oss/modules/c++/mt/tests/ThreadGroupAffinityTest.cpp b/externals/coda-oss/modules/c++/mt/tests/ThreadGroupAffinityTest.cpp index 32d3c63c0..57f3e3f68 100644 --- a/externals/coda-oss/modules/c++/mt/tests/ThreadGroupAffinityTest.cpp +++ b/externals/coda-oss/modules/c++/mt/tests/ThreadGroupAffinityTest.cpp @@ -20,6 +20,8 @@ * */ +#include + #if !defined(__APPLE_CC__) && (defined(__linux) || defined(__linux__)) #include @@ -64,8 +66,8 @@ class MyRunTask : public Runnable // assigned to for (size_t trials = 0; trials < 10; ++trials) { - volatile size_t count = 10000000000; - volatile size_t sum = 0; + constexpr size_t count = 10000000000; + std::atomic sum{0}; for (size_t ii = 0; ii < count; ++ii) { sum++; diff --git a/externals/coda-oss/modules/c++/str/CMakeLists.txt b/externals/coda-oss/modules/c++/str/CMakeLists.txt index 0228ccda7..de3d531b7 100644 --- a/externals/coda-oss/modules/c++/str/CMakeLists.txt +++ b/externals/coda-oss/modules/c++/str/CMakeLists.txt @@ -3,7 +3,7 @@ set(MODULE_NAME str) coda_add_module( ${MODULE_NAME} VERSION 1.0 - DEPS except-c++ coda_oss-c++) + DEPS types-c++ except-c++ coda_oss-c++) coda_add_tests( MODULE_NAME ${MODULE_NAME} diff --git a/externals/coda-oss/modules/c++/str/include/str/Convert.h b/externals/coda-oss/modules/c++/str/include/str/Convert.h index dd32bd5ea..3adda99b4 100644 --- a/externals/coda-oss/modules/c++/str/include/str/Convert.h +++ b/externals/coda-oss/modules/c++/str/include/str/Convert.h @@ -20,12 +20,11 @@ * */ +#pragma once #ifndef CODA_OSS_str_Convert_h_INCLUDED_ #define CODA_OSS_str_Convert_h_INCLUDED_ -#pragma once #include -#include #include #include #include @@ -34,20 +33,22 @@ #include #include #include +#include #include "config/Exports.h" #include "coda_oss/string.h" #include "coda_oss/optional.h" #include "coda_oss/cstddef.h" +#include "types/complex.h" #include "import/except.h" namespace str { -template -int getPrecision(const T& type); - -template -int getPrecision(const std::complex& type); +template int getPrecision(const T& type); +template int getPrecision(const std::complex&); +#if CODA_OSS_types_unique_zinteger +template int getPrecision(const types::zinteger&); +#endif namespace details { @@ -334,6 +335,13 @@ int getPrecision(const std::complex& type) { return getPrecision(type.real()); } +#if CODA_OSS_types_unique_zinteger +template +int getPrecision(const types::zinteger& type) +{ + return getPrecision(type.real()); +} +#endif template <> int getPrecision(const float& type); diff --git a/externals/coda-oss/modules/c++/str/unittests/test_str.cpp b/externals/coda-oss/modules/c++/str/unittests/test_str.cpp index 49e4a4894..0c5af2ff6 100644 --- a/externals/coda-oss/modules/c++/str/unittests/test_str.cpp +++ b/externals/coda-oss/modules/c++/str/unittests/test_str.cpp @@ -22,8 +22,10 @@ #include // std::ignore +#include #include #include + #include "TestCase.h" inline std::string to_string(const std::string& value) @@ -225,6 +227,88 @@ TEST_CASE(testEscapeForXMLKitchenSink) TEST_ASSERT_EQ(message, expectedMessage); } +TEST_CASE(test_toStringComplexFloat) +{ + const std::string expected("(1,-2)"); + + const std::complex std_cx_float(1.0f, -2.0f); + auto actual = str::toString(std_cx_float); + TEST_ASSERT_EQ(actual, expected); + + const types::zreal types_cx_float(1.0f, -2.0f); + actual = str::toString(types_cx_float); + TEST_ASSERT_EQ(actual, expected); + + const types::zfloat zfloat(1.0f, -2.0f); + actual = str::toString(zfloat); + TEST_ASSERT_EQ(actual, expected); +} +TEST_CASE(test_toTypeComplexFloat) +{ + const std::string strValue("(1,-2)"); + + auto actual = str::toType>(strValue); + auto strActual = str::toString(actual); + TEST_ASSERT_EQ(strActual, strValue); + + actual = str::toType>(strValue); + strActual = str::toString(actual); + TEST_ASSERT_EQ(strActual, strValue); + + actual = str::toType(strValue); + strActual = str::toString(actual); + TEST_ASSERT_EQ(strActual, strValue); +} + +TEST_CASE(test_toStringComplexShort) +{ + const std::string expected("(1,-2)"); + + CODA_OSS_disable_warning_push + #if _MSC_VER + #pragma warning(disable: 4996) // '...': warning STL4037: The effect of instantiating the template std::complex for any type other than float, double, or long double is unspecified. You can define _SILENCE_NONFLOATING_COMPLEX_DEPRECATION_WARNING to suppress this warning + #endif + const std::complex std_cx_short(1, -2); + CODA_OSS_disable_warning_pop + auto actual = str::toString(std_cx_short); + TEST_ASSERT_EQ(actual, expected); + + const types::zinteger types_cx_short(std_cx_short); // "copy constructor" or overload + actual = str::toString(types_cx_short); + TEST_ASSERT_EQ(actual, expected); + + const types::zint16_t zint16(1, -2); + actual = str::toString(zint16); + TEST_ASSERT_EQ(actual, expected); +} +TEST_CASE(test_toTypeComplexShort) +{ + const std::string strValue("(1,-2)"); + + CODA_OSS_disable_warning_push + #if _MSC_VER + #pragma warning(disable: 4996) // '...': warning STL4037: The effect of instantiating the template std::complex for any type other than float, double, or long double is unspecified. You can define _SILENCE_NONFLOATING_COMPLEX_DEPRECATION_WARNING to suppress this warning + #endif + const auto cx_actual = str::toType>(strValue); + CODA_OSS_disable_warning_pop + auto strActual = str::toString(cx_actual); + TEST_ASSERT_EQ(strActual, strValue); + + CODA_OSS_disable_warning_push + #if _MSC_VER + #pragma warning(disable: 4996) // '...': warning STL4037: The effect of instantiating the template std::complex for any type other than float, double, or long double is unspecified. You can define _SILENCE_NONFLOATING_COMPLEX_DEPRECATION_WARNING to suppress this warning + #endif + auto zactual = str::toType>(strValue); + CODA_OSS_disable_warning_pop + strActual = str::toString(zactual); + TEST_ASSERT_EQ(strActual, strValue); + + zactual = str::toType(strValue); + strActual = str::toString(zactual); + TEST_ASSERT_EQ(strActual, strValue); +} + + TEST_MAIN( TEST_CHECK(testTrim); TEST_CHECK(testData); @@ -247,4 +331,8 @@ TEST_MAIN( TEST_CHECK(testRoundDouble); TEST_CHECK(testEscapeForXMLNoReplace); TEST_CHECK(testEscapeForXMLKitchenSink); + TEST_CHECK(test_toStringComplexFloat); + TEST_CHECK(test_toTypeComplexFloat); + TEST_CHECK(test_toStringComplexShort); + TEST_CHECK(test_toTypeComplexShort); ) \ No newline at end of file diff --git a/externals/coda-oss/modules/c++/str/wscript b/externals/coda-oss/modules/c++/str/wscript index 8cefbb5f7..d50988cfd 100644 --- a/externals/coda-oss/modules/c++/str/wscript +++ b/externals/coda-oss/modules/c++/str/wscript @@ -1,6 +1,6 @@ NAME = 'str' VERSION = '1.0' -MODULE_DEPS = 'except coda_oss' +MODULE_DEPS = 'types except coda_oss' UNITTEST_DEPS = 'sys std' options = configure = distclean = lambda p: None diff --git a/externals/coda-oss/modules/c++/sys/source/Path.cpp b/externals/coda-oss/modules/c++/sys/source/Path.cpp index c46643854..b775585af 100644 --- a/externals/coda-oss/modules/c++/sys/source/Path.cpp +++ b/externals/coda-oss/modules/c++/sys/source/Path.cpp @@ -178,7 +178,17 @@ std::string Path::absolutePath(const std::string& path) bool Path::isAbsolutePath(const std::string& path) { #ifdef _WIN32 - return !Path::splitDrive(path).first.empty(); + const auto split = Path::splitDrive(path); + const auto drive = split.first; + + // a URL such as "http://example.com" should NOT be an absolute path + // according to std::filesystem::path::is_absolute(). + if (drive.length() > 2) // "C:" + { + return false; // drive letters are single characters, e.g., "C:\Windows" + } + + return !drive.empty(); #else return (!path.empty() && path[0] == Path::delimiter()[0]); #endif diff --git a/externals/coda-oss/modules/c++/sys/unittests/test_path.cpp b/externals/coda-oss/modules/c++/sys/unittests/test_path.cpp index ab838d027..a444b9c65 100644 --- a/externals/coda-oss/modules/c++/sys/unittests/test_path.cpp +++ b/externals/coda-oss/modules/c++/sys/unittests/test_path.cpp @@ -21,6 +21,8 @@ * */ +#include + #include #include "TestCase.h" @@ -81,6 +83,62 @@ TEST_CASE(testPathMerge) TEST_ASSERT_EQ(result, path); } +TEST_CASE(test_std_filesystem_is_absolute) +{ + std::filesystem::path path +#ifdef _WIN32 + (R"(c:\a\b\c)"); +#else + ("/a/b/c"); +#endif + TEST_ASSERT_TRUE(path.is_absolute()); + path = +#ifdef _WIN32 + R"(a\b\c)"; +#else + "a/b/c"; +#endif + TEST_ASSERT_FALSE(path.is_absolute()); + TEST_ASSERT_TRUE(path.is_relative()); + + const std::filesystem::path slash("/"); + // https://en.cppreference.com/w/cpp/filesystem/path/is_absrel + // "The path "/" is absolute on a POSIX OS, but is relative on Windows." +#ifdef _WIN32 + TEST_ASSERT_TRUE(slash.is_relative()); + TEST_ASSERT_FALSE(slash.is_absolute()); +#else + TEST_ASSERT_TRUE(slash.is_absolute()); + TEST_ASSERT_FALSE(slash.is_relative()); +#endif + + std::filesystem::path url("x://example.com"); // 1 letter +#ifdef _WIN32 + TEST_ASSERT_TRUE(url.is_absolute()); // looks like a drive letter on Windows + TEST_ASSERT_FALSE(url.is_relative()); +#else + TEST_ASSERT_FALSE(url.is_absolute()); + TEST_ASSERT_TRUE(url.is_relative()); +#endif + + url = "s3://example.com"; // 2 letters + TEST_ASSERT_FALSE(url.is_absolute()); + TEST_ASSERT_TRUE(url.is_relative()); // Should this be false? + + url = "ftp://example.com"; // 3 letters + TEST_ASSERT_FALSE(url.is_absolute()); + + url = "http://example.com"; // 4 letters + TEST_ASSERT_FALSE(url.is_absolute()); + + url = "https://example.com"; // 5 letters + TEST_ASSERT_FALSE(url.is_absolute()); + + url = "mailto:nobody@example.com"; // 6 letters + TEST_ASSERT_FALSE(url.is_absolute()); + TEST_ASSERT_TRUE(url.is_relative()); // Should this be false? +} + TEST_CASE(testExpandEnvTilde) { auto path = sys::Path::expandEnvironmentVariables("~"); @@ -322,6 +380,7 @@ TEST_CASE(testModifyVar2) TEST_MAIN( TEST_CHECK(testPathMerge); + TEST_CHECK(test_std_filesystem_is_absolute); TEST_CHECK(testExpandEnvTilde); TEST_CHECK(testExpandEnv); TEST_CHECK(testExpandEnvTildePath); diff --git a/externals/coda-oss/modules/c++/types/CMakeLists.txt b/externals/coda-oss/modules/c++/types/CMakeLists.txt index d1accfb2e..00881001c 100644 --- a/externals/coda-oss/modules/c++/types/CMakeLists.txt +++ b/externals/coda-oss/modules/c++/types/CMakeLists.txt @@ -2,7 +2,7 @@ set(MODULE_NAME types) coda_add_module(${MODULE_NAME} VERSION 1.0 - DEPS coda_oss-c++ config-c++ gsl-c++ coda_oss-c++) + DEPS coda_oss-c++ config-c++ gsl-c++) coda_add_tests( MODULE_NAME ${MODULE_NAME} diff --git a/externals/coda-oss/modules/c++/types/include/types/complex.h b/externals/coda-oss/modules/c++/types/include/types/complex.h index f6051594c..95e6132d3 100644 --- a/externals/coda-oss/modules/c++/types/include/types/complex.h +++ b/externals/coda-oss/modules/c++/types/include/types/complex.h @@ -27,49 +27,41 @@ #include -// TODO: remove this once TIntergers are switched to types::details::complex -// '...': warning STL4037: The effect of instantiating the template std::complex for any type other than float, double, or long double is unspecified. You can define _SILENCE_NONFLOATING_COMPLEX_DEPRECATION_WARNING to suppress this warning. -#ifndef _SILENCE_NONFLOATING_COMPLEX_DEPRECATION_WARNING -#define _SILENCE_NONFLOATING_COMPLEX_DEPRECATION_WARNING -#endif - #include #include +#include #include "config/disable_compiler_warnings.h" #include "coda_oss/CPlusPlus.h" namespace types { -namespace details -{ /*! - * \class complex + * \class zintegerT * \brief Our own implementation of std::complex for SIX and friends. * - * `std::complex` is no longer valid C++; provide a (partial) work-around. - * See https://en.cppreference.com/w/cpp/numeric/complex for detals. - * - * SIX (and others) mostly use `std::complex` as a - * convenient package for two values; very little "complex math" is done - * using integers. + * `std::complex` is no longer valid C++; provide a (partial) work-around. + * See https://en.cppreference.com/w/cpp/numeric/complex for details. + * + * SIX (and others) mostly use `std::complex` as a convenient + * package for two values; very little "complex math" is done using integers. */ -template -struct complex final +template +struct zintegerT final { using value_type = T; static_assert(!std::is_floating_point::value, "Use std::complex for floating-point."); static_assert(std::is_signed::value, "T should be a signed integer."); - complex(value_type re = 0, value_type im = 0) : z{re, im} {} - complex(const complex&) = default; - complex& operator=(const complex&) = default; - complex(complex&&) = default; - complex& operator=(complex&&) = default; - ~complex() = default; + zintegerT(value_type re = 0, value_type im = 0) : z{re, im} { } + zintegerT(const zintegerT&) = default; + zintegerT& operator=(const zintegerT&) = default; + zintegerT(zintegerT&&) = default; + zintegerT& operator=(zintegerT&&) = default; + ~zintegerT() = default; - // If someone already has a std::complex, is there any harm in creating ours? - complex(const std::complex& z_) : complex(z_.real(), z_.imag()) {} + // If someone already has a std::complex, is there any harm in creating ours? + zintegerT(const std::complex& z_) : zintegerT(z_.real(), z_.imag()) { } value_type real() const { @@ -95,51 +87,126 @@ struct complex final CODA_OSS_disable_warning_push #ifdef _MSC_VER -#pragma warning(disable: 4996) // '...': warning STL4037: The effect of instantiating the template std::complex for any type other than float, double, or long double is unspecified. You can define _SILENCE_NONFLOATING_COMPLEX_DEPRECATION_WARNING to suppress this warning. +#pragma warning(disable : 4996) // '...': warning STL4037: The effect of instantiating the template std::complex for any type other than float, double, or long double is unspecified. You can define _SILENCE_NONFLOATING_COMPLEX_DEPRECATION_WARNING to suppress this warning. #endif - -template -inline const std::complex& cast(const complex& z) +// Getting different results with GCC vs MSVC :-( So just use +// std::complex Assume by the time we're actually using C++23 with a +// compiler that enforces this restriction, "something" will be different. +template +inline const std::complex& cast(const zintegerT& z) { - // Getting different results with GCC vs MSVC :-( So just use - // std::complex Assume by the time we're actually using C++23 with a - // compiler that enforces this restriction, "something" will be different. const void* const pZ_ = &z; return *static_cast*>(pZ_); } - +template +inline std::complex& cast(zintegerT& z) +{ + void* const pZ_ = &z; + return *static_cast*>(pZ_); +} CODA_OSS_disable_warning_pop -// https://en.cppreference.com/w/cpp/numeric/complex/abs -template -inline auto abs(const complex& z) +// https://en.cppreference.com/w/cpp/numeric/complex/operator_ltltgtgt +template +inline auto& operator<<(std::basic_ostream& o, const zintegerT& z) { - return abs(cast(z)); + return o << cast(z); +} +template +inline auto& operator>>(std::basic_istream& o, zintegerT& z) +{ + return o >> cast(z); } +// https://en.cppreference.com/w/cpp/numeric/complex/operator_cmp +template +inline bool operator==(const zintegerT& lhs, const zintegerT& rhs) +{ + return (lhs.real() == rhs.real()) && (lhs.imag() == rhs.imag()); +} +template +inline bool operator!=(const zintegerT& lhs, const zintegerT& rhs) +{ + return !(lhs == rhs); } -//// Have the compiler pick between std::complex and details::complex -//template -//using complex = std::conditional_t::value, std::complex, details::complex>; -//static_assert(std::is_same, complex>::value, "should be details::complex"); +// Keep functions like abs() to a minimum; complex math probably shouldn't be done with integers. +template +inline auto abs(const zintegerT& z) // https://en.cppreference.com/w/cpp/numeric/complex/abs +{ + return abs(cast(z)); +} + +// Control whether zinteger is std::complex or details::zintegerT. +// If it is std::complex, then a types::zinteger overload normally can't be +// used as it will be the same as std::complex +#ifdef CODA_OSS_types_FORCE_unique_zinteger // bypass checks below +#define CODA_OSS_types_unique_zinteger 1 +#endif +#ifdef CODA_OSS_types_NO_unique_zinteger +#ifdef CODA_OSS_types_unique_zinteger +#error "CODA_OSS_types_unique_zinteger already #define'd" +#endif +#define CODA_OSS_types_unique_zinteger 0 +#endif + +#ifndef CODA_OSS_types_unique_zinteger +// If the warning about using std::complex has been turned off, we might +// as well use std:complex. +#ifdef _SILENCE_NONFLOATING_COMPLEX_DEPRECATION_WARNING +#define CODA_OSS_types_unique_zinteger 0 +#endif +#endif + +#ifndef CODA_OSS_types_unique_zinteger +#define CODA_OSS_types_unique_zinteger 1 +#endif + +template +#if CODA_OSS_types_unique_zinteger +using zinteger = zintegerT; +#else +using zinteger = std::complex; +#endif + +namespace details +{ +// Explicit specializations so that clients can't do zreal +template struct zreal; +template<> struct zreal final +{ + using type = std::complex; +}; +template<> struct zreal final +{ + using type = std::complex; +}; +template<> struct zreal final +{ + using type = std::complex; +}; +} // namespace details template -using complex = std::complex; +using zreal = typename details::zreal::type; -static_assert(std::is_same, complex>::value, "should be std::complex"); -static_assert(sizeof(std::complex) == sizeof(complex), "sizeof(sizeof(std::complex) != sizeof(complex)"); +// This might be more trouble than it's worth: there really isn't that much code +// that is generic for both integer and real complex types; recall that the primary +// use of `std::integer` is a "convenient package" for two values. +// +//Have the compiler pick between std::complex and details::complex +//template +//using complex = std::conditional_t::value, zreal_t, zinteger_t>; +static_assert(sizeof(std::complex) == sizeof(zintegerT), "sizeof(sizeof(std::complex) != sizeof(zintegerT)"); +static_assert(std::is_same, zreal>::value, "should be std::complex"); // Convenient aliases -using zfloat = complex; // std::complex -using zdouble = complex; // std::complex -//using zlong_double = complex; // std::complex - -// Intentionally using somewhat cumbersome names -// TODO: switch TIntergers to types::details::complex -using zint8_t = complex; // details:complex -using zint16_t = complex; // details:complex -using zint32_t = complex; // details::complex -using zint64_t = complex; // details::complex +using zfloat = zreal; // std::complex +using zdouble = zreal; // std::complex +//using zlong_double = zreal_t; // std::complex +using zint8_t = zinteger; // details:complex +using zint16_t = zinteger; // details:complex +using zint32_t = zinteger; // details::complex +using zint64_t = zinteger; // details::complex } #endif // CODA_OSS_types_complex_h_INCLUDED_ diff --git a/externals/coda-oss/modules/c++/types/unittests/test_complex.cpp b/externals/coda-oss/modules/c++/types/unittests/test_complex.cpp index 6410e48f3..a16fc758b 100644 --- a/externals/coda-oss/modules/c++/types/unittests/test_complex.cpp +++ b/externals/coda-oss/modules/c++/types/unittests/test_complex.cpp @@ -46,7 +46,7 @@ TEST_CASE(TestCxShort_abs) auto actual = abs(types_zint16); TEST_ASSERT_EQ(actual, expected); - const types::complex types_cx_int16(cx_short); + const types::zinteger types_cx_int16(cx_short); actual = abs(types_cx_int16); TEST_ASSERT_EQ(actual, expected); } diff --git a/externals/coda-oss/modules/c++/types/wscript b/externals/coda-oss/modules/c++/types/wscript index 67379c3bb..eb974e3b8 100644 --- a/externals/coda-oss/modules/c++/types/wscript +++ b/externals/coda-oss/modules/c++/types/wscript @@ -1,6 +1,6 @@ NAME = 'types' VERSION = '1.0' -MODULE_DEPS = 'coda_oss config gsl coda_oss' +MODULE_DEPS = 'coda_oss config gsl' UNITTEST_DEPS = 'sys' options = configure = distclean = lambda p: None